Author: limpbizkit
Date: Mon Jun 22 23:30:37 2009
New Revision: 1034

Modified:
     
trunk/extensions/multibindings/src/com/google/inject/multibindings/MapBinder.java
     
trunk/extensions/multibindings/src/com/google/inject/multibindings/Multibinder.java
     
trunk/extensions/multibindings/test/com/google/inject/multibindings/MapBinderTest.java
     
trunk/extensions/multibindings/test/com/google/inject/multibindings/MultibinderTest.java

Log:
New API: Multibinder.permitDuplicates() and MapBinder.permitDuplicates()

When a map binder is configured to permit duplicate keys, the value chosen  
is arbitrary! This may be frustrating; but otherwise it would be necessary  
to actually call Provider.get() on the duplicated values to compare 'em; I  
think the cure would be worse than the disease

Modified:  
trunk/extensions/multibindings/src/com/google/inject/multibindings/MapBinder.java
==============================================================================
---  
trunk/extensions/multibindings/src/com/google/inject/multibindings/MapBinder.java
        
(original)
+++  
trunk/extensions/multibindings/src/com/google/inject/multibindings/MapBinder.java
        
Mon Jun 22 23:30:37 2009
@@ -18,6 +18,7 @@

  import com.google.inject.Binder;
  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;
@@ -195,6 +196,15 @@
    }

    /**
+   * Configures the bound map to silently discard duplicate entries. When  
multiple equal keys are
+   * bound, the value that gets included is arbitrary. When multible  
modules contribute elements to
+   * the map, this configuration option impacts all of them.
+   *
+   * @return this map binder
+   */
+  public abstract MapBinder<K, V> permitDuplicates();
+
+  /**
     * Returns a binding builder used to add a new entry in the map. Each
     * key must be distinct (and non-null). Bound providers will be  
evaluated each
     * time the map is injected.
@@ -250,6 +260,11 @@
        this.binder = binder;
      }

+    public MapBinder<K, V> permitDuplicates() {
+      entrySetBinder.permitDuplicates();
+      return this;
+    }
+
      /**
       * This creates two bindings. One for the {...@code Map.Entry<K,  
Provider<V>>}
       * and another for {...@code V}.
@@ -277,12 +292,14 @@
          private Map<K, Provider<V>> providerMap;

          @SuppressWarnings("unused")
-        @Inject void initialize() {
+        @Inject void initialize(Injector injector) {
            RealMapBinder.this.binder = null;
+          boolean permitDuplicates =  
entrySetBinder.permitsDuplicates(injector);

            Map<K, Provider<V>> providerMapMutable = new LinkedHashMap<K,  
Provider<V>>();
            for (Entry<K, Provider<V>> entry : entrySetProvider.get()) {
-            checkConfiguration(providerMapMutable.put(entry.getKey(),  
entry.getValue()) == null,
+            Provider<V> previous = providerMapMutable.put(entry.getKey(),  
entry.getValue());
+            checkConfiguration(previous == null || permitDuplicates,
                  "Map injection failed due to duplicated key \"%s\"",  
entry.getKey());
            }


Modified:  
trunk/extensions/multibindings/src/com/google/inject/multibindings/Multibinder.java
==============================================================================
---  
trunk/extensions/multibindings/src/com/google/inject/multibindings/Multibinder.java
      
(original)
+++  
trunk/extensions/multibindings/src/com/google/inject/multibindings/Multibinder.java
      
Mon Jun 22 23:30:37 2009
@@ -16,6 +16,7 @@

  package com.google.inject.multibindings;

+import com.google.inject.AbstractModule;
  import com.google.inject.Binder;
  import com.google.inject.Binding;
  import com.google.inject.ConfigurationException;
@@ -30,6 +31,7 @@
  import com.google.inject.internal.ImmutableList;
  import com.google.inject.internal.ImmutableSet;
  import com.google.inject.internal.Lists;
+import static com.google.inject.name.Names.named;
  import com.google.inject.spi.Dependency;
  import com.google.inject.spi.HasDependencies;
  import com.google.inject.spi.Message;
@@ -159,6 +161,15 @@
    }

    /**
+   * Configures the bound set to silently discard duplicate elements. When  
multiple equal values are
+   * bound, the one that gets included is arbitrary. When multible modules  
contribute elements to
+   * the set, this configuration option impacts all of them.
+   *
+   * @return this multibinder
+   */
+  public abstract Multibinder<T> permitDuplicates();
+
+  /**
     * Returns a binding builder used to add a new element in the set. Each
     * bound element must have a distinct value. Bound providers will be
     * evaluated each time the set is injected.
@@ -197,6 +208,7 @@
      private final TypeLiteral<T> elementType;
      private final String setName;
      private final Key<Set<T>> setKey;
+    private final Key<Boolean> permitDuplicatesKey;

      /* the target injector's binder. non-null until initialization, null  
afterwards */
      private Binder binder;
