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