blob: 4a8a488ac1efa2dc445098a81a10893ba5746693 [file] [log] [blame]
/**
* Copyright (C) 2014 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.multibindings;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.inject.multibindings.Multibinder.checkConfiguration;
import static com.google.inject.util.Types.newParameterizedType;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import com.google.common.base.Optional;
import com.google.common.collect.ImmutableSet;
import com.google.inject.Binder;
import com.google.inject.Binding;
import com.google.inject.Inject;
import com.google.inject.Injector;
import com.google.inject.Key;
import com.google.inject.Module;
import com.google.inject.Provider;
import com.google.inject.TypeLiteral;
import com.google.inject.binder.LinkedBindingBuilder;
import com.google.inject.spi.BindingTargetVisitor;
import com.google.inject.spi.Dependency;
import com.google.inject.spi.Element;
import com.google.inject.spi.ProviderInstanceBinding;
import com.google.inject.spi.ProviderLookup;
import com.google.inject.spi.ProviderWithDependencies;
import com.google.inject.spi.ProviderWithExtensionVisitor;
import com.google.inject.spi.Toolable;
import com.google.inject.util.Types;
import java.io.Serializable;
import java.lang.annotation.Annotation;
import java.lang.annotation.Retention;
import java.lang.reflect.Type;
import java.util.Set;
import javax.inject.Qualifier;
/**
* An API to bind optional values, optionally with a default value.
* OptionalBinder fulfills two roles: <ol>
* <li>It allows a framework to define an injection point that may or
* may not be bound by users.
* <li>It allows a framework to supply a default value that can be changed
* by users.
* </ol>
*
* <p>When an OptionalBinder is added, it will always supply the bindings:
* {@code Optional<T>} and {@code Optional<Provider<T>>}. If
* {@link #setBinding} or {@link #setDefault} are called, it will also
* bind {@code T}.
*
* <p>{@code setDefault} is intended for use by frameworks that need a default
* value. User code can call {@code setBinding} to override the default.
* <b>Warning: Even if setBinding is called, the default binding
* will still exist in the object graph. If it is a singleton, it will be
* instantiated in {@code Stage.PRODUCTION}.</b>
*
* <p>If setDefault or setBinding are linked to Providers, the Provider may return
* {@code null}. If it does, the Optional bindings will be absent. Binding
* setBinding to a Provider that returns null will not cause OptionalBinder
* to fall back to the setDefault binding.
*
* <p>If neither setDefault nor setBinding are called, it will try to link to a
* user-supplied binding of the same type. If no binding exists, the optionals
* will be absent. Otherwise, if a user-supplied binding of that type exists,
* or if setBinding or setDefault are called, the optionals will return present
* if they are bound to a non-null value.
*
* <p>Values are resolved at injection time. If a value is bound to a
* provider, that provider's get method will be called each time the optional
* is injected (unless the binding is also scoped, or an optional of provider is
* injected).
*
* <p>Annotations are used to create different optionals of the same key/value
* type. Each distinct annotation gets its own independent binding.
*
* <pre><code>
* public class FrameworkModule extends AbstractModule {
* protected void configure() {
* OptionalBinder.newOptionalBinder(binder(), Renamer.class);
* }
* }</code></pre>
*
* <p>With this module, an {@link Optional}{@code <Renamer>} can now be
* injected. With no other bindings, the optional will be absent.
* Users can specify bindings in one of two ways:
*
* <p>Option 1:
* <pre><code>
* public class UserRenamerModule extends AbstractModule {
* protected void configure() {
* bind(Renamer.class).to(ReplacingRenamer.class);
* }
* }</code></pre>
*
* <p>or Option 2:
* <pre><code>
* public class UserRenamerModule extends AbstractModule {
* protected void configure() {
* OptionalBinder.newOptionalBinder(binder(), Renamer.class)
* .setBinding().to(ReplacingRenamer.class);
* }
* }</code></pre>
* With both options, the {@code Optional<Renamer>} will be present and supply the
* ReplacingRenamer.
*
* <p>Default values can be supplied using:
* <pre><code>
* public class FrameworkModule extends AbstractModule {
* protected void configure() {
* OptionalBinder.newOptionalBinder(binder(), Key.get(String.class, LookupUrl.class))
* .setDefault().to(DEFAULT_LOOKUP_URL);
* }
* }</code></pre>
* With the above module, code can inject an {@code @LookupUrl String} and it
* will supply the DEFAULT_LOOKUP_URL. A user can change this value by binding
* <pre><code>
* public class UserLookupModule extends AbstractModule {
* protected void configure() {
* OptionalBinder.newOptionalBinder(binder(), Key.get(String.class, LookupUrl.class))
* .setBinding().to(CUSTOM_LOOKUP_URL);
* }
* }</code></pre>
* ... which will override the default value.
*
* <p>If one module uses setDefault the only way to override the default is to use setBinding.
* It is an error for a user to specify the binding without using OptionalBinder if
* setDefault or setBinding are called. For example,
* <pre><code>
* public class FrameworkModule extends AbstractModule {
* protected void configure() {
* OptionalBinder.newOptionalBinder(binder(), Key.get(String.class, LookupUrl.class))
* .setDefault().to(DEFAULT_LOOKUP_URL);
* }
* }
* public class UserLookupModule extends AbstractModule {
* protected void configure() {
* bind(Key.get(String.class, LookupUrl.class)).to(CUSTOM_LOOKUP_URL);
* }
* }</code></pre>
* ... would generate an error, because both the framework and the user are trying to bind
* {@code @LookupUrl String}.
*
* @author sameb@google.com (Sam Berlin)
*/
public abstract class OptionalBinder<T> {
private OptionalBinder() {}
public static <T> OptionalBinder<T> newOptionalBinder(Binder binder, Class<T> type) {
return newOptionalBinder(binder, Key.get(type));
}
public static <T> OptionalBinder<T> newOptionalBinder(Binder binder, TypeLiteral<T> type) {
return newOptionalBinder(binder, Key.get(type));
}
public static <T> OptionalBinder<T> newOptionalBinder(Binder binder, Key<T> type) {
binder = binder.skipSources(OptionalBinder.class, RealOptionalBinder.class);
RealOptionalBinder<T> optionalBinder = new RealOptionalBinder<T>(binder, type);
binder.install(optionalBinder);
return optionalBinder;
}
@SuppressWarnings("unchecked")
static <T> TypeLiteral<Optional<T>> optionalOf(
TypeLiteral<T> type) {
return (TypeLiteral<Optional<T>>) TypeLiteral.get(
Types.newParameterizedType(Optional.class, type.getType()));
}
@SuppressWarnings("unchecked")
static <T> TypeLiteral<Optional<javax.inject.Provider<T>>> optionalOfJavaxProvider(
TypeLiteral<T> type) {
return (TypeLiteral<Optional<javax.inject.Provider<T>>>) TypeLiteral.get(
Types.newParameterizedType(Optional.class,
newParameterizedType(javax.inject.Provider.class, type.getType())));
}
@SuppressWarnings("unchecked")
static <T> TypeLiteral<Optional<Provider<T>>> optionalOfProvider(TypeLiteral<T> type) {
return (TypeLiteral<Optional<Provider<T>>>) TypeLiteral.get(Types.newParameterizedType(
Optional.class, newParameterizedType(Provider.class, type.getType())));
}
@SuppressWarnings("unchecked")
static <T> Key<Provider<T>> providerOf(Key<T> key) {
Type providerT = Types.providerOf(key.getTypeLiteral().getType());
return (Key<Provider<T>>) key.ofType(providerT);
}
/**
* Returns a binding builder used to set the default value that will be injected.
* The binding set by this method will be ignored if {@link #setBinding} is called.
*
* <p>It is an error to call this method without also calling one of the {@code to}
* methods on the returned binding builder.
*/
public abstract LinkedBindingBuilder<T> setDefault();
/**
* Returns a binding builder used to set the actual value that will be injected.
* This overrides any binding set by {@link #setDefault}.
*
* <p>It is an error to call this method without also calling one of the {@code to}
* methods on the returned binding builder.
*/
public abstract LinkedBindingBuilder<T> setBinding();
enum Source { DEFAULT, ACTUAL }
@Retention(RUNTIME)
@Qualifier
@interface Default {
String value();
}
@Retention(RUNTIME)
@Qualifier
@interface Actual {
String value();
}
/**
* The actual OptionalBinder plays several roles. It implements Module to hide that
* fact from the public API, and installs the various bindings that are exposed to the user.
*/
static final class RealOptionalBinder<T> extends OptionalBinder<T> implements Module {
private final Key<T> typeKey;
private final Key<Optional<T>> optionalKey;
private final Key<Optional<javax.inject.Provider<T>>> optionalJavaxProviderKey;
private final Key<Optional<Provider<T>>> optionalProviderKey;
private final Provider<Optional<Provider<T>>> optionalProviderT;
private final Key<T> defaultKey;
private final Key<T> actualKey;
/** the target injector's binder. non-null until initialization, null afterwards */
private Binder binder;
/** the default binding, for the SPI. */
private Binding<T> defaultBinding;
/** the actual binding, for the SPI */
private Binding<T> actualBinding;
/** the dependencies -- initialized with defaults & overridden when tooled. */
private Set<Dependency<?>> dependencies;
/** the dependencies -- initialized with defaults & overridden when tooled. */
private Set<Dependency<?>> providerDependencies;
private RealOptionalBinder(Binder binder, Key<T> typeKey) {
this.binder = binder;
this.typeKey = checkNotNull(typeKey);
TypeLiteral<T> literal = typeKey.getTypeLiteral();
this.optionalKey = typeKey.ofType(optionalOf(literal));
this.optionalJavaxProviderKey = typeKey.ofType(optionalOfJavaxProvider(literal));
this.optionalProviderKey = typeKey.ofType(optionalOfProvider(literal));
this.optionalProviderT = binder.getProvider(optionalProviderKey);
String name = RealElement.nameOf(typeKey);
this.defaultKey = Key.get(typeKey.getTypeLiteral(), new DefaultImpl(name));
this.actualKey = Key.get(typeKey.getTypeLiteral(), new ActualImpl(name));
// Until the injector initializes us, we don't know what our dependencies are,
// so initialize to the whole Injector (like Multibinder, and MapBinder indirectly).
this.dependencies = ImmutableSet.<Dependency<?>>of(Dependency.get(Key.get(Injector.class)));
this.providerDependencies =
ImmutableSet.<Dependency<?>>of(Dependency.get(Key.get(Injector.class)));
}
/**
* Adds a binding for T. Multiple calls to this are safe, and will be collapsed as duplicate
* bindings.
*/
private void addDirectTypeBinding(Binder binder) {
binder.bind(typeKey).toProvider(new RealOptionalBinderProviderWithDependencies<T>(typeKey) {
@Override public T get() {
Optional<Provider<T>> optional = optionalProviderT.get();
if (optional.isPresent()) {
return optional.get().get();
}
// Let Guice handle blowing up if the injection point doesn't have @Nullable
// (If it does have @Nullable, that's fine. This would only happen if
// setBinding/setDefault themselves were bound to 'null').
return null;
}
@Override public Set<Dependency<?>> getDependencies() {
return dependencies;
}
});
}
@Override public LinkedBindingBuilder<T> setDefault() {
checkConfiguration(!isInitialized(), "already initialized");
addDirectTypeBinding(binder);
return binder.bind(defaultKey);
}
@Override public LinkedBindingBuilder<T> setBinding() {
checkConfiguration(!isInitialized(), "already initialized");
addDirectTypeBinding(binder);
return binder.bind(actualKey);
}
@Override public void configure(Binder binder) {
checkConfiguration(!isInitialized(), "OptionalBinder was already initialized");
binder.bind(optionalProviderKey).toProvider(
new RealOptionalBinderProviderWithDependencies<Optional<Provider<T>>>(typeKey) {
private Optional<Provider<T>> optional;
@Toolable @Inject void initialize(Injector injector) {
RealOptionalBinder.this.binder = null;
actualBinding = injector.getExistingBinding(actualKey);
defaultBinding = injector.getExistingBinding(defaultKey);
Binding<T> userBinding = injector.getExistingBinding(typeKey);
Binding<T> binding = null;
if (actualBinding != null) {
// TODO(sameb): Consider exposing an option that will allow
// ACTUAL to fallback to DEFAULT if ACTUAL's provider returns null.
// Right now, an ACTUAL binding can convert from present -> absent
// if it's bound to a provider that returns null.
binding = actualBinding;
} else if (defaultBinding != null) {
binding = defaultBinding;
} else if (userBinding != null) {
// If neither the actual or default is set, then we fallback
// to the value bound to the type itself and consider that the
// "actual binding" for the SPI.
binding = userBinding;
actualBinding = userBinding;
}
if (binding != null) {
optional = Optional.of(binding.getProvider());
RealOptionalBinder.this.dependencies =
ImmutableSet.<Dependency<?>>of(Dependency.get(binding.getKey()));
RealOptionalBinder.this.providerDependencies =
ImmutableSet.<Dependency<?>>of(Dependency.get(providerOf(binding.getKey())));
} else {
optional = Optional.absent();
RealOptionalBinder.this.dependencies = ImmutableSet.of();
RealOptionalBinder.this.providerDependencies = ImmutableSet.of();
}
}
@Override public Optional<Provider<T>> get() {
return optional;
}
@Override public Set<Dependency<?>> getDependencies() {
return providerDependencies;
}
});
// Optional is immutable, so it's safe to expose Optional<Provider<T>> as
// Optional<javax.inject.Provider<T>> (since Guice provider implements javax Provider).
@SuppressWarnings({"unchecked", "cast"})
Key massagedOptionalProviderKey = (Key) optionalProviderKey;
binder.bind(optionalJavaxProviderKey).to(massagedOptionalProviderKey);
binder.bind(optionalKey).toProvider(new RealOptionalKeyProvider());
}
private class RealOptionalKeyProvider
extends RealOptionalBinderProviderWithDependencies<Optional<T>>
implements ProviderWithExtensionVisitor<Optional<T>>,
OptionalBinderBinding<Optional<T>>,
Provider<Optional<T>> {
RealOptionalKeyProvider() {
super(typeKey);
}
@Override public Optional<T> get() {
Optional<Provider<T>> optional = optionalProviderT.get();
if (optional.isPresent()) {
return Optional.fromNullable(optional.get().get());
} else {
return Optional.absent();
}
}
@Override public Set<Dependency<?>> getDependencies() {
return dependencies;
}
@SuppressWarnings("unchecked")
@Override
public <B, R> R acceptExtensionVisitor(BindingTargetVisitor<B, R> visitor,
ProviderInstanceBinding<? extends B> binding) {
if (visitor instanceof MultibindingsTargetVisitor) {
return ((MultibindingsTargetVisitor<Optional<T>, R>) visitor).visit(this);
} else {
return visitor.visit(binding);
}
}
@Override public Key<Optional<T>> getKey() {
return optionalKey;
}
@Override public Binding<?> getActualBinding() {
if (isInitialized()) {
return actualBinding;
} else {
throw new UnsupportedOperationException(
"getActualBinding() not supported from Elements.getElements, requires an Injector.");
}
}
@Override public Binding<?> getDefaultBinding() {
if (isInitialized()) {
return defaultBinding;
} else {
throw new UnsupportedOperationException(
"getDefaultBinding() not supported from Elements.getElements, requires an Injector.");
}
}
@Override public boolean containsElement(Element element) {
Key<?> elementKey;
if (element instanceof Binding) {
elementKey = ((Binding<?>) element).getKey();
} else if (element instanceof ProviderLookup) {
elementKey = ((ProviderLookup<?>) element).getKey();
} else {
return false; // cannot match;
}
return elementKey.equals(optionalKey)
|| elementKey.equals(optionalProviderKey)
|| elementKey.equals(optionalJavaxProviderKey)
|| elementKey.equals(defaultKey)
|| elementKey.equals(actualKey)
|| matchesTypeKey(element, elementKey);
}
}
/** Returns true if the key & element indicate they were bound by this OptionalBinder. */
private boolean matchesTypeKey(Element element, Key<?> elementKey) {
// Just doing .equals(typeKey) isn't enough, because the user can bind that themselves.
return elementKey.equals(typeKey)
&& element instanceof ProviderInstanceBinding
&& (((ProviderInstanceBinding) element)
.getUserSuppliedProvider() instanceof RealOptionalBinderProviderWithDependencies);
}
private boolean isInitialized() {
return binder == null;
}
@Override public boolean equals(Object o) {
return o instanceof RealOptionalBinder
&& ((RealOptionalBinder<?>) o).typeKey.equals(typeKey);
}
@Override public int hashCode() {
return typeKey.hashCode();
}
/**
* A base class for ProviderWithDependencies that need equality based on a specific object.
*/
private abstract static class RealOptionalBinderProviderWithDependencies<T> implements
ProviderWithDependencies<T> {
private final Object equality;
public RealOptionalBinderProviderWithDependencies(Object equality) {
this.equality = equality;
}
@Override public boolean equals(Object obj) {
return this.getClass() == obj.getClass()
&& equality.equals(((RealOptionalBinderProviderWithDependencies<?>) obj).equality);
}
@Override public int hashCode() {
return equality.hashCode();
}
}
}
static class DefaultImpl extends BaseAnnotation implements Default {
public DefaultImpl(String value) {
super(Default.class, value);
}
}
static class ActualImpl extends BaseAnnotation implements Actual {
public ActualImpl(String value) {
super(Actual.class, value);
}
}
abstract static class BaseAnnotation implements Serializable, Annotation {
private final String value;
private final Class<? extends Annotation> clazz;
BaseAnnotation(Class<? extends Annotation> clazz, String value) {
this.clazz = checkNotNull(clazz, "clazz");
this.value = checkNotNull(value, "value");
}
public String value() {
return this.value;
}
@Override public int hashCode() {
// This is specified in java.lang.Annotation.
return (127 * "value".hashCode()) ^ value.hashCode();
}
@Override public boolean equals(Object o) {
if (!(clazz.isInstance(o))) {
return false;
}
BaseAnnotation other = (BaseAnnotation) o;
return value.equals(other.value());
}
@Override public String toString() {
return "@" + clazz.getName() + (value.isEmpty() ? "" : "(value=" + value + ")");
}
@Override public Class<? extends Annotation> annotationType() {
return clazz;
}
private static final long serialVersionUID = 0;
}
}