@@ -205,12 +217,16 @@
      private List<Provider<T>> providers;
      private Set<Dependency<?>> dependencies;

+    /** whether duplicates are allowed. Possibly configured by a different  
instance */
+    private boolean permitDuplicates;
+
      private RealMultibinder(Binder binder, TypeLiteral<T> elementType,
          String setName, Key<Set<T>> setKey) {
        this.binder = checkNotNull(binder, "binder");
        this.elementType = checkNotNull(elementType, "elementType");
        this.setName = checkNotNull(setName, "setName");
        this.setKey = checkNotNull(setKey, "setKey");
+      this.permitDuplicatesKey = Key.get(Boolean.class, named(toString()  
+ " permits duplicates"));
      }

      @SuppressWarnings("unchecked")
@@ -220,6 +236,11 @@
        binder.bind(setKey).toProvider(this);
      }

+    public Multibinder<T> permitDuplicates() {
+      binder.install(new PermitDuplicatesModule(permitDuplicatesKey));
+      return this;
+    }
+
      @Override public LinkedBindingBuilder<T> addBinding() {
        checkConfiguration(!isInitialized(), "Multibinder was already  
initialized");

@@ -245,9 +266,14 @@
        }

        this.dependencies = ImmutableSet.copyOf(dependencies);
+      this.permitDuplicates = permitsDuplicates(injector);
        this.binder = null;
      }

+    boolean permitsDuplicates(Injector injector) {
+      return injector.getBindings().containsKey(permitDuplicatesKey);
+    }
+
      private boolean keyMatches(Key<?> key) {
        return key.getTypeLiteral().equals(elementType)
            && key.getAnnotation() instanceof Element
@@ -265,7 +291,7 @@
        for (Provider<T> provider : providers) {
          final T newValue = provider.get();
          checkConfiguration(newValue != null, "Set injection failed due to  
null element");
-        checkConfiguration(result.add(newValue),
+        checkConfiguration(result.add(newValue) || permitDuplicates,
              "Set injection failed due to duplicated element \"%s\"",  
newValue);
        }
        return Collections.unmodifiableSet(result);
@@ -300,6 +326,32 @@
            .append(elementType)
            .append(">")
            .toString();
+    }
+  }
+
+  /**
+   * We install the permit duplicates configuration as its own binding,  
all by itself. This way,
+   * if only half of a multibinder user's remember to call  
permitDuplicates(), they're still
+   * permitted.
+   */
+  private static class PermitDuplicatesModule extends AbstractModule {
+    private final Key<Boolean> key;
+
+    PermitDuplicatesModule(Key<Boolean> key) {
+      this.key = key;
+    }
+
+    protected void configure() {
+      bind(key).toInstance(true);
+    }
+
+    @Override public boolean equals(Object o) {
+      return o instanceof PermitDuplicatesModule
+          && ((PermitDuplicatesModule) o).key.equals(key);
+    }
+
+    @Override public int hashCode() {
+      return getClass().hashCode() ^ key.hashCode();
      }
    }


Modified:  
trunk/extensions/multibindings/test/com/google/inject/multibindings/MapBinderTest.java
==============================================================================
---  
trunk/extensions/multibindings/test/com/google/inject/multibindings/MapBinderTest.java
   
(original)
+++  
trunk/extensions/multibindings/test/com/google/inject/multibindings/MapBinderTest.java
   
Mon Jun 22 23:30:37 2009
@@ -233,6 +233,29 @@
      }
    }

