001 package org.bukkit.conversations;
002
003 import org.bukkit.entity.Player;
004 import org.bukkit.plugin.Plugin;
005
006 import java.util.ArrayList;
007 import java.util.HashMap;
008 import java.util.List;
009 import java.util.Map;
010
011 /**
012 * A ConversationFactory is responsible for creating a {@link Conversation}
013 * from a predefined template. A ConversationFactory is typically created when
014 * a plugin is instantiated and builds a Conversation each time a user
015 * initiates a conversation with the plugin. Each Conversation maintains its
016 * own state and calls back as needed into the plugin.
017 * <p>
018 * The ConversationFactory implements a fluid API, allowing parameters to be
019 * set as an extension to the constructor.
020 */
021 public class ConversationFactory {
022
023 protected Plugin plugin;
024 protected boolean isModal;
025 protected boolean localEchoEnabled;
026 protected ConversationPrefix prefix;
027 protected Prompt firstPrompt;
028 protected Map<Object, Object> initialSessionData;
029 protected String playerOnlyMessage;
030 protected List<ConversationCanceller> cancellers;
031 protected List<ConversationAbandonedListener> abandonedListeners;
032
033 /**
034 * Constructs a ConversationFactory.
035 *
036 * @param plugin The plugin that owns the factory.
037 */
038 public ConversationFactory(Plugin plugin)
039 {
040 this.plugin = plugin;
041 isModal = true;
042 localEchoEnabled = true;
043 prefix = new NullConversationPrefix();
044 firstPrompt = Prompt.END_OF_CONVERSATION;
045 initialSessionData = new HashMap<Object, Object>();
046 playerOnlyMessage = null;
047 cancellers = new ArrayList<ConversationCanceller>();
048 abandonedListeners = new ArrayList<ConversationAbandonedListener>();
049 }
050
051 /**
052 * Sets the modality of all {@link Conversation}s created by this factory.
053 * If a conversation is modal, all messages directed to the player are
054 * suppressed for the duration of the conversation.
055 * <p>
056 * The default is True.
057 *
058 * @param modal The modality of all conversations to be created.
059 * @return This object.
060 */
061 public ConversationFactory withModality(boolean modal)
062 {
063 isModal = modal;
064 return this;
065 }
066
067 /**
068 * Sets the local echo status for all {@link Conversation}s created by
069 * this factory. If local echo is enabled, any text submitted to a
070 * conversation gets echoed back into the submitter's chat window.
071 *
072 * @param localEchoEnabled The status of local echo.
073 * @return This object.
074 */
075 public ConversationFactory withLocalEcho(boolean localEchoEnabled) {
076 this.localEchoEnabled = localEchoEnabled;
077 return this;
078 }
079
080 /**
081 * Sets the {@link ConversationPrefix} that prepends all output from all
082 * generated conversations.
083 * <p>
084 * The default is a {@link NullConversationPrefix};
085 *
086 * @param prefix The ConversationPrefix to use.
087 * @return This object.
088 */
089 public ConversationFactory withPrefix(ConversationPrefix prefix) {
090 this.prefix = prefix;
091 return this;
092 }
093
094 /**
095 * Sets the number of inactive seconds to wait before automatically
096 * abandoning all generated conversations.
097 * <p>
098 * The default is 600 seconds (5 minutes).
099 *
100 * @param timeoutSeconds The number of seconds to wait.
101 * @return This object.
102 */
103 public ConversationFactory withTimeout(int timeoutSeconds) {
104 return withConversationCanceller(new InactivityConversationCanceller(plugin, timeoutSeconds));
105 }
106
107 /**
108 * Sets the first prompt to use in all generated conversations.
109 * <p>
110 * The default is Prompt.END_OF_CONVERSATION.
111 *
112 * @param firstPrompt The first prompt.
113 * @return This object.
114 */
115 public ConversationFactory withFirstPrompt(Prompt firstPrompt) {
116 this.firstPrompt = firstPrompt;
117 return this;
118 }
119
120 /**
121 * Sets any initial data with which to populate the conversation context
122 * sessionData map.
123 *
124 * @param initialSessionData The conversation context's initial
125 * sessionData.
126 * @return This object.
127 */
128 public ConversationFactory withInitialSessionData(Map<Object, Object> initialSessionData) {
129 this.initialSessionData = initialSessionData;
130 return this;
131 }
132
133 /**
134 * Sets the player input that, when received, will immediately terminate
135 * the conversation.
136 *
137 * @param escapeSequence Input to terminate the conversation.
138 * @return This object.
139 */
140 public ConversationFactory withEscapeSequence(String escapeSequence) {
141 return withConversationCanceller(new ExactMatchConversationCanceller(escapeSequence));
142 }
143
144
145 /**
146 * Adds a {@link ConversationCanceller} to constructed conversations.
147 *
148 * @param canceller The {@link ConversationCanceller} to add.
149 * @return This object.
150 */
151 public ConversationFactory withConversationCanceller(ConversationCanceller canceller) {
152 cancellers.add(canceller);
153 return this;
154 }
155
156 /**
157 * Prevents this factory from creating a conversation for non-player
158 * {@link Conversable} objects.
159 *
160 * @param playerOnlyMessage The message to return to a non-play in lieu of
161 * starting a conversation.
162 * @return This object.
163 */
164 public ConversationFactory thatExcludesNonPlayersWithMessage(String playerOnlyMessage) {
165 this.playerOnlyMessage = playerOnlyMessage;
166 return this;
167 }
168
169 /**
170 * Adds a {@link ConversationAbandonedListener} to all conversations
171 * constructed by this factory.
172 *
173 * @param listener The listener to add.
174 * @return This object.
175 */
176 public ConversationFactory addConversationAbandonedListener(ConversationAbandonedListener listener) {
177 abandonedListeners.add(listener);
178 return this;
179 }
180
181 /**
182 * Constructs a {@link Conversation} in accordance with the defaults set
183 * for this factory.
184 *
185 * @param forWhom The entity for whom the new conversation is mediating.
186 * @return A new conversation.
187 */
188 public Conversation buildConversation(Conversable forWhom) {
189 //Abort conversation construction if we aren't supposed to talk to non-players
190 if (playerOnlyMessage != null && !(forWhom instanceof Player)) {
191 return new Conversation(plugin, forWhom, new NotPlayerMessagePrompt());
192 }
193
194 //Clone any initial session data
195 Map<Object, Object> copiedInitialSessionData = new HashMap<Object, Object>();
196 copiedInitialSessionData.putAll(initialSessionData);
197
198 //Build and return a conversation
199 Conversation conversation = new Conversation(plugin, forWhom, firstPrompt, copiedInitialSessionData);
200 conversation.setModal(isModal);
201 conversation.setLocalEchoEnabled(localEchoEnabled);
202 conversation.setPrefix(prefix);
203
204 //Clone the conversation cancellers
205 for (ConversationCanceller canceller : cancellers) {
206 conversation.addConversationCanceller(canceller.clone());
207 }
208
209 //Add the ConversationAbandonedListeners
210 for (ConversationAbandonedListener listener : abandonedListeners) {
211 conversation.addConversationAbandonedListener(listener);
212 }
213
214 return conversation;
215 }
216
217 private class NotPlayerMessagePrompt extends MessagePrompt {
218
219 public String getPromptText(ConversationContext context) {
220 return playerOnlyMessage;
221 }
222
223 @Override
224 protected Prompt getNextPrompt(ConversationContext context) {
225 return Prompt.END_OF_CONVERSATION;
226 }
227 }
228 }