summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/main/java/org/bukkit/conversations/Conversable.java7
-rw-r--r--src/main/java/org/bukkit/conversations/Conversation.java34
-rw-r--r--src/main/java/org/bukkit/conversations/ConversationAbandonedEvent.java48
-rw-r--r--src/main/java/org/bukkit/conversations/ConversationAbandonedListener.java13
-rw-r--r--src/main/java/org/bukkit/conversations/ConversationFactory.java21
-rw-r--r--src/main/java/org/bukkit/conversations/InactivityConversationCanceller.java2
-rw-r--r--src/main/java/org/bukkit/conversations/ManuallyAbandonedConversationCanceller.java19
-rw-r--r--src/test/java/org/bukkit/conversations/FakeConversable.java6
-rw-r--r--src/test/java/org/bukkit/plugin/messaging/TestPlayer.java5
9 files changed, 149 insertions, 6 deletions
diff --git a/src/main/java/org/bukkit/conversations/Conversable.java b/src/main/java/org/bukkit/conversations/Conversable.java
index 0633c1d9..4b6b568e 100644
--- a/src/main/java/org/bukkit/conversations/Conversable.java
+++ b/src/main/java/org/bukkit/conversations/Conversable.java
@@ -34,6 +34,13 @@ public interface Conversable {
public void abandonConversation(Conversation conversation);
/**
+ * Abandons an active conversation.
+ * @param conversation The conversation to abandon
+ * @param details Details about why the conversation was abandoned
+ */
+ public void abandonConversation(Conversation conversation, ConversationAbandonedEvent details);
+
+ /**
* Sends this sender a message raw
*
* @param message Message to be displayed
diff --git a/src/main/java/org/bukkit/conversations/Conversation.java b/src/main/java/org/bukkit/conversations/Conversation.java
index 55592552..43cc362b 100644
--- a/src/main/java/org/bukkit/conversations/Conversation.java
+++ b/src/main/java/org/bukkit/conversations/Conversation.java
@@ -1,6 +1,5 @@
package org.bukkit.conversations;
-import org.bukkit.command.CommandSender;
import org.bukkit.plugin.Plugin;
import java.util.ArrayList;
@@ -37,6 +36,7 @@ public class Conversation {
protected boolean localEchoEnabled;
protected ConversationPrefix prefix;
protected List<ConversationCanceller> cancellers;
+ protected List<ConversationAbandonedListener> abandonedListeners;
/**
* Initializes a new Conversation.
@@ -62,6 +62,7 @@ public class Conversation {
this.localEchoEnabled = true;
this.prefix = new NullConversationPrefix();
this.cancellers = new ArrayList<ConversationCanceller>();
+ this.abandonedListeners = new ArrayList<ConversationAbandonedListener>();
}
/**
@@ -190,7 +191,7 @@ public class Conversation {
// Test for conversation abandonment based on input
for(ConversationCanceller canceller : cancellers) {
if (canceller.cancelBasedOnInput(context, input)) {
- abandon();
+ abandon(new ConversationAbandonedEvent(this, canceller));
return;
}
}
@@ -202,13 +203,40 @@ public class Conversation {
}
/**
+ * Adds a {@link ConversationAbandonedListener}.
+ * @param listener The listener to add.
+ */
+ public synchronized void addConversationAbandonedListener(ConversationAbandonedListener listener) {
+ abandonedListeners.add(listener);
+ }
+
+ /**
+ * Removes a {@link ConversationAbandonedListener}.
+ * @param listener The listener to remove.
+ */
+ public synchronized void removeConversationAbandonedListener(ConversationAbandonedListener listener) {
+ abandonedListeners.remove(listener);
+ }
+
+ /**
* Abandons and resets the current conversation. Restores the user's normal chat behavior.
*/
public void abandon() {
+ abandon(new ConversationAbandonedEvent(this, new ManuallyAbandonedConversationCanceller()));
+ }
+
+ /**
+ * Abandons and resets the current conversation. Restores the user's normal chat behavior.
+ * @param details Details about why the conversation was abandoned
+ */
+ public synchronized void abandon(ConversationAbandonedEvent details) {
if (!abandoned) {
abandoned = true;
currentPrompt = null;
context.getForWhom().abandonConversation(this);
+ for (ConversationAbandonedListener listener : abandonedListeners) {
+ listener.conversationAbandoned(details);
+ }
}
}
@@ -217,7 +245,7 @@ public class Conversation {
*/
public void outputNextPrompt() {
if (currentPrompt == null) {
- abandon();
+ abandon(new ConversationAbandonedEvent(this));
} else {
context.getForWhom().sendRawMessage(prefix.getPrefix(context) + currentPrompt.getPromptText(context));
if (!currentPrompt.blocksForInput(context)) {
diff --git a/src/main/java/org/bukkit/conversations/ConversationAbandonedEvent.java b/src/main/java/org/bukkit/conversations/ConversationAbandonedEvent.java
new file mode 100644
index 00000000..720c87f0
--- /dev/null
+++ b/src/main/java/org/bukkit/conversations/ConversationAbandonedEvent.java
@@ -0,0 +1,48 @@
+package org.bukkit.conversations;
+
+import java.util.EventObject;
+
+/**
+ * ConversationAbandonedEvent contains information about an abandoned conversation.
+ */
+public class ConversationAbandonedEvent extends EventObject {
+
+ private ConversationContext context;
+ private ConversationCanceller canceller;
+
+ public ConversationAbandonedEvent(Conversation conversation) {
+ this(conversation, null);
+ }
+
+ public ConversationAbandonedEvent(Conversation conversation, ConversationCanceller canceller) {
+ super(conversation);
+ this.context = conversation.getContext();
+ this.canceller = canceller;
+ }
+
+ /**
+ * Gets the object that caused the conversation to be abandoned.
+ * @return The object that abandoned the conversation.
+ */
+ public ConversationCanceller getCanceller() {
+ return canceller;
+ }
+
+ /**
+ * Gets the abandoned conversation's conversation context.
+ * @return The abandoned conversation's conversation context.
+ */
+ public ConversationContext getContext() {
+ return context;
+ }
+
+ /**
+ * Indicates how the conversation was abandoned - naturally as part of the prompt chain or prematurely via a
+ * {@link ConversationCanceller}.
+ * @return True if the conversation is abandoned gracefully by a {@link Prompt} returning null
+ * or the next prompt. False of the conversations is abandoned prematurely by a ConversationCanceller.
+ */
+ public boolean gracefulExit() {
+ return canceller == null;
+ }
+}
diff --git a/src/main/java/org/bukkit/conversations/ConversationAbandonedListener.java b/src/main/java/org/bukkit/conversations/ConversationAbandonedListener.java
new file mode 100644
index 00000000..325f91a7
--- /dev/null
+++ b/src/main/java/org/bukkit/conversations/ConversationAbandonedListener.java
@@ -0,0 +1,13 @@
+package org.bukkit.conversations;
+
+import java.util.EventListener;
+
+/**
+ */
+public interface ConversationAbandonedListener extends EventListener {
+ /**
+ * Called whenever a {@link Conversation} is abandoned.
+ * @param abandonedEvent Contains details about the abandoned conversation.
+ */
+ public void conversationAbandoned(ConversationAbandonedEvent abandonedEvent);
+}
diff --git a/src/main/java/org/bukkit/conversations/ConversationFactory.java b/src/main/java/org/bukkit/conversations/ConversationFactory.java
index 6eb28692..d31a74d9 100644
--- a/src/main/java/org/bukkit/conversations/ConversationFactory.java
+++ b/src/main/java/org/bukkit/conversations/ConversationFactory.java
@@ -25,6 +25,7 @@ public class ConversationFactory {
protected Map<Object, Object> initialSessionData;
protected String playerOnlyMessage;
protected List<ConversationCanceller> cancellers;
+ protected List<ConversationAbandonedListener> abandonedListeners;
/**
* Constructs a ConversationFactory.
@@ -39,6 +40,7 @@ public class ConversationFactory {
initialSessionData = new HashMap<Object, Object>();
playerOnlyMessage = null;
cancellers = new ArrayList<ConversationCanceller>();
+ abandonedListeners = new ArrayList<ConversationAbandonedListener>();
}
/**
@@ -142,13 +144,23 @@ public class ConversationFactory {
}
/**
+ * Adds a {@link ConversationAbandonedListener} to all conversations constructed by this factory.
+ * @param listener The listener to add.
+ * @return This object.
+ */
+ public ConversationFactory addConversationAbandonedListener(ConversationAbandonedListener listener) {
+ abandonedListeners.add(listener);
+ return this;
+ }
+
+ /**
* Constructs a {@link Conversation} in accordance with the defaults set for this factory.
* @param forWhom The entity for whom the new conversation is mediating.
* @return A new conversation.
*/
public Conversation buildConversation(Conversable forWhom) {
//Abort conversation construction if we aren't supposed to talk to non-players
- if(playerOnlyMessage != null && !(forWhom instanceof Player)) {
+ if (playerOnlyMessage != null && !(forWhom instanceof Player)) {
return new Conversation(plugin, forWhom, new NotPlayerMessagePrompt());
}
@@ -163,9 +175,14 @@ public class ConversationFactory {
conversation.setPrefix(prefix);
//Clone the conversation cancellers
- for(ConversationCanceller canceller : cancellers) {
+ for (ConversationCanceller canceller : cancellers) {
conversation.addConversationCanceller(canceller.clone());
}
+
+ //Add the ConversationAbandonedListeners
+ for (ConversationAbandonedListener listener : abandonedListeners) {
+ conversation.addConversationAbandonedListener(listener);
+ }
return conversation;
}
diff --git a/src/main/java/org/bukkit/conversations/InactivityConversationCanceller.java b/src/main/java/org/bukkit/conversations/InactivityConversationCanceller.java
index ed0ec952..c1d0c9fd 100644
--- a/src/main/java/org/bukkit/conversations/InactivityConversationCanceller.java
+++ b/src/main/java/org/bukkit/conversations/InactivityConversationCanceller.java
@@ -48,7 +48,7 @@ public class InactivityConversationCanceller implements ConversationCanceller {
startTimer();
} else if (conversation.getState() == Conversation.ConversationState.STARTED) {
cancelling(conversation);
- conversation.abandon();
+ conversation.abandon(new ConversationAbandonedEvent(conversation, InactivityConversationCanceller.this));
}
}
}, timeoutSeconds * 20);
diff --git a/src/main/java/org/bukkit/conversations/ManuallyAbandonedConversationCanceller.java b/src/main/java/org/bukkit/conversations/ManuallyAbandonedConversationCanceller.java
new file mode 100644
index 00000000..6d7bd8fb
--- /dev/null
+++ b/src/main/java/org/bukkit/conversations/ManuallyAbandonedConversationCanceller.java
@@ -0,0 +1,19 @@
+package org.bukkit.conversations;
+
+/**
+ * The ManuallyAbandonedConversationCanceller is only used as part of a {@link ConversationAbandonedEvent} to indicate
+ * that the conversation was manually abandoned by programatically calling the abandon() method on it.
+ */
+public class ManuallyAbandonedConversationCanceller implements ConversationCanceller{
+ public void setConversation(Conversation conversation) {
+ throw new UnsupportedOperationException();
+ }
+
+ public boolean cancelBasedOnInput(ConversationContext context, String input) {
+ throw new UnsupportedOperationException();
+ }
+
+ public ConversationCanceller clone() {
+ throw new UnsupportedOperationException();
+ }
+}
diff --git a/src/test/java/org/bukkit/conversations/FakeConversable.java b/src/test/java/org/bukkit/conversations/FakeConversable.java
index a04bfc8c..87fb3113 100644
--- a/src/test/java/org/bukkit/conversations/FakeConversable.java
+++ b/src/test/java/org/bukkit/conversations/FakeConversable.java
@@ -14,6 +14,7 @@ public class FakeConversable implements Conversable {
public String lastSentMessage;
public Conversation begunConversation;
public Conversation abandonedConverstion;
+ public ConversationAbandonedEvent abandonedConversationEvent;
public boolean isConversing() {
return false;
@@ -33,6 +34,11 @@ public class FakeConversable implements Conversable {
abandonedConverstion = conversation;
}
+ public void abandonConversation(Conversation conversation, ConversationAbandonedEvent details) {
+ abandonedConverstion = conversation;
+ abandonedConversationEvent = details;
+ }
+
public void sendRawMessage(String message) {
lastSentMessage = message;
}
diff --git a/src/test/java/org/bukkit/plugin/messaging/TestPlayer.java b/src/test/java/org/bukkit/plugin/messaging/TestPlayer.java
index 9b5aa5e4..d812f5a7 100644
--- a/src/test/java/org/bukkit/plugin/messaging/TestPlayer.java
+++ b/src/test/java/org/bukkit/plugin/messaging/TestPlayer.java
@@ -10,6 +10,7 @@ import java.util.UUID;
import org.bukkit.*;
import org.bukkit.block.Block;
import org.bukkit.conversations.Conversation;
+import org.bukkit.conversations.ConversationAbandonedEvent;
import org.bukkit.entity.Arrow;
import org.bukkit.entity.Egg;
import org.bukkit.entity.Entity;
@@ -739,4 +740,8 @@ public class TestPlayer implements Player {
public void abandonConversation(Conversation conversation) {
throw new UnsupportedOperationException("Not supported yet.");
}
+
+ public void abandonConversation(Conversation conversation, ConversationAbandonedEvent details) {
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
}