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