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.util;
18 
19 import static com.google.inject.Asserts.asModuleChain;
20 import static com.google.inject.Asserts.assertContains;
21 import static com.google.inject.Guice.createInjector;
22 import static com.google.inject.name.Names.named;
23 import static java.lang.annotation.ElementType.METHOD;
24 import static java.lang.annotation.ElementType.TYPE;
25 import static java.lang.annotation.RetentionPolicy.RUNTIME;
26 
27 import com.google.common.base.Objects;
28 import com.google.common.collect.ImmutableSet;
29 import com.google.inject.AbstractModule;
30 import com.google.inject.Binder;
31 import com.google.inject.Binding;
32 import com.google.inject.CreationException;
33 import com.google.inject.Exposed;
34 import com.google.inject.Guice;
35 import com.google.inject.Injector;
36 import com.google.inject.Key;
37 import com.google.inject.Module;
38 import com.google.inject.PrivateModule;
39 import com.google.inject.Provider;
40 import com.google.inject.Provides;
41 import com.google.inject.Scope;
42 import com.google.inject.ScopeAnnotation;
43 import com.google.inject.Stage;
44 import com.google.inject.name.Named;
45 import com.google.inject.name.Names;
46 import com.google.inject.spi.InjectionPoint;
47 import com.google.inject.spi.ModuleAnnotatedMethodScanner;
48 
49 import junit.framework.TestCase;
50 
51 import java.lang.annotation.Annotation;
52 import java.lang.annotation.Documented;
53 import java.lang.annotation.Retention;
54 import java.lang.annotation.Target;
55 import java.util.Date;
56 import java.util.Set;
57 import java.util.concurrent.atomic.AtomicReference;
58 
59 /**
60  * @author sberlin@gmail.com (Sam Berlin)
61  */
62 public class OverrideModuleTest extends TestCase {
63 
64   private static final Key<String> key2 = Key.get(String.class, named("2"));
65   private static final Key<String> key3 = Key.get(String.class, named("3"));
66 
67   private static final Module EMPTY_MODULE = new Module() {
68     public void configure(Binder binder) {}
69   };
70 
testOverride()71   public void testOverride() {
72     Injector injector = createInjector(Modules.override(newModule("A")).with(newModule("B")));
73     assertEquals("B", injector.getInstance(String.class));
74   }
75 
testOverrideMultiple()76   public void testOverrideMultiple() {
77     Module module = Modules.override(newModule("A"), newModule(1), newModule(0.5f))
78         .with(newModule("B"), newModule(2), newModule(1.5d));
79     Injector injector = createInjector(module);
80     assertEquals("B", injector.getInstance(String.class));
81     assertEquals(2, injector.getInstance(Integer.class).intValue());
82     assertEquals(0.5f, injector.getInstance(Float.class));
83     assertEquals(1.5d, injector.getInstance(Double.class));
84   }
85 
testOverrideUnmatchedTolerated()86   public void testOverrideUnmatchedTolerated() {
87     Injector injector = createInjector(Modules.override(EMPTY_MODULE).with(newModule("B")));
88     assertEquals("B", injector.getInstance(String.class));
89   }
90 
testOverrideConstant()91   public void testOverrideConstant() {
92     Module original = new AbstractModule() {
93       @Override protected void configure() {
94         bindConstant().annotatedWith(named("Test")).to("A");
95       }
96     };
97 
98     Module replacements = new AbstractModule() {
99       @Override protected void configure() {
100         bindConstant().annotatedWith(named("Test")).to("B");
101       }
102     };
103 
104     Injector injector = createInjector(Modules.override(original).with(replacements));
105     assertEquals("B", injector.getInstance(Key.get(String.class, named("Test"))));
106   }
107 
testGetProviderInModule()108   public void testGetProviderInModule() {
109     Module original = new AbstractModule() {
110       @Override protected void configure() {
111         bind(String.class).toInstance("A");
112         bind(key2).toProvider(getProvider(String.class));
113       }
114     };
115 
116     Injector injector = createInjector(Modules.override(original).with(EMPTY_MODULE));
117     assertEquals("A", injector.getInstance(String.class));
118     assertEquals("A", injector.getInstance(key2));
119   }
120 
testOverrideWhatGetProviderProvided()121   public void testOverrideWhatGetProviderProvided() {
122     Module original = new AbstractModule() {
123       @Override protected void configure() {
124         bind(String.class).toInstance("A");
125         bind(key2).toProvider(getProvider(String.class));
126       }
127     };
128 
129     Module replacements = newModule("B");
130 
131     Injector injector = createInjector(Modules.override(original).with(replacements));
132     assertEquals("B", injector.getInstance(String.class));
133     assertEquals("B", injector.getInstance(key2));
134   }
135 
testOverrideUsingOriginalsGetProvider()136   public void testOverrideUsingOriginalsGetProvider() {
137     Module original = new AbstractModule() {
138       @Override protected void configure() {
139         bind(String.class).toInstance("A");
140         bind(key2).toInstance("B");
141       }
142     };
143 
144     Module replacements = new AbstractModule() {
145       @Override protected void configure() {
146         bind(String.class).toProvider(getProvider(key2));
147       }
148     };
149 
150     Injector injector = createInjector(Modules.override(original).with(replacements));
151     assertEquals("B", injector.getInstance(String.class));
152     assertEquals("B", injector.getInstance(key2));
153   }
154 
testOverrideOfOverride()155   public void testOverrideOfOverride() {
156     Module original = new AbstractModule() {
157       @Override protected void configure() {
158         bind(String.class).toInstance("A1");
159         bind(key2).toInstance("A2");
160         bind(key3).toInstance("A3");
161       }
162     };
163 
164     Module replacements1 = new AbstractModule() {
165       @Override protected void configure() {
166         bind(String.class).toInstance("B1");
167         bind(key2).toInstance("B2");
168       }
169     };
170 
171     Module overrides = Modules.override(original).with(replacements1);
172 
173     Module replacements2 = new AbstractModule() {
174       @Override protected void configure() {
175         bind(String.class).toInstance("C1");
176         bind(key3).toInstance("C3");
177       }
178     };
179 
180     Injector injector = createInjector(Modules.override(overrides).with(replacements2));
181     assertEquals("C1", injector.getInstance(String.class));
182     assertEquals("B2", injector.getInstance(key2));
183     assertEquals("C3", injector.getInstance(key3));
184   }
185 
186   static class OuterReplacementsModule extends AbstractModule {
configure()187     @Override protected void configure() {
188       install(new InnerReplacementsModule());
189     }
190   }
191   static class InnerReplacementsModule extends AbstractModule {
configure()192     @Override protected void configure() {
193       bind(String.class).toInstance("B");
194       bind(String.class).toInstance("C");
195     }
196   }
testOverridesTwiceFails()197   public void testOverridesTwiceFails() {
198     Module original = newModule("A");
199     Module replacements = new OuterReplacementsModule();
200     Module module = Modules.override(original).with(replacements);
201     try {
202       createInjector(module);
203       fail();
204     } catch (CreationException expected) {
205       assertContains(expected.getMessage(),
206           "A binding to java.lang.String was already configured at "
207               + InnerReplacementsModule.class.getName(),
208           asModuleChain(Modules.OverrideModule.class,
209               OuterReplacementsModule.class, InnerReplacementsModule.class),
210           "at " + InnerReplacementsModule.class.getName(),
211           asModuleChain(Modules.OverrideModule.class,
212               OuterReplacementsModule.class, InnerReplacementsModule.class));
213     }
214   }
215 
testOverridesDoesntFixTwiceBoundInOriginal()216   public void testOverridesDoesntFixTwiceBoundInOriginal() {
217     Module original = new AbstractModule() {
218       @Override protected void configure() {
219         bind(String.class).toInstance("A");
220         bind(String.class).toInstance("B");
221       }
222     };
223 
224     Module replacements = new AbstractModule() {
225       @Override protected void configure() {
226         bind(String.class).toInstance("C");
227       }
228     };
229 
230     Module module = Modules.override(original).with(replacements);
231     try {
232       createInjector(module);
233       fail();
234     } catch (CreationException expected) {
235       // The replacement comes first because we replace A with C,
236       // then we encounter B and freak out.
237       assertContains(expected.getMessage(),
238           "1) A binding to java.lang.String was already configured at "
239               + replacements.getClass().getName(),
240           asModuleChain(Modules.OverrideModule.class, replacements.getClass()),
241           "at " + original.getClass().getName(),
242           asModuleChain(Modules.OverrideModule.class, original.getClass()));
243     }
244   }
245 
testStandardScopeAnnotation()246   public void testStandardScopeAnnotation() {
247     final SingleUseScope scope = new SingleUseScope();
248 
249     Module module = new AbstractModule() {
250       @Override protected void configure() {
251         bindScope(TestScopeAnnotation.class, scope);
252         bind(String.class).in(TestScopeAnnotation.class);
253       }
254     };
255     assertFalse(scope.used);
256 
257     Guice.createInjector(module);
258     assertTrue(scope.used);
259   }
260 
testOverrideUntargettedBinding()261   public void testOverrideUntargettedBinding() {
262     Module original = new AbstractModule() {
263       @Override protected void configure() {
264         bind(Date.class);
265       }
266     };
267 
268     Module replacements = new AbstractModule() {
269       @Override protected void configure() {
270         bind(Date.class).toInstance(new Date(0));
271       }
272     };
273 
274     Injector injector = createInjector(Modules.override(original).with(replacements));
275     assertEquals(0, injector.getInstance(Date.class).getTime());
276   }
277 
testOverrideScopeAnnotation()278   public void testOverrideScopeAnnotation() {
279     final Scope scope = new Scope() {
280       public <T> Provider<T> scope(Key<T> key, Provider<T> unscoped) {
281         throw new AssertionError("Should not be called");
282       }
283     };
284 
285     final SingleUseScope replacementScope = new SingleUseScope();
286 
287     Module original = new AbstractModule() {
288       @Override protected void configure() {
289         bindScope(TestScopeAnnotation.class, scope);
290         bind(Date.class).in(TestScopeAnnotation.class);
291       }
292     };
293 
294     Module replacements = new AbstractModule() {
295       @Override protected void configure() {
296         bindScope(TestScopeAnnotation.class, replacementScope);
297       }
298     };
299 
300     Injector injector = createInjector(Modules.override(original).with(replacements));
301     injector.getInstance(Date.class);
302     assertTrue(replacementScope.used);
303   }
304 
testFailsIfOverridenScopeInstanceHasBeenUsed()305   public void testFailsIfOverridenScopeInstanceHasBeenUsed() {
306     final Scope scope = new Scope() {
307       public <T> Provider<T> scope(Key<T> key, Provider<T> unscoped) {
308         return unscoped;
309       }
310 
311       @Override public String toString() {
312         return "ORIGINAL SCOPE";
313       }
314     };
315 
316     final Module original = new AbstractModule() {
317       @Override protected void configure() {
318         bindScope(TestScopeAnnotation.class, scope);
319         bind(Date.class).in(scope);
320         bind(String.class).in(scope);
321       }
322     };
323     Module originalWrapper = new AbstractModule() {
324       @Override protected void configure() {
325         install(original);
326       }
327     };
328 
329     Module replacements = new AbstractModule() {
330       @Override protected void configure() {
331         bindScope(TestScopeAnnotation.class, new SingleUseScope());
332       }
333     };
334 
335     try {
336       createInjector(Modules.override(originalWrapper).with(replacements));
337       fail("Exception expected");
338     } catch (CreationException e) {
339       assertContains(e.getMessage(),
340           "1) The scope for @TestScopeAnnotation is bound directly and cannot be overridden.",
341           "original binding at " + original.getClass().getName() + ".configure(",
342           asModuleChain(originalWrapper.getClass(), original.getClass()),
343           "bound directly at " + original.getClass().getName() + ".configure(",
344           asModuleChain(originalWrapper.getClass(), original.getClass()),
345           "bound directly at " + original.getClass().getName() + ".configure(",
346           asModuleChain(originalWrapper.getClass(), original.getClass()),
347           "at ", replacements.getClass().getName() + ".configure(",
348           asModuleChain(Modules.OverrideModule.class, replacements.getClass()));
349     }
350   }
351 
testOverrideIsLazy()352   public void testOverrideIsLazy() {
353     final AtomicReference<String> value = new AtomicReference<String>("A");
354     Module overridden = Modules.override(new AbstractModule() {
355       @Override protected void configure() {
356         bind(String.class).annotatedWith(named("original")).toInstance(value.get());
357       }
358     }).with(new AbstractModule() {
359       @Override protected void configure() {
360         bind(String.class).annotatedWith(named("override")).toInstance(value.get());
361       }
362     });
363 
364     // the value.get() call should be deferred until Guice.createInjector
365     value.set("B");
366     Injector injector = Guice.createInjector(overridden);
367     assertEquals("B", injector.getInstance(Key.get(String.class, named("original"))));
368     assertEquals("B", injector.getInstance(Key.get(String.class, named("override"))));
369   }
370 
testOverridePrivateModuleOverPrivateModule()371   public void testOverridePrivateModuleOverPrivateModule() {
372     Module exposes5and6 = new AbstractModule() {
373       @Override protected void configure() {
374         install(new PrivateModule() {
375           @Override protected void configure() {
376             bind(Integer.class).toInstance(5);
377             expose(Integer.class);
378 
379             bind(Character.class).toInstance('E');
380           }
381         });
382 
383         install(new PrivateModule() {
384           @Override protected void configure() {
385             bind(Long.class).toInstance(6L);
386             expose(Long.class);
387 
388             bind(Character.class).toInstance('F');
389           }
390         });
391       }
392     };
393 
394     AbstractModule exposes15 = new AbstractModule() {
395       @Override protected void configure() {
396         install(new PrivateModule() {
397           @Override protected void configure() {
398             bind(Integer.class).toInstance(15);
399             expose(Integer.class);
400 
401             bind(Character.class).toInstance('G');
402           }
403         });
404 
405         install(new PrivateModule() {
406           @Override protected void configure() {
407             bind(Character.class).toInstance('H');
408           }
409         });
410       }
411     };
412 
413     // override forwards
414     Injector injector = Guice.createInjector(Modules.override(exposes5and6).with(exposes15));
415     assertEquals(15, injector.getInstance(Integer.class).intValue());
416     assertEquals(6L, injector.getInstance(Long.class).longValue());
417 
418     // and in reverse order
419     Injector reverse = Guice.createInjector(Modules.override(exposes15).with(exposes5and6));
420     assertEquals(5, reverse.getInstance(Integer.class).intValue());
421     assertEquals(6L, reverse.getInstance(Long.class).longValue());
422   }
423 
testOverrideModuleAndPrivateModule()424   public void testOverrideModuleAndPrivateModule() {
425     Module exposes5 = new PrivateModule() {
426       @Override protected void configure() {
427         bind(Integer.class).toInstance(5);
428         expose(Integer.class);
429       }
430     };
431 
432     Module binds15 = new AbstractModule() {
433       @Override protected void configure() {
434         bind(Integer.class).toInstance(15);
435       }
436     };
437 
438     Injector injector = Guice.createInjector(Modules.override(exposes5).with(binds15));
439     assertEquals(15, injector.getInstance(Integer.class).intValue());
440 
441     Injector reverse = Guice.createInjector(Modules.override(binds15).with(exposes5));
442     assertEquals(5, reverse.getInstance(Integer.class).intValue());
443   }
444 
testOverrideDeepExpose()445   public void testOverrideDeepExpose() {
446     final AtomicReference<Provider<Character>> charAProvider
447         = new AtomicReference<Provider<Character>>();
448 
449     Module exposes5 = new PrivateModule() {
450       @Override protected void configure() {
451         install(new PrivateModule() {
452           @Override protected void configure() {
453             bind(Integer.class).toInstance(5);
454             expose(Integer.class);
455             charAProvider.set(getProvider(Character.class));
456             bind(Character.class).toInstance('A');
457           }
458         });
459         expose(Integer.class);
460       }
461     };
462 
463     Injector injector = Guice.createInjector(Modules.override(exposes5).with(EMPTY_MODULE));
464     assertEquals(5, injector.getInstance(Integer.class).intValue());
465     assertEquals('A', charAProvider.getAndSet(null).get().charValue());
466 
467     injector = Guice.createInjector(Modules.override(EMPTY_MODULE).with(exposes5));
468     assertEquals(5, injector.getInstance(Integer.class).intValue());
469     assertEquals('A', charAProvider.getAndSet(null).get().charValue());
470 
471     final AtomicReference<Provider<Character>> charBProvider
472         = new AtomicReference<Provider<Character>>();
473 
474     Module binds15 = new AbstractModule() {
475       @Override protected void configure() {
476         bind(Integer.class).toInstance(15);
477 
478         install(new PrivateModule() {
479           @Override protected void configure() {
480             charBProvider.set(getProvider(Character.class));
481             bind(Character.class).toInstance('B');
482           }
483         });
484       }
485     };
486 
487     injector = Guice.createInjector(Modules.override(binds15).with(exposes5));
488     assertEquals(5, injector.getInstance(Integer.class).intValue());
489     assertEquals('A', charAProvider.getAndSet(null).get().charValue());
490     assertEquals('B', charBProvider.getAndSet(null).get().charValue());
491 
492     injector = Guice.createInjector(Modules.override(exposes5).with(binds15));
493     assertEquals(15, injector.getInstance(Integer.class).intValue());
494     assertEquals('A', charAProvider.getAndSet(null).get().charValue());
495     assertEquals('B', charBProvider.getAndSet(null).get().charValue());
496   }
497 
498   @Retention(RUNTIME)
499   @Target(TYPE)
500   @ScopeAnnotation
501   private static @interface TestScopeAnnotation {}
502 
503   private static class SingleUseScope implements Scope {
504     boolean used = false;
scope(Key<T> key, Provider<T> unscoped)505     public <T> Provider<T> scope(Key<T> key, Provider<T> unscoped) {
506       assertFalse(used);
507       used = true;
508       return unscoped;
509     }
510   }
511 
512   static class NewModule<T> extends AbstractModule {
513     private final T bound;
NewModule(T bound)514     NewModule(T bound) {
515       this.bound = bound;
516     }
configure()517     @Override protected void configure() {
518       @SuppressWarnings("unchecked")
519       Class<T> type = (Class<T>)bound.getClass();
520       bind(type).toInstance(bound);
521     }
522   }
523 
newModule(final T bound)524   private static <T> Module newModule(final T bound) {
525     return new NewModule<T>(bound);
526   }
527 
528   private static final String RESULT = "RESULT";
529   private static final String PRIVATE_INPUT = "PRIVATE_INPUT";
530   private static final String OVERRIDDEN_INPUT = "FOO";
531   private static final String OVERRIDDEN_RESULT = "Size: 3";
532   private static final Key<String> RESULT_KEY = Key.get(String.class, named(RESULT));
533   private static final Key<String> INPUT_KEY = Key.get(String.class, named(PRIVATE_INPUT));
534 
testExposedBindingOverride()535   public void testExposedBindingOverride() throws Exception {
536     Injector inj = Guice.createInjector(
537         Modules.override(new ExampleModule()).with(
538             new AbstractModule() {
539               @Override protected void configure() {
540                 bind(RESULT_KEY).toInstance(OVERRIDDEN_RESULT);
541               }
542             }));
543     assertEquals(inj.getInstance(RESULT_KEY), OVERRIDDEN_RESULT);
544   }
545 
testPrivateBindingOverride()546   public void testPrivateBindingOverride() throws Exception {
547     Injector inj = Guice.createInjector(
548         Modules.override(new ExampleModule()).with(
549             new AbstractModule() {
550               @Override protected void configure() {
551                 bind(INPUT_KEY).toInstance(OVERRIDDEN_INPUT);
552               }
553             }));
554     assertEquals(inj.getInstance(RESULT_KEY), OVERRIDDEN_RESULT);
555   }
556 
557   public static class ExampleModule extends PrivateModule {
558     @Provides @Exposed @Named(RESULT)
provideResult(@amedPRIVATE_INPUT) String input)559     public String provideResult(@Named(PRIVATE_INPUT) String input) {
560       return "Size: " + input.length();
561     }
562 
563     @Provides @Named(PRIVATE_INPUT)
provideInput()564     public String provideInput() {
565       return "Hello World";
566     }
567 
configure()568     @Override protected void configure() {
569     }
570   }
571 
testEqualsNotCalledByDefaultOnInstance()572   public void testEqualsNotCalledByDefaultOnInstance() {
573     final HashEqualsTester a = new HashEqualsTester();
574     a.throwOnEquals = true;
575     Guice.createInjector(Modules.override(new AbstractModule() {
576       @Override
577       protected void configure() {
578        bind(String.class);
579        bind(HashEqualsTester.class).toInstance(a);
580       }
581     }).with());
582   }
583 
testEqualsNotCalledByDefaultOnProvider()584   public void testEqualsNotCalledByDefaultOnProvider() {
585     final HashEqualsTester a = new HashEqualsTester();
586     a.throwOnEquals = true;
587     Guice.createInjector(Modules.override(new AbstractModule() {
588       @Override
589       protected void configure() {
590        bind(String.class);
591        bind(Object.class).toProvider(a);
592       }
593     }).with());
594   }
595 
testHashcodeNeverCalledOnInstance()596   public void testHashcodeNeverCalledOnInstance() {
597     final HashEqualsTester a = new HashEqualsTester();
598     a.throwOnHashcode = true;
599     a.equality = "test";
600 
601     final HashEqualsTester b = new HashEqualsTester();
602     b.throwOnHashcode = true;
603     b.equality = "test";
604     Guice.createInjector(Modules.override(new AbstractModule() {
605       @Override
606       protected void configure() {
607        bind(String.class);
608        bind(HashEqualsTester.class).toInstance(a);
609        bind(HashEqualsTester.class).toInstance(b);
610       }
611     }).with());
612   }
613 
testHashcodeNeverCalledOnProviderInstance()614   public void testHashcodeNeverCalledOnProviderInstance() {
615     final HashEqualsTester a = new HashEqualsTester();
616     a.throwOnHashcode = true;
617     a.equality = "test";
618 
619     final HashEqualsTester b = new HashEqualsTester();
620     b.throwOnHashcode = true;
621     b.equality = "test";
622     Guice.createInjector(Modules.override(new AbstractModule() {
623       @Override
624       protected void configure() {
625        bind(String.class);
626        bind(Object.class).toProvider(a);
627        bind(Object.class).toProvider(b);
628       }
629     }).with());
630   }
631 
632   private static class HashEqualsTester implements Provider<Object> {
633     private String equality;
634     private boolean throwOnEquals;
635     private boolean throwOnHashcode;
636 
637     @Override
equals(Object obj)638     public boolean equals(Object obj) {
639       if (throwOnEquals) {
640         throw new RuntimeException();
641       } else if (obj instanceof HashEqualsTester) {
642         HashEqualsTester o = (HashEqualsTester)obj;
643         if(o.throwOnEquals) {
644           throw new RuntimeException();
645         }
646         if(equality == null && o.equality == null) {
647           return this == o;
648         } else {
649           return Objects.equal(equality, o.equality);
650         }
651       } else {
652         return false;
653       }
654     }
655 
656     @Override
hashCode()657     public int hashCode() {
658       if(throwOnHashcode) {
659         throw new RuntimeException();
660       } else {
661         return super.hashCode();
662       }
663     }
664 
get()665     public Object get() {
666       return new Object();
667     }
668   }
669 
testCorrectStage()670   public void testCorrectStage() {
671     final Stage stage = Stage.PRODUCTION;
672     Module module = Modules.override(new AbstractModule() {
673       @Override
674       protected void configure() {
675         if (currentStage() != Stage.PRODUCTION) {
676           addError("Wronge stage in overridden module:" + currentStage());
677         }
678       }
679     }).with(new AbstractModule() {
680       @Override
681       protected void configure() {
682         if (currentStage() != Stage.PRODUCTION) {
683           addError("Wronge stage in overriding module:" + currentStage());
684         }
685       }
686     });
687     Guice.createInjector(stage, module);
688   }
689 
testOverridesApplyOriginalScanners()690   public void testOverridesApplyOriginalScanners() {
691     Injector injector =
692         Guice.createInjector(Modules.override(NamedMunger.module()).with(new AbstractModule() {
693       @Override protected void configure() {}
694       @TestProvides @Named("test") String provideString() { return "foo"; }
695     }));
696 
697     assertNull(injector.getExistingBinding(Key.get(String.class, named("test"))));
698     Binding<String> binding = injector.getBinding(Key.get(String.class, named("test-munged")));
699     assertEquals("foo", binding.getProvider().get());
700   }
701 
702   @Documented @Target(METHOD) @Retention(RUNTIME)
703   private @interface TestProvides {}
704 
705   private static class NamedMunger extends ModuleAnnotatedMethodScanner {
module()706     static Module module() {
707       return new AbstractModule() {
708         @Override protected void configure() {
709           binder().scanModulesForAnnotatedMethods(new NamedMunger());
710         }
711       };
712     }
713 
714     @Override
toString()715     public String toString() {
716       return "NamedMunger";
717     }
718 
719     @Override
annotationClasses()720     public Set<? extends Class<? extends Annotation>> annotationClasses() {
721       return ImmutableSet.of(TestProvides.class);
722     }
723 
724     @Override
prepareMethod(Binder binder, Annotation annotation, Key<T> key, InjectionPoint injectionPoint)725     public <T> Key<T> prepareMethod(Binder binder, Annotation annotation, Key<T> key,
726         InjectionPoint injectionPoint) {
727       return Key.get(key.getTypeLiteral(),
728           Names.named(((Named) key.getAnnotation()).value() + "-munged"));
729     }
730   }
731 }
732