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/09/22 18:46:38 UTC

svn commit: r578461 - in /tapestry/tapestry5/trunk/tapestry-core/src: main/java/org/apache/tapestry/corelib/components/ main/java/org/apache/tapestry/internal/services/ main/java/org/apache/tapestry/services/ main/resources/org/apache/tapestry/corelib/...

Author: hlship
Date: Sat Sep 22 09:46:36 2007
New Revision: 578461

URL: http://svn.apache.org/viewvc?rev=578461&view=rev
Log:
TAPESTRY-1579: Allow multiple BeanEditor components within a form

Added:
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/corelib/components/BeanEditor.java
    tapestry/tapestry5/trunk/tapestry-core/src/main/resources/org/apache/tapestry/corelib/components/BeanEditor.html
    tapestry/tapestry5/trunk/tapestry-core/src/test/app1/WEB-INF/MultiBeanDemoResult.html
    tapestry/tapestry5/trunk/tapestry-core/src/test/app1/WEB-INF/MultiBeanEditDemo.html
    tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/corelib/components/BeanEditorTest.java
    tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/integration/app1/data/RoleAccess.java
    tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/integration/app1/data/RolePath.java
    tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/integration/app1/data/UserCredentials.java
    tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/integration/app1/pages/MultiBeanDemoResult.java
    tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/integration/app1/pages/MultiBeanEditDemo.java
Modified:
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/corelib/components/BeanEditForm.java
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/InternalModule.java
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/services/BeanModelSource.java
    tapestry/tapestry5/trunk/tapestry-core/src/main/resources/org/apache/tapestry/corelib/components/BeanEditForm.html
    tapestry/tapestry5/trunk/tapestry-core/src/site/apt/index.apt
    tapestry/tapestry5/trunk/tapestry-core/src/test/app1/WEB-INF/Start.html
    tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/corelib/components/BeanEditFormTest.java
    tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/integration/IntegrationTests.java
    tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/integration/app1/data/RegistrationData.java

Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/corelib/components/BeanEditForm.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/corelib/components/BeanEditForm.java?rev=578461&r1=578460&r2=578461&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/corelib/components/BeanEditForm.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/corelib/components/BeanEditForm.java Sat Sep 22 09:46:36 2007
@@ -24,7 +24,6 @@
 import org.apache.tapestry.annotations.Parameter;
 import org.apache.tapestry.annotations.SupportsInformalParameters;
 import org.apache.tapestry.beaneditor.BeanModel;
-import org.apache.tapestry.internal.beaneditor.BeanModelUtils;
 import org.apache.tapestry.ioc.internal.util.TapestryException;
 import org.apache.tapestry.services.BeanModelSource;
 import org.apache.tapestry.services.ComponentDefaultProvider;
@@ -62,6 +61,7 @@
      * for a "prepare" event, in order to ensure that a non-null value is ready to be read or
      * updated.
      */
+    @SuppressWarnings("unused")
     @Parameter(required = true)
     private Object _object;
 
@@ -69,7 +69,8 @@
      * A comma-separated list of property names to be removed from the {@link BeanModel}. The names
      * are case-insensitive.
      */
-    @Parameter(defaultPrefix="literal")
+    @SuppressWarnings("unused")
+    @Parameter(defaultPrefix = "literal")
     private String _remove;
 
     /**
@@ -77,7 +78,8 @@
      * be presented. The names are case insensitive. Any properties not indicated in the list will
      * be appended to the end of the display order.
      */
-    @Parameter(defaultPrefix="literal")
+    @SuppressWarnings("unused")
+    @Parameter(defaultPrefix = "literal")
     private String _reorder;
 
     /** If true, the default, then the embedded Form component will use client-side validation. */
@@ -85,12 +87,6 @@
     @Parameter
     private boolean _clientValidation;
 
-    @Inject
-    private ComponentResources _resources;
-
-    @Inject
-    private BeanModelSource _modelSource;
-
     @Component(parameters = "clientValidation=inherit:clientValidation")
     private Form _form;
 
@@ -99,16 +95,16 @@
      * If not specified, a default bean model will be created from the type of the object bound to
      * the object parameter.
      */
+    @SuppressWarnings("unused")
     @Parameter
     private BeanModel _model;
 
-    // Values that change with each change to the current property:
-
-    private String _propertyName;
-
     @Inject
     private ComponentDefaultProvider _defaultProvider;
 
+    @Inject
+    private ComponentResources _resources;
+
     /**
      * Defaults the object parameter to a property of the container matching the BeanEditForm's id.
      */
@@ -117,67 +113,16 @@
         return _defaultProvider.defaultBinding("object", _resources);
     }
 
-    public BeanModel getModel()
-    {
-        return _model;
-    }
-
-    public String getPropertyName()
-    {
-        return _propertyName;
-    }
-
-    public void setPropertyName(String propertyName)
-    {
-        _propertyName = propertyName;
-
-    }
-
-    boolean onPrepareFromForm()
+    void onPrepareFromForm()
     {
         // Fire a new prepare event to be consumed by the container. This is the container's
         // chance to ensure that there's an object to edit.
 
         _resources.triggerEvent(Form.PREPARE, null, null);
 
-        // Now check to see if the value is null.
-
-        // The only problem here is that if the bound property is backed by a persistent field, it
-        // is assigned (and stored to the session, and propagated around the cluster) first,
-        // before values are assigned.
-
         if (_object == null) _object = createDefaultObject();
 
         assert _object != null;
-
-        if (_model == null)
-        {
-            Class<? extends Object> beanType = _object.getClass();
-
-            _model = _modelSource.create(beanType, true, _resources.getContainerResources());
-        }
-        
-        if (_remove != null)
-            BeanModelUtils.remove(_model, _remove);
-        
-        if (_reorder != null)
-            BeanModelUtils.reorder(_model, _reorder);
-
-        // Abort the form's prepare event, as we've already sent a prepare on its behalf.
-        return true;
-    }
-
-    /** Used for testing. */
-    void inject(ComponentResources resources, BeanModelSource modelSource)
-    {
-        _resources = resources;
-        _modelSource = modelSource;
-    }
-
-    /** Returns the object being edited. */
-    public Object getObject()
-    {
-        return _object;
     }
 
     private Object createDefaultObject()
@@ -197,6 +142,11 @@
         }
     }
 
