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 2010/03/27 21:23:58 UTC

svn commit: r928260 - in /tapestry/tapestry5/trunk: tapestry-core/src/main/java/org/apache/tapestry5/corelib/components/ tapestry-core/src/main/java/org/apache/tapestry5/corelib/internal/ tapestry-core/src/main/java/org/apache/tapestry5/internal/servic...

Author: hlship
Date: Sat Mar 27 20:23:58 2010
New Revision: 928260

URL: http://svn.apache.org/viewvc?rev=928260&view=rev
Log:
TAP5-1084: Refactoring and reoganizing to make form logic inside FormInjector component general for any Ajax request that results in a partial page render request

Added:
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/ajax/AjaxFormUpdateFilter.java   (with props)
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/util/CaptureResultCallback.java   (with props)
Modified:
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/corelib/components/Form.java
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/corelib/components/FormInjector.java
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/corelib/internal/HiddenFieldPositioner.java
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/AjaxPartialResponseRendererImpl.java
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/PageRenderQueueImpl.java
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/PartialMarkupRendererTerminator.java
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/RequestConstants.java
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/TapestryModule.java
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/ajax/MultiZoneUpdateEventResultProcessor.java
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/ajax/SetupZonesFilter.java
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/ajax/SingleZonePartialRendererFilter.java
    tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/util/IdAllocator.java

Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/corelib/components/Form.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/corelib/components/Form.java?rev=928260&r1=928259&r2=928260&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/corelib/components/Form.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/corelib/components/Form.java Sat Mar 27 20:23:58 2010
@@ -23,6 +23,7 @@ import org.apache.tapestry5.annotations.
 import org.apache.tapestry5.annotations.Events;
 import org.apache.tapestry5.annotations.Log;
 import org.apache.tapestry5.annotations.Mixin;
+import org.apache.tapestry5.annotations.OnEvent;
 import org.apache.tapestry5.annotations.Parameter;
 import org.apache.tapestry5.annotations.Persist;
 import org.apache.tapestry5.corelib.internal.ComponentActionSink;
@@ -409,6 +410,8 @@ public class Form implements ClientEleme
      * Creates an {@link org.apache.tapestry5.corelib.internal.InternalFormSupport} for
      * this Form. This method is used
      * by {@link org.apache.tapestry5.corelib.components.FormInjector}.
+     * <p>
+     * This method may also be invoked as the handler for the "internalCreateRenderTimeFormSupport" event.
      * 
      * @param name
      *            the client-side name and client id for the rendered form
@@ -421,6 +424,7 @@ public class Form implements ClientEleme
      *            used to allocate unique ids
      * @return form support object
      */
