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 2007/01/05 19:01:33 UTC

svn commit: r493107 - in /tapestry/tapestry5/tapestry-core/trunk/src: main/java/org/apache/tapestry/internal/bindings/ main/resources/org/apache/tapestry/internal/bindings/ site/apt/ site/apt/guide/ test/java/org/apache/tapestry/internal/bindings/

Author: hlship
Date: Fri Jan  5 10:01:32 2007
New Revision: 493107

URL: http://svn.apache.org/viewvc?view=rev&rev=493107
Log:
Add support for arbitrary method invocation inside property expressions

Modified:
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/bindings/BindingsMessages.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/bindings/PropBindingFactory.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/resources/org/apache/tapestry/internal/bindings/BindingsStrings.properties
    tapestry/tapestry5/tapestry-core/trunk/src/site/apt/guide/parameters.apt
    tapestry/tapestry5/tapestry-core/trunk/src/site/apt/index.apt
    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/bindings/StringHolder.java
    tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/bindings/StringHolderImpl.java
    tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/bindings/TargetBean.java

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/bindings/BindingsMessages.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/bindings/BindingsMessages.java?view=diff&rev=493107&r1=493106&r2=493107
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/bindings/BindingsMessages.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/bindings/BindingsMessages.java Fri Jan  5 10:01:32 2007
@@ -1,4 +1,4 @@
-// Copyright 2006 The Apache Software Foundation
+// Copyright 2006, 2007 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.
@@ -37,22 +37,40 @@
         return MESSAGES.format("binding-is-write-only", binding);
     }
 
-    static String noSuchProperty(Class targetClass, String propertyName, String propertyPath)
+    static String noSuchProperty(Class targetClass, String propertyName, String propertyExpression)
     {
         return MESSAGES.format(
                 "no-such-property",
                 targetClass.getName(),
                 propertyName,
-                propertyPath);
+                propertyExpression);
     }
 
-    static String writeOnlyProperty(String propertyName, Class clazz, String propertyPath)
+    static String writeOnlyProperty(String propertyName, Class clazz, String propertyExpression)
     {
-        return MESSAGES.format("write-only-property", propertyName, clazz.getName(), propertyPath);
+        return MESSAGES.format(
+                "write-only-property",
+                propertyName,
+                clazz.getName(),
+                propertyExpression);
     }
 
     static String validateBindingForFieldsOnly(ComponentResources component)
     {
         return MESSAGES.format("validate-binding-for-fields-only", component.getCompleteId());
+    }
+
+    static String methodNotFound(String methodName, Class inClass, String propertyExpression)
+    {
+        return MESSAGES.format(
+                "method-not-found",
+                methodName,
+                inClass.getName(),
+                propertyExpression);
+    }
+
+    static String methodIsVoid(String methodName, Class inClass, String propertyExpression)
+    {
+        return MESSAGES.format("method-is-void", methodName, inClass.getName(), propertyExpression);
     }
 }

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=493107&r1=493106&r2=493107
==============================================================================
--- 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 Fri Jan  5 10:01:32 2007
@@ -1,4 +1,4 @@
-// Copyright 2006 The Apache Software Foundation
+// Copyright 2006, 2007 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.
@@ -39,9 +39,31 @@
  * Binding factory for reading and updating JavaBean properties. Uses
  * {@link org.apache.tapestry.ioc.services.PropertyAccess} to analyze the properties, and generates
  * a binding class using the component {@link org.apache.tapestry.ioc.services.ClassFactory}.
+ * <p>
+ * The {@link Binding} object that's returned is from a runtime-genereated class. The property
+ * expression is turned into type-safe Java code.
+ * <p>
+ * The expression is a dotted sequence of <em>terms</em>. Each term is either a property name, or
+ * the name of a public method. In the latter case, the term includes open and close parenthesis
+ * (the method must take no parameters and throw no checked exceptions). A method name is treated as
+ * if it were a <em>read-only property</em>.
+ * <p>
+ * Example property expressions:
+ * <ul>
+ * <li>userName</li>
+ * <li>userData.name</li>
+ * <li>usreList.size()</li>
+ * </ul>
+ * <p>
+ * Everything works in terms of the property or methods <em>declared type</em>, not the
+ * <em>actual type</em>. This means that, for example, if a property is type Collection, you may
+ * <em>not</em> reference the listIterator() method, even if the actual value returned is of type
+ * List. This is only really bites you when a method's return type is Object.
  */
 public class PropBindingFactory implements BindingFactory, InvalidationListener
 {
+    private static final String PARENS = "()";
+
     private final PropertyAccess _access;
 
     private final ClassFactory _classFactory;
@@ -131,70 +153,132 @@
         return result;
     }
 
