| /** |
| * 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.internal; |
| |
| import static com.google.common.base.Preconditions.checkNotNull; |
| import com.google.common.collect.ImmutableSet; |
| import com.google.inject.Binder; |
| import com.google.inject.Binding; |
| import com.google.inject.ConfigurationException; |
| 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.AnnotatedConstantBindingBuilder; |
| import com.google.inject.binder.ConstantBindingBuilder; |
| import com.google.inject.binder.LinkedBindingBuilder; |
| import com.google.inject.binder.ScopedBindingBuilder; |
| import com.google.inject.spi.BindingScopingVisitor; |
| import com.google.inject.spi.BindingTargetVisitor; |
| import com.google.inject.spi.DefaultBindingTargetVisitor; |
| import com.google.inject.spi.ElementVisitor; |
| import com.google.inject.spi.InjectionPoint; |
| import com.google.inject.spi.Message; |
| import com.google.inject.util.Providers; |
| import java.lang.annotation.Annotation; |
| import java.util.Set; |
| |
| /** |
| * Immutable snapshot of a request to bind a value. |
| * |
| * @author jessewilson@google.com (Jesse Wilson) |
| */ |
| public final class ModuleBinding<T> implements Binding<T> { |
| |
| private final Key<?> NULL_KEY = Key.get(Void.class); |
| |
| private static final Target<Object> EMPTY_TARGET = new Target<Object>() { |
| public <V> V acceptTargetVisitor(BindingTargetVisitor<? super Object, V> visitor) { |
| return visitor.visitUntargetted(); |
| } |
| }; |
| |
| private static final Scoping EMPTY_SCOPING = new AbstractScoping() { |
| public <V> V acceptVisitor(BindingScopingVisitor<V> visitor) { |
| return visitor.visitNoScoping(); |
| } |
| }; |
| |
| private static final BindingTargetVisitor<Object, Boolean> SUPPORTS_SCOPES |
| = new DefaultBindingTargetVisitor<Object, Boolean>() { |
| @Override public Boolean visitInstance(Object instance, Set<InjectionPoint> injectionPoints) { |
| return false; |
| } |
| |
| @Override protected Boolean visitOther() { |
| return true; |
| } |
| }; |
| |
| private final Object source; |
| private Key<T> key; |
| |
| @SuppressWarnings("unchecked") |
| private Target<T> target = (Target<T>) EMPTY_TARGET; |
| private Scoping scoping = EMPTY_SCOPING; |
| |
| public ModuleBinding(Object source, Key<T> key) { |
| this.source = checkNotNull(source, "source"); |
| this.key = checkNotNull(key, "key"); |
| } |
| |
| public ModuleBinding(Object source) { |
| @SuppressWarnings("unchecked") // unsafe, but we won't ever return this (Key.get fails) |
| Key<T> NULL_KEY_OF_T = (Key<T>) NULL_KEY; |
| |
| this.source = checkNotNull(source); |
| this.key = NULL_KEY_OF_T; |
| } |
| |
| public Object getSource() { |
| return source; |
| } |
| |
| /** |
| * Returns the scoped provider guice uses to fulfill requests for this |
| * binding. |
| */ |
| public Provider<T> getProvider() { |
| throw new UnsupportedOperationException(); |
| } |
| |
| public <V> V acceptVisitor(ElementVisitor<V> visitor) { |
| return visitor.visitBinding(this); |
| } |
| |
| public Key<T> getKey() { |
| return key; |
| } |
| |
| public <V> V acceptTargetVisitor(BindingTargetVisitor<? super T, V> visitor) { |
| return target.acceptTargetVisitor(visitor); |
| } |
| |
| public <V> V acceptScopingVisitor(BindingScopingVisitor<V> visitor) { |
| return scoping.acceptVisitor(visitor); |
| } |
| |
| private boolean keyTypeIsSet() { |
| return !Void.class.equals(key.getTypeLiteral().getType()); |
| } |
| |
| @Override public String toString() { |
| return "bind " + key |
| + (target == EMPTY_TARGET ? "" : (" to " + target)) |
| + (scoping == EMPTY_SCOPING ? "" : (" in " + scoping)); |
| } |
| |
| private static abstract class AbstractScoping implements Scoping { |
| public Scope getScope() { |
| return null; |
| } |
| public Class<? extends Annotation> getScopeAnnotation() { |
| return null; |
| } |
| } |
| |
| public RegularBuilder regularBuilder(Binder binder) { |
| return new RegularBuilder(binder); |
| } |
| |
| /** |
| * Write access to the internal state of this element. Not for use by the public API. |
| */ |
| public class RegularBuilder implements AnnotatedBindingBuilder<T> { |
| private final Binder binder; |
| |
| RegularBuilder(Binder binder) { |
| this.binder = binder.skipSources(RegularBuilder.class); |
| } |
| |
| 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(); |
| target = new Target<T>() { |
| public <V> V acceptTargetVisitor(BindingTargetVisitor<? super T, V> visitor) { |
| return visitor.visitKey(targetKey); |
| } |
| @Override public String toString() { |
| return String.valueOf(targetKey); |
| } |
| }; |
| return this; |
| } |
| |
| public void toInstance(final T instance) { |
| checkNotTargetted(); |
| |
| if (instance == null) { |
| binder.addError(BINDING_TO_NULL); |
| // we finish the binding to prevent additional errors |
| toProvider(Providers.<T>of(null)); |
| return; |
| } |
| |
| // lookup the injection points, adding any errors to the binder's errors list |
| Set<InjectionPoint> injectionPoints; |
| try { |
| injectionPoints = InjectionPoint.forInstanceMethodsAndFields(instance.getClass()); |
| } catch (ConfigurationException e) { |
| for (Message message : e.getErrorMessages()) { |
| binder.addError(message); |
| } |
| injectionPoints = e.getPartialValue(); |
| } |
| target = new InstanceTarget<T>(instance, injectionPoints); |
| } |
| |
| public ScopedBindingBuilder toProvider(final Provider<? extends T> provider) { |
| checkNotNull(provider, "provider"); |
| checkNotTargetted(); |
| |
| // lookup the injection points, adding any errors to the binder's errors list |
| Set<InjectionPoint> injectionPoints; |
| try { |
| injectionPoints = InjectionPoint.forInstanceMethodsAndFields(provider.getClass()); |
| } catch (ConfigurationException e) { |
| for (Message message : e.getErrorMessages()) { |
| binder.addError(message); |
| } |
| injectionPoints = e.getPartialValue(); |
| } |
| |
| final Set<InjectionPoint> injectionPointsFinal = injectionPoints; |
| target = new Target<T>() { |
| public <V> V acceptTargetVisitor(BindingTargetVisitor<? super T, V> visitor) { |
| return visitor.visitProvider(provider, injectionPointsFinal); |
| } |
| }; |
| 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(); |
| target = new Target<T>() { |
| public <V> V acceptTargetVisitor(BindingTargetVisitor<? super T, V> visitor) { |
| return visitor.visitProviderKey(providerKey); |
| } |
| }; |
| return this; |
| } |
| |
| public void in(final Class<? extends Annotation> scopeAnnotation) { |
| checkNotNull(scopeAnnotation, "scopeAnnotation"); |
| checkNotScoped(); |
| |
| scoping = new AbstractScoping() { |
| @Override public Class<? extends Annotation> getScopeAnnotation() { |
| return scopeAnnotation; |
| } |
| public <V> V acceptVisitor(BindingScopingVisitor<V> visitor) { |
| return visitor.visitScopeAnnotation(scopeAnnotation); |
| } |
| @Override public String toString() { |
| return scopeAnnotation.getName(); |
| } |
| }; |
| } |
| |
| public void in(final Scope scope) { |
| checkNotNull(scope, "scope"); |
| checkNotScoped(); |
| scoping = new AbstractScoping() { |
| @Override public Scope getScope() { |
| return scope; |
| } |
| public <V> V acceptVisitor(BindingScopingVisitor<V> visitor) { |
| return visitor.visitScope(scope); |
| } |
| @Override public String toString() { |
| return String.valueOf(scope); |
| } |
| }; |
| } |
| |
| public void asEagerSingleton() { |
| checkNotScoped(); |
| scoping = new AbstractScoping() { |
| public <V> V acceptVisitor(BindingScopingVisitor<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 (target != EMPTY_TARGET) { |
| binder.addError(IMPLEMENTATION_ALREADY_SET); |
| } |
| } |
| |
| private void checkNotAnnotated() { |
| if (ModuleBinding.this.key.getAnnotationType() != null) { |
| binder.addError(ANNOTATION_ALREADY_SPECIFIED); |
| } |
| } |
| |
| private void checkNotScoped() { |
| @SuppressWarnings("unchecked") BindingTargetVisitor<T,Boolean> supportsScopesOfT |
| = (BindingTargetVisitor<T,Boolean>) SUPPORTS_SCOPES; |
| |
| // Scoping isn't allowed when we have only one instance. |
| if (!target.acceptTargetVisitor(supportsScopesOfT)) { |
| binder.addError(SINGLE_INSTANCE_AND_SCOPE); |
| return; |
| } |
| |
| if (scoping != EMPTY_SCOPING) { |
| binder.addError(SCOPE_ALREADY_SET); |
| } |
| } |
| |
| @Override public String toString() { |
| String type = key.getAnnotationType() == null |
| ? "AnnotatedBindingBuilder<" |
| : "LinkedBindingBuilder<"; |
| return type + key.getTypeLiteral() + ">"; |
| } |
| } |
| |
| public ConstantBuilder constantBuilder(Binder binder) { |
| return new ConstantBuilder(binder); |
| } |
| |
| /** |
| * Package-private write access to the internal state of this element. |
| */ |
| class ConstantBuilder |
| implements AnnotatedConstantBindingBuilder, ConstantBindingBuilder { |
| private final Binder binder; |
| |
| ConstantBuilder(Binder binder) { |
| this.binder = binder.skipSources(ConstantBuilder.class); |
| } |
| |
| public ConstantBindingBuilder annotatedWith(final Class<? extends Annotation> annotationType) { |
| checkNotNull(annotationType, "annotationType"); |
| if (key.getAnnotationType() != null) { |
| binder.addError(ANNOTATION_ALREADY_SPECIFIED); |
| } else { |
| key = Key.get(key.getTypeLiteral(), annotationType); |
| } |
| return this; |
| } |
| |
| public ConstantBindingBuilder annotatedWith(final Annotation annotation) { |
| checkNotNull(annotation, "annotation"); |
| if (key.getAnnotationType() != null) { |
| binder.addError(ANNOTATION_ALREADY_SPECIFIED); |
| } else { |
| key = Key.get(key.getTypeLiteral(), annotation); |
| } |
| return this; |
| } |
| |
| public void to(final String value) { |
| to(String.class, value); |
| } |
| |
| public void to(final int value) { |
| to(Integer.class, value); |
| } |
| |
| public void to(final long value) { |
| to(Long.class, value); |
| } |
| |
| public void to(final boolean value) { |
| to(Boolean.class, value); |
| } |
| |
| public void to(final double value) { |
| to(Double.class, value); |
| } |
| |
| public void to(final float value) { |
| to(Float.class, value); |
| } |
| |
| public void to(final short value) { |
| to(Short.class, value); |
| } |
| |
| public void to(final char value) { |
| to(Character.class, value); |
| } |
| |
| public void to(final Class<?> value) { |
| to(Class.class, value); |
| } |
| |
| public <E extends Enum<E>> void to(final E value) { |
| to(value.getDeclaringClass(), value); |
| } |
| |
| private void to(Class<?> type, Object instance) { |
| // this type will define T, so these assignments are safe |
| @SuppressWarnings("unchecked") |
| Class<T> typeAsClassT = (Class<T>) type; |
| @SuppressWarnings("unchecked") |
| T instanceAsT = (T) instance; |
| |
| if (keyTypeIsSet()) { |
| binder.addError(CONSTANT_VALUE_ALREADY_SET); |
| return; |
| } |
| |
| if (key.getAnnotation() != null) { |
| key = Key.get(typeAsClassT, key.getAnnotation()); |
| } else if (key.getAnnotationType() != null) { |
| key = Key.get(typeAsClassT, key.getAnnotationType()); |
| } else { |
| key = Key.get(typeAsClassT); |
| } |
| |
| ModuleBinding.this.target = new InstanceTarget<T>(instanceAsT, |
| ImmutableSet.<InjectionPoint>of()); |
| |
| if (instanceAsT == null) { |
| binder.addError(BINDING_TO_NULL); |
| } |
| } |
| |
| @Override public String toString() { |
| return key.getAnnotationType() == null |
| ? "AnnotatedConstantBindingBuilder" |
| : "ConstantBindingBuilder"; |
| } |
| } |
| |
| /** A binding target, which provides instances from a specific key. */ |
| private interface Target<T> { |
| <V> V acceptTargetVisitor(BindingTargetVisitor<? super T, V> visitor); |
| } |
| |
| /** Immutable snapshot of a binding scope. */ |
| private interface Scoping { |
| <V> V acceptVisitor(BindingScopingVisitor<V> visitor); |
| } |
| |
| static class InstanceTarget<T> implements Target<T> { |
| private final T instance; |
| private final ImmutableSet<InjectionPoint> injectionPoints; |
| |
| public InstanceTarget(T instance, Set<InjectionPoint> injectionPoints) { |
| this.instance = instance; |
| this.injectionPoints = ImmutableSet.copyOf(injectionPoints); |
| } |
| |
| public <V> V acceptTargetVisitor(BindingTargetVisitor<? super T, V> visitor) { |
| return visitor.visitInstance(instance, injectionPoints); |
| } |
| } |
| |
| static final String BINDING_TO_NULL = "Binding to null instances is not allowed. " |
| + "Use toProvider(Providers.of(null)) if this is your intended behaviour."; |
| static final String CONSTANT_VALUE_ALREADY_SET = "Constant value is set more" |
| + " than once."; |
| static final String ANNOTATION_ALREADY_SPECIFIED = "More than one annotation" |
| + " is specified for this binding."; |
| } |