You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@tapestry.apache.org by hl...@apache.org on 2006/09/24 00:46:10 UTC

svn commit: r449321 - in /tapestry/tapestry5/tapestry-core/trunk/src: main/java/org/apache/tapestry/ main/java/org/apache/tapestry/internal/bindings/ main/java/org/apache/tapestry/internal/structure/ main/resources/org/apache/tapestry/internal/structur...

Author: hlship
Date: Sat Sep 23 15:46:09 2006
New Revision: 449321

URL: http://svn.apache.org/viewvc?view=rev&rev=449321
Log:
Perform type coercion when updating parameters, coercing the value provided to Binding.set().

Modified:
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/Binding.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/bindings/BasePropBinding.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/bindings/LiteralBinding.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/bindings/PropBindingFactory.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/structure/ComponentPageElementImpl.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/structure/StructureMessages.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/resources/org/apache/tapestry/internal/structure/StructureStrings.properties
    tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/bindings/BindingFactoryTest.java
    tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/bindings/PropBindingFactoryTest.java
    tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/structure/ComponentPageElementImplTest.java

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/Binding.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/Binding.java?view=diff&rev=449321&r1=449320&r2=449321
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/Binding.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/Binding.java Sat Sep 23 15:46:09 2006
@@ -46,4 +46,11 @@
      * such values aggressively.
      */
     boolean isInvariant();
+
+    /**
+     * Returns the type of the binding, either the type of resource exposed by the binding, or the
+     * type of the property bound.
+     */
+
+    Class getBindingType();
 }

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/bindings/BasePropBinding.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/bindings/BasePropBinding.java?view=diff&rev=449321&r1=449320&r2=449321
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/bindings/BasePropBinding.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/bindings/BasePropBinding.java Sat Sep 23 15:46:09 2006
@@ -30,10 +30,13 @@
 {
     private final String _toString;
 
-    public BasePropBinding(String toString, Location location)
+    private final Class _bindingType;
+
+    public BasePropBinding(Class bindingType, String toString, Location location)
     {
         super(location);
 
+        _bindingType = bindingType;
         _toString = toString;
     }
 
@@ -54,5 +57,10 @@
     public boolean isInvariant()
     {
         return false;
+    }
+
+    public Class getBindingType()
+    {
+        return _bindingType;
     }
 }

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/bindings/LiteralBinding.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/bindings/LiteralBinding.java?view=diff&rev=449321&r1=449320&r2=449321
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/bindings/LiteralBinding.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/bindings/LiteralBinding.java Sat Sep 23 15:46:09 2006
@@ -46,4 +46,11 @@
     {
         return String.format("LiteralBinding[%s: %s]", _description, _value);
     }
+
+    public Class getBindingType()
+    {
+        // Could be a problem for a LiteralBinding of null but that will certainly be read-only.
+        return _value.getClass();
+    }
+
 }

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/bindings/PropBindingFactory.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/bindings/PropBindingFactory.java?view=diff&rev=449321&r1=449320&r2=449321
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/bindings/PropBindingFactory.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/bindings/PropBindingFactory.java Sat Sep 23 15:46:09 2006
@@ -14,6 +14,8 @@
 
 package org.apache.tapestry.internal.bindings;
 
+import static org.apache.tapestry.util.CollectionFactory.newMap;
+
 import java.lang.reflect.Constructor;
 import java.lang.reflect.Modifier;
 import java.util.Map;
@@ -34,7 +36,6 @@
 import org.apache.tapestry.ioc.services.PropertyAdapter;
 import org.apache.tapestry.services.BindingFactory;
 import org.apache.tapestry.util.BodyBuilder;