+    @OnEvent("internalCreateRenderTimeFormSupport")
     InternalFormSupport createRenderTimeFormSupport(String name, ComponentActionSink actionSink, IdAllocator allocator)
     {
         return new FormSupportImpl(resources, name, actionSink, clientBehaviorSupport, clientValidation, allocator,

Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/corelib/components/FormInjector.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/corelib/components/FormInjector.java?rev=928260&r1=928259&r2=928260&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/corelib/components/FormInjector.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/corelib/components/FormInjector.java Sat Mar 27 20:23:58 2010
@@ -28,6 +28,7 @@ import org.apache.tapestry5.corelib.inte
 import org.apache.tapestry5.corelib.internal.InternalFormSupport;
 import org.apache.tapestry5.dom.Element;
 import org.apache.tapestry5.internal.services.PageRenderQueue;
+import org.apache.tapestry5.internal.services.RequestConstants;
 import org.apache.tapestry5.ioc.annotations.Inject;
 import org.apache.tapestry5.ioc.internal.util.IdAllocator;
 import org.apache.tapestry5.json.JSONObject;
@@ -40,6 +41,7 @@ import org.apache.tapestry5.services.Hea
 import org.apache.tapestry5.services.HiddenFieldLocationRules;
 import org.apache.tapestry5.services.PartialMarkupRenderer;
 import org.apache.tapestry5.services.PartialMarkupRendererFilter;
+import org.apache.tapestry5.services.javascript.JavascriptSupport;
 import org.slf4j.Logger;
 
 /**
@@ -57,9 +59,15 @@ public class FormInjector implements Cli
 {
     public static final String INJECT_EVENT = "inject";
 
-    public static final String FORM_CLIENTID_PARAMETER = "t:formid";
+    /**
+     * @deprecated Use {@link RequestConstants#FORM_CLIENTID_PARAMETER} instead
+     */
+    public static final String FORM_CLIENTID_PARAMETER = RequestConstants.FORM_CLIENTID_PARAMETER;
 
-    public static final String FORM_COMPONENTID_PARAMETER = "t:formcomponentid";
+    /**
+     * @deprecated Use {@link RequestConstants#FORM_COMPONENTID_PARAMETER} instead
+     */
+    public static final String FORM_COMPONENTID_PARAMETER = RequestConstants.FORM_COMPONENTID_PARAMETER;
 
     /**
      * The context for the link (optional parameter). This list of values will be converted into strings and included in
@@ -87,7 +95,7 @@ public class FormInjector implements Cli
     private String element;
 
     @Environmental
-    private RenderSupport renderSupport;
+    private JavascriptSupport javascriptSupport;
 
     @Environmental
     private FormSupport formSupport;
@@ -95,9 +103,6 @@ public class FormInjector implements Cli
     @Environmental
     private ClientBehaviorSupport clientBehaviorSupport;
 
-    @Environmental
-    private Heartbeat heartbeat;
-
     @SuppressWarnings("unchecked")
     @Environmental
     private TrackableComponentEventCallback eventCallback;
@@ -110,15 +115,6 @@ public class FormInjector implements Cli
     @Inject
     private ComponentResources resources;
 
-    @Inject
-    private Environment environment;
-
-    @Inject
-    private Logger logger;
-
-    @Inject
-    private ComponentSource componentSource;
-
     private Element clientElement;
 
     String defaultElement()
@@ -128,7 +124,7 @@ public class FormInjector implements Cli
 
     void beginRender(MarkupWriter writer)
     {
-        clientId = renderSupport.allocateClientId(resources);
+        clientId = javascriptSupport.allocateClientId(resources);
 
         clientElement = writer.element(element, "id", clientId);
 
@@ -138,8 +134,8 @@ public class FormInjector implements Cli
 
         Link link = resources.createEventLink(INJECT_EVENT, context);
 
-        link.addParameter(FORM_CLIENTID_PARAMETER, formSupport.getClientId());
-        link.addParameter(FORM_COMPONENTID_PARAMETER, formSupport.getFormComponentId());
+        link.addParameter(RequestConstants.FORM_CLIENTID_PARAMETER, formSupport.getClientId());
+        link.addParameter(RequestConstants.FORM_COMPONENTID_PARAMETER, formSupport.getFormComponentId());
 
         clientBehaviorSupport.addFormInjector(clientId, link, position, show);
     }
@@ -163,82 +159,27 @@ public class FormInjector implements Cli
     }
 
     /**
-     * Used during Ajax partial rendering to identify the hidden field that will hold the form data (component actions,
-     * used when processing the form submission) for the injection.
-     */
-    private HiddenFieldPositioner hiddenFieldPositioner;
-
-    @Inject
-    private HiddenFieldLocationRules rules;
-
-    @Inject
-    private ClientDataEncoder clientDataEncoder;
-
-    /**
      * Invoked via an Ajax request. Triggers an action event and captures the return value. The return value from the
-     * event notification is what will ultimately render (typically, its a Block). However, we do a <em>lot</em> of
-     * tricks to provide the desired FormSupport around the what renders.
+     * event notification is what will ultimately render (typically, its a Block).
      */
-    void onInject(EventContext context,
-
-    @QueryParameter(FORM_CLIENTID_PARAMETER)
-    final String formClientId,
-
-    @QueryParameter(FORM_COMPONENTID_PARAMETER)
-    String formComponentId) throws IOException
+    void onInject(EventContext context) throws IOException
     {
         resources.triggerContextEvent(EventConstants.ACTION, context, eventCallback);
 
         if (!eventCallback.isAborted())
             return;
 
-        // Here's where it gets very, very tricky.
-
-        final Form form = (Form) componentSource.getComponent(formComponentId);
-
-        final ComponentActionSink actionSink = new ComponentActionSink(logger, clientDataEncoder);
+        // Before rendering, allocate a unique element id and record it into the JSON reply.
 
         PartialMarkupRendererFilter filter = new PartialMarkupRendererFilter()
         {
             public void renderMarkup(MarkupWriter writer, JSONObject reply, PartialMarkupRenderer renderer)
             {
-                hiddenFieldPositioner = new HiddenFieldPositioner(writer, rules);
-
-                // Kind of ugly, but the only way to ensure we don't have name collisions on the
-                // client side is to force a unique id into each name (as well as each id, but that's
-                // RenderSupport's job). It would be nice if we could agree on the uid, but
-                // not essential.
-
-                String uid = Long.toHexString(System.currentTimeMillis());
-
-                IdAllocator idAllocator = new IdAllocator("-" + uid);
-
-                clientId = renderSupport.allocateClientId(resources);
+                clientId = javascriptSupport.allocateClientId(resources);
 
                 reply.put("elementId", clientId);
 
-                InternalFormSupport formSupport = form.createRenderTimeFormSupport(formClientId, actionSink,
-                        idAllocator);
-
-                environment.push(FormSupport.class, formSupport);
-                environment.push(ValidationTracker.class, new ValidationTrackerImpl());
-
-                heartbeat.begin();
-
                 renderer.renderMarkup(writer, reply);
-
-                formSupport.executeDeferred();
-
-                heartbeat.end();
-
-                environment.pop(ValidationTracker.class);
-                environment.pop(FormSupport.class);
-
-                hiddenFieldPositioner.getElement().attributes("type", "hidden",
-
-                "name", Form.FORM_DATA,
-
-                "value", actionSink.getClientData());
             }
         };
 

Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/corelib/internal/HiddenFieldPositioner.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/corelib/internal/HiddenFieldPositioner.java?rev=928260&r1=928259&r2=928260&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/corelib/internal/HiddenFieldPositioner.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/corelib/internal/HiddenFieldPositioner.java Sat Mar 27 20:23:58 2010
@@ -1,3 +1,17 @@
+// 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
+//
+// 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.tapestry5.corelib.internal;
 
 import org.apache.tapestry5.MarkupWriter;
@@ -7,9 +21,10 @@ import org.apache.tapestry5.ioc.internal
 import org.apache.tapestry5.services.HiddenFieldLocationRules;
 
 /**
- * Used to position a hidden field (as part of a form-related component).  Hidden fields are not allowed to go just
- * anywhere, there are rules, dicatated by the (X)HTML schema, about where they are allowed. We use the {@link
- * org.apache.tapestry5.MarkupWriterListener} interface to monitor elements as they are started and ended to find a
+ * Used to position a hidden field (as part of a form-related component). Hidden fields are not allowed to go just
+ * anywhere, there are rules, dictated by the (X)HTML schema, about where they are allowed. We use the
+ * {@link org.apache.tapestry5.MarkupWriterListener} interface to monitor elements as they are started and ended to find
+ * a
  * place to put content.
  */
 public class HiddenFieldPositioner
@@ -42,7 +57,7 @@ public class HiddenFieldPositioner
         {
             if (rules.placeHiddenFieldAfter(element))
             {
-                hiddenFieldElement = element.getParent().element(ELEMENT);
+                hiddenFieldElement = element.getContainer().element(ELEMENT);
                 writer.removeListener(this);
             }
         }
@@ -58,9 +73,10 @@ public class HiddenFieldPositioner
 
     /**
      * Returns the hidden field element, which can have its attributes filled in.
-     *
+     * 
      * @return the element
-     * @throws IllegalStateException if the element was not placed.
+     * @throws IllegalStateException
+     *             if the element was not placed.
      */
     public Element getElement()
     {
@@ -74,7 +90,6 @@ public class HiddenFieldPositioner
             throw new IllegalStateException(
                     "The rendered content did not include any elements that allow for the positioning of the hidden form field's element.");
 
-
         return hiddenFieldElement;
     }
 

Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/AjaxPartialResponseRendererImpl.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/AjaxPartialResponseRendererImpl.java?rev=928260&r1=928259&r2=928260&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/AjaxPartialResponseRendererImpl.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/AjaxPartialResponseRendererImpl.java Sat Mar 27 20:23:58 2010
@@ -62,8 +62,8 @@ public class AjaxPartialResponseRenderer
     public void renderPartialPageMarkup() throws IOException
     {
         // This is a complex area as we are trying to keep public and private services properly
-        // seperated, and trying to keep stateless and stateful (i.e., perthread scope) services
-        // seperated. So we inform the stateful queue service what it needs to do here ...
+        // separated, and trying to keep stateless and stateful (i.e., perthread scope) services
+        // separated. So we inform the stateful queue service what it needs to do here ...
 
         ContentType pageContentType =
                 (ContentType) request.getAttribute(InternalConstants.CONTENT_TYPE_ATTRIBUTE_NAME);

Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/PageRenderQueueImpl.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/PageRenderQueueImpl.java?rev=928260&r1=928259&r2=928260&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/PageRenderQueueImpl.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/PageRenderQueueImpl.java Sat Mar 27 20:23:58 2010
@@ -4,7 +4,7 @@
 // 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
+// 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,
@@ -31,7 +31,7 @@ import org.slf4j.Logger;
 
 /**
  * This services keeps track of the page being rendered and the root command for the partial render, it is therefore
- * request/thread scoped.  There's a filter pipeline around the rendering, and that gets to be stateless because this
+ * request/thread scoped. There's a filter pipeline around the rendering, and that gets to be stateless because this
  * service, at the end of the pipeline, is stateful.
  */
 @Scope(ScopeConstants.PERTHREAD)
@@ -74,7 +74,6 @@ public class PageRenderQueueImpl impleme
         rootCommand = page.getRootElement();
     }
 
-
     public void setRenderingPage(Page page)
     {
         Defense.notNull(page, "page");
@@ -91,7 +90,8 @@ public class PageRenderQueueImpl impleme
     {
         Defense.notNull(rootCommand, "rootCommand");
 
-        if (page == null) throw new IllegalStateException("Page must be specified before root render command.");
+        if (page == null)
+            throw new IllegalStateException("Page must be specified before root render command.");
 
         this.rootCommand = rootCommand;
     }
@@ -149,19 +149,8 @@ public class PageRenderQueueImpl impleme
             delegate = bridge;
         }
 
-        // The partial will quite often contain multiple elements (or just a block of plain text),
-        // so those must be enclosed in a root element.
-
-        Element root = writer.element("ajax-partial");
-
         // The initialize methods will already have been invoked.
 
         delegate.renderMarkup(writer, reply);
-
-        writer.end();
-
-        String content = root.getChildMarkup().trim();
-
-        reply.put("content", content);
     }
 }

Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/PartialMarkupRendererTerminator.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/PartialMarkupRendererTerminator.java?rev=928260&r1=928259&r2=928260&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/PartialMarkupRendererTerminator.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/PartialMarkupRendererTerminator.java Sat Mar 27 20:23:58 2010
@@ -4,7 +4,7 @@
 // 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
+// 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,
@@ -15,14 +15,15 @@
 package org.apache.tapestry5.internal.services;
 
 import org.apache.tapestry5.MarkupWriter;
+import org.apache.tapestry5.dom.Element;
 import org.apache.tapestry5.json.JSONObject;
 import org.apache.tapestry5.services.PartialMarkupRenderer;
 
 /**
  * Terminator for the {@link org.apache.tapestry5.services.PartialMarkupRenderer} pipeline, which ultimately invokes
- * {@link org.apache.tapestry5.internal.services.PageRenderQueue#renderPartial(org.apache.tapestry5.MarkupWriter,
- * org.apache.tapestry5.json.JSONObject)}.
- *
+ * {@link org.apache.tapestry5.internal.services.PageRenderQueue#renderPartial(org.apache.tapestry5.MarkupWriter, org.apache.tapestry5.json.JSONObject)}
+ * .
+ * 
  * @since 5.1.0.0
  */
 public class PartialMarkupRendererTerminator implements PartialMarkupRenderer
@@ -36,6 +37,17 @@ public class PartialMarkupRendererTermin
 
     public void renderMarkup(MarkupWriter writer, JSONObject reply)
     {
+        // The partial will quite often contain multiple elements (or just a block of plain text),
+        // so those must be enclosed in a root element.
+
+        Element root = writer.element("ajax-partial");
+
         renderQueue.renderPartial(writer, reply);
+
+        writer.end();
+
+        String content = root.getChildMarkup().trim();
+
+        reply.put("content", content);
     }
 }

Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/RequestConstants.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/RequestConstants.java?rev=928260&r1=928259&r2=928260&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/RequestConstants.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/RequestConstants.java Sat Mar 27 20:23:58 2010
@@ -4,7 +4,7 @@
 // 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
