blob: 83ae5c62fcf426e2a4609a23d39748a17b4d884d [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 static com.google.inject.internal.Objects.nonNull;
26import com.google.inject.spi.SourceProviders;
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;
61 @SuppressWarnings({"unchecked"})
62 private BindTarget<T> bindTarget = (BindTarget<T>) EMPTY_BIND_TARGET;
63 private BindScoping bindScoping = EMPTY_SCOPING;
64
65 BindCommand(Object source, Key<T> key) {
66 this.source = nonNull(source, "source");
limpbizkita67e39f2008-02-18 20:10:59 +000067 this.key = nonNull(key, "key");
68 }
69
limpbizkit3d58d6b2008-03-08 16:11:47 +000070 public Object getSource() {
71 return source;
72 }
73
limpbizkita67e39f2008-02-18 20:10:59 +000074 public <V> V acceptVisitor(Visitor<V> visitor) {
75 return visitor.visitBind(this);
76 }
77
78 public Key<T> getKey() {
79 return key;
80 }
81
82 public BindTarget<T> getTarget() {
83 return bindTarget;
84 }
85
86 public BindScoping getScoping() {
87 return bindScoping;
88 }
89
90 @Override public String toString() {
91 return "bind " + key
limpbizkit3d58d6b2008-03-08 16:11:47 +000092 + (bindTarget == EMPTY_BIND_TARGET ? "" : (" to " + bindTarget))
93 + (bindScoping == EMPTY_SCOPING ? "" : (" in " + bindScoping));
limpbizkita67e39f2008-02-18 20:10:59 +000094 }
95
96 private static abstract class AbstractTarget<T> implements BindTarget<T> {
97 public void execute(ConstantBindingBuilder builder) {
98 throw new UnsupportedOperationException();
99 }
limpbizkit3d58d6b2008-03-08 16:11:47 +0000100 public T get() {
101 return null;
limpbizkita67e39f2008-02-18 20:10:59 +0000102 }
limpbizkit3d58d6b2008-03-08 16:11:47 +0000103 public Key<? extends Provider<? extends T>> getProviderKey() {
104 return null;
limpbizkita67e39f2008-02-18 20:10:59 +0000105 }
limpbizkit3d58d6b2008-03-08 16:11:47 +0000106 public Provider<? extends T> getProvider() {
107 return null;
limpbizkita67e39f2008-02-18 20:10:59 +0000108 }
limpbizkit3d58d6b2008-03-08 16:11:47 +0000109 public Key<? extends T> getKey() {
110 return null;
limpbizkita67e39f2008-02-18 20:10:59 +0000111 }
112 }
113
114 private static abstract class AbstractScoping implements BindScoping {
115 public boolean isEagerSingleton() {
116 return false;
117 }
limpbizkit3d58d6b2008-03-08 16:11:47 +0000118 public Scope getScope() {
119 return null;
limpbizkita67e39f2008-02-18 20:10:59 +0000120 }
limpbizkit3d58d6b2008-03-08 16:11:47 +0000121 public Class<? extends Annotation> getScopeAnnotation() {
122 return null;
limpbizkita67e39f2008-02-18 20:10:59 +0000123 }
124 }
125
limpbizkit3d58d6b2008-03-08 16:11:47 +0000126 BindingBuilder bindingBuilder(Binder binder) {
127 return new BindingBuilder(binder);
limpbizkita67e39f2008-02-18 20:10:59 +0000128 }
129
130 /**
131 * Package-private write access to the internal state of this command.
132 */
133 class BindingBuilder implements AnnotatedBindingBuilder<T> {
limpbizkit3d58d6b2008-03-08 16:11:47 +0000134 private final Binder binder;
135
136 BindingBuilder(Binder binder) {
137 this.binder = binder;
138 }
139
limpbizkita67e39f2008-02-18 20:10:59 +0000140 public LinkedBindingBuilder<T> annotatedWith(
141 Class<? extends Annotation> annotationType) {
limpbizkit3d58d6b2008-03-08 16:11:47 +0000142 nonNull(annotationType, "annotationType");
limpbizkit7f8eda02008-03-26 01:11:16 +0000143 checkNotAnnotated();
limpbizkita67e39f2008-02-18 20:10:59 +0000144 key = Key.get(key.getTypeLiteral(), annotationType);
145 return this;
146 }
147
148 public LinkedBindingBuilder<T> annotatedWith(Annotation annotation) {
149 nonNull(annotation, "annotation");
limpbizkit7f8eda02008-03-26 01:11:16 +0000150 checkNotAnnotated();
limpbizkita67e39f2008-02-18 20:10:59 +0000151 key = Key.get(key.getTypeLiteral(), annotation);
152 return this;
153 }
154
155 public ScopedBindingBuilder to(final Class<? extends T> implementation) {
156 return to(Key.get(implementation));
157 }
158
159 public ScopedBindingBuilder to(
160 final TypeLiteral<? extends T> implementation) {
161 return to(Key.get(implementation));
162 }
163
164 public ScopedBindingBuilder to(final Key<? extends T> targetKey) {
165 nonNull(targetKey, "targetKey");
limpbizkit7f8eda02008-03-26 01:11:16 +0000166 checkNotTargetted();
limpbizkita67e39f2008-02-18 20:10:59 +0000167 bindTarget = new AbstractTarget<T>() {
168 public ScopedBindingBuilder execute(LinkedBindingBuilder<T> linkedBindingBuilder) {
169 return linkedBindingBuilder.to(targetKey);
170 }
limpbizkit3d58d6b2008-03-08 16:11:47 +0000171 @Override public Key<? extends T> getKey() {
limpbizkita67e39f2008-02-18 20:10:59 +0000172 return targetKey;
173 }
limpbizkit3d58d6b2008-03-08 16:11:47 +0000174 public <V> V acceptVisitor(Visitor<T, V> visitor) {
175 return visitor.visitToKey(targetKey);
176 }
limpbizkita67e39f2008-02-18 20:10:59 +0000177 @Override public String toString() {
178 return String.valueOf(targetKey);
179 }
180 };
181 return this;
182 }
183
184 public void toInstance(final T instance) {
limpbizkit8b237452008-04-22 06:47:36 +0000185 nonNull(instance, ErrorMessages.CANNOT_BIND_TO_NULL_INSTANCE);
limpbizkit3d58d6b2008-03-08 16:11:47 +0000186
limpbizkit7f8eda02008-03-26 01:11:16 +0000187 checkNotTargetted();
limpbizkita67e39f2008-02-18 20:10:59 +0000188 bindTarget = new AbstractTarget<T>() {
189 public ScopedBindingBuilder execute(LinkedBindingBuilder<T> linkedBindingBuilder) {
190 linkedBindingBuilder.toInstance(instance);
191 return null;
192 }
limpbizkit3d58d6b2008-03-08 16:11:47 +0000193 @Override public T get() {
limpbizkita67e39f2008-02-18 20:10:59 +0000194 return instance;
195 }
limpbizkit3d58d6b2008-03-08 16:11:47 +0000196 public <V> V acceptVisitor(Visitor<T, V> visitor) {
197 return visitor.visitToInstance(instance);
198 }
limpbizkita67e39f2008-02-18 20:10:59 +0000199 @Override public String toString() {
200 return "instance " + instance;
201 }
202 };
203 }
204
205 public ScopedBindingBuilder toProvider(final Provider<? extends T> provider) {
206 nonNull(provider, "provider");
limpbizkit7f8eda02008-03-26 01:11:16 +0000207 checkNotTargetted();
limpbizkita67e39f2008-02-18 20:10:59 +0000208 bindTarget = new AbstractTarget<T>() {
209 public ScopedBindingBuilder execute(LinkedBindingBuilder<T> linkedBindingBuilder) {
210 return linkedBindingBuilder.toProvider(provider);
211 }
limpbizkit3d58d6b2008-03-08 16:11:47 +0000212 @Override public Provider<? extends T> getProvider() {
limpbizkita67e39f2008-02-18 20:10:59 +0000213 return provider;
214 }
limpbizkit3d58d6b2008-03-08 16:11:47 +0000215 public <V> V acceptVisitor(Visitor<T, V> visitor) {
216 return visitor.visitToProvider(provider);
217 }
limpbizkita67e39f2008-02-18 20:10:59 +0000218 @Override public String toString() {
219 return "provider " + provider;
220 }
221 };
222 return this;
223 }
224
225 public ScopedBindingBuilder toProvider(
226 Class<? extends Provider<? extends T>> providerType) {
227 return toProvider(Key.get(providerType));
228 }
229
230 public ScopedBindingBuilder toProvider(
231 final Key<? extends Provider<? extends T>> providerKey) {
232 nonNull(providerKey, "providerKey");
limpbizkit7f8eda02008-03-26 01:11:16 +0000233 checkNotTargetted();
limpbizkita67e39f2008-02-18 20:10:59 +0000234 bindTarget = new AbstractTarget<T>() {
235 public ScopedBindingBuilder execute(LinkedBindingBuilder<T> linkedBindingBuilder) {
236 return linkedBindingBuilder.toProvider(providerKey);
237 }
limpbizkit3d58d6b2008-03-08 16:11:47 +0000238 @Override public Key<? extends Provider<? extends T>> getProviderKey() {
limpbizkita67e39f2008-02-18 20:10:59 +0000239 return providerKey;
240 }
limpbizkit3d58d6b2008-03-08 16:11:47 +0000241 public <V> V acceptVisitor(Visitor<T, V> visitor) {
242 return visitor.visitToProviderKey(providerKey);
243 }
limpbizkita67e39f2008-02-18 20:10:59 +0000244 @Override public String toString() {
245 return "provider " + providerKey;
246 }
247 };
248 return this;
249 }
250
251 public void in(final Class<? extends Annotation> scopeAnnotation) {
252 nonNull(scopeAnnotation, "scopeAnnotation");
limpbizkit7f8eda02008-03-26 01:11:16 +0000253 checkNotScoped();
limpbizkita67e39f2008-02-18 20:10:59 +0000254
255 bindScoping = new AbstractScoping() {
256 public void execute(ScopedBindingBuilder scopedBindingBuilder) {
257 scopedBindingBuilder.in(scopeAnnotation);
258 }
limpbizkit3d58d6b2008-03-08 16:11:47 +0000259 @Override public Class<? extends Annotation> getScopeAnnotation() {
limpbizkita67e39f2008-02-18 20:10:59 +0000260 return scopeAnnotation;
261 }
limpbizkit3d58d6b2008-03-08 16:11:47 +0000262 public <V> V acceptVisitor(Visitor<V> visitor) {
263 return visitor.visitScopeAnnotation(scopeAnnotation);
264 }
limpbizkita67e39f2008-02-18 20:10:59 +0000265 @Override public String toString() {
266 return scopeAnnotation.getName();
267 }
268 };
269 }
270
271 public void in(final Scope scope) {
272 nonNull(scope, "scope");
limpbizkit7f8eda02008-03-26 01:11:16 +0000273 checkNotScoped();
limpbizkita67e39f2008-02-18 20:10:59 +0000274 bindScoping = new AbstractScoping() {
limpbizkit3d58d6b2008-03-08 16:11:47 +0000275
limpbizkita67e39f2008-02-18 20:10:59 +0000276 public void execute(ScopedBindingBuilder scopedBindingBuilder) {
277 scopedBindingBuilder.in(scope);
278 }
limpbizkit3d58d6b2008-03-08 16:11:47 +0000279 @Override public Scope getScope() {
limpbizkita67e39f2008-02-18 20:10:59 +0000280 return scope;
281 }
limpbizkit3d58d6b2008-03-08 16:11:47 +0000282 public <V> V acceptVisitor(Visitor<V> visitor) {
283 return visitor.visitScope(scope);
284 }
limpbizkita67e39f2008-02-18 20:10:59 +0000285 @Override public String toString() {
286 return String.valueOf(scope);
287 }
288 };
289 }
290
291 public void asEagerSingleton() {
limpbizkit7f8eda02008-03-26 01:11:16 +0000292 checkNotScoped();
limpbizkita67e39f2008-02-18 20:10:59 +0000293 bindScoping = new AbstractScoping() {
294 public void execute(ScopedBindingBuilder scopedBindingBuilder) {
295 scopedBindingBuilder.asEagerSingleton();
296 }
297 @Override public boolean isEagerSingleton() {
298 return true;
299 }
limpbizkit3d58d6b2008-03-08 16:11:47 +0000300 public <V> V acceptVisitor(Visitor<V> visitor) {
301 return visitor.visitEagerSingleton();
302 }
limpbizkita67e39f2008-02-18 20:10:59 +0000303 @Override public String toString() {
304 return "eager singleton";
305 }
306 };
307 }
308
limpbizkit3d58d6b2008-03-08 16:11:47 +0000309 static final String IMPLEMENTATION_ALREADY_SET
310 = "Implementation is set more than once.";
311 static final String SINGLE_INSTANCE_AND_SCOPE = "Setting the scope is not"
312 + " permitted when binding to a single instance.";
313 static final String SCOPE_ALREADY_SET = "Scope is set more than once.";
314 static final String ANNOTATION_ALREADY_SPECIFIED = "More than one annotation"
315 + " is specified for this binding.";
316
limpbizkit7f8eda02008-03-26 01:11:16 +0000317 private void checkNotTargetted() {
limpbizkit3d58d6b2008-03-08 16:11:47 +0000318 if (bindTarget != EMPTY_BIND_TARGET) {
319 binder.addError(IMPLEMENTATION_ALREADY_SET);
limpbizkita67e39f2008-02-18 20:10:59 +0000320 }
321 }
322
limpbizkit7f8eda02008-03-26 01:11:16 +0000323 private void checkNotAnnotated() {
limpbizkita67e39f2008-02-18 20:10:59 +0000324 if (BindCommand.this.key.getAnnotationType() != null) {
limpbizkit3d58d6b2008-03-08 16:11:47 +0000325 binder.addError(ANNOTATION_ALREADY_SPECIFIED);
limpbizkita67e39f2008-02-18 20:10:59 +0000326 }
327 }
328
limpbizkit7f8eda02008-03-26 01:11:16 +0000329 private void checkNotScoped() {
limpbizkit3d58d6b2008-03-08 16:11:47 +0000330 // Scoping isn't allowed when we have only one instance.
331 if (bindTarget.get() != null) {
332 binder.addError(SINGLE_INSTANCE_AND_SCOPE);
333 return;
334 }
335
336 if (bindScoping != EMPTY_SCOPING) {
337 binder.addError(SCOPE_ALREADY_SET);
limpbizkita67e39f2008-02-18 20:10:59 +0000338 }
339 }
limpbizkitd6967b92008-05-16 15:28:51 +0000340
341 @Override public String toString() {
342 String type = key.getAnnotationType() == null
343 ? "AnnotatedBindingBuilder<"
344 : "LinkedBindingBuilder<";
345 return type + key.getTypeLiteral() + ">";
346 }
limpbizkita67e39f2008-02-18 20:10:59 +0000347 }
348}