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.smackx.muc.packet;
019
020import org.jivesoftware.smack.packet.NamedElement;
021import org.jivesoftware.smack.packet.Stanza;
022
023import java.util.HashMap;
024import java.util.HashSet;
025import java.util.Map;
026import java.util.Set;
027
028import org.jivesoftware.smack.packet.ExtensionElement;
029import org.jivesoftware.smack.util.XmlStringBuilder;
030
031/**
032 * Represents extended presence information about roles, affiliations, full JIDs,
033 * or status codes scoped by the 'http://jabber.org/protocol/muc#user' namespace.
034 *
035 * @author Gaston Dombiak
036 */
037public class MUCUser implements ExtensionElement {
038
039    public static final String ELEMENT = "x";
040    public static final String NAMESPACE = MUCInitialPresence.NAMESPACE + "#user";
041
042    private final Set<Status> statusCodes = new HashSet<Status>(4);
043
044    private Invite invite;
045    private Decline decline;
046    private MUCItem item;
047    private String password;
048    private Destroy destroy;
049
050    public String getElementName() {
051        return ELEMENT;
052    }
053
054    public String getNamespace() {
055        return NAMESPACE;
056    }
057
058    @Override
059    public XmlStringBuilder toXML() {
060        XmlStringBuilder xml = new XmlStringBuilder(this);
061        xml.rightAngleBracket();
062        xml.optElement(getInvite());
063        xml.optElement(getDecline());
064        xml.optElement(getItem());
065        xml.optElement("password", getPassword());
066        xml.append(statusCodes);
067        xml.optElement(getDestroy());
068        xml.closeElement(this);
069        return xml;
070    }
071
072    /**
073     * Returns the invitation for another user to a room. The sender of the invitation
074     * must be an occupant of the room. The invitation will be sent to the room which in turn
075     * will forward the invitation to the invitee.
076     *
077     * @return an invitation for another user to a room.
078     */
079    public Invite getInvite() {
080        return invite;
081    }
082
083    /**
084     * Returns the rejection to an invitation from another user to a room. The rejection will be
085     * sent to the room which in turn will forward the refusal to the inviter.
086     *
087     * @return a rejection to an invitation from another user to a room.
088     */
089    public Decline getDecline() {
090        return decline;
091    }
092
093    /**
094     * Returns the item child that holds information about roles, affiliation, jids and nicks.
095     *
096     * @return an item child that holds information about roles, affiliation, jids and nicks.
097     */
098    public MUCItem getItem() {
099        return item;
100    }
101
102    /**
103     * Returns the password to use to enter Password-Protected Room. A Password-Protected Room is
104     * a room that a user cannot enter without first providing the correct password.
105     *
106     * @return the password to use to enter Password-Protected Room.
107     */
108    public String getPassword() {
109        return password;
110    }
111
112    /**
113     * Returns a set of status which holds the status code that assist in presenting notification messages.
114     *
115     * @return the set of status which holds the status code that assist in presenting notification messages.
116     */
117    public Set<Status> getStatus() {
118        return statusCodes;
119    }
120
121    /**
122     * Returns true if this MUCUser instance has also {@link Status} information.
123     * <p>
124     * If <code>true</code> is returned, then {@link #getStatus()} will return a non-empty set.
125     * </p>
126     *
127     * @return true if this MUCUser has status information.
128     * @since 4.1
129     */
130    public boolean hasStatus() {
131        return !statusCodes.isEmpty();
132    }
133
134    /**
135     * Returns the notification that the room has been destroyed. After a room has been destroyed,
136     * the room occupants will receive a Presence stanza(/packet) of type 'unavailable' with the reason for
137     * the room destruction if provided by the room owner.
138     *
139     * @return a notification that the room has been destroyed.
140     */
141    public Destroy getDestroy() {
142        return destroy;
143    }
144
145    /**
146     * Sets the invitation for another user to a room. The sender of the invitation
147     * must be an occupant of the room. The invitation will be sent to the room which in turn
148     * will forward the invitation to the invitee.
149     *
150     * @param invite the invitation for another user to a room.
151     */
152    public void setInvite(Invite invite) {
153        this.invite = invite;
154    }
155
156    /**
157     * Sets the rejection to an invitation from another user to a room. The rejection will be
158     * sent to the room which in turn will forward the refusal to the inviter.
159     *
160     * @param decline the rejection to an invitation from another user to a room.
161     */
162    public void setDecline(Decline decline) {
163        this.decline = decline;
164    }
165
166    /**
167     * Sets the item child that holds information about roles, affiliation, jids and nicks.
168     *
169     * @param item the item child that holds information about roles, affiliation, jids and nicks.
170     */
171    public void setItem(MUCItem item) {
172        this.item = item;
173    }
174
175    /**
176     * Sets the password to use to enter Password-Protected Room. A Password-Protected Room is
177     * a room that a user cannot enter without first providing the correct password.
178     *
179     * @param string the password to use to enter Password-Protected Room.
180     */
181    public void setPassword(String string) {
182        password = string;
183    }
184
185    /**
186     * Add the status codes which holds the codes that assists in presenting notification messages.
187     *
188     * @param statusCodes the status codes which hold the codes that assists in presenting notification
189     * messages.
190     */
191    public void addStatusCodes(Set<Status> statusCodes) {
192        this.statusCodes.addAll(statusCodes);
193    }
194
195    /**
196     * Add a status code which hold a code that assists in presenting notification messages.
197     *
198     * @param status the status code which olds a code that assists in presenting notification messages.
199     */
200    public void addStatusCode(Status status) {
201        this.statusCodes.add(status);
202    }
203
204    /**
205     * Sets the notification that the room has been destroyed. After a room has been destroyed,
206     * the room occupants will receive a Presence stanza(/packet) of type 'unavailable' with the reason for
207     * the room destruction if provided by the room owner.
208     *
209     * @param destroy the notification that the room has been destroyed.
210     */
211    public void setDestroy(Destroy destroy) {
212        this.destroy = destroy;
213    }
214
215    /**
216     * Retrieve the MUCUser PacketExtension from packet, if any.
217     *
218     * @param packet
219     * @return the MUCUser PacketExtension or {@code null}
220     * @deprecated use {@link #from(Stanza)} instead
221     */
222    @Deprecated
223    public static MUCUser getFrom(Stanza packet) {
224        return from(packet);
225    }
226
227    /**
228     * Retrieve the MUCUser PacketExtension from packet, if any.
229     *
230     * @param packet
231     * @return the MUCUser PacketExtension or {@code null}
232     */
233    public static MUCUser from(Stanza packet) {
234        return packet.getExtension(ELEMENT, NAMESPACE);
235    }
236
237    /**
238     * Represents an invitation for another user to a room. The sender of the invitation
239     * must be an occupant of the room. The invitation will be sent to the room which in turn
240     * will forward the invitation to the invitee.
241     *
242     * @author Gaston Dombiak
243     */
244    public static class Invite implements NamedElement {
245        public static final String ELEMENT ="invite";
246
247        private String reason;
248        private String from;
249        private String to;
250
251        /**
252         * Returns the bare JID of the inviter or, optionally, the room JID. (e.g.
253         * 'crone1@shakespeare.lit/desktop').
254         *
255         * @return the room's occupant that sent the invitation.
256         */
257        public String getFrom() {
258            return from;
259        }
260
261        /**
262         * Returns the message explaining the invitation.
263         *
264         * @return the message explaining the invitation.
265         */
266        public String getReason() {
267            return reason;
268        }
269
270        /**
271         * Returns the bare JID of the invitee. (e.g. 'hecate@shakespeare.lit')
272         *
273         * @return the bare JID of the invitee.
274         */
275        public String getTo() {
276            return to;
277        }
278
279        /**
280         * Sets the bare JID of the inviter or, optionally, the room JID. (e.g.
281         * 'crone1@shakespeare.lit/desktop')
282         *
283         * @param from the bare JID of the inviter or, optionally, the room JID.
284         */
285        public void setFrom(String from) {
286            this.from = from;
287        }
288
289        /**
290         * Sets the message explaining the invitation.
291         *
292         * @param reason the message explaining the invitation.
293         */
294        public void setReason(String reason) {
295            this.reason = reason;
296        }
297
298        /**
299         * Sets the bare JID of the invitee. (e.g. 'hecate@shakespeare.lit')
300         *
301         * @param to the bare JID of the invitee.
302         */
303        public void setTo(String to) {
304            this.to = to;
305        }
306
307        @Override
308        public XmlStringBuilder toXML() {
309            XmlStringBuilder xml = new XmlStringBuilder(this);
310            xml.optAttribute("to", getTo());
311            xml.optAttribute("from", getFrom());
312            xml.rightAngleBracket();
313            xml.optElement("reason", getReason());
314            xml.closeElement(this);
315            return xml;
316        }
317
318        @Override
319        public String getElementName() {
320            return ELEMENT;
321        }
322    }
323
324    /**
325     * Represents a rejection to an invitation from another user to a room. The rejection will be
326     * sent to the room which in turn will forward the refusal to the inviter.
327     *
328     * @author Gaston Dombiak
329     */
330    public static class Decline implements NamedElement {
331        public static final String ELEMENT = "decline";
332
333        private String reason;
334        private String from;
335        private String to;
336
337        /**
338         * Returns the bare JID of the invitee that rejected the invitation. (e.g.
339         * 'crone1@shakespeare.lit/desktop').
340         *
341         * @return the bare JID of the invitee that rejected the invitation.
342         */
343        public String getFrom() {
344            return from;
345        }
346
347        /**
348         * Returns the message explaining why the invitation was rejected.
349         *
350         * @return the message explaining the reason for the rejection.
351         */
352        public String getReason() {
353            return reason;
354        }
355
356        /**
357         * Returns the bare JID of the inviter. (e.g. 'hecate@shakespeare.lit')
358         *
359         * @return the bare JID of the inviter.
360         */
361        public String getTo() {
362            return to;
363        }
364
365        /**
366         * Sets the bare JID of the invitee that rejected the invitation. (e.g.
367         * 'crone1@shakespeare.lit/desktop').
368         *
369         * @param from the bare JID of the invitee that rejected the invitation.
370         */
371        public void setFrom(String from) {
372            this.from = from;
373        }
374
375        /**
376         * Sets the message explaining why the invitation was rejected.
377         *
378         * @param reason the message explaining the reason for the rejection.
379         */
380        public void setReason(String reason) {
381            this.reason = reason;
382        }
383
384        /**
385         * Sets the bare JID of the inviter. (e.g. 'hecate@shakespeare.lit')
386         *
387         * @param to the bare JID of the inviter.
388         */
389        public void setTo(String to) {
390            this.to = to;
391        }
392
393        @Override
394        public XmlStringBuilder toXML() {
395            XmlStringBuilder xml = new XmlStringBuilder(this);
396            xml.optAttribute("to", getTo());
397            xml.optAttribute("from", getFrom());
398            xml.rightAngleBracket();
399            xml.optElement("reason", getReason());
400            xml.closeElement(this);
401            return xml;
402        }
403
404        @Override
405        public String getElementName() {
406            return ELEMENT;
407        }
408    }
409
410    /**
411     * Status code assists in presenting notification messages. The following link provides the
412     * list of existing error codes <a href="http://xmpp.org/registrar/mucstatus.html">Multi-User Chat Status Codes</a>.
413     *
414     * @author Gaston Dombiak
415     */
416    public static class Status implements NamedElement {
417        public static final String ELEMENT = "status";
418
419        private static final Map<Integer, Status> statusMap = new HashMap<Integer, Status>(8);
420
421        public static final Status ROOM_CREATED_201 = Status.create(201);
422        public static final Status BANNED_301 = Status.create(301);
423        public static final Status NEW_NICKNAME_303 = Status.create(303);
424        public static final Status KICKED_307 = Status.create(307);
425        public static final Status REMOVED_AFFIL_CHANGE_321 = Status.create(321);
426
427        private final Integer code;
428
429        public static Status create(String string) {
430            Integer integer = Integer.valueOf(string);
431            return create(integer);
432        }
433
434        public static Status create(Integer i) {
435            Status status = statusMap.get(i);
436            if (status == null) {
437                status = new Status(i);
438                statusMap.put(i, status);
439            }
440            return status;
441        }
442
443        /**
444         * Creates a new instance of Status with the specified code.
445         *
446         * @param code the code that uniquely identifies the reason of the error.
447         */
448        private Status(int code) {
449            this.code = code;
450        }
451
452        /**
453         * Returns the code that uniquely identifies the reason of the error. The code
454         * assists in presenting notification messages.
455         *
456         * @return the code that uniquely identifies the reason of the error.
457         */
458        public int getCode() {
459            return code;
460        }
461
462        @Override
463        public XmlStringBuilder toXML() {
464            XmlStringBuilder xml = new XmlStringBuilder(this);
465            xml.attribute("code", getCode());
466            xml.closeEmptyElement();
467            return xml;
468        }
469
470        @Override
471        public boolean equals(Object other) {
472            if (other == null) {
473                return false;
474            }
475            if (other instanceof Status) {
476                Status otherStatus = (Status) other;
477                return code.equals(otherStatus.getCode());
478            } 
479            return false;
480        }
481
482        @Override
483        public int hashCode() {
484            return code;
485        }
486
487        @Override
488        public String getElementName() {
489            return ELEMENT;
490        }
491    }
492}