From c84229ad72e938608dbfbeafbeb7c4940f0bf3de Mon Sep 17 00:00:00 2001 From: rmichela Date: Sun, 4 Mar 2012 16:29:56 -0500 Subject: [Bleeding] Added ConversationAbandonedEvent and supporting infrastructure. Whenever a conversation exits, the ConversationAbandonedEvent is triggered with details about how the conversation ended and what, if anything caused it to end. Fixes BUKKIT-986 --- .../java/org/bukkit/conversations/Conversable.java | 7 ++++ .../org/bukkit/conversations/Conversation.java | 34 +++++++++++++-- .../conversations/ConversationAbandonedEvent.java | 48 ++++++++++++++++++++++ .../ConversationAbandonedListener.java | 13 ++++++ .../bukkit/conversations/ConversationFactory.java | 21 +++++++++- .../InactivityConversationCanceller.java | 2 +- .../ManuallyAbandonedConversationCanceller.java | 19 +++++++++ 7 files changed, 138 insertions(+), 6 deletions(-) create mode 100644 src/main/java/org/bukkit/conversations/ConversationAbandonedEvent.java create mode 100644 src/main/java/org/bukkit/conversations/ConversationAbandonedListener.java create mode 100644 src/main/java/org/bukkit/conversations/ManuallyAbandonedConversationCanceller.java (limited to 'src/main/java/org') 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 @@ -33,6 +33,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 * 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 cancellers; + protected List abandonedListeners; /** * Initializes a new Conversation. @@ -62,6 +62,7 @@ public class Conversation { this.localEchoEnabled = true; this.prefix = new NullConversationPrefix(); this.cancellers = new ArrayList(); + this.abandonedListeners = new ArrayList(); } /** @@ -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; } } @@ -201,14 +202,41 @@ 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 initialSessionData; protected String playerOnlyMessage; protected List cancellers; + protected List abandonedListeners; /** * Constructs a ConversationFactory. @@ -39,6 +40,7 @@ public class ConversationFactory { initialSessionData = new HashMap(); playerOnlyMessage = null; cancellers = new ArrayList(); + abandonedListeners = new ArrayList(); } /** @@ -141,6 +143,16 @@ public class ConversationFactory { return this; } + /** + * 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. @@ -148,7 +160,7 @@ public class ConversationFactory { */ 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(); + } +} -- cgit v1.2.3