+    public Object getObject()
+    {
+        return _object;
+    }
+
     /** Returns the client id of the embedded form. */
     public String getClientId()
     {
@@ -231,6 +181,11 @@
     public void recordError(String errorMessage)
     {
         _form.recordError(errorMessage);
+    }
+
+    void inject(ComponentResources resources)
+    {
+        _resources = resources;
     }
 
 }

Added: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/corelib/components/BeanEditor.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/corelib/components/BeanEditor.java?rev=578461&view=auto
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/corelib/components/BeanEditor.java (added)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/corelib/components/BeanEditor.java Sat Sep 22 09:46:36 2007
@@ -0,0 +1,189 @@
+// Copyright 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.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package org.apache.tapestry.corelib.components;
+
+import org.apache.tapestry.Binding;
+import org.apache.tapestry.ComponentAction;
+import org.apache.tapestry.ComponentResources;
+import org.apache.tapestry.annotations.Environmental;
+import org.apache.tapestry.annotations.Inject;
+import org.apache.tapestry.annotations.Parameter;
+import org.apache.tapestry.beaneditor.BeanModel;
+import org.apache.tapestry.internal.beaneditor.BeanModelUtils;
+import org.apache.tapestry.ioc.internal.util.TapestryException;
+import org.apache.tapestry.services.BeanModelSource;
+import org.apache.tapestry.services.ComponentDefaultProvider;
+import org.apache.tapestry.services.FormSupport;
+
+/**
+ * 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.
+ */
+public class BeanEditor
+{
+    public static class Prepare implements ComponentAction<BeanEditor>
+    {
+        private static final long serialVersionUID = 6273600092955522585L;
+
+        public void execute(BeanEditor component)
+        {
+            component.doPrepare();
+        }
+    };
+
+    /**
+     * The object to be edited by the BeanEditor. This will be read when the component renders and
+     * updated when the form for the component is submitted. Typically, the container will listen
+     * for a "prepare" event, in order to ensure that a non-null value is ready to be read or
+     * updated.
+     */
+    @Parameter
+    private Object _object;
+
+    /**
+     * A comma-separated list of property names to be removed from the {@link BeanModel}. The names
+     * are case-insensitive.
+     */
+    @Parameter(defaultPrefix = "literal")
+    private String _remove;
+
+    /**
+     * A comma-separated list of property names indicating the order in which the properties should
+     * be presented. The names are case insensitive. Any properties not indicated in the list will
+     * be appended to the end of the display order.
+     */
+    @Parameter(defaultPrefix = "literal")
+    private String _reorder;
+
+    /**
+     * The model that identifies the parameters to be edited, their order, and every other aspect.
+     * If not specified, a default bean model will be created from the type of the object bound to
+     * the object parameter.
+     */
+    @Parameter
+    private BeanModel _model;
+
+    /**
+     * Where to search for local overrides of property editing blocks as block parameters. Further,
+     * the container of the overrides is used as the source for overridden validation messages. This
+     * is normally the component itself, but when the component is used within a BeanEditForm, it
+     * will be the BeanEditForm's block parameter that will be searched.
+     */
+    @Parameter(value = "componentResources")
+    private ComponentResources _overrides;
+
+    @Inject
+    private BeanModelSource _modelSource;
+
+    @Inject
+    private ComponentDefaultProvider _defaultProvider;
+
+    @Inject
+    private ComponentResources _resources;
+
+    @Environmental
+    private FormSupport _formSupport;
+
+    // Value that change with each change to the current property:
+
+    private String _propertyName;
+
+    /**
+     * Defaults the object parameter to a property of the container matching the BeanEditForm's id.
+     */
+    Binding defaultObject()
+    {
+        return _defaultProvider.defaultBinding("object", _resources);
+    }
+
+    public String getPropertyName()
+    {
+        return _propertyName;
+    }
+
+    public void setPropertyName(String propertyName)
+    {
+        _propertyName = propertyName;
+    }
+
+    /** Returns the object being edited. */
+    public Object getObject()
+    {
+        return _object;
+    }
+
+    public ComponentResources getOverrides()
+    {
+        return _overrides;
+    }
+
+    public BeanModel getModel()
+    {
+        return _model;
+    }
+
+    void setupRender()
+    {
+        _formSupport.storeAndExecute(this, new Prepare());
+    }
+
+    void doPrepare()
+    {
+        // The only problem here is that if the bound property is backed by a persistent field, it
+        // is assigned (and stored to the session, and propagated around the cluster) first,
+        // before values are assigned.
+
+        if (_object == null) _object = createDefaultObject();
+
+        assert _object != null;
+
+        if (_model == null)
+        {
+            Class<? extends Object> beanType = _object.getClass();
+
+            _model = _modelSource.create(beanType, true, _overrides.getContainerResources());
+        }
+
+        if (_remove != null) BeanModelUtils.remove(_model, _remove);
+
+        if (_reorder != null) BeanModelUtils.reorder(_model, _reorder);
+    }
+
+    private Object createDefaultObject()
+    {
+        Class type = _resources.getBoundType("object");
+
+        try
+        {
+            return type.newInstance();
+        }
+        catch (Exception ex)
+        {
+            throw new TapestryException(ComponentMessages.failureInstantiatingObject(
+                    type,
+                    _resources.getCompleteId(),
+                    ex), _resources.getLocation(), ex);
+        }
+    }
+
+    // For testing
+    void inject(ComponentResources resources, ComponentResources overrides, BeanModelSource source)
+    {
+        _resources = resources;
+        _overrides = overrides;
+        _modelSource = source;
+    }
+}

Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/InternalModule.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/InternalModule.java?rev=578461&r1=578460&r2=578461&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/InternalModule.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/InternalModule.java Sat Sep 22 09:46:36 2007
@@ -123,7 +123,7 @@
         // This is designed to make it easy to keep synchronized with script.aculo.ous. As we
         // support a new version, we create a new folder, and update the path entry. We can then
         // delete the old version folder (or keep it around). This should be more manageable than
