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; 18 19 import static com.google.inject.Asserts.assertContains; 20 import static com.google.inject.name.Names.named; 21 import static java.lang.annotation.ElementType.METHOD; 22 import static java.lang.annotation.ElementType.TYPE; 23 import static java.lang.annotation.RetentionPolicy.RUNTIME; 24 25 import com.google.common.collect.ImmutableList; 26 import com.google.common.collect.Lists; 27 import com.google.inject.binder.AnnotatedBindingBuilder; 28 import com.google.inject.binder.ScopedBindingBuilder; 29 import com.google.inject.name.Named; 30 import com.google.inject.util.Providers; 31 import java.lang.annotation.Retention; 32 import java.lang.annotation.Target; 33 import java.util.Collections; 34 import java.util.List; 35 import java.util.concurrent.atomic.AtomicInteger; 36 import junit.framework.Test; 37 import junit.framework.TestCase; 38 import junit.framework.TestSuite; 39 40 /** @author jessewilson@google.com (Jesse Wilson) */ 41 public class BinderTestSuite extends TestCase { 42 suite()43 public static Test suite() { 44 TestSuite suite = new TestSuite(); 45 46 new Builder() 47 .name("bind A") 48 .module( 49 new AbstractModule() { 50 @Override 51 protected void configure() { 52 bind(A.class); 53 } 54 }) 55 .creationException("No implementation for %s was bound", A.class.getName()) 56 .addToSuite(suite); 57 58 new Builder() 59 .name("bind PlainA named apple") 60 .module( 61 new AbstractModule() { 62 @Override 63 protected void configure() { 64 bind(PlainA.class).annotatedWith(named("apple")); 65 } 66 }) 67 .creationException( 68 "No implementation for %s annotated with %s was bound", 69 PlainA.class.getName(), named("apple")) 70 .addToSuite(suite); 71 72 new Builder() 73 .name("bind A to new PlainA(1)") 74 .module( 75 new AbstractModule() { 76 @Override 77 protected void configure() { 78 bind(A.class).toInstance(new PlainA(1)); 79 } 80 }) 81 .creationTime(CreationTime.NONE) 82 .expectedValues(new PlainA(1), new PlainA(1), new PlainA(1)) 83 .addToSuite(suite); 84 85 new Builder() 86 .name("no binding, AWithProvidedBy") 87 .key(Key.get(AWithProvidedBy.class), InjectsAWithProvidedBy.class) 88 .addToSuite(suite); 89 90 new Builder() 91 .name("no binding, AWithImplementedBy") 92 .key(Key.get(AWithImplementedBy.class), InjectsAWithImplementedBy.class) 93 .addToSuite(suite); 94 95 new Builder() 96 .name("no binding, ScopedA") 97 .key(Key.get(ScopedA.class), InjectsScopedA.class) 98 .expectedValues(new PlainA(201), new PlainA(201), new PlainA(202), new PlainA(202)) 99 .addToSuite(suite); 100 101 new Builder() 102 .name("no binding, AWithProvidedBy named apple") 103 .key(Key.get(AWithProvidedBy.class, named("apple")), InjectsAWithProvidedByNamedApple.class) 104 .configurationException( 105 "No implementation for %s annotated with %s was bound", 106 AWithProvidedBy.class.getName(), named("apple")) 107 .addToSuite(suite); 108 109 new Builder() 110 .name("no binding, AWithImplementedBy named apple") 111 .key( 112 Key.get(AWithImplementedBy.class, named("apple")), 113 InjectsAWithImplementedByNamedApple.class) 114 .configurationException( 115 "No implementation for %s annotated with %s was bound", 116 AWithImplementedBy.class.getName(), named("apple")) 117 .addToSuite(suite); 118 119 new Builder() 120 .name("no binding, ScopedA named apple") 121 .key(Key.get(ScopedA.class, named("apple")), InjectsScopedANamedApple.class) 122 .configurationException( 123 "No implementation for %s annotated with %s was bound", 124 ScopedA.class.getName(), named("apple")) 125 .addToSuite(suite); 126 127 for (final Scoper scoper : Scoper.values()) { 128 new Builder() 129 .name("bind PlainA") 130 .key(Key.get(PlainA.class), InjectsPlainA.class) 131 .module( 132 new AbstractModule() { 133 @Override 134 protected void configure() { 135 AnnotatedBindingBuilder<PlainA> abb = bind(PlainA.class); 136 scoper.configure(abb); 137 } 138 }) 139 .scoper(scoper) 140 .addToSuite(suite); 141 142 new Builder() 143 .name("bind A to PlainA") 144 .module( 145 new AbstractModule() { 146 @Override 147 protected void configure() { 148 ScopedBindingBuilder sbb = bind(A.class).to(PlainA.class); 149 scoper.configure(sbb); 150 } 151 }) 152 .scoper(scoper) 153 .addToSuite(suite); 154 155 new Builder() 156 .name("bind A to PlainAProvider.class") 157 .module( 158 new AbstractModule() { 159 @Override 160 protected void configure() { 161 ScopedBindingBuilder sbb = bind(A.class).toProvider(PlainAProvider.class); 162 scoper.configure(sbb); 163 } 164 }) 165 .scoper(scoper) 166 .addToSuite(suite); 167 168 new Builder() 169 .name("bind A to new PlainAProvider()") 170 .module( 171 new AbstractModule() { 172 @Override 173 protected void configure() { 174 ScopedBindingBuilder sbb = bind(A.class).toProvider(new PlainAProvider()); 175 scoper.configure(sbb); 176 } 177 }) 178 .scoper(scoper) 179 .addToSuite(suite); 180 181 new Builder() 182 .name("bind AWithProvidedBy") 183 .key(Key.get(AWithProvidedBy.class), InjectsAWithProvidedBy.class) 184 .module( 185 new AbstractModule() { 186 @Override 187 protected void configure() { 188 ScopedBindingBuilder sbb = bind(AWithProvidedBy.class); 189 scoper.configure(sbb); 190 } 191 }) 192 .scoper(scoper) 193 .addToSuite(suite); 194 195 new Builder() 196 .name("bind AWithImplementedBy") 197 .key(Key.get(AWithImplementedBy.class), InjectsAWithImplementedBy.class) 198 .module( 199 new AbstractModule() { 200 @Override 201 protected void configure() { 202 ScopedBindingBuilder sbb = bind(AWithImplementedBy.class); 203 scoper.configure(sbb); 204 } 205 }) 206 .scoper(scoper) 207 .addToSuite(suite); 208 209 new Builder() 210 .name("bind ScopedA") 211 .key(Key.get(ScopedA.class), InjectsScopedA.class) 212 .module( 213 new AbstractModule() { 214 @Override 215 protected void configure() { 216 ScopedBindingBuilder sbb = bind(ScopedA.class); 217 scoper.configure(sbb); 218 } 219 }) 220 .expectedValues(new PlainA(201), new PlainA(201), new PlainA(202), new PlainA(202)) 221 .scoper(scoper) 222 .addToSuite(suite); 223 224 new Builder() 225 .name("bind AWithProvidedBy named apple") 226 .module( 227 new AbstractModule() { 228 @Override 229 protected void configure() { 230 scoper.configure(bind(AWithProvidedBy.class).annotatedWith(named("apple"))); 231 } 232 }) 233 .creationException( 234 "No implementation for %s annotated with %s was bound", 235 AWithProvidedBy.class.getName(), named("apple")) 236 .addToSuite(suite); 237 238 new Builder() 239 .name("bind AWithImplementedBy named apple") 240 .module( 241 new AbstractModule() { 242 @Override 243 protected void configure() { 244 scoper.configure(bind(AWithImplementedBy.class).annotatedWith(named("apple"))); 245 } 246 }) 247 .creationException( 248 "No implementation for %s annotated with %s was bound", 249 AWithImplementedBy.class.getName(), named("apple")) 250 .addToSuite(suite); 251 252 new Builder() 253 .name("bind ScopedA named apple") 254 .module( 255 new AbstractModule() { 256 @Override 257 protected void configure() { 258 scoper.configure(bind(ScopedA.class).annotatedWith(named("apple"))); 259 } 260 }) 261 .creationException( 262 "No implementation for %s annotated with %s was bound", 263 ScopedA.class.getName(), named("apple")) 264 .addToSuite(suite); 265 } 266 267 return suite; 268 } 269 270 enum Scoper { 271 UNSCOPED { 272 @Override configure(ScopedBindingBuilder sbb)273 void configure(ScopedBindingBuilder sbb) {} 274 275 @Override apply(Builder builder)276 void apply(Builder builder) {} 277 }, 278 279 EAGER_SINGLETON { 280 @Override configure(ScopedBindingBuilder sbb)281 void configure(ScopedBindingBuilder sbb) { 282 sbb.asEagerSingleton(); 283 } 284 285 @Override apply(Builder builder)286 void apply(Builder builder) { 287 builder.expectedValues(new PlainA(101), new PlainA(101), new PlainA(101)); 288 builder.creationTime(CreationTime.EAGER); 289 } 290 }, 291 292 SCOPES_SINGLETON { 293 @Override configure(ScopedBindingBuilder sbb)294 void configure(ScopedBindingBuilder sbb) { 295 sbb.in(Scopes.SINGLETON); 296 } 297 298 @Override apply(Builder builder)299 void apply(Builder builder) { 300 builder.expectedValues(new PlainA(201), new PlainA(201), new PlainA(201)); 301 } 302 }, 303 304 SINGLETON_DOT_CLASS { 305 @Override configure(ScopedBindingBuilder sbb)306 void configure(ScopedBindingBuilder sbb) { 307 sbb.in(Singleton.class); 308 } 309 310 @Override apply(Builder builder)311 void apply(Builder builder) { 312 builder.expectedValues(new PlainA(201), new PlainA(201), new PlainA(201)); 313 } 314 }, 315 316 TWO_AT_A_TIME_SCOPED_DOT_CLASS { 317 @Override configure(ScopedBindingBuilder sbb)318 void configure(ScopedBindingBuilder sbb) { 319 sbb.in(TwoAtATimeScoped.class); 320 } 321 322 @Override apply(Builder builder)323 void apply(Builder builder) { 324 builder.expectedValues(new PlainA(201), new PlainA(201), new PlainA(202), new PlainA(202)); 325 } 326 }, 327 328 TWO_AT_A_TIME_SCOPE { 329 @Override configure(ScopedBindingBuilder sbb)330 void configure(ScopedBindingBuilder sbb) { 331 sbb.in(new TwoAtATimeScope()); 332 } 333 334 @Override apply(Builder builder)335 void apply(Builder builder) { 336 builder.expectedValues(new PlainA(201), new PlainA(201), new PlainA(202), new PlainA(202)); 337 } 338 }; 339 configure(ScopedBindingBuilder sbb)340 abstract void configure(ScopedBindingBuilder sbb); 341 apply(Builder builder)342 abstract void apply(Builder builder); 343 } 344 345 /** When Guice creates a value, directly or via a provider */ 346 enum CreationTime { 347 NONE, 348 EAGER, 349 LAZY 350 } 351 352 public static class Builder { 353 private String name = "test"; 354 private Key<?> key = Key.get(A.class); 355 private Class<? extends Injectable> injectsKey = InjectsA.class; 356 private List<Module> modules = 357 Lists.<Module>newArrayList( 358 new AbstractModule() { 359 @Override 360 protected void configure() { 361 bindScope(TwoAtATimeScoped.class, new TwoAtATimeScope()); 362 } 363 }); 364 private List<Object> expectedValues = 365 Lists.<Object>newArrayList(new PlainA(201), new PlainA(202), new PlainA(203)); 366 private CreationTime creationTime = CreationTime.LAZY; 367 private String creationException; 368 private String configurationException; 369 module(Module module)370 public Builder module(Module module) { 371 this.modules.add(module); 372 return this; 373 } 374 creationTime(CreationTime creationTime)375 public Builder creationTime(CreationTime creationTime) { 376 this.creationTime = creationTime; 377 return this; 378 } 379 name(String name)380 public Builder name(String name) { 381 this.name = name; 382 return this; 383 } 384 key(Key<?> key, Class<? extends Injectable> injectsKey)385 public Builder key(Key<?> key, Class<? extends Injectable> injectsKey) { 386 this.key = key; 387 this.injectsKey = injectsKey; 388 return this; 389 } 390 creationException(String message, Object... args)391 private Builder creationException(String message, Object... args) { 392 this.creationException = String.format(message, args); 393 return this; 394 } 395 configurationException(String message, Object... args)396 private Builder configurationException(String message, Object... args) { 397 configurationException = String.format(message, args); 398 return this; 399 } 400 scoper(Scoper scoper)401 private Builder scoper(Scoper scoper) { 402 name(name + " in " + scoper); 403 scoper.apply(this); 404 return this; 405 } 406 expectedValues(T... values)407 private <T> Builder expectedValues(T... values) { 408 this.expectedValues.clear(); 409 Collections.addAll(this.expectedValues, values); 410 return this; 411 } 412 addToSuite(TestSuite suite)413 public void addToSuite(TestSuite suite) { 414 if (creationException != null) { 415 suite.addTest(new CreationExceptionTest(this)); 416 417 } else if (configurationException != null) { 418 suite.addTest(new ConfigurationExceptionTest(this)); 419 420 } else { 421 suite.addTest(new SuccessTest(this)); 422 if (creationTime != CreationTime.NONE) { 423 suite.addTest(new UserExceptionsTest(this)); 424 } 425 } 426 } 427 } 428 429 public static class SuccessTest extends TestCase { 430 final String name; 431 final Key<?> key; 432 final Class<? extends Injectable> injectsKey; 433 final ImmutableList<Module> modules; 434 final ImmutableList<Object> expectedValues; 435 SuccessTest(Builder builder)436 public SuccessTest(Builder builder) { 437 super("test"); 438 name = builder.name; 439 key = builder.key; 440 injectsKey = builder.injectsKey; 441 modules = ImmutableList.copyOf(builder.modules); 442 expectedValues = ImmutableList.copyOf(builder.expectedValues); 443 } 444 445 @Override getName()446 public String getName() { 447 return name; 448 } 449 newInjector()450 Injector newInjector() { 451 nextId.set(101); 452 return Guice.createInjector(modules); 453 } 454 test()455 public void test() throws IllegalAccessException, InstantiationException { 456 Injector injector = newInjector(); 457 nextId.set(201); 458 for (Object value : expectedValues) { 459 assertEquals(value, injector.getInstance(key)); 460 } 461 462 Provider<?> provider = newInjector().getProvider(key); 463 nextId.set(201); 464 for (Object value : expectedValues) { 465 assertEquals(value, provider.get()); 466 } 467 468 Provider<?> bindingProvider = newInjector().getBinding(key).getProvider(); 469 nextId.set(201); 470 for (Object value : expectedValues) { 471 assertEquals(value, bindingProvider.get()); 472 } 473 474 injector = newInjector(); 475 nextId.set(201); 476 for (Object value : expectedValues) { 477 Injectable instance = injector.getInstance(injectsKey); 478 assertEquals(value, instance.value); 479 } 480 481 injector = newInjector(); 482 nextId.set(201); 483 for (Object value : expectedValues) { 484 Injectable injectable = injectsKey.newInstance(); 485 injector.injectMembers(injectable); 486 assertEquals(value, injectable.value); 487 } 488 489 Injector injector1 = newInjector(); 490 nextId.set(201); 491 Injectable hasProvider = injector1.getInstance(injectsKey); 492 hasProvider.provider.get(); 493 nextId.set(201); 494 for (Object value : expectedValues) { 495 assertEquals(value, hasProvider.provider.get()); 496 } 497 } 498 } 499 500 public static class CreationExceptionTest extends TestCase { 501 final String name; 502 final Key<?> key; 503 final ImmutableList<Module> modules; 504 final String creationException; 505 CreationExceptionTest(Builder builder)506 public CreationExceptionTest(Builder builder) { 507 super("test"); 508 name = builder.name; 509 key = builder.key; 510 modules = ImmutableList.copyOf(builder.modules); 511 creationException = builder.creationException; 512 } 513 514 @Override getName()515 public String getName() { 516 return "creation errors:" + name; 517 } 518 test()519 public void test() { 520 try { 521 Guice.createInjector(modules); 522 fail(); 523 } catch (CreationException expected) { 524 assertContains(expected.getMessage(), creationException); 525 } 526 } 527 } 528 529 public static class ConfigurationExceptionTest extends TestCase { 530 final String name; 531 final Key<?> key; 532 final Class<? extends Injectable> injectsKey; 533 final ImmutableList<Module> modules; 534 final String configurationException; 535 ConfigurationExceptionTest(Builder builder)536 public ConfigurationExceptionTest(Builder builder) { 537 super("test"); 538 name = builder.name; 539 key = builder.key; 540 injectsKey = builder.injectsKey; 541 modules = ImmutableList.copyOf(builder.modules); 542 configurationException = builder.configurationException; 543 } 544 545 @Override getName()546 public String getName() { 547 return "provision errors:" + name; 548 } 549 newInjector()550 Injector newInjector() { 551 return Guice.createInjector(modules); 552 } 553 test()554 public void test() throws IllegalAccessException, InstantiationException { 555 try { 556 newInjector().getProvider(key); 557 fail(); 558 } catch (ConfigurationException expected) { 559 assertContains(expected.getMessage(), configurationException); 560 } 561 562 try { 563 newInjector().getBinding(key).getProvider(); 564 fail(); 565 } catch (ConfigurationException expected) { 566 assertContains(expected.getMessage(), configurationException); 567 } 568 569 try { 570 newInjector().getInstance(key); 571 fail(); 572 } catch (ConfigurationException expected) { 573 assertContains(expected.getMessage(), configurationException); 574 } 575 576 try { 577 newInjector().getInstance(injectsKey); 578 fail(); 579 } catch (ConfigurationException expected) { 580 assertContains( 581 expected.getMessage(), 582 configurationException, 583 injectsKey.getName() + ".inject", 584 configurationException, 585 injectsKey.getName() + ".inject", 586 "2 errors"); 587 } 588 589 try { 590 Injectable injectable = injectsKey.newInstance(); 591 newInjector().injectMembers(injectable); 592 fail(); 593 } catch (ConfigurationException expected) { 594 assertContains( 595 expected.getMessage(), 596 configurationException, 597 injectsKey.getName() + ".inject", 598 configurationException, 599 injectsKey.getName() + ".inject", 600 "2 errors"); 601 } 602 } 603 } 604 605 public static class UserExceptionsTest extends TestCase { 606 final String name; 607 final Key<?> key; 608 final Class<? extends Injectable> injectsKey; 609 final ImmutableList<Module> modules; 610 final ImmutableList<Object> expectedValues; 611 final CreationTime creationTime; 612 UserExceptionsTest(Builder builder)613 public UserExceptionsTest(Builder builder) { 614 super("test"); 615 name = builder.name; 616 key = builder.key; 617 injectsKey = builder.injectsKey; 618 modules = ImmutableList.copyOf(builder.modules); 619 expectedValues = ImmutableList.copyOf(builder.expectedValues); 620 creationTime = builder.creationTime; 621 } 622 623 @Override getName()624 public String getName() { 625 return "provision errors:" + name; 626 } 627 newInjector()628 Injector newInjector() { 629 return Guice.createInjector(modules); 630 } 631 test()632 public void test() throws IllegalAccessException, InstantiationException { 633 nextId.set(-1); 634 try { 635 newInjector(); 636 assertEquals(CreationTime.LAZY, creationTime); 637 } catch (CreationException expected) { 638 assertEquals(CreationTime.EAGER, creationTime); 639 assertContains(expected.getMessage(), "Illegal value: -1"); 640 return; 641 } 642 643 Provider<?> provider = newInjector().getProvider(key); 644 Provider<?> bindingProvider = newInjector().getBinding(key).getProvider(); 645 646 nextId.set(-1); 647 try { 648 newInjector().getInstance(key); 649 fail(); 650 } catch (ProvisionException expected) { 651 assertContains(expected.getMessage(), "Illegal value: -1"); 652 } 653 654 nextId.set(-1); 655 try { 656 provider.get(); 657 fail(); 658 } catch (ProvisionException expected) { 659 assertContains(expected.getMessage(), "Illegal value: -1"); 660 } 661 662 nextId.set(-1); 663 try { 664 bindingProvider.get(); 665 fail(); 666 } catch (ProvisionException expected) { 667 assertContains(expected.getMessage(), "Illegal value: -1"); 668 } 669 670 try { 671 nextId.set(-1); 672 newInjector().getInstance(injectsKey); 673 fail("Expected ProvisionException"); 674 } catch (ProvisionException expected) { 675 assertContains( 676 expected.getMessage(), 677 "Illegal value: -1", 678 "for the 1st parameter of " + injectsKey.getName() + ".inject"); 679 } 680 681 nextId.set(201); 682 Injectable injectable = injectsKey.newInstance(); 683 try { 684 nextId.set(-1); 685 newInjector().injectMembers(injectable); 686 } catch (ProvisionException expected) { 687 assertContains( 688 expected.getMessage(), 689 "Illegal value: -1", 690 "for the 1st parameter of " + injectsKey.getName() + ".inject"); 691 } 692 693 nextId.set(201); 694 Injectable hasProvider = newInjector().getInstance(injectsKey); 695 hasProvider.provider.get(); 696 try { 697 nextId.set(-1); 698 hasProvider.provider.get(); 699 // TODO(lukes): insert fail() call here 700 } catch (ProvisionException expected) { 701 assertContains(expected.getMessage(), "Illegal value: -1"); 702 } 703 } 704 } 705 706 /** negative to throw, 101... for eager singletons, 201... for everything else */ 707 static final AtomicInteger nextId = new AtomicInteger(); 708 709 @ProvidedBy(PlainAProvider.class) 710 interface AWithProvidedBy {} 711 712 static class InjectsAWithProvidedBy extends Injectable { 713 @Inject inject( AWithProvidedBy aWithProvidedBy, Provider<AWithProvidedBy> aWithProvidedByProvider)714 public void inject( 715 AWithProvidedBy aWithProvidedBy, Provider<AWithProvidedBy> aWithProvidedByProvider) { 716 this.value = aWithProvidedBy; 717 this.provider = aWithProvidedByProvider; 718 } 719 } 720 721 static class InjectsAWithProvidedByNamedApple extends Injectable { 722 @Inject inject( @amed"apple") AWithProvidedBy aWithProvidedBy, @Named("apple") Provider<AWithProvidedBy> aWithProvidedByProvider)723 public void inject( 724 @Named("apple") AWithProvidedBy aWithProvidedBy, 725 @Named("apple") Provider<AWithProvidedBy> aWithProvidedByProvider) { 726 this.value = aWithProvidedBy; 727 this.provider = aWithProvidedByProvider; 728 } 729 } 730 731 @ImplementedBy(PlainA.class) 732 interface AWithImplementedBy {} 733 734 static class InjectsAWithImplementedBy extends Injectable { 735 @Inject inject( AWithImplementedBy aWithImplementedBy, Provider<AWithImplementedBy> aWithImplementedByProvider)736 public void inject( 737 AWithImplementedBy aWithImplementedBy, 738 Provider<AWithImplementedBy> aWithImplementedByProvider) { 739 this.value = aWithImplementedBy; 740 this.provider = aWithImplementedByProvider; 741 } 742 } 743 744 static class InjectsAWithImplementedByNamedApple extends Injectable { 745 @Inject inject( @amed"apple") AWithImplementedBy aWithImplementedBy, @Named("apple") Provider<AWithImplementedBy> aWithImplementedByProvider)746 public void inject( 747 @Named("apple") AWithImplementedBy aWithImplementedBy, 748 @Named("apple") Provider<AWithImplementedBy> aWithImplementedByProvider) { 749 this.value = aWithImplementedBy; 750 this.provider = aWithImplementedByProvider; 751 } 752 } 753 754 interface A extends AWithProvidedBy, AWithImplementedBy {} 755 756 static class InjectsA extends Injectable { 757 @Inject inject(A a, Provider<A> aProvider)758 public void inject(A a, Provider<A> aProvider) { 759 this.value = a; 760 this.provider = aProvider; 761 } 762 } 763 764 static class PlainA implements A { 765 final int value; 766 PlainA()767 PlainA() { 768 value = nextId.getAndIncrement(); 769 if (value < 0) { 770 throw new RuntimeException("Illegal value: " + value); 771 } 772 } 773 PlainA(int value)774 PlainA(int value) { 775 this.value = value; 776 } 777 778 @Override equals(Object obj)779 public boolean equals(Object obj) { 780 return obj instanceof PlainA && value == ((PlainA) obj).value; 781 } 782 783 @Override hashCode()784 public int hashCode() { 785 return value; 786 } 787 788 @Override toString()789 public String toString() { 790 return "PlainA#" + value; 791 } 792 } 793 794 static class PlainAProvider implements Provider<A> { 795 @Override get()796 public A get() { 797 return new PlainA(); 798 } 799 } 800 801 static class InjectsPlainA extends Injectable { 802 @Inject inject(PlainA plainA, Provider<PlainA> plainAProvider)803 public void inject(PlainA plainA, Provider<PlainA> plainAProvider) { 804 this.value = plainA; 805 this.provider = plainAProvider; 806 } 807 } 808 809 /** This scope hands out each value exactly twice */ 810 static class TwoAtATimeScope implements Scope { 811 @Override scope(Key<T> key, final Provider<T> unscoped)812 public <T> Provider<T> scope(Key<T> key, final Provider<T> unscoped) { 813 return new Provider<T>() { 814 T instance; 815 816 @Override 817 public T get() { 818 if (instance == null) { 819 instance = unscoped.get(); 820 return instance; 821 } else { 822 T result = instance; 823 instance = null; 824 return result; 825 } 826 } 827 }; 828 } 829 } 830 831 @Target({TYPE, METHOD}) 832 @Retention(RUNTIME) 833 @ScopeAnnotation 834 public @interface TwoAtATimeScoped {} 835 836 @TwoAtATimeScoped 837 static class ScopedA extends PlainA {} 838 839 static class InjectsScopedA extends Injectable { 840 @Inject inject(ScopedA scopedA, Provider<ScopedA> scopedAProvider)841 public void inject(ScopedA scopedA, Provider<ScopedA> scopedAProvider) { 842 this.value = scopedA; 843 this.provider = scopedAProvider; 844 } 845 } 846 847 static class InjectsScopedANamedApple extends Injectable { 848 @Inject inject( @amed"apple") ScopedA scopedA, @Named("apple") Provider<ScopedA> scopedAProvider)849 public void inject( 850 @Named("apple") ScopedA scopedA, @Named("apple") Provider<ScopedA> scopedAProvider) { 851 this.value = scopedA; 852 this.provider = scopedAProvider; 853 } 854 } 855 856 static class Injectable { 857 Object value = new Object(); 858 Provider<?> provider = Providers.of(new Object()); 859 } 860 } 861