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 occurring 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 chatMessage = message.asBuilder()
137                        .to(participant)
138                        .ofType(Message.Type.chat)
139                        .setThread(threadID)
140                        .build();
141        chatManager.sendMessage(this, chatMessage);
142    }
143
144    /**
145     * Adds a stanza listener that will be notified of any new messages in the
146     * chat.
147     *
148     * @param listener a stanza listener.
149     */
150    public void addMessageListener(ChatMessageListener listener) {
151        if (listener == null) {
152            return;
153        }
154        // TODO these references should be weak.
155        listeners.add(listener);
156    }
157
158    public void removeMessageListener(ChatMessageListener listener) {
159        listeners.remove(listener);
160    }
161
162    /**
163     * Closes the Chat and removes all references to it from the {@link ChatManager}. The chat will
164     * be unusable when this method returns, so it's recommend to drop all references to the
165     * instance right after calling {@link #close()}.
166     */
167    public void close() {
168        chatManager.closeChat(this);
169        listeners.clear();
170    }
171
172    /**
173     * Returns an unmodifiable set of all of the listeners registered with this chat.
174     *
175     * @return an unmodifiable set of all of the listeners registered with this chat.
176     */
177    public Set<ChatMessageListener> getListeners() {
178        return Collections.unmodifiableSet(listeners);
179    }
180
181    /**
182     * Creates a {@link org.jivesoftware.smack.StanzaCollector} which will accumulate the Messages
183     * for this chat. Always cancel StanzaCollectors when finished with them as they will accumulate
184     * messages indefinitely.
185     *
186     * @return the StanzaCollector which returns Messages for this chat.
187     */
188    public StanzaCollector createCollector() {
189        return chatManager.createStanzaCollector(this);
190    }
191
192    /**
193     * Delivers a message directly to this chat, which will add the message
194     * to the collector and deliver it to all listeners registered with the
195     * Chat. This is used by the XMPPConnection class to deliver messages
196     * without a thread ID.
197     *
198     * @param message the message.
199     */
200    void deliver(Message message) {
201        // Because the collector and listeners are expecting a thread ID with
202        // a specific value, set the thread ID on the message even though it
203        // probably never had one.
204        Message chatMessage = message.asBuilder().setThread(threadID).build();
205
206        for (ChatMessageListener listener : listeners) {
207            listener.processMessage(this, chatMessage);
208        }
209    }
210
211    @Override
212    public String toString() {
213        return "Chat [(participant=" + participant + "), (thread=" + threadID + ")]";
214    }
215
216    @Override
217    public int hashCode() {
218        int hash = 1;
219        hash = hash * 31 + threadID.hashCode();
220        hash = hash * 31 + participant.hashCode();
221        return hash;
222    }
223
224    @Override
225    public boolean equals(Object obj) {
226        return obj instanceof Chat
227                && threadID.equals(((Chat) obj).getThreadID())
228                && participant.equals(((Chat) obj).getParticipant());
229    }
230}