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