Repository: tapestry-5
Updated Branches:
  refs/heads/master ad5cca595 -> ef0d1448a


Fix bugs related to field validation inside a BeanEditor component


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

Branch: refs/heads/master
Commit: ef0d1448aca3bca3ed4f8aea6fbedd6b77438fcc
Parents: ad5cca5
Author: Howard M. Lewis Ship <hls...@apache.org>
Authored: Mon Feb 2 16:39:40 2015 -0800
Committer: Howard M. Lewis Ship <hls...@apache.org>
Committed: Mon Feb 2 16:39:40 2015 -0800

----------------------------------------------------------------------
 .../tapestry5/corelib/base/AbstractField.java   | 10 +++
 .../corelib/components/BeanEditor.java          | 20 +++--
 .../tapestry5/corelib/components/Form.java      |  4 +-
 .../corelib/pages/PropertyEditBlocks.java       | 83 +++++++++++++++-----
 .../internal/BeanValidationContext.java         | 22 ++++--
 .../internal/BeanValidationContextImpl.java     | 20 +++--
 6 files changed, 113 insertions(+), 46 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/ef0d1448/tapestry-core/src/main/java/org/apache/tapestry5/corelib/base/AbstractField.java
----------------------------------------------------------------------
diff --git 
a/tapestry-core/src/main/java/org/apache/tapestry5/corelib/base/AbstractField.java
 
b/tapestry-core/src/main/java/org/apache/tapestry5/corelib/base/AbstractField.java
index 851de73..4f977d0 100644
--- 
a/tapestry-core/src/main/java/org/apache/tapestry5/corelib/base/AbstractField.java
+++ 
b/tapestry-core/src/main/java/org/apache/tapestry5/corelib/base/AbstractField.java
@@ -364,6 +364,16 @@ public abstract class AbstractField implements Field2
         beanValidationContext.setCurrentProperty(null);
     }
 
+    /**
+     * The validation id is used by Tapestry to coordinate an incoming request 
(with input for fields,
+     * an validation errors) with a render of the form containing a field. 
Normally, a validationId is assigned
+     * on first access and persists for the remainder of the request; however 
the {@link org.apache.tapestry5.corelib.components.BeanEditor}
+     * (or rather, the components used by the BeanEditor) may need to override 
this as the same fields render and re-render
+     * multiple times in the same request.
+     *
+     * @since 5.4
+     */
+    @Parameter
     private String validationId;
 
     @Override

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/ef0d1448/tapestry-core/src/main/java/org/apache/tapestry5/corelib/components/BeanEditor.java
----------------------------------------------------------------------
diff --git 
a/tapestry-core/src/main/java/org/apache/tapestry5/corelib/components/BeanEditor.java
 
b/tapestry-core/src/main/java/org/apache/tapestry5/corelib/components/BeanEditor.java
index 03344fe..c44c9fa 100644
--- 
a/tapestry-core/src/main/java/org/apache/tapestry5/corelib/components/BeanEditor.java
+++ 
b/tapestry-core/src/main/java/org/apache/tapestry5/corelib/components/BeanEditor.java
@@ -1,5 +1,3 @@
-// Copyright 2007, 2008, 2010, 2011 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
@@ -35,13 +33,13 @@ import org.apache.tapestry5.services.BeanModelSource;
 import org.apache.tapestry5.services.Environment;
 import org.apache.tapestry5.services.FormSupport;
 
-import java.lang.annotation.Annotation;
+import java.util.UUID;
 
 /**
  * A component that generates a user interface for editing the properties of a 
bean. This is the central component of
  * the {@link BeanEditForm}, and utilizes a {@link PropertyEditor} for much of 
its functionality. This component places
  * a {@link BeanEditContext} into the environment.
- * 
+ *
  * @tapestrydoc
  */
 @SupportsInformalParameters
@@ -183,6 +181,8 @@ public class BeanEditor
         formSupport.storeAndExecute(this, CLEANUP_ENVIRONMENT);
     }
 
