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.packet;
22
23import org.jivesoftware.smack.util.StringUtils;
24
25/**
26 * Represents XMPP presence packets. Every presence packet has a type, which is one of
27 * the following values:
28 * <ul>
29 *      <li>{@link Presence.Type#available available} -- (Default) indicates the user is available to
30 *          receive messages.
31 *      <li>{@link Presence.Type#unavailable unavailable} -- the user is unavailable to receive messages.
32 *      <li>{@link Presence.Type#subscribe subscribe} -- request subscription to recipient's presence.
33 *      <li>{@link Presence.Type#subscribed subscribed} -- grant subscription to sender's presence.
34 *      <li>{@link Presence.Type#unsubscribe unsubscribe} -- request removal of subscription to
35 *          sender's presence.
36 *      <li>{@link Presence.Type#unsubscribed unsubscribed} -- grant removal of subscription to
37 *          sender's presence.
38 *      <li>{@link Presence.Type#error error} -- the presence packet contains an error message.
39 * </ul><p>
40 *
41 * A number of attributes are optional:
42 * <ul>
43 *      <li>Status -- free-form text describing a user's presence (i.e., gone to lunch).
44 *      <li>Priority -- non-negative numerical priority of a sender's resource. The
45 *          highest resource priority is the default recipient of packets not addressed
46 *          to a particular resource.
47 *      <li>Mode -- one of five presence modes: {@link Mode#available available} (the default),
48 *          {@link Mode#chat chat}, {@link Mode#away away}, {@link Mode#xa xa} (extended away), and
49 *          {@link Mode#dnd dnd} (do not disturb).
50 * </ul><p>
51 *
52 * Presence packets are used for two purposes. First, to notify the server of our
53 * the clients current presence status. Second, they are used to subscribe and
54 * unsubscribe users from the roster.
55 *
56 * @see RosterPacket
57 * @author Matt Tucker
58 */
59public class Presence extends Packet {
60
61    private Type type = Type.available;
62    private String status = null;
63    private int priority = Integer.MIN_VALUE;
64    private Mode mode = null;
65    private String language;
66
67    /**
68     * Creates a new presence update. Status, priority, and mode are left un-set.
69     *
70     * @param type the type.
71     */
72    public Presence(Type type) {
73        setType(type);
74    }
75
76    /**
77     * Creates a new presence update with a specified status, priority, and mode.
78     *
79     * @param type the type.
80     * @param status a text message describing the presence update.
81     * @param priority the priority of this presence update.
82     * @param mode the mode type for this presence update.
83     */
84    public Presence(Type type, String status, int priority, Mode mode) {
85        setType(type);
86        setStatus(status);
87        setPriority(priority);
88        setMode(mode);
89    }
90
91    /**
92     * Returns true if the {@link Type presence type} is available (online) and
93     * false if the user is unavailable (offline), or if this is a presence packet
94     * involved in a subscription operation. This is a convenience method
95     * equivalent to <tt>getType() == Presence.Type.available</tt>. Note that even
96     * when the user is available, their presence mode may be {@link Mode#away away},
97     * {@link Mode#xa extended away} or {@link Mode#dnd do not disturb}. Use
98     * {@link #isAway()} to determine if the user is away.
99     *
100     * @return true if the presence type is available.
101     */
102    public boolean isAvailable() {
103        return type == Type.available;
104    }
105
106    /**
107     * Returns true if the presence type is {@link Type#available available} and the presence
108     * mode is {@link Mode#away away}, {@link Mode#xa extended away}, or
109     * {@link Mode#dnd do not disturb}. False will be returned when the type or mode
110     * is any other value, including when the presence type is unavailable (offline).
111     * This is a convenience method equivalent to
112     * <tt>type == Type.available && (mode == Mode.away || mode == Mode.xa || mode == Mode.dnd)</tt>.
113     *
114     * @return true if the presence type is available and the presence mode is away, xa, or dnd.
115     */
116    public boolean isAway() {
117        return type == Type.available && (mode == Mode.away || mode == Mode.xa || mode == Mode.dnd);
118    }
119
120    /**
121     * Returns the type of this presence packet.
122     *
123     * @return the type of the presence packet.
124     */
125    public Type getType() {
126        return type;
127    }
128
129    /**
130     * Sets the type of the presence packet.
131     *
132     * @param type the type of the presence packet.
133     */
134    public void setType(Type type) {
135        if(type == null) {
136            throw new NullPointerException("Type cannot be null");
137        }
138        this.type = type;
139    }
140
141    /**
142     * Returns the status message of the presence update, or <tt>null</tt> if there
143     * is not a status. The status is free-form text describing a user's presence
144     * (i.e., "gone to lunch").
145     *
146     * @return the status message.
147     */
148    public String getStatus() {
149        return status;
150    }
151
152    /**
153     * Sets the status message of the presence update. The status is free-form text
154     * describing a user's presence (i.e., "gone to lunch").
155     *
156     * @param status the status message.
157     */
158    public void setStatus(String status) {
159        this.status = status;
160    }
161
162    /**
163     * Returns the priority of the presence, or Integer.MIN_VALUE if no priority has been set.
164     *
165     * @return the priority.
166     */
167    public int getPriority() {
168        return priority;
169    }
170
171    /**
172     * Sets the priority of the presence. The valid range is -128 through 128.
173     *
174     * @param priority the priority of the presence.
175     * @throws IllegalArgumentException if the priority is outside the valid range.
176     */
177    public void setPriority(int priority) {
178        if (priority < -128 || priority > 128) {
179            throw new IllegalArgumentException("Priority value " + priority +
180                    " is not valid. Valid range is -128 through 128.");
181        }
182        this.priority = priority;
183    }
184
185    /**
186     * Returns the mode of the presence update, or <tt>null</tt> if the mode is not set.
187     * A null presence mode value is interpreted to be the same thing as
188     * {@link Presence.Mode#available}.
189     *
190     * @return the mode.
191     */
192    public Mode getMode() {
193        return mode;
194    }
195
196    /**
197     * Sets the mode of the presence update. A null presence mode value is interpreted
198     * to be the same thing as {@link Presence.Mode#available}.
199     *
200     * @param mode the mode.
201     */
202    public void setMode(Mode mode) {
203        this.mode = mode;
204    }
205
206    /**
207     * Returns the xml:lang of this Presence, or null if one has not been set.
208     *
209     * @return the xml:lang of this Presence, or null if one has not been set.
210     * @since 3.0.2
211     */
212    public String getLanguage() {
213        return language;
214    }
215
216    /**
217     * Sets the xml:lang of this Presence.
218     *
219     * @param language the xml:lang of this Presence.
220     * @since 3.0.2
221     */
222    public void setLanguage(String language) {
223        this.language = language;
224    }
225
226    public String toXML() {
227        StringBuilder buf = new StringBuilder();
228        buf.append("<presence");
229        if(getXmlns() != null) {
230            buf.append(" xmlns=\"").append(getXmlns()).append("\"");
231        }
232        if (language != null) {
233            buf.append(" xml:lang=\"").append(getLanguage()).append("\"");
234        }
235        if (getPacketID() != null) {
236            buf.append(" id=\"").append(getPacketID()).append("\"");
237        }
238        if (getTo() != null) {
239            buf.append(" to=\"").append(StringUtils.escapeForXML(getTo())).append("\"");
240        }
241        if (getFrom() != null) {
242            buf.append(" from=\"").append(StringUtils.escapeForXML(getFrom())).append("\"");
243        }
244        if (type != Type.available) {
245            buf.append(" type=\"").append(type).append("\"");
246        }
247        buf.append(">");
248        if (status != null) {
249            buf.append("<status>").append(StringUtils.escapeForXML(status)).append("</status>");
250        }
251        if (priority != Integer.MIN_VALUE) {
252            buf.append("<priority>").append(priority).append("</priority>");
253        }
254        if (mode != null && mode != Mode.available) {
255            buf.append("<show>").append(mode).append("</show>");
256        }
257
258        buf.append(this.getExtensionsXML());
259
260        // Add the error sub-packet, if there is one.
261        XMPPError error = getError();
262        if (error != null) {
263            buf.append(error.toXML());
264        }
265
266        buf.append("</presence>");
267
268        return buf.toString();
269    }
270
271    public String toString() {
272        StringBuilder buf = new StringBuilder();
273        buf.append(type);
274        if (mode != null) {
275            buf.append(": ").append(mode);
276        }
277        if (getStatus() != null) {
278            buf.append(" (").append(getStatus()).append(")");
279        }
280        return buf.toString();
281    }
282
283    /**
284     * A enum to represent the presecence type. Not that presence type is often confused
285     * with presence mode. Generally, if a user is signed into a server, they have a presence
286     * type of {@link #available available}, even if the mode is {@link Mode#away away},
287     * {@link Mode#dnd dnd}, etc. The presence type is only {@link #unavailable unavailable} when
288     * the user is signing out of the server.
289     */
290    public enum Type {
291
292       /**
293        * The user is available to receive messages (default).
294        */
295        available,
296
297        /**
298         * The user is unavailable to receive messages.
299         */
300        unavailable,
301
302        /**
303         * Request subscription to recipient's presence.
304         */
305        subscribe,
306
307        /**
308         * Grant subscription to sender's presence.
309         */
310        subscribed,
311
312        /**
313         * Request removal of subscription to sender's presence.
314         */
315        unsubscribe,
316
317        /**
318         * Grant removal of subscription to sender's presence.
319         */
320        unsubscribed,
321
322        /**
323         * The presence packet contains an error message.
324         */
325        error
326    }
327
328    /**
329     * An enum to represent the presence mode.
330     */
331    public enum Mode {
332
333        /**
334         * Free to chat.
335         */
336        chat,
337
338        /**
339         * Available (the default).
340         */
341        available,
342
343        /**
344         * Away.
345         */
346        away,
347
348        /**
349         * Away for an extended period of time.
350         */
351        xa,
352
353        /**
354         * Do not disturb.
355         */
356        dnd
357    }
358}