blob: 9adb02583dbfc47f73504e646d6b5f92297d6bf7 [file] [log] [blame]
/**
* Copyright (C) 2008 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.commands;
import static com.google.common.base.Preconditions.checkNotNull;
import com.google.inject.Binder;
import com.google.inject.Key;
import com.google.inject.Provider;
import com.google.inject.Scope;
import com.google.inject.TypeLiteral;
import com.google.inject.binder.AnnotatedBindingBuilder;
import com.google.inject.binder.ConstantBindingBuilder;
import com.google.inject.binder.LinkedBindingBuilder;
import com.google.inject.binder.ScopedBindingBuilder;
import com.google.inject.internal.ErrorMessage;
import com.google.inject.spi.SourceProviders;
import java.lang.annotation.Annotation;
/**
* Immutable snapshot of a request to bind a value.
*
* @author jessewilson@google.com (Jesse Wilson)
*/
public final class BindCommand<T> implements Command {
static {
SourceProviders.skip(BindCommand.BindingBuilder.class);
}
private static final BindTarget<Object> EMPTY_BIND_TARGET = new AbstractTarget<Object>() {
public ScopedBindingBuilder execute(LinkedBindingBuilder<Object> linkedBindingBuilder) {
return linkedBindingBuilder;
}
public <V> V acceptVisitor(Visitor<Object, V> visitor) {
return visitor.visitUntargetted();
}
};
private static final BindScoping EMPTY_SCOPING = new AbstractScoping() {
public void execute(ScopedBindingBuilder scopedBindingBuilder) {
// do nothing
}
public <V> V acceptVisitor(Visitor<V> visitor) {
return visitor.visitNoScoping();
}
};
private final Object source;
private Key<T> key;
@SuppressWarnings("unchecked")
private BindTarget<T> bindTarget = (BindTarget<T>) EMPTY_BIND_TARGET;
private BindScoping bindScoping = EMPTY_SCOPING;
BindCommand(Object source, Key<T> key) {
this.source = checkNotNull(source, "source");
this.key = checkNotNull(key, "key");
}
public Object getSource() {
return source;
}
public <V> V acceptVisitor(Visitor<V> visitor) {
return visitor.visitBind(this);
}
public Key<T> getKey() {
return key;
}
public BindTarget<T> getTarget() {
return bindTarget;
}
public BindScoping getScoping() {
return bindScoping;
}
@Override public String toString() {
return "bind " + key
+ (bindTarget == EMPTY_BIND_TARGET ? "" : (" to " + bindTarget))
+ (bindScoping == EMPTY_SCOPING ? "" : (" in " + bindScoping));
}
private static abstract class AbstractTarget<T> implements BindTarget<T> {
public void execute(ConstantBindingBuilder builder) {
throw new UnsupportedOperationException();
}
public T get() {
return null;
}
public Key<? extends Provider<? extends T>> getProviderKey() {
return null;
}
public Provider<? extends T> getProvider() {
return null;
}
public Key<? extends T> getKey() {
return null;
}
}
private static abstract class AbstractScoping implements BindScoping {
public boolean isEagerSingleton() {
return false;
}
public Scope getScope() {
return null;
}
public Class<? extends Annotation> getScopeAnnotation() {
return null;
}
}
BindingBuilder bindingBuilder(Binder binder) {
return new BindingBuilder(binder);
}
/**
* Package-private write access to the internal state of this command.
*/
class BindingBuilder implements AnnotatedBindingBuilder<T> {
private final Binder binder;
BindingBuilder(Binder binder) {
this.binder = binder;
}
public LinkedBindingBuilder<T> annotatedWith(
Class<? extends Annotation> annotationType) {
checkNotNull(annotationType, "annotationType");
checkNotAnnotated();
key = Key.get(key.getTypeLiteral(), annotationType);
return this;
}
public LinkedBindingBuilder<T> annotatedWith(Annotation annotation) {
checkNotNull(annotation, "annotation");
checkNotAnnotated();
key = Key.get(key.getTypeLiteral(), annotation);
return this;
}
public ScopedBindingBuilder to(final Class<? extends T> implementation) {
return to(Key.get(implementation));
}
public ScopedBindingBuilder to(
final TypeLiteral<? extends T> implementation) {
return to(Key.get(implementation));
}
public ScopedBindingBuilder to(final Key<? extends T> targetKey) {
checkNotNull(targetKey, "targetKey");
checkNotTargetted();
bindTarget = new AbstractTarget<T>() {
public ScopedBindingBuilder execute(LinkedBindingBuilder<T> linkedBindingBuilder) {
return linkedBindingBuilder.to(targetKey);
}
@Override public Key<? extends T> getKey() {
return targetKey;
}
public <V> V acceptVisitor(Visitor<T, V> visitor) {
return visitor.visitToKey(targetKey);
}
@Override public String toString() {
return String.valueOf(targetKey);
}
};
return this;
}
public void toInstance(final T instance) {
checkNotNull(instance, ErrorMessage.cannotBindToNullInstance().toString());
checkNotTargetted();
bindTarget = new AbstractTarget<T>() {
public ScopedBindingBuilder execute(LinkedBindingBuilder<T> linkedBindingBuilder) {
linkedBindingBuilder.toInstance(instance);
return null;
}
@Override public T get() {
return instance;
}
public <V> V acceptVisitor(Visitor<T, V> visitor) {
return visitor.visitToInstance(instance);
}
@Override public String toString() {
return "instance " + instance;
}
};
}
public ScopedBindingBuilder toProvider(final Provider<? extends T> provider) {
checkNotNull(provider, "provider");
checkNotTargetted();
bindTarget = new AbstractTarget<T>() {
public ScopedBindingBuilder execute(LinkedBindingBuilder<T> linkedBindingBuilder) {
return linkedBindingBuilder.toProvider(provider);
}
@Override public Provider<? extends T> getProvider() {
return provider;
}
public <V> V acceptVisitor(Visitor<T, V> visitor) {
return visitor.visitToProvider(provider);
}
@Override public String toString() {
return "provider " + provider;
}
};
return this;
}
public ScopedBindingBuilder toProvider(
Class<? extends Provider<? extends T>> providerType) {
return toProvider(Key.get(providerType));
}
public ScopedBindingBuilder toProvider(
final Key<? extends Provider<? extends T>> providerKey) {
checkNotNull(providerKey, "providerKey");
checkNotTargetted();
bindTarget = new AbstractTarget<T>() {
public ScopedBindingBuilder execute(LinkedBindingBuilder<T> linkedBindingBuilder) {
return linkedBindingBuilder.toProvider(providerKey);
}
@Override public Key<? extends Provider<? extends T>> getProviderKey() {
return providerKey;
}
public <V> V acceptVisitor(Visitor<T, V> visitor) {
return visitor.visitToProviderKey(providerKey);
}
@Override public String toString() {
return "provider " + providerKey;
}
};
return this;
}
public void in(final Class<? extends Annotation> scopeAnnotation) {
checkNotNull(scopeAnnotation, "scopeAnnotation");
checkNotScoped();
bindScoping = new AbstractScoping() {
public void execute(ScopedBindingBuilder scopedBindingBuilder) {
scopedBindingBuilder.in(scopeAnnotation);
}
@Override public Class<? extends Annotation> getScopeAnnotation() {
return scopeAnnotation;
}
public <V> V acceptVisitor(Visitor<V> visitor) {
return visitor.visitScopeAnnotation(scopeAnnotation);
}
@Override public String toString() {
return scopeAnnotation.getName();
}
};
}
public void in(final Scope scope) {
checkNotNull(scope, "scope");
checkNotScoped();
bindScoping = new AbstractScoping() {
public void execute(ScopedBindingBuilder scopedBindingBuilder) {
scopedBindingBuilder.in(scope);
}
@Override public Scope getScope() {
return scope;
}
public <V> V acceptVisitor(Visitor<V> visitor) {
return visitor.visitScope(scope);
}
@Override public String toString() {
return String.valueOf(scope);
}
};
}
public void asEagerSingleton() {
checkNotScoped();
bindScoping = new AbstractScoping() {
public void execute(ScopedBindingBuilder scopedBindingBuilder) {
scopedBindingBuilder.asEagerSingleton();
}
@Override public boolean isEagerSingleton() {
return true;
}
public <V> V acceptVisitor(Visitor<V> visitor) {
return visitor.visitEagerSingleton();
}
@Override public String toString() {
return "eager singleton";
}
};
}
static final String IMPLEMENTATION_ALREADY_SET
= "Implementation is set more than once.";
static final String SINGLE_INSTANCE_AND_SCOPE = "Setting the scope is not"
+ " permitted when binding to a single instance.";
static final String SCOPE_ALREADY_SET = "Scope is set more than once.";
static final String ANNOTATION_ALREADY_SPECIFIED = "More than one annotation"
+ " is specified for this binding.";
private void checkNotTargetted() {
if (bindTarget != EMPTY_BIND_TARGET) {
binder.addError(IMPLEMENTATION_ALREADY_SET);
}
}
private void checkNotAnnotated() {
if (BindCommand.this.key.getAnnotationType() != null) {
binder.addError(ANNOTATION_ALREADY_SPECIFIED);
}
}
private void checkNotScoped() {
// Scoping isn't allowed when we have only one instance.
if (bindTarget.get() != null) {
binder.addError(SINGLE_INSTANCE_AND_SCOPE);
return;
}
if (bindScoping != EMPTY_SCOPING) {
binder.addError(SCOPE_ALREADY_SET);
}
}
@Override public String toString() {
String type = key.getAnnotationType() == null
? "AnnotatedBindingBuilder<"
: "LinkedBindingBuilder<";
return type + key.getTypeLiteral() + ">";
}
}
}