[picocontainer-scm] [scm-git][2/2] Work on allowing Providers and their provided types in CDI.
Michael Rimov <git-yCVjj/[email protected]> Thu, 21 Feb 2013 01:42:25 -0600 (CST)
| Newsgroups | gmane.comp.java.picocontainer.cvs |
|---|---|
| Message-ID | <[email protected]> |
commit 6fdd2fc9b3d7b769dd97193a49a353f79aa69c20 Author: Michael Rimov <[email protected]> AuthorDate: Wed, 20 Feb 2013 23:41:14 -0800 Commit: Michael Rimov <[email protected]> CommitDate: Wed, 20 Feb 2013 23:41:14 -0800 Work on allowing Providers and their provided types in CDI. Better testing for JTypeHelper diff --git a/pico/container/src/java/org/picocontainer/DefaultPicoContainer.java b/pico/container/src/java/org/picocontainer/DefaultPicoContainer.java index feee697..df37f32 100644 --- a/pico/container/src/java/org/picocontainer/DefaultPicoContainer.java +++ b/pico/container/src/java/org/picocontainer/DefaultPicoContainer.java @@ -480,11 +480,11 @@ public class DefaultPicoContainer implements MutablePicoContainer, Converting, C boolean compatible = JTypeHelper.isAssignableFrom(componentType, implementation); - if (componentAdapter.findAdapterOfType(ProviderAdapter.class) != null) { + if (!compatible && componentAdapter.findAdapterOfType(ProviderAdapter.class) != null) { //If provider //Todo: Direct access of provider adapter... work around. ProviderAdapter adapter = (ProviderAdapter)componentAdapter.findAdapterOfType(ProviderAdapter.class); - compatible |= JTypeHelper.isAssignableFrom(componentType, adapter.getProviderReturnType()); + compatible = JTypeHelper.isAssignableFrom(componentType, adapter.getProviderReturnType()); } if (compatible && diff --git a/pico/container/src/java/org/picocontainer/JTypeHelper.java b/pico/container/src/java/org/picocontainer/JTypeHelper.java index 114d0b7..345cb67 100644 --- a/pico/container/src/java/org/picocontainer/JTypeHelper.java +++ b/pico/container/src/java/org/picocontainer/JTypeHelper.java @@ -4,6 +4,8 @@ import com.googlecode.jtype.Generic; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; +import java.lang.reflect.TypeVariable; +import java.lang.reflect.WildcardType; @SuppressWarnings("rawtypes") public class JTypeHelper { @@ -25,28 +27,91 @@ public class JTypeHelper { * @return */ @SuppressWarnings("unchecked") - public static boolean isAssignableFrom(Generic<?> generic, Class<?> aClass) { + public static boolean isAssignableFrom(final Generic<?> generic, final Class<?> aClass) { Type type = generic.getType(); - if (type instanceof Class) { - return ((Class) type).isAssignableFrom(aClass); - } else if (type instanceof ParameterizedType) { + Class<?> typeToCompare = aClass; + if (type instanceof ParameterizedType) { //Generic g = Generic.get(aClass); - Type[] types = aClass.getGenericInterfaces(); - while (types.length == 0 && canGetSuperClass(aClass)) { - aClass = aClass.getSuperclass(); - types = aClass.getGenericInterfaces(); + //Recursively look for first super class that has a a parameterized type argument. + + ParameterizedType castType = (ParameterizedType)type; + boolean isWildcardType = false; + if (castType.getActualTypeArguments()[0] instanceof WildcardType) { + isWildcardType= true; + } + + + Type[] types = typeToCompare.getGenericInterfaces(); + while (types.length == 0 && canGetSuperClass(typeToCompare)) { + typeToCompare = typeToCompare.getSuperclass(); + types = typeToCompare.getGenericInterfaces(); } if (types.length == 0) { - return false; + //Parameter aClass doesn't have a type assigned to it, if the types are compatible + //then we'll ignore the generic and hope for the best because aClass is a raw type. + return generic.getRawType().isAssignableFrom(aClass); } Generic aClassGeneric = Generic.get(types[0]); boolean b = generic.equals(aClassGeneric); - boolean from = generic.getRawType().isAssignableFrom(aClass); + + + //boolean from = generic.getRawType().isAssignableFrom(aClass); + boolean from = false; + if (isWildcardType || isRawType(aClass)) { + from = generic.getRawType().isAssignableFrom(aClass); + } + return b || from; + } else if (type instanceof Class) { + return ((Class) type).isAssignableFrom(typeToCompare); + } + return false; + } + + /** + * Returns true if the type inspected is a raw type. Example: List, as opposed to a List<String> + * @todo I am NOT a generic expert, I came up with this code by watching the debugger, and reading javadocs. If + * there is a better way to determine if something is new List() vs new List<String> I'd love to see it. + * @param aClass + * @return + */ + public static boolean isRawType(Class<?> aClass) { + Class<?> typeToCompare = aClass; + Type[] types = typeToCompare.getGenericInterfaces(); + while (types.length == 0 && canGetSuperClass(typeToCompare)) { + typeToCompare = typeToCompare.getSuperclass(); + types = typeToCompare.getGenericInterfaces(); + } + + if (types.length == 0) { + return true; + } + + // + // List is a good example + // + Type typeToExamine = types[0]; + if (typeToExamine instanceof ParameterizedType) { + ParameterizedType pt = (ParameterizedType)typeToExamine; + Type arg = pt.getActualTypeArguments()[0]; + if (arg instanceof TypeVariable) { + TypeVariable tv = (TypeVariable)arg; + //best I can figure out, if the declaration has a "< such as List<String> + //then its not a raw type, if it doesn't, then its a raw type. -MR + if (!tv.getGenericDeclaration().toString().contains("<")) { + return true; } + } + + return false; } + + + return true; + } + /** * Checks for conditions where aClass.getSuperClass() would * return null. diff --git a/pico/container/src/java/org/picocontainer/injectors/Jsr330Injection.java b/pico/container/src/java/org/picocontainer/injectors/Jsr330Injection.java index 6674aa6..e5730d1 100644 --- a/pico/container/src/java/org/picocontainer/injectors/Jsr330Injection.java +++ b/pico/container/src/java/org/picocontainer/injectors/Jsr330Injection.java @@ -4,6 +4,7 @@ import org.picocontainer.ComponentMonitor; import org.picocontainer.Parameter; import org.picocontainer.containers.JSRPicoContainer; import org.picocontainer.parameters.ComponentParameter; +import org.picocontainer.parameters.JSR330ComponentParameter; import java.lang.annotation.Annotation; import java.lang.reflect.Constructor; @@ -46,7 +47,7 @@ public class Jsr330Injection extends ConstructorInjection { */ @Override protected Parameter getParameterToUse(Constructor<?> constructorToExamine, int constructorParameterIndex, Parameter parameter) { - if (parameter == ComponentParameter.DEFAULT) { + if (isDefaultParameter(parameter)) { //Search for Named class for (Annotation eachAnnotation : constructorToExamine.getParameterAnnotations()[constructorParameterIndex]) { @@ -68,6 +69,19 @@ public class Jsr330Injection extends ConstructorInjection { return super.getParameterToUse(constructorToExamine, constructorParameterIndex, parameter); } + private boolean isDefaultParameter(Parameter parameter) { + if (parameter == ComponentParameter.DEFAULT || parameter == JSR330ComponentParameter.DEFAULT) { + return true; + } + + if (parameter instanceof ComponentParameter) { + return !((ComponentParameter)parameter).isKeyDefined(); + + + } + return false; + } + diff --git a/pico/container/src/java/org/picocontainer/injectors/ProviderAdapter.java b/pico/container/src/java/org/picocontainer/injectors/ProviderAdapter.java index f6a6aaa..85697f0 100644 --- a/pico/container/src/java/org/picocontainer/injectors/ProviderAdapter.java +++ b/pico/container/src/java/org/picocontainer/injectors/ProviderAdapter.java @@ -149,7 +149,7 @@ public class ProviderAdapter implements org.picocontainer.Injector, Provider, Li } - public Class<?> getProviderImplmentation() { + public Class<?> getProviderImplementation() { return provider.getClass(); } @@ -231,6 +231,26 @@ public class ProviderAdapter implements org.picocontainer.Injector, Provider, Li return "ProviderAdapter"; } + + @Override + public String toString() { + StringBuilder result = new StringBuilder(); + + result.append(getClass().getName()); + result.append("@"); + result.append(System.identityHashCode(this)); + result.append(" (key = "); + result.append(getComponentKey()); + result.append(" ; implementation = "); + result.append(getProviderImplementation()); + result.append(" ; provided type = "); + result.append(providerReturnType); + result.append(" )"); + + return result.toString(); + } + + public void start(Object component) { lifecycle.start(component); } @@ -268,4 +288,5 @@ public class ProviderAdapter implements org.picocontainer.Injector, Provider, Li return null; } + } diff --git a/pico/container/src/java/org/picocontainer/parameters/BasicComponentParameter.java b/pico/container/src/java/org/picocontainer/parameters/BasicComponentParameter.java index 47cd81b..f10a04c 100644 --- a/pico/container/src/java/org/picocontainer/parameters/BasicComponentParameter.java +++ b/pico/container/src/java/org/picocontainer/parameters/BasicComponentParameter.java @@ -349,4 +349,9 @@ public class BasicComponentParameter extends AbstractParameter implements Parame found.remove(exclude); } + public boolean isKeyDefined() { + return key != null; + } + + } diff --git a/pico/container/src/java/org/picocontainer/parameters/JSR330ComponentParameter.java b/pico/container/src/java/org/picocontainer/parameters/JSR330ComponentParameter.java index e8167ad..5310ee3 100644 --- a/pico/container/src/java/org/picocontainer/parameters/JSR330ComponentParameter.java +++ b/pico/container/src/java/org/picocontainer/parameters/JSR330ComponentParameter.java @@ -24,6 +24,8 @@ import com.googlecode.jtype.Generic; @SuppressWarnings("serial") public class JSR330ComponentParameter extends ComponentParameter { + public static final JSR330ComponentParameter DEFAULT = new JSR330ComponentParameter(); + /** * @param key */ diff --git a/pico/container/src/test/org/picocontainer/JTypeHelperTestCase.java b/pico/container/src/test/org/picocontainer/JTypeHelperTestCase.java index 341e704..5e250a7 100644 --- a/pico/container/src/test/org/picocontainer/JTypeHelperTestCase.java +++ b/pico/container/src/test/org/picocontainer/JTypeHelperTestCase.java @@ -4,18 +4,24 @@ package org.picocontainer; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; +import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; +import java.util.ArrayList; import java.util.List; import javax.inject.Provider; -import org.junit.After; -import org.junit.Before; import org.junit.Test; +import org.junit.runners.Parameterized; import org.picocontainer.adapters.SimpleNamedBindingAnnotationTestCase.Apple; +import org.picocontainer.containers.JSRPicoContainerTestCase.A; +import org.picocontainer.containers.JSRPicoContainerTestCase.C; import org.picocontainer.containers.JSRPicoContainerTestCase.ThreeAProvider; import org.picocontainer.containers.JSRPicoContainerTestCase.ThreeCProvider; -import org.picocontainer.containers.JSRPicoContainerTestCase.C; +import org.picocontainer.defaults.issues.Issue0382TestCase.AcceptsParameterized; +import org.picocontainer.defaults.issues.Issue0382TestCase.AcceptsParameterizedWithWildcardList; +import org.picocontainer.defaults.issues.Issue0382TestCase.AcceptsParameterizedWithoutWildcardList; +import org.picocontainer.defaults.issues.Issue0382TestCase.StringParameterized; import com.googlecode.jtype.Generic; @@ -38,13 +44,48 @@ public class JTypeHelperTestCase { } + @SuppressWarnings({ "serial", "rawtypes" }) + public static class RawTest extends ArrayList { + + } + + @SuppressWarnings({ "serial", "rawtypes" }) + public static class RawTestTwo extends ArrayList<String> { + + } + + @SuppressWarnings("rawtypes") + public static class UntypedProvider implements Provider { + + public Object get() { + // TODO Auto-generated method stub + return null; + } + + } + + @Test + public void testIsRawType() { + assertTrue(JTypeHelper.isRawType(List.class)); + assertFalse(JTypeHelper.isRawType(ThreeAProvider.class)); + + assertTrue(JTypeHelper.isRawType(UntypedProvider.class)); + } @Test public void testIsAssignableWithACompoundTypeAndANormalClass() { - Generic<?> listType = Generic.get(List.class, String.class); - assertFalse(JTypeHelper.isAssignableFrom(listType, String.class)); - assertTrue(JTypeHelper.isAssignableFrom(listType, List.class)); + Generic<?> listType = Generic.get(List.class, String.class); //Testing: List<String> + + assertFalse(JTypeHelper.isAssignableFrom(listType, String.class)); //Testing: List<String> = String + + + + assertTrue(JTypeHelper.isAssignableFrom(listType, List.class)); //List<String> = List + + //Proof that the above type should pass. + @SuppressWarnings({ "rawtypes", "unchecked" }) + List<String> test = new ArrayList(); } @@ -72,20 +113,65 @@ public class JTypeHelperTestCase { public void doSomething(Provider<C> threeCProvider) { } + + public void doSomethingElse(Provider<A> threeAProvider) { + + } } + @SuppressWarnings("rawtypes") @Test public void testCombinationOfTypeFromOtherTests() throws NoSuchMethodException, SecurityException { Generic<ThreeAProvider> aProvider = Generic.get(ThreeAProvider.class); + Generic<ThreeCProvider> cProvider = Generic.get(ThreeCProvider.class); + + //Test reflection. + assertTrue(JTypeHelper.isAssignableFrom(aProvider, ThreeAProvider.class)); + assertTrue(JTypeHelper.isAssignableFrom(cProvider, ThreeCProvider.class)); + //Test as long as we're dealing standalone types. assertFalse(JTypeHelper.isAssignableFrom(aProvider, ThreeCProvider.class)); + assertFalse(JTypeHelper.isAssignableFrom(cProvider, ThreeAProvider.class)); - Class<?> paramType = TestArg.class.getMethod("doSomething", Provider.class).getParameterTypes()[0]; + //Check if we get the type from a parameter. + Type paramType = TestArg.class.getMethod("doSomething", Provider.class).getGenericParameterTypes()[0]; + + assertTrue(paramType instanceof ParameterizedType); Generic<?> argType = Generic.get(paramType); + Type paramTypeTwo = TestArg.class.getMethod("doSomethingElse", Provider.class).getGenericParameterTypes()[0]; + Generic<?> argTypeTwo = Generic.get(paramTypeTwo); + + //Check reflection + assertTrue(JTypeHelper.isAssignableFrom(argTypeTwo, ThreeAProvider.class)); + assertTrue(JTypeHelper.isAssignableFrom(argType,ThreeCProvider.class)); + + //Test opposite assertFalse(JTypeHelper.isAssignableFrom(argType, ThreeAProvider.class)); + assertFalse(JTypeHelper.isAssignableFrom(argTypeTwo, ThreeCProvider.class)); + + //Test raw type CAN be assigned + //Below is compiler proof that this test should pass. + assertTrue(JTypeHelper.isAssignableFrom(argTypeTwo, Provider.class)); + @SuppressWarnings("unchecked") + Provider<C> c = new Provider() { + + public Object get() { + // TODO Auto-generated method stub + return null; + } + + }; } + @Test + public void testWildcardAssignment() { + Type parameterType = AcceptsParameterized.class.getConstructors()[0].getGenericParameterTypes()[0]; + Generic<?> generic = Generic.get(parameterType); + + assertTrue(JTypeHelper.isAssignableFrom(generic, StringParameterized.class)); + } + } diff --git a/pico/jsr330-tck/src/test/PicoContainerJsr330TestCase.java b/pico/jsr330-tck/src/test/PicoContainerJsr330TestCase.java index a4da7d0..7422344 100644 --- a/pico/jsr330-tck/src/test/PicoContainerJsr330TestCase.java +++ b/pico/jsr330-tck/src/test/PicoContainerJsr330TestCase.java @@ -28,6 +28,7 @@ import org.picocontainer.injectors.AdaptingInjection; import org.picocontainer.injectors.AnnotatedMethodInjection; import org.picocontainer.injectors.Jsr330Injection; import org.picocontainer.monitors.NullComponentMonitor; +import org.picocontainer.parameters.JSR330ComponentParameter; public class PicoContainerJsr330TestCase extends TestCase { @@ -50,12 +51,28 @@ public class PicoContainerJsr330TestCase extends TestCase { public Seat get() { try { - return pico.getComponent(DriversSeat.class); + return (Seat)pico.getComponent("theDriversSeat"); } catch (Exception e) { throw new RuntimeException(e); } } + } + + public static class PlainSeatProvider implements Provider<Seat> { + private MutablePicoContainer pico; + + public PlainSeatProvider(MutablePicoContainer pico) { + this.pico = pico; + } + + public Seat get() { + try { + return (Seat)pico.getComponent("theDriversSeat"); + } catch (Exception e) { + throw new RuntimeException(e); + } + } } public static class PlainTireProvider implements Provider<Tire> { @@ -66,7 +83,7 @@ public class PicoContainerJsr330TestCase extends TestCase { } public Tire get() { - return pico.getComponent(Tire.class); + return (Tire) pico.getComponent("plainTire"); } } @@ -82,7 +99,7 @@ public class PicoContainerJsr330TestCase extends TestCase { public Tire get() { try { - return (Tire)pico.getComponent("spare"); + return (Tire)pico.getComponent("spareTire"); } catch (Exception e) { throw new RuntimeException(e); } @@ -98,20 +115,36 @@ public class PicoContainerJsr330TestCase extends TestCase { DriverSeatProvider driversSeatProvider = new DriverSeatProvider(pico); PlainTireProvider plainTireProvider = new PlainTireProvider(pico); SpareTireProvider spareTireProvider = new SpareTireProvider(pico); - - - pico.addComponent(Car.class, Convertible.class) + PlainSeatProvider plainSeatProvider = new PlainSeatProvider(pico); + + pico.addComponent(Car.class, Convertible.class, + new JSR330ComponentParameter(), + new JSR330ComponentParameter(), //Auto wire all 8 arguments. + new JSR330ComponentParameter(), + new JSR330ComponentParameter(), + new JSR330ComponentParameter(), + new JSR330ComponentParameter(), + new JSR330ComponentParameter(), + new JSR330ComponentParameter() + ) //.addAdapter(new AnnotatedMethodInjection.AnnotatedMethodInjector(DriversSeat.class, DriversSeat.class, Parameter.DEFAULT, new NullComponentMonitor(), false, Drivers.class)) - .addComponent(Seat.class) - .addComponent(DriversSeat.class) - .addComponent("spare", SpareTire.class) - .addComponent(Tire.class) .addComponent(FuelTank.class) .addComponent(Engine.class, V8Engine.class) .addComponent(Seatbelt.class) + .addComponent(Cupholder.class) .addProvider(driversSeatProvider) + .addProvider(plainSeatProvider) .addProvider(plainTireProvider) - .addProvider(spareTireProvider); + .addProvider(spareTireProvider) + + //Components Used By the providers + .addComponent("plainSeat", Seat.class) + .addComponent("theDriversSeat", DriversSeat.class) + .addComponent("spareTire", SpareTire.class) + .addComponent("plainTire", Tire.class) + + ; + Car car = pico.getComponent(Car.class);