    001    // Copyright 2007, 2008, 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.internal.services;
    016
    017    import static org.apache.tapestry5.ioc.internal.util.CollectionFactory.newList;
    018
    019    import java.util.Collection;
    020    import java.util.Collections;
    021    import java.util.List;
    022
    023    import org.apache.tapestry5.ioc.internal.util.InternalUtils;
    024    import org.apache.tapestry5.services.PersistentFieldChange;
    025    import org.apache.tapestry5.services.PersistentFieldStrategy;
    026    import org.apache.tapestry5.services.Request;
    027    import org.apache.tapestry5.services.Session;
    028
    029    /**
    030     * Base class for strategies that store their values as keys in the session. Implements a uniform format for the keys,
    031     * based on a prefix to identify the particular strategy.
    032     */
    033    public abstract class AbstractSessionPersistentFieldStrategy implements PersistentFieldStrategy
    034    {
    035        private final String prefix;
    036
    037        private final Request request;
    038
    039        protected AbstractSessionPersistentFieldStrategy(String prefix, Request request)
    040        {
    041            this.prefix = prefix;
    042            this.request = request;
    043        }
    044
    045        public final Collection<PersistentFieldChange> gatherFieldChanges(String pageName)
    046        {
    047            Session session = request.getSession(false);
    048
    049            if (session == null) return Collections.emptyList();
    050
    051            List<PersistentFieldChange> result = newList();
    052
    053            String fullPrefix = prefix + pageName + ":";
    054
    055            for (String name : session.getAttributeNames(fullPrefix))
    056            {
    057                Object persistedValue = session.getAttribute(name);
    058
    059                Object applicationValue = persistedValue == null ? null : convertPersistedToApplicationValue(
    060                        persistedValue);
    061
    062                PersistentFieldChange change = buildChange(name, applicationValue);
    063
    064                result.add(change);
    065
    066                didReadChange(session, name);
    067            }
    068
    069            return result;
    070        }
    071
    072        public void discardChanges(String pageName)
    073        {
    074            Session session = request.getSession(false);
    075
    076            if (session == null) return;
    077
    078            String fullPrefix = prefix + pageName + ":";
    079
    080            for (String name : session.getAttributeNames(fullPrefix))
    081            {
    082                session.setAttribute(name, null);
    083            }
    084        }
    085
    086        /**
    087         * Called after each key is read by {@link #gatherFieldChanges(String)}. This implementation does nothing,
    088         * subclasses may override.
    089         *
    090         * @param session       the session from which a value was just read
    091         * @param attributeName the name of the attribute used to read a value
    092         */
    093        protected void didReadChange(Session session, String attributeName)
    094        {
    095        }
    096
    097        private PersistentFieldChange buildChange(String name, Object newValue)
    098        {
    099            String[] chunks = name.split(":");
    100
    101            // Will be empty string for the root component
    102            String componentId = chunks[2];
    103            String fieldName = chunks[3];
    104
    105            return new PersistentFieldChangeImpl(componentId, fieldName, newValue);
    106        }
    107
    108        public final void postChange(String pageName, String componentId, String fieldName,
    109                                     Object newValue)
    110        {
    111            assert InternalUtils.isNonBlank(pageName);
    112            assert InternalUtils.isNonBlank(fieldName);
    113            Object persistedValue = newValue == null ? null : convertApplicationValueToPersisted(newValue);
    114
    115            StringBuilder builder = new StringBuilder(prefix);
    116            builder.append(pageName);
    117            builder.append(':');
    118
    119            if (componentId != null) builder.append(componentId);
    120
    121            builder.append(':');
    122            builder.append(fieldName);
    123
    124            Session session = request.getSession(persistedValue != null);
    125
    126            // TAPESTRY-2308: The session will be false when newValue is null and the session
    127            // does not already exist.
    128
    129            if (session != null)
    130            {
    131                session.setAttribute(builder.toString(), persistedValue);
    132            }
    133        }
    134
    135        /**
    136         * Hook that allows a value to be converted as it is written to the session. Passed the new value provided by the
    137         * application, returns the object to be stored in the session. This implementation simply returns the provided
    138         * value.
    139         *
    140         * @param newValue non-null value
    141         * @return persisted value
    142         * @see #convertPersistedToApplicationValue(Object)
    143         */
    144        protected Object convertApplicationValueToPersisted(Object newValue)
    145        {
    146            return newValue;
    147        }
    148
    149        /**
    150         * Converts a persisted value stored in the session back into an application value.   This implementation returns
    151         * the persisted value as is.
    152         *
    153         * @param persistedValue non-null persisted value
    154         * @return application value
    155         * @see #convertPersistedToApplicationValue(Object)
    156         */
    157        protected Object convertPersistedToApplicationValue(Object persistedValue)
    158        {
    159            return persistedValue;
    160        }
    161    }