001 package org.tynamo.conversations.services;
002
003 import static org.apache.tapestry5.ioc.internal.util.CollectionFactory.newList;
004 import static org.apache.tapestry5.ioc.internal.util.Defense.notBlank;
005
006 import java.util.Collection;
007 import java.util.Collections;
008 import java.util.List;
009
010 import org.apache.tapestry5.internal.services.PersistentFieldChangeImpl;
011 import org.apache.tapestry5.services.PersistentFieldChange;
012 import org.apache.tapestry5.services.PersistentFieldStrategy;
013 import org.apache.tapestry5.services.Request;
014 import org.apache.tapestry5.services.Session;
015
016 public class ConversationalPersistentFieldStrategy implements PersistentFieldStrategy {
017 /**
018 * Prefix used to identify keys stored in the session that are being used to store persistent field data.
019 */
020 static final String PREFIX = "state:";
021
022 private final ConversationManager conversationManager;
023
024 public ConversationalPersistentFieldStrategy(Request request, ConversationManager conversationManager) {
025 this.prefix = PREFIX;
026 this.request = request;
027 conversationManager.setPagePersistentFieldStrategy(this);
028 this.conversationManager = conversationManager;
029 }
030
031 private final String prefix;
032
033 private final Request request;
034
035 private String buildPrefix(String pageName) {
036 // System.out.println("Conversation id is: " + conversationManager.getActiveConversation());
037 return prefix + pageName + "_conversation_" + conversationManager.getActiveConversation() + ":";
038 }
039
040 public final Collection<PersistentFieldChange> gatherFieldChanges(String pageName) {
041 Session session = request.getSession(false);
042
043 if (session == null) return Collections.emptyList();
044
045 List<PersistentFieldChange> result = newList();
046
047 // if conversation is not active don't gather any changes
048 if (conversationManager.getActiveConversation() == null) return result;
049
050 String fullPrefix = buildPrefix(pageName);
051
052 for (String name : session.getAttributeNames(fullPrefix)) {
053 Object persistedValue = session.getAttribute(name);
054
055 Object applicationValue = persistedValue == null ? null : convertPersistedToApplicationValue(persistedValue);
056
057 PersistentFieldChange change = buildChange(name, applicationValue);
058
059 result.add(change);
060
061 didReadChange(session, name);
062 }
063
064 return result;
065 }
066
067 public void discardChanges(String pageName) {
068 Session session = request.getSession(false);
069
070 if (session == null || conversationManager.getActiveConversation() == null) return;
071
072 String fullPrefix = buildPrefix(pageName);
073
074 for (String name : session.getAttributeNames(fullPrefix)) {
075 session.setAttribute(name, null);
076 }
077 }
078
079 /**
080 * Called after each key is read by {@link #gatherFieldChanges(String)}. This implementation does nothing, subclasses
081 * may override.
082 *
083 * @param session
084 * the session from which a value was just read
085 * @param attributeName
086 * the name of the attribute used to read a value
087 */
088 protected void didReadChange(Session session, String attributeName) {
089 }
090
091 private PersistentFieldChange buildChange(String name, Object newValue) {
092 String[] chunks = name.split(":");
093
094 // Will be empty string for the root component
095 String componentId = chunks[2];
096 String fieldName = chunks[3];
097
098 return new PersistentFieldChangeImpl(componentId, fieldName, newValue);
099 }
100
101 public final void postChange(String pageName, String componentId, String fieldName, Object newValue) {
102 notBlank(pageName, "pageName");
103 notBlank(fieldName, "fieldName");
104
105 // If no active conversation, no changes to post
106 if (conversationManager.getActiveConversation() == null) return;
107
108 Object persistedValue = newValue == null ? null : convertApplicationValueToPersisted(newValue);
109
110 StringBuilder builder = new StringBuilder(buildPrefix(pageName));
111
112 if (componentId != null) builder.append(componentId);
113
114 builder.append(':');
115 builder.append(fieldName);
116
117 Session session = request.getSession(persistedValue != null);
118
119 // TAPESTRY-2308: The session will be false when newValue is null and the session
120 // does not already exist.
121
122 if (session != null) {
123 session.setAttribute(builder.toString(), persistedValue);
124 }
125 }
126
127 /**
128 * Hook that allows a value to be converted as it is written to the session. Passed the new value provided by the
129 * application, returns the object to be stored in the session. This implementation simply returns the provided value.
130 *
131 * @param newValue
132 * non-null value
133 * @return persisted value
134 * @see #convertPersistedToApplicationValue(Object)
135 */
136 protected Object convertApplicationValueToPersisted(Object newValue) {
137 return newValue;
138 }
139
140 /**
141 * Converts a persisted value stored in the session back into an application value. This implementation returns the
142 * persisted value as is.
143 *
144 * @param persistedValue
145 * non-null persisted value
146 * @return application value
147 * @see #convertPersistedToApplicationValue(Object)
148 */
149 protected Object convertPersistedToApplicationValue(Object persistedValue) {
150 return persistedValue;
151 }
152 }