+// 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,
@@ -14,6 +14,9 @@
 
 package org.apache.tapestry5.internal.services;
 
+import org.apache.tapestry5.corelib.components.Form;
+import org.apache.tapestry5.corelib.components.FormInjector;
+
 /**
  * Constants used when processing requests from the client web browser.
  */
@@ -25,22 +28,35 @@ public final class RequestConstants
      */
     public static final String ASSET_PATH_PREFIX = "/assets/";
 
-
     /**
      * Virtual folder name for assets that are actually stored in the context, but are exposed (much like classpath
      * assets) to gain far-future expires headers and automatic content compression. The application version number
      * comes after this prefix and before the true path.
-     *
+     * 
      * @since 5.1.0.0
      */
     public static final String CONTEXT_FOLDER = "ctx/";
 
-
     /**
      * Folder for virtual assets: combined JavaScript files. The file name is actualy a compressed bytestream
      * of the names of each file.
-     *
+     * 
      * @since 5.1.0.2
      */
     public static final String VIRTUAL_FOLDER = "virtual/";
+
+    /**
+     * Name of parameter, in an Ajax update, that identifies the client-side id of the {@link Form} being extended. Used
+     * with {@link Zone}, {@link FormInjector} and other similar components that may be contained within a form.
+     * 
+     * @since 5.2.0
+     */
+    public static final String FORM_CLIENTID_PARAMETER = "t:formid";
+
+    /**
+     * The server-side part of {@link #FORM_CLIENTID_PARAMETER} identifying the server-side component id.
+     * 
+     * @since 5.2.0
+     */
+    public static final String FORM_COMPONENTID_PARAMETER = "t:formcomponentid";
 }

