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