-    private BindingConstructor createConstructor(String key, Class targetClass, String propertyPath)
+    private BindingConstructor createConstructor(String key, Class targetClass,
+            String propertyExpression)
     {
-        // Race condition: simulataneous calls to createConstructor() for the same targetClass/propertyName
-        // combination may result in duplicate binding classes being created, which causes no great
-        // harm.
+        // Race condition: simultaneous calls to createConstructor() for the same
+        // targetClass/propertyExpression combination may result in duplicate binding classes being
+        // created, which causes no great harm.
 
         StringBuilder builder = new StringBuilder("_target");
 
         Class step = targetClass;
-        String[] terms = propertyPath.split("\\.");
+        String[] terms = propertyExpression.split("\\.");
 
         for (int i = 0; i < terms.length - 1; i++)
         {
-            String name = terms[i];
-
-            PropertyAdapter pa = _access.getAdapter(step).getPropertyAdapter(name);
-
-            if (pa == null)
-                throw new RuntimeException(BindingsMessages
-                        .noSuchProperty(step, name, propertyPath));
+            String term = terms[i];
 
-            Method m = pa.getReadMethod();
-
-            if (m == null)
-                throw new RuntimeException(BindingsMessages.writeOnlyProperty(
-                        name,
-                        step,
-                        propertyPath));
+            boolean isMethodReference = term.endsWith(PARENS);
 
-            // Extend the expression by invoking the read access method.
+            step = isMethodReference ? extendWithMethodTerm(step, term, propertyExpression, builder)
+                    : extendWithPropertyTerm(step, term, propertyExpression, builder);
+        }
 
-            builder.append(".");
-            builder.append(m.getName());
-            builder.append("()");
+        String terminal = terms[terms.length - 1];
 
-            // The next property (in the path, or the terminal) that is evaluated is in terms of the
-            // return type of this method.
+        Class bindingType = null;
+        Method readMethod = null;
+        Method writeMethod = null;
 
-            step = pa.getType();
+        if (terminal.endsWith(PARENS))
+        {
+            readMethod = validateMethodName(step, terminal, propertyExpression);
+            bindingType = readMethod.getReturnType();
         }
+        else
+        {
+            PropertyAdapter adapter = _access.getAdapter(step).getPropertyAdapter(terminal);
 
-        String terminalName = terms[terms.length - 1];
-
-        PropertyAdapter adapter = _access.getAdapter(step).getPropertyAdapter(terminalName);
+            if (adapter == null)
+                throw new RuntimeException(BindingsMessages.noSuchProperty(
+                        step,
+                        terminal,
+                        propertyExpression));
 
-        if (adapter == null)
-            throw new RuntimeException(BindingsMessages.noSuchProperty(
-                    step,
-                    terminalName,
-                    propertyPath));
+            bindingType = adapter.getType();
+            readMethod = adapter.getReadMethod();
+            writeMethod = adapter.getWriteMethod();
+        }
 
         Class bindingClass = createBindingClass(
                 targetClass,
                 builder.toString(),
-                terminalName,
-                adapter);
+                terminal,
+                bindingType,
+                readMethod,
+                writeMethod);
 
         // The fabricated class is only going to have the one constructor. This is the easiest
         // way to access it.
 
-        BindingConstructor result = new BindingConstructor(adapter.getType(), bindingClass
-                .getConstructors()[0]);
+        return new BindingConstructor(bindingType, bindingClass.getConstructors()[0]);
+    }
 