Added: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/ajax/AjaxFormUpdateFilter.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/ajax/AjaxFormUpdateFilter.java?rev=928260&view=auto
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/ajax/AjaxFormUpdateFilter.java (added)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/ajax/AjaxFormUpdateFilter.java Sat Mar 27 20:23:58 2010
@@ -0,0 +1,170 @@
+// 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
+//
+// 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.tapestry5.internal.services.ajax;
+
+import java.io.IOException;
+
+import org.apache.tapestry5.MarkupWriter;
+import org.apache.tapestry5.ValidationTracker;
+import org.apache.tapestry5.ValidationTrackerImpl;
+import org.apache.tapestry5.corelib.components.Form;
+import org.apache.tapestry5.corelib.components.Zone;
+import org.apache.tapestry5.corelib.internal.ComponentActionSink;
+import org.apache.tapestry5.corelib.internal.HiddenFieldPositioner;
+import org.apache.tapestry5.corelib.internal.InternalFormSupport;
+import org.apache.tapestry5.internal.services.PageRenderQueue;
+import org.apache.tapestry5.internal.services.RequestConstants;
+import org.apache.tapestry5.internal.util.CaptureResultCallback;
+import org.apache.tapestry5.ioc.internal.util.IdAllocator;
+import org.apache.tapestry5.ioc.internal.util.InternalUtils;
+import org.apache.tapestry5.json.JSONObject;
+import org.apache.tapestry5.runtime.Component;
+import org.apache.tapestry5.services.*;
+import org.slf4j.Logger;
+
+/**
+ * Filter for the {@link Ajax} {@link ComponentEventRequestHandler} pipeline that may
+ * contribute a {@link PartialMarkupRendererFilter} into the {@link PageRenderQueue}.
+ * The contributed filter detects the case of a a client-side Tapestry.ZoneManager (i.e., a {@link Zone} component) and,
+ * if it indicates the containing {@link Form} component, sets up a {@link PartialMarkupRendererFilter} to re-establish
+ * the Form so that it will all mesh together on the client side.
+ * 
+ * @since 5.2.0
+ */
+public class AjaxFormUpdateFilter implements ComponentEventRequestFilter
+{
+    private final Request request;
+
+    private final ComponentSource componentSource;
+
+    private final HiddenFieldLocationRules rules;
+
+    private final Environment environment;
+
+    private final Heartbeat heartbeat;
+
+    private final ClientDataEncoder clientDataEncoder;
+
+    private final PageRenderQueue queue;
+
+    private final Logger logger;
+
+    public AjaxFormUpdateFilter(Request request, ComponentSource componentSource, HiddenFieldLocationRules rules,
+            Environment environment, Heartbeat heartbeat, ClientDataEncoder clientDataEncoder, PageRenderQueue queue,
+            Logger logger)
+    {
+        this.request = request;
+        this.componentSource = componentSource;
+        this.rules = rules;
+        this.environment = environment;
+        this.heartbeat = heartbeat;
+        this.clientDataEncoder = clientDataEncoder;
+        this.queue = queue;
+        this.logger = logger;
+    }
+
+    public void handle(ComponentEventRequestParameters parameters, ComponentEventRequestHandler handler)
+            throws IOException
+    {
+        String formClientId = request.getParameter(RequestConstants.FORM_CLIENTID_PARAMETER);
+        String formComponentId = request.getParameter(RequestConstants.FORM_COMPONENTID_PARAMETER);
+
+        if (InternalUtils.isNonBlank(formClientId) && InternalUtils.isNonBlank(formComponentId))
+            addFilterToPartialRenderQueue(formClientId, formComponentId);
+
+        handler.handle(parameters);
+    }
+
+    private void addFilterToPartialRenderQueue(String formClientId, String formComponentId)
+    {
+        queue.addPartialMarkupRendererFilter(createFilter(formClientId, formComponentId));
+    }
+
+    private PartialMarkupRendererFilter createFilter(final String formClientId, final String formComponentId)
+    {
+        return new PartialMarkupRendererFilter()
+        {
+            public void renderMarkup(MarkupWriter writer, JSONObject reply, PartialMarkupRenderer renderer)
+            {
+                HiddenFieldPositioner hiddenFieldPositioner = new HiddenFieldPositioner(writer, rules);
+
+                ComponentActionSink actionSink = new ComponentActionSink(logger, clientDataEncoder);
+
+                InternalFormSupport formSupport = createInternalFormSupport(formClientId, formComponentId, actionSink);
+
+                setupBeforeRender(formSupport);
+
+                renderer.renderMarkup(writer, reply);
+
+                cleanupAfterRender(formSupport);
+
+                injectHiddenFieldIntoDOM(hiddenFieldPositioner, actionSink);
+            }
+
+            private void setupBeforeRender(InternalFormSupport formSupport)
+            {
+                environment.push(FormSupport.class, formSupport);
+                environment.push(ValidationTracker.class, new ValidationTrackerImpl());
+
+                heartbeat.begin();
+            }
+
+            private void cleanupAfterRender(InternalFormSupport formSupport)
+            {
+                formSupport.executeDeferred();
+
+                heartbeat.end();
+
+                environment.pop(ValidationTracker.class);
+                environment.pop(FormSupport.class);
+            }
+
+            private void injectHiddenFieldIntoDOM(HiddenFieldPositioner hiddenFieldPositioner,
+                    ComponentActionSink actionSink)
+            {
+                hiddenFieldPositioner.getElement().attributes("type", "hidden",
+
+                "name", Form.FORM_DATA,
+
+                "value", actionSink.getClientData());
+            }
+
+            private InternalFormSupport createInternalFormSupport(String formClientId, String formComponentId,
+                    ComponentActionSink actionSink)
+            {
+                // Kind of ugly, but the only way to ensure we don't have name collisions on the
+                // client side is to force a unique id into each name (as well as each id, but that's
+                // JavascriptSupport's job). It would be nice if we could agree on the uid, but
+                // not essential.
+
+                String uid = Long.toHexString(System.currentTimeMillis());
+
+                IdAllocator idAllocator = new IdAllocator("_" + uid);
+
+                Component formComponent = componentSource.getComponent(formComponentId);
+
+                CaptureResultCallback<InternalFormSupport> callback = CaptureResultCallback.create();
+
+                // This is a bit of a back-door to access a non-public method!
+
+                formComponent.getComponentResources().triggerEvent("internalCreateRenderTimeFormSupport", new Object[]
+                { formClientId, actionSink, idAllocator }, callback);
+
+                return callback.getResult();
+            }
+
+        };
+    }
+}

