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.PacketExtension;
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 PacketExtension {
057
058    public static final String OFFLINE = "offline";
059    public static final String COMPOSING = "composing";
060    public static final String DISPLAYED = "displayed";
061    public static final String DELIVERED = "delivered";
062    public static final String CANCELLED = "cancelled";
063
064    private boolean offline = false;
065    private boolean delivered = false;
066    private boolean displayed = false;
067    private boolean composing = false;
068    private boolean cancelled = true;
069
070    private String packetID = null;
071
072    /**
073    * Returns the XML element name of the extension sub-packet root element.
074    * Always returns "x"
075    *
076    * @return the XML element name of the packet extension.
077    */
078    public String getElementName() {
079        return "x";
080    }
081
082    /** 
083     * Returns the XML namespace of the extension sub-packet root element.
084     * According the specification the namespace is always "jabber:x:event"
085     *
086     * @return the XML namespace of the packet extension.
087     */
088    public String getNamespace() {
089        return "jabber:x:event";
090    }
091
092    /**
093     * When the message is a request returns if the sender of the message requests to be notified
094     * when the receiver is composing a reply.
095     * When the message is a notification returns if the receiver of the message is composing a 
096     * reply.
097     * 
098     * @return true if the sender is requesting to be notified when composing or when notifying
099     * that the receiver of the message is composing a reply
100     */
101    public boolean isComposing() {
102        return composing;
103    }
104
105    /**
106     * When the message is a request returns if the sender of the message requests to be notified
107     * when the message is delivered.
108     * When the message is a notification returns if the message was delivered or not.
109     * 
110     * @return true if the sender is requesting to be notified when delivered or when notifying 
111     * that the message was delivered 
112     */
113    public boolean isDelivered() {
114        return delivered;
115    }
116
117    /**
118     * When the message is a request returns if the sender of the message requests to be notified
119     * when the message is displayed.
120     * When the message is a notification returns if the message was displayed or not.
121     * 
122     * @return true if the sender is requesting to be notified when displayed or when notifying 
123     * that the message was displayed
124     */
125    public boolean isDisplayed() {
126        return displayed;
127    }
128
129    /**
130     * When the message is a request returns if the sender of the message requests to be notified
131     * when the receiver of the message is offline.
132     * When the message is a notification returns if the receiver of the message was offline.
133     * 
134     * @return true if the sender is requesting to be notified when offline or when notifying 
135     * that the receiver of the message is offline
136     */
137    public boolean isOffline() {
138        return offline;
139    }
140
141    /**
142     * When the message is a notification returns if the receiver of the message cancelled 
143     * composing a reply.
144     * 
145     * @return true if the receiver of the message cancelled composing a reply
146     */
147    public boolean isCancelled() {
148        return cancelled;
149    }
150
151    /**
152     * Returns the unique ID of the message that requested to be notified of the event.
153     * The packet id is not used when the message is a request for notifications
154     *
155     * @return the message id that requested to be notified of the event.
156     */
157    public String getPacketID() {
158        return packetID;
159    }
160
161    /**
162     * Returns the types of events. The type of event could be:
163     * "offline", "composing","delivered","displayed", "offline"
164     *
165     * @return a List of all the types of events of the MessageEvent.
166     */
167    public List<String> getEventTypes() {
168        ArrayList<String> allEvents = new ArrayList<String>();
169        if (isDelivered()) {
170            allEvents.add(MessageEvent.DELIVERED);
171        }
172        if (!isMessageEventRequest() && isCancelled()) {
173            allEvents.add(MessageEvent.CANCELLED);
174        }
175        if (isComposing()) {
176            allEvents.add(MessageEvent.COMPOSING);
177        }
178        if (isDisplayed()) {
179            allEvents.add(MessageEvent.DISPLAYED);
180        }
181        if (isOffline()) {
182            allEvents.add(MessageEvent.OFFLINE);
183        }
184        return allEvents;
185    }
186
187    /**
188     * When the message is a request sets if the sender of the message requests to be notified
189     * when the receiver is composing a reply.
190     * When the message is a notification sets if the receiver of the message is composing a 
191     * reply.
192     * 
193     * @param composing sets if the sender is requesting to be notified when composing or when 
194     * notifying that the receiver of the message is composing a reply
195     */
196    public void setComposing(boolean composing) {
197        this.composing = composing;
198        setCancelled(false);
199    }
200
201    /**
202     * When the message is a request sets if the sender of the message requests to be notified
203     * when the message is delivered.
204     * When the message is a notification sets if the message was delivered or not.
205     * 
206     * @param delivered sets if the sender is requesting to be notified when delivered or when 
207     * notifying that the message was delivered 
208     */
209    public void setDelivered(boolean delivered) {
210        this.delivered = delivered;
211        setCancelled(false);
212    }
213
214    /**
215     * When the message is a request sets if the sender of the message requests to be notified
216     * when the message is displayed.
217     * When the message is a notification sets if the message was displayed or not.
218     * 
219     * @param displayed sets if the sender is requesting to be notified when displayed or when 
220     * notifying that the message was displayed
221     */
222    public void setDisplayed(boolean displayed) {
223        this.displayed = displayed;
224        setCancelled(false);
225    }
226
227    /**
228     * When the message is a request sets if the sender of the message requests to be notified
229     * when the receiver of the message is offline.
230     * When the message is a notification sets if the receiver of the message was offline.
231     * 
232     * @param offline sets if the sender is requesting to be notified when offline or when 
233     * notifying that the receiver of the message is offline
234     */
235    public void setOffline(boolean offline) {
236        this.offline = offline;
237        setCancelled(false);
238    }
239
240    /**
241     * When the message is a notification sets if the receiver of the message cancelled 
242     * composing a reply.
243     * The Cancelled event is never requested explicitly. It is requested implicitly when
244     * requesting to be notified of the Composing event.
245     * 
246     * @param cancelled sets if the receiver of the message cancelled composing a reply
247     */
248    public void setCancelled(boolean cancelled) {
249        this.cancelled = cancelled;
250    }
251
252    /**
253     * Sets the unique ID of the message that requested to be notified of the event.
254     * The packet id is not used when the message is a request for notifications
255     *
256     * @param packetID the message id that requested to be notified of the event.
257     */
258    public void setPacketID(String packetID) {
259        this.packetID = packetID;
260    }
261
262    /**
263     * Returns true if this MessageEvent is a request for notifications.
264     * Returns false if this MessageEvent is a notification of an event.
265     *
266    * @return true if this message is a request for notifications.
267     */
268    public boolean isMessageEventRequest() {
269        return this.packetID == null;
270    }
271
272    /**
273     * Returns the XML representation of a Message Event according the specification.
274     * 
275     * Usually the XML representation will be inside of a Message XML representation like
276     * in the following examples:<p>
277     * 
278     * Request to be notified when displayed:
279     * <pre>
280     * &lt;message
281     *    to='romeo@montague.net/orchard'
282     *    from='juliet@capulet.com/balcony'
283     *    id='message22'&gt;
284     * &lt;x xmlns='jabber:x:event'&gt;
285     *   &lt;displayed/&gt;
286     * &lt;/x&gt;
287     * &lt;/message&gt;
288     * </pre>
289     * 
290     * Notification of displayed:
291     * <pre>
292     * &lt;message
293     *    from='romeo@montague.net/orchard'
294     *    to='juliet@capulet.com/balcony'&gt;
295     * &lt;x xmlns='jabber:x:event'&gt;
296     *   &lt;displayed/&gt;
297     *   &lt;id&gt;message22&lt;/id&gt;
298     * &lt;/x&gt;
299     * &lt;/message&gt;
300     * </pre>
301     * 
302     */
303    public String toXML() {
304        StringBuilder buf = new StringBuilder();
305        buf.append("<").append(getElementName()).append(" xmlns=\"").append(getNamespace()).append(
306            "\">");
307        // Note: Cancellation events don't specify any tag. They just send the packetID
308
309        // Add the offline tag if the sender requests to be notified of offline events or if 
310        // the target is offline
311        if (isOffline())
312            buf.append("<").append(MessageEvent.OFFLINE).append("/>");
313        // Add the delivered tag if the sender requests to be notified when the message is 
314        // delivered or if the target notifies that the message has been delivered
315        if (isDelivered())
316            buf.append("<").append(MessageEvent.DELIVERED).append("/>");
317        // Add the displayed tag if the sender requests to be notified when the message is 
318        // displayed or if the target notifies that the message has been displayed
319        if (isDisplayed())
320            buf.append("<").append(MessageEvent.DISPLAYED).append("/>");
321        // Add the composing tag if the sender requests to be notified when the target is 
322        // composing a reply or if the target notifies that he/she is composing a reply
323        if (isComposing())
324            buf.append("<").append(MessageEvent.COMPOSING).append("/>");
325        // Add the id tag only if the MessageEvent is a notification message (not a request)
326        if (getPacketID() != null)
327            buf.append("<id>").append(getPacketID()).append("</id>");
328        buf.append("</").append(getElementName()).append(">");
329        return buf.toString();
330    }
331
332}