| /** |
| * 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() + ">"; |
| } |
| } |
| } |