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.ioc.internal.util.CollectionFactory.newList; 018 019import java.util.Collection; 020import java.util.Collections; 021import java.util.List; 022 023import org.apache.tapestry5.ioc.internal.util.InternalUtils; 024import org.apache.tapestry5.services.PersistentFieldChange; 025import org.apache.tapestry5.services.PersistentFieldStrategy; 026import org.apache.tapestry5.services.Request; 027import 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 */ 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 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}