Propchange: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/ajax/AjaxFormUpdateFilter.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/util/CaptureResultCallback.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/util/CaptureResultCallback.java?rev=928260&view=auto
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/util/CaptureResultCallback.java (added)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/util/CaptureResultCallback.java Sat Mar 27 20:23:58 2010
@@ -0,0 +1,44 @@
+// 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
+//
+// 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.tapestry5.internal.util;
+
+import org.apache.tapestry5.ComponentEventCallback;
+
+/**
+ * Implementation of {@link ComponentEventCallback} that simply captures the result value.
+ * 
+ * @since 5.2.0
+ */
+public class CaptureResultCallback<T> implements ComponentEventCallback<T>
+{
+    private T result;
+
+    public boolean handleResult(T result)
+    {
+        this.result = result;
+
+        return true;
+    }
+
+    public T getResult()
+    {
+        return result;
+    }
+
+    public static <T> CaptureResultCallback<T> create()
+    {
+        return new CaptureResultCallback<T>();
+    }
+}

Propchange: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/util/CaptureResultCallback.java
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/TapestryModule.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/TapestryModule.java?rev=928260&r1=928259&r2=928260&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/TapestryModule.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/TapestryModule.java Sat Mar 27 20:23:58 2010
@@ -73,6 +73,7 @@ import org.apache.tapestry5.internal.ren
 import org.apache.tapestry5.internal.renderers.ObjectArrayRenderer;
 import org.apache.tapestry5.internal.renderers.RequestRenderer;
 import org.apache.tapestry5.internal.services.*;
