Home | History | Annotate | Download | only in assistedinject
      1 /**
      2  * Copyright (C) 2010 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.assistedinject;
     18 
     19 import static com.google.inject.name.Names.named;
     20 
     21 import com.google.common.collect.ImmutableList;
     22 import com.google.common.collect.ImmutableSet;
     23 import com.google.common.collect.Iterables;
     24 import com.google.common.collect.Lists;
     25 import com.google.common.collect.Sets;
     26 import com.google.inject.AbstractModule;
     27 import com.google.inject.Binding;
     28 import com.google.inject.Guice;
     29 import com.google.inject.Inject;
     30 import com.google.inject.Injector;
     31 import com.google.inject.Key;
     32 import com.google.inject.Stage;
     33 import com.google.inject.name.Named;
     34 import com.google.inject.spi.DefaultBindingTargetVisitor;
     35 import com.google.inject.spi.Dependency;
     36 import com.google.inject.spi.Element;
     37 import com.google.inject.spi.Elements;
     38 
     39 import junit.framework.AssertionFailedError;
     40 import junit.framework.TestCase;
     41 
     42 import java.util.List;
     43 import java.util.Set;
     44 import java.util.logging.Logger;
     45 
     46 /**
     47  * Tests for AssistedInject Spi.
     48  *
     49  * @author ramakrishna (at) google.com (Ramakrishna Rajanna)
     50  */
     51 public class ExtensionSpiTest extends TestCase {
     52 
     53   public final void testSpiOnElements() throws Exception {
     54     AssistedInjectSpiVisitor visitor = new AssistedInjectSpiVisitor();
     55     Integer count = 0;
     56     for(Element element : Elements.getElements(new Module())) {
     57       if(element instanceof Binding) {
     58         assertEquals(count++, ((Binding<?>)element).acceptTargetVisitor(visitor));
     59       }
     60     }
     61     validateVisitor(visitor);
     62   }
     63 
     64   public void testSpiOnVisitor() throws Exception {
     65     AssistedInjectSpiVisitor visitor = new AssistedInjectSpiVisitor();
     66     Integer count = 0;
     67     Injector injector = Guice.createInjector(new Module());
     68     for(Binding<?> binding : injector.getBindings().values()) {
     69       assertEquals(count++, binding.acceptTargetVisitor(visitor));
     70     }
     71     validateVisitor(visitor);
     72   }
     73 
     74   private void validateVisitor(AssistedInjectSpiVisitor visitor) throws Exception {
     75     assertEquals(1, visitor.assistedBindingCount);
     76     List<AssistedMethod> assistedMethods =
     77         Lists.newArrayList(Iterables.getOnlyElement(
     78             visitor.assistedInjectBindings).getAssistedMethods());
     79     assertEquals(7, assistedMethods.size());
     80     assertEquals(1, visitor.assistedBindingCount);
     81     assertEquals(1, visitor.assistedInjectBindings.size());
     82 
     83     // Validate for each of the methods in AnimalFactory
     84 
     85     Set<String> names = Sets.newHashSet();
     86     for (AssistedMethod method : assistedMethods) {
     87       String name = method.getFactoryMethod().getName();
     88       names.add(name);
     89       if (name.equals("createAStrangeCatAsAnimal")) {
     90         validateAssistedMethod(method, name, StrangeCat.class, ImmutableList.<Key<?>>of());
     91       } else if (name.equals("createStrangeCatWithConstructorForOwner")) {
     92         validateAssistedMethod(method, name, StrangeCat.class, ImmutableList.<Key<?>>of());
     93       } else if (name.equals("createStrangeCatWithConstructorForAge")) {
     94         validateAssistedMethod(method, name, StrangeCat.class, ImmutableList.<Key<?>>of());
     95       } else if (name.equals("createCatWithANonAssistedDependency")) {
     96         validateAssistedMethod(method, name, CatWithAName.class,
     97             ImmutableList.<Key<?>>of(Key.get(String.class, named("catName2"))));
     98       } else if (name.equals("createCat")) {
     99         validateAssistedMethod(method, name, Cat.class, ImmutableList.<Key<?>>of());
    100       } else if (name.equals("createASimpleCatAsAnimal")) {
    101         validateAssistedMethod(method, name, SimpleCat.class, ImmutableList.<Key<?>>of());
    102       } else if (name.equals("createCatWithNonAssistedDependencies")) {
    103         List<Key<?>> dependencyKeys = ImmutableList.<Key<?>>of(
    104             Key.get(String.class, named("catName1")),
    105             Key.get(String.class, named("petName")),
    106             Key.get(Integer.class, named("age")));
    107         validateAssistedMethod(method, name, ExplodingCat.class, dependencyKeys);
    108       } else {
    109         fail("Invalid method: " + method);
    110       }
    111     }
    112     assertEquals(names, ImmutableSet.of("createAStrangeCatAsAnimal",
    113         "createStrangeCatWithConstructorForOwner",
    114         "createStrangeCatWithConstructorForAge",
    115         "createCatWithANonAssistedDependency",
    116         "createCat",
    117         "createASimpleCatAsAnimal",
    118         "createCatWithNonAssistedDependencies"));
    119   }
    120 
    121   private void validateAssistedMethod(AssistedMethod assistedMethod, String factoryMethodName,
    122                                       Class clazz, List<Key<?>> dependencyKeys){
    123     assertEquals(factoryMethodName, assistedMethod.getFactoryMethod().getName());
    124     assertEquals(clazz, assistedMethod.getImplementationConstructor().getDeclaringClass());
    125     assertEquals(dependencyKeys.size(), assistedMethod.getDependencies().size());
    126     for (Dependency<?> dependency : assistedMethod.getDependencies()) {
    127       assertTrue(dependencyKeys.contains(dependency.getKey()));
    128     }
    129     assertEquals(clazz, assistedMethod.getImplementationType().getType());
    130   }
    131 
    132 
    133   interface AnimalFactory {
    134     Cat createCat(String owner);
    135     CatWithAName createCatWithANonAssistedDependency(String owner);
    136     @Named("SimpleCat") Animal createASimpleCatAsAnimal(String owner);
    137     Animal createAStrangeCatAsAnimal(String owner);
    138     StrangeCat createStrangeCatWithConstructorForOwner(String owner);
    139     StrangeCat createStrangeCatWithConstructorForAge(Integer age);
    140     ExplodingCat createCatWithNonAssistedDependencies(String owner);
    141   }
    142 
    143   interface Animal {}
    144 
    145   private static class Cat implements Animal {
    146     @Inject Cat(@Assisted String owner) {}
    147   }
    148 
    149   private static class SimpleCat implements Animal {
    150     @Inject SimpleCat(@Assisted String owner) {
    151     }
    152   }
    153 
    154   private static class StrangeCat implements Animal {
    155     @AssistedInject StrangeCat(@Assisted String owner) {}
    156     @AssistedInject StrangeCat(@Assisted Integer age) {}
    157   }
    158 
    159   private static class ExplodingCat implements Animal {
    160     @Inject public ExplodingCat(@Named("catName1") String name, @Assisted String owner,
    161                                 @Named("age") Integer age, @Named("petName") String petName) {}
    162   }
    163 
    164   private static class CatWithAName extends Cat {
    165     @Inject CatWithAName(@Assisted String owner, @Named("catName2") String name) {
    166       super(owner);
    167     }
    168   }
    169 
    170   public class Module extends AbstractModule{
    171     @Override
    172     protected void configure() {
    173       bind(String.class).annotatedWith(named("catName1")).toInstance("kitty1");
    174       bind(String.class).annotatedWith(named("catName2")).toInstance("kitty2");
    175       bind(String.class).annotatedWith(named("petName")).toInstance("pussy");
    176       bind(Integer.class).annotatedWith(named("age")).toInstance(12);
    177       install(new FactoryModuleBuilder()
    178         .implement(Animal.class, StrangeCat.class)
    179         .implement(Animal.class, named("SimpleCat"), SimpleCat.class)
    180         .build(AnimalFactory.class));
    181     }
    182   }
    183 
    184   public class AssistedInjectSpiVisitor extends DefaultBindingTargetVisitor<Object, Integer>
    185       implements AssistedInjectTargetVisitor<Object, Integer>  {
    186 
    187     private final Set<Class> allowedClasses =
    188       ImmutableSet.<Class> of(
    189         Injector.class, Stage.class, Logger.class,
    190         String.class, Integer.class);
    191 
    192     private int assistedBindingCount = 0;
    193     private int currentCount = 0;
    194     private List<AssistedInjectBinding<?>> assistedInjectBindings = Lists.newArrayList();
    195 
    196     public Integer visit(AssistedInjectBinding assistedInjectBinding) {
    197       assistedInjectBindings.add(assistedInjectBinding);
    198       assistedBindingCount++;
    199       return currentCount++;
    200     }
    201 
    202     @Override
    203     protected Integer visitOther(Binding<? extends Object> binding) {
    204       if(!allowedClasses.contains(binding.getKey().getTypeLiteral().getRawType())) {
    205         throw new AssertionFailedError("invalid other binding: " + binding);
    206       }
    207       return currentCount++;
    208     }
    209   }
    210 }
    211