-import org.apache.tapestry.util.CollectionFactory;
 
 /**
  * Binding factory for reading and updating JavaBean properties. Uses
@@ -51,7 +52,27 @@
 
     private final ClassFactory _classFactory;
 
-    private final Map<String, Class> _cache = CollectionFactory.newMap();
+    // The key is a combination of class name and property name.
+    private final Map<String, BindingConstructor> _cache = newMap();
+
+    private static class BindingConstructor
+    {
+        private final Class _propertyType;
+
+        private final Constructor _constructor;
+
+        BindingConstructor(Class propertyType, Constructor constructor)
+        {
+            _propertyType = propertyType;
+            _constructor = constructor;
+        }
+
+        Binding newBindingInstance(Object target, String toString, Location location)
+                throws Exception
+        {
+            return (Binding) _constructor.newInstance(target, _propertyType, toString, location);
+        }
+    }
 
     private static final MethodSignature GET_SIGNATURE = new MethodSignature(Object.class, "get",
             null, null);
@@ -74,14 +95,12 @@
 
         try
         {
-            Class bindingClass = findBindingClass(targetClass, expression);
+            BindingConstructor cons = findCachedConstructor(targetClass, expression);
 
             String toString = String.format("PropBinding[%s %s(%s)]", description, component
                     .getCompleteId(), expression);
 
-            Constructor cons = bindingClass.getConstructors()[0];
-
-            return (Binding) cons.newInstance(target, toString, location);
+            return cons.newBindingInstance(target, toString, location);
         }
         catch (Exception ex)
         {
@@ -89,8 +108,18 @@
         }
     }
 
+    /**
+     * Searches for a cached binding constructor for the given target class and property name. As
+     * necessary, a new cached constructor is created.
+     * 
+     * @param targetClass
+     *            the class of the component which contains the property
+     * @param propertyName
+     *            the name of the property to expose as a binding
+     * @return
+     */
     @Concurrent.Read
-    private Class findBindingClass(Class targetClass, String propertyName)
+    private BindingConstructor findCachedConstructor(Class targetClass, String propertyName)
     {
         // The only problem with this key is if we can get in a situation where two different
         // versions of the class (from the default class loader, and the component class loader)
@@ -98,16 +127,17 @@
 
         String key = targetClass.getName() + ":" + propertyName;
 
-        Class result = _cache.get(key);
+        BindingConstructor result = _cache.get(key);
 
         if (result == null)
-            result = fillCache(key, targetClass, propertyName);
+            result = createConstructorAndFillCache(key, targetClass, propertyName);
 
         return result;
     }
 
     @Concurrent.Write
-    private Class fillCache(String key, Class targetClass, String propertyName)
+    private BindingConstructor createConstructorAndFillCache(String key, Class targetClass,
+            String propertyName)
     {
         // Race condition: simulataneous calls to fillCache() for the same targetClass/propertyName
         // combination may result in duplicate binding classes being created, which causes no great
@@ -121,9 +151,15 @@
 
         Class bindingClass = createBindingClass(targetClass, propertyName, adapter);
 
-        _cache.put(key, bindingClass);
+        // The fabricated class is only going to have the one constructor. This is the easiest
+        // way to access it.
 
-        return bindingClass;
+        BindingConstructor result = new BindingConstructor(adapter.getType(), bindingClass
+                .getConstructors()[0]);
+
+        _cache.put(key, result);
+
+        return result;
     }
 
     private Class createBindingClass(Class targetClass, String propertyName, PropertyAdapter adapter)
@@ -134,8 +170,11 @@
 
         classFab.addField("_target", targetClass);
 
-        classFab.addConstructor(new Class[]
-        { targetClass, String.class, Location.class }, null, "{ super($2, $3); _target = $1; }");
+        classFab.addConstructor(
+                new Class[]
+                { targetClass, Class.class, String.class, Location.class },
+                null,
+                "{ super($2, $3, $4); _target = $1; }");
 
         if (adapter.isRead())
         {
@@ -177,6 +216,10 @@
         return classFab.createClass();
     }
 
+    /**
+     * The cache contains references to classes loaded by the Tapestry class loader. When that
+     * loader is invalidated (due to class file changes), the cache is cleared and rebuilt.
+     */
     @Concurrent.Write
     public void objectWasInvalidated(InvalidationEvent event)
     {

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/structure/ComponentPageElementImpl.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/structure/ComponentPageElementImpl.java?view=diff&rev=449321&r1=449320&r2=449321
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/structure/ComponentPageElementImpl.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/structure/ComponentPageElementImpl.java Sat Sep 23 15:46:09 2006
@@ -469,6 +469,7 @@
         }
     }
 
