Fix issue 670, keep values from MapBinder & Multibinder distinct.
diff --git a/extensions/multibindings/src/com/google/inject/multibindings/Element.java b/extensions/multibindings/src/com/google/inject/multibindings/Element.java
index 3149865..21126ea 100644
--- a/extensions/multibindings/src/com/google/inject/multibindings/Element.java
+++ b/extensions/multibindings/src/com/google/inject/multibindings/Element.java
@@ -32,6 +32,13 @@
*/
@Retention(RUNTIME) @BindingAnnotation
@interface Element {
+
+ enum Type {
+ MAPBINDER,
+ MULTIBINDER;
+ }
+
String setName();
int uniqueId();
+ Type type();
}
diff --git a/extensions/multibindings/src/com/google/inject/multibindings/MapBinder.java b/extensions/multibindings/src/com/google/inject/multibindings/MapBinder.java
index 9a15efd..7c71e75 100644
--- a/extensions/multibindings/src/com/google/inject/multibindings/MapBinder.java
+++ b/extensions/multibindings/src/com/google/inject/multibindings/MapBinder.java
@@ -16,11 +16,20 @@
package com.google.inject.multibindings;
+import static com.google.inject.multibindings.Element.Type.MAPBINDER;
import static com.google.inject.multibindings.Multibinder.checkConfiguration;
import static com.google.inject.multibindings.Multibinder.checkNotNull;
import static com.google.inject.multibindings.Multibinder.setOf;
import static com.google.inject.util.Types.newParameterizedTypeWithOwner;
+import java.lang.annotation.Annotation;
+import java.util.Collections;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
@@ -44,14 +53,6 @@
import com.google.inject.spi.Toolable;
import com.google.inject.util.Types;
-import java.lang.annotation.Annotation;
-import java.util.Collections;
-import java.util.LinkedHashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Map.Entry;
-import java.util.Set;
-
/**
* An API to bind multiple map entries separately, only to later inject them as
* a complete map. MapBinder is intended for use in your application's module:
@@ -333,7 +334,7 @@
checkNotNull(key, "key");
checkConfiguration(!isInitialized(), "MapBinder was already initialized");
- Key<V> valueKey = Key.get(valueType, new RealElement(entrySetBinder.getSetName()));
+ Key<V> valueKey = Key.get(valueType, new RealElement(entrySetBinder.getSetName(), MAPBINDER));
entrySetBinder.addBinding().toInstance(new MapEntry<K, Provider<V>>(key,
binder.getProvider(valueKey), valueKey));
return binder.bind(valueKey);
@@ -465,6 +466,7 @@
private boolean matchesValueKey(Key key) {
return key.getAnnotation() instanceof Element
&& ((Element) key.getAnnotation()).setName().equals(entrySetBinder.getSetName())
+ && ((Element) key.getAnnotation()).type() == MAPBINDER
&& key.getTypeLiteral().equals(valueType);
}
diff --git a/extensions/multibindings/src/com/google/inject/multibindings/Multibinder.java b/extensions/multibindings/src/com/google/inject/multibindings/Multibinder.java
index a066e33..59fe82d 100644
--- a/extensions/multibindings/src/com/google/inject/multibindings/Multibinder.java
+++ b/extensions/multibindings/src/com/google/inject/multibindings/Multibinder.java
@@ -16,8 +16,16 @@
package com.google.inject.multibindings;
+import static com.google.inject.multibindings.Element.Type.MULTIBINDER;
import static com.google.inject.name.Names.named;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Type;
+import java.util.Collections;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Set;
+
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Lists;
@@ -42,13 +50,6 @@
import com.google.inject.spi.Toolable;
import com.google.inject.util.Types;
-import java.lang.annotation.Annotation;
-import java.lang.reflect.Type;
-import java.util.Collections;
-import java.util.LinkedHashSet;
-import java.util.List;
-import java.util.Set;
-
/**
* An API to bind multiple values separately, only to later inject them as a
* complete collection. Multibinder is intended for use in your application's
@@ -263,7 +264,6 @@
}
}
- @SuppressWarnings("unchecked")
public void configure(Binder binder) {
checkConfiguration(!isInitialized(), "Multibinder was already initialized");
@@ -279,7 +279,7 @@
@Override public LinkedBindingBuilder<T> addBinding() {
checkConfiguration(!isInitialized(), "Multibinder was already initialized");
- return binder.bind(Key.get(elementType, new RealElement(setName)));
+ return binder.bind(Key.get(elementType, new RealElement(setName, MULTIBINDER)));
}
/**
@@ -312,7 +312,8 @@
private boolean keyMatches(Key<?> key) {
return key.getTypeLiteral().equals(elementType)
&& key.getAnnotation() instanceof Element
- && ((Element) key.getAnnotation()).setName().equals(setName);
+ && ((Element) key.getAnnotation()).setName().equals(setName)
+ && ((Element) key.getAnnotation()).type() == MULTIBINDER;
}
private boolean isInitialized() {
diff --git a/extensions/multibindings/src/com/google/inject/multibindings/RealElement.java b/extensions/multibindings/src/com/google/inject/multibindings/RealElement.java
index d297f90..cf7b382 100644
--- a/extensions/multibindings/src/com/google/inject/multibindings/RealElement.java
+++ b/extensions/multibindings/src/com/google/inject/multibindings/RealElement.java
@@ -27,10 +27,12 @@
private final int uniqueId;
private final String setName;
+ private final Type type;
- RealElement(String setName) {
+ RealElement(String setName, Type type) {
uniqueId = nextUniqueId.getAndIncrement();
this.setName = setName;
+ this.type = type;
}
public String setName() {
@@ -40,6 +42,10 @@
public int uniqueId() {
return uniqueId;
}
+
+ public Type type() {
+ return type;
+ }
public Class<? extends Annotation> annotationType() {
return Element.class;
@@ -47,17 +53,19 @@
@Override public String toString() {
return "@" + Element.class.getName() + "(setName=" + setName
- + ",uniqueId=" + uniqueId + ")";
+ + ",uniqueId=" + uniqueId + ", type=" + type + ")";
}
@Override public boolean equals(Object o) {
return o instanceof Element
&& ((Element) o).setName().equals(setName())
- && ((Element) o).uniqueId() == uniqueId();
+ && ((Element) o).uniqueId() == uniqueId()
+ && ((Element) o).type() == type();
}
@Override public int hashCode() {
return 127 * ("setName".hashCode() ^ setName.hashCode())
- + 127 * ("uniqueId".hashCode() ^ uniqueId);
+ + 127 * ("uniqueId".hashCode() ^ uniqueId)
+ + 127 * ("type".hashCode() ^ type.hashCode());
}
}
diff --git a/extensions/multibindings/test/com/google/inject/multibindings/MultibinderTest.java b/extensions/multibindings/test/com/google/inject/multibindings/MultibinderTest.java
index 652fb89..4e1d3f1 100644
--- a/extensions/multibindings/test/com/google/inject/multibindings/MultibinderTest.java
+++ b/extensions/multibindings/test/com/google/inject/multibindings/MultibinderTest.java
@@ -41,12 +41,14 @@
import com.google.inject.Stage;
import com.google.inject.TypeLiteral;
import com.google.inject.name.Names;
+import com.google.inject.spi.DefaultBindingTargetVisitor;
import com.google.inject.spi.Dependency;
import com.google.inject.spi.HasDependencies;
import com.google.inject.spi.InstanceBinding;
import com.google.inject.spi.LinkedKeyBinding;
import com.google.inject.util.Modules;
import com.google.inject.util.Providers;
+import com.google.inject.util.Types;
import junit.framework.TestCase;
@@ -60,6 +62,7 @@
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
+import java.util.Map;
import java.util.Set;
/**
@@ -67,6 +70,8 @@
*/
public class MultibinderTest extends TestCase {
+ final TypeLiteral<Map<String, String>> mapOfStringString =
+ new TypeLiteral<Map<String, String>>() {};
final TypeLiteral<Set<String>> setOfString = new TypeLiteral<Set<String>>() {};
final TypeLiteral<Set<Integer>> setOfInteger = new TypeLiteral<Set<Integer>>() {};
final TypeLiteral<String> stringType = TypeLiteral.get(String.class);
@@ -600,7 +605,8 @@
assertEquals(expected, s1);
}
- public void failing_testSetAndMapValueConflict() {
+ // See issue 670
+ public void testSetAndMapValueAreDistinct() {
Injector injector = Guice.createInjector(new AbstractModule() {
@Override protected void configure() {
Multibinder.newSetBinder(binder(), String.class)
@@ -613,4 +619,61 @@
assertEquals(ImmutableSet.<String>of("A"), injector.getInstance(Key.get(setOfString)));
}
+
+ // See issue 670
+ public void testSetAndMapValueAreDistinctInSpi() {
+ Injector injector = Guice.createInjector(new AbstractModule() {
+ @Override protected void configure() {
+ Multibinder.newSetBinder(binder(), String.class)
+ .addBinding().toInstance("A");
+
+ MapBinder.newMapBinder(binder(), String.class, String.class)
+ .addBinding("B").toInstance("b");
+ }
+ });
+ Collector collector = new Collector();
+ Binding<Map<String, String>> mapbinding = injector.getBinding(Key.get(mapOfStringString));
+ mapbinding.acceptTargetVisitor(collector);
+ assertNotNull(collector.mapbinding);
+
+ Binding<Set<String>> setbinding = injector.getBinding(Key.get(setOfString));
+ setbinding.acceptTargetVisitor(collector);
+ assertNotNull(collector.setbinding);
+
+ // Capture the value bindings and assert we have them right --
+ // they'll always be in the right order because we preserve
+ // binding order.
+ List<Binding<String>> bindings = injector.findBindingsByType(stringType);
+ assertEquals(2, bindings.size());
+ Binding<String> a = bindings.get(0);
+ Binding<String> b = bindings.get(1);
+ assertEquals("A", ((InstanceBinding<String>)a).getInstance());
+ assertEquals("b", ((InstanceBinding<String>)b).getInstance());
+
+ // Now make sure "A" belongs only to the set binding,
+ // and "b" only belongs to the map binding.
+ assertFalse(collector.mapbinding.containsElement(a));
+ assertTrue(collector.mapbinding.containsElement(b));
+
+ assertTrue(collector.setbinding.containsElement(a));
+ assertFalse(collector.setbinding.containsElement(b));
+ }
+
+ private static class Collector extends DefaultBindingTargetVisitor<Object, Object> implements
+ MultibindingsTargetVisitor<Object, Object> {
+ MapBinderBinding<? extends Object> mapbinding;
+ MultibinderBinding<? extends Object> setbinding;
+
+ @Override
+ public Object visit(MapBinderBinding<? extends Object> mapbinding) {
+ this.mapbinding = mapbinding;
+ return null;
+ }
+
+ @Override
+ public Object visit(MultibinderBinding<? extends Object> multibinding) {
+ this.setbinding = multibinding;
+ return null;
+ }
+ }
}