[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);