Repository: tapestry-5
Updated Branches:
  refs/heads/master b452c6fcd -> 737ebd64c


TAP5-1213 - Access generic type for bound parameters


Project: http://git-wip-us.apache.org/repos/asf/tapestry-5/repo
Commit: http://git-wip-us.apache.org/repos/asf/tapestry-5/commit/daa27902
Tree: http://git-wip-us.apache.org/repos/asf/tapestry-5/tree/daa27902
Diff: http://git-wip-us.apache.org/repos/asf/tapestry-5/diff/daa27902

Branch: refs/heads/master
Commit: daa279024e6b66c6c6b2c77e279f42fee50c8dc5
Parents: b452c6f
Author: uklance <[email protected]>
Authored: Sat May 17 18:48:27 2014 +0100
Committer: uklance <[email protected]>
Committed: Mon May 19 21:34:49 2014 +0100

----------------------------------------------------------------------
 .../main/java/org/apache/tapestry5/Binding.java |  9 ++++
 .../apache/tapestry5/ComponentResources.java    | 13 +++++
 .../org/apache/tapestry5/PropertyConduit.java   | 13 +++++
 .../internal/TapestryInternalUtils.java         |  6 +++
 .../internal/bindings/AbstractBinding.java      | 10 ++++
 .../internal/bindings/PropBinding.java          | 11 ++++
 .../CoercingPropertyConduitWrapper.java         |  6 +++
 .../services/LiteralPropertyConduit.java        |  6 +++
 .../services/PropertyConduitSourceImpl.java     | 57 ++++++++++++++++++--
 .../InternalComponentResourcesImpl.java         |  9 ++++
 .../integration/app1/CoreBehaviorsTests.java    | 16 ++++++
 .../app1/components/GenericTypeDisplay.java     | 45 ++++++++++++++++
 .../integration/app1/pages/EmptyGrid.java       |  6 +++
 .../integration/app1/pages/GenericTypeDemo.java | 33 ++++++++++++
 .../tapestry5/integration/app1/pages/Index.java |  4 +-
 .../services/PropertyConduitSourceImplTest.java | 27 ++++++++++
 .../integration/app1/pages/GenericTypeDemo.tml  | 12 +++++
 17 files changed, 278 insertions(+), 5 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/daa27902/tapestry-core/src/main/java/org/apache/tapestry5/Binding.java
----------------------------------------------------------------------
diff --git a/tapestry-core/src/main/java/org/apache/tapestry5/Binding.java 
b/tapestry-core/src/main/java/org/apache/tapestry5/Binding.java
index 8a99914..711c748 100644
--- a/tapestry-core/src/main/java/org/apache/tapestry5/Binding.java
+++ b/tapestry-core/src/main/java/org/apache/tapestry5/Binding.java
@@ -14,6 +14,8 @@
 
 package org.apache.tapestry5;
 
+import java.lang.reflect.Type;
+
 import org.apache.tapestry5.ioc.AnnotationProvider;
 
 /**
@@ -53,4 +55,11 @@ public interface Binding extends AnnotationProvider
      * bound.
      */
     Class getBindingType();
+    
+    /**
+     * Returns the generic type of the binding, either the generic type of 
resource exposed by the binding, or the 
+     * generic type of the property bound.
+     * @since 5.4
+     */
+    Type getBindingGenericType();
 }

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/daa27902/tapestry-core/src/main/java/org/apache/tapestry5/ComponentResources.java
----------------------------------------------------------------------
diff --git 
a/tapestry-core/src/main/java/org/apache/tapestry5/ComponentResources.java 
b/tapestry-core/src/main/java/org/apache/tapestry5/ComponentResources.java
index 137c43d..5c51903 100644
--- a/tapestry-core/src/main/java/org/apache/tapestry5/ComponentResources.java
+++ b/tapestry-core/src/main/java/org/apache/tapestry5/ComponentResources.java
@@ -23,6 +23,7 @@ import org.apache.tapestry5.runtime.PageLifecycleCallbackHub;
 import org.apache.tapestry5.runtime.PageLifecycleListener;
 
 import java.lang.annotation.Annotation;
+import java.lang.reflect.Type;
 import java.util.List;
 
 /**
@@ -128,6 +129,18 @@ public interface ComponentResources extends 
ComponentResourcesCommon
      * @see Binding#getBindingType()
      */
     Class getBoundType(String parameterName);
