001/** 002 * 003 * Copyright 2003-2007 Jive Software. 004 * 005 * Licensed under the Apache License, Version 2.0 (the "License"); 006 * you may not use this file except in compliance with the License. 007 * You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017 018package org.jivesoftware.smack; 019 020import org.jivesoftware.smack.SmackException.NotConnectedException; 021import org.jivesoftware.smack.packet.Message; 022 023import java.util.Set; 024import java.util.Collection; 025import java.util.Collections; 026import java.util.concurrent.CopyOnWriteArraySet; 027 028/** 029 * A chat is a series of messages sent between two users. Each chat has a unique 030 * thread ID, which is used to track which messages are part of a particular 031 * conversation. Some messages are sent without a thread ID, and some clients 032 * don't send thread IDs at all. Therefore, if a message without a thread ID 033 * arrives it is routed to the most recently created Chat with the message 034 * sender. 035 * 036 * @author Matt Tucker 037 */ 038public class Chat { 039 040 private ChatManager chatManager; 041 private String threadID; 042 private String participant; 043 private final Set<MessageListener> listeners = new CopyOnWriteArraySet<MessageListener>(); 044 045 /** 046 * Creates a new chat with the specified user and thread ID. 047 * 048 * @param chatManager the chatManager the chat will use. 049 * @param participant the user to chat with. 050 * @param threadID the thread ID to use. 051 */ 052 Chat(ChatManager chatManager, String participant, String threadID) { 053 this.chatManager = chatManager; 054 this.participant = participant; 055 this.threadID = threadID; 056 } 057 058 /** 059 * Returns the thread id associated with this chat, which corresponds to the 060 * <tt>thread</tt> field of XMPP messages. This method may return <tt>null</tt> 061 * if there is no thread ID is associated with this Chat. 062 * 063 * @return the thread ID of this chat. 064 */ 065 public String getThreadID() { 066 return threadID; 067 } 068 069 /** 070 * Returns the name of the user the chat is with. 071 * 072 * @return the name of the user the chat is occuring with. 073 */ 074 public String getParticipant() { 075 return participant; 076 } 077 078 /** 079 * Sends the specified text as a message to the other chat participant. 080 * This is a convenience method for: 081 * 082 * <pre> 083 * Message message = chat.createMessage(); 084 * message.setBody(messageText); 085 * chat.sendMessage(message); 086 * </pre> 087 * 088 * @param text the text to send. 089 * @throws XMPPException if sending the message fails. 090 * @throws NotConnectedException 091 */ 092 public void sendMessage(String text) throws XMPPException, NotConnectedException { 093 Message message = new Message(participant, Message.Type.chat); 094 message.setThread(threadID); 095 message.setBody(text); 096 chatManager.sendMessage(this, message); 097 } 098 099 /** 100 * Sends a message to the other chat participant. The thread ID, recipient, 101 * and message type of the message will automatically set to those of this chat. 102 * 103 * @param message the message to send. 104 * @throws NotConnectedException 105 */ 106 public void sendMessage(Message message) throws NotConnectedException { 107 // Force the recipient, message type, and thread ID since the user elected 108 // to send the message through this chat object. 109 message.setTo(participant); 110 message.setType(Message.Type.chat); 111 message.setThread(threadID); 112 chatManager.sendMessage(this, message); 113 } 114 115 /** 116 * Adds a packet listener that will be notified of any new messages in the 117 * chat. 118 * 119 * @param listener a packet listener. 120 */ 121 public void addMessageListener(MessageListener listener) { 122 if(listener == null) { 123 return; 124 } 125 // TODO these references should be weak. 126 listeners.add(listener); 127 } 128 129 public void removeMessageListener(MessageListener listener) { 130 listeners.remove(listener); 131 } 132 133 /** 134 * Closes the Chat and removes all references to it from the {@link ChatManager}. The chat will 135 * be unusable when this method returns, so it's recommend to drop all references to the 136 * instance right after calling {@link #close()}. 137 */ 138 public void close() { 139 chatManager.closeChat(this); 140 listeners.clear(); 141 } 142 143 /** 144 * Returns an unmodifiable collection of all of the listeners registered with this chat. 145 * 146 * @return an unmodifiable collection of all of the listeners registered with this chat. 147 */ 148 public Collection<MessageListener> getListeners() { 149 return Collections.unmodifiableCollection(listeners); 150 } 151 152 /** 153 * Creates a {@link org.jivesoftware.smack.PacketCollector} which will accumulate the Messages 154 * for this chat. Always cancel PacketCollectors when finished with them as they will accumulate 155 * messages indefinitely. 156 * 157 * @return the PacketCollector which returns Messages for this chat. 158 */ 159 public PacketCollector createCollector() { 160 return chatManager.createPacketCollector(this); 161 } 162 163 /** 164 * Delivers a message directly to this chat, which will add the message 165 * to the collector and deliver it to all listeners registered with the 166 * Chat. This is used by the XMPPConnection class to deliver messages 167 * without a thread ID. 168 * 169 * @param message the message. 170 */ 171 void deliver(Message message) { 172 // Because the collector and listeners are expecting a thread ID with 173 // a specific value, set the thread ID on the message even though it 174 // probably never had one. 175 message.setThread(threadID); 176 177 for (MessageListener listener : listeners) { 178 listener.processMessage(this, message); 179 } 180 } 181 182 @Override 183 public String toString() { 184 return "Chat [(participant=" + participant + "), (thread=" + threadID + ")]"; 185 } 186 187 @Override 188 public int hashCode() { 189 int hash = 1; 190 hash = hash * 31 + threadID.hashCode(); 191 hash = hash * 31 + participant.hashCode(); 192 return hash; 193 } 194 195 @Override 196 public boolean equals(Object obj) { 197 return obj instanceof Chat 198 && threadID.equals(((Chat)obj).getThreadID()) 199 && participant.equals(((Chat)obj).getParticipant()); 200 } 201}