Bukkit-API  1.7.9-R0.2
The inofficial Bukkit-API
Conversation.java
1 package org.bukkit.conversations;
2 
3 import org.bukkit.plugin.Plugin;
4 
5 import java.util.ArrayList;
6 import java.util.HashMap;
7 import java.util.List;
8 import java.util.Map;
9 
10 /**
11  * The Conversation class is responsible for tracking the current state of a
12  * conversation, displaying prompts to the user, and dispatching the user's
13  * response to the appropriate place. Conversation objects are not typically
14  * instantiated directly. Instead a {@link ConversationFactory} is used to
15  * construct identical conversations on demand.
16  * <p>
17  * Conversation flow consists of a directed graph of {@link Prompt} objects.
18  * Each time a prompt gets input from the user, it must return the next prompt
19  * in the graph. Since each Prompt chooses the next Prompt, complex
20  * conversation trees can be implemented where the nature of the player's
21  * response directs the flow of the conversation.
22  * <p>
23  * Each conversation has a {@link ConversationPrefix} that prepends all output
24  * from the conversation to the player. The ConversationPrefix can be used to
25  * display the plugin name or conversation status as the conversation evolves.
26  * <p>
27  * Each conversation has a timeout measured in the number of inactive seconds
28  * to wait before abandoning the conversation. If the inactivity timeout is
29  * reached, the conversation is abandoned and the user's incoming and outgoing
30  * chat is returned to normal.
31  * <p>
32  * You should not construct a conversation manually. Instead, use the {@link
33  * ConversationFactory} for access to all available options.
34  */
35 public class Conversation {
36 
37  private Prompt firstPrompt;
38  private boolean abandoned;
39  protected Prompt currentPrompt;
40  protected ConversationContext context;
41  protected boolean modal;
42  protected boolean localEchoEnabled;
43  protected ConversationPrefix prefix;
44  protected List<ConversationCanceller> cancellers;
45  protected List<ConversationAbandonedListener> abandonedListeners;
46 
47  /**
48  * Initializes a new Conversation.
49  *
50  * @param plugin The plugin that owns this conversation.
51  * @param forWhom The entity for whom this conversation is mediating.
52  * @param firstPrompt The first prompt in the conversation graph.
53  */
54  public Conversation(Plugin plugin, Conversable forWhom, Prompt firstPrompt) {
55  this(plugin, forWhom, firstPrompt, new HashMap<Object, Object>());
56  }
57 
58  /**
59  * Initializes a new Conversation.
60  *
61  * @param plugin The plugin that owns this conversation.
62  * @param forWhom The entity for whom this conversation is mediating.
63  * @param firstPrompt The first prompt in the conversation graph.
64  * @param initialSessionData Any initial values to put in the conversation
65  * context sessionData map.
66  */
67  public Conversation(Plugin plugin, Conversable forWhom, Prompt firstPrompt, Map<Object, Object> initialSessionData) {
68  this.firstPrompt = firstPrompt;
69  this.context = new ConversationContext(plugin, forWhom, initialSessionData);
70  this.modal = true;
71  this.localEchoEnabled = true;
72  this.prefix = new NullConversationPrefix();
73  this.cancellers = new ArrayList<ConversationCanceller>();
74  this.abandonedListeners = new ArrayList<ConversationAbandonedListener>();
75  }
76 
77  /**
78  * Gets the entity for whom this conversation is mediating.
79  *
80  * @return The entity.
81  */
83  return context.getForWhom();
84  }
85 
86  /**
87  * Gets the modality of this conversation. If a conversation is modal, all
88  * messages directed to the player are suppressed for the duration of the
89  * conversation.
90  *
91  * @return The conversation modality.
92  */
93  public boolean isModal() {
94  return modal;
95  }
96 
97  /**
98  * Sets the modality of this conversation. If a conversation is modal,
99  * all messages directed to the player are suppressed for the duration of
100  * the conversation.
101  *
102  * @param modal The new conversation modality.
103  */
104  void setModal(boolean modal) {
105  this.modal = modal;
106  }
107 
108  /**
109  * Gets the status of local echo for this conversation. If local echo is
110  * enabled, any text submitted to a conversation gets echoed back into the
111  * submitter's chat window.
112  *
113  * @return The status of local echo.
114  */
115  public boolean isLocalEchoEnabled() {
116  return localEchoEnabled;
117  }
118 
119  /**
120  * Sets the status of local echo for this conversation. If local echo is
121  * enabled, any text submitted to a conversation gets echoed back into the
122  * submitter's chat window.
123  *
124  * @param localEchoEnabled The status of local echo.
125  */
126  public void setLocalEchoEnabled(boolean localEchoEnabled) {
127  this.localEchoEnabled = localEchoEnabled;
128  }
129 
130  /**
131  * Gets the {@link ConversationPrefix} that prepends all output from this
132  * conversation.
133  *
134  * @return The ConversationPrefix in use.
135  */
137  return prefix;
138  }
139 
140  /**
141  * Sets the {@link ConversationPrefix} that prepends all output from this
142  * conversation.
143  *
144  * @param prefix The ConversationPrefix to use.
145  */
146  void setPrefix(ConversationPrefix prefix) {
147  this.prefix = prefix;
148  }
149 
150  /**
151  * Adds a {@link ConversationCanceller} to the cancellers collection.
152  *
153  * @param canceller The {@link ConversationCanceller} to add.
154  */
155  void addConversationCanceller(ConversationCanceller canceller) {
156  canceller.setConversation(this);
157  this.cancellers.add(canceller);
158  }
159 
160  /**
161  * Gets the list of {@link ConversationCanceller}s
162  *
163  * @return The list.
164  */
165  public List<ConversationCanceller> getCancellers() {
166  return cancellers;
167  }
168 
169  /**
170  * Returns the Conversation's {@link ConversationContext}.
171  *
172  * @return The ConversationContext.
173  */
175  return context;
176  }
177 
178  /**
179  * Displays the first prompt of this conversation and begins redirecting
180  * the user's chat responses.
181  */
182  public void begin() {
183  if (currentPrompt == null) {
184  abandoned = false;
185  currentPrompt = firstPrompt;
186  context.getForWhom().beginConversation(this);
187  }
188  }
189 
190  /**
191  * Returns Returns the current state of the conversation.
192  *
193  * @return The current state of the conversation.
194  */
196  if (currentPrompt != null) {
197  return ConversationState.STARTED;
198  } else if (abandoned) {
199  return ConversationState.ABANDONED;
200  } else {
201  return ConversationState.UNSTARTED;
202  }
203  }
204 
205  /**
206  * Passes player input into the current prompt. The next prompt (as
207  * determined by the current prompt) is then displayed to the user.
208  *
209  * @param input The user's chat text.
210  */
211  public void acceptInput(String input) {
212  if (currentPrompt != null) {
213 
214  // Echo the user's input
215  if (localEchoEnabled) {
216  context.getForWhom().sendRawMessage(prefix.getPrefix(context) + input);
217  }
218 
219  // Test for conversation abandonment based on input
220  for(ConversationCanceller canceller : cancellers) {
221  if (canceller.cancelBasedOnInput(context, input)) {
222  abandon(new ConversationAbandonedEvent(this, canceller));
223  return;
224  }
225  }
226 
227  // Not abandoned, output the next prompt
228  currentPrompt = currentPrompt.acceptInput(context, input);
230  }
231  }
232 
233  /**
234  * Adds a {@link ConversationAbandonedListener}.
235  *
236  * @param listener The listener to add.
237  */
239  abandonedListeners.add(listener);
240  }
241 
242  /**
243  * Removes a {@link ConversationAbandonedListener}.
244  *
245  * @param listener The listener to remove.
246  */
248  abandonedListeners.remove(listener);
249  }
250 
251  /**
252  * Abandons and resets the current conversation. Restores the user's
253  * normal chat behavior.
254  */
255  public void abandon() {
257  }
258 
259  /**
260  * Abandons and resets the current conversation. Restores the user's
261  * normal chat behavior.
262  *
263  * @param details Details about why the conversation was abandoned
264  */
265  public synchronized void abandon(ConversationAbandonedEvent details) {
266  if (!abandoned) {
267  abandoned = true;
268  currentPrompt = null;
269  context.getForWhom().abandonConversation(this);
270  for (ConversationAbandonedListener listener : abandonedListeners) {
271  listener.conversationAbandoned(details);
272  }
273  }
274  }
275 
276  /**
277  * Displays the next user prompt and abandons the conversation if the next
278  * prompt is null.
279  */
280  public void outputNextPrompt() {
281  if (currentPrompt == null) {
283  } else {
284  context.getForWhom().sendRawMessage(prefix.getPrefix(context) + currentPrompt.getPromptText(context));
285  if (!currentPrompt.blocksForInput(context)) {
286  currentPrompt = currentPrompt.acceptInput(context, null);
288  }
289  }
290  }
291 
292  public enum ConversationState {
293  UNSTARTED,
294  STARTED,
295  ABANDONED
296  }
297 }
synchronized void removeConversationAbandonedListener(ConversationAbandonedListener listener)
void abandonConversation(Conversation conversation)
void setLocalEchoEnabled(boolean localEchoEnabled)
Conversation(Plugin plugin, Conversable forWhom, Prompt firstPrompt, Map< Object, Object > initialSessionData)
void sendRawMessage(String message)
boolean beginConversation(Conversation conversation)
boolean blocksForInput(ConversationContext context)
String getPromptText(ConversationContext context)
List< ConversationCanceller > getCancellers()
synchronized void addConversationAbandonedListener(ConversationAbandonedListener listener)
Conversation(Plugin plugin, Conversable forWhom, Prompt firstPrompt)
String getPrefix(ConversationContext context)
synchronized void abandon(ConversationAbandonedEvent details)
Prompt acceptInput(ConversationContext context, String input)