+    
+    
+    /**
+     * Returns the generic type of the bound parameter, or null if the 
parameter is not bound. This is useful
+     * for when the parameter is bound to a generic property (eg 
java.util.List) to determine the element type.
+     * 
+     * @param parameterName
+     *        used to select the parameter (case is ignored)
+     * @return the generic type of the bound parameter, or null if the 
parameter is not bound
+     * @see Binding#getBindingGenericType()
+     */
+    Type getBoundGenericType(String parameterName);
 
     /**
      * Returns an annotation provider, used to obtain annotations related to 
the parameter.

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/daa27902/tapestry-core/src/main/java/org/apache/tapestry5/PropertyConduit.java
----------------------------------------------------------------------
diff --git 
a/tapestry-core/src/main/java/org/apache/tapestry5/PropertyConduit.java 
b/tapestry-core/src/main/java/org/apache/tapestry5/PropertyConduit.java
index 3dbb0c0..b28293a 100644
--- a/tapestry-core/src/main/java/org/apache/tapestry5/PropertyConduit.java
+++ b/tapestry-core/src/main/java/org/apache/tapestry5/PropertyConduit.java
@@ -14,6 +14,8 @@
 
 package org.apache.tapestry5;
 
+import java.lang.reflect.Type;
+
 import org.apache.tapestry5.ioc.AnnotationProvider;
 
 /**
@@ -42,4 +44,15 @@ public interface PropertyConduit extends AnnotationProvider
      * Returns the type of the property read or updated by the conduit.
      */
     Class getPropertyType();
+    
+    /**
+     * Returns a Type object that represents the declared type for the 
property.
+     * If the Type is a parameterized type, the Type object returned must 
accurately 
+     * reflect the actual type parameters used in the source code. If the type 
of the
+     * underlying property is a type variable or a parameterized type, it is 
created.
+     * Otherwise, it is resolved.
+     * 
+     * @since 5.4
+     */
+    Type getPropertyGenericType();
 }

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/daa27902/tapestry-core/src/main/java/org/apache/tapestry5/internal/TapestryInternalUtils.java
----------------------------------------------------------------------
diff --git 
a/tapestry-core/src/main/java/org/apache/tapestry5/internal/TapestryInternalUtils.java
 
b/tapestry-core/src/main/java/org/apache/tapestry5/internal/TapestryInternalUtils.java
index 7c6e8b0..45da70b 100644
--- 
a/tapestry-core/src/main/java/org/apache/tapestry5/internal/TapestryInternalUtils.java
+++ 
b/tapestry-core/src/main/java/org/apache/tapestry5/internal/TapestryInternalUtils.java
@@ -32,6 +32,7 @@ import java.io.InputStream;
 import java.io.OutputStream;
 import java.lang.annotation.Annotation;
 import java.lang.ref.Reference;
+import java.lang.reflect.Type;
 import java.util.List;
 import java.util.Map;
 import java.util.regex.Pattern;
@@ -491,6 +492,11 @@ public class TapestryInternalUtils
                 return conduit.getPropertyType();
             }
 