+    private String validationId;
+
     /**
      * Used to initialize the model if necessary, to instantiate the object 
being edited if necessary, and to push the
      * BeanEditContext into the environment.
@@ -206,8 +206,7 @@ public class BeanEditor
             try
             {
                 object = model.newInstance();
-            }
-            catch (Exception ex)
+            } catch (Exception ex)
             {
                 String message = String.format("Exception instantiating 
instance of %s (for component '%s'): %s",
                         PlasticUtils.toTypeName(model.getBeanType()), 
resources.getCompleteId(), ex);
@@ -215,13 +214,18 @@ public class BeanEditor
             }
         }
 
+        // Set to a new value on each request; the value lasts until the end 
of the request.
+        if (validationId == null) {
+            validationId = UUID.randomUUID().toString();
+        }
+
         BeanEditContext context = new BeanEditContextImpl(model.getBeanType());
 
         cachedObject = object;
 
         environment.push(BeanEditContext.class, context);
         // TAP5-2101: Always provide a new BeanValidationContext
-        environment.push(BeanValidationContext.class, new 
BeanValidationContextImpl(object));
+        environment.push(BeanValidationContext.class, new 
BeanValidationContextImpl(object, validationId));
     }
 
     void cleanupEnvironment()
@@ -232,7 +236,7 @@ public class BeanEditor
 
     // For testing
     void inject(ComponentResources resources, PropertyOverrides overrides, 
BeanModelSource source,
-            Environment environment)
+                Environment environment)
     {
         this.resources = resources;
         this.overrides = overrides;

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/ef0d1448/tapestry-core/src/main/java/org/apache/tapestry5/corelib/components/Form.java
----------------------------------------------------------------------
diff --git 
a/tapestry-core/src/main/java/org/apache/tapestry5/corelib/components/Form.java 
b/tapestry-core/src/main/java/org/apache/tapestry5/corelib/components/Form.java
index 78d5615..ea40106 100644
--- 
a/tapestry-core/src/main/java/org/apache/tapestry5/corelib/components/Form.java
+++ 
b/tapestry-core/src/main/java/org/apache/tapestry5/corelib/components/Form.java
@@ -353,7 +353,7 @@ public class Form implements ClientElement, 
FormValidationControl
         resources.triggerEvent(EventConstants.PREPARE, context, null);
 
         // Push BeanValidationContext only after the container had a chance to 
prepare
-        environment.push(BeanValidationContext.class, new 
BeanValidationContextImpl(validate));
+        environment.push(BeanValidationContext.class, new 
BeanValidationContextImpl(validate, null));
 
         // Save the form element for later, in case we want to write an 
encoding
         // type attribute.
@@ -509,7 +509,7 @@ public class Form implements ClientElement, 
FormValidationControl
                     return true;
             }
 
-            environment.push(BeanValidationContext.class, new 
BeanValidationContextImpl(validate));
+            environment.push(BeanValidationContext.class, new 
BeanValidationContextImpl(validate, null));
 
             didPushBeanValidationContext = true;
 

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/ef0d1448/tapestry-core/src/main/java/org/apache/tapestry5/corelib/pages/PropertyEditBlocks.java
----------------------------------------------------------------------
diff --git 
a/tapestry-core/src/main/java/org/apache/tapestry5/corelib/pages/PropertyEditBlocks.java
 
b/tapestry-core/src/main/java/org/apache/tapestry5/corelib/pages/PropertyEditBlocks.java
index 19326cc..2013319 100644
--- 
a/tapestry-core/src/main/java/org/apache/tapestry5/corelib/pages/PropertyEditBlocks.java
+++ 
b/tapestry-core/src/main/java/org/apache/tapestry5/corelib/pages/PropertyEditBlocks.java
@@ -19,6 +19,7 @@ import org.apache.tapestry5.ValueEncoder;
 import org.apache.tapestry5.annotations.Component;
 import org.apache.tapestry5.annotations.Environmental;
 import org.apache.tapestry5.corelib.components.*;
+import org.apache.tapestry5.internal.BeanValidationContext;
 import org.apache.tapestry5.ioc.annotations.Inject;
 import org.apache.tapestry5.ioc.services.TypeCoercer;
 import org.apache.tapestry5.services.BeanBlockContribution;
@@ -39,56 +40,96 @@ public class PropertyEditBlocks
     @Environmental
     private PropertyEditContext context;
 
+    @Environmental
+    private BeanValidationContext validationContext;
+
+    public String getValidationId()
+    {
+        return validationContext.getEditorValidationId() + "-" + 
context.getPropertyId();
+    }
+
     @Component(
-            parameters = {"value=context.propertyValue", 
"label=prop:context.label",
-                    "translate=prop:textFieldTranslator", 
"validate=prop:textFieldValidator",
-                    "clientId=prop:context.propertyId", 
"annotationProvider=context",
+            parameters = {"value=context.propertyValue",
+                    "label=prop:context.label",
+                    "translate=prop:textFieldTranslator",
+                    "validate=prop:textFieldValidator",
+                    "validationId=validationId",
+                    "clientId=prop:context.propertyId",
+                    "annotationProvider=context",
                     "ensureClientIdUnique=true"})
     private TextField textField;
 
     @Component(
-            parameters = {"value=context.propertyValue", 
"label=prop:context.label",
-                    "translate=prop:numberFieldTranslator", 
"validate=prop:numberFieldValidator",
-                    "clientId=prop:context.propertyId", 
"annotationProvider=context",
+            parameters = {"value=context.propertyValue",
+                    "label=prop:context.label",
+                    "translate=prop:numberFieldTranslator",
+                    "validate=prop:numberFieldValidator",
+                    "validationId=validationId",
+                    "clientId=prop:context.propertyId",
+                    "annotationProvider=context",
                     "ensureClientIdUnique=true"})
     private TextField numberField;
 
 
     @Component(
-            parameters = {"value=context.propertyValue", 
"label=prop:context.label", "encoder=valueEncoderForProperty",
-                    "model=selectModelForProperty", 
"validate=prop:selectValidator",
-                    "clientId=prop:context.propertyId", 
"ensureClientIdUnique=true"})
+            parameters = {"value=context.propertyValue",
+                    "label=prop:context.label",
+                    "encoder=valueEncoderForProperty",
+                    "validationId=validationId",
+                    "model=selectModelForProperty",
+                    "validate=prop:selectValidator",
+                    "clientId=prop:context.propertyId",
+                    "ensureClientIdUnique=true"})
     private Select select;
 
     @SuppressWarnings("unused")
     @Component(
-            parameters = {"value=context.propertyValue", 
"label=prop:context.label",
-                    "clientId=prop:context.propertyId", 
"ensureClientIdUnique=true"})
+            parameters = {"value=context.propertyValue",
+                    "label=prop:context.label",
+                    "validationId=validationId",
+                    "clientId=prop:context.propertyId",
+                    "ensureClientIdUnique=true"})
     private Checkbox checkboxField;
 
     @SuppressWarnings("unused")
     @Component(
-            parameters = {"value=context.propertyValue", 
"label=prop:context.label", "clientId=prop:context.propertyid",
-                    "validate=prop:dateFieldValidator", 
"ensureClientIdUnique=true"})
+            parameters = {"value=context.propertyValue",
+                    "label=prop:context.label",
+                    "clientId=prop:context.propertyid",
+                    "validate=prop:dateFieldValidator",
+                    "ensureClientIdUnique=true"})
     private DateField dateField;
 
     @SuppressWarnings("unused")
     @Component(
-            parameters = {"value=context.propertyValue", 
"label=prop:context.label", "clientId=prop:context.propertyid",
-                    "validate=prop:calendarFieldValidator", 
"ensureClientIdUnique=true"})
+            parameters = {"value=context.propertyValue",
+                    "label=prop:context.label",
+                    "clientId=prop:context.propertyid",
+                    "validate=prop:calendarFieldValidator",
+                    "validationId=validationId",
+                    "ensureClientIdUnique=true"})
     private DateField calendarField;
 
     @Component(
-            parameters = {"value=context.propertyValue", 
"label=prop:context.label",
-                    "translate=prop:passwordFieldTranslator", 
"validate=prop:passwordFieldValidator",
-                    "clientId=prop:context.propertyId", 
"annotationProvider=context", "ensureClientIdUnique=true"})
+            parameters = {"value=context.propertyValue",
+                    "label=prop:context.label",
+                    "translate=prop:passwordFieldTranslator",
+                    "validate=prop:passwordFieldValidator",
+                    "validationId=validationId",
+                    "clientId=prop:context.propertyId",
+                    "annotationProvider=context",
+                    "ensureClientIdUnique=true"})
     private PasswordField passwordField;
 
     @Component(
-            parameters = {"value=context.propertyValue", 
"label=prop:context.label",
+            parameters = {"value=context.propertyValue",
+                    "label=prop:context.label",
                     "translate=prop:textAreaTranslator",
-                    "validate=prop:textAreaValidator", 
"clientId=prop:context.propertyId",
-                    "annotationProvider=context", "ensureClientIdUnique=true"})
+                    "validationId=validationId",
+                    "validate=prop:textAreaValidator",
+                    "clientId=prop:context.propertyId",
+                    "annotationProvider=context",
+                    "ensureClientIdUnique=true"})
     private TextArea textArea;
 
 

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/ef0d1448/tapestry-core/src/main/java/org/apache/tapestry5/internal/BeanValidationContext.java
----------------------------------------------------------------------
diff --git 
a/tapestry-core/src/main/java/org/apache/tapestry5/internal/BeanValidationContext.java
 
b/tapestry-core/src/main/java/org/apache/tapestry5/internal/BeanValidationContext.java
index 29c0f85..707101d 100644
--- 
a/tapestry-core/src/main/java/org/apache/tapestry5/internal/BeanValidationContext.java
+++ 
b/tapestry-core/src/main/java/org/apache/tapestry5/internal/BeanValidationContext.java
@@ -1,5 +1,3 @@
-// Copyright 2009, 2010 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
@@ -15,7 +13,7 @@ package org.apache.tapestry5.internal;
 
 /**
  * Defines a context for validating beans.
- * 
+ *
  * @since 5.2.0
  */
 public interface BeanValidationContext