+import org.apache.tapestry5.internal.services.ajax.AjaxFormUpdateFilter;
 import org.apache.tapestry5.internal.services.ajax.JavascriptSupportImpl;
 import org.apache.tapestry5.internal.services.messages.PropertiesFileParserImpl;
 import org.apache.tapestry5.internal.transform.*;
@@ -151,7 +152,7 @@ public final class TapestryModule
      * services are always lazy proxies). This isn't
      * about efficiency (it may be slightly more efficient, but not in any
      * noticable way), it's about eliminating the
-     * need to keep injecting these dependencies into invividual service builder
+     * need to keep injecting these dependencies into individual service builder
      * and contribution methods.
      */
     public TapestryModule(PipelineBuilder pipelineBuilder,
@@ -2078,7 +2079,7 @@ public final class TapestryModule
             {
                 String uid = Long.toHexString(System.currentTimeMillis());
 
-                String namespace = "-" + uid;
+                String namespace = "_" + uid;
 
                 IdAllocator idAllocator = new IdAllocator(namespace);
 
@@ -2102,9 +2103,9 @@ public final class TapestryModule
             public void renderMarkup(MarkupWriter writer, JSONObject reply, PartialMarkupRenderer renderer)
             {
                 DocumentLinker linker = environment.peekRequired(DocumentLinker.class);
+                JavascriptSupport javascriptSupport = environment.peekRequired(JavascriptSupport.class);
 
-                RenderSupportImpl support = new RenderSupportImpl(linker, symbolSource, assetSource, environment
-                        .peekRequired(JavascriptSupport.class));
+                RenderSupportImpl support = new RenderSupportImpl(linker, symbolSource, assetSource, javascriptSupport);
 
                 environment.push(RenderSupport.class, support);
 
@@ -2515,6 +2516,21 @@ public final class TapestryModule
     }
 
     /**
+     * Contributes:
+     * <dl>
+     * <dt>AjaxFormUpdate</dt>
+     * <dd>{@link AjaxFormUpdateFilter}</dd>
+     * </dl>
+     * 
+     * @since 5.2.0
+     */
+    public static void contributeAjaxComponentEventRequestHandler(
+            OrderedConfiguration<ComponentEventRequestFilter> configuration)
+    {
+        configuration.addInstance("AjaxFormUpdate", AjaxFormUpdateFilter.class);
+    }
+
+    /**
      * Contributes strategies accessible via the {@link NullFieldStrategySource} service.
      * <p/>
      * <dl>

Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/ajax/MultiZoneUpdateEventResultProcessor.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/ajax/MultiZoneUpdateEventResultProcessor.java?rev=928260&r1=928259&r2=928260&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/ajax/MultiZoneUpdateEventResultProcessor.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/ajax/MultiZoneUpdateEventResultProcessor.java Sat Mar 27 20:23:58 2010
@@ -4,7 +4,7 @@
 // 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
+// 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,
@@ -30,11 +30,11 @@ import java.util.Map;
  * Handler for {@link org.apache.tapestry5.ajax.MultiZoneUpdate} responses from a component event handler method. Works
  * by adding {@link org.apache.tapestry5.services.ajax.SingleZonePartialRendererFilter}s for each zone to the
  * {@linkplain org.apache.tapestry5.internal.services.PageRenderQueue#addPartialMarkupRendererFilter(org.apache.tapestry5.services.PartialMarkupRendererFilter)
- * filter stack}.  Each zone writes its content as a string in the zones object of the reply, keyed on its id.
- * JavaScript and CSS are collected for all zones rendered in the request (not for each individua zone).  The final
- * repsonse will have some combination of "script", "scripts", "stylesheets", "content" (which is expected to be blank)
+ * filter stack}. Each zone writes its content as a string in the zones object of the reply, keyed on its id.
+ * JavaScript and CSS are collected for all zones rendered in the request (not for each individual zone). The final
+ * response will have some combination of "script", "scripts", "stylesheets", "content" (which is expected to be blank)
  * and "zones".
- *
+ * 
  * @since 5.1.0.1
  */
 public class MultiZoneUpdateEventResultProcessor implements ComponentEventResultProcessor<MultiZoneUpdate>
@@ -81,9 +81,8 @@ public class MultiZoneUpdateEventResultP
         }
         catch (Exception ex)
         {
-            throw new IllegalArgumentException(String.format("Failure converting renderer for zone '%s': %s",
-                                                             zoneId,
-                                                             InternalUtils.toMessage(ex)), ex);
+            throw new IllegalArgumentException(String.format("Failure converting renderer for zone '%s': %s", zoneId,
+                    InternalUtils.toMessage(ex)), ex);
         }
     }