+            public Type getPropertyGenericType()
+            {
+                return conduit.getPropertyGenericType();
+            }
+            
             public Object get(Object instance)
             {
                 return conduit.get(instance);

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/daa27902/tapestry-core/src/main/java/org/apache/tapestry5/internal/bindings/AbstractBinding.java
----------------------------------------------------------------------
diff --git 
a/tapestry-core/src/main/java/org/apache/tapestry5/internal/bindings/AbstractBinding.java
 
b/tapestry-core/src/main/java/org/apache/tapestry5/internal/bindings/AbstractBinding.java
index a2478b5..12885e1 100644
--- 
a/tapestry-core/src/main/java/org/apache/tapestry5/internal/bindings/AbstractBinding.java
+++ 
b/tapestry-core/src/main/java/org/apache/tapestry5/internal/bindings/AbstractBinding.java
@@ -20,6 +20,7 @@ import org.apache.tapestry5.ioc.Location;
 import org.apache.tapestry5.ioc.internal.util.TapestryException;
 
 import java.lang.annotation.Annotation;
+import java.lang.reflect.Type;
 
 /**
  * Abstract base class for bindings. Assumes that the binding is read only and 
invariant. Subclasses must provide an
@@ -62,6 +63,15 @@ public abstract class AbstractBinding extends BaseLocatable 
implements Binding
     {
         return get().getClass();
     }
+    
+    /**
+     * Passes straight through to {@link AbstractBinding#getBindingType()}. 
Subclasses may override this method to
+     * return the generic type if it is available
+     */
+    public Type getBindingGenericType()
+    {
+       return getBindingType();
+    }
 
     /**
      * Always returns null. Bindings that provide access to a method or field 
will override this method to return the

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/daa27902/tapestry-core/src/main/java/org/apache/tapestry5/internal/bindings/PropBinding.java
----------------------------------------------------------------------
diff --git 
a/tapestry-core/src/main/java/org/apache/tapestry5/internal/bindings/PropBinding.java
 
b/tapestry-core/src/main/java/org/apache/tapestry5/internal/bindings/PropBinding.java
index e7802bc..55ea9d8 100644
--- 
a/tapestry-core/src/main/java/org/apache/tapestry5/internal/bindings/PropBinding.java
+++ 
b/tapestry-core/src/main/java/org/apache/tapestry5/internal/bindings/PropBinding.java
@@ -21,6 +21,7 @@ import org.apache.tapestry5.ioc.Location;
 import org.apache.tapestry5.ioc.internal.util.TapestryException;
 
 import java.lang.annotation.Annotation;
+import java.lang.reflect.Type;
 
 /**
  * Base class for bindings created by the {@link 
org.apache.tapestry5.internal.bindings.PropBindingFactory}. A subclass
@@ -100,6 +101,16 @@ public class PropBinding extends AbstractBinding 
implements InternalPropBinding
     {
         return conduit.getPropertyType();
     }
+    
+    /**
+     * Get the generic type from the underlying field or getter.
+     * @see PropertyConduit#getPropertyGenericType()
+     */
+    @Override
+    public Type getBindingGenericType()
+    {
+       return conduit.getPropertyGenericType();
+    }
 
     @Override
     public <T extends Annotation> T getAnnotation(Class<T> annotationClass)

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/daa27902/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/CoercingPropertyConduitWrapper.java
----------------------------------------------------------------------
diff --git 
a/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/CoercingPropertyConduitWrapper.java
 
b/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/CoercingPropertyConduitWrapper.java
index e1135bf..158d4a4 100644
--- 
a/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/CoercingPropertyConduitWrapper.java
+++ 
b/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/CoercingPropertyConduitWrapper.java
@@ -18,6 +18,7 @@ import org.apache.tapestry5.PropertyConduit;
 import org.apache.tapestry5.ioc.services.TypeCoercer;
 
 import java.lang.annotation.Annotation;
+import java.lang.reflect.Type;
 
 public class CoercingPropertyConduitWrapper implements PropertyConduit
 {
@@ -45,6 +46,11 @@ public class CoercingPropertyConduitWrapper implements 
PropertyConduit
     {
         return conduit.getPropertyType();
     }
+    
+    public Type getPropertyGenericType()
+    {
+       return conduit.getPropertyGenericType();
+    }
 
     @SuppressWarnings("unchecked")
     public void set(Object instance, Object value)

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/daa27902/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/LiteralPropertyConduit.java
----------------------------------------------------------------------
diff --git 
a/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/LiteralPropertyConduit.java
 
b/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/LiteralPropertyConduit.java
index bc2eb12..1fffd4f 100644
--- 
a/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/LiteralPropertyConduit.java
+++ 
b/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/LiteralPropertyConduit.java
@@ -15,6 +15,7 @@
 package org.apache.tapestry5.internal.services;
 
 import java.lang.annotation.Annotation;
+import java.lang.reflect.Type;
 
 import org.apache.tapestry5.internal.InternalPropertyConduit;
 import org.apache.tapestry5.ioc.AnnotationProvider;
@@ -59,6 +60,11 @@ public class LiteralPropertyConduit extends 
PropertyConduitDelegate implements I
     {
         return propertyType;
     }
+    
+    public Type getPropertyGenericType()
+    {
+       return propertyType;
+    }
 
     public <T extends Annotation> T getAnnotation(Class<T> annotationClass)
     {

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/daa27902/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/PropertyConduitSourceImpl.java
----------------------------------------------------------------------
diff --git 
a/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/PropertyConduitSourceImpl.java
 
b/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/PropertyConduitSourceImpl.java
index 5f41fb1..6dab120 100644
--- 
a/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/PropertyConduitSourceImpl.java
+++ 
b/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/PropertyConduitSourceImpl.java
@@ -63,9 +63,12 @@ public class PropertyConduitSourceImpl implements 
PropertyConduitSource
         private static final MethodDescription GET_PROPERTY_TYPE = 
getMethodDescription(PropertyConduit.class,
                 "getPropertyType");
 
+        private static final MethodDescription GET_PROPERTY_GENERIC_TYPE = 
getMethodDescription(PropertyConduit.class,
+                "getPropertyGenericType");
+        
         private static final MethodDescription GET_PROPERTY_NAME = 
getMethodDescription(InternalPropertyConduit.class,
                 "getPropertyName");
-
+        
         private static final MethodDescription GET_ANNOTATION = 
getMethodDescription(AnnotationProvider.class,
                 "getAnnotation", Class.class);
 
@@ -245,6 +248,8 @@ public class PropertyConduitSourceImpl implements 
PropertyConduitSource
 
         private Class conduitPropertyType;
 
+        private Type conduitPropertyGenericType;
+
         private String conduitPropertyName;
 
         private AnnotationProvider annotationProvider = nullAnnotationProvider;
@@ -302,6 +307,16 @@ public class PropertyConduitSourceImpl implements 
PropertyConduitSource
                 }
             });
 
