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