You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@tapestry.apache.org by th...@apache.org on 2014/05/27 16:29:28 UTC

git commit: Backports the fix for TAP5-1718 to 5.3.x

Repository: tapestry-5
Updated Branches:
  refs/heads/5.3 4445ae4df -> 4a5b8fc69


Backports the fix for TAP5-1718 to 5.3.x


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

Branch: refs/heads/5.3
Commit: 4a5b8fc6994b0138e60f90666b55f3b54c71ecfe
Parents: 4445ae4
Author: Thiago H. de Paula Figueiredo <th...@apache.org>
Authored: Fri Jan 10 15:25:12 2014 -0200
Committer: Thiago H. de Paula Figueiredo <th...@arsmachina.com.br>
Committed: Tue May 27 11:29:11 2014 -0300

----------------------------------------------------------------------
 .../beanvalidator/BeanFieldValidator.java       | 348 +++++++++++--------
 .../TapestryBeanValidationIntegrationTests.java |  24 +-
 .../example/testapp/entities/ComplexBean.java   |  13 +
 .../example/testapp/pages/NestedObjectDemo.java |  38 ++
 .../org/example/testapp/services/AppModule.java |   1 +
 .../src/test/webapp/Index.tml                   |   3 +
 .../src/test/webapp/NestedObjectDemo.tml        |  22 ++
 .../internal/bindings/PropBinding.java          |  12 +-
 .../internal/bindings/PropBindingFactory.java   |   2 +-
 .../InternalComponentResourcesImpl.java         |   7 +
 10 files changed, 312 insertions(+), 158 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/4a5b8fc6/tapestry-beanvalidator/src/main/java/org/apache/tapestry5/internal/beanvalidator/BeanFieldValidator.java
----------------------------------------------------------------------
diff --git a/tapestry-beanvalidator/src/main/java/org/apache/tapestry5/internal/beanvalidator/BeanFieldValidator.java b/tapestry-beanvalidator/src/main/java/org/apache/tapestry5/internal/beanvalidator/BeanFieldValidator.java
index 43eedfd..06e6fb9 100644
--- a/tapestry-beanvalidator/src/main/java/org/apache/tapestry5/internal/beanvalidator/BeanFieldValidator.java
+++ b/tapestry-beanvalidator/src/main/java/org/apache/tapestry5/internal/beanvalidator/BeanFieldValidator.java
@@ -1,4 +1,4 @@
-// Copyright 2010 The Apache Software Foundation
+// Copyright 2010, 2012 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.
@@ -13,21 +13,6 @@
 // limitations under the License.
 package org.apache.tapestry5.internal.beanvalidator;
 
-import static java.lang.String.format;
-
-import java.lang.annotation.Annotation;
-import java.util.Iterator;
-import java.util.Set;
-
-import javax.validation.ConstraintViolation;
-import javax.validation.MessageInterpolator;
-import javax.validation.Validator;
-import javax.validation.ValidatorFactory;
-import javax.validation.MessageInterpolator.Context;
-import javax.validation.metadata.BeanDescriptor;
-import javax.validation.metadata.ConstraintDescriptor;
-import javax.validation.metadata.PropertyDescriptor;
-
 import org.apache.tapestry5.Field;
 import org.apache.tapestry5.FieldValidator;
 import org.apache.tapestry5.MarkupWriter;
@@ -36,156 +21,213 @@ import org.apache.tapestry5.beanvalidator.BeanValidatorGroupSource;
 import org.apache.tapestry5.beanvalidator.ClientConstraintDescriptor;
 import org.apache.tapestry5.beanvalidator.ClientConstraintDescriptorSource;
 import org.apache.tapestry5.internal.BeanValidationContext;
+import org.apache.tapestry5.ioc.internal.util.CollectionFactory;
 import org.apache.tapestry5.json.JSONObject;
 import org.apache.tapestry5.services.Environment;
 import org.apache.tapestry5.services.FormSupport;
 