+            final PlasticField propertyGenericTypeField = 
plasticClass.introduceField(Type.class, "propertyGenericType").inject(
+                    conduitPropertyGenericType);
+
+            
plasticClass.introduceMethod(ConduitMethods.GET_PROPERTY_GENERIC_TYPE, new 
InstructionBuilderCallback()
+            {
+                public void doBuild(InstructionBuilder builder)
+                {
+                    
builder.loadThis().getField(propertyGenericTypeField).returnResult();
+                }
+            });
         }
 
         /**
@@ -424,6 +439,7 @@ public class PropertyConduitSourceImpl implements 
PropertyConduitSource
                     implementNoOpSetter();
 
                     conduitPropertyType = IntegerRange.class;
+                    conduitPropertyGenericType = IntegerRange.class;
 
                     return;
 
@@ -433,7 +449,8 @@ public class PropertyConduitSourceImpl implements 
PropertyConduitSource
                     implementNoOpSetter();
 
                     conduitPropertyType = List.class;
-
+                    conduitPropertyGenericType = List.class;
+                    
                     return;
 
                 case MAP:
@@ -441,6 +458,7 @@ public class PropertyConduitSourceImpl implements 
PropertyConduitSource
                     implementNoOpSetter();
 
                     conduitPropertyType = Map.class;
+                    conduitPropertyGenericType = Map.class;
 
                     return;
 
@@ -450,6 +468,7 @@ public class PropertyConduitSourceImpl implements 
PropertyConduitSource
                     implementNoOpSetter();
 
                     conduitPropertyType = boolean.class;
+                    conduitPropertyGenericType = boolean.class;
 
                     return;
 
@@ -466,6 +485,7 @@ public class PropertyConduitSourceImpl implements 
PropertyConduitSource
 
             conduitPropertyName = term.description;
             conduitPropertyType = term.genericType;
+            conduitPropertyGenericType = term.genericType;
             annotationProvider = term.annotationProvider;
 
             plasticClass.introduceMethod(ConduitMethods.GET, new 
InstructionBuilderCallback()
@@ -491,15 +511,39 @@ public class PropertyConduitSourceImpl implements 
PropertyConduitSource
 
             PropertyAdapter adapter = findPropertyAdapter(activeType, 
propertyName);
 
-            conduitPropertyType = adapter.getType();
             conduitPropertyName = propertyName;
+            conduitPropertyType = adapter.getType();
+            conduitPropertyGenericType = getGenericType(adapter);
             annotationProvider = adapter;
 
             implementGetter(adapter);
             implementSetter(adapter);
         }
 
-        private void implementSetter(PropertyAdapter adapter)
+        private Type getGenericType(PropertyAdapter adapter)
+        {
+               Type genericType = null;
+               if (adapter.getField() != null)
+               {
+                       genericType = adapter.getField().getGenericType();
+               }
+               else if (adapter.getReadMethod() != null)
+               {
+                       genericType = 
adapter.getReadMethod().getGenericReturnType(); 
+               }
+               else if (adapter.getWriteMethod() != null)
+               {
+                       genericType = 
adapter.getWriteMethod().getGenericParameterTypes()[0];
+               }
+               else
+               {
+                       throw new RuntimeException("Could not find accessor for 
property " + adapter.getName());
+               }
+               
+               return genericType == null ? adapter.getType() : genericType;
+               }
+
+               private void implementSetter(PropertyAdapter adapter)
         {
             if (adapter.getWriteMethod() != null)
             {
@@ -1439,6 +1483,11 @@ public class PropertyConduitSourceImpl implements 
PropertyConduitSource
             {
                 return rootClass;
             }
+            
+            public Type getPropertyGenericType()
+            {
+               return rootClass;
+            }
 
             public <T extends Annotation> T getAnnotation(Class<T> 
annotationClass)
             {

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/daa27902/tapestry-core/src/main/java/org/apache/tapestry5/internal/structure/InternalComponentResourcesImpl.java
----------------------------------------------------------------------
diff --git 
a/tapestry-core/src/main/java/org/apache/tapestry5/internal/structure/InternalComponentResourcesImpl.java
 
b/tapestry-core/src/main/java/org/apache/tapestry5/internal/structure/InternalComponentResourcesImpl.java
index 39defda..5e0e440 100644
--- 
a/tapestry-core/src/main/java/org/apache/tapestry5/internal/structure/InternalComponentResourcesImpl.java
+++ 
b/tapestry-core/src/main/java/org/apache/tapestry5/internal/structure/InternalComponentResourcesImpl.java
@@ -41,6 +41,7 @@ import 
org.apache.tapestry5.services.pageload.ComponentResourceSelector;
 import org.slf4j.Logger;
 
 import java.lang.annotation.Annotation;
+import java.lang.reflect.Type;
 import java.util.List;
 import java.util.Locale;
 import java.util.Map;
@@ -344,6 +345,14 @@ public class InternalComponentResourcesImpl extends 
LockSupport implements Inter
 
         return binding == null ? null : binding.getBindingType();
     }
+    
+    public Type getBoundGenericType(String parameterName)
+    {
+        Binding binding = getBinding(parameterName);
+
+        return binding == null ? null : binding.getBindingGenericType();
+    }
+    
 
     public Binding getBinding(String parameterName)
     {

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/daa27902/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/CoreBehaviorsTests.java
----------------------------------------------------------------------
diff --git 
a/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/CoreBehaviorsTests.java
 
b/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/CoreBehaviorsTests.java
index 2179b98..afdc002 100644
--- 
a/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/CoreBehaviorsTests.java
+++ 
b/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/CoreBehaviorsTests.java
@@ -1720,4 +1720,20 @@ public class CoreBehaviorsTests extends App1TestCase
         
         assertTextPresent("This page throws an exception");
     }
+    
+    @Test
+    public void bound_generic_types()
+    {
+       openLinks("Generic bound type demo");
+       
+               
assertTextPresent("clientId=one,type=java.util.Map,genericType=java.util.Map<java.lang.String,
 java.lang.String>");
+               
assertTextPresent("clientId=two,type=java.lang.String,genericType=class 
java.lang.String");
+               
assertTextPresent("clientId=three,type=java.util.Set,genericType=java.util.Set<java.lang.Long>");
+               
assertTextPresent("clientId=four,type=java.util.List,genericType=java.util.List<java.util.List<java.util.Date>>");
+               
assertTextPresent("clientId=five,type=java.util.List,genericType=interface 
java.util.List");
+               
assertTextPresent("clientId=six,type=java.util.Date,genericType=class 
java.util.Date");
+               
assertTextPresent("clientId=seven,type=java.util.List,genericType=interface 
java.util.List");
+               
assertTextPresent("clientId=eight,type=java.util.Map,genericType=interface 
java.util.Map");
+               
assertTextPresent("clientId=nine,type=java.lang.String,genericType=class 
java.lang.String");
+    }
 }

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/daa27902/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/components/GenericTypeDisplay.java
----------------------------------------------------------------------
diff --git 
a/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/components/GenericTypeDisplay.java
 
b/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/components/GenericTypeDisplay.java
new file mode 100644
index 0000000..ab95807
--- /dev/null
+++ 
b/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/components/GenericTypeDisplay.java
@@ -0,0 +1,45 @@
+// Copyright 2006-2014 The Apache Software Foundation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+package org.apache.tapestry5.integration.app1.components;
+
+import java.lang.reflect.Type;
+
+import org.apache.tapestry5.BindingConstants;
+import org.apache.tapestry5.ComponentResources;
+import org.apache.tapestry5.MarkupWriter;
+import org.apache.tapestry5.annotations.Parameter;
+import org.apache.tapestry5.ioc.annotations.Inject;
+
+/**
+ * Outputs the type and genericType of the 'value' parameter in a div
+ */
+public class GenericTypeDisplay {
+       @Inject
+       private ComponentResources resources;
+       
+       @Parameter(required=true, defaultPrefix=BindingConstants.LITERAL)
+       private String clientId;
+       
+       @Parameter(required=true)
+       private Object value;
+       
+       void afterRender(MarkupWriter writer) {
+               writer.element("div", "id", clientId);
+               Class<?> type = resources.getBoundType("value");
+               Type genericType = resources.getBoundGenericType("value");
+               String text = 
String.format("clientId=%s,type=%s,genericType=%s", clientId, type.getName(), 
genericType.toString());
+               writer.write(text);
+               writer.end();
+       }
+}

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/daa27902/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/pages/EmptyGrid.java
----------------------------------------------------------------------
diff --git 
a/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/pages/EmptyGrid.java
 
