diff options
Diffstat (limited to 'src/org/jivesoftware/smack/ChatManager.java')
-rw-r--r-- | src/org/jivesoftware/smack/ChatManager.java | 284 |
1 files changed, 284 insertions, 0 deletions
diff --git a/src/org/jivesoftware/smack/ChatManager.java b/src/org/jivesoftware/smack/ChatManager.java new file mode 100644 index 0000000..22dc3f9 --- /dev/null +++ b/src/org/jivesoftware/smack/ChatManager.java @@ -0,0 +1,284 @@ +/** + * $RCSfile$ + * $Revision: 2407 $ + * $Date: 2004-11-02 15:37:00 -0800 (Tue, 02 Nov 2004) $ + * + * Copyright 2003-2007 Jive Software. + * + * All rights reserved. Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jivesoftware.smack; + +import java.util.Collection; +import java.util.Collections; +import java.util.Map; +import java.util.Set; +import java.util.WeakHashMap; +import java.util.concurrent.CopyOnWriteArraySet; + +import org.jivesoftware.smack.filter.AndFilter; +import org.jivesoftware.smack.filter.FromContainsFilter; +import org.jivesoftware.smack.filter.PacketFilter; +import org.jivesoftware.smack.filter.ThreadFilter; +import org.jivesoftware.smack.packet.Message; +import org.jivesoftware.smack.packet.Packet; +import org.jivesoftware.smack.util.StringUtils; +import org.jivesoftware.smack.util.collections.ReferenceMap; + +import java.util.*; +import java.util.concurrent.CopyOnWriteArraySet; + +/** + * The chat manager keeps track of references to all current chats. It will not hold any references + * in memory on its own so it is neccesary to keep a reference to the chat object itself. To be + * made aware of new chats, register a listener by calling {@link #addChatListener(ChatManagerListener)}. + * + * @author Alexander Wenckus + */ +public class ChatManager { + + /** + * Returns the next unique id. Each id made up of a short alphanumeric + * prefix along with a unique numeric value. + * + * @return the next id. + */ + private static synchronized String nextID() { + return prefix + Long.toString(id++); + } + + /** + * A prefix helps to make sure that ID's are unique across mutliple instances. + */ + private static String prefix = StringUtils.randomString(5); + + /** + * Keeps track of the current increment, which is appended to the prefix to + * forum a unique ID. + */ + private static long id = 0; + + /** + * Maps thread ID to chat. + */ + private Map<String, Chat> threadChats = Collections.synchronizedMap(new ReferenceMap<String, Chat>(ReferenceMap.HARD, + ReferenceMap.WEAK)); + + /** + * Maps jids to chats + */ + private Map<String, Chat> jidChats = Collections.synchronizedMap(new ReferenceMap<String, Chat>(ReferenceMap.HARD, + ReferenceMap.WEAK)); + + /** + * Maps base jids to chats + */ + private Map<String, Chat> baseJidChats = Collections.synchronizedMap(new ReferenceMap<String, Chat>(ReferenceMap.HARD, + ReferenceMap.WEAK)); + + private Set<ChatManagerListener> chatManagerListeners + = new CopyOnWriteArraySet<ChatManagerListener>(); + + private Map<PacketInterceptor, PacketFilter> interceptors + = new WeakHashMap<PacketInterceptor, PacketFilter>(); + + private Connection connection; + + ChatManager(Connection connection) { + this.connection = connection; + + PacketFilter filter = new PacketFilter() { + public boolean accept(Packet packet) { + if (!(packet instanceof Message)) { + return false; + } + Message.Type messageType = ((Message) packet).getType(); + return messageType != Message.Type.groupchat && + messageType != Message.Type.headline; + } + }; + // Add a listener for all message packets so that we can deliver errant + // messages to the best Chat instance available. + connection.addPacketListener(new PacketListener() { + public void processPacket(Packet packet) { + Message message = (Message) packet; + Chat chat; + if (message.getThread() == null) { + chat = getUserChat(message.getFrom()); + } + else { + chat = getThreadChat(message.getThread()); + if (chat == null) { + // Try to locate the chat based on the sender of the message + chat = getUserChat(message.getFrom()); + } + } + + if(chat == null) { + chat = createChat(message); + } + deliverMessage(chat, message); + } + }, filter); + } + + /** + * Creates a new chat and returns it. + * + * @param userJID the user this chat is with. + * @param listener the listener which will listen for new messages from this chat. + * @return the created chat. + */ + public Chat createChat(String userJID, MessageListener listener) { + String threadID; + do { + threadID = nextID(); + } while (threadChats.get(threadID) != null); + + return createChat(userJID, threadID, listener); + } + + /** + * Creates a new chat using the specified thread ID, then returns it. + * + * @param userJID the jid of the user this chat is with + * @param thread the thread of the created chat. + * @param listener the listener to add to the chat + * @return the created chat. + */ + public Chat createChat(String userJID, String thread, MessageListener listener) { + if(thread == null) { + thread = nextID(); + } + Chat chat = threadChats.get(thread); + if(chat != null) { + throw new IllegalArgumentException("ThreadID is already used"); + } + chat = createChat(userJID, thread, true); + chat.addMessageListener(listener); + return chat; + } + + private Chat createChat(String userJID, String threadID, boolean createdLocally) { + Chat chat = new Chat(this, userJID, threadID); + threadChats.put(threadID, chat); + jidChats.put(userJID, chat); + baseJidChats.put(StringUtils.parseBareAddress(userJID), chat); + + for(ChatManagerListener listener : chatManagerListeners) { + listener.chatCreated(chat, createdLocally); + } + + return chat; + } + + private Chat createChat(Message message) { + String threadID = message.getThread(); + if(threadID == null) { + threadID = nextID(); + } + String userJID = message.getFrom(); + + return createChat(userJID, threadID, false); + } + + /** + * Try to get a matching chat for the given user JID. Try the full + * JID map first, the try to match on the base JID if no match is + * found. + * + * @param userJID + * @return + */ + private Chat getUserChat(String userJID) { + Chat match = jidChats.get(userJID); + + if (match == null) { + match = baseJidChats.get(StringUtils.parseBareAddress(userJID)); + } + return match; + } + + public Chat getThreadChat(String thread) { + return threadChats.get(thread); + } + + /** + * Register a new listener with the ChatManager to recieve events related to chats. + * + * @param listener the listener. + */ + public void addChatListener(ChatManagerListener listener) { + chatManagerListeners.add(listener); + } + + /** + * Removes a listener, it will no longer be notified of new events related to chats. + * + * @param listener the listener that is being removed + */ + public void removeChatListener(ChatManagerListener listener) { + chatManagerListeners.remove(listener); + } + + /** + * Returns an unmodifiable collection of all chat listeners currently registered with this + * manager. + * + * @return an unmodifiable collection of all chat listeners currently registered with this + * manager. + */ + public Collection<ChatManagerListener> getChatListeners() { + return Collections.unmodifiableCollection(chatManagerListeners); + } + + private void deliverMessage(Chat chat, Message message) { + // Here we will run any interceptors + chat.deliver(message); + } + + void sendMessage(Chat chat, Message message) { + for(Map.Entry<PacketInterceptor, PacketFilter> interceptor : interceptors.entrySet()) { + PacketFilter filter = interceptor.getValue(); + if(filter != null && filter.accept(message)) { + interceptor.getKey().interceptPacket(message); + } + } + // Ensure that messages being sent have a proper FROM value + if (message.getFrom() == null) { + message.setFrom(connection.getUser()); + } + connection.sendPacket(message); + } + + PacketCollector createPacketCollector(Chat chat) { + return connection.createPacketCollector(new AndFilter(new ThreadFilter(chat.getThreadID()), + new FromContainsFilter(chat.getParticipant()))); + } + + /** + * Adds an interceptor which intercepts any messages sent through chats. + * + * @param packetInterceptor the interceptor. + */ + public void addOutgoingMessageInterceptor(PacketInterceptor packetInterceptor) { + addOutgoingMessageInterceptor(packetInterceptor, null); + } + + public void addOutgoingMessageInterceptor(PacketInterceptor packetInterceptor, PacketFilter filter) { + if (packetInterceptor != null) { + interceptors.put(packetInterceptor, filter); + } + } +} |