-        return result;
+    private Class extendWithMethodTerm(Class inClass, String term, String propertyExpression,
+            StringBuilder builder)
+    {
+        Method method = validateMethodName(inClass, term, propertyExpression);
+
+        builder.append(".");
+        builder.append(term);
+
+        return method.getReturnType();
+
+    }
+
+    private Method validateMethodName(Class inClass, String term, String propertyExpression)
+    {
+        String methodName = term.substring(0, term.length() - PARENS.length());
+
+        try
+        {
+            // Find a public method that takes no parameters.
+
+            Method method = inClass.getMethod(methodName);
+
+            if (method.getReturnType().equals(void.class))
+                throw new RuntimeException(BindingsMessages.methodIsVoid(
+                        term,
+                        inClass,
+                        propertyExpression));
+
+            return method;
+
+        }
+        catch (NoSuchMethodException ex)
+        {
+            throw new RuntimeException(BindingsMessages.methodNotFound(
+                    term,
+                    inClass,
+                    propertyExpression), ex);
+        }
+    }
+
+    private Class extendWithPropertyTerm(Class inClass, String term, String propertyExpression,
+            StringBuilder builder)
+    {
+        PropertyAdapter pa = _access.getAdapter(inClass).getPropertyAdapter(term);
+
+        if (pa == null)
+            throw new RuntimeException(BindingsMessages.noSuchProperty(
+                    inClass,
+                    term,
+                    propertyExpression));
+
+        Method m = pa.getReadMethod();
+
+        if (m == null)
+            throw new RuntimeException(BindingsMessages.writeOnlyProperty(
+                    term,
+                    inClass,
+                    propertyExpression));
+
+        builder.append(".");
+        builder.append(m.getName());
+        builder.append(PARENS);
+
+        return pa.getType();
     }
 
     /**
@@ -205,12 +289,10 @@
      *            _target instance variable
      * @param propertyName
      *            the name of the terminal property
-     * @param adapter
-     *            property adapter for the terminal property
      * @return Class that implements Binding
      */
     private Class createBindingClass(Class targetClass, String pathExpression, String propertyName,
-            PropertyAdapter adapter)
+            Class propertyType, Method readMethod, Method writeMethod)
     {
         String name = ClassFabUtils.generateClassName("PropBinding");
 
@@ -224,18 +306,16 @@
                 null,
                 "{ super($2, $3, $4); _target = $1; }");
 
-        if (adapter.isRead())
+        if (readMethod != null)
         {
-            String body = String.format("return ($w) %s.%s();", pathExpression, adapter
-                    .getReadMethod().getName());
+            String body = String.format("return ($w) %s.%s();", pathExpression, readMethod
+                    .getName());
 
             classFab.addMethod(Modifier.PUBLIC, GET_SIGNATURE, body);
         }
 
