• Home
  • History
  • Annotate
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.multibindings;
18 
19 import static com.google.inject.Asserts.asModuleChain;
20 import static com.google.inject.Asserts.assertContains;
21 import static com.google.inject.multibindings.SpiUtils.VisitType.BOTH;
22 import static com.google.inject.multibindings.SpiUtils.VisitType.MODULE;
23 import static com.google.inject.multibindings.SpiUtils.assertMapVisitor;
24 import static com.google.inject.multibindings.SpiUtils.instance;
25 import static com.google.inject.multibindings.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.Maps;
33 import com.google.common.collect.Sets;
34 import com.google.inject.AbstractModule;
35 import com.google.inject.Asserts;
36 import com.google.inject.Binding;
37 import com.google.inject.BindingAnnotation;
38 import com.google.inject.ConfigurationException;
39 import com.google.inject.CreationException;
40 import com.google.inject.Guice;
41 import com.google.inject.Inject;
42 import com.google.inject.Injector;
43 import com.google.inject.Key;
44 import com.google.inject.Module;
45 import com.google.inject.Provider;
46 import com.google.inject.Provides;
47 import com.google.inject.ProvisionException;
48 import com.google.inject.Stage;
49 import com.google.inject.TypeLiteral;
50 import com.google.inject.internal.WeakKeySetUtils;
51 import com.google.inject.name.Names;
52 import com.google.inject.spi.Dependency;
53 import com.google.inject.spi.HasDependencies;
54 import com.google.inject.spi.InstanceBinding;
55 import com.google.inject.util.Modules;
56 import com.google.inject.util.Providers;
57 import com.google.inject.util.Types;
58 
59 import junit.framework.TestCase;
60 
61 import java.lang.annotation.Annotation;
62 import java.lang.annotation.ElementType;
63 import java.lang.annotation.Retention;
64 import java.lang.annotation.RetentionPolicy;
65 import java.lang.annotation.Target;
66 import java.lang.ref.WeakReference;
67 import java.lang.reflect.Method;
68 import java.lang.reflect.Type;
69 import java.util.Arrays;
70 import java.util.Collection;
71 import java.util.Collections;
72 import java.util.HashMap;
73 import java.util.HashSet;
74 import java.util.Iterator;
75 import java.util.List;
76 import java.util.Map;
77 import java.util.Set;
78 import java.util.concurrent.atomic.AtomicReference;
79 
80 /**
81  * @author dpb@google.com (David P. Baker)
82  */
83 public 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 
javaxProviderOf(Type type)105   private Type javaxProviderOf(Type type) {
106     return Types.newParameterizedType(javax.inject.Provider.class, type);
107   }
108 
mapEntryOf(Type keyType, Type valueType)109   private Type mapEntryOf(Type keyType, Type valueType) {
110     return Types.newParameterizedTypeWithOwner(Map.class, Map.Entry.class, keyType, valueType);
111   }
112 
collectionOf(Type type)113   private Type collectionOf(Type type) {
114     return Types.newParameterizedType(Collection.class, type);
115   }
116 
testAllBindings()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 
testMapBinderAggregatesMultipleModules()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 
testMapBinderAggregationForAnnotationInstance()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 
testMapBinderAggregationForAnnotationType()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 
testMapBinderWithMultipleAnnotationValueSets()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 
testMapBinderWithMultipleAnnotationTypeSets()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 
testMapBinderWithMultipleTypes()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 
testMapBinderWithEmptyMap()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 
testMapBinderMapIsUnmodifiable()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 
testMapBinderMapIsLazy()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 
testMapBinderMapForbidsDuplicateKeys()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 
testExhaustiveDuplicateErrorMessage()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 
testMapBinderMapPermitDuplicateElements()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 
testMapBinderMapDoesNotDedupeDuplicateValues()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 
testMapBinderMultimap()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 
testMapBinderMultimapWithAnotation()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 
testMapBinderMultimapIsUnmodifiable()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 
testMapBinderMapForbidsNullKeys()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 
testMapBinderMapForbidsNullValues()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 
testMapBinderProviderIsScoped()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 
testSourceLinesInMapBindings()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. */
testMultibinderDependencies()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 
recurseForDependencies(Injector injector, HasDependencies hasDependencies)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. */
testMultibinderDependenciesInToolStage()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    */
testBindOrderEqualsIterationOrder()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    */
testModuleOverrideAndMapBindings()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 
testDeduplicateMapBindings()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    */
testModuleOverrideAndMapBindingsWithPermitDuplicates()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. */
testBasicMapDependencyInjection()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. */
testProviderMultimapDependencyInjection()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")
mapOf(Object... elements)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")
setOf(V... elements)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
testMapBinderMatching()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 
testTwoMapBindersAreDistinct()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.
testWeakKeySet_integration_mapbinder()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