b/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/pages/EmptyGrid.java
index 522fa6a..a89c039 100644
--- 
a/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/pages/EmptyGrid.java
+++ 
b/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/pages/EmptyGrid.java
@@ -15,6 +15,7 @@
 package org.apache.tapestry5.integration.app1.pages;
 
 import java.lang.annotation.Annotation;
+import java.lang.reflect.Type;
 import java.util.Collections;
 import java.util.List;
 import java.util.Random;
@@ -66,6 +67,11 @@ public class EmptyGrid
       {
         return Long.class;
       }
+
+      public Type getPropertyGenericType()
+      {
+        return Long.class;
+      }
       
       public Object get(Object instance)
       {

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/daa27902/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/pages/GenericTypeDemo.java
----------------------------------------------------------------------
diff --git 
a/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/pages/GenericTypeDemo.java
 
b/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/pages/GenericTypeDemo.java
new file mode 100644
index 0000000..80bf40a
--- /dev/null
+++ 
b/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/pages/GenericTypeDemo.java
@@ -0,0 +1,33 @@
+package org.apache.tapestry5.integration.app1.pages;
+
+import java.util.Arrays;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.tapestry5.annotations.Property;
+import org.apache.tapestry5.annotations.SetupRender;
+
+public class GenericTypeDemo {
+       private Set<Long> setOfLongs;
+       
+       @Property
+       private Map<String, String> mapOfStrings;
+       
+       public List<List<Date>> getListOfListOfDates() {
+               List<Date> dates = Arrays.asList(new Date(Long.MIN_VALUE), new 
Date(0), new Date(Long.MAX_VALUE));
+               return Arrays.asList(dates);
+       }
+       
+       public void setSetOfLongs(Set<Long> setOfLongs) {
+               this.setOfLongs = setOfLongs;
+       }
+       
+       @SetupRender
+       void setupRender() {
+               mapOfStrings = new HashMap<String,String>();
+               mapOfStrings.put("foo", "bar");
+       }
+}

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/daa27902/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/pages/Index.java
----------------------------------------------------------------------
diff --git 
a/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/pages/Index.java
 
b/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/pages/Index.java
index 009a97d..99f2a16 100644
--- 
a/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/pages/Index.java
+++ 
b/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/pages/Index.java
@@ -566,7 +566,9 @@ public class Index
 
                     new Item("nested/PageThatThrowsException", "Reload on 
nested page", "Tests a page reload from a nested page's exception report"),
                     
-                    new Item("inplacegridinloopdemo", "In-Place Grid in a Loop 
Demo", "In-place grid in a loop")
+                    new Item("inplacegridinloopdemo", "In-Place Grid in a Loop 
Demo", "In-place grid in a loop"),
+                    
+                    new Item("GenericTypeDemo", "Generic bound type demo", 
"Tests that generic type info is available for generic bindings")
 
             );
 

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/daa27902/tapestry-core/src/test/java/org/apache/tapestry5/internal/services/PropertyConduitSourceImplTest.java
----------------------------------------------------------------------
diff --git 
a/tapestry-core/src/test/java/org/apache/tapestry5/internal/services/PropertyConduitSourceImplTest.java
 
b/tapestry-core/src/test/java/org/apache/tapestry5/internal/services/PropertyConduitSourceImplTest.java
index 1f550cd..5c9f88a 100644
--- 
a/tapestry-core/src/test/java/org/apache/tapestry5/internal/services/PropertyConduitSourceImplTest.java
+++ 
b/tapestry-core/src/test/java/org/apache/tapestry5/internal/services/PropertyConduitSourceImplTest.java
@@ -29,6 +29,8 @@ import org.testng.annotations.AfterClass;
 import org.testng.annotations.BeforeClass;
 import org.testng.annotations.Test;
 
+import java.util.Collections;
+import java.util.Date;
 import java.util.List;
 import java.util.Map;
 
@@ -161,6 +163,31 @@ public class PropertyConduitSourceImplTest extends 
InternalBaseTestCase
         smart.set(bean, "Howard");
     }
 
+    static class GenericBean {
+       public List<Date> dates;
+       public List<GenericBean> genericBeans;
+       
+       public List<Long> getLongs() {
+               return Collections.emptyList();
+       }
+       
+       public void setMap(Map<String, Integer> map) {
+       }
+    }
+    
+    @Test
+    public void generic_types_are_determined()
+    {
+        PropertyConduit datesConduit = source.create(GenericBean.class, 
"dates");
+        PropertyConduit longsConduit = source.create(GenericBean.class, 
"longs");
+        PropertyConduit nestedDatesConduit = source.create(GenericBean.class, 
"genericBeans.get(0).dates");
+        PropertyConduit mapConduit = source.create(GenericBean.class, "map");
+        assertEquals(datesConduit.getPropertyGenericType().toString(), 
"java.util.List<java.util.Date>");
+        assertEquals(longsConduit.getPropertyGenericType().toString(), 
"java.util.List<java.lang.Long>");
+        assertEquals(nestedDatesConduit.getPropertyGenericType().toString(), 
"java.util.List<java.util.Date>");
+        assertEquals(mapConduit.getPropertyGenericType().toString(), 
"java.util.Map<java.lang.String, java.lang.Integer>");
+    }
+    
     @Test
     public void method_names_are_matched_caselessly()
     {

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/daa27902/tapestry-core/src/test/resources/org/apache/tapestry5/integration/app1/pages/GenericTypeDemo.tml
----------------------------------------------------------------------
diff --git 
a/tapestry-core/src/test/resources/org/apache/tapestry5/integration/app1/pages/GenericTypeDemo.tml
 
b/tapestry-core/src/test/resources/org/apache/tapestry5/integration/app1/pages/GenericTypeDemo.tml
new file mode 100644
index 0000000..c9f4b78
--- /dev/null
+++ 
b/tapestry-core/src/test/resources/org/apache/tapestry5/integration/app1/pages/GenericTypeDemo.tml
@@ -0,0 +1,12 @@
+<html t:type="Border" 
xmlns:t="http://tapestry.apache.org/schema/tapestry_5_0_0.xsd";>
+       <h1>Generic Type Tests</h1>
+       <t:genericTypeDisplay clientId="one" value="mapOfStrings" />
+       <t:genericTypeDisplay clientId="two" value="mapOfStrings.get('foo')" />
+       <t:genericTypeDisplay clientId="three" value="setOfLongs" />
+       <t:genericTypeDisplay clientId="four" value="listOfListOfDates" />
+       <t:genericTypeDisplay clientId="five" value="listOfListOfDates.get(0)" 
/>
+       <t:genericTypeDisplay clientId="six" 
value="listOfListOfDates.get(0).get(0)" />
+       <t:genericTypeDisplay clientId="seven" value="[1,2,3]" />
+       <t:genericTypeDisplay clientId="eight" value="{'foo':'bar'}" />
+       <t:genericTypeDisplay clientId="nine" value="literal:aaa" />
+</html>

Reply via email to