Home | History | Annotate | Download | only in internal
      1 /*
      2  * Copyright (C) 2008 Google Inc.
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  * http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.google.inject.internal;
     18 
     19 import static com.google.inject.Asserts.asModuleChain;
     20 import static com.google.inject.Asserts.assertContains;
     21 import static com.google.inject.internal.SpiUtils.VisitType.BOTH;
     22 import static com.google.inject.internal.SpiUtils.VisitType.MODULE;
     23 import static com.google.inject.internal.SpiUtils.assertMapVisitor;
     24 import static com.google.inject.internal.SpiUtils.instance;
     25 import static com.google.inject.internal.SpiUtils.providerInstance;
     26 import static com.google.inject.name.Names.named;
     27 import static java.lang.annotation.RetentionPolicy.RUNTIME;
     28 
     29 import com.google.common.base.Function;
     30 import com.google.common.collect.ImmutableSet;
     31 import com.google.common.collect.Iterables;
     32 import com.google.common.collect.Lists;
     33 import com.google.common.collect.Maps;
     34 import com.google.common.collect.Sets;
     35 import com.google.inject.AbstractModule;
     36 import com.google.inject.Asserts;
     37 import com.google.inject.Binding;
     38 import com.google.inject.BindingAnnotation;
     39 import com.google.inject.ConfigurationException;
     40 import com.google.inject.CreationException;
     41 import com.google.inject.Guice;
     42 import com.google.inject.Inject;
     43 import com.google.inject.Injector;
     44 import com.google.inject.Key;
     45 import com.google.inject.Module;
     46 import com.google.inject.Provider;
     47 import com.google.inject.Provides;
     48 import com.google.inject.ProvisionException;
     49 import com.google.inject.Stage;
     50 import com.google.inject.TypeLiteral;
     51 import com.google.inject.internal.RealMapBinder.ProviderMapEntry;
     52 import com.google.inject.multibindings.MapBinder;
     53 import com.google.inject.multibindings.MapBinderBinding;
     54 import com.google.inject.name.Names;
     55 import com.google.inject.spi.DefaultElementVisitor;
     56 import com.google.inject.spi.Dependency;
     57 import com.google.inject.spi.Elements;
     58 import com.google.inject.spi.HasDependencies;
     59 import com.google.inject.spi.InstanceBinding;
     60 import com.google.inject.spi.ProviderInstanceBinding;
     61 import com.google.inject.util.Modules;
     62 import com.google.inject.util.Providers;
     63 import com.google.inject.util.Types;
     64 import java.lang.annotation.Annotation;
     65 import java.lang.annotation.ElementType;
     66 import java.lang.annotation.Retention;
     67 import java.lang.annotation.RetentionPolicy;
     68 import java.lang.annotation.Target;
     69 import java.lang.ref.WeakReference;
     70 import java.lang.reflect.Method;
     71 import java.lang.reflect.Type;
     72 import java.util.Arrays;
     73 import java.util.Collection;
     74 import java.util.Collections;
     75 import java.util.HashMap;
     76 import java.util.HashSet;
     77 import java.util.Iterator;
     78 import java.util.List;
     79 import java.util.Map;
     80 import java.util.Set;
     81 import java.util.concurrent.atomic.AtomicReference;
     82 import junit.framework.TestCase;
     83 
     84 /** @author dpb (at) google.com (David P. Baker) */
     85 public class MapBinderTest extends TestCase {
     86 
     87   private static final ImmutableSet<Key<?>> FRAMEWORK_KEYS =
     88       ImmutableSet.of(
     89           Key.get(java.util.logging.Logger.class), Key.get(Stage.class), Key.get(Injector.class));
     90 
     91   final TypeLiteral<Map<String, javax.inject.Provider<String>>> mapOfStringJavaxProvider =
     92       new TypeLiteral<Map<String, javax.inject.Provider<String>>>() {};
     93   final TypeLiteral<Map<String, Provider<String>>> mapOfStringProvider =
     94       new TypeLiteral<Map<String, Provider<String>>>() {};
     95   final TypeLiteral<Map<String, String>> mapOfString = new TypeLiteral<Map<String, String>>() {};
     96   final TypeLiteral<Map<Integer, String>> mapOfIntString =
     97       new TypeLiteral<Map<Integer, String>>() {};
     98   final TypeLiteral<Map<String, Integer>> mapOfInteger = new TypeLiteral<Map<String, Integer>>() {};
     99   final TypeLiteral<Map<String, Set<String>>> mapOfSetOfString =
    100       new TypeLiteral<Map<String, Set<String>>>() {};
    101 
    102   private final TypeLiteral<String> stringType = TypeLiteral.get(String.class);
    103   private final TypeLiteral<Integer> intType = TypeLiteral.get(Integer.class);
    104 
    105   private Type javaxProviderOf(Type type) {
    106     return Types.javaxProviderOf(type);
    107   }
    108 
    109   private Type mapEntryOf(Type keyType, Type valueType) {
    110     return Types.newParameterizedTypeWithOwner(Map.class, Map.Entry.class, keyType, valueType);
    111   }
    112 
    113   private Type collectionOf(Type type) {
    114     return Types.newParameterizedType(Collection.class, type);
    115   }
    116 
    117   public void testAllBindings() {
    118     Module module =
    119         new AbstractModule() {
    120           @Override
    121           protected void configure() {
    122             MapBinder.newMapBinder(binder(), String.class, String.class).permitDuplicates();
    123           }
    124         };
    125 
    126     Injector injector = Guice.createInjector(module);
    127 
    128     Map<Key<?>, Binding<?>> bindings = injector.getBindings();
    129 
    130     ImmutableSet<Key<?>> expectedBindings =
    131         ImmutableSet.<Key<?>>builder()
    132             .add(
    133                 // Map<K, V>
    134                 Key.get(Types.mapOf(String.class, String.class)),
    135                 // Map<K, Provider<V>>
    136                 Key.get(Types.mapOf(String.class, Types.providerOf(String.class))),
    137                 // Map<K, javax.inject.Provider<V>>
    138                 Key.get(Types.mapOf(String.class, javaxProviderOf(String.class))),
    139                 // Map<K, Set<V>>
    140                 Key.get(Types.mapOf(String.class, Types.setOf(String.class))),
    141                 // Map<K, Set<Provider<V>>
    142                 Key.get(Types.mapOf(String.class, Types.setOf(Types.providerOf(String.class)))),
    143                 // Map<K, Set<javax.inject.Provider<V>>
    144                 Key.get(
    145                     Types.mapOf(String.class, Types.setOf(Types.javaxProviderOf(String.class)))),
    146                 // Map<K, Collection<Provider<V>>
    147                 Key.get(
    148                     Types.mapOf(String.class, Types.collectionOf(Types.providerOf(String.class)))),
    149                 // Map<K, Collection<javax.inject.Provider<V>>
    150                 Key.get(
    151                     Types.mapOf(
    152                         String.class, Types.collectionOf(Types.javaxProviderOf(String.class)))),
    153                 // Set<Map.Entry<K, Provider<V>>>
    154                 Key.get(Types.setOf(mapEntryOf(String.class, Types.providerOf(String.class)))),
    155                 // Set<Map.Entry<K, javax.inject.Provider<V>>>
    156                 Key.get(Types.setOf(mapEntryOf(String.class, Types.javaxProviderOf(String.class)))),
    157                 // Collection<Provider<Map.Entry<K, Provider<V>>>>
    158                 Key.get(
    159                     collectionOf(
    160                         Types.providerOf(
    161                             mapEntryOf(String.class, Types.providerOf(String.class))))),
    162                 // Collection<javax.inject.Provider<Map.Entry<K, Provider<V>>>>
    163                 Key.get(
    164                     collectionOf(
    165                         Types.javaxProviderOf(
    166                             mapEntryOf(String.class, Types.providerOf(String.class))))),
    167                 // @Named(...) Boolean
    168                 Key.get(
    169                     Boolean.class,
    170                     named(
    171                         "Multibinder<java.util.Map$Entry<java.lang.String, "
    172                             + "com.google.inject.Provider<java.lang.String>>> permits duplicates")))
    173             .addAll(FRAMEWORK_KEYS)
    174             .build();
    175 
    176     Set<Key<?>> missingBindings = Sets.difference(expectedBindings, bindings.keySet());
    177     Set<Key<?>> extraBindings = Sets.difference(bindings.keySet(), expectedBindings);
    178 
    179     assertTrue(
    180         "There should be no missing bindings. Missing: " + missingBindings,
    181         missingBindings.isEmpty());
    182     assertTrue(
    183         "There should be no extra bindings. Extra: " + extraBindings, extraBindings.isEmpty());
    184   }
    185 
    186   public void testMapBinderAggregatesMultipleModules() {
    187     Module abc =
    188         new AbstractModule() {
    189           @Override
    190           protected void configure() {
    191             MapBinder<String, String> multibinder =
    192                 MapBinder.newMapBinder(binder(), String.class, String.class);
    193             multibinder.addBinding("a").toInstance("A");
    194             multibinder.addBinding("b").toInstance("B");
    195             multibinder.addBinding("c").toInstance("C");
    196           }
    197         };
    198     Module de =
    199         new AbstractModule() {
    200           @Override
    201           protected void configure() {
    202             MapBinder<String, String> multibinder =
    203                 MapBinder.newMapBinder(binder(), String.class, String.class);
    204             multibinder.addBinding("d").toInstance("D");
    205             multibinder.addBinding("e").toInstance("E");
    206           }
    207         };
    208 
    209     Injector injector = Guice.createInjector(abc, de);
    210     Map<String, String> abcde = injector.getInstance(Key.get(mapOfString));
    211 
    212     assertEquals(mapOf("a", "A", "b", "B", "c", "C", "d", "D", "e", "E"), abcde);
    213     assertMapVisitor(
    214         Key.get(mapOfString),
    215         stringType,
    216         stringType,
    217         setOf(abc, de),
    218         BOTH,
    219         false,
    220         0,
    221         instance("a", "A"),
    222         instance("b", "B"),
    223         instance("c", "C"),
    224         instance("d", "D"),
    225         instance("e", "E"));
    226 
    227     // just make sure these succeed
    228     injector.getInstance(Key.get(mapOfStringProvider));
    229     injector.getInstance(Key.get(mapOfStringJavaxProvider));
    230   }
    231 
    232   public void testMapBinderAggregationForAnnotationInstance() {
    233     Module module =
    234         new AbstractModule() {
    235           @Override
    236           protected void configure() {
    237             MapBinder<String, String> multibinder =
    238                 MapBinder.newMapBinder(binder(), String.class, String.class, Names.named("abc"));
    239             multibinder.addBinding("a").toInstance("A");
    240             multibinder.addBinding("b").toInstance("B");
    241 
    242             multibinder =
    243                 MapBinder.newMapBinder(binder(), String.class, String.class, Names.named("abc"));
    244             multibinder.addBinding("c").toInstance("C");
    245           }
    246         };
    247     Injector injector = Guice.createInjector(module);
    248 
    249     Key<Map<String, String>> key = Key.get(mapOfString, Names.named("abc"));
    250     Map<String, String> abc = injector.getInstance(key);
    251     assertEquals(mapOf("a", "A", "b", "B", "c", "C"), abc);
    252     assertMapVisitor(
    253         key,
    254         stringType,
    255         stringType,
    256         setOf(module),
    257         BOTH,
    258         false,
    259         0,
    260         instance("a", "A"),
    261         instance("b", "B"),
    262         instance("c", "C"));
    263 
    264     // just make sure these succeed
    265     injector.getInstance(Key.get(mapOfStringProvider, Names.named("abc")));
    266     injector.getInstance(Key.get(mapOfStringJavaxProvider, Names.named("abc")));
    267   }
    268 
    269   public void testMapBinderAggregationForAnnotationType() {
    270     Module module =
    271         new AbstractModule() {
    272           @Override
    273           protected void configure() {
    274             MapBinder<String, String> multibinder =
    275                 MapBinder.newMapBinder(binder(), String.class, String.class, Abc.class);
    276             multibinder.addBinding("a").toInstance("A");
    277             multibinder.addBinding("b").toInstance("B");
    278 
    279             multibinder = MapBinder.newMapBinder(binder(), String.class, String.class, Abc.class);
    280             multibinder.addBinding("c").toInstance("C");
    281           }
    282         };
    283     Injector injector = Guice.createInjector(module);
    284 
    285     Key<Map<String, String>> key = Key.get(mapOfString, Abc.class);
    286     Map<String, String> abc = injector.getInstance(key);
    287     assertEquals(mapOf("a", "A", "b", "B", "c", "C"), abc);
    288     assertMapVisitor(
    289         key,
    290         stringType,
    291         stringType,
    292         setOf(module),
    293         BOTH,
    294         false,
    295         0,
    296         instance("a", "A"),
    297         instance("b", "B"),
    298         instance("c", "C"));
    299 
    300     // just make sure these succeed
    301     injector.getInstance(Key.get(mapOfStringProvider, Abc.class));
    302     injector.getInstance(Key.get(mapOfStringJavaxProvider, Abc.class));
    303   }
    304 
    305   public void testMapBinderWithMultipleAnnotationValueSets() {
    306     Module module =
    307         new AbstractModule() {
    308           @Override
    309           protected void configure() {
    310             MapBinder<String, String> abcMapBinder =
    311                 MapBinder.newMapBinder(binder(), String.class, String.class, named("abc"));
    312             abcMapBinder.addBinding("a").toInstance("A");
    313             abcMapBinder.addBinding("b").toInstance("B");
    314             abcMapBinder.addBinding("c").toInstance("C");
    315 
    316             MapBinder<String, String> deMapBinder =
    317                 MapBinder.newMapBinder(binder(), String.class, String.class, named("de"));
    318             deMapBinder.addBinding("d").toInstance("D");
    319             deMapBinder.addBinding("e").toInstance("E");
    320           }
    321         };
    322     Injector injector = Guice.createInjector(module);
    323 
    324     Key<Map<String, String>> abcKey = Key.get(mapOfString, named("abc"));
    325     Map<String, String> abc = injector.getInstance(abcKey);
    326     Key<Map<String, String>> deKey = Key.get(mapOfString, named("de"));
    327     Map<String, String> de = injector.getInstance(deKey);
    328     assertEquals(mapOf("a", "A", "b", "B", "c", "C"), abc);
    329     assertEquals(mapOf("d", "D", "e", "E"), de);
    330     assertMapVisitor(
    331         abcKey,
    332         stringType,
    333         stringType,
    334         setOf(module),
    335         BOTH,
    336         false,
    337         1,
    338         instance("a", "A"),
    339         instance("b", "B"),
    340         instance("c", "C"));
    341     assertMapVisitor(
    342         deKey,
    343         stringType,
    344         stringType,
    345         setOf(module),
    346         BOTH,
    347         false,
    348         1,
    349         instance("d", "D"),
    350         instance("e", "E"));
    351 
    352     // just make sure these succeed
    353     injector.getInstance(Key.get(mapOfStringProvider, named("abc")));
    354     injector.getInstance(Key.get(mapOfStringJavaxProvider, named("abc")));
    355     injector.getInstance(Key.get(mapOfStringProvider, named("de")));
    356     injector.getInstance(Key.get(mapOfStringJavaxProvider, named("de")));
    357   }
    358 
    359   public void testMapBinderWithMultipleAnnotationTypeSets() {
    360     Module module =
    361         new AbstractModule() {
    362           @Override
    363           protected void configure() {
    364             MapBinder<String, String> abcMapBinder =
    365                 MapBinder.newMapBinder(binder(), String.class, String.class, Abc.class);
    366             abcMapBinder.addBinding("a").toInstance("A");
    367             abcMapBinder.addBinding("b").toInstance("B");
    368             abcMapBinder.addBinding("c").toInstance("C");
    369 
    370             MapBinder<String, String> deMapBinder =
    371                 MapBinder.newMapBinder(binder(), String.class, String.class, De.class);
    372             deMapBinder.addBinding("d").toInstance("D");
    373             deMapBinder.addBinding("e").toInstance("E");
    374           }
    375         };
    376     Injector injector = Guice.createInjector(module);
    377 
    378     Key<Map<String, String>> abcKey = Key.get(mapOfString, Abc.class);
    379     Map<String, String> abc = injector.getInstance(abcKey);
    380     Key<Map<String, String>> deKey = Key.get(mapOfString, De.class);
    381     Map<String, String> de = injector.getInstance(deKey);
    382     assertEquals(mapOf("a", "A", "b", "B", "c", "C"), abc);
    383     assertEquals(mapOf("d", "D", "e", "E"), de);
    384     assertMapVisitor(
    385         abcKey,
    386         stringType,
    387         stringType,
    388         setOf(module),
    389         BOTH,
    390         false,
    391         1,
    392         instance("a", "A"),
    393         instance("b", "B"),
    394         instance("c", "C"));
    395     assertMapVisitor(
    396         deKey,
    397         stringType,
    398         stringType,
    399         setOf(module),
    400         BOTH,
    401         false,
    402         1,
    403         instance("d", "D"),
    404         instance("e", "E"));
    405 
    406     // just make sure these succeed
    407     injector.getInstance(Key.get(mapOfStringProvider, Abc.class));
    408     injector.getInstance(Key.get(mapOfStringJavaxProvider, Abc.class));
    409     injector.getInstance(Key.get(mapOfStringProvider, De.class));
    410     injector.getInstance(Key.get(mapOfStringJavaxProvider, De.class));
    411   }
    412 
    413   public void testMapBinderWithMultipleTypes() {
    414     Module module =
    415         new AbstractModule() {
    416           @Override
    417           protected void configure() {
    418             MapBinder.newMapBinder(binder(), String.class, String.class)
    419                 .addBinding("a")
    420                 .toInstance("A");
    421             MapBinder.newMapBinder(binder(), String.class, Integer.class)
    422                 .addBinding("1")
    423                 .toInstance(1);
    424           }
    425         };
    426     Injector injector = Guice.createInjector(module);
    427 
    428     assertEquals(mapOf("a", "A"), injector.getInstance(Key.get(mapOfString)));
    429     assertEquals(mapOf("1", 1), injector.getInstance(Key.get(mapOfInteger)));
    430     assertMapVisitor(
    431         Key.get(mapOfString),
    432         stringType,
    433         stringType,
    434         setOf(module),
    435         BOTH,
    436         false,
    437         1,
    438         instance("a", "A"));
    439     assertMapVisitor(
    440         Key.get(mapOfInteger),
    441         stringType,
    442         intType,
    443         setOf(module),
    444         BOTH,
    445         false,
    446         1,
    447         instance("1", 1));
    448   }
    449 
    450   public void testMapBinderWithEmptyMap() {
    451     Module module =
    452         new AbstractModule() {
    453           @Override
    454           protected void configure() {
    455             MapBinder.newMapBinder(binder(), String.class, String.class);
    456           }
    457         };
    458     Injector injector = Guice.createInjector(module);
    459 
    460     Map<String, String> map = injector.getInstance(Key.get(mapOfString));
    461     assertEquals(Collections.emptyMap(), map);
    462     assertMapVisitor(Key.get(mapOfString), stringType, stringType, setOf(module), BOTH, false, 0);
    463   }
    464 
    465   public void testMapBinderMapIsUnmodifiable() {
    466     Injector injector =
    467         Guice.createInjector(
    468             new AbstractModule() {
    469               @Override
    470               protected void configure() {
    471                 MapBinder.newMapBinder(binder(), String.class, String.class)
    472                     .addBinding("a")
    473                     .toInstance("A");
    474               }
    475             });
    476 
    477     Map<String, String> map = injector.getInstance(Key.get(mapOfString));
    478     try {
    479       map.clear();
    480       fail();
    481     } catch (UnsupportedOperationException expected) {
    482     }
    483   }
    484 
    485   public void testMapBinderMapIsLazy() {
    486     Module module =
    487         new AbstractModule() {
    488           @Override
    489           protected void configure() {
    490             MapBinder.newMapBinder(binder(), String.class, Integer.class)
    491                 .addBinding("num")
    492                 .toProvider(
    493                     new Provider<Integer>() {
    494                       int nextValue = 1;
    495 
    496                       @Override
    497                       public Integer get() {
    498                         return nextValue++;
    499                       }
    500                     });
    501           }
    502         };
    503     Injector injector = Guice.createInjector(module);
    504 
    505     assertEquals(mapOf("num", 1), injector.getInstance(Key.get(mapOfInteger)));
    506     assertEquals(mapOf("num", 2), injector.getInstance(Key.get(mapOfInteger)));
    507     assertEquals(mapOf("num", 3), injector.getInstance(Key.get(mapOfInteger)));
    508     assertMapVisitor(
    509         Key.get(mapOfInteger),
    510         stringType,
    511         intType,
    512         setOf(module),
    513         BOTH,
    514         false,
    515         0,
    516         providerInstance("num", 1));
    517   }
    518 
    519   public void testMapBinderMapForbidsDuplicateKeys() {
    520     Module module =
    521         new AbstractModule() {
    522           @Override
    523           protected void configure() {
    524             MapBinder<String, String> multibinder =
    525                 MapBinder.newMapBinder(binder(), String.class, String.class);
    526             multibinder.addBinding("a").toInstance("A");
    527             multibinder.addBinding("a").toInstance("B");
    528           }
    529         };
    530     try {
    531       Guice.createInjector(module);
    532       fail();
    533     } catch (CreationException expected) {
    534       assertContains(expected.getMessage(), "Map injection failed due to duplicated key \"a\"");
    535     }
    536 
    537     assertMapVisitor(
    538         Key.get(mapOfString),
    539         stringType,
    540         stringType,
    541         setOf(module),
    542         MODULE,
    543         false,
    544         0,
    545         instance("a", "A"),
    546         instance("a", "B"));
    547   }
    548 
    549   public void testExhaustiveDuplicateErrorMessage() throws Exception {
    550     class Module1 extends AbstractModule {
    551       @Override
    552       protected void configure() {
    553         MapBinder<String, Object> mapbinder =
    554             MapBinder.newMapBinder(binder(), String.class, Object.class);
    555         mapbinder.addBinding("a").to(String.class);
    556       }
    557     }
    558     class Module2 extends AbstractModule {
    559       @Override
    560       protected void configure() {
    561         MapBinder<String, Object> mapbinder =
    562             MapBinder.newMapBinder(binder(), String.class, Object.class);
    563         mapbinder.addBinding("a").to(Integer.class);
    564         mapbinder.addBinding("b").to(String.class);
    565       }
    566     }
    567     class Module3 extends AbstractModule {
    568       @Override
    569       protected void configure() {
    570         MapBinder<String, Object> mapbinder =
    571             MapBinder.newMapBinder(binder(), String.class, Object.class);
    572         mapbinder.addBinding("b").to(Integer.class);
    573       }
    574     }
    575     class Main extends AbstractModule {
    576       @Override
    577       protected void configure() {
    578         MapBinder.newMapBinder(binder(), String.class, Object.class);
    579         install(new Module1());
    580         install(new Module2());
    581         install(new Module3());
    582       }
    583 
    584       @Provides
    585       String provideString() {
    586         return "foo";
    587       }
    588 
    589       @Provides
    590       Integer provideInt() {
    591         return 42;
    592       }
    593     }
    594     try {
    595       Guice.createInjector(new Main());
    596       fail();
    597     } catch (CreationException ce) {
    598       assertContains(
    599           ce.getMessage(),
    600           "Map injection failed due to duplicated key \"a\", from bindings:",
    601           asModuleChain(Main.class, Module1.class),
    602           asModuleChain(Main.class, Module2.class),
    603           "and key: \"b\", from bindings:",
    604           asModuleChain(Main.class, Module2.class),
    605           asModuleChain(Main.class, Module3.class),
    606           "at " + Main.class.getName() + ".configure(",
    607           asModuleChain(Main.class, RealMapBinder.class));
    608       assertEquals(1, ce.getErrorMessages().size());
    609     }
    610   }
    611 
    612   public void testMapBinderMapPermitDuplicateElements() {
    613     Module ab =
    614         new AbstractModule() {
    615           @Override
    616           protected void configure() {
    617             MapBinder<String, String> multibinder =
    618                 MapBinder.newMapBinder(binder(), String.class, String.class);
    619             multibinder.addBinding("a").toInstance("A");
    620             multibinder.addBinding("b").toInstance("B");
    621             multibinder.permitDuplicates();
    622           }
    623         };
    624     Module bc =
    625         new AbstractModule() {
    626           @Override
    627           protected void configure() {
    628             MapBinder<String, String> multibinder =
    629                 MapBinder.newMapBinder(binder(), String.class, String.class);
    630             multibinder.addBinding("b").toInstance("B");
    631             multibinder.addBinding("c").toInstance("C");
    632             multibinder.permitDuplicates();
    633           }
    634         };
    635     Injector injector = Guice.createInjector(ab, bc);
    636 
    637     assertEquals(mapOf("a", "A", "b", "B", "c", "C"), injector.getInstance(Key.get(mapOfString)));
    638     assertMapVisitor(
    639         Key.get(mapOfString),
    640         stringType,
    641         stringType,
    642         setOf(ab, bc),
    643         BOTH,
    644         true,
    645         0,
    646         instance("a", "A"),
    647         instance("b", "B"),
    648         instance("c", "C"));
    649   }
    650 
    651   public void testMapBinderMapDoesNotDedupeDuplicateValues() {
    652     class ValueType {
    653       int keyPart;
    654       int dataPart;
    655 
    656       private ValueType(int keyPart, int dataPart) {
    657         this.keyPart = keyPart;
    658         this.dataPart = dataPart;
    659       }
    660 
    661       @Override
    662       public boolean equals(Object obj) {
    663         return (obj instanceof ValueType) && (keyPart == ((ValueType) obj).keyPart);
    664       }
    665 
    666       @Override
    667       public int hashCode() {
    668         return keyPart;
    669       }
    670     }
    671     Module m1 =
    672         new AbstractModule() {
    673           @Override
    674           protected void configure() {
    675             MapBinder<String, ValueType> multibinder =
    676                 MapBinder.newMapBinder(binder(), String.class, ValueType.class);
    677             multibinder.addBinding("a").toInstance(new ValueType(1, 2));
    678           }
    679         };
    680     Module m2 =
    681         new AbstractModule() {
    682           @Override
    683           protected void configure() {
    684             MapBinder<String, ValueType> multibinder =
    685                 MapBinder.newMapBinder(binder(), String.class, ValueType.class);
    686             multibinder.addBinding("b").toInstance(new ValueType(1, 3));
    687           }
    688         };
    689 
    690     Injector injector = Guice.createInjector(m1, m2);
    691     Map<String, ValueType> map = injector.getInstance(new Key<Map<String, ValueType>>() {});
    692     assertEquals(2, map.get("a").dataPart);
    693     assertEquals(3, map.get("b").dataPart);
    694   }
    695 
    696   public void testMapBinderMultimap() {
    697     AbstractModule ab1c =
    698         new AbstractModule() {
    699           @Override
    700           protected void configure() {
    701             MapBinder<String, String> multibinder =
    702                 MapBinder.newMapBinder(binder(), String.class, String.class);
    703             multibinder.addBinding("a").toInstance("A");
    704             multibinder.addBinding("b").toInstance("B1");
    705             multibinder.addBinding("c").toInstance("C");
    706           }
    707         };
    708     AbstractModule b2c =
    709         new AbstractModule() {
    710           @Override
    711           protected void configure() {
    712             MapBinder<String, String> multibinder =
    713                 MapBinder.newMapBinder(binder(), String.class, String.class);
    714             multibinder.addBinding("b").toInstance("B2");
    715             multibinder.addBinding("c").toInstance("C");
    716             multibinder.permitDuplicates();
    717           }
    718         };
    719     Injector injector = Guice.createInjector(ab1c, b2c);
    720 
    721     assertEquals(
    722         mapOf("a", setOf("A"), "b", setOf("B1", "B2"), "c", setOf("C")),
    723         injector.getInstance(Key.get(mapOfSetOfString)));
    724     assertMapVisitor(
    725         Key.get(mapOfString),
    726         stringType,
    727         stringType,
    728         setOf(ab1c, b2c),
    729         BOTH,
    730         true,
    731         0,
    732         instance("a", "A"),
    733         instance("b", "B1"),
    734         instance("b", "B2"),
    735         instance("c", "C"));
    736   }
    737 
    738   public void testMapBinderMultimapWithAnotation() {
    739     AbstractModule ab1 =
    740         new AbstractModule() {
    741           @Override
    742           protected void configure() {
    743             MapBinder<String, String> multibinder =
    744                 MapBinder.newMapBinder(binder(), String.class, String.class, Abc.class);
    745             multibinder.addBinding("a").toInstance("A");
    746             multibinder.addBinding("b").toInstance("B1");
    747           }
    748         };
    749     AbstractModule b2c =
    750         new AbstractModule() {
    751           @Override
    752           protected void configure() {
    753             MapBinder<String, String> multibinder =
    754                 MapBinder.newMapBinder(binder(), String.class, String.class, Abc.class);
    755             multibinder.addBinding("b").toInstance("B2");
    756             multibinder.addBinding("c").toInstance("C");
    757             multibinder.permitDuplicates();
    758           }
    759         };
    760     Injector injector = Guice.createInjector(ab1, b2c);
    761 
    762     assertEquals(
    763         mapOf("a", setOf("A"), "b", setOf("B1", "B2"), "c", setOf("C")),
    764         injector.getInstance(Key.get(mapOfSetOfString, Abc.class)));
    765     try {
    766       injector.getInstance(Key.get(mapOfSetOfString));
    767       fail();
    768     } catch (ConfigurationException expected) {
    769     }
    770 
    771     assertMapVisitor(
    772         Key.get(mapOfString, Abc.class),
    773         stringType,
    774         stringType,
    775         setOf(ab1, b2c),
    776         BOTH,
    777         true,
    778         0,
    779         instance("a", "A"),
    780         instance("b", "B1"),
    781         instance("b", "B2"),
    782         instance("c", "C"));
    783   }
    784 
    785   public void testMapBinderMultimapIsUnmodifiable() {
    786     Injector injector =
    787         Guice.createInjector(
    788             new AbstractModule() {
    789               @Override
    790               protected void configure() {
    791                 MapBinder<String, String> mapBinder =
    792                     MapBinder.newMapBinder(binder(), String.class, String.class);
    793                 mapBinder.addBinding("a").toInstance("A");
    794                 mapBinder.permitDuplicates();
    795               }
    796             });
    797 
    798     Map<String, Set<String>> map = injector.getInstance(Key.get(mapOfSetOfString));
    799     try {
    800       map.clear();
    801       fail();
    802     } catch (UnsupportedOperationException expected) {
    803     }
    804     try {
    805       map.get("a").clear();
    806       fail();
    807     } catch (UnsupportedOperationException expected) {
    808     }
    809   }
    810 
    811   public void testMapBinderMapForbidsNullKeys() {
    812     try {
    813       Guice.createInjector(
    814           new AbstractModule() {
    815             @Override
    816             protected void configure() {
    817               MapBinder.newMapBinder(binder(), String.class, String.class).addBinding(null);
    818             }
    819           });
    820       fail();
    821     } catch (CreationException expected) {
    822     }
    823   }
    824 
    825   public void testMapBinderMapForbidsNullValues() {
    826     Module m =
    827         new AbstractModule() {
    828           @Override
    829           protected void configure() {
    830             MapBinder.newMapBinder(binder(), String.class, String.class)
    831                 .addBinding("null")
    832                 .toProvider(Providers.<String>of(null));
    833           }
    834         };
    835     Injector injector = Guice.createInjector(m);
    836 
    837     try {
    838       injector.getInstance(Key.get(mapOfString));
    839       fail();
    840     } catch (ProvisionException expected) {
    841       assertContains(
    842           expected.getMessage(),
    843           "1) Map injection failed due to null value for key \"null\", bound at: "
    844               + m.getClass().getName()
    845               + ".configure(");
    846     }
    847   }
    848 
    849   public void testMapBinderProviderIsScoped() {
    850     final Provider<Integer> counter =
    851         new Provider<Integer>() {
    852           int next = 1;
    853 
    854           @Override
    855           public Integer get() {
    856             return next++;
    857           }
    858         };
    859 
    860     Injector injector =
    861         Guice.createInjector(
    862             new AbstractModule() {
    863               @Override
    864               protected void configure() {
    865                 MapBinder.newMapBinder(binder(), String.class, Integer.class)
    866                     .addBinding("one")
    867                     .toProvider(counter)
    868                     .asEagerSingleton();
    869               }
    870             });
    871 
    872     assertEquals(1, (int) injector.getInstance(Key.get(mapOfInteger)).get("one"));
    873     assertEquals(1, (int) injector.getInstance(Key.get(mapOfInteger)).get("one"));
    874   }
    875 
    876   public void testSourceLinesInMapBindings() {
    877     try {
    878       Guice.createInjector(
    879           new AbstractModule() {
    880             @Override
    881             protected void configure() {
    882               MapBinder.newMapBinder(binder(), String.class, Integer.class).addBinding("one");
    883             }
    884           });
    885       fail();
    886     } catch (CreationException expected) {
    887       assertContains(
    888           expected.getMessage(),
    889           "1) No implementation for java.lang.Integer",
    890           "at " + getClass().getName());
    891     }
    892   }
    893 
    894   /** Check that the dependencies are correct. */
    895   public void testMultibinderDependencies() {
    896     Injector injector =
    897         Guice.createInjector(
    898             new AbstractModule() {
    899               @Override
    900               protected void configure() {
    901                 MapBinder<Integer, String> mapBinder =
    902                     MapBinder.newMapBinder(binder(), Integer.class, String.class);
    903                 mapBinder.addBinding(1).toInstance("A");
    904                 mapBinder.addBinding(2).to(Key.get(String.class, Names.named("b")));
    905 
    906                 bindConstant().annotatedWith(Names.named("b")).to("B");
    907               }
    908             });
    909 
    910     Binding<Map<Integer, String>> binding = injector.getBinding(new Key<Map<Integer, String>>() {});
    911     HasDependencies withDependencies = (HasDependencies) binding;
    912     Set<Dependency<?>> actualDependencies = withDependencies.getDependencies();
    913 
    914     // We expect two dependencies, because the dependencies are annotated with
    915     // Element, which has a uniqueId, it's difficult to directly compare them.
    916     // Instead we will manually compare all the fields except the uniqueId
    917     assertEquals(2, actualDependencies.size());
    918     for (Dependency<?> dependency : actualDependencies) {
    919       Key<?> key = dependency.getKey();
    920       assertEquals(new TypeLiteral<String>() {}, key.getTypeLiteral());
    921       Annotation annotation = dependency.getKey().getAnnotation();
    922       assertTrue(annotation instanceof Element);
    923       Element element = (Element) annotation;
    924       assertEquals("", element.setName());
    925       assertEquals(Element.Type.MAPBINDER, element.type());
    926       assertEquals("java.lang.Integer", element.keyType());
    927     }
    928 
    929     Set<String> elements = Sets.newHashSet();
    930     elements.addAll(recurseForDependencies(injector, withDependencies));
    931     assertEquals(ImmutableSet.of("A", "B"), elements);
    932   }
    933 
    934   private Set<String> recurseForDependencies(Injector injector, HasDependencies hasDependencies) {
    935     Set<String> elements = Sets.newHashSet();
    936     for (Dependency<?> dependency : hasDependencies.getDependencies()) {
    937       Binding<?> binding = injector.getBinding(dependency.getKey());
    938       HasDependencies deps = (HasDependencies) binding;
    939       if (binding instanceof InstanceBinding) {
    940         elements.add((String) ((InstanceBinding<?>) binding).getInstance());
    941       } else {
    942         elements.addAll(recurseForDependencies(injector, deps));
    943       }
    944     }
    945     return elements;
    946   }
    947 
    948   /** Check that the dependencies are correct in the Tool Stage. */
    949   public void testMultibinderDependenciesInToolStage() {
    950     Injector injector =
    951         Guice.createInjector(
    952             Stage.TOOL,
    953             new AbstractModule() {
    954               @Override
    955               protected void configure() {
    956                 MapBinder<Integer, String> mapBinder =
    957                     MapBinder.newMapBinder(binder(), Integer.class, String.class);
    958                 mapBinder.addBinding(1).toInstance("A");
    959                 mapBinder.addBinding(2).to(Key.get(String.class, Names.named("b")));
    960 
    961                 bindConstant().annotatedWith(Names.named("b")).to("B");
    962               }
    963             });
    964 
    965     Binding<Map<Integer, String>> binding = injector.getBinding(new Key<Map<Integer, String>>() {});
    966     HasDependencies withDependencies = (HasDependencies) binding;
    967     Set<Dependency<?>> actualDependencies = withDependencies.getDependencies();
    968 
    969     // We expect two dependencies, because the dependencies are annotated with
    970     // Element, which has a uniqueId, it's difficult to directly compare them.
    971     // Instead we will manually compare all the fields except the uniqueId
    972     assertEquals(2, actualDependencies.size());
    973     for (Dependency<?> dependency : actualDependencies) {
    974       Key<?> key = dependency.getKey();
    975       assertEquals(new TypeLiteral<String>() {}, key.getTypeLiteral());
    976       Annotation annotation = dependency.getKey().getAnnotation();
    977       assertTrue(annotation instanceof Element);
    978       Element element = (Element) annotation;
    979       assertEquals("", element.setName());
    980       assertEquals(Element.Type.MAPBINDER, element.type());
    981       assertEquals("java.lang.Integer", element.keyType());
    982     }
    983   }
    984 
    985   /** Our implementation maintains order, but doesn't guarantee it in the API spec. */
    986   // TODO: specify the iteration order
    987   public void testBindOrderEqualsIterationOrder() {
    988     Injector injector =
    989         Guice.createInjector(
    990             new AbstractModule() {
    991               @Override
    992               protected void configure() {
    993                 MapBinder<String, String> mapBinder =
    994                     MapBinder.newMapBinder(binder(), String.class, String.class);
    995                 mapBinder.addBinding("leonardo").toInstance("blue");
    996                 mapBinder.addBinding("donatello").toInstance("purple");
    997                 install(
    998                     new AbstractModule() {
    999                       @Override
   1000                       protected void configure() {
   1001                         MapBinder.newMapBinder(binder(), String.class, String.class)
   1002                             .addBinding("michaelangelo")
   1003                             .toInstance("orange");
   1004                       }
   1005                     });
   1006               }
   1007             },
   1008             new AbstractModule() {
   1009               @Override
   1010               protected void configure() {
   1011                 MapBinder.newMapBinder(binder(), String.class, String.class)
   1012                     .addBinding("raphael")
   1013                     .toInstance("red");
   1014               }
   1015             });
   1016 
   1017     Map<String, String> map = injector.getInstance(new Key<Map<String, String>>() {});
   1018     Iterator<Map.Entry<String, String>> iterator = map.entrySet().iterator();
   1019     assertEquals(Maps.immutableEntry("leonardo", "blue"), iterator.next());
   1020     assertEquals(Maps.immutableEntry("donatello", "purple"), iterator.next());
   1021     assertEquals(Maps.immutableEntry("michaelangelo", "orange"), iterator.next());
   1022     assertEquals(Maps.immutableEntry("raphael", "red"), iterator.next());
   1023   }
   1024 
   1025   /** With overrides, we should get the union of all map bindings. */
   1026   public void testModuleOverrideAndMapBindings() {
   1027     Module ab =
   1028         new AbstractModule() {
   1029           @Override
   1030           protected void configure() {
   1031             MapBinder<String, String> multibinder =
   1032                 MapBinder.newMapBinder(binder(), String.class, String.class);
   1033             multibinder.addBinding("a").toInstance("A");
   1034             multibinder.addBinding("b").toInstance("B");
   1035           }
   1036         };
   1037     Module cd =
   1038         new AbstractModule() {
   1039           @Override
   1040           protected void configure() {
   1041             MapBinder<String, String> multibinder =
   1042                 MapBinder.newMapBinder(binder(), String.class, String.class);
   1043             multibinder.addBinding("c").toInstance("C");
   1044             multibinder.addBinding("d").toInstance("D");
   1045           }
   1046         };
   1047     Module ef =
   1048         new AbstractModule() {
   1049           @Override
   1050           protected void configure() {
   1051             MapBinder<String, String> multibinder =
   1052                 MapBinder.newMapBinder(binder(), String.class, String.class);
   1053             multibinder.addBinding("e").toInstance("E");
   1054             multibinder.addBinding("f").toInstance("F");
   1055           }
   1056         };
   1057 
   1058     Module abcd = Modules.override(ab).with(cd);
   1059     Injector injector = Guice.createInjector(abcd, ef);
   1060     assertEquals(
   1061         mapOf("a", "A", "b", "B", "c", "C", "d", "D", "e", "E", "f", "F"),
   1062         injector.getInstance(Key.get(mapOfString)));
   1063     assertMapVisitor(
   1064         Key.get(mapOfString),
   1065         stringType,
   1066         stringType,
   1067         setOf(abcd, ef),
   1068         BOTH,
   1069         false,
   1070         0,
   1071         instance("a", "A"),
   1072         instance("b", "B"),
   1073         instance("c", "C"),
   1074         instance("d", "D"),
   1075         instance("e", "E"),
   1076         instance("f", "F"));
   1077   }
   1078 
   1079   public void testDeduplicateMapBindings() {
   1080     Module module =
   1081         new AbstractModule() {
   1082           @Override
   1083           protected void configure() {
   1084             MapBinder<String, String> mapbinder =
   1085                 MapBinder.newMapBinder(binder(), String.class, String.class);
   1086             mapbinder.addBinding("a").toInstance("A");
   1087             mapbinder.addBinding("a").toInstance("A");
   1088             mapbinder.addBinding("b").toInstance("B");
   1089             mapbinder.addBinding("b").toInstance("B");
   1090           }
   1091         };
   1092     Injector injector = Guice.createInjector(module);
   1093     assertEquals(mapOf("a", "A", "b", "B"), injector.getInstance(Key.get(mapOfString)));
   1094     assertMapVisitor(
   1095         Key.get(mapOfString),
   1096         stringType,
   1097         stringType,
   1098         setOf(module),
   1099         BOTH,
   1100         false,
   1101         0,
   1102         instance("a", "A"),
   1103         instance("b", "B"));
   1104   }
   1105 
   1106   /** With overrides, we should get the union of all map bindings. */
   1107   public void testModuleOverrideAndMapBindingsWithPermitDuplicates() {
   1108     Module abc =
   1109         new AbstractModule() {
   1110           @Override
   1111           protected void configure() {
   1112             MapBinder<String, String> multibinder =
   1113                 MapBinder.newMapBinder(binder(), String.class, String.class);
   1114             multibinder.addBinding("a").toInstance("A");
   1115             multibinder.addBinding("b").toInstance("B");
   1116             multibinder.addBinding("c").toInstance("C");
   1117             multibinder.permitDuplicates();
   1118           }
   1119         };
   1120     Module cd =
   1121         new AbstractModule() {
   1122           @Override
   1123           protected void configure() {
   1124             MapBinder<String, String> multibinder =
   1125                 MapBinder.newMapBinder(binder(), String.class, String.class);
   1126             multibinder.addBinding("c").toInstance("C");
   1127             multibinder.addBinding("d").toInstance("D");
   1128             multibinder.permitDuplicates();
   1129           }
   1130         };
   1131     Module ef =
   1132         new AbstractModule() {
   1133           @Override
   1134           protected void configure() {
   1135             MapBinder<String, String> multibinder =
   1136                 MapBinder.newMapBinder(binder(), String.class, String.class);
   1137             multibinder.addBinding("e").toInstance("E");
   1138             multibinder.addBinding("f").toInstance("F");
   1139             multibinder.permitDuplicates();
   1140           }
   1141         };
   1142 
   1143     Module abcd = Modules.override(abc).with(cd);
   1144     Injector injector = Guice.createInjector(abcd, ef);
   1145     assertEquals(
   1146         mapOf("a", "A", "b", "B", "c", "C", "d", "D", "e", "E", "f", "F"),
   1147         injector.getInstance(Key.get(mapOfString)));
   1148     assertMapVisitor(
   1149         Key.get(mapOfString),
   1150         stringType,
   1151         stringType,
   1152         setOf(abcd, ef),
   1153         BOTH,
   1154         true,
   1155         0,
   1156         instance("a", "A"),
   1157         instance("b", "B"),
   1158         instance("c", "C"),
   1159         instance("d", "D"),
   1160         instance("e", "E"),
   1161         instance("f", "F"));
   1162   }
   1163 
   1164   /** Ensure there are no initialization race conditions in basic map injection. */
   1165   public void testBasicMapDependencyInjection() {
   1166     final AtomicReference<Map<String, String>> injectedMap =
   1167         new AtomicReference<Map<String, String>>();
   1168     final Object anObject =
   1169         new Object() {
   1170           @Inject
   1171           void initialize(Map<String, String> map) {
   1172             injectedMap.set(map);
   1173           }
   1174         };
   1175     Module abc =
   1176         new AbstractModule() {
   1177           @Override
   1178           protected void configure() {
   1179             requestInjection(anObject);
   1180             MapBinder<String, String> multibinder =
   1181                 MapBinder.newMapBinder(binder(), String.class, String.class);
   1182             multibinder.addBinding("a").toInstance("A");
   1183             multibinder.addBinding("b").toInstance("B");
   1184             multibinder.addBinding("c").toInstance("C");
   1185           }
   1186         };
   1187     Guice.createInjector(abc);
   1188     assertEquals(mapOf("a", "A", "b", "B", "c", "C"), injectedMap.get());
   1189   }
   1190 
   1191   /** Ensure there are no initialization race conditions in provider multimap injection. */
   1192   public void testProviderMultimapDependencyInjection() {
   1193     final AtomicReference<Map<String, Set<Provider<String>>>> injectedMultimap =
   1194         new AtomicReference<Map<String, Set<Provider<String>>>>();
   1195     final Object anObject =
   1196         new Object() {
   1197           @Inject
   1198           void initialize(Map<String, Set<Provider<String>>> multimap) {
   1199             injectedMultimap.set(multimap);
   1200           }
   1201         };
   1202     Module abc =
   1203         new AbstractModule() {
   1204           @Override
   1205           protected void configure() {
   1206             requestInjection(anObject);
   1207             MapBinder<String, String> multibinder =
   1208                 MapBinder.newMapBinder(binder(), String.class, String.class);
   1209             multibinder.permitDuplicates();
   1210             multibinder.addBinding("a").toInstance("A");
   1211             multibinder.addBinding("b").toInstance("B");
   1212             multibinder.addBinding("c").toInstance("C");
   1213           }
   1214         };
   1215     Guice.createInjector(abc);
   1216     Map<String, String> map =
   1217         Maps.transformValues(
   1218             injectedMultimap.get(),
   1219             new Function<Set<Provider<String>>, String>() {
   1220               @Override
   1221               public String apply(Set<Provider<String>> stringProvidersSet) {
   1222                 return Iterables.getOnlyElement(stringProvidersSet).get();
   1223               }
   1224             });
   1225     assertEquals(mapOf("a", "A", "b", "B", "c", "C"), map);
   1226   }
   1227 
   1228   @Retention(RUNTIME)
   1229   @BindingAnnotation
   1230   @interface Abc {}
   1231 
   1232   @Retention(RUNTIME)
   1233   @BindingAnnotation
   1234   @interface De {}
   1235 
   1236   @SuppressWarnings("unchecked")
   1237   private <K, V> Map<K, V> mapOf(Object... elements) {
   1238     Map<K, V> result = new HashMap<>();
   1239     for (int i = 0; i < elements.length; i += 2) {
   1240       result.put((K) elements[i], (V) elements[i + 1]);
   1241     }
   1242     return result;
   1243   }
   1244 
   1245   @SuppressWarnings("unchecked")
   1246   private <V> Set<V> setOf(V... elements) {
   1247     return new HashSet<V>(Arrays.asList(elements));
   1248   }
   1249 
   1250   @BindingAnnotation
   1251   @Retention(RetentionPolicy.RUNTIME)
   1252   @Target({ElementType.FIELD, ElementType.PARAMETER, ElementType.METHOD})
   1253   private static @interface Marker {}
   1254 
   1255   @Marker
   1256   public void testMapBinderMatching() throws Exception {
   1257     Method m = MapBinderTest.class.getDeclaredMethod("testMapBinderMatching");
   1258     assertNotNull(m);
   1259     final Annotation marker = m.getAnnotation(Marker.class);
   1260     Injector injector =
   1261         Guice.createInjector(
   1262             new AbstractModule() {
   1263               @Override
   1264               public void configure() {
   1265                 MapBinder<Integer, Integer> mb1 =
   1266                     MapBinder.newMapBinder(binder(), Integer.class, Integer.class, Marker.class);
   1267                 MapBinder<Integer, Integer> mb2 =
   1268                     MapBinder.newMapBinder(binder(), Integer.class, Integer.class, marker);
   1269                 mb1.addBinding(1).toInstance(1);
   1270                 mb2.addBinding(2).toInstance(2);
   1271 
   1272                 // This assures us that the two binders are equivalent, so we expect the instance added to
   1273                 // each to have been added to one set.
   1274                 assertEquals(mb1, mb2);
   1275               }
   1276             });
   1277     TypeLiteral<Map<Integer, Integer>> t = new TypeLiteral<Map<Integer, Integer>>() {};
   1278     Map<Integer, Integer> s1 = injector.getInstance(Key.get(t, Marker.class));
   1279     Map<Integer, Integer> s2 = injector.getInstance(Key.get(t, marker));
   1280 
   1281     // This assures us that the two sets are in fact equal.  They may not be same set (as in Java
   1282     // object identical), but we shouldn't expect that, since probably Guice creates the set each
   1283     // time in case the elements are dependent on scope.
   1284     assertEquals(s1, s2);
   1285 
   1286     // This ensures that MultiBinder is internally using the correct set name --
   1287     // making sure that instances of marker annotations have the same set name as
   1288     // MarkerAnnotation.class.
   1289     Map<Integer, Integer> expected = new HashMap<>();
   1290     expected.put(1, 1);
   1291     expected.put(2, 2);
   1292     assertEquals(expected, s1);
   1293   }
   1294 
   1295   public void testTwoMapBindersAreDistinct() {
   1296     Injector injector =
   1297         Guice.createInjector(
   1298             new AbstractModule() {
   1299               @Override
   1300               protected void configure() {
   1301                 MapBinder.newMapBinder(binder(), String.class, String.class)
   1302                     .addBinding("A")
   1303                     .toInstance("a");
   1304 
   1305                 MapBinder.newMapBinder(binder(), Integer.class, String.class)
   1306                     .addBinding(1)
   1307                     .toInstance("b");
   1308               }
   1309             });
   1310     Collector collector = new Collector();
   1311     Binding<Map<String, String>> map1 = injector.getBinding(Key.get(mapOfString));
   1312     map1.acceptTargetVisitor(collector);
   1313     assertNotNull(collector.mapbinding);
   1314     MapBinderBinding<?> map1Binding = collector.mapbinding;
   1315 
   1316     Binding<Map<Integer, String>> map2 = injector.getBinding(Key.get(mapOfIntString));
   1317     map2.acceptTargetVisitor(collector);
   1318     assertNotNull(collector.mapbinding);
   1319     MapBinderBinding<?> map2Binding = collector.mapbinding;
   1320 
   1321     List<Binding<String>> bindings = injector.findBindingsByType(stringType);
   1322     assertEquals("should have two elements: " + bindings, 2, bindings.size());
   1323     Binding<String> a = bindings.get(0);
   1324     Binding<String> b = bindings.get(1);
   1325     assertEquals("a", ((InstanceBinding<String>) a).getInstance());
   1326     assertEquals("b", ((InstanceBinding<String>) b).getInstance());
   1327 
   1328     // Make sure the correct elements belong to their own sets.
   1329     assertTrue(map1Binding.containsElement(a));
   1330     assertFalse(map1Binding.containsElement(b));
   1331 
   1332     assertFalse(map2Binding.containsElement(a));
   1333     assertTrue(map2Binding.containsElement(b));
   1334   }
   1335 
   1336   // Tests for com.google.inject.internal.WeakKeySet not leaking memory.
   1337   public void testWeakKeySet_integration_mapbinder() {
   1338     Key<Map<String, String>> mapKey = Key.get(new TypeLiteral<Map<String, String>>() {});
   1339 
   1340     Injector parentInjector =
   1341         Guice.createInjector(
   1342             new AbstractModule() {
   1343               @Override
   1344               protected void configure() {
   1345                 bind(String.class).toInstance("hi");
   1346               }
   1347             });
   1348     WeakKeySetUtils.assertNotBlacklisted(parentInjector, mapKey);
   1349 
   1350     Injector childInjector =
   1351         parentInjector.createChildInjector(
   1352             new AbstractModule() {
   1353               @Override
   1354               protected void configure() {
   1355                 MapBinder<String, String> binder =
   1356                     MapBinder.newMapBinder(binder(), String.class, String.class);
   1357                 binder.addBinding("bar").toInstance("foo");
   1358               }
   1359             });
   1360     WeakReference<Injector> weakRef = new WeakReference<>(childInjector);
   1361     WeakKeySetUtils.assertBlacklisted(parentInjector, mapKey);
   1362 
   1363     // Clear the ref, GC, and ensure that we are no longer blacklisting.
   1364     childInjector = null;
   1365 
   1366     Asserts.awaitClear(weakRef);
   1367     WeakKeySetUtils.assertNotBlacklisted(parentInjector, mapKey);
   1368   }
   1369 
   1370   @SuppressWarnings("rawtypes")
   1371   public void testGetEntries() {
   1372     List<com.google.inject.spi.Element> elements =
   1373         Elements.getElements(new MapBinderWithTwoEntriesModule());
   1374 
   1375     // Get the MapBinderBinding
   1376     MapBinderBinding<?> mapBinderBinding = getMapBinderBinding(elements);
   1377 
   1378     // Execute the call to getEntries
   1379     List<Map.Entry<?, Binding<?>>> mapEntries = mapBinderBinding.getEntries(elements);
   1380 
   1381     // Assert on the results
   1382     Map.Entry<?, Binding<?>> firstEntry = mapEntries.get(0);
   1383     assertEquals("keyOne", firstEntry.getKey());
   1384     Binding<?> firstBinding = firstEntry.getValue();
   1385     assertEquals("valueOne", ((InstanceBinding) firstBinding).getInstance());
   1386 
   1387     Map.Entry<?, Binding<?>> secondEntry = mapEntries.get(1);
   1388     assertEquals("keyTwo", secondEntry.getKey());
   1389     Binding<?> secondBinding = secondEntry.getValue();
   1390     assertEquals("valueTwo", ((InstanceBinding) secondBinding).getInstance());
   1391   }
   1392 
   1393   @SuppressWarnings("rawtypes")
   1394   public void testGetEntriesWithDuplicateKeys() {
   1395     // Set up the module
   1396     Module module =
   1397         new AbstractModule() {
   1398           @Override
   1399           protected void configure() {
   1400             MapBinder<String, String> mapBinder =
   1401                 MapBinder.newMapBinder(binder(), String.class, String.class);
   1402             mapBinder.addBinding("A").toInstance("a1");
   1403             mapBinder.addBinding("A").toInstance("a2");
   1404             mapBinder.permitDuplicates();
   1405           }
   1406         };
   1407 
   1408     // Get the MapBinderBinding
   1409     List<com.google.inject.spi.Element> elements = Elements.getElements(module);
   1410     MapBinderBinding<?> mapBinderBinding = getMapBinderBinding(elements);
   1411 
   1412     // Execute the call to getEntries
   1413     List<Map.Entry<?, Binding<?>>> mapEntries = mapBinderBinding.getEntries(elements);
   1414 
   1415     // Assert on the results
   1416     Map.Entry<?, Binding<?>> firstEntry = mapEntries.get(0);
   1417     assertEquals("A", firstEntry.getKey());
   1418     Binding<?> firstBinding = firstEntry.getValue();
   1419     assertEquals("a1", ((InstanceBinding) firstBinding).getInstance());
   1420 
   1421     Map.Entry<?, Binding<?>> secondEntry = mapEntries.get(1);
   1422     assertEquals("A", secondEntry.getKey());
   1423     Binding<?> secondBinding = secondEntry.getValue();
   1424     assertEquals("a2", ((InstanceBinding) secondBinding).getInstance());
   1425   }
   1426 
   1427   @SuppressWarnings("rawtypes")
   1428   public void testGetEntriesWithDuplicateValues() {
   1429     // Set up the module
   1430     Module module =
   1431         new AbstractModule() {
   1432           @Override
   1433           protected void configure() {
   1434             MapBinder<String, String> mapBinder =
   1435                 MapBinder.newMapBinder(binder(), String.class, String.class);
   1436             mapBinder.addBinding("A").toInstance("a");
   1437             mapBinder.addBinding("A").toInstance("a");
   1438           }
   1439         };
   1440 
   1441     // Get the MapBinderBinding
   1442     List<com.google.inject.spi.Element> elements = Elements.getElements(module);
   1443     MapBinderBinding<?> mapBinderBinding = getMapBinderBinding(elements);
   1444 
   1445     // Execute the call to getEntries
   1446     List<Map.Entry<?, Binding<?>>> mapEntries = mapBinderBinding.getEntries(elements);
   1447 
   1448     // Assert on the results
   1449     Map.Entry<?, Binding<?>> firstEntry = mapEntries.get(0);
   1450     assertEquals("A", firstEntry.getKey());
   1451     Binding<?> firstBinding = firstEntry.getValue();
   1452     assertEquals("a", ((InstanceBinding) firstBinding).getInstance());
   1453 
   1454     Map.Entry<?, Binding<?>> secondEntry = mapEntries.get(1);
   1455     assertEquals("A", secondEntry.getKey());
   1456     Binding<?> secondBinding = secondEntry.getValue();
   1457     assertEquals("a", ((InstanceBinding) secondBinding).getInstance());
   1458   }
   1459 
   1460   @SuppressWarnings("rawtypes")
   1461   public void testGetEntriesMissingProviderMapEntry() {
   1462     List<com.google.inject.spi.Element> elements =
   1463         Lists.newArrayList(Elements.getElements(new MapBinderWithTwoEntriesModule()));
   1464 
   1465     // Get the MapBinderBinding
   1466     MapBinderBinding<?> mapBinderBinding = getMapBinderBinding(elements);
   1467 
   1468     // Remove the ProviderMapEntry for "a" from the elements
   1469     com.google.inject.spi.Element providerMapEntryForA = getProviderMapEntry("keyOne", elements);
   1470     boolean removeSuccessful = elements.remove(providerMapEntryForA);
   1471     assertTrue(removeSuccessful);
   1472 
   1473     // Execute the call to getEntries, we expect it to fail
   1474     try {
   1475       mapBinderBinding.getEntries(elements);
   1476       fail();
   1477     } catch (IllegalArgumentException expected) {
   1478       assertContains(
   1479           expected.getMessage(),
   1480           "Expected a 1:1 mapping from map keys to values.",
   1481           "Found these Bindings that were missing an associated entry:",
   1482           "java.lang.String",
   1483           "bound at:",
   1484           "MapBinderWithTwoEntriesModule");
   1485     }
   1486   }
   1487 
   1488   /**
   1489    * Will find and return the {@link com.google.inject.spi.Element} that is a {@link
   1490    * ProviderMapEntry} with a key that matches the one supplied by the user in {@code k}.
   1491    *
   1492    * <p>Will return {@code null} if it cannot be found.
   1493    */
   1494   private static com.google.inject.spi.Element getProviderMapEntry(
   1495       Object kToFind, Iterable<com.google.inject.spi.Element> elements) {
   1496     for (com.google.inject.spi.Element element : elements) {
   1497       if (element instanceof ProviderInstanceBinding) {
   1498         javax.inject.Provider<?> usp =
   1499             ((ProviderInstanceBinding<?>) element).getUserSuppliedProvider();
   1500         if (usp instanceof ProviderMapEntry) {
   1501           ProviderMapEntry<?, ?> pme = (ProviderMapEntry<?, ?>) usp;
   1502 
   1503           // Check if the key from the ProviderMapEntry matches the one we're looking for
   1504           if (kToFind.equals(pme.getKey())) {
   1505             return element;
   1506           }
   1507         }
   1508       }
   1509     }
   1510     // No matching ProviderMapEntry found
   1511     return null;
   1512   }
   1513 
   1514   @SuppressWarnings("rawtypes")
   1515   public void testGetEntriesMissingBindingForValue() {
   1516     List<com.google.inject.spi.Element> elements =
   1517         Lists.newArrayList(Elements.getElements(new MapBinderWithTwoEntriesModule()));
   1518 
   1519     // Get the MapBinderBinding
   1520     MapBinderBinding<?> mapBinderBinding = getMapBinderBinding(elements);
   1521 
   1522     // Remove the ProviderMapEntry for "a" from the elements
   1523     com.google.inject.spi.Element bindingForA = getInstanceBindingForValue("valueOne", elements);
   1524     boolean removeSuccessful = elements.remove(bindingForA);
   1525     assertTrue(removeSuccessful);
   1526 
   1527     // Execute the call to getEntries, we expect it to fail
   1528     try {
   1529       mapBinderBinding.getEntries(elements);
   1530       fail();
   1531     } catch (IllegalArgumentException expected) {
   1532       assertContains(
   1533           expected.getMessage(),
   1534           "Expected a 1:1 mapping from map keys to values.",
   1535           "Found these map keys without a corresponding value:",
   1536           "keyOne",
   1537           "bound at:",
   1538           "MapBinderWithTwoEntriesModule");
   1539     }
   1540   }
   1541 
   1542   /**
   1543    * Will find and return the {@link com.google.inject.spi.Element} that is an {@link
   1544    * InstanceBinding} and binds {@code vToFind}.
   1545    */
   1546   private static com.google.inject.spi.Element getInstanceBindingForValue(
   1547       Object vToFind, Iterable<com.google.inject.spi.Element> elements) {
   1548     for (com.google.inject.spi.Element element : elements) {
   1549       if (element instanceof InstanceBinding) {
   1550         Object instanceFromBinding = ((InstanceBinding<?>) element).getInstance();
   1551         if (vToFind.equals(instanceFromBinding)) {
   1552           return element;
   1553         }
   1554       }
   1555     }
   1556     // No matching binding found
   1557     return null;
   1558   }
   1559 
   1560   /** A simple module with a MapBinder with two entries. */
   1561   private static final class MapBinderWithTwoEntriesModule extends AbstractModule {
   1562     @Override
   1563     protected void configure() {
   1564       MapBinder<String, String> mapBinder =
   1565           MapBinder.newMapBinder(binder(), String.class, String.class);
   1566       mapBinder.addBinding("keyOne").toInstance("valueOne");
   1567       mapBinder.addBinding("keyTwo").toInstance("valueTwo");
   1568     }
   1569   }
   1570 
   1571   /**
   1572    * Given an {@link Iterable} of elements, return the one that is a {@link MapBinderBinding}, or
   1573    * {@code null} if it cannot be found.
   1574    */
   1575   private static MapBinderBinding<?> getMapBinderBinding(
   1576       Iterable<com.google.inject.spi.Element> elements) {
   1577     final Collector collector = new Collector();
   1578     for (com.google.inject.spi.Element element : elements) {
   1579       element.acceptVisitor(
   1580           new DefaultElementVisitor<Void>() {
   1581             @Override
   1582             public <T> Void visit(Binding<T> binding) {
   1583               binding.acceptTargetVisitor(collector);
   1584               return null;
   1585             }
   1586           });
   1587     }
   1588     return collector.mapbinding;
   1589   }
   1590 }
   1591