+  public void testMapBinderMapPermitDuplicateElements() {
+    Injector injector = Guice.createInjector(
+        new AbstractModule() {
+          @Override protected void configure() {
+            MapBinder<String, String> multibinder = MapBinder.newMapBinder(
+                binder(), String.class, String.class);
+            multibinder.addBinding("a").toInstance("A");
+            multibinder.addBinding("b").toInstance("B");
+          }
+        },
+        new AbstractModule() {
+          @Override protected void configure() {
+            MapBinder<String, String> multibinder = MapBinder.newMapBinder(
+                binder(), String.class, String.class);
+            multibinder.addBinding("b").toInstance("B");
+            multibinder.addBinding("c").toInstance("C");
+            multibinder.permitDuplicates();
+          }
+        });
+
+    assertEquals(mapOf("a", "A", "b", "B", "c", "C"),  
injector.getInstance(Key.get(mapOfString)));
+  }
+
    public void testMapBinderMapForbidsNullKeys() {
      try {
        Guice.createInjector(new AbstractModule() {

Modified:  
trunk/extensions/multibindings/test/com/google/inject/multibindings/MultibinderTest.java
==============================================================================
---  
trunk/extensions/multibindings/test/com/google/inject/multibindings/MultibinderTest.java
         
(original)
+++  
trunk/extensions/multibindings/test/com/google/inject/multibindings/MultibinderTest.java
         
Mon Jun 22 23:30:37 2009
@@ -229,6 +229,49 @@
            "1) Set injection failed due to duplicated element \"A\"");
      }
    }
+
+  public void testMultibinderSetPermitDuplicateElements() {
+    Injector injector = Guice.createInjector(
+        new AbstractModule() {
+          protected void configure() {
+            Multibinder<String> multibinder =  
Multibinder.newSetBinder(binder(), String.class);
+            multibinder.addBinding().toInstance("A");
+            multibinder.addBinding().toInstance("B");
+          }
+        },
+        new AbstractModule() {
+          protected void configure() {
+            Multibinder<String> multibinder =  
Multibinder.newSetBinder(binder(), String.class);
+            multibinder.permitDuplicates();
+            multibinder.addBinding().toInstance("B");
+            multibinder.addBinding().toInstance("C");
+          }
+        });
+
+    assertEquals(setOf("A", "B", "C"),  
injector.getInstance(Key.get(setOfString)));
+  }
+
+  public void testMultibinderSetPermitDuplicateCallsToPermitDuplicates() {
+    Injector injector = Guice.createInjector(
+        new AbstractModule() {
+          protected void configure() {
+            Multibinder<String> multibinder =  
Multibinder.newSetBinder(binder(), String.class);
+            multibinder.permitDuplicates();
+            multibinder.addBinding().toInstance("A");
+            multibinder.addBinding().toInstance("B");
+          }
+        },
+        new AbstractModule() {
+          protected void configure() {
+            Multibinder<String> multibinder =  
Multibinder.newSetBinder(binder(), String.class);
+            multibinder.permitDuplicates();
+            multibinder.addBinding().toInstance("B");
+            multibinder.addBinding().toInstance("C");
+          }
+        });
+
+    assertEquals(setOf("A", "B", "C"),  
injector.getInstance(Key.get(setOfString)));
+  }

    public void testMultibinderSetForbidsNullElements() {
      Injector injector = Guice.createInjector(new AbstractModule() {

--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google Groups 
"google-guice-dev" group.
To post to this group, send email to [email protected]
To unsubscribe from this group, send email to 
[email protected]
For more options, visit this group at 
http://groups.google.com/group/google-guice-dev?hl=en
-~----------~----~----~----~------~----~------~--~---

Reply via email to