blob: f01af3a189f7d38da75086f646a9c185fbd377c7 [file] [log] [blame]
/*
* Copyright (C) 2007 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.inject;
import com.google.inject.BinderImpl.CreationListener;
import com.google.inject.binder.ScopedBindingBuilder;
import com.google.inject.binder.AnnotatedBindingBuilder;
import com.google.inject.util.Annotations;
import com.google.inject.util.Objects;
import com.google.inject.util.StackTraceElements;
import com.google.inject.util.ToStringBuilder;
import java.lang.annotation.Annotation;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* Binds a {@link com.google.inject.Key} to an implementation in a given scope.
*/
class BindingBuilderImpl<T> implements AnnotatedBindingBuilder<T> {
private static final Logger logger
= Logger.getLogger(BindingBuilderImpl.class.getName());
final Object source;
Key<T> key;
InternalFactory<? extends T> factory;
T instance;
Scope scope;
boolean preload = false;
private BinderImpl binder;
BindingBuilderImpl(BinderImpl binder, Key<T> key, Object source) {
this.binder = binder;
this.key = Objects.nonNull(key, "key");
this.source = source;
}
Object getSource() {
return source;
}
Key<T> getKey() {
return key;
}
public BindingBuilderImpl<T> annotatedWith(
Class<? extends Annotation> annotationType) {
if (this.key.hasAnnotationType()) {
binder.addError(source, ErrorMessages.ANNOTATION_ALREADY_SPECIFIED);
} else {
boolean retainedAtRuntime =
Annotations.isRetainedAtRuntime(annotationType);
boolean bindingAnnotation = Key.isBindingAnnotation(annotationType);
if (!retainedAtRuntime) {
binder.addError(StackTraceElements.forType(annotationType),
ErrorMessages.MISSING_RUNTIME_RETENTION, binder.source());
}
if (!bindingAnnotation) {
binder.addError(StackTraceElements.forType(annotationType),
ErrorMessages.MISSING_BINDING_ANNOTATION, binder.source());
}
if (retainedAtRuntime && bindingAnnotation) {
this.key = Key.get(this.key.getTypeLiteral(), annotationType);
}
}
return this;
}
public BindingBuilderImpl<T> annotatedWith(Annotation annotation) {
if (this.key.hasAnnotationType()) {
binder.addError(source, ErrorMessages.ANNOTATION_ALREADY_SPECIFIED);
} else {
Class<? extends Annotation> annotationType = annotation.annotationType();
boolean retainedAtRuntime =
Annotations.isRetainedAtRuntime(annotationType);
boolean bindingAnnotation = Key.isBindingAnnotation(annotationType);
if (!retainedAtRuntime) {
binder.addError(StackTraceElements.forType(annotationType),
ErrorMessages.MISSING_RUNTIME_RETENTION, binder.source());
}
if (!bindingAnnotation) {
binder.addError(StackTraceElements.forType(annotationType),
ErrorMessages.MISSING_BINDING_ANNOTATION, binder.source());
}
if (retainedAtRuntime && bindingAnnotation) {
this.key = Key.get(this.key.getTypeLiteral(), annotation);
}
}
return this;
}
public ScopedBindingBuilder to(Class<? extends T> implementation) {
return to(TypeLiteral.get(implementation));
}
public ScopedBindingBuilder to(TypeLiteral<? extends T> implementation) {
return to(Key.get(implementation));
}
public ScopedBindingBuilder to(Key<? extends T> targetKey) {
ensureImplementationIsNotSet();
if (key.equals(targetKey)) {
binder.addError(source, ErrorMessages.RECURSIVE_BINDING);
}
final FactoryProxy<? extends T> factoryProxy =
new FactoryProxy<T>(key, targetKey, source);
this.factory = factoryProxy;
binder.creationListeners.add(factoryProxy);
return this;
}
public void toInstance(T instance) {
ensureImplementationIsNotSet();
this.instance = Objects.nonNull(instance, "instance");
this.factory = new ConstantFactory<T>(instance);
registerInstanceForInjection(instance);
if (this.scope != null) {
binder.addError(source, ErrorMessages.SINGLE_INSTANCE_AND_SCOPE);
}
}
/**
* Binds to instances from the given factory.
*/
BindingBuilderImpl<T> to(InternalFactory<? extends T> factory) {
ensureImplementationIsNotSet();
this.factory = factory;
return this;
}
public ScopedBindingBuilder toProvider(Provider<? extends T> provider) {
ensureImplementationIsNotSet();
this.factory = new InternalFactoryToProviderAdapter<T>(provider, source);
registerInstanceForInjection(provider);
return this;
}
public BindingBuilderImpl<T> toProvider(
Class<? extends Provider<? extends T>> providerType) {
return toProvider(Key.get(providerType));
}
public BindingBuilderImpl<T> toProvider(
Key<? extends Provider<? extends T>> providerKey) {
ensureImplementationIsNotSet();
final BoundProviderFactory<T> boundProviderFactory =
new BoundProviderFactory<T>(providerKey, source);
binder.creationListeners.add(boundProviderFactory);
this.factory = boundProviderFactory;
return this;
}
/**
* Adds an error message if the implementation has already been bound.
*/
private void ensureImplementationIsNotSet() {
if (factory != null) {
binder.addError(source, ErrorMessages.IMPLEMENTATION_ALREADY_SET);
}
}
public void in(Class<? extends Annotation> scopeAnnotation) {
// this method not test-covered
ensureScopeNotSet();
// We could defer this lookup to when we create the Injector, but this
// is fine for now.
this.scope = binder.scopes.get(
Objects.nonNull(scopeAnnotation, "scope annotation"));
if (this.scope == null) {
binder.addError(source, ErrorMessages.SCOPE_NOT_FOUND,
"@" + scopeAnnotation.getSimpleName());
}
}
public void in(Scope scope) {
ensureScopeNotSet();
this.scope = Objects.nonNull(scope, "scope");
}
private void ensureScopeNotSet() {
// Scoping isn't allowed when we have only one instance.
if (this.instance != null) {
binder.addError(source, ErrorMessages.SINGLE_INSTANCE_AND_SCOPE);
return;
}
if (this.scope != null) {
binder.addError(source, ErrorMessages.SCOPE_ALREADY_SET);
}
}
public void asEagerSingleton() {
in(Scopes.SINGLETON);
this.preload = true;
}
boolean shouldPreload() {
return preload;
}
InternalFactory<? extends T> getInternalFactory(InjectorImpl injector) {
if (this.factory == null && !key.hasAnnotationType()) {
// Try an implicit binding.
final ImplicitImplementation<T> implicitImplementation =
new ImplicitImplementation<T>(key, scope, source);
binder.creationListeners.add(implicitImplementation);
// We need to record the scope. If it's singleton, we'll preload in prod.
if (this.scope == null) {
// We can ignore errors because the error will already have been
// recorded.
this.scope = Scopes.getScopeForType(
key.getTypeLiteral().getRawType(), binder.scopes, IGNORE_ERRORS);
}
return implicitImplementation;
}
return Scopes.scope(this.key, injector, this.factory, scope);
}
boolean isSingletonScoped() {
return this.scope == Scopes.SINGLETON;
}
void registerInstanceForInjection(final Object o) {
binder.instanceInjectors.add(new CreationListener() {
public void notify(InjectorImpl injector) {
try {
injector.injectMembers(o);
}
catch (Exception e) {
String className = e.getClass().getSimpleName();
String message = ErrorMessages.getRootMessage(e);
String logMessage = String.format(
ErrorMessages.ERROR_INJECTING_MEMBERS, o, message);
logger.log(Level.INFO, logMessage, e);
binder.addError(source, ErrorMessages.ERROR_INJECTING_MEMBERS_SEE_LOG,
className, o, message);
}
}
});
}
/**
* A placeholder which enables us to swap in the real factory once the
* container is created.
*/
private static class FactoryProxy<T> implements InternalFactory<T>,
CreationListener {
private final Key<T> key;
private final Key<? extends T> targetKey;
private final Object source;
InternalFactory<? extends T> targetFactory;
FactoryProxy(Key<T> key, Key<? extends T> targetKey, Object source) {
this.key = key;
this.targetKey = targetKey;
this.source = source;
}
public void notify(final InjectorImpl injector) {
injector.withDefaultSource(source, new Runnable() {
public void run() {
targetFactory = injector.getInternalFactory(null, targetKey);
}
});
}
public T get(InternalContext context) {
return targetFactory.get(context);
}
public String toString() {
return new ToStringBuilder(FactoryProxy.class)
.add("key", key)
.add("provider", targetFactory)
.toString();
}
}
private static class ImplicitImplementation<T> implements InternalFactory<T>,
CreationListener {
private final Key<T> key;
private final Object source;
private final Scope scope;
InternalFactory<? extends T> implicitFactory;
ImplicitImplementation(Key<T> key, Scope scope, Object source) {
this.key = key;
this.scope = scope;
this.source = source;
}
public void notify(final InjectorImpl injector) {
injector.withDefaultSource(source, new Runnable() {
public void run() {
implicitFactory = injector.getImplicitBinding(null,
(Class) key.getTypeLiteral().getRawType(), scope);
}
});
}
public T get(InternalContext context) {
return implicitFactory.get(context);
}
public String toString() {
return new ToStringBuilder(FactoryProxy.class)
.add("key", key)
.add("provider", implicitFactory)
.toString();
}
}
static ErrorHandler IGNORE_ERRORS = new ErrorHandler() {
public void handle(Object source, String message) {}
public void handle(Object source, String message, Object... arguments) {}
};
}