blob: b57c50a0b5ddd87a52d1ede8fcb64ea4f37c4ace [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.*;
20import com.google.inject.spi.SourceProviders;
limpbizkita67e39f2008-02-18 20:10:59 +000021import com.google.inject.binder.AnnotatedBindingBuilder;
22import com.google.inject.binder.ConstantBindingBuilder;
23import com.google.inject.binder.LinkedBindingBuilder;
24import com.google.inject.binder.ScopedBindingBuilder;
25import static com.google.inject.internal.Objects.nonNull;
26
27import java.lang.annotation.Annotation;
28
29/**
30 * Immutable snapshot of a request to bind a value.
31 *
32 * @author jessewilson@google.com (Jesse Wilson)
33 */
34public final class BindCommand<T> implements Command {
limpbizkita67e39f2008-02-18 20:10:59 +000035
limpbizkit3d58d6b2008-03-08 16:11:47 +000036 static {
37 SourceProviders.skip(BindCommand.BindingBuilder.class);
38 }
39
40 private static final BindTarget<Object> EMPTY_BIND_TARGET = new AbstractTarget<Object>() {
41 public ScopedBindingBuilder execute(LinkedBindingBuilder<Object> linkedBindingBuilder) {
42 return linkedBindingBuilder;
43 }
44 public <V> V acceptVisitor(Visitor<Object, V> visitor) {
45 return visitor.visitUntargetted();
46 }
47 };
48
49 private static final BindScoping EMPTY_SCOPING = new AbstractScoping() {
50 public void execute(ScopedBindingBuilder scopedBindingBuilder) {
51 // do nothing
52 }
53 public <V> V acceptVisitor(Visitor<V> visitor) {
54 return visitor.visitNoScoping();
55 }
56 };
57
58 private final Object source;
59 private Key<T> key;
60 @SuppressWarnings({"unchecked"})
61 private BindTarget<T> bindTarget = (BindTarget<T>) EMPTY_BIND_TARGET;
62 private BindScoping bindScoping = EMPTY_SCOPING;
63
64 BindCommand(Object source, Key<T> key) {
65 this.source = nonNull(source, "source");
limpbizkita67e39f2008-02-18 20:10:59 +000066 this.key = nonNull(key, "key");
67 }
68
limpbizkit3d58d6b2008-03-08 16:11:47 +000069 public Object getSource() {
70 return source;
71 }
72
limpbizkita67e39f2008-02-18 20:10:59 +000073 public <V> V acceptVisitor(Visitor<V> visitor) {
74 return visitor.visitBind(this);
75 }
76
77 public Key<T> getKey() {
78 return key;
79 }
80
81 public BindTarget<T> getTarget() {
82 return bindTarget;
83 }
84
85 public BindScoping getScoping() {
86 return bindScoping;
87 }
88
89 @Override public String toString() {
90 return "bind " + key
limpbizkit3d58d6b2008-03-08 16:11:47 +000091 + (bindTarget == EMPTY_BIND_TARGET ? "" : (" to " + bindTarget))
92 + (bindScoping == EMPTY_SCOPING ? "" : (" in " + bindScoping));
limpbizkita67e39f2008-02-18 20:10:59 +000093 }
94
95 private static abstract class AbstractTarget<T> implements BindTarget<T> {
96 public void execute(ConstantBindingBuilder builder) {
97 throw new UnsupportedOperationException();
98 }
limpbizkit3d58d6b2008-03-08 16:11:47 +000099 public T get() {
100 return null;
limpbizkita67e39f2008-02-18 20:10:59 +0000101 }
limpbizkit3d58d6b2008-03-08 16:11:47 +0000102 public Key<? extends Provider<? extends T>> getProviderKey() {
103 return null;
limpbizkita67e39f2008-02-18 20:10:59 +0000104 }
limpbizkit3d58d6b2008-03-08 16:11:47 +0000105 public Provider<? extends T> getProvider() {
106 return null;
limpbizkita67e39f2008-02-18 20:10:59 +0000107 }
limpbizkit3d58d6b2008-03-08 16:11:47 +0000108 public Key<? extends T> getKey() {
109 return null;
limpbizkita67e39f2008-02-18 20:10:59 +0000110 }
111 }
112
113 private static abstract class AbstractScoping implements BindScoping {
114 public boolean isEagerSingleton() {
115 return false;
116 }
limpbizkit3d58d6b2008-03-08 16:11:47 +0000117 public Scope getScope() {
118 return null;
limpbizkita67e39f2008-02-18 20:10:59 +0000119 }
limpbizkit3d58d6b2008-03-08 16:11:47 +0000120 public Class<? extends Annotation> getScopeAnnotation() {
121 return null;
limpbizkita67e39f2008-02-18 20:10:59 +0000122 }
123 }
124
limpbizkit3d58d6b2008-03-08 16:11:47 +0000125 BindingBuilder bindingBuilder(Binder binder) {
126 return new BindingBuilder(binder);
limpbizkita67e39f2008-02-18 20:10:59 +0000127 }
128
129 /**
130 * Package-private write access to the internal state of this command.
131 */
132 class BindingBuilder implements AnnotatedBindingBuilder<T> {
limpbizkit3d58d6b2008-03-08 16:11:47 +0000133 private final Binder binder;
134
135 BindingBuilder(Binder binder) {
136 this.binder = binder;
137 }
138
limpbizkita67e39f2008-02-18 20:10:59 +0000139 public LinkedBindingBuilder<T> annotatedWith(
140 Class<? extends Annotation> annotationType) {
limpbizkit3d58d6b2008-03-08 16:11:47 +0000141 nonNull(annotationType, "annotationType");
limpbizkit7f8eda02008-03-26 01:11:16 +0000142 checkNotAnnotated();
limpbizkita67e39f2008-02-18 20:10:59 +0000143 key = Key.get(key.getTypeLiteral(), annotationType);
144 return this;
145 }
146
147 public LinkedBindingBuilder<T> annotatedWith(Annotation annotation) {
148 nonNull(annotation, "annotation");
limpbizkit7f8eda02008-03-26 01:11:16 +0000149 checkNotAnnotated();
limpbizkita67e39f2008-02-18 20:10:59 +0000150 key = Key.get(key.getTypeLiteral(), annotation);
151 return this;
152 }
153
154 public ScopedBindingBuilder to(final Class<? extends T> implementation) {
155 return to(Key.get(implementation));
156 }
157
158 public ScopedBindingBuilder to(
159 final TypeLiteral<? extends T> implementation) {
160 return to(Key.get(implementation));
161 }
162
163 public ScopedBindingBuilder to(final Key<? extends T> targetKey) {
164 nonNull(targetKey, "targetKey");
limpbizkit7f8eda02008-03-26 01:11:16 +0000165 checkNotTargetted();
limpbizkita67e39f2008-02-18 20:10:59 +0000166 bindTarget = new AbstractTarget<T>() {
167 public ScopedBindingBuilder execute(LinkedBindingBuilder<T> linkedBindingBuilder) {
168 return linkedBindingBuilder.to(targetKey);
169 }
limpbizkit3d58d6b2008-03-08 16:11:47 +0000170 @Override public Key<? extends T> getKey() {
limpbizkita67e39f2008-02-18 20:10:59 +0000171 return targetKey;
172 }
limpbizkit3d58d6b2008-03-08 16:11:47 +0000173 public <V> V acceptVisitor(Visitor<T, V> visitor) {
174 return visitor.visitToKey(targetKey);
175 }
limpbizkita67e39f2008-02-18 20:10:59 +0000176 @Override public String toString() {
177 return String.valueOf(targetKey);
178 }
179 };
180 return this;
181 }
182
183 public void toInstance(final T instance) {
limpbizkit3d58d6b2008-03-08 16:11:47 +0000184 // might someday want to tolerate null here, probably by setting up a
185 // Provider<null> rather than trying to distinguish between null and
186 // not set
187 nonNull(instance, "instance");
188
limpbizkit7f8eda02008-03-26 01:11:16 +0000189 checkNotTargetted();
limpbizkita67e39f2008-02-18 20:10:59 +0000190 bindTarget = new AbstractTarget<T>() {
191 public ScopedBindingBuilder execute(LinkedBindingBuilder<T> linkedBindingBuilder) {
192 linkedBindingBuilder.toInstance(instance);
193 return null;
194 }
limpbizkit3d58d6b2008-03-08 16:11:47 +0000195 @Override public T get() {
limpbizkita67e39f2008-02-18 20:10:59 +0000196 return instance;
197 }
limpbizkit3d58d6b2008-03-08 16:11:47 +0000198 public <V> V acceptVisitor(Visitor<T, V> visitor) {
199 return visitor.visitToInstance(instance);
200 }
limpbizkita67e39f2008-02-18 20:10:59 +0000201 @Override public String toString() {
202 return "instance " + instance;
203 }
204 };
205 }
206
207 public ScopedBindingBuilder toProvider(final Provider<? extends T> provider) {
208 nonNull(provider, "provider");
limpbizkit7f8eda02008-03-26 01:11:16 +0000209 checkNotTargetted();
limpbizkita67e39f2008-02-18 20:10:59 +0000210 bindTarget = new AbstractTarget<T>() {
211 public ScopedBindingBuilder execute(LinkedBindingBuilder<T> linkedBindingBuilder) {
212 return linkedBindingBuilder.toProvider(provider);
213 }
limpbizkit3d58d6b2008-03-08 16:11:47 +0000214 @Override public Provider<? extends T> getProvider() {
limpbizkita67e39f2008-02-18 20:10:59 +0000215 return provider;
216 }
limpbizkit3d58d6b2008-03-08 16:11:47 +0000217 public <V> V acceptVisitor(Visitor<T, V> visitor) {
218 return visitor.visitToProvider(provider);
219 }
limpbizkita67e39f2008-02-18 20:10:59 +0000220 @Override public String toString() {
221 return "provider " + provider;
222 }
223 };
224 return this;
225 }
226
227 public ScopedBindingBuilder toProvider(
228 Class<? extends Provider<? extends T>> providerType) {
229 return toProvider(Key.get(providerType));
230 }
231
232 public ScopedBindingBuilder toProvider(
233 final Key<? extends Provider<? extends T>> providerKey) {
234 nonNull(providerKey, "providerKey");
limpbizkit7f8eda02008-03-26 01:11:16 +0000235 checkNotTargetted();
limpbizkita67e39f2008-02-18 20:10:59 +0000236 bindTarget = new AbstractTarget<T>() {
237 public ScopedBindingBuilder execute(LinkedBindingBuilder<T> linkedBindingBuilder) {
238 return linkedBindingBuilder.toProvider(providerKey);
239 }
limpbizkit3d58d6b2008-03-08 16:11:47 +0000240 @Override public Key<? extends Provider<? extends T>> getProviderKey() {
limpbizkita67e39f2008-02-18 20:10:59 +0000241 return providerKey;
242 }
limpbizkit3d58d6b2008-03-08 16:11:47 +0000243 public <V> V acceptVisitor(Visitor<T, V> visitor) {
244 return visitor.visitToProviderKey(providerKey);
245 }
limpbizkita67e39f2008-02-18 20:10:59 +0000246 @Override public String toString() {
247 return "provider " + providerKey;
248 }
249 };
250 return this;
251 }
252
253 public void in(final Class<? extends Annotation> scopeAnnotation) {
254 nonNull(scopeAnnotation, "scopeAnnotation");
limpbizkit7f8eda02008-03-26 01:11:16 +0000255 checkNotScoped();
limpbizkita67e39f2008-02-18 20:10:59 +0000256
257 bindScoping = new AbstractScoping() {
258 public void execute(ScopedBindingBuilder scopedBindingBuilder) {
259 scopedBindingBuilder.in(scopeAnnotation);
260 }
limpbizkit3d58d6b2008-03-08 16:11:47 +0000261 @Override public Class<? extends Annotation> getScopeAnnotation() {
limpbizkita67e39f2008-02-18 20:10:59 +0000262 return scopeAnnotation;
263 }
limpbizkit3d58d6b2008-03-08 16:11:47 +0000264 public <V> V acceptVisitor(Visitor<V> visitor) {
265 return visitor.visitScopeAnnotation(scopeAnnotation);
266 }
limpbizkita67e39f2008-02-18 20:10:59 +0000267 @Override public String toString() {
268 return scopeAnnotation.getName();
269 }
270 };
271 }
272
273 public void in(final Scope scope) {
274 nonNull(scope, "scope");
limpbizkit7f8eda02008-03-26 01:11:16 +0000275 checkNotScoped();
limpbizkita67e39f2008-02-18 20:10:59 +0000276 bindScoping = new AbstractScoping() {
limpbizkit3d58d6b2008-03-08 16:11:47 +0000277
limpbizkita67e39f2008-02-18 20:10:59 +0000278 public void execute(ScopedBindingBuilder scopedBindingBuilder) {
279 scopedBindingBuilder.in(scope);
280 }
limpbizkit3d58d6b2008-03-08 16:11:47 +0000281 @Override public Scope getScope() {
limpbizkita67e39f2008-02-18 20:10:59 +0000282 return scope;
283 }
limpbizkit3d58d6b2008-03-08 16:11:47 +0000284 public <V> V acceptVisitor(Visitor<V> visitor) {
285 return visitor.visitScope(scope);
286 }
limpbizkita67e39f2008-02-18 20:10:59 +0000287 @Override public String toString() {
288 return String.valueOf(scope);
289 }
290 };
291 }
292
293 public void asEagerSingleton() {
limpbizkit7f8eda02008-03-26 01:11:16 +0000294 checkNotScoped();
limpbizkita67e39f2008-02-18 20:10:59 +0000295 bindScoping = new AbstractScoping() {
296 public void execute(ScopedBindingBuilder scopedBindingBuilder) {
297 scopedBindingBuilder.asEagerSingleton();
298 }
299 @Override public boolean isEagerSingleton() {
300 return true;
301 }
limpbizkit3d58d6b2008-03-08 16:11:47 +0000302 public <V> V acceptVisitor(Visitor<V> visitor) {
303 return visitor.visitEagerSingleton();
304 }
limpbizkita67e39f2008-02-18 20:10:59 +0000305 @Override public String toString() {
306 return "eager singleton";
307 }
308 };
309 }
310
limpbizkit3d58d6b2008-03-08 16:11:47 +0000311 static final String IMPLEMENTATION_ALREADY_SET
312 = "Implementation is set more than once.";
313 static final String SINGLE_INSTANCE_AND_SCOPE = "Setting the scope is not"
314 + " permitted when binding to a single instance.";
315 static final String SCOPE_ALREADY_SET = "Scope is set more than once.";
316 static final String ANNOTATION_ALREADY_SPECIFIED = "More than one annotation"
317 + " is specified for this binding.";
318
limpbizkit7f8eda02008-03-26 01:11:16 +0000319 private void checkNotTargetted() {
limpbizkit3d58d6b2008-03-08 16:11:47 +0000320 if (bindTarget != EMPTY_BIND_TARGET) {
321 binder.addError(IMPLEMENTATION_ALREADY_SET);
limpbizkita67e39f2008-02-18 20:10:59 +0000322 }
323 }
324
limpbizkit7f8eda02008-03-26 01:11:16 +0000325 private void checkNotAnnotated() {
limpbizkita67e39f2008-02-18 20:10:59 +0000326 if (BindCommand.this.key.getAnnotationType() != null) {
limpbizkit3d58d6b2008-03-08 16:11:47 +0000327 binder.addError(ANNOTATION_ALREADY_SPECIFIED);
limpbizkita67e39f2008-02-18 20:10:59 +0000328 }
329 }
330
limpbizkit7f8eda02008-03-26 01:11:16 +0000331 private void checkNotScoped() {
limpbizkit3d58d6b2008-03-08 16:11:47 +0000332 // Scoping isn't allowed when we have only one instance.
333 if (bindTarget.get() != null) {
334 binder.addError(SINGLE_INSTANCE_AND_SCOPE);
335 return;
336 }
337
338 if (bindScoping != EMPTY_SCOPING) {
339 binder.addError(SCOPE_ALREADY_SET);
limpbizkita67e39f2008-02-18 20:10:59 +0000340 }
341 }
342 }
343}