/*
* Grapht, an open source dependency injector.
* Copyright 2014-2015 various contributors (see CONTRIBUTORS.txt)
* Copyright 2010-2014 Regents of the University of Minnesota
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License along with
* this program; if not, write to the Free Software Foundation, Inc., 51
* Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
package org.grouplens.grapht.reflect.internal;
import org.grouplens.grapht.ResolutionException;
import org.grouplens.grapht.reflect.Desire;
import org.grouplens.grapht.reflect.InjectionPoint;
import org.grouplens.grapht.reflect.MockInjectionPoint;
import org.grouplens.grapht.reflect.internal.types.*;
import org.grouplens.grapht.solver.*;
import org.junit.Assert;
import org.junit.Test;
import javax.annotation.Nullable;
import javax.inject.Inject;
import java.lang.reflect.Method;
import java.util.List;
import static org.hamcrest.Matchers.instanceOf;
import static org.hamcrest.Matchers.nullValue;
public class ReflectionDesireTest {
@Test
public void testSubtypeInjectionPointSatisfactionConstructor() throws Exception {
ClassSatisfaction satis = new ClassSatisfaction(B.class);
InjectionPoint inject = new MockInjectionPoint(A.class, false);
ReflectionDesire desire = new ReflectionDesire(B.class, inject, satis);
Assert.assertEquals(B.class, desire.getDesiredType());
Assert.assertEquals(satis, desire.getSatisfaction());
Assert.assertEquals(inject, desire.getInjectionPoint());
}
@Test
public void testInheritedRoleDefault() throws Exception {
// Test that the default desire for the setRoleE injection point in TypeC
// defaults to TypeB. This also tests qualifier default inheritence
List<Desire> desires = ReflectionDesire.getDesires(TypeC.class);
ReflectionDesire dflt = getDefaultDesire(TypeC.class.getMethod("setRoleD", InterfaceB.class), desires);
Assert.assertTrue(dflt.getSatisfaction() instanceof ClassSatisfaction);
Assert.assertThat(dflt.getInjectionPoint().getQualifier(),
instanceOf(RoleD.class));
Assert.assertEquals(TypeB.class, ((ClassSatisfaction) dflt.getSatisfaction()).getErasedType());
Assert.assertEquals(TypeB.class, dflt.getDesiredType());
}
@Test
public void testRoleParameterDefault() throws Exception {
// Test that the default desire for the constructor injection in TypeC
// defaults to the int value 5
List<Desire> desires = ReflectionDesire.getDesires(TypeC.class);
ReflectionDesire dflt = getDefaultDesire(0, desires);
Assert.assertTrue(dflt.getSatisfaction() instanceof InstanceSatisfaction);
Assert.assertThat(dflt.getInjectionPoint().getQualifier(),
instanceOf(ParameterA.class));
Assert.assertEquals(Integer.class, dflt.getDesiredType());
Assert.assertEquals(5, ((InstanceSatisfaction) dflt.getSatisfaction()).getInstance());
}
@Test
public void testProvidedByDefault() throws Exception {
// Test that the default desire for the setTypeA injection point in TypeC
// is satisfied by a provider satisfaction to ProviderA
List<Desire> desires = ReflectionDesire.getDesires(TypeC.class);
ReflectionDesire dflt = getDefaultDesire(TypeC.class.getMethod("setTypeA", TypeA.class), desires);
Assert.assertTrue(dflt.getSatisfaction() instanceof ProviderClassSatisfaction);
Assert.assertThat(dflt.getInjectionPoint().getQualifier(),
nullValue());
Assert.assertEquals(TypeA.class, dflt.getDesiredType());
Assert.assertEquals(ProviderA.class, ((ProviderClassSatisfaction) dflt.getSatisfaction()).getProviderType());
}
@Test
public void testImplementedByDefault() throws Exception {
// Test that the default desire for the setRoleA injection point in TypeC
// is satisfied by a type binding to TypeA
List<Desire> desires = ReflectionDesire.getDesires(TypeC.class);
ReflectionDesire dflt = getDefaultDesire(TypeC.class.getMethod("setRoleA", InterfaceA.class), desires);
Assert.assertTrue(dflt.getSatisfaction() instanceof ClassSatisfaction);
Assert.assertThat(dflt.getInjectionPoint().getQualifier(),
instanceOf(RoleA.class));
Assert.assertEquals(TypeA.class, ((ClassSatisfaction) dflt.getSatisfaction()).getErasedType());
Assert.assertEquals(TypeA.class, dflt.getDesiredType());
}
@Test
public void testNoDefaultDesire() throws Exception {
// Test that there is no default desire for the setTypeB injection point
// in TypeC, but that it is still satisfiable
List<Desire> desires = ReflectionDesire.getDesires(TypeC.class);
ReflectionDesire dflt = getDefaultDesire(TypeC.class.getMethod("setTypeB", TypeB.class), desires);
Assert.assertNull(dflt);
}
/**
* If we have a nullable injection point, and restrict the desire to a class, we
* should throw out the null satisfaction (and recompute it, if appropriate) based
* on the restricted type.
*/
@Test
public void testRestrictNullableDesire() throws NoSuchMethodException, ResolutionException {
List<Desire> desires = ReflectionDesire.getDesires(ReqB.class);
Assert.assertEquals(1, desires.size());
Desire desire = desires.get(0);
Desire restricted = desire.restrict(TypeB.class);
Assert.assertNotNull(restricted);
}
private ReflectionDesire getDefaultDesire(Object methodOrCtorParam, List<Desire> desires) throws ResolutionException {
BindingResult result = null;
for (Desire d: desires) {
if (methodOrCtorParam instanceof Method) {
if (d.getInjectionPoint() instanceof SetterInjectionPoint) {
SetterInjectionPoint sp = (SetterInjectionPoint) (d.getInjectionPoint());
if (sp.getMember().equals(methodOrCtorParam)) {
result = DefaultDesireBindingFunction.create()
.bind(DependencySolver.initialContext(),
DesireChain.singleton(d));
break;
}
}
} else { // assume its an Integer
if (d.getInjectionPoint() instanceof ConstructorParameterInjectionPoint) {
ConstructorParameterInjectionPoint cp = (ConstructorParameterInjectionPoint) (d.getInjectionPoint());
if (((Integer) methodOrCtorParam).intValue() == cp.getParameterIndex()) {
result = DefaultDesireBindingFunction.create()
.bind(DependencySolver.initialContext(),
DesireChain.singleton(d));
break;
}
}
}
}
return (result == null ? null : (ReflectionDesire) result.getDesire());
}
public static class A { }
public static class B extends A { }
public static class C { }
public static class ReqB {
@Inject
public void setB(@Nullable InterfaceB foo) {
/* do nothing */
}
}
}