001    // Copyright 2009, 2010, 2011 The Apache Software Foundation
002    //
003    // Licensed under the Apache License, Version 2.0 (the "License");
004    // you may not use this file except in compliance with the License.
005    // You may obtain a copy of the License at
006    //
007    // http://www.apache.org/licenses/LICENSE-2.0
008    //
009    // Unless required by applicable law or agreed to in writing, software
010    // distributed under the License is distributed on an "AS IS" BASIS,
011    // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
012    // See the License for the specific language governing permissions and
013    // limitations under the License.
014    
015    package org.apache.tapestry5.internal.services.ajax;
016    
017    import org.apache.tapestry5.MarkupWriter;
018    import org.apache.tapestry5.dom.Element;
019    import org.apache.tapestry5.internal.services.PageRenderQueue;
020    import org.apache.tapestry5.json.JSONObject;
021    import org.apache.tapestry5.runtime.RenderCommand;
022    import org.apache.tapestry5.runtime.RenderQueue;
023    import org.apache.tapestry5.services.PartialMarkupRenderer;
024    import org.apache.tapestry5.services.PartialMarkupRendererFilter;
025    
026    /**
027     * Responsible for capturing the content for a single zone and storing it into the JSON reply object. As a {@link PartialMarkupRendererFilter} , this
028     * has access to the {@link JSONObject} for the reply, and can {@linkplain PageRenderQueue#addPartialRenderer(org.apache.tapestry5.runtime.RenderCommand) add renderers that generate and package the markup content}.
029     *
030     * @see org.apache.tapestry5.ajax.MultiZoneUpdate
031     * @see org.apache.tapestry5.services.ajax.AjaxResponseRenderer#addRender(String, Object)
032     * @since 5.1.0.1
033     */
034    public class SingleZonePartialRendererFilter implements PartialMarkupRendererFilter
035    {
036        private final String zoneId;
037    
038        private final RenderCommand zoneRenderCommand;
039    
040        private final PageRenderQueue queue;
041    
042        private final AjaxFormUpdateController ajaxFormUpdateController;
043    
044        public SingleZonePartialRendererFilter(String zoneId, RenderCommand zoneRenderCommand, PageRenderQueue queue,
045                                               AjaxFormUpdateController ajaxFormUpdateController)
046        {
047            this.zoneId = zoneId;
048            this.zoneRenderCommand = zoneRenderCommand;
049            this.queue = queue;
050            this.ajaxFormUpdateController = ajaxFormUpdateController;
051        }
052    
053        public void renderMarkup(MarkupWriter writer, final JSONObject reply, PartialMarkupRenderer renderer)
054        {
055            RenderCommand forZone = new RenderCommand()
056            {
057                public void render(MarkupWriter writer, RenderQueue queue)
058                {
059                    // Create an element to contain the content for the zone. We give it a mnemonic
060                    // element name and attribute just to help with debugging (the element itself is discarded).
061    
062                    final Element zoneContainer = writer.element("zone-update", "zoneId", zoneId);
063    
064                    ajaxFormUpdateController.setupBeforePartialZoneRender(writer);
065    
066                    queue.push(new RenderCommand()
067                    {
068                        public void render(MarkupWriter writer, RenderQueue queue)
069                        {
070                            writer.end(); // the zoneContainer element
071    
072                            // Need to do this Ajax Form-related cleanup here, before we extract the zone content.
073    
074                            ajaxFormUpdateController.cleanupAfterPartialZoneRender();
075    
076                            String zoneUpdateContent = zoneContainer.getChildMarkup();
077    
078                            zoneContainer.remove();
079    
080                            if (!reply.has("zones"))
081                            {
082                                reply.put("zones", new JSONObject());
083                            }
084    
085                            reply.getJSONObject("zones").put(zoneId, zoneUpdateContent);
086                        }
087                    });
088    
089                    // Make sure the zone's actual rendering command is processed first, then the inline
090                    // RenderCommand just above.
091    
092                    queue.push(zoneRenderCommand);
093                }
094            };
095    
096            queue.addPartialRenderer(forZone);
097    
098            renderer.renderMarkup(writer, reply);
099        }
100    }