blob: bb8c5274242e422e8e1898fd8dfd10007610f015 [file] [log] [blame]
crazyboblee66b415a2006-08-25 02:01:19 +00001/**
2 * Copyright (C) 2006 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
crazyboblee7c5b2c42007-01-20 02:05:20 +000019import com.google.inject.util.Objects;
20import static com.google.inject.util.Objects.nonNull;
crazyboblee4727ee22007-01-30 03:13:38 +000021import com.google.inject.spi.ErrorMessage;
crazyboblee7c5b2c42007-01-20 02:05:20 +000022
crazyboblee66b415a2006-08-25 02:01:19 +000023import java.lang.reflect.Member;
24import java.util.ArrayList;
25import java.util.Arrays;
26import java.util.HashMap;
crazyboblee66b415a2006-08-25 02:01:19 +000027import java.util.List;
28import java.util.Map;
crazyboblee07e41822006-11-21 01:27:08 +000029import java.util.Properties;
crazyboblee7c5b2c42007-01-20 02:05:20 +000030import java.util.Set;
31import java.util.LinkedHashSet;
crazyboblee66b415a2006-08-25 02:01:19 +000032import java.util.logging.Logger;
33
34/**
crazyboblee7c5b2c42007-01-20 02:05:20 +000035 * Builds a dependency injection {@link Container}. Binds {@link Key}s to
36 * implementations. For example, a binding implementation could be anything
37 * from a constant value to an object in the HTTP session.
crazyboblee66b415a2006-08-25 02:01:19 +000038 *
crazyboblee7c5b2c42007-01-20 02:05:20 +000039 * <p>Not safe for concurrent use.
40 *
41 * <p>Default bindings include:
crazyboblee66b415a2006-08-25 02:01:19 +000042 *
43 * <ul>
crazyboblee78e1cc12007-01-26 02:18:06 +000044 * <li>A {@code Factory<T>} for each binding of type {@code T}
crazyboblee7c5b2c42007-01-20 02:05:20 +000045 * <li>The {@link Container} iself
46 * <li>The {@link Logger} for the class being injected
crazyboblee66b415a2006-08-25 02:01:19 +000047 * </ul>
48 *
crazyboblee07e41822006-11-21 01:27:08 +000049 * <p>Converts constants as needed from {@code String} to any primitive type
crazyboblee7c5b2c42007-01-20 02:05:20 +000050 * in addition to {@code enum} and {@code Class<?>}.
crazyboblee07e41822006-11-21 01:27:08 +000051 *
crazyboblee66b415a2006-08-25 02:01:19 +000052 * @author crazybob@google.com (Bob Lee)
53 */
54public final class ContainerBuilder {
55
crazyboblee63b592b2007-01-25 02:45:24 +000056 private static final Logger logger =
57 Logger.getLogger(ContainerBuilder.class.getName());
58
crazyboblee7c5b2c42007-01-20 02:05:20 +000059 final List<BindingBuilder<?>> bindingBuilders =
60 new ArrayList<BindingBuilder<?>>();
61 final List<ConstantBindingBuilder> constantBindingBuilders =
62 new ArrayList<ConstantBindingBuilder>();
63 final List<LinkedBindingBuilder<?>> linkedBindingBuilders =
64 new ArrayList<LinkedBindingBuilder<?>>();
crazyboblee63b592b2007-01-25 02:45:24 +000065 final Map<String, Scope> scopes = new HashMap<String, Scope>();
crazyboblee7c5b2c42007-01-20 02:05:20 +000066
crazyboblee66b415a2006-08-25 02:01:19 +000067 final List<Class<?>> staticInjections = new ArrayList<Class<?>>();
crazyboblee7c5b2c42007-01-20 02:05:20 +000068
crazyboblee66b415a2006-08-25 02:01:19 +000069 boolean created;
70
crazyboblee7c5b2c42007-01-20 02:05:20 +000071 /**
72 * Keeps error messages in order and prevents duplicates.
73 */
74 Set<ErrorMessage> errorMessages = new LinkedHashSet<ErrorMessage>();
75
crazyboblee66b415a2006-08-25 02:01:19 +000076 private static final InternalFactory<Container> CONTAINER_FACTORY =
77 new InternalFactory<Container>() {
crazyboblee7c5b2c42007-01-20 02:05:20 +000078 public Container get(InternalContext context) {
crazyboblee66b415a2006-08-25 02:01:19 +000079 return context.getContainer();
80 }
81 };
82
83 private static final InternalFactory<Logger> LOGGER_FACTORY =
84 new InternalFactory<Logger>() {
crazyboblee7c5b2c42007-01-20 02:05:20 +000085 public Logger get(InternalContext context) {
crazyboblee66b415a2006-08-25 02:01:19 +000086 Member member = context.getExternalContext().getMember();
87 return member == null ? Logger.getAnonymousLogger()
88 : Logger.getLogger(member.getDeclaringClass().getName());
89 }
90 };
91
crazyboblee7c5b2c42007-01-20 02:05:20 +000092 static final String UNKNOWN_SOURCE = "[unknown source]";
93
crazyboblee63b592b2007-01-25 02:45:24 +000094 static final Scope DEFAULT_SCOPE = new Scope() {
95 public <T> Factory<T> scope(Key<T> key, Factory<T> creator) {
96 // We actually optimize around this.
97 throw new UnsupportedOperationException();
98 }
99 };
100
crazyboblee66b415a2006-08-25 02:01:19 +0000101 /**
102 * Constructs a new builder.
103 */
104 public ContainerBuilder() {
crazyboblee63b592b2007-01-25 02:45:24 +0000105 put(Scopes.DEFAULT, DEFAULT_SCOPE);
106 put(Scopes.SINGLETON, SingletonScope.INSTANCE);
107
crazyboblee7c5b2c42007-01-20 02:05:20 +0000108 bind(Container.class).to(CONTAINER_FACTORY);
109 bind(Logger.class).to(LOGGER_FACTORY);
crazyboblee66b415a2006-08-25 02:01:19 +0000110 }
111
crazyboblee4727ee22007-01-30 03:13:38 +0000112 final List<Validation> validations = new ArrayList<Validation>();
113
114 /**
115 * Registers a type to be validated for injection when we create the
116 * container.
117 */
118 void validate(final Object source, final Class<?> type) {
119 validations.add(new Validation() {
120 public void run(ContainerImpl container) {
121 ErrorHandler previous = container.getErrorHandler();
122 try {
123 container.setErrorHandler(new ConfigurationErrorHandler(source));
124 container.getConstructor(type);
125 } finally {
126 container.setErrorHandler(previous);
127 }
128 }
129 });
130 }
131
132 interface Validation {
133 void run(ContainerImpl container);
134 }
135
crazyboblee66b415a2006-08-25 02:01:19 +0000136 /**
crazybobleedac49912007-01-29 23:17:41 +0000137 * Creates an object pointing to the current location within the
138 * configuration. If we run into a problem later, we'll be able to trace it
139 * back to the original source. Useful for debugging. The default
140 * implementation returns {@code ContainerBuilder}'s caller's {@code
141 * StackTraceElement}.
crazyboblee66b415a2006-08-25 02:01:19 +0000142 */
crazyboblee7c5b2c42007-01-20 02:05:20 +0000143 protected Object source() {
144 // Search up the stack until we find a class outside of this one.
crazyboblee4727ee22007-01-30 03:13:38 +0000145 for (final StackTraceElement element : new Throwable().getStackTrace()) {
146 String className = element.getClassName();
147 if (!className.equals(ContainerBuilder.class.getName())
148 && !className.equals(AbstractModule.class.getName()))
149 return element.getFileName() + ":"
150 + element.getLineNumber() + ":";
crazyboblee7c5b2c42007-01-20 02:05:20 +0000151 }
152 throw new AssertionError();
153 }
154
155 /**
crazybobleedac49912007-01-29 23:17:41 +0000156 * Maps a {@link Scope} instance to a given scope name. Scopes should be
157 * mapped before used in bindings. @{@link Scoped#value()} references this
158 * name.
crazyboblee63b592b2007-01-25 02:45:24 +0000159 */
160 public void put(String name, Scope scope) {
161 if (scopes.containsKey(nonNull(name, "name"))) {
162 add(new ErrorMessage(source(), "Scope named '" + name
163 + "' is already defined."));
164 } else {
165 scopes.put(nonNull(name, "name"), nonNull(scope, "scope"));
166 }
167 }
168
169 /**
crazyboblee7c5b2c42007-01-20 02:05:20 +0000170 * Binds the given key.
171 */
172 public <T> BindingBuilder<T> bind(Key<T> key) {
crazyboblee66b415a2006-08-25 02:01:19 +0000173 ensureNotCreated();
crazyboblee7c5b2c42007-01-20 02:05:20 +0000174 BindingBuilder<T> builder = new BindingBuilder<T>(key).from(source());
175 bindingBuilders.add(builder);
176 return builder;
crazyboblee66b415a2006-08-25 02:01:19 +0000177 }
178
179 /**
crazyboblee7c5b2c42007-01-20 02:05:20 +0000180 * Binds the given type.
crazyboblee66b415a2006-08-25 02:01:19 +0000181 */
crazyboblee7c5b2c42007-01-20 02:05:20 +0000182 public <T> BindingBuilder<T> bind(TypeToken<T> typeToken) {
183 return bind(Key.get(typeToken));
crazyboblee66b415a2006-08-25 02:01:19 +0000184 }
185
186 /**
crazyboblee7c5b2c42007-01-20 02:05:20 +0000187 * Binds the given type.
crazyboblee66b415a2006-08-25 02:01:19 +0000188 */
crazyboblee7c5b2c42007-01-20 02:05:20 +0000189 public <T> BindingBuilder<T> bind(Class<T> clazz) {
190 return bind(Key.get(clazz));
crazyboblee66b415a2006-08-25 02:01:19 +0000191 }
192
193 /**
crazyboblee7c5b2c42007-01-20 02:05:20 +0000194 * Links the given key to another key effectively creating an alias for a
195 * binding.
crazyboblee66b415a2006-08-25 02:01:19 +0000196 */
crazyboblee7c5b2c42007-01-20 02:05:20 +0000197 public <T> LinkedBindingBuilder<T> link(Key<T> key) {
198 ensureNotCreated();
199 LinkedBindingBuilder<T> builder =
200 new LinkedBindingBuilder<T>(key).from(source());
201 linkedBindingBuilders.add(builder);
202 return builder;
crazyboblee66b415a2006-08-25 02:01:19 +0000203 }
204
205 /**
crazyboblee7c5b2c42007-01-20 02:05:20 +0000206 * Binds a constant to the given name.
crazyboblee66b415a2006-08-25 02:01:19 +0000207 */
crazyboblee7c5b2c42007-01-20 02:05:20 +0000208 public ConstantBindingBuilder bind(String name) {
209 ensureNotCreated();
210 return bind(name, source());
crazyboblee66b415a2006-08-25 02:01:19 +0000211 }
212
213 /**
crazyboblee7c5b2c42007-01-20 02:05:20 +0000214 * Binds a constant to the given name from the given source.
crazyboblee66b415a2006-08-25 02:01:19 +0000215 */
crazyboblee7c5b2c42007-01-20 02:05:20 +0000216 private ConstantBindingBuilder bind(String name, Object source) {
217 ConstantBindingBuilder builder =
218 new ConstantBindingBuilder(Objects.nonNull(name, "name")).from(source);
219 constantBindingBuilders.add(builder);
220 return builder;
crazyboblee66b415a2006-08-25 02:01:19 +0000221 }
222
223 /**
crazyboblee63b592b2007-01-25 02:45:24 +0000224 * Binds a string constant for each property.
crazyboblee66b415a2006-08-25 02:01:19 +0000225 */
crazyboblee041e9332007-01-29 22:58:35 +0000226 public void bindProperties(Map<String, String> properties) {
crazyboblee7c5b2c42007-01-20 02:05:20 +0000227 ensureNotCreated();
228 Object source = source();
crazyboblee07e41822006-11-21 01:27:08 +0000229 for (Map.Entry<String, String> entry : properties.entrySet()) {
crazyboblee7c5b2c42007-01-20 02:05:20 +0000230 String key = entry.getKey();
231 String value = entry.getValue();
232 bind(key, source).to(value);
crazyboblee07e41822006-11-21 01:27:08 +0000233 }
crazyboblee07e41822006-11-21 01:27:08 +0000234 }
235
236 /**
crazyboblee63b592b2007-01-25 02:45:24 +0000237 * Binds a string constant for each property.
crazyboblee07e41822006-11-21 01:27:08 +0000238 */
crazyboblee041e9332007-01-29 22:58:35 +0000239 public void bindProperties(Properties properties) {
donald.brown263c5bc2006-11-16 18:39:55 +0000240 ensureNotCreated();
crazyboblee7c5b2c42007-01-20 02:05:20 +0000241 Object source = source();
242 for (Map.Entry<Object, Object> entry : properties.entrySet()) {
243 String key = (String) entry.getKey();
244 String value = (String) entry.getValue();
245 bind(key, source).to(value);
donald.brown263c5bc2006-11-16 18:39:55 +0000246 }
donald.brown263c5bc2006-11-16 18:39:55 +0000247 }
248
249 /**
crazyboblee4776db72007-01-29 23:27:30 +0000250 * Upon successful creation, the {@link Container} will inject static fields
251 * and methods in the given classes.
crazyboblee66b415a2006-08-25 02:01:19 +0000252 *
253 * @param types for which static members will be injected
254 */
crazyboblee4776db72007-01-29 23:27:30 +0000255 public void requestStaticInjection(Class<?>... types) {
crazyboblee66b415a2006-08-25 02:01:19 +0000256 staticInjections.addAll(Arrays.asList(types));
crazyboblee041e9332007-01-29 22:58:35 +0000257 }
258
259 /**
260 * Applies the given module to this builder.
261 */
262 public void apply(Module module) {
263 module.configure(this);
crazyboblee66b415a2006-08-25 02:01:19 +0000264 }
265
266 /**
crazyboblee7c5b2c42007-01-20 02:05:20 +0000267 * Adds an error message to be reported at creation time.
crazyboblee66b415a2006-08-25 02:01:19 +0000268 */
crazyboblee7c5b2c42007-01-20 02:05:20 +0000269 void add(ErrorMessage errorMessage) {
270 errorMessages.add(errorMessage);
crazyboblee66b415a2006-08-25 02:01:19 +0000271 }
272
273 /**
274 * Creates a {@link Container} instance. Injects static members for classes
crazyboblee4776db72007-01-29 23:27:30 +0000275 * which were registered using {@link #requestStaticInjection(Class...)}.
crazyboblee66b415a2006-08-25 02:01:19 +0000276 *
277 * @param loadSingletons If true, the container will load all singletons
278 * now. If false, the container will lazily load singletons. Eager loading
279 * is appropriate for production use while lazy loading can speed
280 * development.
281 * @throws IllegalStateException if called more than once
282 */
283 public Container create(boolean loadSingletons) {
284 ensureNotCreated();
285 created = true;
crazyboblee7c5b2c42007-01-20 02:05:20 +0000286
287 HashMap<Key<?>, InternalFactory<?>> factories =
288 new HashMap<Key<?>, InternalFactory<?>>();
crazyboblee63b592b2007-01-25 02:45:24 +0000289 ContainerImpl container = new ContainerImpl(factories);
crazyboblee7c5b2c42007-01-20 02:05:20 +0000290
291 for (ConstantBindingBuilder builder : constantBindingBuilders) {
292 if (builder.hasValue()) {
crazybobleefc9337f2007-01-26 00:51:34 +0000293 Key<?> key = builder.getKey();
294 InternalFactory<?> factory = builder.getInternalFactory();
295 factories.put(key, factory);
crazyboblee7c5b2c42007-01-20 02:05:20 +0000296 } else {
297 add(new ErrorMessage(builder.getSource(),
298 "Constant value isn't set."));
299 }
300 }
301
302 final List<ContainerImpl.ContextualCallable<Void>> singletonLoaders =
303 new ArrayList<ContainerImpl.ContextualCallable<Void>>();
304
305 for (BindingBuilder<?> builder : bindingBuilders) {
306 final Key<?> key = builder.getKey();
crazyboblee63b592b2007-01-25 02:45:24 +0000307 final InternalFactory<?> factory = builder.getInternalFactory(container);
crazyboblee7c5b2c42007-01-20 02:05:20 +0000308 factories.put(key, factory);
309
310 if (builder.isSingleton()) {
311 singletonLoaders.add(new ContainerImpl.ContextualCallable<Void>() {
312 public Void call(InternalContext context) {
313 context.setExternalContext(
314 ExternalContext.newInstance(null, key,
315 context.getContainerImpl()));
316 try {
317 factory.get(context);
318 return null;
319 } finally {
320 context.setExternalContext(null);
321 }
322 }
323 });
324 }
325 }
326
327 for (LinkedBindingBuilder<?> builder : linkedBindingBuilders) {
328 // TODO: Support alias to a later-declared alias.
329 Key<?> destination = builder.getDestination();
330 if (destination == null) {
331 add(new ErrorMessage(builder.getSource(),
332 "Link destination isn't set."));
333 continue;
334 }
335
336 InternalFactory<?> factory = factories.get(destination);
337 if (factory == null) {
338 add(new ErrorMessage(builder.getSource(),
339 "Destination of link binding not found: " + destination));
340 continue;
341 }
342
343 factories.put(builder.getKey(), factory);
344 }
345
crazyboblee4727ee22007-01-30 03:13:38 +0000346 // Run validations.
347 for (Validation validation : validations) {
348 validation.run(container);
349 }
350
crazyboblee7c5b2c42007-01-20 02:05:20 +0000351 if (!errorMessages.isEmpty()) {
crazyboblee4727ee22007-01-30 03:13:38 +0000352 StringBuilder error = new StringBuilder();
353 error.append("Configuration errors:\n");
crazyboblee7c5b2c42007-01-20 02:05:20 +0000354 for (ErrorMessage errorMessage : errorMessages) {
crazyboblee4727ee22007-01-30 03:13:38 +0000355 error.append(errorMessage).append('\n');
crazyboblee7c5b2c42007-01-20 02:05:20 +0000356 }
crazyboblee4727ee22007-01-30 03:13:38 +0000357 logger.severe(error.toString());
358 throw new ConfigurationException("Encountered configuration errors."
crazyboblee63b592b2007-01-25 02:45:24 +0000359 + " See the log for details.");
crazyboblee7c5b2c42007-01-20 02:05:20 +0000360 }
crazyboblee4727ee22007-01-30 03:13:38 +0000361 container.setErrorHandler(new RuntimeErrorHandler());
crazyboblee7c5b2c42007-01-20 02:05:20 +0000362
crazyboblee7c5b2c42007-01-20 02:05:20 +0000363 container.injectStatics(staticInjections);
364
crazyboblee66b415a2006-08-25 02:01:19 +0000365 if (loadSingletons) {
366 container.callInContext(new ContainerImpl.ContextualCallable<Void>() {
367 public Void call(InternalContext context) {
crazyboblee7c5b2c42007-01-20 02:05:20 +0000368 for (ContainerImpl.ContextualCallable<Void> singletonLoader
369 : singletonLoaders) {
370 singletonLoader.call(context);
crazyboblee66b415a2006-08-25 02:01:19 +0000371 }
372 return null;
373 }
374 });
375 }
crazyboblee7c5b2c42007-01-20 02:05:20 +0000376
crazyboblee66b415a2006-08-25 02:01:19 +0000377 return container;
378 }
379
380 /**
381 * Currently we only support creating one Container instance per builder.
382 * If we want to support creating more than one container per builder,
383 * we should move to a "factory factory" model where we create a factory
384 * instance per Container. Right now, one factory instance would be
385 * shared across all the containers, singletons synchronize on the
386 * container when lazy loading, etc.
387 */
388 private void ensureNotCreated() {
389 if (created) {
390 throw new IllegalStateException("Container already created.");
391 }
392 }
393
394 /**
crazyboblee7c5b2c42007-01-20 02:05:20 +0000395 * Binds a {@link Key} to an implementation in a given scope.
396 */
crazyboblee63b592b2007-01-25 02:45:24 +0000397 public class BindingBuilder<T> {
crazyboblee7c5b2c42007-01-20 02:05:20 +0000398
399 Object source = ContainerBuilder.UNKNOWN_SOURCE;
400 Key<T> key;
401 InternalFactory<? extends T> factory;
402 Scope scope;
403
404 BindingBuilder(Key<T> key) {
405 this.key = nonNull(key, "key");
406 }
407
408 Key<T> getKey() {
409 return key;
410 }
411
crazyboblee63b592b2007-01-25 02:45:24 +0000412 BindingBuilder<T> from(Object source) {
crazyboblee7c5b2c42007-01-20 02:05:20 +0000413 this.source = source;
414 return this;
415 }
416
417 /**
418 * Sets the name of this binding.
419 */
420 public BindingBuilder<T> named(String name) {
421 if (!this.key.hasDefaultName()) {
422 add(new ErrorMessage(source, "Name set more than once."));
423 }
424
425 this.key = this.key.named(name);
426 return this;
427 }
428
429 /**
430 * Binds to instances of the given implementation class. The {@link
431 * Container} will inject the implementation instances as well. Sets the
432 * scope based on the @{@link Scoped} annotation on the implementation
433 * class if present.
434 */
435 public <I extends T> BindingBuilder<T> to(Class<I> implementation) {
436 return to(TypeToken.get(implementation));
437 }
438
439 /**
440 * Binds to instances of the given implementation type. The {@link
441 * Container} will inject the implementation instances as well. Sets the
442 * scope based on the @{@link Scoped} annotation on the implementation
443 * class if present.
444 */
445 public <I extends T> BindingBuilder<T> to(TypeToken<I> implementation) {
446 ensureImplementationIsNotSet();
crazyboblee4727ee22007-01-30 03:13:38 +0000447 validate(source, implementation.getRawType());
crazyboblee7c5b2c42007-01-20 02:05:20 +0000448 this.factory = new DefaultFactory<I>(implementation);
449 setScopeFromType(implementation.getRawType());
450 return this;
451 }
452
453 private void setScopeFromType(Class<?> implementation) {
454 Scoped scoped = implementation.getAnnotation(Scoped.class);
455 if (scoped != null) {
456 in(scoped.value());
457 }
458 }
459
460 /**
461 * Binds to instances from the given factory.
462 */
463 public BindingBuilder<T> to(
464 final ContextualFactory<? extends T> factory) {
465 ensureImplementationIsNotSet();
466
467 this.factory = new InternalFactory<T>() {
468 public T get(InternalContext context) {
469 return factory.get(context.getExternalContext());
470 }
471
472 public String toString() {
473 return factory.toString();
474 }
475 };
476
477 return this;
478 }
479
480 /**
481 * Binds to instances from the given factory.
482 */
483 public BindingBuilder<T> to(final Factory<? extends T> factory) {
484 ensureImplementationIsNotSet();
485
486 this.factory = new InternalFactory<T>() {
487 public T get(InternalContext context) {
488 return factory.get();
489 }
490
491 public String toString() {
492 return factory.toString();
493 }
494 };
495
496 return this;
497 }
498
499 /**
500 * Binds to the given instance.
501 */
502 BindingBuilder<T> to(T instance) {
503 ensureImplementationIsNotSet();
504 this.factory = new ConstantFactory<T>(instance);
505 return this;
506 }
507
508 /**
509 * Binds to instances from the given factory.
510 */
511 BindingBuilder<T> to(final InternalFactory<? extends T> factory) {
512 ensureImplementationIsNotSet();
513 this.factory = factory;
514 return this;
515 }
516
517 /**
518 * Adds an error message if the implementation has already been bound.
519 */
520 private void ensureImplementationIsNotSet() {
521 if (factory != null) {
522 add(new ErrorMessage(source, "Implementation set more than once."));
523 }
524 }
525
526 /**
crazyboblee63b592b2007-01-25 02:45:24 +0000527 * Specifies the scope. References the name passed to {@link
528 * ContainerBuilder#put(String, Scope)}.
crazyboblee7c5b2c42007-01-20 02:05:20 +0000529 */
crazyboblee63b592b2007-01-25 02:45:24 +0000530 public BindingBuilder<T> in(String scopeName) {
531 ensureScopeNotSet();
crazyboblee7c5b2c42007-01-20 02:05:20 +0000532
crazyboblee63b592b2007-01-25 02:45:24 +0000533 // We could defer this lookup to when we create the container, but this
534 // is fine for now.
535 this.scope = scopes.get(scopeName);
536 if (this.scope == null) {
537 add(new ErrorMessage(source, "Scope named '" + scopeName
538 + "' not found."));
539 }
crazyboblee7c5b2c42007-01-20 02:05:20 +0000540 return this;
541 }
542
crazyboblee63b592b2007-01-25 02:45:24 +0000543 /**
544 * Specifies the scope.
545 */
546 public BindingBuilder<T> in(Scope scope) {
547 ensureScopeNotSet();
548
549 this.scope = nonNull(scope, "scope");
550 return this;
551 }
552
553 private void ensureScopeNotSet() {
554 if (this.scope != null) {
555 add(new ErrorMessage(source, "Scope set more than once."));
556 }
557 }
558
559 InternalFactory<? extends T> getInternalFactory(
560 final ContainerImpl container) {
crazyboblee7c5b2c42007-01-20 02:05:20 +0000561 // If an implementation wasn't specified, use the injection type.
562 if (this.factory == null) {
563 to(key.getTypeToken());
564 }
565
crazyboblee63b592b2007-01-25 02:45:24 +0000566 if (scope == null || scope == DEFAULT_SCOPE) {
crazyboblee7c5b2c42007-01-20 02:05:20 +0000567 return this.factory;
568 }
569
crazyboblee63b592b2007-01-25 02:45:24 +0000570 // TODO: This is a little hairy.
571 final InternalFactory<? extends T> internalFactory = this.factory;
572 final Factory<T> factory = scope.scope(this.key, new Factory<T>() {
573 public T get() {
574 return container.callInContext(
575 new ContainerImpl.ContextualCallable<T>() {
576 public T call(InternalContext context) {
577 return internalFactory.get(context);
578 }
579 });
580 }
581
582 public String toString() {
583 return internalFactory.toString();
584 }
585 });
586
587 return new InternalFactory<T>() {
588 public T get(InternalContext context) {
589 return factory.get();
590 }
591
592 public String toString() {
593 return factory.toString();
594 }
595 };
crazyboblee7c5b2c42007-01-20 02:05:20 +0000596 }
597
598 boolean isSingleton() {
crazyboblee63b592b2007-01-25 02:45:24 +0000599 return this.scope == SingletonScope.INSTANCE;
crazyboblee7c5b2c42007-01-20 02:05:20 +0000600 }
601
602 /**
603 * Injects new instances of the specified implementation class.
604 */
605 private class DefaultFactory<I extends T> implements InternalFactory<I> {
606
607 volatile ContainerImpl.ConstructorInjector<I> constructor;
608
609 private final TypeToken<I> implementation;
610
611 public DefaultFactory(TypeToken<I> implementation) {
612 // TODO: Ensure this is a concrete implementation.
613 this.implementation = implementation;
614 }
615
616 @SuppressWarnings("unchecked")
617 public I get(InternalContext context) {
618 if (constructor == null) {
crazyboblee4727ee22007-01-30 03:13:38 +0000619 // This unnecessary cast is a workaround for an annoying compiler
620 // bug I keep running into.
621 Object c = context.getContainerImpl().getConstructor(implementation);
622 this.constructor = (ContainerImpl.ConstructorInjector<I>) c;
crazyboblee7c5b2c42007-01-20 02:05:20 +0000623 }
624 return (I) constructor.construct(context, key.getRawType());
625 }
626
627 public String toString() {
628 return implementation.toString();
629 }
630 }
631 }
632
633 /**
634 * Builds a constant binding.
635 */
crazyboblee63b592b2007-01-25 02:45:24 +0000636 public class ConstantBindingBuilder {
crazyboblee7c5b2c42007-01-20 02:05:20 +0000637
638 final String name;
639 Class<?> type;
640 Object value;
641 Object source = ContainerBuilder.UNKNOWN_SOURCE;
642
643 ConstantBindingBuilder(String name) {
644 this.name = name;
645 }
646
647 Key<?> getKey() {
648 return Key.get(type, name);
649 }
650
651 boolean hasValue() {
652 return type != null;
653 }
654
655 Object getSource() {
656 return source;
657 }
658
659 InternalFactory<?> getInternalFactory() {
660 return new ConstantFactory<Object>(value);
661 }
662
crazyboblee63b592b2007-01-25 02:45:24 +0000663 ConstantBindingBuilder from(Object source) {
crazyboblee7c5b2c42007-01-20 02:05:20 +0000664 this.source = source;
665 return this;
666 }
667
668 /**
669 * Binds constant to the given value.
670 */
crazyboblee78e1cc12007-01-26 02:18:06 +0000671 public void to(String value) {
672 to(String.class, value);
crazyboblee7c5b2c42007-01-20 02:05:20 +0000673 }
674
675 /**
676 * Binds constant to the given value.
677 */
crazyboblee78e1cc12007-01-26 02:18:06 +0000678 public void to(int value) {
679 to(int.class, value);
crazyboblee7c5b2c42007-01-20 02:05:20 +0000680 }
681
682 /**
683 * Binds constant to the given value.
684 */
crazyboblee78e1cc12007-01-26 02:18:06 +0000685 public void to(long value) {
686 to(long.class, value);
crazyboblee7c5b2c42007-01-20 02:05:20 +0000687 }
688
689 /**
690 * Binds constant to the given value.
691 */
crazyboblee78e1cc12007-01-26 02:18:06 +0000692 public void to(boolean value) {
693 to(boolean.class, value);
crazyboblee7c5b2c42007-01-20 02:05:20 +0000694 }
695
696 /**
697 * Binds constant to the given value.
698 */
crazyboblee78e1cc12007-01-26 02:18:06 +0000699 public void to(double value) {
700 to(double.class, value);
crazyboblee7c5b2c42007-01-20 02:05:20 +0000701 }
702
703 /**
704 * Binds constant to the given value.
705 */
crazyboblee78e1cc12007-01-26 02:18:06 +0000706 public void to(float value) {
707 to(float.class, value);
crazyboblee7c5b2c42007-01-20 02:05:20 +0000708 }
709
710 /**
711 * Binds constant to the given value.
712 */
crazyboblee78e1cc12007-01-26 02:18:06 +0000713 public void to(short value) {
714 to(short.class, value);
crazyboblee7c5b2c42007-01-20 02:05:20 +0000715 }
716
717 /**
718 * Binds constant to the given value.
719 */
crazyboblee78e1cc12007-01-26 02:18:06 +0000720 public void to(char value) {
721 to(char.class, value);
crazyboblee7c5b2c42007-01-20 02:05:20 +0000722 }
723
724 /**
725 * Binds constant to the given value.
726 */
crazyboblee78e1cc12007-01-26 02:18:06 +0000727 public void to(Class<?> value) {
728 to(Class.class, value);
crazyboblee7c5b2c42007-01-20 02:05:20 +0000729 }
730
731 /**
732 * Binds constant to the given value.
733 */
crazyboblee78e1cc12007-01-26 02:18:06 +0000734 public <E extends Enum<E>> void to(E value) {
735 to(value.getDeclaringClass(), value);
crazyboblee7c5b2c42007-01-20 02:05:20 +0000736 }
737
738 /**
739 * Maps a constant value to the given type and name.
740 */
crazyboblee78e1cc12007-01-26 02:18:06 +0000741 <T> void to(final Class<T> type, final T value) {
crazyboblee7c5b2c42007-01-20 02:05:20 +0000742 this.type = type;
743 this.value = value;
crazyboblee7c5b2c42007-01-20 02:05:20 +0000744 }
745 }
746
747 /**
748 * Links one binding to another.
749 */
crazyboblee63b592b2007-01-25 02:45:24 +0000750 public class LinkedBindingBuilder<T> {
crazyboblee7c5b2c42007-01-20 02:05:20 +0000751
752 Key<T> key;
753 Key<? extends T> destination;
754 Object source = ContainerBuilder.UNKNOWN_SOURCE;
755
756 LinkedBindingBuilder(Key<T> key) {
757 this.key = key;
758 }
759
760 Object getSource() {
761 return source;
762 }
763
764 Key<T> getKey() {
765 return key;
766 }
767
768 Key<? extends T> getDestination() {
769 return destination;
770 }
771
crazyboblee63b592b2007-01-25 02:45:24 +0000772 LinkedBindingBuilder<T> from(Object source) {
crazyboblee7c5b2c42007-01-20 02:05:20 +0000773 this.source = source;
774 return this;
775 }
776
777 /**
778 * Links to another binding with the given key.
779 */
crazyboblee78e1cc12007-01-26 02:18:06 +0000780 public void to(Key<? extends T> destination) {
crazyboblee7c5b2c42007-01-20 02:05:20 +0000781 this.destination = destination;
crazyboblee7c5b2c42007-01-20 02:05:20 +0000782 }
783 }
crazyboblee4727ee22007-01-30 03:13:38 +0000784
785 class ConfigurationErrorHandler implements ErrorHandler {
786
787 final Object source;
788
789 ConfigurationErrorHandler(Object source) {
790 this.source = source;
791 }
792
793 public void handle(String message) {
794 add(new ErrorMessage(source, message));
795 }
796
797 public void handle(Throwable t) {
798 add(new ErrorMessage(source, t.getMessage()));
799 }
800 }
801
802 class RuntimeErrorHandler implements ErrorHandler {
803
804 public void handle(String message) {
805 throw new ConfigurationException(message);
806 }
807
808 public void handle(Throwable t) {
809 throw new ConfigurationException(t);
810 }
811 }
crazyboblee66b415a2006-08-25 02:01:19 +0000812}