+    @SuppressWarnings("unchecked")
     public <T> void writeParameter(String parameterName, T parameterValue)
     {
         Binding b = getBinding(parameterName);
@@ -477,7 +478,21 @@
 
         // TODO: Type coercion when writing value into binding
 
-        b.set(parameterValue);
+        Class bindingType = b.getBindingType();
+
+        try
+        {
+            Object coerced = _typeCoercer.coerce(parameterValue, bindingType);
+
+            b.set(coerced);
+        }
+        catch (Exception ex)
+        {
+            throw new TapestryException(StructureMessages.writeParameterFailure(
+                    parameterName,
+                    getCompleteId(),
+                    ex), ex);
+        }
     }
 
     public boolean isRendering()

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/structure/StructureMessages.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/structure/StructureMessages.java?view=diff&rev=449321&r1=449320&r2=449321
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/structure/StructureMessages.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/structure/StructureMessages.java Sat Sep 23 15:46:09 2006
@@ -44,4 +44,9 @@
     {
         return MESSAGES.format("get-parameter-failure", parameterName, componentId, cause);
     }
+    
+    static String writeParameterFailure(String parameterName, String componentId, Throwable cause)
+    {
+        return MESSAGES.format("write-parameter-failure", parameterName, componentId, cause);
+    }
 }

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/resources/org/apache/tapestry/internal/structure/StructureStrings.properties
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/resources/org/apache/tapestry/internal/structure/StructureStrings.properties?view=diff&rev=449321&r1=449320&r2=449321
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/resources/org/apache/tapestry/internal/structure/StructureStrings.properties (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/resources/org/apache/tapestry/internal/structure/StructureStrings.properties Sat Sep 23 15:46:09 2006
@@ -14,4 +14,5 @@
 
 missing-parameters=Parameter(s) %s are required for %s, but have not been bound.
 no-such-component=Component %s does not contain an embedded component with id '%s'.
-get-parameter-failure=Failure reading parameter %s of component %s: %s
\ No newline at end of file
+get-parameter-failure=Failure reading parameter %s of component %s: %s
+write-parameter-failure=Failure writing parameter %s of component %s: %s
\ No newline at end of file

Modified: tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/bindings/BindingFactoryTest.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/bindings/BindingFactoryTest.java?view=diff&rev=449321&r1=449320&r2=449321
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/bindings/BindingFactoryTest.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/bindings/BindingFactoryTest.java Sat Sep 23 15:46:09 2006
@@ -47,6 +47,7 @@
 
         assertEquals(b.get(), "Tapestry5");
         assertTrue(b.isInvariant());
+        assertSame(b.getBindingType(), String.class);
 
         try
         {

Modified: tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/bindings/PropBindingFactoryTest.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/bindings/PropBindingFactoryTest.java?view=diff&rev=449321&r1=449320&r2=449321
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/bindings/PropBindingFactoryTest.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/bindings/PropBindingFactoryTest.java Sat Sep 23 15:46:09 2006
@@ -77,6 +77,8 @@
 
         Binding binding = factory.newBinding("test binding", resources, "objectValue", l);
 
+        assertSame(binding.getBindingType(), String.class);
+
         bean.setObjectValue("first");
 
         assertEquals(binding.get(), "first");
@@ -102,6 +104,8 @@
         replay();
 
         Binding binding = factory.newBinding("test binding", resources, "intValue", l);
+
+        assertSame(binding.getBindingType(), int.class);
 
         bean.setIntValue(1);
 

Modified: tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/structure/ComponentPageElementImplTest.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/structure/ComponentPageElementImplTest.java?view=diff&rev=449321&r1=449320&r2=449321
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/structure/ComponentPageElementImplTest.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/structure/ComponentPageElementImplTest.java Sat Sep 23 15:46:09 2006
@@ -209,14 +209,20 @@
         ComponentLifecycle component = newComponentLifecycle();
         Instantiator ins = newInstantiator(component);
         ComponentModel model = newComponentModel();
+        TypeCoercer coercer = newTypeCoercer();
 
         Binding binding = newBinding();
 
+        binding.getBindingType();
+        setReturnValue(Integer.class);
+
+        train_coerce(coercer, 23, Integer.class, 23);
+
         binding.set(23);
 
         replay();
 
-        ComponentPageElement cpe = new ComponentPageElementImpl(page, ins, model, null);
+        ComponentPageElement cpe = new ComponentPageElementImpl(page, ins, model, coercer);
 
         cpe.addParameter("barney", binding);