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.chat;
019
020import java.util.Collections;
021import java.util.Set;
022import java.util.concurrent.CopyOnWriteArraySet;
023
024import org.jivesoftware.smack.SmackException.NotConnectedException;
025import org.jivesoftware.smack.StanzaCollector;
026import org.jivesoftware.smack.packet.Message;
027import org.jivesoftware.smack.packet.MessageBuilder;
028import org.jivesoftware.smack.packet.StanzaBuilder;
029import org.jivesoftware.smack.util.StringUtils;
030
031import org.jxmpp.jid.EntityJid;
032
033/**
034 * A chat is a series of messages sent between two users. Each chat has a unique
035 * thread ID, which is used to track which messages are part of a particular
036 * conversation. Some messages are sent without a thread ID, and some clients
037 * don't send thread IDs at all. Therefore, if a message without a thread ID
038 * arrives it is routed to the most recently created Chat with the message
039 * sender.
040 *
041 * @author Matt Tucker
042 * @deprecated use <code>org.jivesoftware.smack.chat2.Chat</code> from <code>smack-extensions</code> instead.
043 */
044@Deprecated
045public class Chat {
046
047    private final ChatManager chatManager;
048    private final String threadID;
049    private final EntityJid participant;
050    private final Set<ChatMessageListener> listeners = new CopyOnWriteArraySet<>();
051
052    /**
053     * Creates a new chat with the specified user and thread ID.
054     *
055     * @param chatManager the chatManager the chat will use.
056     * @param participant the user to chat with.
057     * @param threadID the thread ID to use.
058     */
059    Chat(ChatManager chatManager, EntityJid participant, String threadID) {
060        if (StringUtils.isEmpty(threadID)) {
061            throw new IllegalArgumentException("Thread ID must not be null");
062        }
063        this.chatManager = chatManager;
064        this.participant = participant;
065        this.threadID = threadID;
066    }
067
068    /**
069     * Returns the thread id associated with this chat, which corresponds to the
070     * <code>thread</code> field of XMPP messages. This method may return <code>null</code>
071     * if there is no thread ID is associated with this Chat.
072     *
073     * @return the thread ID of this chat.
074     */
075    public String getThreadID() {
076        return threadID;
077    }
078
079    /**
080     * Returns the name of the user the chat is with.
081     *
082     * @return the name of the user the chat is occuring with.
083     */
084    public EntityJid getParticipant() {
085        return participant;
086    }
087
088    /**
089     * Sends the specified text as a message to the other chat participant.
090     * This is a convenience method for:
091     *
092     * <pre>
093     *     Message message = chat.createMessage();
094     *     message.setBody(messageText);
095     *     chat.sendMessage(message);
096     * </pre>
097     *
098     * @param text the text to send.
099     * @throws NotConnectedException if the XMPP connection is not connected.
100     * @throws InterruptedException if the calling thread was interrupted.
101     */
102    public void sendMessage(String text) throws NotConnectedException, InterruptedException {
103        MessageBuilder message = StanzaBuilder.buildMessage()
104                .setBody(text);
105        sendMessage(message);
106    }
107
108    /**
109     * Sends a message to the other chat participant. The thread ID, recipient,
110     * and message type of the message will automatically set to those of this chat.
111     *
112     * @param message the message to send.
113     * @throws NotConnectedException if the XMPP connection is not connected.
114     * @throws InterruptedException if the calling thread was interrupted.
115     */
116    public void sendMessage(MessageBuilder message) throws NotConnectedException, InterruptedException {
117        // Force the recipient, message type, and thread ID since the user elected
118        // to send the message through this chat object.
119        message.to(participant);
120        message.ofType(Message.Type.chat);
121        message.setThread(threadID);
122        chatManager.sendMessage(this, message.build());
123    }
124
125    /**
126     * Sends a message to the other chat participant. The thread ID, recipient,
127     * and message type of the message will automatically set to those of this chat.
128     *
129     * @param message the message to send.
130     * @throws NotConnectedException if the XMPP connection is not connected.
131     * @throws InterruptedException if the calling thread was interrupted.
132     */
133    public void sendMessage(Message message) throws NotConnectedException, InterruptedException {
134        // Force the recipient, message type, and thread ID since the user elected
135        // to send the message through this chat object.
136        message.setTo(participant);
137        message.setType(Message.Type.chat);
138        message.setThread(threadID);
139        chatManager.sendMessage(this, message);
140    }
141
142    /**
143     * Adds a stanza listener that will be notified of any new messages in the
144     * chat.
145     *
146     * @param listener a stanza listener.
147     */
148    public void addMessageListener(ChatMessageListener listener) {
149        if (listener == null) {
150            return;
151        }
152        // TODO these references should be weak.
153        listeners.add(listener);
154    }
155
156    public void removeMessageListener(ChatMessageListener listener) {
157        listeners.remove(listener);
158    }
159
160    /**
161     * Closes the Chat and removes all references to it from the {@link ChatManager}. The chat will
162     * be unusable when this method returns, so it's recommend to drop all references to the
163     * instance right after calling {@link #close()}.
164     */
165    public void close() {
166        chatManager.closeChat(this);
167        listeners.clear();
168    }
169
170    /**
171     * Returns an unmodifiable set of all of the listeners registered with this chat.
172     *
173     * @return an unmodifiable set of all of the listeners registered with this chat.
174     */
175    public Set<ChatMessageListener> getListeners() {
176        return Collections.unmodifiableSet(listeners);
177    }
178
179    /**
180     * Creates a {@link org.jivesoftware.smack.StanzaCollector} which will accumulate the Messages
181     * for this chat. Always cancel StanzaCollectors when finished with them as they will accumulate
182     * messages indefinitely.
183     *
184     * @return the StanzaCollector which returns Messages for this chat.
185     */
186    public StanzaCollector createCollector() {
187        return chatManager.createStanzaCollector(this);
188    }
189
190    /**
191     * Delivers a message directly to this chat, which will add the message
192     * to the collector and deliver it to all listeners registered with the
193     * Chat. This is used by the XMPPConnection class to deliver messages
194     * without a thread ID.
195     *
196     * @param message the message.
197     */
198    void deliver(Message message) {
199        // Because the collector and listeners are expecting a thread ID with
200        // a specific value, set the thread ID on the message even though it
201        // probably never had one.
202        message.setThread(threadID);
203
204        for (ChatMessageListener listener : listeners) {
205            listener.processMessage(this, message);
206        }
207    }
208
209    @Override
210    public String toString() {
211        return "Chat [(participant=" + participant + "), (thread=" + threadID + ")]";
212    }
213
214    @Override
215    public int hashCode() {
216        int hash = 1;
217        hash = hash * 31 + threadID.hashCode();
218        hash = hash * 31 + participant.hashCode();
219        return hash;
220    }
221
222    @Override
223    public boolean equals(Object obj) {
224        return obj instanceof Chat
225                && threadID.equals(((Chat) obj).getThreadID())
226                && participant.equals(((Chat) obj).getParticipant());
227    }
228}