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// limitations under the License.
012
013package org.apache.tapestry5.ajax;
014
015import org.apache.tapestry5.ClientBodyElement;
016import org.apache.tapestry5.commons.util.CollectionFactory;
017import org.apache.tapestry5.ioc.internal.util.InternalUtils;
018
019import java.util.Map;
020
021/**
022 * A mapping from <em>client-side zone ids</em> to objects that can render the content for that zone on the client. An
023 * event handler method may instantiate an instance and chain together a series of calls to {@link #add(String, Object)}
024 * , and return the final result.
025 *
026 * Remember that client-side element ids may not match server-side component ids, especially once Ajax is added to the
027 * mix. Because of this, it is highly recommended that the client-side logic gather the actual component ids and include
028 * those in the Ajax request, to ensure that the server generates updates that the client can process. Better yet, use
029 * the Zone's id parameter to lock down the zone's id to a known, predictable value.
030 *
031 * @since 5.1.0.1
032 * @deprecated Deprecated in 5.3; use the {@link org.apache.tapestry5.services.ajax.AjaxResponseRenderer} service instead of
033 *             returning an instance of MultiZoneUpdate
034 */
035public class MultiZoneUpdate
036{
037    private final MultiZoneUpdate parent;
038
039    private final String zoneId;
040
041    private final Object renderer;
042
043    public MultiZoneUpdate(String zoneId, Object renderer)
044    {
045        this(zoneId, renderer, null);
046    }
047
048    /**
049     * Alternate constructor that takes a ClientBodyElement (typically, a
050     * {@link org.apache.tapestry5.corelib.components.Zone}).
051     */
052    public MultiZoneUpdate(ClientBodyElement zone)
053    {
054        this(zone.getClientId(), zone.getBody());
055    }
056
057    private MultiZoneUpdate(String zoneId, Object renderer, MultiZoneUpdate parent)
058    {
059        assert renderer != null;
060        assert InternalUtils.isNonBlank(zoneId);
061
062        this.zoneId = zoneId;
063        this.renderer = renderer;
064        this.parent = parent;
065    }
066
067    /**
068     * Adds the zone (represented by the {@link ClientBodyElement}) to the update.
069     *
070     * @since 5.2.3
071     */
072    public MultiZoneUpdate add(ClientBodyElement zone)
073    {
074        assert zone != null;
075
076        return add(zone.getClientId(), zone.getBody());
077    }
078
079    /**
080     * Returns a <strong>new</strong> MultiZoneUpdate reflecting the mapping from the indicated zone to an object that
081     * will render the content for that zone.
082     *
083     * @param zoneId   client id of zone to update
084     * @param renderer object that can provide the content for the zone
085     * @return new MultiZoneUpdate
086     */
087    public MultiZoneUpdate add(String zoneId, Object renderer)
088    {
089        return new MultiZoneUpdate(zoneId, renderer, this);
090    }
091
092    /**
093     * Returns a mapping from client zone id to renderer object for that zone.
094     *
095     * @return string to renderer map
096     */
097    public Map<String, Object> getZoneToRenderMap()
098    {
099        Map<String, Object> result = CollectionFactory.newMap();
100
101        MultiZoneUpdate cursor = this;
102
103        while (cursor != null)
104        {
105            result.put(cursor.zoneId, cursor.renderer);
106
107            cursor = cursor.parent;
108        }
109
110        return result;
111    }
112
113    @Override
114    public String toString()
115    {
116        return String.format("MultiZoneUpdate[%s]", getZoneToRenderMap());
117    }
118}