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 }