1/**
2 * $RCSfile$
3 * $Revision$
4 * $Date$
5 *
6 * Copyright 2003-2007 Jive Software.
7 *
8 * All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
9 * you may not use this file except in compliance with the License.
10 * You may obtain a copy of the License at
11 *
12 *     http://www.apache.org/licenses/LICENSE-2.0
13 *
14 * Unless required by applicable law or agreed to in writing, software
15 * distributed under the License is distributed on an "AS IS" BASIS,
16 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17 * See the License for the specific language governing permissions and
18 * limitations under the License.
19 */
20
21package org.jivesoftware.smack;
22
23import org.jivesoftware.smack.packet.Message;
24
25import java.util.Set;
26import java.util.Collection;
27import java.util.Collections;
28import java.util.concurrent.CopyOnWriteArraySet;
29
30/**
31 * A chat is a series of messages sent between two users. Each chat has a unique
32 * thread ID, which is used to track which messages are part of a particular
33 * conversation. Some messages are sent without a thread ID, and some clients
34 * don't send thread IDs at all. Therefore, if a message without a thread ID
35 * arrives it is routed to the most recently created Chat with the message
36 * sender.
37 *
38 * @author Matt Tucker
39 */
40public class Chat {
41
42    private ChatManager chatManager;
43    private String threadID;
44    private String participant;
45    private final Set<MessageListener> listeners = new CopyOnWriteArraySet<MessageListener>();
46
47    /**
48     * Creates a new chat with the specified user and thread ID.
49     *
50     * @param chatManager the chatManager the chat will use.
51     * @param participant the user to chat with.
52     * @param threadID the thread ID to use.
53     */
54    Chat(ChatManager chatManager, String participant, String threadID) {
55        this.chatManager = chatManager;
56        this.participant = participant;
57        this.threadID = threadID;
58    }
59
60    /**
61     * Returns the thread id associated with this chat, which corresponds to the
62     * <tt>thread</tt> field of XMPP messages. This method may return <tt>null</tt>
63     * if there is no thread ID is associated with this Chat.
64     *
65     * @return the thread ID of this chat.
66     */
67    public String getThreadID() {
68        return threadID;
69    }
70
71    /**
72     * Returns the name of the user the chat is with.
73     *
74     * @return the name of the user the chat is occuring with.
75     */
76    public String getParticipant() {
77        return participant;
78    }
79
80    /**
81     * Sends the specified text as a message to the other chat participant.
82     * This is a convenience method for:
83     *
84     * <pre>
85     *     Message message = chat.createMessage();
86     *     message.setBody(messageText);
87     *     chat.sendMessage(message);
88     * </pre>
89     *
90     * @param text the text to send.
91     * @throws XMPPException if sending the message fails.
92     */
93    public void sendMessage(String text) throws XMPPException {
94        Message message = new Message(participant, Message.Type.chat);
95        message.setThread(threadID);
96        message.setBody(text);
97        chatManager.sendMessage(this, message);
98    }
99
100    /**
101     * Sends a message to the other chat participant. The thread ID, recipient,
102     * and message type of the message will automatically set to those of this chat.
103     *
104     * @param message the message to send.
105     * @throws XMPPException if an error occurs sending the message.
106     */
107    public void sendMessage(Message message) throws XMPPException {
108        // Force the recipient, message type, and thread ID since the user elected
109        // to send the message through this chat object.
110        message.setTo(participant);
111        message.setType(Message.Type.chat);
112        message.setThread(threadID);
113        chatManager.sendMessage(this, message);
114    }
115
116    /**
117     * Adds a packet listener that will be notified of any new messages in the
118     * chat.
119     *
120     * @param listener a packet listener.
121     */
122    public void addMessageListener(MessageListener listener) {
123        if(listener == null) {
124            return;
125        }
126        // TODO these references should be weak.
127        listeners.add(listener);
128    }
129
130    public void removeMessageListener(MessageListener listener) {
131        listeners.remove(listener);
132    }
133
134    /**
135     * Returns an unmodifiable collection of all of the listeners registered with this chat.
136     *
137     * @return an unmodifiable collection of all of the listeners registered with this chat.
138     */
139    public Collection<MessageListener> getListeners() {
140        return Collections.unmodifiableCollection(listeners);
141    }
142
143    /**
144     * Creates a {@link org.jivesoftware.smack.PacketCollector} which will accumulate the Messages
145     * for this chat. Always cancel PacketCollectors when finished with them as they will accumulate
146     * messages indefinitely.
147     *
148     * @return the PacketCollector which returns Messages for this chat.
149     */
150    public PacketCollector createCollector() {
151        return chatManager.createPacketCollector(this);
152    }
153
154    /**
155     * Delivers a message directly to this chat, which will add the message
156     * to the collector and deliver it to all listeners registered with the
157     * Chat. This is used by the Connection class to deliver messages
158     * without a thread ID.
159     *
160     * @param message the message.
161     */
162    void deliver(Message message) {
163        // Because the collector and listeners are expecting a thread ID with
164        // a specific value, set the thread ID on the message even though it
165        // probably never had one.
166        message.setThread(threadID);
167
168        for (MessageListener listener : listeners) {
169            listener.processMessage(this, message);
170        }
171    }
172
173
174    @Override
175    public boolean equals(Object obj) {
176        return obj instanceof Chat
177                && threadID.equals(((Chat)obj).getThreadID())
178                && participant.equals(((Chat)obj).getParticipant());
179    }
180}