-        // ovewriting the local copy with updates. There's also a ClasspathAliasManager
+        // overwriting the local copy with updates. There's also a ClasspathAliasManager
         // contribution based on the path.
 
         configuration.add("tapestry.scriptaculous", "classpath:${tapestry.scriptaculous.path}");

Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/services/BeanModelSource.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/services/BeanModelSource.java?rev=578461&r1=578460&r2=578461&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/services/BeanModelSource.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/services/BeanModelSource.java Sat Sep 22 09:46:36 2007
@@ -25,7 +25,7 @@
 public interface BeanModelSource
 {
     /**
-     * Creates a new model used for editting the indicated bean class. The model will represent all
+     * Creates a new model used for editing the indicated bean class. The model will represent all
      * read/write properties of the bean. The order of the properties is defined by the
      * {@link OrderBefore} annotation on the getter or setter methods. The labels for the properties are
      * derived from the property names, but if the component's message catalog has keys of the form
@@ -35,7 +35,7 @@
      * time.
      * 
      * @param beanClass
-     *            class of object to be editted
+     *            class of object to be edited
      * @param filterReadOnlyProperties
      *            if true, then properties that are read-only will be skipped (leaving only
      *            read-write properties). If false, then both read-only and read-write properties

Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/resources/org/apache/tapestry/corelib/components/BeanEditForm.html
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/resources/org/apache/tapestry/corelib/components/BeanEditForm.html?rev=578461&r1=578460&r2=578461&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/resources/org/apache/tapestry/corelib/components/BeanEditForm.html (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/resources/org/apache/tapestry/corelib/components/BeanEditForm.html Sat Sep 22 09:46:36 2007
@@ -1,14 +1,15 @@
-<form t:id="form" xmlns:t="http://tapestry.apache.org/schema/tapestry_5_0_0.xsd">
-    <t:errors/>
+<form t:id="form"
+  xmlns:t="http://tapestry.apache.org/schema/tapestry_5_0_0.xsd">
+  <t:errors />
 
-    <div class="t-beaneditor">
-        <div class="t-beaneditor-row" t:type="loop" t:source="model.propertyNames" t:volatile="true"
-            t:value="propertyName">
-            <t:propertyEditor property="propertyName" object="object" model="model" overrides="this"/>
-        </div>
-        <div class="t-beaneditor-row">
-            <input type="submit" value="${submitLabel}"/>
-        </div>
+  <div class="t-beaneditor">
+
+    <t:beaneditor object="object" remove="inherit:remove"
+      reorder="inherit:reorder" model="inherit:model" overrides="this" />
+
+    <div class="t-beaneditor-row">
+      <input type="submit" value="${submitLabel}" />
     </div>
+  </div>
 
 </form>

Added: tapestry/tapestry5/trunk/tapestry-core/src/main/resources/org/apache/tapestry/corelib/components/BeanEditor.html
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/resources/org/apache/tapestry/corelib/components/BeanEditor.html?rev=578461&view=auto
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/resources/org/apache/tapestry/corelib/components/BeanEditor.html (added)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/resources/org/apache/tapestry/corelib/components/BeanEditor.html Sat Sep 22 09:46:36 2007
@@ -0,0 +1,6 @@
+<div xmlns:t="http://tapestry.apache.org/schema/tapestry_5_0_0.xsd"
+  class="t-beaneditor-row" t:type="loop" t:source="model.propertyNames"
+  t:volatile="true" t:value="propertyName">
+  <t:propertyEditor property="propertyName" object="object"
+    model="model" overrides="overrides" />
+</div>
\ No newline at end of file

Modified: tapestry/tapestry5/trunk/tapestry-core/src/site/apt/index.apt
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/site/apt/index.apt?rev=578461&r1=578460&r2=578461&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/site/apt/index.apt (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/site/apt/index.apt Sat Sep 22 09:46:36 2007
@@ -13,6 +13,8 @@
   Progress on Tapestry 5 is really taking off. This space lists some cool new features that have been added
   recently.
   
+  * The core of BeanEditForm has been factored out as a new component, BeanEditor (this allows you to edit multiple beans within the same form).
+  
   * Some of the annotations that aren't used specifically with component classes have been split off into the new
     {{{../tapestry-annotations/}tapestry-annotations}} module.
   
@@ -38,7 +40,7 @@
   * Explicit \<!DOCTYPE\> declarations inside page and component templates will now be forwarded through to the client
     web browser.
     
-  * The mapping from class names to page names (or component types) has been tweaked to remove some redunancy;
+  * The mapping from class names to page names (or component types) has been tweaked to remove some redundancy;
     For example,
     class org.example.myapp.pages.edit.EditUser will now have the name "edit/User" rather than "edit/EditUser". This
     results in shorter, clearer, more natural URLs.
@@ -50,7 +52,7 @@
     built on {{{http://script.taculo.us}script.taculo.us}}.
   
   * The default ExceptionReport page has been improved to show details of the incoming request,
-    to filter out repetative and unnecessary data, 
+    to filter out repetitive and unnecessary data, 
     and to display file content when an exception includes a location.
   
   * Action request URLs have been shortened and simplified for the common case. They may also include a query
@@ -86,7 +88,7 @@
   
   * Initial support for {{{guide/appstate.html}application state objects}}.
   
-  * Input validation messages may now be overriden by providing a particular message key in the containing component's message catalog.
+  * Input validation messages may now be overridden by providing a particular message key in the containing component's message catalog.
   
   * Property expressions may now reference public methods (with no parameters) in addition to traditional property names.
   
@@ -98,7 +100,7 @@
 
   * Component parameters may have default values.
   
-  * The @ComponentClass anntotation, seen in the earlier {{{../screencast.html}screencasts}} has been removed.
+  * The @ComponentClass annotation, seen in the earlier {{{../screencast.html}screencasts}} has been removed.
   
 Changes from Tapestry 4 to Tapestry 5
 
@@ -204,8 +206,8 @@
   into the method by adding parameters to the method. Again, Tapestry will adapt
   to your parameters, in whatever order you supply them.
   
-  Finally, Tapestry 5 explicitly seperates actions (requests that change things) and rendering (requests that
-  render pages) into two seperate requests.  Performing an action, such as clicking a link or submitting a form,
+  Finally, Tapestry 5 explicitly separates actions (requests that change things) and rendering (requests that
+  render pages) into two separate requests.  Performing an action, such as clicking a link or submitting a form,
   results in a <client side redirect> to the new page. This is often called "redirect after post". This helps ensure
   that URLs in the browser are book-markable ... but also requires that a bit more information be stored in the session
   between requests (using the @Persist annotation).  

Added: tapestry/tapestry5/trunk/tapestry-core/src/test/app1/WEB-INF/MultiBeanDemoResult.html
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/test/app1/WEB-INF/MultiBeanDemoResult.html?rev=578461&view=auto
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/test/app1/WEB-INF/MultiBeanDemoResult.html (added)
+++ tapestry/tapestry5/trunk/tapestry-core/src/test/app1/WEB-INF/MultiBeanDemoResult.html Sat Sep 22 09:46:36 2007
@@ -0,0 +1,13 @@
+<html t:type="Border"
+  xmlns:t="http://tapestry.apache.org/schema/tapestry_5_0_0.xsd">
+
+  <h1>MultiBean Edit Demo</h1>
+
+  <ul>
+    <li>First Name: [${credentials.firstName}]</li>
+    <li>Last Name: [${credentials.lastName}]</li>
+    <li>Path: [${rolePath.path}]</li>
+    <li>Role: [${rolePath.role}]</li>
+  </ul>
+
+</html>
\ No newline at end of file

Added: tapestry/tapestry5/trunk/tapestry-core/src/test/app1/WEB-INF/MultiBeanEditDemo.html
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/test/app1/WEB-INF/MultiBeanEditDemo.html?rev=578461&view=auto
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/test/app1/WEB-INF/MultiBeanEditDemo.html (added)
+++ tapestry/tapestry5/trunk/tapestry-core/src/test/app1/WEB-INF/MultiBeanEditDemo.html Sat Sep 22 09:46:36 2007
@@ -0,0 +1,32 @@
+<html t:type="Border"
+  xmlns:t="http://tapestry.apache.org/schema/tapestry_5_0_0.xsd">
+
+  <h1>MultiBean Edit</h1>
+
+  <t:form>
+
+    <t:errors />
+
+    <div class="t-beaneditor">
+
+      <!--  Demonstrate default object binding from component id -->
+      <t:beaneditor t:id="credentials" />
+      <t:beaneditor t:id="rolePath" />
+
+      <div class="t-beaneditor-row">
+        <input type="submit" value="Set Access" />
+      </div>
+
+
+    </div>
+
+
+  </t:form>
+
+  <p>
+    [
+    <t:actionlink t:id="clear">Clear Data</t:actionlink>
+    ]
+  </p>
+
+</html>
\ No newline at end of file

Modified: tapestry/tapestry5/trunk/tapestry-core/src/test/app1/WEB-INF/Start.html
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/test/app1/WEB-INF/Start.html?rev=578461&r1=578460&r2=578461&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/test/app1/WEB-INF/Start.html (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/test/app1/WEB-INF/Start.html Sat Sep 22 09:46:36 2007
@@ -244,6 +244,12 @@
             -- Use of the remove and reorder parameters with
             BeanEditForm
           </li>
+          <li>
+            <t:pagelink page="MultiBeanEditDemo">
+              MultiBeanEdit Demo
+            </t:pagelink>
+            -- Multiple BeanEditor components in a single form
+          </li>
         </ul>
       </td>
     </tr>

Modified: tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/corelib/components/BeanEditFormTest.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/corelib/components/BeanEditFormTest.java?rev=578461&r1=578460&r2=578461&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/corelib/components/BeanEditFormTest.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/corelib/components/BeanEditFormTest.java Sat Sep 22 09:46:36 2007
@@ -14,16 +14,10 @@
 
 package org.apache.tapestry.corelib.components;
 
-import static org.easymock.EasyMock.eq;
-import static org.easymock.EasyMock.isA;
-
 import org.apache.tapestry.ComponentResources;
-import org.apache.tapestry.beaneditor.BeanModel;
 import org.apache.tapestry.integration.app1.data.RegistrationData;
 import org.apache.tapestry.ioc.Location;
 import org.apache.tapestry.ioc.internal.util.TapestryException;
-import org.apache.tapestry.services.BeanModelSource;
-import org.apache.tapestry.services.Environment;
 import org.apache.tapestry.test.TapestryTestCase;
 import org.testng.annotations.Test;
 
@@ -33,25 +27,18 @@
     public void object_created_as_needed()
     {
         ComponentResources resources = mockComponentResources();
-        ComponentResources containerResources = mockComponentResources();
-        BeanModelSource source = mockBeanModelSource();
-        BeanModel model = mockBeanModel();
 
         expect(resources.triggerEvent(Form.PREPARE, null, null)).andReturn(false);
 
         train_getBoundType(resources, RegistrationData.class);
 
-        train_getContainerResources(resources, containerResources);
-
-        train_create(source, RegistrationData.class, true, containerResources, model);
-
         replay();
 
         BeanEditForm component = new BeanEditForm();
 
-        component.inject(resources, source);
+        component.inject(resources);
 
-        assertTrue(component.onPrepareFromForm());
+        component.onPrepareFromForm();
 
         Object object = component.getObject();
 
@@ -79,7 +66,7 @@
 
         BeanEditForm component = new BeanEditForm();
 
-        component.inject(resources, null);
+        component.inject(resources);
 
         try
         {
@@ -95,10 +82,5 @@
         }
 
         verify();
-    }
-
-    protected final <T> void train_push(Environment environment, Class<T> type)
-    {
-        expect(environment.push(eq(type), isA(type))).andReturn(null);
     }
 }

Added: tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/corelib/components/BeanEditorTest.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/corelib/components/BeanEditorTest.java?rev=578461&view=auto
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/corelib/components/BeanEditorTest.java (added)
+++ tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/corelib/components/BeanEditorTest.java Sat Sep 22 09:46:36 2007
@@ -0,0 +1,92 @@
+// Copyright 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.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package org.apache.tapestry.corelib.components;
+
+import org.apache.tapestry.ComponentResources;
+import org.apache.tapestry.beaneditor.BeanModel;
+import org.apache.tapestry.integration.app1.data.RegistrationData;
+import org.apache.tapestry.ioc.Location;
+import org.apache.tapestry.ioc.internal.util.TapestryException;
+import org.apache.tapestry.services.BeanModelSource;
+import org.apache.tapestry.test.TapestryTestCase;
+import org.testng.annotations.Test;
+
+public class BeanEditorTest extends TapestryTestCase
+{
+    @Test
+    public void object_created_as_needed()
+    {
+        ComponentResources resources = mockComponentResources();
+        ComponentResources overrides = mockComponentResources();
+        ComponentResources containerResources = mockComponentResources();
+        BeanModelSource source = mockBeanModelSource();
+        BeanModel model = mockBeanModel();
+
+        train_getBoundType(resources, RegistrationData.class);
+
+        train_getContainerResources(overrides, containerResources);
+
+        train_create(source, RegistrationData.class, true, containerResources, model);
+
+        replay();
+
+        BeanEditor component = new BeanEditor();
+
+        component.inject(resources, overrides, source);
+
+        component.doPrepare();
+
+        Object object = component.getObject();
+
+        assertNotNull(object);
+        assertSame(object.getClass(), RegistrationData.class);
+
+        verify();
+    }
+
+    @Test
+    public void object_can_not_be_instantiated()
+    {
+        ComponentResources resources = mockComponentResources();
+        Location l = mockLocation();
+
+        train_getBoundType(resources, Runnable.class);
+
+        train_getCompleteId(resources, "Foo.bar");
+
+        train_getLocation(resources, l);
+
+        replay();
+
+        BeanEditor component = new BeanEditor();
+
+        component.inject(resources, null, null);
+
+        try
+        {
+            component.doPrepare();
+            unreachable();
+        }
+        catch (TapestryException ex)
+        {
+            assertMessageContains(
+                    ex,
+                    "Exception instantiating instance of java.lang.Runnable (for component \'Foo.bar\'):");
+            assertSame(ex.getLocation(), l);
+        }
+
+        verify();
+    }
+}

Modified: tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/integration/IntegrationTests.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/integration/IntegrationTests.java?rev=578461&r1=578460&r2=578461&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/integration/IntegrationTests.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/integration/IntegrationTests.java Sat Sep 22 09:46:36 2007
@@ -42,14 +42,13 @@
     @Test
     public void assets() throws Exception
     {
-        open(BASE_URL);
-        clickAndWait("link=AssetDemo");
+        start("AssetDemo");
 
         assertText("//img[@id='icon']/@src", "/images/tapestry_banner.gif");
 
-        // This doesn't prove that the image shows up in the client browser (it does, but
+        // doesn't prove that the image shows up in the client browser (it does, but
         // it could just as easily be a broken image). Haven't figured out how Selenium
-        // allows this to be verified. Note that the path below represents some aliasing
+        // allows to be verified. Note that the path below represents some aliasing
         // of the raw classpath resource path.
 
         assertText("//img[@id='button']/@src", "/assets/app1/pages/tapestry-button.png");
@@ -72,12 +71,10 @@
     public void basic_parameters() throws Exception
     {
 
-        // OK ... this could be a separate test, but for efficiency, we'll mix it in here.
+        // OK ... could be a separate test, but for efficiency, we'll mix it in here.
         // It takes a while to start up Selenium RC (and a Firefox browser).
 
-        open(BASE_URL);
-
-        clickAndWait("link=Count Page");
+        start("Count Page");
 
         assertTextPresent("Ho! Ho! Ho!");
     }
@@ -88,8 +85,7 @@
     @Test
     public void block_rendering() throws Exception
     {
-        open(BASE_URL);
-        clickAndWait("link=BlockDemo");
+        start("BlockDemo");
 
         assertTextPresent("[]");
 
@@ -111,8 +107,7 @@
     @Test
     public void component_parameter_default_from_method() throws Exception
     {
-        open(BASE_URL);
-        clickAndWait("link=ParameterDefault");
+        start("ParameterDefault");
 
         assertTextPresent("Echo component default: [ParameterDefault:echo]");
     }
@@ -120,9 +115,7 @@
     @Test
     public void embedded_components()
     {
-        open(BASE_URL);
-
-        clickAndWait("link=Countdown Page");
+        start("Countdown Page");
 
         assertTextPresent("regexp:\\s+5\\s+4\\s+3\\s+2\\s+1\\s+");
 
@@ -138,9 +131,7 @@
     @Test
     public void environmental()
     {
-        open(BASE_URL);
-
-        clickAndWait("link=Environmental Annotation Usage");
+        start("Environmental Annotation Usage");
 
         assertSourcePresent("[<strong>A message provided by the RenderableProvider component.</strong>]");
     }
@@ -148,9 +139,7 @@
     @Test
     public void exception_report()
     {
-        open(BASE_URL);
-
-        clickAndWait("link=BadTemplate Page");
+        start("BadTemplate Page");
 
         assertTextPresent(
                 "org.apache.tapestry.ioc.internal.util.TapestryException",
@@ -162,23 +151,20 @@
     @Test
     public void expansion()
     {
-        open(BASE_URL);
-
-        clickAndWait("link=Expansion Page");
+        start("Expansion Page");
 
         assertTextPresent("[value provided by a template expansion]");
     }
 
     /**
      * {@link InjectComponentWorker} is largely tested by the forms tests ({@link RenderDisabled}
-     * is built on it). This test is for the failure case, where a mixin class is used with the
-     * wrong type of component.
+     * is built on it). test is for the failure case, where a mixin class is used with the wrong
+     * type of component.
      */
     @Test
     public void inject_component_failure() throws Exception
     {
-        open(BASE_URL);
-        clickAndWait("link=InjectComponentMismatch");
+        start("InjectComponentMismatch");
 
         // And exception message:
 
@@ -188,14 +174,12 @@
     @Test
     public void injection() throws Exception
     {
-        open(BASE_URL);
+        start("Inject Demo");
 
-        clickAndWait("link=Inject Demo");
-
-        // This is a test for a named @Inject:
+        // is a test for a named @Inject:
         assertTextPresent("<Proxy for Request(org.apache.tapestry.services.Request)>");
 
-        // This is a test for an anonymous @Inject and ComponentResourcesInjectionProvider
+        // is a test for an anonymous @Inject and ComponentResourcesInjectionProvider
         assertTextPresent("ComponentResources[InjectDemo]");
 
         // Another test, DefaultInjectionProvider
@@ -205,13 +189,11 @@
     @Test
     public void instance_mixin()
     {
-        open(BASE_URL);
+        start("InstanceMixin");
 
         final String[] dates =
         { "Jun 13, 1999", "Jul 15, 2001", "Dec 4, 2005" };
 
-        clickAndWait("link=InstanceMixin");
-
         for (String date : dates)
         {
             String snippet = String.format("[%s]", date);
@@ -231,8 +213,7 @@
     @Test
     public void localization()
     {
-        open(BASE_URL);
-        clickAndWait("link=Localization");
+        start("Localization");
 
         assertTextPresent("Via injected Messages property: [Accessed via injected Messages]");
         assertTextPresent("Via message: binding prefix: [Accessed via message: binding prefix]");
@@ -247,9 +228,7 @@
     @Test
     public void page_injection() throws Exception
     {
-        open(BASE_URL);
-
-        clickAndWait("link=Inject Demo");
+        start("Inject Demo");
 
         clickAndWait("link=Fred");
 
@@ -268,18 +247,17 @@
     @Test
     public void passivate_activate() throws Exception
     {
-        open(BASE_URL);
-        clickAndWait("link=NumberSelect");
+        start("NumberSelect");
+
         clickAndWait("link=5");
+
         assertTextPresent("You chose 5.");
     }
 
     @Test
     public void password_field()
     {
-        open(BASE_URL);
-
-        clickAndWait("link=PasswordFieldDemo");
+        start("PasswordFieldDemo");
 
         type("userName", "howard");
         type("password", "wrong-password");
@@ -303,8 +281,7 @@
     @Test
     public void render_phase_method_returns_a_component() throws Exception
     {
-        open(BASE_URL);
-        clickAndWait("link=RenderComponentDemo");
+        start("RenderComponentDemo");
 
         assertText("//span[@id='container']", "[]");
 
@@ -326,9 +303,7 @@
     @Test
     public void render_phase_order()
     {
-        open(BASE_URL);
-
-        clickAndWait("link=RenderPhaseOrder");
+        start("RenderPhaseOrder");
 
         assertTextPresent("[BEGIN-TRACER-MIXIN BEGIN-ABSTRACT-TRACER BEGIN-TRACER BODY AFTER-TRACER AFTER-ABSTRACT-TRACER AFTER-TRACER-MIXIN]");
     }
@@ -336,11 +311,11 @@
     @Test
     public void server_side_validation_for_textfield_and_textarea() throws Exception
     {
-        open(BASE_URL);
-        clickAndWait("link=ValidForm");
+        start("ValidForm");
+
         clickAndWait("//input[@type='submit']");
         assertTextPresent("You must provide a value for Email.");
-        // This is an overrdden validation error message:
+        // is an overridden validation error message:
         assertTextPresent("Please provide a detailed description of the incident.");
 
         // Check on decorations via the default validation decorator:
@@ -385,9 +360,7 @@
     {
         final String YOU_CHOSE = "You chose: ";
 
-        open(BASE_URL);
-
-        clickAndWait("link=Action Page");
+        start("Action Page");
 
         assertFalse(isTextPresent(YOU_CHOSE));
 
@@ -400,15 +373,13 @@
     }
 
     /**
-     * Tests for forms and form submissions and basic form control components. This also tests a few
+     * Tests for forms and form submissions and basic form control components. also tests a few
      * other things, such as computed default bindings and invisible instrumentation.
      */
     @Test
     public void simple_form()
     {
-        open(BASE_URL);
-
-        clickAndWait("link=SimpleForm");
+        start("SimpleForm");
 
         assertText("//label[@id='disabled:label']", "Disabled");
         assertText("//label[@id='email:label']", "Email");
@@ -450,9 +421,7 @@
     @Test
     public void subclass_inherits_parent_template()
     {
-        open(BASE_URL);
-
-        clickAndWait("link=ExpansionSubclass");
+        start("ExpansionSubclass");
 
         assertTextPresent("[value provided, in the subclass, via a template expansion]");
     }
@@ -460,9 +429,7 @@
     @Test
     public void template_overridden()
     {
-        open(BASE_URL);
-
-        clickAndWait("link=Template Overridden by Class Page");
+        start("Template Overridden by Class Page");
 
         assertTextPresent("Output: ClassValue");
     }
@@ -473,13 +440,11 @@
         test_loop_inside_form("ToDo List (Volatile)");
     }
 
-    /** This also verifies the use of meta data to set the default strategy. */
+    /** also verifies the use of meta data to set the default strategy. */
     @Test
     public void flash_persistence()
     {
-        open(BASE_URL);
-
-        clickAndWait("link=FlashDemo");
+        start("FlashDemo");
 
         assertTextPresent("[]");
 
@@ -517,9 +482,8 @@
 
     private void test_loop_inside_form(String linkLabel)
     {
-        open(BASE_URL);
+        start(linkLabel);
 
-        clickAndWait("link=" + linkLabel);
         clickAndWait("reset");
 
         assertFieldValue("title", "End World Hunger");
@@ -557,9 +521,7 @@
     {
         String submitButton = "//input[@type='submit']";
 
-        open(BASE_URL);
-        clickAndWait("link=BeanEditor Demo");
-        clickAndWait("link=Clear Data");
+        start("BeanEditor Demo", "Clear Data");
         clickAndWait(submitButton);
 
         assertTextPresent(
@@ -601,9 +563,7 @@
     @Test
     public void bean_editor_property_reorder_remove()
     {
-        open(BASE_URL);
-        clickAndWait("link=BeanEdit Remove/Reorder");
-        clickAndWait("link=Clear Data");
+        start("BeanEdit Remove/Reorder", "Clear Data");
 
         // Looks like a bug in Selenium; we can see //label[1] but not //label[2].
         // assertTextSeries("//label[%d]", 1, "Last Name", "First Name", "Sex", "U.S. Citizen");
@@ -619,8 +579,7 @@
     @Test
     public void pageloaded_lifecycle_method_invoked()
     {
-        open(BASE_URL);
-        clickAndWait("link=PageLoaded Demo");
+        start("PageLoaded Demo");
 
         assertTextPresent("[pageLoaded() was invoked.]");
     }
@@ -631,8 +590,7 @@
     @Test
     public void basic_grid()
     {
-        open(BASE_URL);
-        clickAndWait("link=Grid Demo");
+        start("Grid Demo");
 
         assertTextSeries("//th[%d]", 1, "Title", "Album", "Artist", "Genre", "Play Count", "Rating");
 
@@ -722,8 +680,7 @@
     @Test
     public void grid_remove_reorder()
     {
-        open(BASE_URL);
-        clickAndWait("link=Grid Remove/Reorder Demo");
+        start("Grid Remove/Reorder Demo");
 
         assertTextSeries("//th[%d]", 1, "Rating", "Title", "Album", "Artist", "Genre");
     }
@@ -731,8 +688,7 @@
     @Test
     public void grid_from_explicit_interface_model()
     {
-        open(BASE_URL);
-        clickAndWait("link=SimpleTrack Grid Demo");
+        start("SimpleTrack Grid Demo");
 
         assertTextSeries("//th[%d]", 1, "Title", "Album", "Rating");
 
@@ -742,9 +698,7 @@
     @Test
     public void grid_enum_display()
     {
-        open(BASE_URL);
-        clickAndWait("link=Grid Enum Demo");
-        clickAndWait("link=reset");
+        start("Grid Enum Demo", "reset");
 
         assertTextSeries("//tr[1]/td[%d]", 2, "End World Hunger", "Medium");
         assertTextSeries("//tr[2]/td[%d]", 2, "Develop Faster-Than-Light Travel", "High");
@@ -754,9 +708,7 @@
     @Test
     public void null_grid() throws Exception
     {
-        open(BASE_URL);
-
-        clickAndWait("link=Null Grid");
+        start("Null Grid");
 
         assertTextPresent("There is no data to display.");
     }
@@ -764,9 +716,7 @@
     @Test
     public void navigation_response_from_page_activate() throws Exception
     {
-        open(BASE_URL);
-
-        clickAndWait("link=Protected Page");
+        start("Protected Page");
 
         assertText("//h1", "Security Alert");
 
@@ -778,9 +728,7 @@
     @Test
     public void mixed_page_activation_context_and_component_context()
     {
-        open(BASE_URL);
-
-        clickAndWait("link=Kicker");
+        start("Kicker");
 
         clickAndWait("actionlink");
 
@@ -796,9 +744,7 @@
     @Test
     public void page_link_with_explicit_empty_context()
     {
-        open(BASE_URL);
-
-        clickAndWait("link=Kicker");
+        start("Kicker");
 
         clickAndWait("actionlink");
 
@@ -812,11 +758,7 @@
     @Test
     public void page_link_with_explicit_activation_context()
     {
-        open(BASE_URL);
-
-        clickAndWait("link=PageLink Context Demo");
-
-        clickAndWait("link=no context");
+        start("PageLink Context Demo", "no context");
 
         assertTextPresent("No activation context.");
 
@@ -836,9 +778,7 @@
     @Test
     public void client_side_validation()
     {
-        open(BASE_URL);
-
-        clickAndWait("link=Client Validation Demo");
+        start("Client Validation Demo");
 
         assertTextSeries(
                 "//script[%d]/@src",
@@ -877,22 +817,20 @@
     @Test
     public void recursive_components_are_identified_as_errors()
     {
-        open(BASE_URL);
-        clickAndWait("link=Recursive Demo");
+        start("Recursive Demo");
 
         assertTextPresent(
                 "An unexpected application exception has occurred.",
-                "The template for component org.apache.tapestry.integration.app1.components.Recursive is recursive (contains another direct or indirect reference to component org.apache.tapestry.integration.app1.components.Recursive). This is not supported (components may not contain themselves).",
-                "This component is <t:recursive>recursive</t:recursive>, so we\'ll see a failure.");
+                "The template for component org.apache.tapestry.integration.app1.components.Recursive is recursive (contains another direct or indirect reference to component org.apache.tapestry.integration.app1.components.Recursive). is not supported (components may not contain themselves).",
+                "component is <t:recursive>recursive</t:recursive>, so we\'ll see a failure.");
     }
 
     @Test
     public void render_phase_method_may_return_renderable()
     {
-        open(BASE_URL);
-        clickAndWait("link=Renderable Demo");
+        start("Renderable Demo");
 
-        assertTextPresent("Renderable Demo", "[This proves it works.]");
+        assertTextPresent("Renderable Demo", "[proves it works.]");
     }
 
     @Test
@@ -900,8 +838,8 @@
     {
         String clear = "link=clear";
 
-        open(BASE_URL);
-        clickAndWait("link=EventHandler Demo");
+        start("EventHandler Demo");
+
         clickAndWait(clear);
 
         clickAndWait("wilma");
@@ -925,8 +863,7 @@
     @Test
     public void inherited_bindings()
     {
-        open(BASE_URL);
-        clickAndWait("link=Inherited Bindings Demo");
+        start("Inherited Bindings Demo");
 
         assertTextPresent(
                 "Bound: [ value: the-bound-value, bound: true ]",
@@ -936,8 +873,7 @@
     @Test
     public void client_persistence()
     {
-        open(BASE_URL);
-        clickAndWait("link=Client Persistence Demo");
+        start("Client Persistence Demo");
 
         assertTextPresent("Persisted value: []", "Session: [false]");
 
@@ -949,8 +885,7 @@
     @Test
     public void attribute_expansions()
     {
-        open(BASE_URL);
-        clickAndWait("link=Attribute Expansions Demo");
+        start("Attribute Expansions Demo");
 
         assertText("//div[@id='mixed-expansion']/@style", "color: blue;");
         assertText("//div[@id='single']/@class", "red");
@@ -968,9 +903,7 @@
     @Test
     public void palette_component()
     {
-        open(BASE_URL);
-        clickAndWait("link=Palette Demo");
-        clickAndWait("link=reset");
+        start("Palette Demo", "reset");
 
         addSelection("languages:avail", "label=Haskell");
         addSelection("languages:avail", "label=Javascript");
@@ -1020,6 +953,7 @@
     {
 
         open(BASE_URL);
+
         assertTextPresent("Tapestry 5 Integration Application 1");
 
         clickAndWait("link=Return Types");
@@ -1075,9 +1009,7 @@
     @Test
     public void form_encoding_type()
     {
-        open(BASE_URL);
-
-        clickAndWait("link=Form Encoding Type");
+        start("Form Encoding Type");
 
         assertText("//form/@enctype", "x-override");
     }
@@ -1085,9 +1017,7 @@
     @Test
     public void radio_button_and_group()
     {
-        open(BASE_URL);
-
-        clickAndWait("link=RadioDemo");
+        start("RadioDemo");
 
         String update = "//input[@type='submit']";
 
@@ -1107,9 +1037,7 @@
     @Test
     public void regexp_validator()
     {
-        open(BASE_URL);
-
-        clickAndWait("link=Regexp Demo");
+        start("Regexp Demo");
 
         String update = "//input[@type='submit']";
 
@@ -1131,4 +1059,36 @@
 
         assertTextPresent("Zip code: [12345-9876]");
     }
+
+    private void start(String... linkText)
+    {
+        open(BASE_URL);
+
+        for (String s : linkText)
+            clickAndWait(String.format("link=%s", s));
+    }
+
+    @Test
+    public void multiple_beaneditor_components()
+    {
+        start("MultiBeanEdit Demo");
+
+        clickAndWait("link=Clear Data");
+
+        type("firstName", "Howard");
+        type("lastName", "Lewis Ship");
+        type("path", "/var/www");
+        clickAndWait("//input[@value='Set Access']");
+
+        waitForPageToLoad("30000");
+
+        assertTextSeries(
+                "//li[%d]",
+                1,
+                "First Name: [Howard]",
+                "Last Name: [Lewis Ship]",
+                "Path: [/var/www]",
+                "Role: [GRANT]");
+    }
+
 }

Modified: tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/integration/app1/data/RegistrationData.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/integration/app1/data/RegistrationData.java?rev=578461&r1=578460&r2=578461&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/integration/app1/data/RegistrationData.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/integration/app1/data/RegistrationData.java Sat Sep 22 09:46:36 2007
@@ -37,12 +37,13 @@
         return _birthYear;
     }
 
-    @OrderBefore("citizen")
+    @OrderAfter("lastname,birthyear")
     public Sex getSex()
     {
         return _sex;
     }
 
+    @OrderBefore("lastname")
     public String getFirstName()
     {
         return _firstName;

Added: tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/integration/app1/data/RoleAccess.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/integration/app1/data/RoleAccess.java?rev=578461&view=auto
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/integration/app1/data/RoleAccess.java (added)
+++ tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/integration/app1/data/RoleAccess.java Sat Sep 22 09:46:36 2007
@@ -0,0 +1,20 @@
+// Copyright 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.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package org.apache.tapestry.integration.app1.data;
+
+public enum RoleAccess
+{
+    GRANT, DENY;
+}

Added: tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/integration/app1/data/RolePath.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/integration/app1/data/RolePath.java?rev=578461&view=auto
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/integration/app1/data/RolePath.java (added)
+++ tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/integration/app1/data/RolePath.java Sat Sep 22 09:46:36 2007
@@ -0,0 +1,47 @@
+// Copyright 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.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package org.apache.tapestry.integration.app1.data;
+
+import org.apache.tapestry.beaneditor.Validate;
+
+public class RolePath
+{
+    private String _path;
+
+    private RoleAccess _role = RoleAccess.GRANT;
+
+    @Validate("required")
+    public String getPath()
+    {
+        return _path;
+    }
+
+    @Validate("required")
+    public RoleAccess getRole()
+    {
+        return _role;
+    }
+
+    public void setPath(String path)
+    {
+        _path = path;
+    }
+
+    public void setRole(RoleAccess role)
+    {
+        _role = role;
+    }
+
+}

Added: tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/integration/app1/data/UserCredentials.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/integration/app1/data/UserCredentials.java?rev=578461&view=auto
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/integration/app1/data/UserCredentials.java (added)
+++ tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/integration/app1/data/UserCredentials.java Sat Sep 22 09:46:36 2007
@@ -0,0 +1,47 @@
+// Copyright 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.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package org.apache.tapestry.integration.app1.data;
+
+import org.apache.tapestry.beaneditor.Validate;
+
+public class UserCredentials
+{
+    private String _lastName;
+
+    private String _firstName;
+
+    @Validate("required")
+    public String getFirstName()
+    {
+        return _firstName;
+    }
+
+    @Validate("required")
+    public String getLastName()
+    {
+        return _lastName;
+    }
+
+    public void setLastName(String lastName)
+    {
+        _lastName = lastName;
+    }
+
+    public void setFirstName(String firstName)
+    {
+        _firstName = firstName;
+    }
+
+}

Added: tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/integration/app1/pages/MultiBeanDemoResult.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/integration/app1/pages/MultiBeanDemoResult.java?rev=578461&view=auto
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/integration/app1/pages/MultiBeanDemoResult.java (added)
+++ tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/integration/app1/pages/MultiBeanDemoResult.java Sat Sep 22 09:46:36 2007
@@ -0,0 +1,39 @@
+// Copyright 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.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package org.apache.tapestry.integration.app1.pages;
+
+import org.apache.tapestry.annotations.ApplicationState;
+import org.apache.tapestry.integration.app1.data.RolePath;
+import org.apache.tapestry.integration.app1.data.UserCredentials;
+
+public class MultiBeanDemoResult
+{
+    @ApplicationState
+    private UserCredentials _credentials;
+
+    @ApplicationState
+    private RolePath _rolePath;
+
+    public UserCredentials getCredentials()
+    {
+        return _credentials;
+    }
+
+    public RolePath getRolePath()
+    {
+        return _rolePath;
+    }
+
+}

Added: tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/integration/app1/pages/MultiBeanEditDemo.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/integration/app1/pages/MultiBeanEditDemo.java?rev=578461&view=auto
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/integration/app1/pages/MultiBeanEditDemo.java (added)
+++ tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/integration/app1/pages/MultiBeanEditDemo.java Sat Sep 22 09:46:36 2007
@@ -0,0 +1,64 @@
+// Copyright 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.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package org.apache.tapestry.integration.app1.pages;
+
+import org.apache.tapestry.annotations.ApplicationState;
+import org.apache.tapestry.annotations.InjectPage;
+import org.apache.tapestry.integration.app1.data.RolePath;
+import org.apache.tapestry.integration.app1.data.UserCredentials;
+
+public class MultiBeanEditDemo
+{
+    @ApplicationState
+    private UserCredentials _credentials;
+
+    @ApplicationState
+    private RolePath _rolePath;
+
+    @InjectPage
+    private MultiBeanDemoResult _resultPage;
+
+    public UserCredentials getCredentials()
+    {
+        return _credentials;
+    }
+
+    public RolePath getRolePath()
+    {
+        return _rolePath;
+    }
+
+    public void setCredentials(UserCredentials credentials)
+    {
+        _credentials = credentials;
+    }
+
+    public void setRolePath(RolePath rolePath)
+    {
+        _rolePath = rolePath;
+    }
+
+    Object onSuccess()
+    {
+        return _resultPage;
+    }
+
+    void onActionFromClear()
+    {
+        // Force these to be re-created.
+        _credentials = null;
+        _rolePath = null;
+    }
+}