+import javax.validation.ConstraintViolation;
+import javax.validation.MessageInterpolator;
+import javax.validation.MessageInterpolator.Context;
+import javax.validation.Validator;
+import javax.validation.ValidatorFactory;
+import javax.validation.metadata.BeanDescriptor;
+import javax.validation.metadata.ConstraintDescriptor;
+import javax.validation.metadata.PropertyDescriptor;
+
+import java.lang.annotation.Annotation;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+
+import static java.lang.String.format;
+
 
 public class BeanFieldValidator implements FieldValidator
 {
-	private final Field field;
-	private final ValidatorFactory validatorFactory;
-	private final BeanValidatorGroupSource beanValidationGroupSource;
-	private final ClientConstraintDescriptorSource clientValidatorSource;
-	private final FormSupport formSupport;
-	private final Environment environment;
-	
-	public BeanFieldValidator(Field field,
-			ValidatorFactory validatorFactory,
-			BeanValidatorGroupSource beanValidationGroupSource,
-			ClientConstraintDescriptorSource clientValidatorSource,
-			FormSupport formSupport,
-			Environment environment) 
-	{
-		this.field = field;
-		this.validatorFactory = validatorFactory;
-		this.beanValidationGroupSource = beanValidationGroupSource;
-		this.clientValidatorSource = clientValidatorSource;
-		this.formSupport = formSupport;
-		this.environment = environment;
-	}
-	
-	public boolean isRequired() 
-	{
-		return false;
-	}
-
-	public void render(final MarkupWriter writer) 
-	{
-		final BeanValidationContext beanValidationContext = environment.peek(BeanValidationContext.class);
-
-		if (beanValidationContext == null) 
-		{
-			return;
-		}
-		
-		final Validator validator = validatorFactory.getValidator();
-		
-		BeanDescriptor beanDescriptor = validator.getConstraintsForClass(beanValidationContext.getBeanType());
-		
-		String currentProperty = beanValidationContext.getCurrentProperty();
-		
-		if(currentProperty == null) return;
-		
-		PropertyDescriptor propertyDescriptor = beanDescriptor.getConstraintsForProperty(currentProperty);
-		
-		if(propertyDescriptor == null) return;
-		
-		for (final ConstraintDescriptor<?> descriptor :propertyDescriptor.getConstraintDescriptors()) 
-		{
-			Class<? extends Annotation> annotationType = descriptor.getAnnotation().annotationType();
-			
-			ClientConstraintDescriptor clientConstraintDescriptor = clientValidatorSource.getConstraintDescriptor(annotationType);
-			
-			if(clientConstraintDescriptor != null)
-			{	
-				String message = format("%s %s", field.getLabel(), interpolateMessage(descriptor));
-				
-				JSONObject specs = new JSONObject();
-				
-                for (String attribute : clientConstraintDescriptor.getAttributes()) 
+    private final Field field;
+    private final ValidatorFactory validatorFactory;
+    private final BeanValidatorGroupSource beanValidationGroupSource;
+    private final ClientConstraintDescriptorSource clientValidatorSource;
+    private final FormSupport formSupport;
+    private final Environment environment;
+
+    public BeanFieldValidator(Field field,
+                              ValidatorFactory validatorFactory,
+                              BeanValidatorGroupSource beanValidationGroupSource,
+                              ClientConstraintDescriptorSource clientValidatorSource,
+                              FormSupport formSupport,
+                              Environment environment)
+    {
+        this.field = field;
+        this.validatorFactory = validatorFactory;
+        this.beanValidationGroupSource = beanValidationGroupSource;
+        this.clientValidatorSource = clientValidatorSource;
+        this.formSupport = formSupport;
+        this.environment = environment;
+    }
+
+    public boolean isRequired()
+    {
+        return false;
+    }
+
+    public void render(final MarkupWriter writer)
+    {
+        final BeanValidationContext beanValidationContext = environment.peek(BeanValidationContext.class);
+
+        if (beanValidationContext == null)
+        {
+            return;
+        }
+
+        final Validator validator = validatorFactory.getValidator();
+
+        BeanDescriptor beanDescriptor = validator.getConstraintsForClass(beanValidationContext.getBeanType());
+
+        String currentProperty = beanValidationContext.getCurrentProperty();
+
+        if (currentProperty == null) return;
+
+        PropertyDescriptor propertyDescriptor = beanDescriptor.getConstraintsForProperty(currentProperty);
+
+        if (propertyDescriptor == null) return;
+
+        for (final ConstraintDescriptor<?> descriptor : propertyDescriptor.getConstraintDescriptors())
+        {
+            Class<? extends Annotation> annotationType = descriptor.getAnnotation().annotationType();
+
+            ClientConstraintDescriptor clientConstraintDescriptor = clientValidatorSource.getConstraintDescriptor(annotationType);
+
+            if (clientConstraintDescriptor == null)
+            {
+                continue;
+            }
+
+            String message = format("%s %s", field.getLabel(), interpolateMessage(descriptor));
+            JSONObject specs = new JSONObject();
+
+            Map<String, Object> attributes = CollectionFactory.newMap();
+
+            for (String attribute : clientConstraintDescriptor.getAttributes())
+            {
+                Object object = descriptor.getAttributes().get(attribute);
+
+                if (object == null)
                 {
-                    Object object = descriptor.getAttributes().get(attribute);
-                    
-                    if (object == null) 
-                    {
-                      throw new RuntimeException("Expected attribute is null");
-                    }
-                    specs.put(attribute, object);
+                    throw new NullPointerException(
+                            String.format("Attribute '%s' of %s is null but is required to apply client-side validation.",
+                                    attribute, descriptor));
                 }
+                attributes.put(attribute, object);
+            
+                specs.put(attribute, object);
                 
-				formSupport.addValidation(field, clientConstraintDescriptor.getValidatorName(), message, specs);
-			}
-		}
-	}
-
-	@SuppressWarnings("unchecked")
-	public void validate(final Object value) throws ValidationException 
-	{
-
-		final BeanValidationContext beanValidationContext = environment.peek(BeanValidationContext.class);
-
-		if (beanValidationContext == null) 
-		{
-			return;
-		}
-		
-		final Validator validator = validatorFactory.getValidator();
-		
-		String currentProperty = beanValidationContext.getCurrentProperty();
-		
-		if(currentProperty == null) return;
-		
-		BeanDescriptor beanDescriptor = validator.getConstraintsForClass(beanValidationContext.getBeanType());
-		
-		PropertyDescriptor propertyDescriptor = beanDescriptor.getConstraintsForProperty(currentProperty);
-		
-		if(propertyDescriptor == null) return;
-		
-		final Set<ConstraintViolation<Object>> violations = validator.validateValue(
-						(Class<Object>) beanValidationContext.getBeanType(), currentProperty, 
-						value, beanValidationGroupSource.get());
-		
-		if (violations.isEmpty()) 
-		{
-			return;
-		}
-		
-		final StringBuilder builder = new StringBuilder();
-		
-		for (Iterator iterator = violations.iterator(); iterator.hasNext();) 
-		{
-			ConstraintViolation<?> violation = (ConstraintViolation<Object>) iterator.next();
-			
-			builder.append(format("%s %s", field.getLabel(), violation.getMessage()));
-			
-			if(iterator.hasNext())
-				builder.append(", ");
-	
-		}
-		
-		throw new ValidationException(builder.toString());
-
-	}
-	
-	private String interpolateMessage(final ConstraintDescriptor<?> descriptor)
-	{
-		String messageTemplate = (String) descriptor.getAttributes().get("message");
-		
-		MessageInterpolator messageInterpolator = validatorFactory.getMessageInterpolator();
-		
-		return messageInterpolator.interpolate(messageTemplate, new Context() 
-		{
-
-            public ConstraintDescriptor<?> getConstraintDescriptor() 
+            }
+            
+            formSupport.addValidation(field, clientConstraintDescriptor.getValidatorName(), message, specs);
+
+        }
+        
+    }
+
+    @SuppressWarnings("unchecked")
+    public void validate(final Object value) throws ValidationException
+    {
+
+        final BeanValidationContext beanValidationContext = environment.peek(BeanValidationContext.class);
+
+        if (beanValidationContext == null)
+        {
+            return;
+        }
+
+        final Validator validator = validatorFactory.getValidator();
+
+        String currentProperty = beanValidationContext.getCurrentProperty();
+
+        if (currentProperty == null) return;
+        
+        Class<?> beanType = beanValidationContext.getBeanType();
+        String[] path = currentProperty.split("\\.");
+        BeanDescriptor beanDescriptor = validator.getConstraintsForClass(beanType);
+        
+        for (int i = 1; i < path.length - 1; i++) 
+        {
+            Class<?> constrainedPropertyClass = getConstrainedPropertyClass(beanDescriptor, path[i]);
+            if (constrainedPropertyClass != null) {
+                beanType = constrainedPropertyClass;
+                beanDescriptor = validator.getConstraintsForClass(beanType);
+            }
+        }
+
+        final String propertyName = path[path.length - 1];
+        PropertyDescriptor propertyDescriptor = beanDescriptor.getConstraintsForProperty(propertyName);
+
+        if (propertyDescriptor == null) return;
+
+        final Set<ConstraintViolation<Object>> violations = validator.validateValue(
+                (Class<Object>) beanType, propertyName,
+                value, beanValidationGroupSource.get());
+
+        if (violations.isEmpty())
+        {
+            return;
+        }
+
+        final StringBuilder builder = new StringBuilder();
+
+        for (Iterator<ConstraintViolation<Object>> iterator = violations.iterator(); iterator.hasNext(); )
+        {
+            ConstraintViolation<?> violation = iterator.next();
+
+            builder.append(format("%s %s", field.getLabel(), violation.getMessage()));
+
+            if (iterator.hasNext())
+                builder.append(", ");
+
+        }
+
+        throw new ValidationException(builder.toString());
+
+    }
+
+    /**
+     * Returns the class of a given property, but only if it is a constrained property of the
+     * parent class. Otherwise, it returns null.
+     */
+    final private static Class<?> getConstrainedPropertyClass(BeanDescriptor beanDescriptor, String propertyName)
+    {
+        Class<?> clasz = null;
+        for (PropertyDescriptor descriptor : beanDescriptor.getConstrainedProperties()) 
+        {
+            if (descriptor.getPropertyName().equals(propertyName)) 
+            {
+                clasz = descriptor.getElementClass();
+                break;
+            }
+        }
+        return clasz;
+    }
+
+    private String interpolateMessage(final ConstraintDescriptor<?> descriptor)
+    {
+        String messageTemplate = (String) descriptor.getAttributes().get("message");
+
+        MessageInterpolator messageInterpolator = validatorFactory.getMessageInterpolator();
+
+        return messageInterpolator.interpolate(messageTemplate, new Context()
+        {
+
+            public ConstraintDescriptor<?> getConstraintDescriptor()
             {
-              return descriptor;
+                return descriptor;
             }
 
-            public Object getValidatedValue() 
+            public Object getValidatedValue()
             {
-              return null;
+                return null;
             }
         });
-	}
-}
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/4a5b8fc6/tapestry-beanvalidator/src/test/java/org/apache/tapestry5/beanvalidator/integration/TapestryBeanValidationIntegrationTests.java
----------------------------------------------------------------------
diff --git a/tapestry-beanvalidator/src/test/java/org/apache/tapestry5/beanvalidator/integration/TapestryBeanValidationIntegrationTests.java b/tapestry-beanvalidator/src/test/java/org/apache/tapestry5/beanvalidator/integration/TapestryBeanValidationIntegrationTests.java
index 1d686c4..f068d6a 100644
--- a/tapestry-beanvalidator/src/test/java/org/apache/tapestry5/beanvalidator/integration/TapestryBeanValidationIntegrationTests.java
+++ b/tapestry-beanvalidator/src/test/java/org/apache/tapestry5/beanvalidator/integration/TapestryBeanValidationIntegrationTests.java
@@ -1,4 +1,4 @@
-// Copyright 2009, 2010 The Apache Software Foundation
+// Copyright 2009-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.
@@ -18,7 +18,7 @@ import org.apache.tapestry5.test.TapestryTestConfiguration;
 import org.testng.annotations.Test;
 
 @Test(sequential = true, groups = "integration")
-@TapestryTestConfiguration(webAppFolder = "src/test/webapp")
+@TapestryTestConfiguration(webAppFolder = "src/test/webapp", port = 8080, browserStartCommand="*opera"/*browserStartCommand="*custom /usr/bin/chromium-browser"*/)
 public class TapestryBeanValidationIntegrationTests extends SeleniumTestCase
 {
     @Test
@@ -217,6 +217,26 @@ public class TapestryBeanValidationIntegrationTests extends SeleniumTestCase
 
     }
     
+    // TAP5-1718
+    @Test
+    public void nested_object_validation() throws Exception
+    {
+        
+        final String locatorTemplate = "//p[@data-error-block-for='%s']";
+        
+        openLinks("NestedObject Demo");
+
+        clickAndWait(SUBMIT);
+        
+        assertEquals("You must provide a value for Not Null String.", 
+                getText(String.format(locatorTemplate, "notNullString")));
+        assertEquals("Simple Not Null Property may not be null", 
+                getText(String.format(locatorTemplate, "simpleNotNullProperty")));
+        assertEquals("Min Value must be greater than or equal to 6", 
+                getText(String.format(locatorTemplate, "minValue")));
+
+    }    
+    
     protected final void assertBubbleMessage(String fieldId, String expected)
     {
         String popupId = fieldId + "_errorpopup";

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/4a5b8fc6/tapestry-beanvalidator/src/test/java/org/example/testapp/entities/ComplexBean.java
----------------------------------------------------------------------
diff --git a/tapestry-beanvalidator/src/test/java/org/example/testapp/entities/ComplexBean.java b/tapestry-beanvalidator/src/test/java/org/example/testapp/entities/ComplexBean.java
index beaa840..44af90d 100644
--- a/tapestry-beanvalidator/src/test/java/org/example/testapp/entities/ComplexBean.java
+++ b/tapestry-beanvalidator/src/test/java/org/example/testapp/entities/ComplexBean.java
@@ -1,10 +1,13 @@
 package org.example.testapp.entities;
 
+import javax.validation.Valid;
 import javax.validation.constraints.NotNull;
 
 public class ComplexBean
 {
 
+    @Valid
+    private SomeSimpleBean otherSimpleBean;
     private SomeSimpleBean someSimpleBean;
     private SomeOtherSimpleBean someOtherSimpleBean;
 
@@ -41,4 +44,14 @@ public class ComplexBean
         this.simpleNotNullProperty = simpleNotNullProperty;
     }
 
+    public SomeSimpleBean getOtherSimpleBean()
+    {
+        return otherSimpleBean;
+    }
+
+    public void setOtherSimpleBean(SomeSimpleBean otherSimpleBean)
+    {
+        this.otherSimpleBean = otherSimpleBean;
+    }
+
 }

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/4a5b8fc6/tapestry-beanvalidator/src/test/java/org/example/testapp/pages/NestedObjectDemo.java
----------------------------------------------------------------------
diff --git a/tapestry-beanvalidator/src/test/java/org/example/testapp/pages/NestedObjectDemo.java b/tapestry-beanvalidator/src/test/java/org/example/testapp/pages/NestedObjectDemo.java
new file mode 100644
index 0000000..f6406bd
--- /dev/null
+++ b/tapestry-beanvalidator/src/test/java/org/example/testapp/pages/NestedObjectDemo.java
@@ -0,0 +1,38 @@
+package org.example.testapp.pages;
+
+import org.apache.tapestry5.alerts.AlertManager;
+import org.apache.tapestry5.alerts.Duration;
+import org.apache.tapestry5.alerts.Severity;
+import org.apache.tapestry5.annotations.Persist;
+import org.apache.tapestry5.annotations.Property;
+import org.apache.tapestry5.ioc.annotations.Inject;
+import org.example.testapp.entities.ComplexBean;
+import org.example.testapp.entities.SomeSimpleBean;
+
+public class NestedObjectDemo
+{
+
+    @Property
+    @Persist
+    private ComplexBean complexBean;
+    
+    @Property
+    @Persist
+    private String notNullString;
+    
+    @Inject
+    private AlertManager alertManager;
+    
+    public void onActivate() {
+        if (complexBean == null) { 
+            complexBean = new ComplexBean();
+            SomeSimpleBean otherSimpleBean = new SomeSimpleBean();
+            complexBean.setOtherSimpleBean(otherSimpleBean);
+        }
+    }
+    
+    void onSuccess() {
+        alertManager.alert(Duration.TRANSIENT, Severity.SUCCESS, "Validation passed");
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/4a5b8fc6/tapestry-beanvalidator/src/test/java/org/example/testapp/services/AppModule.java
----------------------------------------------------------------------
diff --git a/tapestry-beanvalidator/src/test/java/org/example/testapp/services/AppModule.java b/tapestry-beanvalidator/src/test/java/org/example/testapp/services/AppModule.java
index 446d217..d8905b3 100644
--- a/tapestry-beanvalidator/src/test/java/org/example/testapp/services/AppModule.java
+++ b/tapestry-beanvalidator/src/test/java/org/example/testapp/services/AppModule.java
@@ -28,6 +28,7 @@ public class AppModule
     public static void contributeApplicationDefaults(MappedConfiguration<String, String> configuration)
     {
         configuration.add(SymbolConstants.PRODUCTION_MODE, "false");
+        configuration.add(SymbolConstants.HMAC_PASSPHRASE, "u93490jhsprf2904rh29-3uj");
     }
     
 	public static void contributeBeanValidatorSource(

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/4a5b8fc6/tapestry-beanvalidator/src/test/webapp/Index.tml
----------------------------------------------------------------------
diff --git a/tapestry-beanvalidator/src/test/webapp/Index.tml b/tapestry-beanvalidator/src/test/webapp/Index.tml
index 0970c96..6734533 100644
--- a/tapestry-beanvalidator/src/test/webapp/Index.tml
+++ b/tapestry-beanvalidator/src/test/webapp/Index.tml
@@ -27,6 +27,9 @@
             <li>
                 <t:pagelink page="ComplexBeanDemo">ComplexBean Demo</t:pagelink>
             </li>            
+            <li>
+                <t:pagelink page="NestedObjectDemo">NestedObject Demo</t:pagelink>
+            </li>            
         </ul>
     </body>
 </html>

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/4a5b8fc6/tapestry-beanvalidator/src/test/webapp/NestedObjectDemo.tml
----------------------------------------------------------------------
diff --git a/tapestry-beanvalidator/src/test/webapp/NestedObjectDemo.tml b/tapestry-beanvalidator/src/test/webapp/NestedObjectDemo.tml
new file mode 100644
index 0000000..cfe9371
--- /dev/null
+++ b/tapestry-beanvalidator/src/test/webapp/NestedObjectDemo.tml
@@ -0,0 +1,22 @@
+<html xmlns:t="http://tapestry.apache.org/schema/tapestry_5_1_0.xsd">
+    <body>
+    	<t:alerts/>
+        <t:form clientValidation="none" validate="complexBean">
+	    	<t:errors/>
+	    	<div class="t-beaneditor-row">
+		    	<t:label for="notNullString"/>
+		    	<t:textfield t:id="notNullString" t:validate="required"/>
+	    	</div>
+	    	<div class="t-beaneditor-row">
+		    	<t:label for="simpleNotNullProperty"/>
+	        	<t:textfield t:id="simpleNotNullProperty" t:value="complexBean.simpleNotNullProperty"/>
+        	</div>
+        	<div class="t-beaneditor-row">
+	        	<t:label for="minValue"/>
+	        	<t:textfield t:id="minValue" t:value="complexBean.otherSimpleBean.minValue"/>
+	        </div>
+<!-- 			<t:beaneditor object="complexBean"/> -->
+        	<input type="submit" id="submit" value="Go"/>
+        </t:form>
+     </body>
+</html>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/4a5b8fc6/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 4656fe2..0e7e097 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
@@ -35,14 +35,16 @@ public class PropBinding extends AbstractBinding implements InternalPropBinding
     private final String toString;
 
     private boolean invariant;
+    
+    private final String expression;
 
-    public PropBinding(final Location location, final Object root, final PropertyConduit conduit, final String toString
-    )
+    public PropBinding(final Location location, final Object root, final PropertyConduit conduit, final String expression, final String toString)
     {
         super(location);
 
         this.root = root;
         this.conduit = conduit;
+        this.expression = expression;
         this.toString = toString;
 
         invariant = conduit.getAnnotation(Invariant.class) != null;
@@ -109,4 +111,10 @@ public class PropBinding extends AbstractBinding implements InternalPropBinding
 	{
 		return TapestryInternalUtils.toInternalPropertyConduit(conduit).getPropertyName();
 	}
+
+    public String getExpression()
+    {
+        return expression;
+    }
+	
 }

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/4a5b8fc6/tapestry-core/src/main/java/org/apache/tapestry5/internal/bindings/PropBindingFactory.java
----------------------------------------------------------------------
diff --git a/tapestry-core/src/main/java/org/apache/tapestry5/internal/bindings/PropBindingFactory.java b/tapestry-core/src/main/java/org/apache/tapestry5/internal/bindings/PropBindingFactory.java
index 2865808..4f65bac 100644
--- a/tapestry-core/src/main/java/org/apache/tapestry5/internal/bindings/PropBindingFactory.java
+++ b/tapestry-core/src/main/java/org/apache/tapestry5/internal/bindings/PropBindingFactory.java
@@ -51,6 +51,6 @@ public class PropBindingFactory implements BindingFactory
         String toString = interner.format("PropBinding[%s %s(%s)]", description, container
                 .getCompleteId(), expression);
 
-        return new PropBinding(location, target, conduit, toString);
+        return new PropBinding(location, target, conduit, expression, toString);
     }
 }

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/4a5b8fc6/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 78cdd07..39defda 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
@@ -18,6 +18,7 @@ import org.apache.tapestry5.*;
 import org.apache.tapestry5.func.Worker;
 import org.apache.tapestry5.internal.InternalComponentResources;
 import org.apache.tapestry5.internal.bindings.InternalPropBinding;
+import org.apache.tapestry5.internal.bindings.PropBinding;
 import org.apache.tapestry5.internal.services.Instantiator;
 import org.apache.tapestry5.internal.transform.ParameterConduit;
 import org.apache.tapestry5.internal.util.NamedSet;
@@ -668,6 +669,12 @@ public class InternalComponentResourcesImpl extends LockSupport implements Inter
             return null;
         }
 
+        // TAP5-1718: we need the full prop binding expression, not just the (final) property name
+        if (binding instanceof PropBinding) 
+        {
+            return ((PropBinding) binding).getExpression();
+        }
+        
         if (binding instanceof InternalPropBinding)
         {
             return ((InternalPropBinding) binding).getPropertyName();