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
17package com.google.inject.multibindings;
18
19import static com.google.inject.Asserts.asModuleChain;
20import static com.google.inject.Asserts.assertContains;
21import static com.google.inject.multibindings.SpiUtils.VisitType.BOTH;
22import static com.google.inject.multibindings.SpiUtils.VisitType.MODULE;
23import static com.google.inject.multibindings.SpiUtils.assertMapVisitor;
24import static com.google.inject.multibindings.SpiUtils.instance;
25import static com.google.inject.multibindings.SpiUtils.providerInstance;
26import static com.google.inject.name.Names.named;
27import static java.lang.annotation.RetentionPolicy.RUNTIME;
28
29import com.google.common.base.Function;
30import com.google.common.collect.ImmutableSet;
31import com.google.common.collect.Iterables;
32import com.google.common.collect.Maps;
33import com.google.common.collect.Sets;
34import com.google.inject.AbstractModule;
35import com.google.inject.Asserts;
36import com.google.inject.Binding;
37import com.google.inject.BindingAnnotation;
38import com.google.inject.ConfigurationException;
39import com.google.inject.CreationException;
40import com.google.inject.Guice;
41import com.google.inject.Inject;
42import com.google.inject.Injector;
43import com.google.inject.Key;
44import com.google.inject.Module;
45import com.google.inject.Provider;
46import com.google.inject.Provides;
47import com.google.inject.ProvisionException;
48import com.google.inject.Stage;
49import com.google.inject.TypeLiteral;
50import com.google.inject.internal.WeakKeySetUtils;
51import com.google.inject.name.Names;
52import com.google.inject.spi.Dependency;
53import com.google.inject.spi.HasDependencies;
54import com.google.inject.spi.InstanceBinding;
55import com.google.inject.util.Modules;
56import com.google.inject.util.Providers;
57import com.google.inject.util.Types;
58
59import junit.framework.TestCase;
60
61import java.lang.annotation.Annotation;
62import java.lang.annotation.ElementType;
63import java.lang.annotation.Retention;
64import java.lang.annotation.RetentionPolicy;
65import java.lang.annotation.Target;
66import java.lang.ref.WeakReference;
67import java.lang.reflect.Method;
68import java.lang.reflect.Type;
69import java.util.Arrays;
70import java.util.Collection;
71import java.util.Collections;
72import java.util.HashMap;
73import java.util.HashSet;
74import java.util.Iterator;
75import java.util.List;
76import java.util.Map;
77import java.util.Set;
78import java.util.concurrent.atomic.AtomicReference;
79
80/**
81 * @author dpb@google.com (David P. Baker)
82 */
83public class MapBinderTest extends TestCase {
84
85  private static final Set<Key<?>> FRAMEWORK_KEYS = ImmutableSet.of(
86      Key.get(java.util.logging.Logger.class),
87      Key.get(Stage.class),
88      Key.get(Injector.class)
89  );
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.newParameterizedType(javax.inject.Provider.class, 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 = new AbstractModule() {
119      @Override
120      protected void configure() {
121        MapBinder.newMapBinder(binder(), String.class, String.class).permitDuplicates();
122      }
123    };
124
125    Injector injector = Guice.createInjector(module);
126
127    Map<Key<?>, Binding<?>> bindings = injector.getBindings();
128
129    ImmutableSet<Key<?>> expectedBindings = ImmutableSet.<Key<?>>builder()
130        .add(
131            // Map<K, V>
132            Key.get(Types.mapOf(String.class, String.class)),
133            // Map<K, Provider<V>>
134            Key.get(Types.mapOf(String.class, Types.providerOf(String.class))),
135            // Map<K, javax.inject.Provider<V>>
136            Key.get(Types.mapOf(String.class, javaxProviderOf(String.class))),
137            // Map<K, Set<V>>
138            Key.get(Types.mapOf(String.class, Types.setOf(String.class))),
139            // Map<K, Set<Provider<V>>
140            Key.get(Types.mapOf(String.class, Types.setOf(Types.providerOf(String.class)))),
141            // Set<Map.Entry<K, Provider<V>>>
142            Key.get(Types.setOf(mapEntryOf(String.class, Types.providerOf(String.class)))),
143            // Collection<Provider<Map.Entry<K, Provider<V>>>>
144            Key.get(collectionOf(Types.providerOf(
145                mapEntryOf(String.class, Types.providerOf(String.class))))),
146            // Collection<javax.inject.Provider<Map.Entry<K, Provider<V>>>>
147            Key.get(collectionOf(javaxProviderOf(
148                mapEntryOf(String.class, Types.providerOf(String.class))))),
149            // @Named(...) Boolean
150            Key.get(Boolean.class,
151                named("Multibinder<java.util.Map$Entry<java.lang.String, "
152                    + "com.google.inject.Provider<java.lang.String>>> permits duplicates"))
153        )
154        .addAll(FRAMEWORK_KEYS).build();
155
156    Set<Key<?>> missingBindings = Sets.difference(expectedBindings, bindings.keySet());
157    Set<Key<?>> extraBindings = Sets.difference(bindings.keySet(), expectedBindings);
158
159    assertTrue("There should be no missing bindings. Missing: " + missingBindings,
160        missingBindings.isEmpty());
161    assertTrue("There should be no extra bindings. Extra: " + extraBindings,
162        extraBindings.isEmpty());
163  }
164
165  public void testMapBinderAggregatesMultipleModules() {
166    Module abc = new AbstractModule() {
167      @Override protected void configure() {
168        MapBinder<String, String> multibinder = MapBinder.newMapBinder(
169            binder(), String.class, String.class);
170        multibinder.addBinding("a").toInstance("A");
171        multibinder.addBinding("b").toInstance("B");
172        multibinder.addBinding("c").toInstance("C");
173      }
174    };
175    Module de = new AbstractModule() {
176      @Override protected void configure() {
177        MapBinder<String, String> multibinder = MapBinder.newMapBinder(
178            binder(), String.class, String.class);
179        multibinder.addBinding("d").toInstance("D");
180        multibinder.addBinding("e").toInstance("E");
181      }
182    };
183
184    Injector injector = Guice.createInjector(abc, de);
185    Map<String, String> abcde = injector.getInstance(Key.get(mapOfString));
186
187    assertEquals(mapOf("a", "A", "b", "B", "c", "C", "d", "D", "e", "E"), abcde);
188    assertMapVisitor(Key.get(mapOfString), stringType, stringType, setOf(abc, de), BOTH, false, 0,
189        instance("a", "A"), instance("b", "B"), instance("c", "C"), instance("d", "D"), instance("e", "E"));
190
191    // just make sure these succeed
192    injector.getInstance(Key.get(mapOfStringProvider));
193    injector.getInstance(Key.get(mapOfStringJavaxProvider));
194  }
195
196  public void testMapBinderAggregationForAnnotationInstance() {
197    Module module = new AbstractModule() {
198      @Override protected void configure() {
199        MapBinder<String, String> multibinder = MapBinder.newMapBinder(
200            binder(), String.class, String.class, Names.named("abc"));
201        multibinder.addBinding("a").toInstance("A");
202        multibinder.addBinding("b").toInstance("B");
203
204        multibinder = MapBinder.newMapBinder(
205            binder(), String.class, String.class, Names.named("abc"));
206        multibinder.addBinding("c").toInstance("C");
207      }
208    };
209    Injector injector = Guice.createInjector(module);
210
211    Key<Map<String, String>> key = Key.get(mapOfString, Names.named("abc"));
212    Map<String, String> abc = injector.getInstance(key);
213    assertEquals(mapOf("a", "A", "b", "B", "c", "C"), abc);
214    assertMapVisitor(key, stringType, stringType, setOf(module), BOTH, false, 0,
215        instance("a", "A"), instance("b", "B"), instance("c", "C"));
216
217    // just make sure these succeed
218    injector.getInstance(Key.get(mapOfStringProvider, Names.named("abc")));
219    injector.getInstance(Key.get(mapOfStringJavaxProvider, Names.named("abc")));
220  }
221
222  public void testMapBinderAggregationForAnnotationType() {
223    Module module = new AbstractModule() {
224      @Override protected void configure() {
225        MapBinder<String, String> multibinder = MapBinder.newMapBinder(
226            binder(), String.class, String.class, Abc.class);
227        multibinder.addBinding("a").toInstance("A");
228        multibinder.addBinding("b").toInstance("B");
229
230        multibinder = MapBinder.newMapBinder(
231            binder(), String.class, String.class, Abc.class);
232        multibinder.addBinding("c").toInstance("C");
233      }
234    };
235    Injector injector = Guice.createInjector(module);
236
237    Key<Map<String, String>> key = Key.get(mapOfString, Abc.class);
238    Map<String, String> abc = injector.getInstance(key);
239    assertEquals(mapOf("a", "A", "b", "B", "c", "C"), abc);
240    assertMapVisitor(key, stringType, stringType, setOf(module), BOTH, false, 0,
241        instance("a", "A"), instance("b", "B"), instance("c", "C"));
242
243    // just make sure these succeed
244    injector.getInstance(Key.get(mapOfStringProvider, Abc.class));
245    injector.getInstance(Key.get(mapOfStringJavaxProvider, Abc.class));
246  }
247
248  public void testMapBinderWithMultipleAnnotationValueSets() {
249    Module module = new AbstractModule() {
250      @Override protected void configure() {
251        MapBinder<String, String> abcMapBinder = MapBinder.newMapBinder(
252            binder(), String.class, String.class, named("abc"));
253        abcMapBinder.addBinding("a").toInstance("A");
254        abcMapBinder.addBinding("b").toInstance("B");
255        abcMapBinder.addBinding("c").toInstance("C");
256
257        MapBinder<String, String> deMapBinder = MapBinder.newMapBinder(
258            binder(), String.class, String.class, named("de"));
259        deMapBinder.addBinding("d").toInstance("D");
260        deMapBinder.addBinding("e").toInstance("E");
261      }
262    };
263    Injector injector = Guice.createInjector(module);
264
265    Key<Map<String, String>> abcKey = Key.get(mapOfString, named("abc"));
266    Map<String, String> abc = injector.getInstance(abcKey);
267    Key<Map<String, String>> deKey = Key.get(mapOfString, named("de"));
268    Map<String, String> de = injector.getInstance(deKey);
269    assertEquals(mapOf("a", "A", "b", "B", "c", "C"), abc);
270    assertEquals(mapOf("d", "D", "e", "E"), de);
271    assertMapVisitor(abcKey, stringType, stringType, setOf(module), BOTH, false, 1,
272        instance("a", "A"), instance("b", "B"), instance("c", "C"));
273    assertMapVisitor(deKey, stringType, stringType, setOf(module), BOTH, false, 1,
274        instance("d", "D"), instance("e", "E"));
275
276    // just make sure these succeed
277    injector.getInstance(Key.get(mapOfStringProvider, named("abc")));
278    injector.getInstance(Key.get(mapOfStringJavaxProvider, named("abc")));
279    injector.getInstance(Key.get(mapOfStringProvider, named("de")));
280    injector.getInstance(Key.get(mapOfStringJavaxProvider, named("de")));
281  }
282
283  public void testMapBinderWithMultipleAnnotationTypeSets() {
284    Module module = new AbstractModule() {
285      @Override protected void configure() {
286        MapBinder<String, String> abcMapBinder = MapBinder.newMapBinder(
287            binder(), String.class, String.class, Abc.class);
288        abcMapBinder.addBinding("a").toInstance("A");
289        abcMapBinder.addBinding("b").toInstance("B");
290        abcMapBinder.addBinding("c").toInstance("C");
291
292        MapBinder<String, String> deMapBinder = MapBinder.newMapBinder(
293            binder(), String.class, String.class, De.class);
294        deMapBinder.addBinding("d").toInstance("D");
295        deMapBinder.addBinding("e").toInstance("E");
296      }
297    };
298    Injector injector = Guice.createInjector(module);
299
300    Key<Map<String, String>> abcKey = Key.get(mapOfString, Abc.class);
301    Map<String, String> abc = injector.getInstance(abcKey);
302    Key<Map<String, String>> deKey = Key.get(mapOfString, De.class);
303    Map<String, String> de = injector.getInstance(deKey);
304    assertEquals(mapOf("a", "A", "b", "B", "c", "C"), abc);
305    assertEquals(mapOf("d", "D", "e", "E"), de);
306    assertMapVisitor(abcKey, stringType, stringType, setOf(module), BOTH, false, 1,
307        instance("a", "A"), instance("b", "B"), instance("c", "C"));
308    assertMapVisitor(deKey, stringType, stringType, setOf(module), BOTH, false, 1,
309        instance("d", "D"), instance("e", "E"));
310
311    // just make sure these succeed
312    injector.getInstance(Key.get(mapOfStringProvider, Abc.class));
313    injector.getInstance(Key.get(mapOfStringJavaxProvider, Abc.class));
314    injector.getInstance(Key.get(mapOfStringProvider, De.class));
315    injector.getInstance(Key.get(mapOfStringJavaxProvider, De.class));
316  }
317
318  public void testMapBinderWithMultipleTypes() {
319    Module module = new AbstractModule() {
320      @Override protected void configure() {
321        MapBinder.newMapBinder(binder(), String.class, String.class)
322            .addBinding("a").toInstance("A");
323        MapBinder.newMapBinder(binder(), String.class, Integer.class)
324            .addBinding("1").toInstance(1);
325      }
326    };
327    Injector injector = Guice.createInjector(module);
328
329    assertEquals(mapOf("a", "A"), injector.getInstance(Key.get(mapOfString)));
330    assertEquals(mapOf("1", 1), injector.getInstance(Key.get(mapOfInteger)));
331    assertMapVisitor(Key.get(mapOfString), stringType, stringType, setOf(module), BOTH, false, 1,
332        instance("a", "A"));
333    assertMapVisitor(Key.get(mapOfInteger), stringType, intType, setOf(module), BOTH, false, 1,
334        instance("1", 1));
335  }
336
337  public void testMapBinderWithEmptyMap() {
338    Module module = new AbstractModule() {
339      @Override protected void configure() {
340        MapBinder.newMapBinder(binder(), String.class, String.class);
341      }
342    };
343    Injector injector = Guice.createInjector(module);
344
345    Map<String, String> map = injector.getInstance(Key.get(mapOfString));
346    assertEquals(Collections.emptyMap(), map);
347    assertMapVisitor(Key.get(mapOfString), stringType, stringType, setOf(module), BOTH, false, 0);
348  }
349
350  public void testMapBinderMapIsUnmodifiable() {
351    Injector injector = Guice.createInjector(new AbstractModule() {
352      @Override protected void configure() {
353        MapBinder.newMapBinder(binder(), String.class, String.class)
354            .addBinding("a").toInstance("A");
355      }
356    });
357
358    Map<String, String> map = injector.getInstance(Key.get(mapOfString));
359    try {
360      map.clear();
361      fail();
362    } catch(UnsupportedOperationException expected) {
363    }
364  }
365
366  public void testMapBinderMapIsLazy() {
367    Module module = new AbstractModule() {
368      @Override protected void configure() {
369        MapBinder.newMapBinder(binder(), String.class, Integer.class)
370            .addBinding("num").toProvider(new Provider<Integer>() {
371          int nextValue = 1;
372          @Override public Integer get() {
373            return nextValue++;
374          }
375        });
376      }
377    };
378    Injector injector = Guice.createInjector(module);
379
380    assertEquals(mapOf("num", 1), injector.getInstance(Key.get(mapOfInteger)));
381    assertEquals(mapOf("num", 2), injector.getInstance(Key.get(mapOfInteger)));
382    assertEquals(mapOf("num", 3), injector.getInstance(Key.get(mapOfInteger)));
383    assertMapVisitor(Key.get(mapOfInteger), stringType, intType, setOf(module), BOTH, false, 0,
384        providerInstance("num", 1));
385  }
386
387  public void testMapBinderMapForbidsDuplicateKeys() {
388    Module module = new AbstractModule() {
389      @Override protected void configure() {
390        MapBinder<String, String> multibinder = MapBinder.newMapBinder(
391            binder(), String.class, String.class);
392        multibinder.addBinding("a").toInstance("A");
393        multibinder.addBinding("a").toInstance("B");
394      }
395    };
396    try {
397      Guice.createInjector(module);
398      fail();
399    } catch(CreationException expected) {
400      assertContains(expected.getMessage(),
401          "Map injection failed due to duplicated key \"a\"");
402    }
403
404    assertMapVisitor(Key.get(mapOfString), stringType, stringType, setOf(module), MODULE, false, 0,
405        instance("a", "A"), instance("a", "B"));
406  }
407
408  public void testExhaustiveDuplicateErrorMessage() throws Exception {
409    class Module1 extends AbstractModule {
410      @Override protected void configure() {
411        MapBinder<String, Object> mapbinder =
412            MapBinder.newMapBinder(binder(), String.class, Object.class);
413        mapbinder.addBinding("a").to(String.class);
414      }
415    }
416    class Module2 extends AbstractModule {
417      @Override protected void configure() {
418        MapBinder<String, Object> mapbinder =
419            MapBinder.newMapBinder(binder(), String.class, Object.class);
420        mapbinder.addBinding("a").to(Integer.class);
421        mapbinder.addBinding("b").to(String.class);
422      }
423    }
424    class Module3 extends AbstractModule {
425      @Override protected void configure() {
426        MapBinder<String, Object> mapbinder =
427            MapBinder.newMapBinder(binder(), String.class, Object.class);
428        mapbinder.addBinding("b").to(Integer.class);
429      }
430    }
431    class Main extends AbstractModule {
432      @Override protected void configure() {
433        MapBinder.newMapBinder(binder(), String.class, Object.class);
434        install(new Module1());
435        install(new Module2());
436        install(new Module3());
437      }
438      @Provides String provideString() { return "foo"; }
439      @Provides Integer provideInt() { return 42; }
440    }
441    try {
442      Guice.createInjector(new Main());
443      fail();
444    } catch(CreationException ce) {
445      assertContains(ce.getMessage(),
446          "Map injection failed due to duplicated key \"a\", from bindings:",
447          asModuleChain(Main.class, Module1.class),
448          asModuleChain(Main.class, Module2.class),
449          "and key: \"b\", from bindings:",
450          asModuleChain(Main.class, Module2.class),
451          asModuleChain(Main.class, Module3.class),
452          "at " + Main.class.getName() + ".configure(",
453          asModuleChain(Main.class, MapBinder.RealMapBinder.class));
454      assertEquals(1, ce.getErrorMessages().size());
455    }
456  }
457
458  public void testMapBinderMapPermitDuplicateElements() {
459    Module ab = new AbstractModule() {
460      @Override protected void configure() {
461        MapBinder<String, String> multibinder = MapBinder.newMapBinder(
462            binder(), String.class, String.class);
463        multibinder.addBinding("a").toInstance("A");
464        multibinder.addBinding("b").toInstance("B");
465        multibinder.permitDuplicates();
466      }
467    };
468    Module bc = new AbstractModule() {
469      @Override protected void configure() {
470        MapBinder<String, String> multibinder = MapBinder.newMapBinder(
471            binder(), String.class, String.class);
472        multibinder.addBinding("b").toInstance("B");
473        multibinder.addBinding("c").toInstance("C");
474        multibinder.permitDuplicates();
475      }
476    };
477    Injector injector = Guice.createInjector(ab, bc);
478
479    assertEquals(mapOf("a", "A", "b", "B", "c", "C"), injector.getInstance(Key.get(mapOfString)));
480    assertMapVisitor(Key.get(mapOfString), stringType, stringType, setOf(ab, bc), BOTH, true, 0,
481        instance("a", "A"), instance("b", "B"), instance("c", "C"));
482  }
483
484  public void testMapBinderMapDoesNotDedupeDuplicateValues() {
485    class ValueType {
486      int keyPart;
487      int dataPart;
488      private ValueType(int keyPart, int dataPart) {
489        this.keyPart = keyPart;
490        this.dataPart = dataPart;
491      }
492      @Override
493      public boolean equals(Object obj) {
494        return (obj instanceof ValueType) && (keyPart == ((ValueType) obj).keyPart);
495      }
496      @Override
497      public int hashCode() {
498        return keyPart;
499      }
500    }
501    Module m1 = new AbstractModule() {
502      @Override protected void configure() {
503        MapBinder<String, ValueType> multibinder = MapBinder.newMapBinder(
504            binder(), String.class, ValueType.class);
505        multibinder.addBinding("a").toInstance(new ValueType(1, 2));
506      }
507    };
508    Module m2 = new AbstractModule() {
509      @Override protected void configure() {
510        MapBinder<String, ValueType> multibinder = MapBinder.newMapBinder(
511            binder(), String.class, ValueType.class);
512        multibinder.addBinding("b").toInstance(new ValueType(1, 3));
513      }
514    };
515
516    Injector injector = Guice.createInjector(m1, m2);
517    Map<String, ValueType> map = injector.getInstance(new Key<Map<String, ValueType>>() {});
518    assertEquals(2, map.get("a").dataPart);
519    assertEquals(3, map.get("b").dataPart);
520  }
521
522  public void testMapBinderMultimap() {
523    AbstractModule ab1c = new AbstractModule() {
524      @Override protected void configure() {
525        MapBinder<String, String> multibinder = MapBinder.newMapBinder(
526            binder(), String.class, String.class);
527        multibinder.addBinding("a").toInstance("A");
528        multibinder.addBinding("b").toInstance("B1");
529        multibinder.addBinding("c").toInstance("C");
530      }
531    };
532    AbstractModule b2c = new AbstractModule() {
533      @Override protected void configure() {
534        MapBinder<String, String> multibinder = MapBinder.newMapBinder(
535            binder(), String.class, String.class);
536        multibinder.addBinding("b").toInstance("B2");
537        multibinder.addBinding("c").toInstance("C");
538        multibinder.permitDuplicates();
539      }
540    };
541    Injector injector = Guice.createInjector(ab1c, b2c);
542
543    assertEquals(mapOf("a", setOf("A"), "b", setOf("B1", "B2"), "c", setOf("C")),
544        injector.getInstance(Key.get(mapOfSetOfString)));
545    assertMapVisitor(Key.get(mapOfString), stringType, stringType, setOf(ab1c, b2c), BOTH, true, 0,
546        instance("a", "A"), instance("b", "B1"), instance("b", "B2"), instance("c", "C"));
547  }
548
549  public void testMapBinderMultimapWithAnotation() {
550    AbstractModule ab1 = new AbstractModule() {
551      @Override protected void configure() {
552        MapBinder<String, String> multibinder = MapBinder.newMapBinder(
553            binder(), String.class, String.class, Abc.class);
554        multibinder.addBinding("a").toInstance("A");
555        multibinder.addBinding("b").toInstance("B1");
556      }
557    };
558    AbstractModule b2c = new AbstractModule() {
559      @Override protected void configure() {
560        MapBinder<String, String> multibinder = MapBinder.newMapBinder(
561            binder(), String.class, String.class, Abc.class);
562        multibinder.addBinding("b").toInstance("B2");
563        multibinder.addBinding("c").toInstance("C");
564        multibinder.permitDuplicates();
565      }
566    };
567    Injector injector = Guice.createInjector(ab1, b2c);
568
569    assertEquals(mapOf("a", setOf("A"), "b", setOf("B1", "B2"), "c", setOf("C")),
570        injector.getInstance(Key.get(mapOfSetOfString, Abc.class)));
571    try {
572      injector.getInstance(Key.get(mapOfSetOfString));
573      fail();
574    } catch (ConfigurationException expected) {}
575
576    assertMapVisitor(Key.get(mapOfString, Abc.class), stringType, stringType, setOf(ab1, b2c), BOTH, true, 0,
577        instance("a", "A"), instance("b", "B1"), instance("b", "B2"), instance("c", "C"));
578  }
579
580  public void testMapBinderMultimapIsUnmodifiable() {
581    Injector injector = Guice.createInjector(new AbstractModule() {
582      @Override protected void configure() {
583        MapBinder<String, String> mapBinder = MapBinder.newMapBinder(
584            binder(), String.class, String.class);
585        mapBinder.addBinding("a").toInstance("A");
586        mapBinder.permitDuplicates();
587      }
588    });
589
590    Map<String, Set<String>> map = injector.getInstance(Key.get(mapOfSetOfString));
591    try {
592      map.clear();
593      fail();
594    } catch(UnsupportedOperationException expected) {
595    }
596    try {
597      map.get("a").clear();
598      fail();
599    } catch(UnsupportedOperationException expected) {
600    }
601  }
602
603  public void testMapBinderMapForbidsNullKeys() {
604    try {
605      Guice.createInjector(new AbstractModule() {
606        @Override protected void configure() {
607          MapBinder.newMapBinder(binder(), String.class, String.class).addBinding(null);
608        }
609      });
610      fail();
611    } catch (CreationException expected) {}
612  }
613
614  public void testMapBinderMapForbidsNullValues() {
615    Module m = new AbstractModule() {
616      @Override protected void configure() {
617        MapBinder.newMapBinder(binder(), String.class, String.class)
618            .addBinding("null").toProvider(Providers.<String>of(null));
619      }
620    };
621    Injector injector = Guice.createInjector(m);
622
623    try {
624      injector.getInstance(Key.get(mapOfString));
625      fail();
626    } catch(ProvisionException expected) {
627      assertContains(expected.getMessage(),
628          "1) Map injection failed due to null value for key \"null\", bound at: "
629          + m.getClass().getName() + ".configure(");
630    }
631  }
632
633  public void testMapBinderProviderIsScoped() {
634    final Provider<Integer> counter = new Provider<Integer>() {
635      int next = 1;
636      @Override public Integer get() {
637        return next++;
638      }
639    };
640
641    Injector injector = Guice.createInjector(new AbstractModule() {
642      @Override protected void configure() {
643        MapBinder.newMapBinder(binder(), String.class, Integer.class)
644            .addBinding("one").toProvider(counter).asEagerSingleton();
645      }
646    });
647
648    assertEquals(1, (int) injector.getInstance(Key.get(mapOfInteger)).get("one"));
649    assertEquals(1, (int) injector.getInstance(Key.get(mapOfInteger)).get("one"));
650  }
651
652  public void testSourceLinesInMapBindings() {
653    try {
654      Guice.createInjector(new AbstractModule() {
655        @Override protected void configure() {
656          MapBinder.newMapBinder(binder(), String.class, Integer.class)
657              .addBinding("one");
658        }
659      });
660      fail();
661    } catch (CreationException expected) {
662      assertContains(expected.getMessage(),
663          "1) No implementation for java.lang.Integer",
664          "at " + getClass().getName());
665    }
666  }
667
668  /** We just want to make sure that mapbinder's binding depends on the underlying multibinder. */
669  public void testMultibinderDependencies() {
670    Injector injector = Guice.createInjector(new AbstractModule() {
671      @Override protected void configure() {
672        MapBinder<Integer, String> mapBinder
673            = MapBinder.newMapBinder(binder(), Integer.class, String.class);
674        mapBinder.addBinding(1).toInstance("A");
675        mapBinder.addBinding(2).to(Key.get(String.class, Names.named("b")));
676
677        bindConstant().annotatedWith(Names.named("b")).to("B");
678      }
679    });
680
681    Binding<Map<Integer, String>> binding = injector.getBinding(new Key<Map<Integer, String>>() {});
682    HasDependencies withDependencies = (HasDependencies) binding;
683    Key<?> setKey = new Key<Set<Map.Entry<Integer, Provider<String>>>>() {};
684    assertEquals(ImmutableSet.<Dependency<?>>of(Dependency.get(setKey)),
685        withDependencies.getDependencies());
686    Set<String> elements = Sets.newHashSet();
687    elements.addAll(recurseForDependencies(injector, withDependencies));
688    assertEquals(ImmutableSet.of("A", "B"), elements);
689  }
690
691  private Set<String> recurseForDependencies(Injector injector, HasDependencies hasDependencies) {
692    Set<String> elements = Sets.newHashSet();
693    for (Dependency<?> dependency : hasDependencies.getDependencies()) {
694      Binding<?> binding = injector.getBinding(dependency.getKey());
695      HasDependencies deps = (HasDependencies) binding;
696      if (binding instanceof InstanceBinding) {
697        elements.add((String) ((InstanceBinding<?>) binding).getInstance());
698      } else {
699        elements.addAll(recurseForDependencies(injector, deps));
700      }
701    }
702    return elements;
703  }
704
705  /** We just want to make sure that mapbinder's binding depends on the underlying multibinder. */
706  public void testMultibinderDependenciesInToolStage() {
707    Injector injector = Guice.createInjector(Stage.TOOL, new AbstractModule() {
708      @Override protected void configure() {
709          MapBinder<Integer, String> mapBinder
710              = MapBinder.newMapBinder(binder(), Integer.class, String.class);
711          mapBinder.addBinding(1).toInstance("A");
712          mapBinder.addBinding(2).to(Key.get(String.class, Names.named("b")));
713
714          bindConstant().annotatedWith(Names.named("b")).to("B");
715        }});
716
717    Binding<Map<Integer, String>> binding = injector.getBinding(new Key<Map<Integer, String>>() {});
718    HasDependencies withDependencies = (HasDependencies) binding;
719    Key<?> setKey = new Key<Set<Map.Entry<Integer, Provider<String>>>>() {};
720    assertEquals(ImmutableSet.<Dependency<?>>of(Dependency.get(setKey)),
721        withDependencies.getDependencies());
722  }
723
724
725  /**
726   * Our implementation maintains order, but doesn't guarantee it in the API spec.
727   * TODO: specify the iteration order?
728   */
729  public void testBindOrderEqualsIterationOrder() {
730    Injector injector = Guice.createInjector(
731        new AbstractModule() {
732          @Override protected void configure() {
733            MapBinder<String, String> mapBinder
734                = MapBinder.newMapBinder(binder(), String.class, String.class);
735            mapBinder.addBinding("leonardo").toInstance("blue");
736            mapBinder.addBinding("donatello").toInstance("purple");
737            install(new AbstractModule() {
738              @Override protected void configure() {
739                MapBinder.newMapBinder(binder(), String.class, String.class)
740                    .addBinding("michaelangelo").toInstance("orange");
741              }
742            });
743          }
744        },
745        new AbstractModule() {
746          @Override protected void configure() {
747            MapBinder.newMapBinder(binder(), String.class, String.class)
748                .addBinding("raphael").toInstance("red");
749          }
750        });
751
752    Map<String, String> map = injector.getInstance(new Key<Map<String, String>>() {});
753    Iterator<Map.Entry<String, String>> iterator = map.entrySet().iterator();
754    assertEquals(Maps.immutableEntry("leonardo", "blue"), iterator.next());
755    assertEquals(Maps.immutableEntry("donatello", "purple"), iterator.next());
756    assertEquals(Maps.immutableEntry("michaelangelo", "orange"), iterator.next());
757    assertEquals(Maps.immutableEntry("raphael", "red"), iterator.next());
758  }
759
760  /**
761   * With overrides, we should get the union of all map bindings.
762   */
763  public void testModuleOverrideAndMapBindings() {
764    Module ab = new AbstractModule() {
765      @Override protected void configure() {
766        MapBinder<String, String> multibinder = MapBinder.newMapBinder(binder(), String.class, String.class);
767        multibinder.addBinding("a").toInstance("A");
768        multibinder.addBinding("b").toInstance("B");
769      }
770    };
771    Module cd = new AbstractModule() {
772      @Override protected void configure() {
773        MapBinder<String, String> multibinder = MapBinder.newMapBinder(binder(), String.class, String.class);
774        multibinder.addBinding("c").toInstance("C");
775        multibinder.addBinding("d").toInstance("D");
776      }
777    };
778    Module ef = new AbstractModule() {
779      @Override protected void configure() {
780        MapBinder<String, String> multibinder = MapBinder.newMapBinder(binder(), String.class, String.class);
781        multibinder.addBinding("e").toInstance("E");
782        multibinder.addBinding("f").toInstance("F");
783      }
784    };
785
786    Module abcd = Modules.override(ab).with(cd);
787    Injector injector = Guice.createInjector(abcd, ef);
788    assertEquals(mapOf("a", "A", "b", "B", "c", "C", "d", "D", "e", "E", "f", "F"),
789        injector.getInstance(Key.get(mapOfString)));
790    assertMapVisitor(Key.get(mapOfString), stringType, stringType, setOf(abcd, ef), BOTH, false, 0,
791        instance("a", "A"), instance("b", "B"), instance("c", "C"), instance("d", "D"), instance(
792            "e", "E"), instance("f", "F"));
793  }
794
795  public void testDeduplicateMapBindings() {
796    Module module = new AbstractModule() {
797      @Override protected void configure() {
798        MapBinder<String, String> mapbinder =
799            MapBinder.newMapBinder(binder(), String.class, String.class);
800        mapbinder.addBinding("a").toInstance("A");
801        mapbinder.addBinding("a").toInstance("A");
802        mapbinder.addBinding("b").toInstance("B");
803        mapbinder.addBinding("b").toInstance("B");
804
805      }
806    };
807    Injector injector = Guice.createInjector(module);
808    assertEquals(mapOf("a", "A", "b", "B"),
809        injector.getInstance(Key.get(mapOfString)));
810    assertMapVisitor(Key.get(mapOfString), stringType, stringType, setOf(module), BOTH, false, 0,
811        instance("a", "A"), instance("b", "B"));
812  }
813
814  /**
815   * With overrides, we should get the union of all map bindings.
816   */
817  public void testModuleOverrideAndMapBindingsWithPermitDuplicates() {
818    Module abc = new AbstractModule() {
819      @Override protected void configure() {
820        MapBinder<String, String> multibinder = MapBinder.newMapBinder(binder(), String.class, String.class);
821        multibinder.addBinding("a").toInstance("A");
822        multibinder.addBinding("b").toInstance("B");
823        multibinder.addBinding("c").toInstance("C");
824        multibinder.permitDuplicates();
825      }
826    };
827    Module cd = new AbstractModule() {
828      @Override protected void configure() {
829        MapBinder<String, String> multibinder = MapBinder.newMapBinder(binder(), String.class, String.class);
830        multibinder.addBinding("c").toInstance("C");
831        multibinder.addBinding("d").toInstance("D");
832        multibinder.permitDuplicates();
833      }
834    };
835    Module ef = new AbstractModule() {
836      @Override protected void configure() {
837        MapBinder<String, String> multibinder = MapBinder.newMapBinder(binder(), String.class, String.class);
838        multibinder.addBinding("e").toInstance("E");
839        multibinder.addBinding("f").toInstance("F");
840        multibinder.permitDuplicates();
841      }
842    };
843
844    Module abcd = Modules.override(abc).with(cd);
845    Injector injector = Guice.createInjector(abcd, ef);
846    assertEquals(mapOf("a", "A", "b", "B", "c", "C", "d", "D", "e", "E", "f", "F"),
847        injector.getInstance(Key.get(mapOfString)));
848    assertMapVisitor(Key.get(mapOfString), stringType, stringType, setOf(abcd, ef), BOTH, true, 0,
849        instance("a", "A"), instance("b", "B"), instance("c", "C"), instance(
850            "d", "D"), instance("e", "E"), instance("f", "F"));
851
852  }
853
854  /** Ensure there are no initialization race conditions in basic map injection. */
855  public void testBasicMapDependencyInjection() {
856    final AtomicReference<Map<String, String>> injectedMap =
857        new AtomicReference<Map<String, String>>();
858    final Object anObject = new Object() {
859      @Inject void initialize(Map<String, String> map) {
860        injectedMap.set(map);
861      }
862    };
863    Module abc = new AbstractModule() {
864      @Override protected void configure() {
865        requestInjection(anObject);
866        MapBinder<String, String> multibinder =
867            MapBinder.newMapBinder(binder(), String.class, String.class);
868        multibinder.addBinding("a").toInstance("A");
869        multibinder.addBinding("b").toInstance("B");
870        multibinder.addBinding("c").toInstance("C");
871      }
872    };
873    Guice.createInjector(abc);
874    assertEquals(mapOf("a", "A", "b", "B", "c", "C"), injectedMap.get());
875  }
876
877  /** Ensure there are no initialization race conditions in provider multimap injection. */
878  public void testProviderMultimapDependencyInjection() {
879    final AtomicReference<Map<String, Set<Provider<String>>>> injectedMultimap =
880        new AtomicReference<Map<String, Set<Provider<String>>>>();
881    final Object anObject = new Object() {
882      @Inject void initialize(Map<String, Set<Provider<String>>> multimap) {
883        injectedMultimap.set(multimap);
884      }
885    };
886    Module abc = new AbstractModule() {
887      @Override protected void configure() {
888        requestInjection(anObject);
889        MapBinder<String, String> multibinder =
890            MapBinder.newMapBinder(binder(), String.class, String.class);
891        multibinder.permitDuplicates();
892        multibinder.addBinding("a").toInstance("A");
893        multibinder.addBinding("b").toInstance("B");
894        multibinder.addBinding("c").toInstance("C");
895      }
896    };
897    Guice.createInjector(abc);
898    Map<String, String> map = Maps.transformValues(injectedMultimap.get(),
899        new Function<Set<Provider<String>>, String>() {
900          @Override public String apply(Set<Provider<String>> stringProvidersSet) {
901            return Iterables.getOnlyElement(stringProvidersSet).get();
902          }
903        });
904    assertEquals(mapOf("a", "A", "b", "B", "c", "C"), map);
905  }
906
907  @Retention(RUNTIME) @BindingAnnotation
908  @interface Abc {}
909
910  @Retention(RUNTIME) @BindingAnnotation
911  @interface De {}
912
913  @SuppressWarnings("unchecked")
914  private <K, V> Map<K, V> mapOf(Object... elements) {
915    Map<K, V> result = new HashMap<K, V>();
916    for (int i = 0; i < elements.length; i += 2) {
917      result.put((K)elements[i], (V)elements[i+1]);
918    }
919    return result;
920  }
921
922  @SuppressWarnings("unchecked")
923  private <V> Set<V> setOf(V... elements) {
924    return new HashSet<V>(Arrays.asList(elements));
925  }
926
927  @BindingAnnotation
928  @Retention(RetentionPolicy.RUNTIME)
929  @Target({ElementType.FIELD, ElementType.PARAMETER, ElementType.METHOD})
930  private static @interface Marker {}
931
932  @Marker
933  public void testMapBinderMatching() throws Exception {
934    Method m = MapBinderTest.class.getDeclaredMethod("testMapBinderMatching");
935    assertNotNull(m);
936    final Annotation marker = m.getAnnotation(Marker.class);
937    Injector injector = Guice.createInjector(new AbstractModule() {
938      @Override public void configure() {
939        MapBinder<Integer, Integer> mb1 =
940          MapBinder.newMapBinder(binder(), Integer.class, Integer.class, Marker.class);
941        MapBinder<Integer, Integer> mb2 =
942          MapBinder.newMapBinder(binder(), Integer.class, Integer.class, marker);
943        mb1.addBinding(1).toInstance(1);
944        mb2.addBinding(2).toInstance(2);
945
946        // This assures us that the two binders are equivalent, so we expect the instance added to
947        // each to have been added to one set.
948        assertEquals(mb1, mb2);
949      }
950    });
951    TypeLiteral<Map<Integer, Integer>> t = new TypeLiteral<Map<Integer, Integer>>() {};
952    Map<Integer, Integer> s1 = injector.getInstance(Key.get(t, Marker.class));
953    Map<Integer, Integer> s2 = injector.getInstance(Key.get(t, marker));
954
955    // This assures us that the two sets are in fact equal.  They may not be same set (as in Java
956    // object identical), but we shouldn't expect that, since probably Guice creates the set each
957    // time in case the elements are dependent on scope.
958    assertEquals(s1, s2);
959
960    // This ensures that MultiBinder is internally using the correct set name --
961    // making sure that instances of marker annotations have the same set name as
962    // MarkerAnnotation.class.
963    Map<Integer, Integer> expected = new HashMap<Integer, Integer>();
964    expected.put(1, 1);
965    expected.put(2, 2);
966    assertEquals(expected, s1);
967  }
968
969  public void testTwoMapBindersAreDistinct() {
970    Injector injector = Guice.createInjector(new AbstractModule() {
971      @Override protected void configure() {
972        MapBinder.newMapBinder(binder(), String.class, String.class)
973            .addBinding("A").toInstance("a");
974
975        MapBinder.newMapBinder(binder(), Integer.class, String.class)
976            .addBinding(1).toInstance("b");
977      }
978    });
979    Collector collector = new Collector();
980    Binding<Map<String, String>> map1 = injector.getBinding(Key.get(mapOfString));
981    map1.acceptTargetVisitor(collector);
982    assertNotNull(collector.mapbinding);
983    MapBinderBinding<?> map1Binding = collector.mapbinding;
984
985    Binding<Map<Integer, String>> map2 = injector.getBinding(Key.get(mapOfIntString));
986    map2.acceptTargetVisitor(collector);
987    assertNotNull(collector.mapbinding);
988    MapBinderBinding<?> map2Binding = collector.mapbinding;
989
990    List<Binding<String>> bindings = injector.findBindingsByType(stringType);
991    assertEquals("should have two elements: " + bindings, 2, bindings.size());
992    Binding<String> a = bindings.get(0);
993    Binding<String> b = bindings.get(1);
994    assertEquals("a", ((InstanceBinding<String>) a).getInstance());
995    assertEquals("b", ((InstanceBinding<String>) b).getInstance());
996
997    // Make sure the correct elements belong to their own sets.
998    assertTrue(map1Binding.containsElement(a));
999    assertFalse(map1Binding.containsElement(b));
1000
1001    assertFalse(map2Binding.containsElement(a));
1002    assertTrue(map2Binding.containsElement(b));
1003  }
1004
1005  // Tests for com.google.inject.internal.WeakKeySet not leaking memory.
1006  public void testWeakKeySet_integration_mapbinder() {
1007    Key<Map<String, String>> mapKey = Key.get(new TypeLiteral<Map<String, String>>() {});
1008
1009    Injector parentInjector = Guice.createInjector(new AbstractModule() {
1010          @Override protected void configure() {
1011            bind(String.class).toInstance("hi");
1012          }
1013        });
1014    WeakKeySetUtils.assertNotBlacklisted(parentInjector, mapKey);
1015
1016    Injector childInjector = parentInjector.createChildInjector(new AbstractModule() {
1017      @Override protected void configure() {
1018        MapBinder<String, String> binder =
1019            MapBinder.newMapBinder(binder(), String.class, String.class);
1020        binder.addBinding("bar").toInstance("foo");
1021      }
1022    });
1023    WeakReference<Injector> weakRef = new WeakReference<Injector>(childInjector);
1024    WeakKeySetUtils.assertBlacklisted(parentInjector, mapKey);
1025
1026    // Clear the ref, GC, and ensure that we are no longer blacklisting.
1027    childInjector = null;
1028
1029    Asserts.awaitClear(weakRef);
1030    WeakKeySetUtils.assertNotBlacklisted(parentInjector, mapKey);
1031  }
1032}
1033