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.xevent.packet;
019
020import org.jivesoftware.smack.packet.ExtensionElement;
021
022import java.util.ArrayList;
023import java.util.List;
024
025/**
026 * Represents message events relating to the delivery, display, composition and cancellation of 
027 * messages.<p>
028 * 
029 * There are four message events currently defined in this namespace:
030 * <ol>
031 * <li>Offline<br>
032 * Indicates that the message has been stored offline by the intended recipient's server. This 
033 * event is triggered only if the intended recipient's server supports offline storage, has that 
034 * support enabled, and the recipient is offline when the server receives the message for delivery.</li>
035 * 
036 * <li>Delivered<br>
037 * Indicates that the message has been delivered to the recipient. This signifies that the message
038 * has reached the recipient's XMPP client, but does not necessarily mean that the message has 
039 * been displayed. This event is to be raised by the XMPP client.</li>
040 * 
041 * <li>Displayed<br>
042 * Once the message has been received by the recipient's XMPP client, it may be displayed to the
043 * user. This event indicates that the message has been displayed, and is to be raised by the 
044 * XMPP client. Even if a message is displayed multiple times, this event should be raised only 
045 * once.</li>
046 * 
047 * <li>Composing<br>
048 * In threaded chat conversations, this indicates that the recipient is composing a reply to a 
049 * message. The event is to be raised by the recipient's XMPP client. A XMPP client is allowed
050 * to raise this event multiple times in response to the same request, providing the original 
051 * event is cancelled first.</li>
052 * </ol>
053 *
054 * @author Gaston Dombiak
055 */
056public class MessageEvent implements ExtensionElement {
057
058    public static final String NAMESPACE = "jabber:x:event";
059    public static final String ELEMENT = "x";
060
061    public static final String OFFLINE = "offline";
062    public static final String COMPOSING = "composing";
063    public static final String DISPLAYED = "displayed";
064    public static final String DELIVERED = "delivered";
065    public static final String CANCELLED = "cancelled";
066
067    private boolean offline = false;
068    private boolean delivered = false;
069    private boolean displayed = false;
070    private boolean composing = false;
071    private boolean cancelled = true;
072
073    private String packetID = null;
074
075    /**
076    * Returns the XML element name of the extension sub-packet root element.
077    * Always returns "x"
078    *
079    * @return the XML element name of the stanza(/packet) extension.
080    */
081    public String getElementName() {
082        return ELEMENT;
083    }
084
085    /** 
086     * Returns the XML namespace of the extension sub-packet root element.
087     * According the specification the namespace is always "jabber:x:event"
088     *
089     * @return the XML namespace of the stanza(/packet) extension.
090     */
091    public String getNamespace() {
092        return NAMESPACE;
093    }
094
095    /**
096     * When the message is a request returns if the sender of the message requests to be notified
097     * when the receiver is composing a reply.
098     * When the message is a notification returns if the receiver of the message is composing a 
099     * reply.
100     * 
101     * @return true if the sender is requesting to be notified when composing or when notifying
102     * that the receiver of the message is composing a reply
103     */
104    public boolean isComposing() {
105        return composing;
106    }
107
108    /**
109     * When the message is a request returns if the sender of the message requests to be notified
110     * when the message is delivered.
111     * When the message is a notification returns if the message was delivered or not.
112     * 
113     * @return true if the sender is requesting to be notified when delivered or when notifying 
114     * that the message was delivered 
115     */
116    public boolean isDelivered() {
117        return delivered;
118    }
119
120    /**
121     * When the message is a request returns if the sender of the message requests to be notified
122     * when the message is displayed.
123     * When the message is a notification returns if the message was displayed or not.
124     * 
125     * @return true if the sender is requesting to be notified when displayed or when notifying 
126     * that the message was displayed
127     */
128    public boolean isDisplayed() {
129        return displayed;
130    }
131
132    /**
133     * When the message is a request returns if the sender of the message requests to be notified
134     * when the receiver of the message is offline.
135     * When the message is a notification returns if the receiver of the message was offline.
136     * 
137     * @return true if the sender is requesting to be notified when offline or when notifying 
138     * that the receiver of the message is offline
139     */
140    public boolean isOffline() {
141        return offline;
142    }
143
144    /**
145     * When the message is a notification returns if the receiver of the message cancelled 
146     * composing a reply.
147     * 
148     * @return true if the receiver of the message cancelled composing a reply
149     */
150    public boolean isCancelled() {
151        return cancelled;
152    }
153
154    /**
155     * Returns the unique ID of the message that requested to be notified of the event.
156     * The stanza(/packet) id is not used when the message is a request for notifications
157     *
158     * @return the message id that requested to be notified of the event.
159     */
160    public String getStanzaId() {
161        return packetID;
162    }
163
164    /**
165     * Returns the types of events. The type of event could be:
166     * "offline", "composing","delivered","displayed", "offline"
167     *
168     * @return a List of all the types of events of the MessageEvent.
169     */
170    public List<String> getEventTypes() {
171        ArrayList<String> allEvents = new ArrayList<String>();
172        if (isDelivered()) {
173            allEvents.add(MessageEvent.DELIVERED);
174        }
175        if (!isMessageEventRequest() && isCancelled()) {
176            allEvents.add(MessageEvent.CANCELLED);
177        }
178        if (isComposing()) {
179            allEvents.add(MessageEvent.COMPOSING);
180        }
181        if (isDisplayed()) {
182            allEvents.add(MessageEvent.DISPLAYED);
183        }
184        if (isOffline()) {
185            allEvents.add(MessageEvent.OFFLINE);
186        }
187        return allEvents;
188    }
189
190    /**
191     * When the message is a request sets if the sender of the message requests to be notified
192     * when the receiver is composing a reply.
193     * When the message is a notification sets if the receiver of the message is composing a 
194     * reply.
195     * 
196     * @param composing sets if the sender is requesting to be notified when composing or when 
197     * notifying that the receiver of the message is composing a reply
198     */
199    public void setComposing(boolean composing) {
200        this.composing = composing;
201        setCancelled(false);
202    }
203
204    /**
205     * When the message is a request sets if the sender of the message requests to be notified
206     * when the message is delivered.
207     * When the message is a notification sets if the message was delivered or not.
208     * 
209     * @param delivered sets if the sender is requesting to be notified when delivered or when 
210     * notifying that the message was delivered 
211     */
212    public void setDelivered(boolean delivered) {
213        this.delivered = delivered;
214        setCancelled(false);
215    }
216
217    /**
218     * When the message is a request sets if the sender of the message requests to be notified
219     * when the message is displayed.
220     * When the message is a notification sets if the message was displayed or not.
221     * 
222     * @param displayed sets if the sender is requesting to be notified when displayed or when 
223     * notifying that the message was displayed
224     */
225    public void setDisplayed(boolean displayed) {
226        this.displayed = displayed;
227        setCancelled(false);
228    }
229
230    /**
231     * When the message is a request sets if the sender of the message requests to be notified
232     * when the receiver of the message is offline.
233     * When the message is a notification sets if the receiver of the message was offline.
234     * 
235     * @param offline sets if the sender is requesting to be notified when offline or when 
236     * notifying that the receiver of the message is offline
237     */
238    public void setOffline(boolean offline) {
239        this.offline = offline;
240        setCancelled(false);
241    }
242
243    /**
244     * When the message is a notification sets if the receiver of the message cancelled 
245     * composing a reply.
246     * The Cancelled event is never requested explicitly. It is requested implicitly when
247     * requesting to be notified of the Composing event.
248     * 
249     * @param cancelled sets if the receiver of the message cancelled composing a reply
250     */
251    public void setCancelled(boolean cancelled) {
252        this.cancelled = cancelled;
253    }
254
255    /**
256     * Sets the unique ID of the message that requested to be notified of the event.
257     * The stanza(/packet) id is not used when the message is a request for notifications
258     *
259     * @param packetID the message id that requested to be notified of the event.
260     */
261    public void setStanzaId(String packetID) {
262        this.packetID = packetID;
263    }
264
265    /**
266     * Returns true if this MessageEvent is a request for notifications.
267     * Returns false if this MessageEvent is a notification of an event.
268     *
269    * @return true if this message is a request for notifications.
270     */
271    public boolean isMessageEventRequest() {
272        return this.packetID == null;
273    }
274
275    /**
276     * Returns the XML representation of a Message Event according the specification.
277     * 
278     * Usually the XML representation will be inside of a Message XML representation like
279     * in the following examples:<p>
280     * 
281     * Request to be notified when displayed:
282     * <pre>
283     * &lt;message
284     *    to='romeo@montague.net/orchard'
285     *    from='juliet@capulet.com/balcony'
286     *    id='message22'&gt;
287     * &lt;x xmlns='jabber:x:event'&gt;
288     *   &lt;displayed/&gt;
289     * &lt;/x&gt;
290     * &lt;/message&gt;
291     * </pre>
292     * 
293     * Notification of displayed:
294     * <pre>
295     * &lt;message
296     *    from='romeo@montague.net/orchard'
297     *    to='juliet@capulet.com/balcony'&gt;
298     * &lt;x xmlns='jabber:x:event'&gt;
299     *   &lt;displayed/&gt;
300     *   &lt;id&gt;message22&lt;/id&gt;
301     * &lt;/x&gt;
302     * &lt;/message&gt;
303     * </pre>
304     * 
305     */
306    public String toXML() {
307        StringBuilder buf = new StringBuilder();
308        buf.append("<").append(getElementName()).append(" xmlns=\"").append(getNamespace()).append(
309            "\">");
310        // Note: Cancellation events don't specify any tag. They just send the packetID
311
312        // Add the offline tag if the sender requests to be notified of offline events or if 
313        // the target is offline
314        if (isOffline())
315            buf.append("<").append(MessageEvent.OFFLINE).append("/>");
316        // Add the delivered tag if the sender requests to be notified when the message is 
317        // delivered or if the target notifies that the message has been delivered
318        if (isDelivered())
319            buf.append("<").append(MessageEvent.DELIVERED).append("/>");
320        // Add the displayed tag if the sender requests to be notified when the message is 
321        // displayed or if the target notifies that the message has been displayed
322        if (isDisplayed())
323            buf.append("<").append(MessageEvent.DISPLAYED).append("/>");
324        // Add the composing tag if the sender requests to be notified when the target is 
325        // composing a reply or if the target notifies that he/she is composing a reply
326        if (isComposing())
327            buf.append("<").append(MessageEvent.COMPOSING).append("/>");
328        // Add the id tag only if the MessageEvent is a notification message (not a request)
329        if (getStanzaId() != null)
330            buf.append("<id>").append(getStanzaId()).append("</id>");
331        buf.append("</").append(getElementName()).append(">");
332        return buf.toString();
333    }
334
335}