@@ -24,21 +22,29 @@ public interface BeanValidationContext
      * Returns the type of the object to validate. This method is needed for 
client side validation.
      */
     Class getBeanType();
-    
+
     /**
      * Return the object to validate.
      */
     Object getBeanInstance();
-    
+
     /**
      * Returns name of the property to validate. The current name is 
overwritten by every form field.
      */
     String getCurrentProperty();
-    
+
     /**
      * Sets name of the property to validate.
-     * 
-     * @param propertyName name of the property
+     *
+     * @param propertyName
+     *         name of the property
      */
     void setCurrentProperty(String propertyName);
+
+    /**
+     * Returns a validation id that should be used as a prefix for any fields 
inside a {@link org.apache.tapestry5.corelib.components.BeanEditor}.
+     *
+     * @since 5.4
+     */
+    String getEditorValidationId();
 }

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/ef0d1448/tapestry-core/src/main/java/org/apache/tapestry5/internal/BeanValidationContextImpl.java
----------------------------------------------------------------------
diff --git 
a/tapestry-core/src/main/java/org/apache/tapestry5/internal/BeanValidationContextImpl.java
 
b/tapestry-core/src/main/java/org/apache/tapestry5/internal/BeanValidationContextImpl.java
index c52d897..2f3d83c 100644
--- 
a/tapestry-core/src/main/java/org/apache/tapestry5/internal/BeanValidationContextImpl.java
+++ 
b/tapestry-core/src/main/java/org/apache/tapestry5/internal/BeanValidationContextImpl.java
@@ -1,5 +1,3 @@
-// Copyright 2010 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
@@ -16,17 +14,21 @@ package org.apache.tapestry5.internal;
 
 public class BeanValidationContextImpl implements BeanValidationContext
 {
-    private Object bean;
+    private final Object bean;
+
+    private final String editorValidationId;
+
     private String currentProperty;
 
-    public BeanValidationContextImpl(Object bean)
+    public BeanValidationContextImpl(Object bean, String editorValidationId)
     {
         this.bean = bean;
+        this.editorValidationId = editorValidationId;
     }
 
     public Class getBeanType()
     {
-        return bean==null?null:bean.getClass();
+        return bean == null ? null : bean.getClass();
     }
 
     public Object getBeanInstance()
@@ -34,14 +36,18 @@ public class BeanValidationContextImpl implements 
BeanValidationContext
         return bean;
     }
 
-    public String getCurrentProperty() 
+    public String getCurrentProperty()
     {
         return currentProperty;
     }
 
-    public void setCurrentProperty(String propertyName) 
+    public void setCurrentProperty(String propertyName)
     {
         this.currentProperty = propertyName;
     }
 
+    public String getEditorValidationId()
+    {
+        return editorValidationId;
+    }
 }

Reply via email to