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.Iterator;
018    
019    import org.apache.hivemind.util.Defense;
020    import org.apache.tapestry.TapestryUtils;
021    import org.apache.tapestry.web.WebSession;
022    
023    /**
024     * Utility methods to support implementations of
025     * {@link org.apache.tapestry.record.PropertyPersistenceStrategy}. This consists of code refactored
026     * out of {@link org.apache.tapestry.record.SessionPropertyPersistenceStrategy} to support other,
027     * similar, persistence types with different rules for how long values are stored in the session.
028     * 
029     * @author Howard M. Lewis Ship
030     * @since 4.0
031     */
032    public final class RecordUtils
033    {
034        /* defeat instantiation */
035        private RecordUtils() { }
036        
037        /**
038         * Builds a {@link PropertyChange} instance for the given key and value pulled from the
039         * {@link org.apache.tapestry.web.WebSession}.
040         * 
041         * @param key
042         *            a key, previously created by
043         *            {@link #buildChangeKey(String, String, String, String, String)}, consisting of a
044         *            strategy id, application id, page name, id path (optional), and a property name,
045         *            all seperated by commas.
046         * @param value
047         *            the value stored in the session with this key
048         * @return a {@link PropertyChange} storing the property name and id path (if any), and the
049         *         value
050         */
051        public static PropertyChange buildChange(String key, Object value)
052        {
053            String[] tokens = TapestryUtils.split(key);
054    
055            // Either strategy-id, app-name,page-name,id-path,property
056            // or strategy-id,app-name,page-name,property
057    
058            String idPath = (tokens.length == 5) ? tokens[3] : null;
059            String propertyName = tokens[tokens.length - 1];
060    
061            return new PropertyChangeImpl(idPath, propertyName, value);
062        }
063    
064        /**
065         * Iterates over the attributes stored in the session, invoking a callback on each one that
066         * matches the given prefix, applicationid and page name. This is used to operate over all
067         * stored data for a particular combination of strategy, applicationId and page.
068         * 
069         * @param strategyId
070         *            a unique identifier for a particular implementation of
071         *            {@link PropertyPersistenceStrategy}
072         * @param applicationId
073         *            a unique id for the application
074         * @param pageName
075         *            the name of the page
076         * @param session
077         *            the session to search
078         * @param callback
079         *            the callback to invoke on each matching attibute name
080         */
081        public static void iterateOverMatchingAttributes(String strategyId, String applicationId,
082                String pageName, WebSession session, WebSessionAttributeCallback callback)
083        {
084            Defense.notNull(strategyId, "strategyId");
085            Defense.notNull(applicationId, "applicationId");
086            Defense.notNull(pageName, "pageName");
087            Defense.notNull(session, "session");
088    
089            String prefix = strategyId + "," + applicationId + "," + pageName + ",";
090    
091            Iterator i = session.getAttributeNames().iterator();
092            while (i.hasNext())
093            {
094                String name = (String) i.next();
095    
096                if (name.startsWith(prefix))
097                    callback.handleAttribute(session, name);
098            }
099        }
100    
101        /**
102         * Builds a change key, used to identify the change within the {@link WebSession}. A change key
103         * can be used as a session attribute name, without reasonable fear of conflict.
104         * 
105         * @param strategyId
106         *            a unique identifier for a particular implementation of
107         *            {@link PropertyPersistenceStrategy}
108         * @param applicationId
109         *            a unique identifier for the application
110         * @param pageName
111         *            the name of the page containing the change
112         * @param idPath
113         *            the id path of the component within the page containing the page, possibly null
114         * @param propertyName
115         *            the name of the property
116         * @return the above values, seperated by commas (well, no comma between the prefix and the
117         *         application id)
118         */
119        public static String buildChangeKey(String strategyId, String applicationId, String pageName,
120                String idPath, String propertyName)
121        {
122            Defense.notNull(strategyId, "strategyId");
123            Defense.notNull(applicationId, "applicationId");
124            Defense.notNull(pageName, "pageName");
125            Defense.notNull(propertyName, "propertyName");
126    
127            StringBuffer buffer = new StringBuffer(strategyId);
128    
129            buffer.append(",");
130            buffer.append(applicationId);
131            buffer.append(",");
132            buffer.append(pageName);
133    
134            if (idPath != null)
135            {
136                buffer.append(",");
137                buffer.append(idPath);
138            }
139    
140            buffer.append(",");
141            buffer.append(propertyName);
142    
143            return buffer.toString();
144        }
145    }