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