Automated g4 rollback of changelist 70734332.
*** Reason for rollback ***
Broke a project.
*** Original change description ***
Simplify OptionalBinder implementation to not delegate to a MapBinder.
Instead, bind directly to annotated @Actual/@Default keys. This is in
preparation for future improvements.
This contains two logical changes:
1) The error message for duplicate actual/default bindings is now the default Guice message, instead of a custom thing in MapBinder.
2) getDependencies now returns the live dependency (either the actual or the default, not both). Also, Optional<Provider<T>> continues to return a de...
***
-------------
Created by MOE: http://code.google.com/p/moe-java
MOE_MIGRATED_REVID=70738452
diff --git a/extensions/multibindings/src/com/google/inject/multibindings/OptionalBinder.java b/extensions/multibindings/src/com/google/inject/multibindings/OptionalBinder.java
index 60c4f91..5d4447d 100644
--- a/extensions/multibindings/src/com/google/inject/multibindings/OptionalBinder.java
+++ b/extensions/multibindings/src/com/google/inject/multibindings/OptionalBinder.java
@@ -17,12 +17,14 @@
package com.google.inject.multibindings;
import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.inject.internal.RehashableKeys.Keys.needsRehashing;
+import static com.google.inject.internal.RehashableKeys.Keys.rehash;
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.common.collect.Iterables;
import com.google.inject.Binder;
import com.google.inject.Binding;
import com.google.inject.Inject;
@@ -32,9 +34,12 @@
import com.google.inject.Provider;
import com.google.inject.TypeLiteral;
import com.google.inject.binder.LinkedBindingBuilder;
+import com.google.inject.internal.Errors;
+import com.google.inject.multibindings.Element.Type;
+import com.google.inject.multibindings.MapBinder.RealMapBinder;
import com.google.inject.spi.BindingTargetVisitor;
import com.google.inject.spi.Dependency;
-import com.google.inject.spi.Element;
+import com.google.inject.spi.HasDependencies;
import com.google.inject.spi.ProviderInstanceBinding;
import com.google.inject.spi.ProviderLookup;
import com.google.inject.spi.ProviderWithDependencies;
@@ -42,14 +47,9 @@
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.Map;
import java.util.Set;
-import javax.inject.Qualifier;
-
/**
* An API to bind optional values, optionally with a default value.
@@ -168,12 +168,6 @@
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.
@@ -195,18 +189,6 @@
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
@@ -217,21 +199,18 @@
private final Key<Optional<T>> optionalKey;
private final Key<Optional<javax.inject.Provider<T>>> optionalJavaxProviderKey;
private final Key<Optional<Provider<T>>> optionalProviderKey;
+ private final Key<Map<Source, Provider<T>>> mapKey;
+ private final RealMapBinder<Source, T> mapBinder;
+ private final Set<Dependency<?>> dependencies;
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;
+ private Binding<?> 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 Binding<?> actualBinding;
private RealOptionalBinder(Binder binder, Key<T> typeKey) {
this.binder = binder;
@@ -240,14 +219,26 @@
this.optionalKey = typeKey.ofType(optionalOf(literal));
this.optionalJavaxProviderKey = typeKey.ofType(optionalOfJavaxProvider(literal));
this.optionalProviderKey = typeKey.ofType(optionalOfProvider(literal));
+ this.mapKey =
+ typeKey.ofType(MapBinder.mapOfProviderOf(TypeLiteral.get(Source.class), literal));
+ this.dependencies = ImmutableSet.<Dependency<?>>of(Dependency.get(mapKey));
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));
- this.dependencies = ImmutableSet.<Dependency<?>>of(
- Dependency.get(defaultKey), Dependency.get(actualKey));
- this.providerDependencies = ImmutableSet.<Dependency<?>>of(
- Dependency.get(providerOf(defaultKey)), Dependency.get(providerOf(actualKey)));
+ if (typeKey.getAnnotation() != null) {
+ this.mapBinder = (RealMapBinder<Source, T>) MapBinder.newMapBinder(binder,
+ TypeLiteral.get(Source.class), typeKey.getTypeLiteral(), typeKey.getAnnotation());
+ } else if (typeKey.getAnnotationType() != null) {
+ this.mapBinder = (RealMapBinder<Source, T>) MapBinder.newMapBinder(binder,
+ TypeLiteral.get(Source.class), typeKey.getTypeLiteral(), typeKey.getAnnotationType());
+ } else {
+ this.mapBinder = (RealMapBinder<Source, T>) MapBinder.newMapBinder(binder,
+ TypeLiteral.get(Source.class), typeKey.getTypeLiteral());
+ }
+ mapBinder.updateDuplicateKeyMessage(Source.DEFAULT, "OptionalBinder for "
+ + Errors.convert(typeKey)
+ + " called with different setDefault values, from bindings:\n");
+ mapBinder.updateDuplicateKeyMessage(Source.ACTUAL, "OptionalBinder for "
+ + Errors.convert(typeKey)
+ + " called with different setBinding values, from bindings:\n");
}
/**
@@ -274,49 +265,82 @@
}
@Override public LinkedBindingBuilder<T> setDefault() {
- checkConfiguration(!isInitialized(), "already initialized");
+ checkConfiguration(!isInitialized(), "already initialized");
+
addDirectTypeBinding(binder);
- return binder.bind(defaultKey);
+
+ RealElement.BindingBuilder<T> valueBinding = RealElement.addBinding(binder,
+ Element.Type.OPTIONALBINDER, typeKey.getTypeLiteral(), RealElement.nameOf(typeKey));
+ Key<T> valueKey = Key.get(typeKey.getTypeLiteral(), valueBinding.getAnnotation());
+ mapBinder.addBinding(Source.DEFAULT).toProvider(
+ new ValueProvider<T>(valueKey, binder.getProvider(valueKey)));
+ return valueBinding;
}
@Override public LinkedBindingBuilder<T> setBinding() {
- checkConfiguration(!isInitialized(), "already initialized");
+ checkConfiguration(!isInitialized(), "already initialized");
+
addDirectTypeBinding(binder);
- return binder.bind(actualKey);
+
+ RealElement.BindingBuilder<T> valueBinding = RealElement.addBinding(binder,
+ Element.Type.OPTIONALBINDER, typeKey.getTypeLiteral(), RealElement.nameOf(typeKey));
+ Key<T> valueKey = Key.get(typeKey.getTypeLiteral(), valueBinding.getAnnotation());
+ mapBinder.addBinding(Source.ACTUAL).toProvider(
+ new ValueProvider<T>(valueKey, binder.getProvider(valueKey)));
+ return valueBinding;
+ }
+
+ /**
+ * Traverses through the dependencies of the providers in order to get to the user's binding.
+ */
+ private Binding<?> getBindingFromMapProvider(Injector injector, Provider<T> mapProvider) {
+ HasDependencies deps = (HasDependencies) mapProvider;
+ Key<?> depKey = Iterables.getOnlyElement(deps.getDependencies()).getKey();
+ // The dep flow is (and will stay this way, until we change the internals) --
+ // Key[type=Provider<java.lang.String>, annotation=@Element(type=MAPBINDER)]
+ // -> Key[type=String, annotation=@Element(type=MAPBINDER)]
+ // -> Key[type=Provider<String>, annotation=@Element(type=OPTIONALBINDER)]
+ // -> Key[type=String, annotation=@Element(type=OPTIONALBINDER)]
+ // The last one points to the user's binding.
+ for (int i = 0; i < 3; i++) {
+ deps = (HasDependencies) injector.getBinding(depKey);
+ depKey = Iterables.getOnlyElement(deps.getDependencies()).getKey();
+ }
+ return injector.getBinding(depKey);
}
public void configure(Binder binder) {
checkConfiguration(!isInitialized(), "OptionalBinder was already initialized");
+ final Provider<Map<Source, Provider<T>>> mapProvider = binder.getProvider(mapKey);
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> 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;
- }
-
- 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();
+ Map<Source, Provider<T>> map = mapProvider.get();
+ // Map might be null if duplicates prevented MapBinder from initializing
+ if (map != null) {
+ if (map.containsKey(Source.ACTUAL)) {
+ // 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.
+ optional = Optional.fromNullable(map.get(Source.ACTUAL));
+ } else if (map.containsKey(Source.DEFAULT)) {
+ optional = Optional.fromNullable(map.get(Source.DEFAULT));
+ } else {
+ optional = Optional.absent();
+ }
+
+ // Also set up the bindings for the SPI.
+ if (map.containsKey(Source.ACTUAL)) {
+ actualBinding = getBindingFromMapProvider(injector, map.get(Source.ACTUAL));
+ }
+ if (map.containsKey(Source.DEFAULT)) {
+ defaultBinding = getBindingFromMapProvider(injector, map.get(Source.DEFAULT));
+ }
}
}
@@ -325,7 +349,7 @@
}
public Set<Dependency<?>> getDependencies() {
- return providerDependencies;
+ return dependencies;
}
});
@@ -344,7 +368,7 @@
OptionalBinderBinding<Optional<T>>,
Provider<Optional<T>> {
RealOptionalKeyProvider() {
- super(typeKey);
+ super(mapKey);
}
public Optional<T> get() {
@@ -392,27 +416,30 @@
}
}
- public boolean containsElement(Element element) {
- Key<?> elementKey;
- if (element instanceof Binding) {
- elementKey = ((Binding<?>) element).getKey();
- } else if (element instanceof ProviderLookup) {
- elementKey = ((ProviderLookup<?>) element).getKey();
+ public boolean containsElement(com.google.inject.spi.Element element) {
+ if (mapBinder.containsElement(element)) {
+ return true;
} else {
- return false; // cannot match;
- }
+ 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);
+ return elementKey.equals(optionalKey)
+ || elementKey.equals(optionalProviderKey)
+ || elementKey.equals(optionalJavaxProviderKey)
+ || matchesTypeKey(element, elementKey)
+ || matchesUserBinding(elementKey);
+ }
}
}
/** Returns true if the key & element indicate they were bound by this OptionalBinder. */
- private boolean matchesTypeKey(Element element, Key<?> elementKey) {
+ private boolean matchesTypeKey(com.google.inject.spi.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
@@ -420,17 +447,73 @@
.getUserSuppliedProvider() instanceof RealOptionalBinderProviderWithDependencies);
}
+ /** Returns true if the key indicates this is a user bound value for the optional binder. */
+ private boolean matchesUserBinding(Key<?> elementKey) {
+ return elementKey.getAnnotation() instanceof Element
+ && ((Element) elementKey.getAnnotation()).setName().equals(RealElement.nameOf(typeKey))
+ && ((Element) elementKey.getAnnotation()).type() == Type.OPTIONALBINDER
+ && elementKey.getTypeLiteral().equals(typeKey.getTypeLiteral());
+ }
+
private boolean isInitialized() {
return binder == null;
}
@Override public boolean equals(Object o) {
return o instanceof RealOptionalBinder
- && ((RealOptionalBinder<?>) o).typeKey.equals(typeKey);
+ && ((RealOptionalBinder<?>) o).mapKey.equals(mapKey);
}
@Override public int hashCode() {
- return typeKey.hashCode();
+ return mapKey.hashCode();
+ }
+
+ /** A Provider that bases equality & hashcodes off another key. */
+ private static final class ValueProvider<T> implements ProviderWithDependencies<T> {
+ private final Provider<T> provider;
+ private volatile Key<T> key;
+
+ private ValueProvider(Key<T> key, Provider<T> provider) {
+ this.key = key;
+ this.provider = provider;
+ }
+
+ public T get() {
+ return provider.get();
+ }
+
+ public Set<Dependency<?>> getDependencies() {
+ return ((HasDependencies) provider).getDependencies();
+ }
+
+ private Key<T> getCurrentKey() {
+ // Every time, check if the key needs rehashing.
+ // If so, update the field as an optimization for next time.
+ Key<T> currentKey = key;
+ if (needsRehashing(currentKey)) {
+ currentKey = rehash(currentKey);
+ key = currentKey;
+ }
+ return currentKey;
+ }
+
+ /**
+ * Equality is based on the key (which includes the target information, because of how
+ * RealElement works). This lets duplicate bindings collapse.
+ */
+ @Override public boolean equals(Object obj) {
+ return obj instanceof ValueProvider
+ && getCurrentKey().equals(((ValueProvider) obj).getCurrentKey());
+ }
+
+ /** We only use the hashcode of the typeliteral, which can't change. */
+ @Override public int hashCode() {
+ return key.getTypeLiteral().hashCode();
+ }
+
+ @Override public String toString() {
+ return provider.toString();
+ }
}
/**
@@ -456,55 +539,4 @@
}
}
}
-
- 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;
- }
}
diff --git a/extensions/multibindings/test/com/google/inject/multibindings/OptionalBinderTest.java b/extensions/multibindings/test/com/google/inject/multibindings/OptionalBinderTest.java
index 2529c18..246c7a3 100644
--- a/extensions/multibindings/test/com/google/inject/multibindings/OptionalBinderTest.java
+++ b/extensions/multibindings/test/com/google/inject/multibindings/OptionalBinderTest.java
@@ -43,8 +43,6 @@
import com.google.inject.Scopes;
import com.google.inject.TypeLiteral;
import com.google.inject.internal.RehashableKeys;
-import com.google.inject.multibindings.OptionalBinder.Actual;
-import com.google.inject.multibindings.OptionalBinder.Default;
import com.google.inject.multibindings.SpiUtils.VisitType;
import com.google.inject.name.Named;
import com.google.inject.name.Names;
@@ -347,10 +345,11 @@
} catch (CreationException ce) {
assertEquals(ce.getMessage(), 1, ce.getErrorMessages().size());
assertContains(ce.getMessage(),
- "1) A binding to java.lang.String annotated with @"
- + Default.class.getName() + " was already configured at "
- + module.getClass().getName() + ".configure(",
- "at " + module.getClass().getName() + ".configure(");
+ "1) OptionalBinder for java.lang.String called with different setDefault values, "
+ + "from bindings:",
+ "at " + module.getClass().getName() + ".configure(",
+ "at " + module.getClass().getName() + ".configure(",
+ "at " + MapBinder.RealMapBinder.class.getName());
}
}
@@ -367,10 +366,11 @@
} catch (CreationException ce) {
assertEquals(ce.getMessage(), 1, ce.getErrorMessages().size());
assertContains(ce.getMessage(),
- "1) A binding to java.lang.String annotated with @"
- + Actual.class.getName() + " was already configured at "
- + module.getClass().getName() + ".configure(",
- "at " + module.getClass().getName() + ".configure(");
+ "1) OptionalBinder for java.lang.String called with different setBinding values, "
+ + "from bindings:",
+ "at " + module.getClass().getName() + ".configure(",
+ "at " + module.getClass().getName() + ".configure(",
+ "at " + MapBinder.RealMapBinder.class.getName());
}
}
@@ -387,16 +387,17 @@
Guice.createInjector(module);
fail();
} catch (CreationException ce) {
- assertEquals(ce.getMessage(), 2, ce.getErrorMessages().size());
+ assertEquals(ce.getMessage(), 1, ce.getErrorMessages().size());
assertContains(ce.getMessage(),
- "1) A binding to java.lang.String annotated with @"
- + Default.class.getName() + " was already configured at "
- + module.getClass().getName() + ".configure(",
+ "1) OptionalBinder for java.lang.String called with different setDefault values, "
+ + "from bindings:",
"at " + module.getClass().getName() + ".configure(",
- "2) A binding to java.lang.String annotated with @"
- + Actual.class.getName() + " was already configured at "
- + module.getClass().getName() + ".configure(",
- "at " + module.getClass().getName() + ".configure(");
+ "at " + module.getClass().getName() + ".configure(",
+ "and OptionalBinder for java.lang.String called with different setBinding values, "
+ + "from bindings:",
+ "at " + module.getClass().getName() + ".configure(",
+ "at " + module.getClass().getName() + ".configure(",
+ "at " + MapBinder.RealMapBinder.class.getName());
}
}
@@ -448,27 +449,17 @@
}
public void testMultipleDifferentOptionals() {
- final Key<String> bKey = Key.get(String.class, named("b"));
- final Key<String> cKey = Key.get(String.class, named("c"));
Module module = new AbstractModule() {
@Override protected void configure() {
OptionalBinder.newOptionalBinder(binder(), String.class).setDefault().toInstance("a");
OptionalBinder.newOptionalBinder(binder(), Integer.class).setDefault().toInstance(1);
-
- OptionalBinder.newOptionalBinder(binder(), bKey).setDefault().toInstance("b");
- OptionalBinder.newOptionalBinder(binder(), cKey).setDefault().toInstance("c");
}
};
Injector injector = Guice.createInjector(module);
assertEquals("a", injector.getInstance(String.class));
assertEquals(1, injector.getInstance(Integer.class).intValue());
- assertEquals("b", injector.getInstance(bKey));
- assertEquals("c", injector.getInstance(cKey));
- assertOptionalVisitor(stringKey, setOf(module), VisitType.BOTH, 3, instance("a"), null);
- assertOptionalVisitor(intKey, setOf(module), VisitType.BOTH, 3, instance(1), null);
- assertOptionalVisitor(bKey, setOf(module), VisitType.BOTH, 3, instance("b"), null);
- assertOptionalVisitor(cKey, setOf(module), VisitType.BOTH, 3, instance("c"), null);
+ assertOptionalVisitor(stringKey, setOf(module), VisitType.BOTH, 1, instance("a"), null);
}
public void testOptionalIsAppropriatelyLazy() {
@@ -613,7 +604,7 @@
}
}
- public void testDependencies_both() {
+ public void testDependencies() {
Injector injector = Guice.createInjector(new AbstractModule() {
@Override protected void configure() {
OptionalBinder<String> optionalbinder =
@@ -628,40 +619,7 @@
HasDependencies withDependencies = (HasDependencies) binding;
Set<String> elements = Sets.newHashSet();
elements.addAll(recurseForDependencies(injector, withDependencies));
- assertEquals(ImmutableSet.of("B"), elements);
- }
-
- public void testDependencies_actual() {
- Injector injector = Guice.createInjector(new AbstractModule() {
- @Override protected void configure() {
- OptionalBinder<String> optionalbinder =
- OptionalBinder.newOptionalBinder(binder(), String.class);
- optionalbinder.setBinding().to(Key.get(String.class, Names.named("b")));
- bindConstant().annotatedWith(Names.named("b")).to("B");
- }
- });
-
- Binding<String> binding = injector.getBinding(Key.get(String.class));
- HasDependencies withDependencies = (HasDependencies) binding;
- Set<String> elements = Sets.newHashSet();
- elements.addAll(recurseForDependencies(injector, withDependencies));
- assertEquals(ImmutableSet.of("B"), elements);
- }
-
- public void testDependencies_default() {
- Injector injector = Guice.createInjector(new AbstractModule() {
- @Override protected void configure() {
- OptionalBinder<String> optionalbinder =
- OptionalBinder.newOptionalBinder(binder(), String.class);
- optionalbinder.setDefault().toInstance("A");
- }
- });
-
- Binding<String> binding = injector.getBinding(Key.get(String.class));
- HasDependencies withDependencies = (HasDependencies) binding;
- Set<String> elements = Sets.newHashSet();
- elements.addAll(recurseForDependencies(injector, withDependencies));
- assertEquals(ImmutableSet.of("A"), elements);
+ assertEquals(ImmutableSet.of("A", "B"), elements);
}
private Set<String> recurseForDependencies(Injector injector, HasDependencies hasDependencies) {
@@ -920,6 +878,7 @@
list.add("C");
Key<?> keyAfter = binding.getKey();
assertSame(keyBefore, keyAfter);
+ assertTrue(RehashableKeys.Keys.needsRehashing(keyAfter));
}
@BindingAnnotation
diff --git a/extensions/multibindings/test/com/google/inject/multibindings/SpiUtils.java b/extensions/multibindings/test/com/google/inject/multibindings/SpiUtils.java
index 9517425..d121944 100644
--- a/extensions/multibindings/test/com/google/inject/multibindings/SpiUtils.java
+++ b/extensions/multibindings/test/com/google/inject/multibindings/SpiUtils.java
@@ -16,6 +16,7 @@
package com.google.inject.multibindings;
+import static com.google.common.collect.Iterables.getOnlyElement;
import static com.google.inject.multibindings.MapBinder.entryOfProviderOf;
import static com.google.inject.multibindings.MapBinder.mapOf;
import static com.google.inject.multibindings.MapBinder.mapOfJavaxProviderOf;
@@ -53,12 +54,14 @@
import com.google.inject.spi.DefaultBindingTargetVisitor;
import com.google.inject.spi.Element;
import com.google.inject.spi.Elements;
+import com.google.inject.spi.HasDependencies;
import com.google.inject.spi.InstanceBinding;
import com.google.inject.spi.LinkedKeyBinding;
import com.google.inject.spi.ProviderInstanceBinding;
import com.google.inject.spi.ProviderKeyBinding;
import com.google.inject.spi.ProviderLookup;
+import java.lang.reflect.ParameterizedType;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
@@ -523,11 +526,16 @@
keyType.ofType(optionalOfJavaxProvider(keyType.getTypeLiteral()));
Key<Optional<Provider<T>>> optionalProviderKey =
keyType.ofType(optionalOfProvider(keyType.getTypeLiteral()));
+ Binding<Map<Source, T>> mapBinding = injector.getBinding(
+ keyType.ofType(mapOf(TypeLiteral.get(Source.class), keyType.getTypeLiteral())));
+ MapBinderBinding<Map<Source, T>> mapbinderBinding = (MapBinderBinding<
+ Map<Source, T>>) mapBinding.acceptTargetVisitor(new Visitor<Map<Source, T>>());
boolean keyMatch = false;
boolean optionalKeyMatch = false;
boolean optionalJavaxProviderKeyMatch = false;
boolean optionalProviderKeyMatch = false;
+ boolean mapBindingMatch = false;
boolean defaultMatch = false;
boolean actualMatch = false;
List<Object> otherOptionalBindings = Lists.newArrayList();
@@ -558,12 +566,19 @@
} else if (b.getKey().equals(optionalProviderKey)) {
assertTrue(contains);
optionalProviderKeyMatch = true;
+ } else if (b.getKey().equals(mapBinding.getKey())) {
+ assertTrue(contains);
+ mapBindingMatch = true;
+ // Validate that this binding is also a MapBinding.
+ assertEquals(mapbinderBinding, b.acceptTargetVisitor(visitor));
} else if (expectedDefault != null && matches(b, expectedDefault)) {
assertTrue(contains);
defaultMatch = true;
} else if (expectedActual != null && matches(b, expectedActual)) {
assertTrue(contains);
actualMatch = true;
+ } else if (mapbinderBinding.containsElement(b)) {
+ assertTrue(contains);
} else if (contains) {
otherMatches.add(b);
}
@@ -575,6 +590,7 @@
assertTrue(optionalKeyMatch);
assertTrue(optionalJavaxProviderKeyMatch);
assertTrue(optionalProviderKeyMatch);
+ assertTrue(mapBindingMatch);
assertEquals(expectedDefault != null, defaultMatch);
assertEquals(expectedActual != null, actualMatch);
assertEquals("other OptionalBindings found: " + otherOptionalBindings,
@@ -589,22 +605,27 @@
Map<Key<?>, Binding<?>> indexed = index(elements);
Key<Optional<T>> optionalKey =
keyType.ofType(OptionalBinder.optionalOf(keyType.getTypeLiteral()));
+ Key<Map<Source, T>> sourceMapKey =
+ keyType.ofType(mapOf(TypeLiteral.get(Source.class), keyType.getTypeLiteral()));
Visitor<Optional<T>> visitor = new Visitor<Optional<T>>();
OptionalBinderBinding<T> optionalBinder = null;
+ MapBinderBinding<Map<Source, T>> mapbinderBinding = null;
Key<?> defaultKey = null;
Key<?> actualKey = null;
Binding optionalBinding = indexed.get(optionalKey);
optionalBinder = (OptionalBinderBinding<T>) optionalBinding.acceptTargetVisitor(visitor);
+ Binding mapBinding = indexed.get(sourceMapKey);
+ mapbinderBinding = (MapBinderBinding<Map<Source, T>>) mapBinding.acceptTargetVisitor(visitor);
// Locate the defaultKey & actualKey
for (Element element : elements) {
if (optionalBinder.containsElement(element) && element instanceof Binding) {
Binding binding = (Binding) element;
if (isSourceEntry(binding, Source.DEFAULT)) {
- defaultKey = binding.getKey();
+ defaultKey = keyFromOptionalSourceBinding(binding, indexed);
} else if (isSourceEntry(binding, Source.ACTUAL)) {
- actualKey = binding.getKey();
+ actualKey = keyFromOptionalSourceBinding(binding, indexed);
}
}
}
@@ -620,6 +641,7 @@
boolean optionalKeyMatch = false;
boolean optionalJavaxProviderKeyMatch = false;
boolean optionalProviderKeyMatch = false;
+ boolean mapBindingMatch = false;
boolean defaultMatch = false;
boolean actualMatch = false;
List<Object> otherOptionalElements = Lists.newArrayList();
@@ -663,6 +685,11 @@
} else if (key != null && key.equals(optionalProviderKey)) {
assertTrue(contains);
optionalProviderKeyMatch = true;
+ } else if (key != null && key.equals(sourceMapKey)) {
+ assertTrue(contains);
+ mapBindingMatch = true;
+ // Validate that this binding is also a MapBinding.
+ assertEquals(mapbinderBinding, b.acceptTargetVisitor(visitor));
} else if (key != null && key.equals(defaultKey)) {
assertTrue(contains);
if (b != null) { // otherwise it might just be a ProviderLookup into it
@@ -676,6 +703,8 @@
assertTrue("expected: " + expectedActual + ", but was: " + b, matches(b, expectedActual));
actualMatch = true;
}
+ } else if (mapbinderBinding.containsElement(element)) {
+ assertTrue(contains);
} else if (contains) {
otherContains.add(element);
}
@@ -686,6 +715,7 @@
assertTrue(optionalKeyMatch);
assertTrue(optionalJavaxProviderKeyMatch);
assertTrue(optionalProviderKeyMatch);
+ assertTrue(mapBindingMatch);
assertEquals(expectedDefault != null, defaultMatch);
assertEquals(expectedActual != null, actualMatch);
assertEquals(otherContains.toString(), 0, otherContains.size());
@@ -695,16 +725,40 @@
// Validate that we can construct an injector out of the remaining bindings.
Guice.createInjector(Elements.getModule(nonContainedElements));
}
+
+ private static Key<?> keyFromOptionalSourceBinding(Binding<?> binding,
+ Map<Key<?>, Binding<?>> elements) {
+ // Flow is:
+ // binding == ProviderInstanceBinding<Map.Entry<Source, Provider<String>>
+ // dependency on: Provider<String> that maps to ProviderInstanceBinding<String> in MapBinder
+ // dependency on: Provider<String> of user set value.
+ Key<?> mapKey =
+ genericOf(getOnlyElement(((HasDependencies) binding).getDependencies()).getKey());
+ Binding<?> mapBinding = elements.get(mapKey);
+ Key<?> userKey =
+ genericOf(getOnlyElement(((HasDependencies) mapBinding).getDependencies()).getKey());
+ return userKey;
+ }
+
+ /** Returns {@code Key<T>} for something like {@code Key<Provider<T>>} */
+ private static Key<?> genericOf(Key<?> key) {
+ ParameterizedType type = (ParameterizedType) key.getTypeLiteral().getType();
+ assertEquals(1, type.getActualTypeArguments().length);
+ Key<?> result = key.ofType(type.getActualTypeArguments()[0]);
+ return result;
+ }
private static boolean isSourceEntry(Binding b, Source type) {
- switch(type) {
- case ACTUAL:
- return b.getKey().getAnnotation() instanceof OptionalBinder.Actual;
- case DEFAULT:
- return b.getKey().getAnnotation() instanceof OptionalBinder.Default;
- default:
- throw new IllegalStateException("invalid type: " + type);
+ if (b instanceof ProviderInstanceBinding && b.getKey().getAnnotation() instanceof RealElement) {
+ javax.inject.Provider provider = ((ProviderInstanceBinding) b).getUserSuppliedProvider();
+ if (provider instanceof Map.Entry) {
+ Map.Entry entry = (Map.Entry) provider;
+ if (entry.getKey() == type) {
+ return true;
+ }
+ }
}
+ return false;
}
/** Returns the subset of elements that have keys, indexed by them. */