001// Licensed under the Apache License, Version 2.0 (the "License");
002// you may not use this file except in compliance with the License.
003// You may obtain a copy of the License at
004//
005//     http://www.apache.org/licenses/LICENSE-2.0
006//
007// Unless required by applicable law or agreed to in writing, software
008// distributed under the License is distributed on an "AS IS" BASIS,
009// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
010// See the License for the specific language governing permissions and
011
012package org.apache.tapestry5.internal.services.ajax;
013
014import org.apache.tapestry5.ClientBodyElement;
015import org.apache.tapestry5.MarkupWriter;
016import org.apache.tapestry5.commons.services.TypeCoercer;
017import org.apache.tapestry5.http.services.Request;
018import org.apache.tapestry5.internal.InternalConstants;
019import org.apache.tapestry5.internal.services.PageRenderQueue;
020import org.apache.tapestry5.internal.services.PageSource;
021import org.apache.tapestry5.internal.services.RequestPageCache;
022import org.apache.tapestry5.internal.structure.Page;
023import org.apache.tapestry5.ioc.internal.util.InternalUtils;
024import org.apache.tapestry5.json.JSONObject;
025import org.apache.tapestry5.runtime.RenderCommand;
026import org.apache.tapestry5.services.PartialMarkupRenderer;
027import org.apache.tapestry5.services.PartialMarkupRendererFilter;
028import org.apache.tapestry5.services.ajax.AjaxResponseRenderer;
029import org.apache.tapestry5.services.ajax.JSONCallback;
030import org.apache.tapestry5.services.ajax.JavaScriptCallback;
031import org.apache.tapestry5.services.javascript.JavaScriptSupport;
032
033
034public class AjaxResponseRendererImpl implements AjaxResponseRenderer
035{
036    private final PageRenderQueue queue;
037
038    private final AjaxFormUpdateController ajaxFormUpdateController;
039
040    private final TypeCoercer typeCoercer;
041
042    private final JavaScriptSupport javaScriptSupport;
043
044    private final RequestPageCache requestPageCache;
045
046    private final Request request;
047
048    public AjaxResponseRendererImpl(PageRenderQueue queue, AjaxFormUpdateController ajaxFormUpdateController, TypeCoercer typeCoercer, JavaScriptSupport javaScriptSupport, RequestPageCache requestPageCache, Request request)
049    {
050        this.queue = queue;
051        this.ajaxFormUpdateController = ajaxFormUpdateController;
052        this.typeCoercer = typeCoercer;
053        this.javaScriptSupport = javaScriptSupport;
054        this.requestPageCache = requestPageCache;
055        this.request = request;
056    }
057
058    public AjaxResponseRenderer addRender(String clientId, Object renderer)
059    {
060        assert InternalUtils.isNonBlank(clientId);
061        assert renderer != null;
062
063        RenderCommand command = typeCoercer.coerce(renderer, RenderCommand.class);
064
065        addFilter(new SingleZonePartialRendererFilter(clientId, command, queue, ajaxFormUpdateController));
066
067        return this;
068    }
069
070    public AjaxResponseRenderer addRender(ClientBodyElement zone)
071    {
072        assert zone != null;
073
074        final String clientId = zone.getClientId();
075
076        if (clientId == null)
077        {
078            throw new IllegalArgumentException(
079                    "Attempt to render a ClientBodyElement, probably a Zone, with a null clientId. "
080                            + "You can solve this by using the id parameter.");
081        }
082
083        addRender(clientId, zone.getBody());
084
085        return this;
086    }
087
088    public AjaxResponseRenderer addCallback(final JavaScriptCallback callback)
089    {
090        assert callback != null;
091
092        addFilter(new PartialMarkupRendererFilter()
093        {
094            public void renderMarkup(MarkupWriter writer, JSONObject reply, PartialMarkupRenderer renderer)
095            {
096                renderer.renderMarkup(writer, reply);
097
098                callback.run(javaScriptSupport);
099            }
100        });
101
102        return this;
103    }
104
105    public AjaxResponseRenderer addCallback(final Runnable callback)
106    {
107        assert callback != null;
108
109        addFilter(new PartialMarkupRendererFilter()
110        {
111            public void renderMarkup(MarkupWriter writer, JSONObject reply, PartialMarkupRenderer renderer)
112            {
113                renderer.renderMarkup(writer, reply);
114
115                callback.run();
116            }
117        });
118
119
120        return this;
121    }
122
123    private boolean isRedirect(JSONObject reply)
124    {
125
126        return reply.has(InternalConstants.PARTIAL_KEY) &&
127                reply.in(InternalConstants.PARTIAL_KEY).has("redirectURL");
128    }
129
130    public AjaxResponseRenderer addFilter(final PartialMarkupRendererFilter filter)
131    {
132        assert filter != null;
133
134        queue.addPartialMarkupRendererFilter(new PartialMarkupRendererFilter()
135        {
136            public void renderMarkup(MarkupWriter writer, JSONObject reply, PartialMarkupRenderer delete)
137            {
138                if (isRedirect(reply))
139                {
140                    // Bypass the callback.
141                    delete.renderMarkup(writer, reply);
142                    return;
143                }
144
145                filter.renderMarkup(writer, reply, delete);
146            }
147        });
148
149        return this;
150    }
151
152    public AjaxResponseRenderer addCallback(final JSONCallback callback)
153    {
154        assert callback != null;
155
156        addFilter(new PartialMarkupRendererFilter()
157        {
158            public void renderMarkup(MarkupWriter writer, JSONObject reply, PartialMarkupRenderer renderer)
159            {
160                renderer.renderMarkup(writer, reply);
161
162                callback.run(reply);
163            }
164        });
165
166        return this;
167    }
168
169    @Override
170    public void setupPartial(String pageName)
171    {
172        Page page = requestPageCache.get(pageName);
173
174        queue.setRenderingPage(page);
175
176        request.setAttribute(InternalConstants.PAGE_NAME_ATTRIBUTE_NAME, page.getName());
177    }
178}