Home | History | Annotate | Download | only in spi
      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.spi;
     18 
     19 import static com.google.common.collect.Iterables.getOnlyElement;
     20 import static com.google.inject.Asserts.assertContains;
     21 import static com.google.inject.Asserts.getDeclaringSourcePart;
     22 import static com.google.inject.Asserts.isIncludeStackTraceComplete;
     23 import static java.lang.annotation.RetentionPolicy.RUNTIME;
     24 
     25 import com.google.common.collect.ImmutableMap;
     26 import com.google.common.collect.ImmutableSet;
     27 import com.google.inject.AbstractModule;
     28 import com.google.inject.Binding;
     29 import com.google.inject.BindingAnnotation;
     30 import com.google.inject.Inject;
     31 import com.google.inject.Key;
     32 import com.google.inject.MembersInjector;
     33 import com.google.inject.Module;
     34 import com.google.inject.PrivateBinder;
     35 import com.google.inject.Provider;
     36 import com.google.inject.Scope;
     37 import com.google.inject.Scopes;
     38 import com.google.inject.Singleton;
     39 import com.google.inject.Stage;
     40 import com.google.inject.TypeLiteral;
     41 import com.google.inject.binder.AnnotatedBindingBuilder;
     42 import com.google.inject.binder.AnnotatedConstantBindingBuilder;
     43 import com.google.inject.binder.ConstantBindingBuilder;
     44 import com.google.inject.binder.ScopedBindingBuilder;
     45 import com.google.inject.matcher.Matcher;
     46 import com.google.inject.matcher.Matchers;
     47 import com.google.inject.name.Named;
     48 import com.google.inject.name.Names;
     49 import com.google.inject.util.Providers;
     50 import java.lang.annotation.Annotation;
     51 import java.lang.annotation.ElementType;
     52 import java.lang.annotation.Retention;
     53 import java.lang.annotation.Target;
     54 import java.lang.reflect.Constructor;
     55 import java.lang.reflect.Field;
     56 import java.util.ArrayList;
     57 import java.util.Arrays;
     58 import java.util.Collection;
     59 import java.util.HashMap;
     60 import java.util.Iterator;
     61 import java.util.List;
     62 import java.util.Map;
     63 import java.util.Set;
     64 import java.util.TreeSet;
     65 import java.util.concurrent.atomic.AtomicInteger;
     66 import java.util.concurrent.atomic.AtomicReference;
     67 import junit.framework.TestCase;
     68 
     69 /** @author jessewilson (at) google.com (Jesse Wilson) */
     70 public class ElementsTest extends TestCase {
     71 
     72   // Binder fidelity tests
     73 
     74   public void testAddMessageErrorCommand() {
     75     checkModule(
     76         new AbstractModule() {
     77           @Override
     78           protected void configure() {
     79             addError("Message %s %d %s", "A", 5, "C");
     80           }
     81         },
     82         new FailingElementVisitor() {
     83           @Override
     84           public Void visit(Message command) {
     85             assertEquals("Message A 5 C", command.getMessage());
     86             assertNull(command.getCause());
     87             assertContains(
     88                 command.getSources().toString(),
     89                 ElementsTest.class.getName(),
     90                 getDeclaringSourcePart(ElementsTest.class));
     91             assertContains(command.getSource(), getDeclaringSourcePart(ElementsTest.class));
     92             return null;
     93           }
     94         });
     95   }
     96 
     97   public void testAddThrowableErrorCommand() {
     98     checkModule(
     99         new AbstractModule() {
    100           @Override
    101           protected void configure() {
    102             addError(new Exception("A"));
    103           }
    104         },
    105         new FailingElementVisitor() {
    106           @Override
    107           public Void visit(Message command) {
    108             assertEquals("A", command.getCause().getMessage());
    109             assertEquals("An exception was caught and reported. Message: A", command.getMessage());
    110             assertContains(command.getSource(), getDeclaringSourcePart(ElementsTest.class));
    111             return null;
    112           }
    113         });
    114   }
    115 
    116   public void testErrorsAddedWhenExceptionsAreThrown() {
    117     checkModule(
    118         new AbstractModule() {
    119           @Override
    120           protected void configure() {
    121             install(
    122                 new AbstractModule() {
    123                   @Override
    124                   protected void configure() {
    125                     throw new RuntimeException(
    126                         "Throwing RuntimeException in AbstractModule.configure().");
    127                   }
    128                 });
    129 
    130             addError("Code after the exception still gets executed");
    131           }
    132         },
    133         new FailingElementVisitor() {
    134           @Override
    135           public Void visit(Message command) {
    136             assertEquals(
    137                 "Throwing RuntimeException in AbstractModule.configure().",
    138                 command.getCause().getMessage());
    139             return null;
    140           }
    141         },
    142         new FailingElementVisitor() {
    143           @Override
    144           public Void visit(Message command) {
    145             assertEquals("Code after the exception still gets executed", command.getMessage());
    146             return null;
    147           }
    148         });
    149   }
    150 
    151   private <T> T getInstance(Binding<T> binding) {
    152     return binding.acceptTargetVisitor(Elements.<T>getInstanceVisitor());
    153   }
    154 
    155   public void testBindConstantAnnotations() {
    156     checkModule(
    157         new AbstractModule() {
    158           @Override
    159           protected void configure() {
    160             bindConstant().annotatedWith(SampleAnnotation.class).to("A");
    161             bindConstant().annotatedWith(Names.named("Bee")).to("B");
    162           }
    163         },
    164         new FailingElementVisitor() {
    165           @Override
    166           public <T> Void visit(Binding<T> command) {
    167             assertTrue(command instanceof InstanceBinding);
    168             assertEquals(Key.get(String.class, SampleAnnotation.class), command.getKey());
    169             assertEquals("A", getInstance(command));
    170             return null;
    171           }
    172         },
    173         new FailingElementVisitor() {
    174           @Override
    175           public <T> Void visit(Binding<T> command) {
    176             assertTrue(command instanceof InstanceBinding);
    177             assertEquals(Key.get(String.class, Names.named("Bee")), command.getKey());
    178             assertEquals("B", getInstance(command));
    179             return null;
    180           }
    181         });
    182   }
    183 
    184   public void testBindConstantTypes() {
    185     checkModule(
    186         new AbstractModule() {
    187           @Override
    188           protected void configure() {
    189             bindConstant().annotatedWith(Names.named("String")).to("A");
    190             bindConstant().annotatedWith(Names.named("int")).to(2);
    191             bindConstant().annotatedWith(Names.named("long")).to(3L);
    192             bindConstant().annotatedWith(Names.named("boolean")).to(false);
    193             bindConstant().annotatedWith(Names.named("double")).to(5.0d);
    194             bindConstant().annotatedWith(Names.named("float")).to(6.0f);
    195             bindConstant().annotatedWith(Names.named("short")).to((short) 7);
    196             bindConstant().annotatedWith(Names.named("char")).to('h');
    197             bindConstant().annotatedWith(Names.named("byte")).to((byte) 8);
    198             bindConstant().annotatedWith(Names.named("Class")).to(Iterator.class);
    199             bindConstant().annotatedWith(Names.named("Enum")).to(CoinSide.TAILS);
    200           }
    201         },
    202         new FailingElementVisitor() {
    203           @Override
    204           public <T> Void visit(Binding<T> command) {
    205             assertTrue(command instanceof InstanceBinding);
    206             assertEquals(Key.get(String.class, Names.named("String")), command.getKey());
    207             assertEquals("A", getInstance(command));
    208             return null;
    209           }
    210         },
    211         new FailingElementVisitor() {
    212           @Override
    213           public <T> Void visit(Binding<T> command) {
    214             assertTrue(command instanceof InstanceBinding);
    215             assertEquals(Key.get(Integer.class, Names.named("int")), command.getKey());
    216             assertEquals(2, getInstance(command));
    217             return null;
    218           }
    219         },
    220         new FailingElementVisitor() {
    221           @Override
    222           public <T> Void visit(Binding<T> command) {
    223             assertTrue(command instanceof InstanceBinding);
    224             assertEquals(Key.get(Long.class, Names.named("long")), command.getKey());
    225             assertEquals(3L, getInstance(command));
    226             return null;
    227           }
    228         },
    229         new FailingElementVisitor() {
    230           @Override
    231           public <T> Void visit(Binding<T> command) {
    232             assertTrue(command instanceof InstanceBinding);
    233             assertEquals(Key.get(Boolean.class, Names.named("boolean")), command.getKey());
    234             assertEquals(false, getInstance(command));
    235             return null;
    236           }
    237         },
    238         new FailingElementVisitor() {
    239           @Override
    240           public <T> Void visit(Binding<T> command) {
    241             assertTrue(command instanceof InstanceBinding);
    242             assertEquals(Key.get(Double.class, Names.named("double")), command.getKey());
    243             assertEquals(5.0d, getInstance(command));
    244             return null;
    245           }
    246         },
    247         new FailingElementVisitor() {
    248           @Override
    249           public <T> Void visit(Binding<T> command) {
    250             assertTrue(command instanceof InstanceBinding);
    251             assertEquals(Key.get(Float.class, Names.named("float")), command.getKey());
    252             assertEquals(6.0f, getInstance(command));
    253             return null;
    254           }
    255         },
    256         new FailingElementVisitor() {
    257           @Override
    258           public <T> Void visit(Binding<T> command) {
    259             assertTrue(command instanceof InstanceBinding);
    260             assertEquals(Key.get(Short.class, Names.named("short")), command.getKey());
    261             assertEquals((short) 7, getInstance(command));
    262             return null;
    263           }
    264         },
    265         new FailingElementVisitor() {
    266           @Override
    267           public <T> Void visit(Binding<T> command) {
    268             assertTrue(command instanceof InstanceBinding);
    269             assertEquals(Key.get(Character.class, Names.named("char")), command.getKey());
    270             assertEquals('h', getInstance(command));
    271             return null;
    272           }
    273         },
    274         new FailingElementVisitor() {
    275           @Override
    276           public <T> Void visit(Binding<T> command) {
    277             assertTrue(command instanceof InstanceBinding);
    278             assertEquals(Key.get(Byte.class, Names.named("byte")), command.getKey());
    279             assertEquals((byte) 8, getInstance(command));
    280             return null;
    281           }
    282         },
    283         new FailingElementVisitor() {
    284           @Override
    285           public <T> Void visit(Binding<T> command) {
    286             assertTrue(command instanceof InstanceBinding);
    287             assertEquals(Key.get(Class.class, Names.named("Class")), command.getKey());
    288             assertEquals(Iterator.class, getInstance(command));
    289             return null;
    290           }
    291         },
    292         new FailingElementVisitor() {
    293           @Override
    294           public <T> Void visit(Binding<T> command) {
    295             assertTrue(command instanceof InstanceBinding);
    296             assertEquals(Key.get(CoinSide.class, Names.named("Enum")), command.getKey());
    297             assertEquals(CoinSide.TAILS, getInstance(command));
    298             return null;
    299           }
    300         });
    301   }
    302 
    303   public void testBindKeysNoAnnotations() {
    304     FailingElementVisitor keyChecker =
    305         new FailingElementVisitor() {
    306           @Override
    307           public <T> Void visit(Binding<T> command) {
    308             assertEquals(Key.get(String.class), command.getKey());
    309             return null;
    310           }
    311         };
    312 
    313     checkModule(
    314         new AbstractModule() {
    315           @Override
    316           protected void configure() {
    317             bind(String.class).toInstance("A");
    318             bind(new TypeLiteral<String>() {}).toInstance("B");
    319             bind(Key.get(String.class)).toInstance("C");
    320           }
    321         },
    322         keyChecker,
    323         keyChecker,
    324         keyChecker);
    325   }
    326 
    327   public void testBindKeysWithAnnotationType() {
    328     FailingElementVisitor annotationChecker =
    329         new FailingElementVisitor() {
    330           @Override
    331           public <T> Void visit(Binding<T> command) {
    332             assertEquals(Key.get(String.class, SampleAnnotation.class), command.getKey());
    333             return null;
    334           }
    335         };
    336 
    337     checkModule(
    338         new AbstractModule() {
    339           @Override
    340           protected void configure() {
    341             bind(String.class).annotatedWith(SampleAnnotation.class).toInstance("A");
    342             bind(new TypeLiteral<String>() {})
    343                 .annotatedWith(SampleAnnotation.class)
    344                 .toInstance("B");
    345           }
    346         },
    347         annotationChecker,
    348         annotationChecker);
    349   }
    350 
    351   public void testBindKeysWithAnnotationInstance() {
    352     FailingElementVisitor annotationChecker =
    353         new FailingElementVisitor() {
    354           @Override
    355           public <T> Void visit(Binding<T> command) {
    356             assertEquals(Key.get(String.class, Names.named("a")), command.getKey());
    357             return null;
    358           }
    359         };
    360 
    361     checkModule(
    362         new AbstractModule() {
    363           @Override
    364           protected void configure() {
    365             bind(String.class).annotatedWith(Names.named("a")).toInstance("B");
    366             bind(new TypeLiteral<String>() {}).annotatedWith(Names.named("a")).toInstance("C");
    367           }
    368         },
    369         annotationChecker,
    370         annotationChecker);
    371   }
    372 
    373   public void testBindToProvider() {
    374     final Provider<String> aProvider =
    375         new Provider<String>() {
    376           @Override
    377           public String get() {
    378             return "A";
    379           }
    380         };
    381 
    382     final javax.inject.Provider<Integer> intJavaxProvider =
    383         new javax.inject.Provider<Integer>() {
    384           @Override
    385           public Integer get() {
    386             return 42;
    387           }
    388         };
    389 
    390     final javax.inject.Provider<Double> doubleJavaxProvider =
    391         new javax.inject.Provider<Double>() {
    392           @javax.inject.Inject String string;
    393 
    394           @Override
    395           public Double get() {
    396             return 42.42;
    397           }
    398         };
    399 
    400     checkModule(
    401         new AbstractModule() {
    402           @Override
    403           protected void configure() {
    404             bind(String.class).toProvider(aProvider);
    405             bind(Integer.class).toProvider(intJavaxProvider);
    406             bind(Double.class).toProvider(doubleJavaxProvider);
    407             bind(List.class).toProvider(ListProvider.class);
    408             bind(Collection.class).toProvider(Key.get(ListProvider.class));
    409             bind(Iterable.class).toProvider(new TypeLiteral<TProvider<List>>() {});
    410           }
    411         },
    412         new FailingElementVisitor() {
    413           @Override
    414           public <T> Void visit(Binding<T> command) {
    415             assertTrue(command instanceof ProviderInstanceBinding);
    416             assertEquals(Key.get(String.class), command.getKey());
    417             command.acceptTargetVisitor(
    418                 new FailingTargetVisitor<T>() {
    419                   @Override
    420                   public Void visit(ProviderInstanceBinding<? extends T> binding) {
    421                     assertSame(aProvider, binding.getUserSuppliedProvider());
    422                     assertSame(aProvider, binding.getProviderInstance());
    423                     return null;
    424                   }
    425                 });
    426             return null;
    427           }
    428         },
    429         new FailingElementVisitor() {
    430           @Override
    431           public <T> Void visit(Binding<T> command) {
    432             assertTrue(command instanceof ProviderInstanceBinding);
    433             assertEquals(Key.get(Integer.class), command.getKey());
    434             command.acceptTargetVisitor(
    435                 new FailingTargetVisitor<T>() {
    436                   @Override
    437                   public Void visit(ProviderInstanceBinding<? extends T> binding) {
    438                     assertSame(intJavaxProvider, binding.getUserSuppliedProvider());
    439                     assertEquals(42, binding.getProviderInstance().get());
    440                     // we don't wrap this w/ dependencies if there were none.
    441                     assertFalse(binding.getProviderInstance() instanceof HasDependencies);
    442                     return null;
    443                   }
    444                 });
    445             return null;
    446           }
    447         },
    448         new FailingElementVisitor() {
    449           @Override
    450           public <T> Void visit(Binding<T> command) {
    451             assertTrue(command instanceof ProviderInstanceBinding);
    452             assertEquals(Key.get(Double.class), command.getKey());
    453             command.acceptTargetVisitor(
    454                 new FailingTargetVisitor<T>() {
    455                   @Override
    456                   public Void visit(ProviderInstanceBinding<? extends T> binding) {
    457                     assertSame(doubleJavaxProvider, binding.getUserSuppliedProvider());
    458                     assertEquals(42.42, binding.getProviderInstance().get());
    459                     // we do wrap it with dependencies if there were some.
    460                     assertTrue(binding.getProviderInstance() instanceof HasDependencies);
    461                     Set<Dependency<?>> deps =
    462                         ((HasDependencies) binding.getProviderInstance()).getDependencies();
    463                     assertEquals(1, deps.size());
    464                     assertEquals(
    465                         String.class,
    466                         deps.iterator().next().getKey().getTypeLiteral().getRawType());
    467                     return null;
    468                   }
    469                 });
    470             return null;
    471           }
    472         },
    473         new FailingElementVisitor() {
    474           @Override
    475           public <T> Void visit(Binding<T> command) {
    476             assertTrue(command instanceof ProviderKeyBinding);
    477             assertEquals(Key.get(List.class), command.getKey());
    478             command.acceptTargetVisitor(
    479                 new FailingTargetVisitor<T>() {
    480                   @Override
    481                   public Void visit(ProviderKeyBinding<? extends T> binding) {
    482                     assertEquals(Key.get(ListProvider.class), binding.getProviderKey());
    483                     return null;
    484                   }
    485                 });
    486             return null;
    487           }
    488         },
    489         new FailingElementVisitor() {
    490           @Override
    491           public <T> Void visit(Binding<T> command) {
    492             assertTrue(command instanceof ProviderKeyBinding);
    493             assertEquals(Key.get(Collection.class), command.getKey());
    494             command.acceptTargetVisitor(
    495                 new FailingTargetVisitor<T>() {
    496                   @Override
    497                   public Void visit(ProviderKeyBinding<? extends T> binding) {
    498                     assertEquals(Key.get(ListProvider.class), binding.getProviderKey());
    499                     return null;
    500                   }
    501                 });
    502             return null;
    503           }
    504         },
    505         new FailingElementVisitor() {
    506           @Override
    507           public <T> Void visit(Binding<T> command) {
    508             assertTrue(command instanceof ProviderKeyBinding);
    509             assertEquals(Key.get(Iterable.class), command.getKey());
    510             command.acceptTargetVisitor(
    511                 new FailingTargetVisitor<T>() {
    512                   @Override
    513                   public Void visit(ProviderKeyBinding<? extends T> binding) {
    514                     assertEquals(new Key<TProvider<List>>() {}, binding.getProviderKey());
    515                     return null;
    516                   }
    517                 });
    518             return null;
    519           }
    520         });
    521   }
    522 
    523   public void testBindToLinkedBinding() {
    524     checkModule(
    525         new AbstractModule() {
    526           @Override
    527           protected void configure() {
    528             bind(List.class).to(ArrayList.class);
    529             bind(Map.class).to(new TypeLiteral<HashMap<Integer, String>>() {});
    530             bind(Set.class).to(Key.get(TreeSet.class, SampleAnnotation.class));
    531           }
    532         },
    533         new FailingElementVisitor() {
    534           @Override
    535           public <T> Void visit(Binding<T> command) {
    536             assertTrue(command instanceof LinkedKeyBinding);
    537             assertEquals(Key.get(List.class), command.getKey());
    538             command.acceptTargetVisitor(
    539                 new FailingTargetVisitor<T>() {
    540                   @Override
    541                   public Void visit(LinkedKeyBinding<? extends T> binding) {
    542                     assertEquals(Key.get(ArrayList.class), binding.getLinkedKey());
    543                     return null;
    544                   }
    545                 });
    546             return null;
    547           }
    548         },
    549         new FailingElementVisitor() {
    550           @Override
    551           public <T> Void visit(Binding<T> command) {
    552             assertTrue(command instanceof LinkedKeyBinding);
    553             assertEquals(Key.get(Map.class), command.getKey());
    554             command.acceptTargetVisitor(
    555                 new FailingTargetVisitor<T>() {
    556                   @Override
    557                   public Void visit(LinkedKeyBinding<? extends T> binding) {
    558                     assertEquals(
    559                         Key.get(new TypeLiteral<HashMap<Integer, String>>() {}),
    560                         binding.getLinkedKey());
    561                     return null;
    562                   }
    563                 });
    564             return null;
    565           }
    566         },
    567         new FailingElementVisitor() {
    568           @Override
    569           public <T> Void visit(Binding<T> command) {
    570             assertTrue(command instanceof LinkedKeyBinding);
    571             assertEquals(Key.get(Set.class), command.getKey());
    572             command.acceptTargetVisitor(
    573                 new FailingTargetVisitor<T>() {
    574                   @Override
    575                   public Void visit(LinkedKeyBinding<? extends T> binding) {
    576                     assertEquals(
    577                         Key.get(TreeSet.class, SampleAnnotation.class), binding.getLinkedKey());
    578                     return null;
    579                   }
    580                 });
    581             return null;
    582           }
    583         });
    584   }
    585 
    586   public void testBindToInstance() {
    587     checkModule(
    588         new AbstractModule() {
    589           @Override
    590           protected void configure() {
    591             bind(String.class).toInstance("A");
    592           }
    593         },
    594         new FailingElementVisitor() {
    595           @Override
    596           public <T> Void visit(Binding<T> command) {
    597             assertTrue(command instanceof InstanceBinding);
    598             assertEquals(Key.get(String.class), command.getKey());
    599             assertEquals("A", getInstance(command));
    600             return null;
    601           }
    602         });
    603   }
    604 
    605   public void testBindInScopes() {
    606     checkModule(
    607         new AbstractModule() {
    608           @Override
    609           protected void configure() {
    610             bind(String.class);
    611             bind(List.class).to(ArrayList.class).in(Scopes.SINGLETON);
    612             bind(Map.class).to(HashMap.class).in(Singleton.class);
    613             bind(Set.class).to(TreeSet.class).asEagerSingleton();
    614           }
    615         },
    616         new FailingElementVisitor() {
    617           @Override
    618           public <T> Void visit(Binding<T> command) {
    619             assertEquals(Key.get(String.class), command.getKey());
    620             command.acceptScopingVisitor(
    621                 new FailingBindingScopingVisitor() {
    622                   @Override
    623                   public Void visitNoScoping() {
    624                     return null;
    625                   }
    626                 });
    627             return null;
    628           }
    629         },
    630         new FailingElementVisitor() {
    631           @Override
    632           public <T> Void visit(Binding<T> command) {
    633             assertEquals(Key.get(List.class), command.getKey());
    634             command.acceptScopingVisitor(
    635                 new FailingBindingScopingVisitor() {
    636                   @Override
    637                   public Void visitScope(Scope scope) {
    638                     assertEquals(Scopes.SINGLETON, scope);
    639                     return null;
    640                   }
    641                 });
    642             return null;
    643           }
    644         },
    645         new FailingElementVisitor() {
    646           @Override
    647           public <T> Void visit(Binding<T> command) {
    648             assertEquals(Key.get(Map.class), command.getKey());
    649             command.acceptScopingVisitor(
    650                 new FailingBindingScopingVisitor() {
    651                   @Override
    652                   public Void visitScopeAnnotation(Class<? extends Annotation> annotation) {
    653                     assertEquals(Singleton.class, annotation);
    654                     return null;
    655                   }
    656                 });
    657             return null;
    658           }
    659         },
    660         new FailingElementVisitor() {
    661           @Override
    662           public <T> Void visit(Binding<T> command) {
    663             assertEquals(Key.get(Set.class), command.getKey());
    664             command.acceptScopingVisitor(
    665                 new FailingBindingScopingVisitor() {
    666                   @Override
    667                   public Void visitEagerSingleton() {
    668                     return null;
    669                   }
    670                 });
    671             return null;
    672           }
    673         });
    674   }
    675 
    676   public void testBindToInstanceInScope() {
    677     checkModule(
    678         new AbstractModule() {
    679           @Override
    680           protected void configure() {
    681             AnnotatedBindingBuilder<String> b = bind(String.class);
    682             b.toInstance("A");
    683             b.in(Singleton.class);
    684           }
    685         },
    686         new FailingElementVisitor() {
    687           @Override
    688           public <T> Void visit(Binding<T> command) {
    689             return null;
    690           }
    691         },
    692         new FailingElementVisitor() {
    693           @Override
    694           public Void visit(Message command) {
    695             assertEquals(
    696                 "Setting the scope is not permitted when binding to a single instance.",
    697                 command.getMessage());
    698             assertNull(command.getCause());
    699             assertContains(command.getSource(), getDeclaringSourcePart(ElementsTest.class));
    700             return null;
    701           }
    702         });
    703   }
    704 
    705   public void testBindToInstanceScope() {
    706     checkModule(
    707         new AbstractModule() {
    708           @Override
    709           protected void configure() {
    710             bind(String.class).toInstance("A");
    711           }
    712         },
    713         new FailingElementVisitor() {
    714           @Override
    715           public <T> Void visit(Binding<T> binding) {
    716             assertEquals(Key.get(String.class), binding.getKey());
    717             binding.acceptScopingVisitor(
    718                 new FailingBindingScopingVisitor() {
    719                   @Override
    720                   public Void visitEagerSingleton() {
    721                     return null;
    722                   }
    723                 });
    724             return null;
    725           }
    726         });
    727   }
    728 
    729   /*if[AOP]*/
    730   public void testBindIntercepor() {
    731     final Matcher<Class> classMatcher = Matchers.subclassesOf(List.class);
    732     final Matcher<Object> methodMatcher = Matchers.any();
    733     final org.aopalliance.intercept.MethodInterceptor methodInterceptor =
    734         new org.aopalliance.intercept.MethodInterceptor() {
    735           @Override
    736           public Object invoke(org.aopalliance.intercept.MethodInvocation methodInvocation) {
    737             return null;
    738           }
    739         };
    740 
    741     checkModule(
    742         new AbstractModule() {
    743           @Override
    744           protected void configure() {
    745             bindInterceptor(classMatcher, methodMatcher, methodInterceptor);
    746           }
    747         },
    748         new FailingElementVisitor() {
    749           @Override
    750           public Void visit(InterceptorBinding command) {
    751             assertSame(classMatcher, command.getClassMatcher());
    752             assertSame(methodMatcher, command.getMethodMatcher());
    753             assertEquals(Arrays.asList(methodInterceptor), command.getInterceptors());
    754             return null;
    755           }
    756         });
    757   }
    758   /*end[AOP]*/
    759 
    760   public void testBindScope() {
    761     checkModule(
    762         new AbstractModule() {
    763           @Override
    764           protected void configure() {
    765             bindScope(SampleAnnotation.class, Scopes.NO_SCOPE);
    766           }
    767         },
    768         new FailingElementVisitor() {
    769           @Override
    770           public Void visit(ScopeBinding command) {
    771             assertSame(SampleAnnotation.class, command.getAnnotationType());
    772             assertSame(Scopes.NO_SCOPE, command.getScope());
    773             return null;
    774           }
    775         });
    776   }
    777 
    778   public void testBindListener() {
    779     final Matcher<Object> typeMatcher = Matchers.only(TypeLiteral.get(String.class));
    780     final TypeListener listener =
    781         new TypeListener() {
    782           @Override
    783           public <I> void hear(TypeLiteral<I> type, TypeEncounter<I> encounter) {
    784             throw new UnsupportedOperationException();
    785           }
    786         };
    787 
    788     checkModule(
    789         new AbstractModule() {
    790           @Override
    791           protected void configure() {
    792             bindListener(typeMatcher, listener);
    793           }
    794         },
    795         new FailingElementVisitor() {
    796           @Override
    797           public Void visit(TypeListenerBinding binding) {
    798             assertSame(typeMatcher, binding.getTypeMatcher());
    799             assertSame(listener, binding.getListener());
    800             return null;
    801           }
    802         });
    803   }
    804 
    805   public void testConvertToTypes() {
    806     final TypeConverter typeConverter =
    807         new TypeConverter() {
    808           @Override
    809           public Object convert(String value, TypeLiteral<?> toType) {
    810             return value;
    811           }
    812         };
    813 
    814     checkModule(
    815         new AbstractModule() {
    816           @Override
    817           protected void configure() {
    818             convertToTypes(Matchers.any(), typeConverter);
    819           }
    820         },
    821         new FailingElementVisitor() {
    822           @Override
    823           public Void visit(TypeConverterBinding command) {
    824             assertSame(typeConverter, command.getTypeConverter());
    825             assertSame(Matchers.any(), command.getTypeMatcher());
    826             return null;
    827           }
    828         });
    829   }
    830 
    831   public void testGetProvider() {
    832     checkModule(
    833         new AbstractModule() {
    834           @Override
    835           protected void configure() {
    836             Provider<String> keyGetProvider =
    837                 getProvider(Key.get(String.class, SampleAnnotation.class));
    838             try {
    839               keyGetProvider.get();
    840               fail("Expected IllegalStateException");
    841             } catch (IllegalStateException e) {
    842               assertEquals(
    843                   "This Provider cannot be used until the Injector has been created.",
    844                   e.getMessage());
    845             }
    846 
    847             Provider<String> typeGetProvider = getProvider(String.class);
    848             try {
    849               typeGetProvider.get();
    850               fail("Expected IllegalStateException");
    851             } catch (IllegalStateException e) {
    852               assertEquals(
    853                   "This Provider cannot be used until the Injector has been created.",
    854                   e.getMessage());
    855             }
    856           }
    857         },
    858         new FailingElementVisitor() {
    859           @Override
    860           public <T> Void visit(ProviderLookup<T> command) {
    861             assertEquals(Key.get(String.class, SampleAnnotation.class), command.getKey());
    862             assertNull(command.getDelegate());
    863             return null;
    864           }
    865         },
    866         new FailingElementVisitor() {
    867           @Override
    868           public <T> Void visit(ProviderLookup<T> command) {
    869             assertEquals(Key.get(String.class), command.getKey());
    870             assertNull(command.getDelegate());
    871             return null;
    872           }
    873         });
    874   }
    875 
    876   public void testElementInitialization() {
    877     final AtomicReference<Provider<String>> providerFromBinder =
    878         new AtomicReference<Provider<String>>();
    879     final AtomicReference<MembersInjector<String>> membersInjectorFromBinder =
    880         new AtomicReference<MembersInjector<String>>();
    881 
    882     final AtomicReference<String> lastInjected = new AtomicReference<>();
    883     final MembersInjector<String> stringInjector =
    884         new MembersInjector<String>() {
    885           @Override
    886           public void injectMembers(String instance) {
    887             lastInjected.set(instance);
    888           }
    889         };
    890 
    891     checkModule(
    892         new AbstractModule() {
    893           @Override
    894           protected void configure() {
    895             providerFromBinder.set(getProvider(String.class));
    896             membersInjectorFromBinder.set(getMembersInjector(String.class));
    897           }
    898         },
    899         new FailingElementVisitor() {
    900           @Override
    901           public <T> Void visit(ProviderLookup<T> providerLookup) {
    902             @SuppressWarnings("unchecked") // we know that T is a String here
    903             ProviderLookup<String> stringLookup = (ProviderLookup<String>) providerLookup;
    904             stringLookup.initializeDelegate(Providers.of("out"));
    905 
    906             assertEquals("out", providerFromBinder.get().get());
    907             return null;
    908           }
    909         },
    910         new FailingElementVisitor() {
    911           @Override
    912           public <T> Void visit(MembersInjectorLookup<T> lookup) {
    913             @SuppressWarnings("unchecked") // we know that T is a String here
    914             MembersInjectorLookup<String> stringLookup = (MembersInjectorLookup<String>) lookup;
    915             stringLookup.initializeDelegate(stringInjector);
    916 
    917             membersInjectorFromBinder.get().injectMembers("in");
    918             assertEquals("in", lastInjected.get());
    919             return null;
    920           }
    921         });
    922   }
    923 
    924   public void testGetMembersInjector() {
    925     checkModule(
    926         new AbstractModule() {
    927           @Override
    928           protected void configure() {
    929             MembersInjector<A<String>> typeMembersInjector =
    930                 getMembersInjector(new TypeLiteral<A<String>>() {});
    931             try {
    932               typeMembersInjector.injectMembers(new A<String>());
    933               fail("Expected IllegalStateException");
    934             } catch (IllegalStateException e) {
    935               assertEquals(
    936                   "This MembersInjector cannot be used until the Injector has been created.",
    937                   e.getMessage());
    938             }
    939 
    940             MembersInjector<String> classMembersInjector = getMembersInjector(String.class);
    941             try {
    942               classMembersInjector.injectMembers("hello");
    943               fail("Expected IllegalStateException");
    944             } catch (IllegalStateException e) {
    945               assertEquals(
    946                   "This MembersInjector cannot be used until the Injector has been created.",
    947                   e.getMessage());
    948             }
    949           }
    950         },
    951         new FailingElementVisitor() {
    952           @Override
    953           public <T> Void visit(MembersInjectorLookup<T> command) {
    954             assertEquals(new TypeLiteral<A<String>>() {}, command.getType());
    955             assertNull(command.getDelegate());
    956             return null;
    957           }
    958         },
    959         new FailingElementVisitor() {
    960           @Override
    961           public <T> Void visit(MembersInjectorLookup<T> command) {
    962             assertEquals(TypeLiteral.get(String.class), command.getType());
    963             assertNull(command.getDelegate());
    964             return null;
    965           }
    966         });
    967   }
    968 
    969   public void testRequestInjection() {
    970     final Object firstObject = new Object();
    971     final Object secondObject = new Object();
    972 
    973     checkModule(
    974         new AbstractModule() {
    975           @Override
    976           protected void configure() {
    977             requestInjection(firstObject);
    978             requestInjection(secondObject);
    979           }
    980         },
    981         new FailingElementVisitor() {
    982           @Override
    983           public Void visit(InjectionRequest<?> command) {
    984             assertEquals(firstObject, command.getInstance());
    985             return null;
    986           }
    987         },
    988         new FailingElementVisitor() {
    989           @Override
    990           public Void visit(InjectionRequest<?> command) {
    991             assertEquals(secondObject, command.getInstance());
    992             return null;
    993           }
    994         });
    995   }
    996 
    997   public void testRequestStaticInjection() {
    998     checkModule(
    999         new AbstractModule() {
   1000           @Override
   1001           protected void configure() {
   1002             requestStaticInjection(ArrayList.class);
   1003           }
   1004         },
   1005         new FailingElementVisitor() {
   1006           @Override
   1007           public Void visit(StaticInjectionRequest command) {
   1008             assertEquals(ArrayList.class, command.getType());
   1009             return null;
   1010           }
   1011         });
   1012   }
   1013 
   1014   public void testNewPrivateBinder() {
   1015     final Key<Collection> collection = Key.get(Collection.class, SampleAnnotation.class);
   1016     final Key<ArrayList> arrayList = Key.get(ArrayList.class);
   1017     final ImmutableSet<Key<?>> collections = ImmutableSet.<Key<?>>of(arrayList, collection);
   1018 
   1019     final Key<?> a = Key.get(String.class, Names.named("a"));
   1020     final Key<?> b = Key.get(String.class, Names.named("b"));
   1021     final ImmutableSet<Key<?>> ab = ImmutableSet.of(a, b);
   1022 
   1023     checkModule(
   1024         new AbstractModule() {
   1025           @Override
   1026           protected void configure() {
   1027             PrivateBinder one = binder().newPrivateBinder();
   1028             one.expose(ArrayList.class);
   1029             one.expose(Collection.class).annotatedWith(SampleAnnotation.class);
   1030             one.bind(List.class).to(ArrayList.class);
   1031 
   1032             PrivateBinder two =
   1033                 binder().withSource("1 FooBar").newPrivateBinder().withSource("2 FooBar");
   1034             two.expose(String.class).annotatedWith(Names.named("a"));
   1035             two.expose(b);
   1036             two.bind(List.class).to(ArrayList.class);
   1037           }
   1038         },
   1039         new FailingElementVisitor() {
   1040           @Override
   1041           public Void visit(PrivateElements one) {
   1042             assertEquals(collections, one.getExposedKeys());
   1043             checkElements(
   1044                 one.getElements(),
   1045                 new FailingElementVisitor() {
   1046                   @Override
   1047                   public <T> Void visit(Binding<T> binding) {
   1048                     assertEquals(Key.get(List.class), binding.getKey());
   1049                     return null;
   1050                   }
   1051                 });
   1052             return null;
   1053           }
   1054         },
   1055         new ExternalFailureVisitor() {
   1056           @Override
   1057           public Void visit(PrivateElements two) {
   1058             assertEquals(ab, two.getExposedKeys());
   1059             assertEquals("1 FooBar", two.getSource().toString());
   1060             checkElements(
   1061                 two.getElements(),
   1062                 new ExternalFailureVisitor() {
   1063                   @Override
   1064                   public <T> Void visit(Binding<T> binding) {
   1065                     assertEquals("2 FooBar", binding.getSource().toString());
   1066                     assertEquals(Key.get(List.class), binding.getKey());
   1067                     return null;
   1068                   }
   1069                 });
   1070             return null;
   1071           }
   1072         });
   1073   }
   1074 
   1075   public void testBindWithMultipleAnnotationsAddsError() {
   1076     checkModule(
   1077         new AbstractModule() {
   1078           @Override
   1079           protected void configure() {
   1080             AnnotatedBindingBuilder<String> abb = bind(String.class);
   1081             abb.annotatedWith(SampleAnnotation.class);
   1082             abb.annotatedWith(Names.named("A"));
   1083           }
   1084         },
   1085         new FailingElementVisitor() {
   1086           @Override
   1087           public <T> Void visit(Binding<T> command) {
   1088             return null;
   1089           }
   1090         },
   1091         new FailingElementVisitor() {
   1092           @Override
   1093           public Void visit(Message command) {
   1094             assertEquals(
   1095                 "More than one annotation is specified for this binding.", command.getMessage());
   1096             assertNull(command.getCause());
   1097             assertContains(command.getSource(), getDeclaringSourcePart(ElementsTest.class));
   1098             return null;
   1099           }
   1100         });
   1101   }
   1102 
   1103   public void testBindWithMultipleTargetsAddsError() {
   1104     checkModule(
   1105         new AbstractModule() {
   1106           @Override
   1107           protected void configure() {
   1108             AnnotatedBindingBuilder<String> abb = bind(String.class);
   1109             abb.toInstance("A");
   1110             abb.toInstance("B");
   1111           }
   1112         },
   1113         new FailingElementVisitor() {
   1114           @Override
   1115           public <T> Void visit(Binding<T> command) {
   1116             return null;
   1117           }
   1118         },
   1119         new FailingElementVisitor() {
   1120           @Override
   1121           public Void visit(Message command) {
   1122             assertEquals("Implementation is set more than once.", command.getMessage());
   1123             assertNull(command.getCause());
   1124             assertContains(command.getSource(), getDeclaringSourcePart(ElementsTest.class));
   1125             return null;
   1126           }
   1127         });
   1128   }
   1129 
   1130   public void testBindWithMultipleScopesAddsError() {
   1131     checkModule(
   1132         new AbstractModule() {
   1133           @Override
   1134           protected void configure() {
   1135             ScopedBindingBuilder sbb = bind(List.class).to(ArrayList.class);
   1136             sbb.in(Scopes.NO_SCOPE);
   1137             sbb.asEagerSingleton();
   1138           }
   1139         },
   1140         new FailingElementVisitor() {
   1141           @Override
   1142           public <T> Void visit(Binding<T> command) {
   1143             return null;
   1144           }
   1145         },
   1146         new FailingElementVisitor() {
   1147           @Override
   1148           public Void visit(Message command) {
   1149             assertEquals("Scope is set more than once.", command.getMessage());
   1150             assertNull(command.getCause());
   1151             assertContains(command.getSource(), getDeclaringSourcePart(ElementsTest.class));
   1152             return null;
   1153           }
   1154         });
   1155   }
   1156 
   1157   public void testBindConstantWithMultipleAnnotationsAddsError() {
   1158     checkModule(
   1159         new AbstractModule() {
   1160           @Override
   1161           protected void configure() {
   1162             AnnotatedConstantBindingBuilder cbb = bindConstant();
   1163             cbb.annotatedWith(SampleAnnotation.class).to("A");
   1164             cbb.annotatedWith(Names.named("A"));
   1165           }
   1166         },
   1167         new FailingElementVisitor() {
   1168           @Override
   1169           public <T> Void visit(Binding<T> command) {
   1170             return null;
   1171           }
   1172         },
   1173         new FailingElementVisitor() {
   1174           @Override
   1175           public Void visit(Message command) {
   1176             assertEquals(
   1177                 "More than one annotation is specified for this binding.", command.getMessage());
   1178             assertNull(command.getCause());
   1179             assertContains(command.getSource(), getDeclaringSourcePart(ElementsTest.class));
   1180             return null;
   1181           }
   1182         });
   1183   }
   1184 
   1185   public void testBindConstantWithMultipleTargetsAddsError() {
   1186     checkModule(
   1187         new AbstractModule() {
   1188           @Override
   1189           protected void configure() {
   1190             ConstantBindingBuilder cbb = bindConstant().annotatedWith(SampleAnnotation.class);
   1191             cbb.to("A");
   1192             cbb.to("B");
   1193           }
   1194         },
   1195         new FailingElementVisitor() {
   1196           @Override
   1197           public <T> Void visit(Binding<T> command) {
   1198             return null;
   1199           }
   1200         },
   1201         new FailingElementVisitor() {
   1202           @Override
   1203           public Void visit(Message message) {
   1204             assertEquals("Constant value is set more than once.", message.getMessage());
   1205             assertNull(message.getCause());
   1206             assertContains(message.getSource(), getDeclaringSourcePart(ElementsTest.class));
   1207             return null;
   1208           }
   1209         });
   1210   }
   1211 
   1212   public void testBindToConstructor() throws NoSuchMethodException, NoSuchFieldException {
   1213     final Constructor<A> aConstructor = A.class.getDeclaredConstructor();
   1214     final Constructor<B> bConstructor = B.class.getDeclaredConstructor(Object.class);
   1215     final Field field = B.class.getDeclaredField("stage");
   1216 
   1217     checkModule(
   1218         new AbstractModule() {
   1219           @Override
   1220           protected void configure() {
   1221             bind(A.class).toConstructor(aConstructor);
   1222             bind(B.class)
   1223                 .toConstructor(bConstructor, new TypeLiteral<B<Integer>>() {})
   1224                 .in(Singleton.class);
   1225           }
   1226         },
   1227         new FailingElementVisitor() {
   1228           @Override
   1229           public <T> Void visit(Binding<T> binding) {
   1230             assertEquals(new Key<A>() {}, binding.getKey());
   1231 
   1232             return binding.acceptTargetVisitor(
   1233                 new FailingTargetVisitor<T>() {
   1234                   @Override
   1235                   public Void visit(ConstructorBinding<? extends T> constructorBinding) {
   1236                     InjectionPoint injectionPoint = constructorBinding.getConstructor();
   1237                     assertEquals(aConstructor, injectionPoint.getMember());
   1238                     assertEquals(new TypeLiteral<A>() {}, injectionPoint.getDeclaringType());
   1239                     return null;
   1240                   }
   1241                 });
   1242           }
   1243         },
   1244         new FailingElementVisitor() {
   1245           @Override
   1246           public <T> Void visit(Binding<T> binding) {
   1247             assertEquals(new Key<B>() {}, binding.getKey());
   1248             binding.acceptScopingVisitor(
   1249                 new FailingBindingScopingVisitor() {
   1250                   @Override
   1251                   public Void visitScopeAnnotation(Class<? extends Annotation> annotation) {
   1252                     assertEquals(Singleton.class, annotation);
   1253                     return null;
   1254                   }
   1255                 });
   1256 
   1257             binding.acceptTargetVisitor(
   1258                 new FailingTargetVisitor<T>() {
   1259                   @Override
   1260                   public Void visit(ConstructorBinding<? extends T> constructorBinding) {
   1261                     assertEquals(bConstructor, constructorBinding.getConstructor().getMember());
   1262                     assertEquals(
   1263                         Key.get(Integer.class),
   1264                         getOnlyElement(constructorBinding.getConstructor().getDependencies())
   1265                             .getKey());
   1266                     assertEquals(
   1267                         field,
   1268                         getOnlyElement(constructorBinding.getInjectableMembers()).getMember());
   1269                     assertEquals(2, constructorBinding.getDependencies().size());
   1270                     /*if[AOP]*/
   1271                     assertEquals(ImmutableMap.of(), constructorBinding.getMethodInterceptors());
   1272                     /*end[AOP]*/
   1273                     return null;
   1274                   }
   1275                 });
   1276             return null;
   1277           }
   1278         });
   1279   }
   1280 
   1281   public void testBindToMalformedConstructor() throws NoSuchMethodException, NoSuchFieldException {
   1282     final Constructor<C> constructor = C.class.getDeclaredConstructor(Integer.class);
   1283 
   1284     checkModule(
   1285         new AbstractModule() {
   1286           @Override
   1287           protected void configure() {
   1288             bind(C.class).toConstructor(constructor);
   1289           }
   1290         },
   1291         new FailingElementVisitor() {
   1292           @Override
   1293           public <T> Void visit(Binding<T> binding) {
   1294             assertEquals(Key.get(C.class), binding.getKey());
   1295             assertTrue(binding instanceof UntargettedBinding);
   1296             return null;
   1297           }
   1298         },
   1299         new ExternalFailureVisitor() {
   1300           @Override
   1301           public Void visit(Message message) {
   1302             assertContains(
   1303                 message.getMessage(),
   1304                 C.class.getName() + ".a has more than one annotation ",
   1305                 Named.class.getName(),
   1306                 SampleAnnotation.class.getName());
   1307             return null;
   1308           }
   1309         },
   1310         new ExternalFailureVisitor() {
   1311           @Override
   1312           public Void visit(Message message) {
   1313             assertContains(
   1314                 message.getMessage(),
   1315                 C.class.getName() + ".<init>() has more than one annotation ",
   1316                 Named.class.getName(),
   1317                 SampleAnnotation.class.getName());
   1318             return null;
   1319           }
   1320         });
   1321   }
   1322 
   1323   // Business logic tests
   1324 
   1325   public void testModulesAreInstalledAtMostOnce() {
   1326     final AtomicInteger aConfigureCount = new AtomicInteger(0);
   1327     final Module a =
   1328         new AbstractModule() {
   1329           @Override
   1330           public void configure() {
   1331             aConfigureCount.incrementAndGet();
   1332           }
   1333         };
   1334 
   1335     Elements.getElements(a, a);
   1336     assertEquals(1, aConfigureCount.get());
   1337 
   1338     aConfigureCount.set(0);
   1339     Module b =
   1340         new AbstractModule() {
   1341           @Override
   1342           protected void configure() {
   1343             install(a);
   1344             install(a);
   1345           }
   1346         };
   1347 
   1348     Elements.getElements(b);
   1349     assertEquals(1, aConfigureCount.get());
   1350   }
   1351 
   1352   /** Ensures the module performs the commands consistent with {@code visitors}. */
   1353   protected void checkModule(Module module, ElementVisitor<?>... visitors) {
   1354     List<Element> elements = Elements.getElements(module);
   1355     assertEquals(elements.size(), visitors.length);
   1356     checkElements(elements, visitors);
   1357   }
   1358 
   1359   protected void checkElements(List<Element> elements, ElementVisitor<?>... visitors) {
   1360     for (int i = 0; i < visitors.length; i++) {
   1361       ElementVisitor<?> visitor = visitors[i];
   1362       Element element = elements.get(i);
   1363       if (!(element instanceof Message)) {
   1364         ElementSource source = (ElementSource) element.getSource();
   1365         assertFalse(source.getModuleClassNames().isEmpty());
   1366         if (isIncludeStackTraceComplete()) {
   1367           assertTrue(source.getStackTrace().length > 0);
   1368         } else {
   1369           assertEquals(0, source.getStackTrace().length);
   1370         }
   1371       }
   1372       if (!(visitor instanceof ExternalFailureVisitor)) {
   1373         assertContains(element.getSource().toString(), getDeclaringSourcePart(ElementsTest.class));
   1374       }
   1375       element.acceptVisitor(visitor);
   1376     }
   1377   }
   1378 
   1379   private static class ListProvider implements Provider<List> {
   1380     @Override
   1381     public List get() {
   1382       return new ArrayList();
   1383     }
   1384   }
   1385 
   1386   private static class TProvider<T> implements Provider<T> {
   1387     @Override
   1388     public T get() {
   1389       return null;
   1390     }
   1391   }
   1392 
   1393   /**
   1394    * By extending this interface rather than FailingElementVisitor, the source of the error doesn't
   1395    * need to contain the string {@code ElementsTest.java}.
   1396    */
   1397   abstract static class ExternalFailureVisitor extends FailingElementVisitor {}
   1398 
   1399   @Retention(RUNTIME)
   1400   @Target({ElementType.FIELD, ElementType.PARAMETER, ElementType.METHOD})
   1401   @BindingAnnotation
   1402   public @interface SampleAnnotation {}
   1403 
   1404   public enum CoinSide {
   1405     HEADS,
   1406     TAILS
   1407   }
   1408 
   1409   static class A<T> {
   1410     @Inject Stage stage;
   1411   }
   1412 
   1413   static class B<T> {
   1414     @Inject Stage stage;
   1415 
   1416     B(T t) {}
   1417   }
   1418 
   1419   static class C {
   1420     @Inject
   1421     @Named("foo")
   1422     @SampleAnnotation
   1423     String a;
   1424 
   1425     C(@Named("bar") @SampleAnnotation Integer b) {}
   1426   }
   1427 }
   1428