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.packet;
019
020import java.util.Locale;
021
022import org.jivesoftware.smack.util.Objects;
023import org.jivesoftware.smack.util.TypedCloneable;
024import org.jivesoftware.smack.util.XmlStringBuilder;
025
026/**
027 * Represents XMPP presence packets. Every presence stanza(/packet) has a type, which is one of
028 * the following values:
029 * <ul>
030 *      <li>{@link Presence.Type#available available} -- (Default) indicates the user is available to
031 *          receive messages.
032 *      <li>{@link Presence.Type#unavailable unavailable} -- the user is unavailable to receive messages.
033 *      <li>{@link Presence.Type#subscribe subscribe} -- request subscription to recipient's presence.
034 *      <li>{@link Presence.Type#subscribed subscribed} -- grant subscription to sender's presence.
035 *      <li>{@link Presence.Type#unsubscribe unsubscribe} -- request removal of subscription to
036 *          sender's presence.
037 *      <li>{@link Presence.Type#unsubscribed unsubscribed} -- grant removal of subscription to
038 *          sender's presence.
039 *      <li>{@link Presence.Type#error error} -- the presence stanza(/packet) contains an error message.
040 * </ul><p>
041 *
042 * A number of attributes are optional:
043 * <ul>
044 *      <li>Status -- free-form text describing a user's presence (i.e., gone to lunch).
045 *      <li>Priority -- non-negative numerical priority of a sender's resource. The
046 *          highest resource priority is the default recipient of packets not addressed
047 *          to a particular resource.
048 *      <li>Mode -- one of five presence modes: {@link Mode#available available} (the default),
049 *          {@link Mode#chat chat}, {@link Mode#away away}, {@link Mode#xa xa} (extended away), and
050 *          {@link Mode#dnd dnd} (do not disturb).
051 * </ul><p>
052 *
053 * Presence packets are used for two purposes. First, to notify the server of
054 * the user's current presence status. Second, they are used to subscribe and
055 * unsubscribe users from the roster.
056 *
057 * @author Matt Tucker
058 */
059public final class Presence extends Stanza implements TypedCloneable<Presence> {
060
061    public static final String ELEMENT = "presence";
062
063    private Type type = Type.available;
064    private String status = null;
065    private int priority = Integer.MIN_VALUE;
066    private Mode mode = null;
067
068    /**
069     * Creates a new presence update. Status, priority, and mode are left un-set.
070     *
071     * @param type the type.
072     */
073    public Presence(Type type) {
074        setType(type);
075    }
076
077    /**
078     * Creates a new presence update with a specified status, priority, and mode.
079     *
080     * @param type the type.
081     * @param status a text message describing the presence update.
082     * @param priority the priority of this presence update.
083     * @param mode the mode type for this presence update.
084     */
085    public Presence(Type type, String status, int priority, Mode mode) {
086        setType(type);
087        setStatus(status);
088        setPriority(priority);
089        setMode(mode);
090    }
091
092    /**
093     * Copy constructor.
094     * <p>
095     * This does not perform a deep clone, as extension elements are shared between the new and old
096     * instance.
097     * </p>
098     *
099     * @param other
100     */
101    public Presence(Presence other) {
102        super(other);
103        this.type = other.type;
104        this.status = other.status;
105        this.priority = other.priority;
106        this.mode = other.mode;
107    }
108
109    /**
110     * Returns true if the {@link Type presence type} is available (online) and
111     * false if the user is unavailable (offline), or if this is a presence packet
112     * involved in a subscription operation. This is a convenience method
113     * equivalent to <tt>getType() == Presence.Type.available</tt>. Note that even
114     * when the user is available, their presence mode may be {@link Mode#away away},
115     * {@link Mode#xa extended away} or {@link Mode#dnd do not disturb}. Use
116     * {@link #isAway()} to determine if the user is away.
117     *
118     * @return true if the presence type is available.
119     */
120    public boolean isAvailable() {
121        return type == Type.available;    
122    }
123
124    /**
125     * Returns true if the presence type is {@link Type#available available} and the presence
126     * mode is {@link Mode#away away}, {@link Mode#xa extended away}, or
127     * {@link Mode#dnd do not disturb}. False will be returned when the type or mode
128     * is any other value, including when the presence type is unavailable (offline).
129     * This is a convenience method equivalent to
130     * <tt>type == Type.available && (mode == Mode.away || mode == Mode.xa || mode == Mode.dnd)</tt>.
131     *
132     * @return true if the presence type is available and the presence mode is away, xa, or dnd.
133     */
134    public boolean isAway() {
135        return type == Type.available && (mode == Mode.away || mode == Mode.xa || mode == Mode.dnd); 
136    }
137
138    /**
139     * Returns the type of this presence packet.
140     *
141     * @return the type of the presence packet.
142     */
143    public Type getType() {
144        return type;
145    }
146
147    /**
148     * Sets the type of the presence packet.
149     *
150     * @param type the type of the presence packet.
151     */
152    public void setType(Type type) {
153        this.type = Objects.requireNonNull(type, "Type cannot be null");
154    }
155
156    /**
157     * Returns the status message of the presence update, or <tt>null</tt> if there
158     * is not a status. The status is free-form text describing a user's presence
159     * (i.e., "gone to lunch").
160     *
161     * @return the status message.
162     */
163    public String getStatus() {
164        return status;
165    }
166
167    /**
168     * Sets the status message of the presence update. The status is free-form text
169     * describing a user's presence (i.e., "gone to lunch").
170     *
171     * @param status the status message.
172     */
173    public void setStatus(String status) {
174        this.status = status;
175    }
176
177    /**
178     * Returns the priority of the presence, or Integer.MIN_VALUE if no priority has been set.
179     *
180     * @return the priority.
181     */
182    public int getPriority() {
183        return priority;
184    }
185
186    /**
187     * Sets the priority of the presence. The valid range is -128 through 128.
188     *
189     * @param priority the priority of the presence.
190     * @throws IllegalArgumentException if the priority is outside the valid range.
191     */
192    public void setPriority(int priority) {
193        if (priority < -128 || priority > 128) {
194            throw new IllegalArgumentException("Priority value " + priority +
195                    " is not valid. Valid range is -128 through 128.");
196        }
197        this.priority = priority;
198    }
199
200    /**
201     * Returns the mode of the presence update.
202     *
203     * @return the mode.
204     */
205    public Mode getMode() {
206        if (mode == null) {
207            return Mode.available;
208        }
209        return mode;
210    }
211
212    /**
213     * Sets the mode of the presence update. A null presence mode value is interpreted
214     * to be the same thing as {@link Presence.Mode#available}.
215     *
216     * @param mode the mode.
217     */
218    public void setMode(Mode mode) {
219        this.mode = mode;
220    }
221
222    @Override
223    public XmlStringBuilder toXML() {
224        XmlStringBuilder buf = new XmlStringBuilder();
225        buf.halfOpenElement(ELEMENT);
226        addCommonAttributes(buf);
227        if (type != Type.available) {
228            buf.attribute("type", type);
229        }
230        buf.rightAngleBracket();
231
232        buf.optElement("status", status);
233        if (priority != Integer.MIN_VALUE) {
234            buf.element("priority", Integer.toString(priority));
235        }
236        if (mode != null && mode != Mode.available) {
237            buf.element("show", mode);
238        }
239        buf.append(getExtensionsXML());
240
241        // Add the error sub-packet, if there is one.
242        appendErrorIfExists(buf);
243
244        buf.closeElement(ELEMENT);
245
246        return buf;
247    }
248
249    /**
250     * Creates and returns a copy of this presence stanza.
251     * <p>
252     * This does not perform a deep clone, as extension elements are shared between the new and old
253     * instance.
254     * </p>
255     * @return a clone of this presence.
256     */
257    @Override
258    public Presence clone() {
259        return new Presence(this);
260    }
261
262    /**
263     * An enum to represent the presence type. Note that presence type is often confused
264     * with presence mode. Generally, if a user is signed in to a server, they have a presence
265     * type of {@link #available available}, even if the mode is {@link Mode#away away},
266     * {@link Mode#dnd dnd}, etc. The presence type is only {@link #unavailable unavailable} when
267     * the user is signing out of the server.
268     */
269    public enum Type {
270
271       /**
272        * The user is available to receive messages (default).
273        */
274        available,
275
276        /**
277         * The user is unavailable to receive messages.
278         */
279        unavailable,
280
281        /**
282         * Request subscription to recipient's presence.
283         */
284        subscribe,
285
286        /**
287         * Grant subscription to sender's presence.
288         */
289        subscribed,
290
291        /**
292         * Request removal of subscription to sender's presence.
293         */
294        unsubscribe,
295
296        /**
297         * Grant removal of subscription to sender's presence.
298         */
299        unsubscribed,
300
301        /**
302         * The presence stanza(/packet) contains an error message.
303         */
304        error,
305
306        /**
307         * A presence probe as defined in section 4.3 of RFC 6121
308         */
309        probe,
310        ;
311
312        /**
313         * Converts a String into the corresponding types. Valid String values that can be converted
314         * to types are: "available", "unavailable", "subscribe", "subscribed", "unsubscribe",
315         * "unsubscribed" and "error".
316         * 
317         * @param string the String value to covert.
318         * @return the corresponding Type.
319         * @throws IllegalArgumentException when not able to parse the string parameter
320         * @throws NullPointerException if the string is null
321         */
322        public static Type fromString(String string) {
323            return Type.valueOf(string.toLowerCase(Locale.US));
324        }
325    }
326
327    /**
328     * An enum to represent the presence mode.
329     */
330    public enum Mode {
331
332        /**
333         * Free to chat.
334         */
335        chat,
336
337        /**
338         * Available (the default).
339         */
340        available,
341
342        /**
343         * Away.
344         */
345        away,
346
347        /**
348         * Away for an extended period of time.
349         */
350        xa,
351
352        /**
353         * Do not disturb.
354         */
355        dnd;
356
357        /**
358         * Converts a String into the corresponding types. Valid String values that can be converted
359         * to types are: "chat", "available", "away", "xa", and "dnd".
360         * 
361         * @param string the String value to covert.
362         * @return the corresponding Type.
363         * @throws IllegalArgumentException when not able to parse the string parameter
364         * @throws NullPointerException if the string is null
365         */
366        public static Mode fromString(String string) {
367            return Mode.valueOf(string.toLowerCase(Locale.US));
368        }
369    }
370}