    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
    015package org.apache.tapestry5.internal.services;
    016
    017import static org.apache.tapestry5.commons.util.CollectionFactory.newList;
    018
    019import java.util.Collection;
    020import java.util.Collections;
    021import java.util.List;
    022
    023import org.apache.tapestry5.http.services.Request;
    024import org.apache.tapestry5.http.services.Session;
    025import org.apache.tapestry5.ioc.internal.util.InternalUtils;
    026import org.apache.tapestry5.services.PersistentFieldChange;
    027import org.apache.tapestry5.services.PersistentFieldStrategy;
    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 */
    033public 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    public 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    public Object convertPersistedToApplicationValue(Object persistedValue)
    158    {
    159        return persistedValue;
    160    }
    161}