001// Copyright 2009-2012 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
015package org.apache.tapestry5.internal.services.ajax;
016
017import org.apache.tapestry5.MarkupWriter;
018import org.apache.tapestry5.dom.Element;
019import org.apache.tapestry5.internal.InternalConstants;
020import org.apache.tapestry5.internal.services.PageRenderQueue;
021import org.apache.tapestry5.json.JSONArray;
022import org.apache.tapestry5.json.JSONObject;
023import org.apache.tapestry5.runtime.RenderCommand;
024import org.apache.tapestry5.runtime.RenderQueue;
025import org.apache.tapestry5.services.PartialMarkupRenderer;
026import org.apache.tapestry5.services.PartialMarkupRendererFilter;
027
028/**
029 * Responsible for capturing the content for a single zone and storing it into the JSON reply object. As a {@link PartialMarkupRendererFilter} , this
030 * 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}.
031 *
032 * @see org.apache.tapestry5.ajax.MultiZoneUpdate
033 * @see org.apache.tapestry5.services.ajax.AjaxResponseRenderer#addRender(String, Object)
034 * @since 5.1.0.1
035 */
036public class SingleZonePartialRendererFilter implements PartialMarkupRendererFilter
037{
038    private final String zoneId;
039
040    private final RenderCommand zoneRenderCommand;
041
042    private final PageRenderQueue queue;
043
044    private final AjaxFormUpdateController ajaxFormUpdateController;
045
046    public SingleZonePartialRendererFilter(String zoneId, RenderCommand zoneRenderCommand, PageRenderQueue queue,
047                                           AjaxFormUpdateController ajaxFormUpdateController)
048    {
049        this.zoneId = zoneId;
050        this.zoneRenderCommand = zoneRenderCommand;
051        this.queue = queue;
052        this.ajaxFormUpdateController = ajaxFormUpdateController;
053    }
054
055    public void renderMarkup(MarkupWriter writer, final JSONObject reply, PartialMarkupRenderer renderer)
056    {
057        RenderCommand forZone = new RenderCommand()
058        {
059            public void render(MarkupWriter writer, RenderQueue queue)
060            {
061                // Create an element to contain the content for the zone. We give it a mnemonic
062                // element name and attribute just to help with debugging (the element itself is discarded).
063
064                final Element zoneContainer = writer.element("zone-update", "zoneId", zoneId);
065
066                ajaxFormUpdateController.setupBeforePartialZoneRender(writer);
067
068                queue.push(new RenderCommand()
069                {
070                    public void render(MarkupWriter writer, RenderQueue queue)
071                    {
072                        writer.end(); // the zoneContainer element
073
074                        // Need to do this Ajax Form-related cleanup here, before we extract the zone content.
075
076                        ajaxFormUpdateController.cleanupAfterPartialZoneRender();
077
078                        String zoneUpdateContent = zoneContainer.getChildMarkup();
079
080                        zoneContainer.remove();
081
082                        // This has changed a bit in 5.4;
083                        // In 5.3, it was just "zones", and was key/value pairs for id and content.
084                        // In 5.4, it is "content", and is an array of id/content arrays
085                        reply.in(InternalConstants.PARTIAL_KEY).append("content",
086                                new JSONArray(zoneId, zoneUpdateContent));
087                    }
088                });
089
090                // Make sure the zone's actual rendering command is processed first, then the inline
091                // RenderCommand just above.
092
093                queue.push(zoneRenderCommand);
094            }
095        };
096
097        queue.addPartialRenderer(forZone);
098
099        renderer.renderMarkup(writer, reply);
100    }
101}