001    // Copyright 2005 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.tapestry.record;
016    
017    import java.util.Collection;
018    import java.util.Collections;
019    import java.util.HashMap;
020    import java.util.Iterator;
021    import java.util.List;
022    import java.util.Map;
023    
024    import org.apache.hivemind.util.Defense;
025    import org.apache.tapestry.IRequestCycle;
026    import org.apache.tapestry.engine.ServiceEncoding;
027    import org.apache.tapestry.web.WebRequest;
028    
029    /**
030     * Service tapestry.persist.ClientPropertyPersistenceStrategy. Encodes persistent page properties on
031     * the client as query parameters.
032     * <p>
033     * Uses the threaded model.
034     * 
035     * @author Howard M. Lewis Ship
036     * @since 4.0
037     * @see org.apache.tapestry.engine.ILink
038     */
039    public class ClientPropertyPersistenceStrategy implements PropertyPersistenceStrategy
040    {
041        /**
042         * Keyed on page name (String), values are
043         * {@link org.apache.tapestry.record.PersistentPropertyData}.
044         */
045        private final Map _data = new HashMap();
046    
047        private final PersistentPropertyDataEncoder _encoder;
048    
049        private WebRequest _request;
050    
051        private ClientPropertyPersistenceScope _scope;
052    
053        public ClientPropertyPersistenceStrategy()
054        {
055            this(new PersistentPropertyDataEncoderImpl());
056        }
057    
058        // Alternate constructor used for testing
059        ClientPropertyPersistenceStrategy(PersistentPropertyDataEncoder encoder)
060        {
061            _encoder = encoder;
062        }
063    
064        /**
065         * Initializer for this service, invoked every time a service instance is created. This
066         * initializer pulls out of the request and query parameters whose prefix is "client:" and
067         * expects them to be encoded {@link PersistentPropertyData}, which are stored internally.
068         * Because the service model is threaded, this information is specific to a single request, and
069         * will be discarded at the end of the request.
070         */
071    
072        public void initializeService()
073        {
074            List names = _request.getParameterNames();
075            Iterator i = names.iterator();
076            while (i.hasNext())
077            {
078                String name = (String) i.next();
079    
080                if (!_scope.isParameterForScope(name))
081                    continue;
082    
083                String pageName = _scope.extractPageName(name);
084    
085                String encoded = _request.getParameterValue(name);
086    
087                PersistentPropertyData data = new PersistentPropertyData(_encoder);
088                data.storeEncoded(encoded);
089    
090                _data.put(pageName, data);
091            }
092        }
093    
094        public void store(String pageName, String idPath, String propertyName, Object newValue)
095        {
096            PersistentPropertyData data = (PersistentPropertyData) _data.get(pageName);
097            if (data == null)
098            {
099                data = new PersistentPropertyData(_encoder);
100                _data.put(pageName, data);
101            }
102    
103            data.store(idPath, propertyName, newValue);
104        }
105    
106        public Collection getStoredChanges(String pageName, IRequestCycle cycle)
107        {
108            PersistentPropertyData data = (PersistentPropertyData) _data.get(pageName);
109    
110            if (data == null)
111                return Collections.EMPTY_LIST;
112    
113            return data.getPageChanges();
114        }
115    
116        public void discardStoredChanges(String pageName, IRequestCycle cycle)
117        {
118            _data.remove(pageName);
119        }
120    
121        public void addParametersForPersistentProperties(ServiceEncoding encoding, IRequestCycle cycle, boolean post)
122        {
123            Defense.notNull(encoding, "encoding");
124            Defense.notNull(cycle, "cycle");
125    
126            Iterator i = _data.entrySet().iterator();
127            while (i.hasNext())
128            {
129                Map.Entry e = (Map.Entry) i.next();
130    
131                String pageName = (String) e.getKey();
132                PersistentPropertyData data = (PersistentPropertyData) e.getValue();
133    
134                ClientPropertyPersistenceScope scope = getScope();
135    
136                if (scope.shouldEncodeState(encoding, cycle, pageName, data))
137                {
138                    String parameterName = _scope.constructParameterName(pageName);
139                    encoding.setParameterValue(parameterName, data.getEncoded());
140                }
141            }
142        }
143    
144        public void setRequest(WebRequest request)
145        {
146            _request = request;
147        }
148    
149        public ClientPropertyPersistenceScope getScope()
150        {
151            return _scope;
152        }
153    
154        public void setScope(ClientPropertyPersistenceScope scope)
155        {
156            _scope = scope;
157        }
158    }