-        if (adapter.isUpdate())
+        if (writeMethod != null)
         {
-            Class propertyType = adapter.getType();
-
             BodyBuilder builder = new BodyBuilder();
             builder.begin();
 
@@ -255,7 +335,7 @@
             else
                 builder.addln("(%s) $1;", propertyTypeName);
 
-            builder.addln("%s.%s(value);", pathExpression, adapter.getWriteMethod().getName());
+            builder.addln("%s.%s(value);", pathExpression, writeMethod.getName());
             builder.end();
 
             classFab.addMethod(Modifier.PUBLIC, SET_SIGNATURE, builder.toString());

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/resources/org/apache/tapestry/internal/bindings/BindingsStrings.properties
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/resources/org/apache/tapestry/internal/bindings/BindingsStrings.properties?view=diff&rev=493107&r1=493106&r2=493107
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/resources/org/apache/tapestry/internal/bindings/BindingsStrings.properties (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/resources/org/apache/tapestry/internal/bindings/BindingsStrings.properties Fri Jan  5 10:01:32 2007
@@ -1,4 +1,4 @@
-# Copyright 2006 The Apache Software Foundation
+# Copyright 2006, 2007 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.
@@ -14,6 +14,8 @@
 
 binding-is-read-only=Binding %s is read-only.
 binding-is-write-only=Binding %s is write-only.
-no-such-property=Class %s does not contain a property named '%s' (within property path '%s').
-write-only-property=Property '%s' of class %s (within property path '%s') is not readable (it has no read accessor method).
+no-such-property=Class %s does not contain a property named '%s' (within property expression '%s').
+write-only-property=Property '%s' of class %s (within property expression '%s') is not readable (it has no read accessor method).
 validate-binding-for-fields-only=Component '%s' is not a field (it does not implement the Field interface) and may not be used with the validate: binding prefix.
+method-not-found=No public method '%s' in class %s (within property expression '%s').
+method-is-void=Method '%s' returns void (in class %s, within property expression '%s').
\ No newline at end of file

Modified: tapestry/tapestry5/tapestry-core/trunk/src/site/apt/guide/parameters.apt
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/site/apt/guide/parameters.apt?view=diff&rev=493107&r1=493106&r2=493107
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/site/apt/guide/parameters.apt (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/site/apt/guide/parameters.apt Fri Jan  5 10:01:32 2007
@@ -138,12 +138,24 @@
 
   The "prop:" prefix indicates a property binding.
   
-  Property bindings are normally either simple names of properties ("prop:userName")
-  or paths to properties ("prop:user.address.city").
-    
+  The expression for a property binding is a dotted sequence of property names.  Simple
+  property expressions are just the name of a property, "prop:userName".  Complex property
+  expression may do a little navigation before getting to the property to read and/or update:
+  "prop:userData.name".  
+  
+  In addition to property names, you may also invoke arbitrary methods.  The methods must be public,
+  return a non-void value, throw no checked exceptions, and take no parameters.  To differentiate
+  a method name from a property name, you simply append the open and close parenthesis.  Thus
+  the prior examples could be rewritten as "prop:getUserName()" and "prop:getUserData().getName()".
+  Note that when the last term in the expression is a method name, the binding will be read-only,
+  rather than read/write.
+  
+  This feature is most useful for accessing a couple of propertys of standard collection classes
+  that aren't named as proper properties, such as Collection.size(), or Map.keySet().
+      
   In addition, a few special cases are also supported. 
   In most cases, these special values save you the trouble of adding a "literal:" prefix to
-  the value.
+  the value.  These special cases are <alternatives> to property expressions.
   
   * "true" and "false" will be converted to booleans. 
   

Modified: tapestry/tapestry5/tapestry-core/trunk/src/site/apt/index.apt
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/site/apt/index.apt?view=diff&rev=493107&r1=493106&r2=493107
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/site/apt/index.apt (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/site/apt/index.apt Fri Jan  5 10:01:32 2007
@@ -36,6 +36,8 @@
   Progress on Tapestry 5 is really taking off. This space lists some cool new features that have been added
   recently.
   
+  * Property expressions may now reference public methods (with no parameters) in addition to traditional property names.
+  
   * Page templates are now allowed to be stored in the web application root, as well as on the classpath.
   
   * Invisible instrumentation, hiding Tapestry components inside ordinary HTML elements (a favorite feature of Tapestry 4), has been added to Tapestry 5.

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=493107&r1=493106&r2=493107
==============================================================================
--- 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 Fri Jan  5 10:01:32 2007
@@ -1,4 +1,4 @@
-// Copyright 2006 The Apache Software Foundation
+// Copyright 2006, 2007 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.
@@ -114,7 +114,184 @@
                 "PropBinding[test binding foo.Bar:baz(stringHolder.value)]");
 
         verify();
+    }
+
+    /** The "preamble" are the non-terminal property or method names. */
+    @Test
+    public void property_path_with_explicit_method_in_preamble()
+    {
+        TargetBean bean = new TargetBean();
+        ComponentResources resources = newComponentResources(bean);
+        Location l = newLocation();
+
+        replay();
+
+        Binding binding = _factory.newBinding(
+                "test binding",
+                resources,
+                null,
+                "stringHolderMethod().value",
+                l);
+
+        assertSame(binding.getBindingType(), String.class);
+
+        bean.getStringHolder().setValue("first");
+
+        assertEquals(binding.get(), "first");
+
+        assertEquals(
+                binding.toString(),
+                "PropBinding[test binding foo.Bar:baz(stringHolderMethod().value)]");
+
+        verify();
+    }
+
+    @Test
+    public void method_call_as_terminal()
+    {
+        TargetBean bean = new TargetBean();
+        ComponentResources resources = newComponentResources(bean);
+        Location l = newLocation();
+
+        replay();
+
+        Binding binding = _factory.newBinding(
+                "test binding",
+                resources,
+                null,
+                "stringHolderMethod().stringValue()",
+                l);
+
+        assertSame(binding.getBindingType(), String.class);
+
+        bean.getStringHolder().setValue("first");
+
+        assertEquals(binding.get(), "first");
+
+        try
+        {
+            binding.set("read-only");
+            unreachable();
+        }
+        catch (TapestryException ex)
+        {
+            assertEquals(
+                    ex.getMessage(),
+                    "Binding PropBinding[test binding foo.Bar:baz(stringHolderMethod().stringValue())] is read-only.");
+            assertSame(ex.getLocation(), l);
+        }
+
+        verify();
+
+    }
+
+    @Test
+    public void method_not_found_in_preamble()
+    {
+        TargetBean bean = new TargetBean();
+        ComponentResources resources = newComponentResources();
+        Location l = newLocation();
+
+        train_getComponent(resources, bean);
+
+        replay();
+
+        try
+        {
+            _factory.newBinding("test binding", resources, null, "isThatRealBlood().value", l);
+            unreachable();
+        }
+        catch (RuntimeException ex)
+        {
+            assertEquals(
+                    ex.getMessage(),
+                    "No public method \'isThatRealBlood()\' in class org.apache.tapestry.internal.bindings.TargetBean (within property expression \'isThatRealBlood().value\').");
+        }
+
+        verify();
+    }
+
+    @Test
+    public void method_not_found_in_terminal()
+    {
+        TargetBean bean = new TargetBean();
+        ComponentResources resources = newComponentResources();
+        Location l = newLocation();
 
+        train_getComponent(resources, bean);
+
+        replay();
+
+        try
+        {
+            _factory.newBinding(
+                    "test binding",
+                    resources,
+                    null,
+                    "stringHolder.isThatRealBlood()",
+                    l);
+            unreachable();
+        }
+        catch (RuntimeException ex)
+        {
+            assertEquals(
+                    ex.getMessage(),
+                    "No public method \'isThatRealBlood()\' in class org.apache.tapestry.internal.bindings.StringHolder (within property expression \'stringHolder.isThatRealBlood()\').");
+        }
+
+        verify();
+    }
+
+    @Test
+    public void void_method_in_preamble()
+    {
+        TargetBean bean = new TargetBean();
+        ComponentResources resources = newComponentResources();
+        Location l = newLocation();
+
+        train_getComponent(resources, bean);
+
+        replay();
+
+        try
+        {
+            _factory.newBinding("test binding", resources, null, "voidMethod().value", l);
+            unreachable();
+        }
+        catch (RuntimeException ex)
+        {
+            assertEquals(
+                    ex.getMessage(),
+                    "Method \'voidMethod()\' returns void (in class org.apache.tapestry.internal.bindings.TargetBean, within property expression \'voidMethod().value\').");
+        }
+
+        verify();
+    }
+
+    @Test
+    public void void_method_as_terminal()
+    {
+        TargetBean bean = new TargetBean();
+        ComponentResources resources = newComponentResources();
+        Location l = newLocation();
+
+        train_getComponent(resources, bean);
+
+        replay();
+
+        try
+        {
+            _factory.newBinding("test binding", resources, null, "stringHolder.voidMethod()", l);
+            unreachable();
+        }
+        catch (RuntimeException ex)
+        {
+            assertEquals(
+                    ex.getMessage(),
+                    "Method \'voidMethod()\' returns void (in class org.apache.tapestry.internal.bindings.StringHolder, within property expression \'stringHolder.voidMethod()\').");
+        }
+
+        verify();
     }
 
     @Test

Modified: tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/bindings/StringHolder.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/bindings/StringHolder.java?view=diff&rev=493107&r1=493106&r2=493107
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/bindings/StringHolder.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/bindings/StringHolder.java Fri Jan  5 10:01:32 2007
@@ -1,4 +1,4 @@
-// Copyright 2006 The Apache Software Foundation
+// Copyright 2006, 2007 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.
@@ -18,5 +18,9 @@
 {
     void setValue(String value);
 
-    String getValue();
+    String getValue();
+    
+    String stringValue();
+    
+    void voidMethod();
 }

Modified: tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/bindings/StringHolderImpl.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/bindings/StringHolderImpl.java?view=diff&rev=493107&r1=493106&r2=493107
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/bindings/StringHolderImpl.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/bindings/StringHolderImpl.java Fri Jan  5 10:01:32 2007
@@ -1,4 +1,4 @@
-// Copyright 2006 The Apache Software Foundation
+// Copyright 2006, 2007 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.
@@ -26,5 +26,14 @@
     public void setValue(String value)
     {
         _value = value;
+    }
+
+    public String stringValue()
+    {
+        return _value;
+    }
+
+    public void voidMethod()
+    {
     }
 }

Modified: tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/bindings/TargetBean.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/bindings/TargetBean.java?view=diff&rev=493107&r1=493106&r2=493107
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/bindings/TargetBean.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/bindings/TargetBean.java Fri Jan  5 10:01:32 2007
@@ -1,4 +1,4 @@
-// Copyright 2006 The Apache Software Foundation
+// Copyright 2006, 2007 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.
@@ -27,6 +27,16 @@
     public StringHolder getStringHolder()
     {
         return _stringHolder;
+    }
+
+    public StringHolder stringHolderMethod()
+    {
+        return _stringHolder;
+    }
+
+    public void voidMethod()
+    {
+
     }
 
     public int getIntValue()