-}
\ No newline at end of file
+}

Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/ajax/SetupZonesFilter.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/ajax/SetupZonesFilter.java?rev=928260&r1=928259&r2=928260&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/ajax/SetupZonesFilter.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/ajax/SetupZonesFilter.java Sat Mar 27 20:23:58 2010
@@ -4,7 +4,7 @@
 // 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
+// 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,
@@ -20,9 +20,10 @@ import org.apache.tapestry5.services.Par
 import org.apache.tapestry5.services.PartialMarkupRendererFilter;
 
 /**
- * Creates a "zones" object in the JSON reply, reading for the {@link org.apache.tapestry5.services.ajax.SingleZonePartialRendererFilter}s
+ * Creates a "zones" object in the JSON reply, ready for the
+ * {@link org.apache.tapestry5.services.ajax.SingleZonePartialRendererFilter}s
  * to store values into.
- *
+ * 
  * @since 5.1.0.1
  */
 public class SetupZonesFilter implements PartialMarkupRendererFilter

Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/ajax/SingleZonePartialRendererFilter.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/ajax/SingleZonePartialRendererFilter.java?rev=928260&r1=928259&r2=928260&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/ajax/SingleZonePartialRendererFilter.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/ajax/SingleZonePartialRendererFilter.java Sat Mar 27 20:23:58 2010
@@ -4,7 +4,7 @@
 // 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
