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