+// 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,
@@ -25,7 +25,7 @@ import org.apache.tapestry5.services.Par
 
 /**
  * Responsible for capturing the content for a single zone and storing it into the JSON reply object.
- *
+ * 
  * @see org.apache.tapestry5.ajax.MultiZoneUpdate
  * @since 5.1.0.1
  */
@@ -50,7 +50,7 @@ public class SingleZonePartialRendererFi
         {
             public void render(MarkupWriter writer, RenderQueue queue)
             {
-                // Create an element to contain the content for the zone. We give it a menumonic
+                // Create an element to contain the content for the zone. We give it a mnemonic
                 // element name and attribute just to help with debugging (the element itself is discarded).
 
                 final Element zoneContainer = writer.element("zone-update", "zoneId", zoneId);

Modified: tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/util/IdAllocator.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/util/IdAllocator.java?rev=928260&r1=928259&r2=928260&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/util/IdAllocator.java (original)
+++ tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/util/IdAllocator.java Sat Mar 27 20:23:58 2010
@@ -58,7 +58,7 @@ public final class IdAllocator
         }
 
         /**
-         * Clones this instance, returning an equivalent but seperate copy.
+         * Clones this instance, returning an equivalent but separate copy.
          */
         @SuppressWarnings({ "CloneDoesntDeclareCloneNotSupportedException" })
         @Override