001: package org.xmpp.packet;
002:
003: import org.dom4j.Element;
004: import org.dom4j.QName;
005:
006: import java.lang.reflect.Constructor;
007: import java.util.Iterator;
008: import java.util.List;
009: import java.util.Random;
010:
011: /**
012: * IQ (Info/Query) packet. IQ packets are used to get and set information
013: * on the server, including authentication, roster operations, and creating
014: * accounts. Each IQ packet has a specific type that indicates what type of action
015: * is being taken: "get", "set", "result", or "error".<p>
016: *
017: * IQ packets can contain a single child element that exists in a extended XML
018: * namespace.
019: */
020: public class IQ extends Packet {
021:
022: // Sequence and random number generator used for creating unique ID's.
023: private static int sequence = 0;
024: private static Random random = new Random();
025:
026: /**
027: * Constructs a new IQ with an automatically generated ID and a type
028: * of {@link Type#get IQ.Type.get}.
029: */
030: public IQ() {
031: this .element = docFactory.createDocument().addElement("iq");
032: String id = String.valueOf(random.nextInt(1000) + "-"
033: + sequence++);
034: setType(Type.get);
035: setID(id);
036: }
037:
038: /**
039: * Constructs a new IQ using the specified type. A packet ID will
040: * be automatically generated.
041: *
042: * @param type the IQ type.
043: */
044: public IQ(Type type) {
045: this .element = docFactory.createDocument().addElement("iq");
046: setType(type);
047: String id = String.valueOf(random.nextInt(1000) + "-"
048: + sequence++);
049: setID(id);
050: }
051:
052: /**
053: * Constructs a new IQ using the specified type and ID.
054: *
055: * @param ID the packet ID of the IQ.
056: * @param type the IQ type.
057: */
058: public IQ(Type type, String ID) {
059: this .element = docFactory.createDocument().addElement("iq");
060: setType(type);
061: setID(ID);
062: }
063:
064: /**
065: * Constructs a new IQ using an existing Element. This is useful
066: * for parsing incoming IQ Elements into IQ objects.
067: *
068: * @param element the IQ Element.
069: */
070: public IQ(Element element) {
071: super (element);
072: }
073:
074: /**
075: * Constructs a new IQ using an existing Element. This is useful
076: * for parsing incoming IQ Elements into IQ objects. Stringprep validation
077: * on the TO address can be disabled. The FROM address will not be validated since the
078: * server is the one that sets that value.
079: *
080: * @param element the IQ Element.
081: * @param skipValidation true if stringprep should not be applied to the TO address.
082: */
083: public IQ(Element element, boolean skipValidation) {
084: super (element, skipValidation);
085: }
086:
087: /**
088: * Constructs a new IQ that is a copy of an existing IQ.
089: *
090: * @param iq the iq packet.
091: * @see #createCopy()
092: */
093: private IQ(IQ iq) {
094: Element elementCopy = iq.element.createCopy();
095: docFactory.createDocument().add(elementCopy);
096: this .element = elementCopy;
097: // Copy cached JIDs (for performance reasons)
098: this .toJID = iq.toJID;
099: this .fromJID = iq.fromJID;
100: }
101:
102: /**
103: * Returns the type of this IQ.
104: *
105: * @return the IQ type.
106: * @see Type
107: */
108: public Type getType() {
109: String type = element.attributeValue("type");
110: if (type != null) {
111: return Type.valueOf(type);
112: } else {
113: return null;
114: }
115: }
116:
117: /**
118: * Sets the type of this IQ.
119: *
120: * @param type the IQ type.
121: * @see Type
122: */
123: public void setType(Type type) {
124: element.addAttribute("type", type == null ? null : type
125: .toString());
126: }
127:
128: /**
129: * Convenience routine to indicate if this is a request stanza. (get or set)
130: *
131: * @return True or false if this is a request stanza
132: */
133: public boolean isRequest() {
134: Type type = getType();
135: return (type != null && (type.equals(Type.get) || type
136: .equals(Type.set)));
137: }
138:
139: /**
140: * Convenience routine to indicate if this is a response stanza. (result or error)
141: *
142: * @return True or false if this is a response stanza
143: */
144: public boolean isResponse() {
145: Type type = getType();
146: return (type != null && (type.equals(Type.result) || type
147: .equals(Type.error)));
148: }
149:
150: /**
151: * Returns the child element of this IQ. IQ packets may have a single child
152: * element in an extended namespace. This is a convenience method to
153: * avoid manipulating the underlying packet's Element instance directly.<p>
154: *
155: * An IQ child element in extended namespaces is used to extend the features
156: * of XMPP. Although any valid XML can be included in a child element
157: * in an extended namespace, many common features have been standardized
158: * as <a href="http://www.jabber.org/jeps">Jabber Enhancement Proposals</a>
159: * (JEPs).
160: *
161: * @return the child element.
162: */
163: public Element getChildElement() {
164: List elements = element.elements();
165: if (elements.isEmpty()) {
166: return null;
167: } else {
168: // Search for a child element that is in a different namespace.
169: for (int i = 0; i < elements.size(); i++) {
170: Element element = (Element) elements.get(i);
171: String namespace = element.getNamespaceURI();
172: if (!namespace.equals("")
173: && !namespace.equals("jabber:client")
174: && !namespace.equals("jabber:server")) {
175: return element;
176: }
177: }
178: return null;
179: }
180: }
181:
182: /**
183: * Sets the child element of this IQ. IQ packets may have a single child
184: * element in an extended namespace. This is a convenience method to
185: * avoid manipulating this underlying packet's Element instance directly.<p>
186: *
187: * A sample use of this method might look like the following:
188: * <pre>
189: * IQ iq = new IQ("time_1");
190: * iq.setTo("mary@example.com");
191: * iq.setType(IQ.Type.GET);
192: * iq.setChildElement(docFactory.createElement("query", "jabber:iq:time"));</pre><p>
193: *
194: * An IQ child element in extended namespaces is used to extend the features
195: * of XMPP. Although any valid XML can be included in a child element
196: * in an extended namespace, many common features have been standardized
197: * as <a href="http://www.jabber.org/jeps">Jabber Enhancement Proposals</a>
198: * (JEPs).
199: *
200: * @param childElement the child element.
201: */
202: public void setChildElement(Element childElement) {
203: for (Iterator i = element.elementIterator(); i.hasNext();) {
204: element.remove((Element) i.next());
205: }
206: element.add(childElement);
207: }
208:
209: /**
210: * Sets the child element of this IQ by constructing a new Element with the
211: * given name and namespace. The newly created child element is returned.
212: * IQ packets may have a single child element in an extended namespace.
213: * This method is a convenience method to avoid manipulating the underlying
214: * packet's Element instance directly.<p>
215: *
216: * In some cases, additional custom sub-elements must be added to an IQ child
217: * element (called packet extensions). For example, when adding a data form to
218: * an IQ response. See {@link #addExtension(PacketExtension)}.<p>
219: *
220: * A sample use of this method might look like the following:
221: * <pre>
222: * IQ iq = new IQ("time_1");
223: * iq.setTo("mary@example.com");
224: * iq.setType(IQ.Type.GET);
225: * iq.setChildElement("query", "jabber:iq:time");</pre>
226: *
227: * @param name the child element name.
228: * @param namespace the child element namespace.
229: * @return the newly created child element.
230: */
231: public Element setChildElement(String name, String namespace) {
232: for (Iterator i = element.elementIterator(); i.hasNext();) {
233: element.remove((Element) i.next());
234: }
235: return element.addElement(name, namespace);
236: }
237:
238: /**
239: * Adds the element contained in the PacketExtension to the child element of the IQ
240: * packet. IQ packets, unlike the other packet types, have a unique child element that
241: * holds the packet extensions. If an extension is added to an IQ packet that does
242: * not have a child element then an IllegalStateException will be thrown.<p>
243: *
244: * It is important that this is the first and last time the element contained in
245: * PacketExtension is added to another Packet. Otherwise, a runtime error will be
246: * thrown when trying to add the PacketExtension's element to the Packet's element.
247: * Future modifications to the PacketExtension will be reflected in this Packet.<p>
248: *
249: * Note: packet extensions on IQ packets are only for use in specialized situations.
250: * In most cases, you should only need to set the child element of the IQ.
251: *
252: * @param extension the PacketExtension whose element will be added to this Packet's element.
253: */
254: public void addExtension(PacketExtension extension) {
255: Element childElement = getChildElement();
256: if (childElement == null) {
257: throw new IllegalStateException(
258: "Cannot add packet extension when child element is null");
259: }
260: // Add the extension to the child element
261: childElement.add(extension.getElement());
262: }
263:
264: /**
265: * Returns a {@link PacketExtension} on the first element found in this packet's
266: * child element for the specified <tt>name</tt> and <tt>namespace</tt> or <tt>null</tt> if
267: * none was found. If the IQ packet does not have a child element then <tt>null</tt>
268: * will be returned.<p>
269: *
270: * Note: packet extensions on IQ packets are only for use in specialized situations.
271: * In most cases, you should only need to set the child element of the IQ.
272: *
273: * @param name the child element name.
274: * @param namespace the child element namespace.
275: * @return a PacketExtension on the first element found in this packet for the specified
276: * name and namespace or <tt>null</tt> if none was found.
277: */
278: public PacketExtension getExtension(String name, String namespace) {
279: Element childElement = getChildElement();
280: if (childElement == null) {
281: return null;
282: }
283: // Search for extensions in the child element
284: List extensions = childElement.elements(QName.get(name,
285: namespace));
286: if (!extensions.isEmpty()) {
287: Class extensionClass = PacketExtension.getExtensionClass(
288: name, namespace);
289: if (extensionClass != null) {
290: try {
291: Constructor constructor = extensionClass
292: .getDeclaredConstructor(new Class[] { Element.class });
293: return (PacketExtension) constructor
294: .newInstance(new Object[] { extensions
295: .get(0) });
296: } catch (Exception e) {
297: }
298: }
299: }
300: return null;
301: }
302:
303: /**
304: * Deletes the first element whose element name and namespace matches the specified
305: * element name and namespace in this packet's child element. If the
306: * IQ packet does not have a child element then this method does nothing and returns
307: * <tt>false</tt>.<p>
308: *
309: * Notice that this method may remove any child element that matches the specified
310: * element name and namespace even if that element was not added to the Packet using a
311: * {@link PacketExtension}.<p>
312: *
313: * Note: packet extensions on IQ packets are only for use in specialized situations.
314: * In most cases, you should only need to set the child element of the IQ.
315: *
316: * @param name the child element name.
317: * @param namespace the child element namespace.
318: * @return true if a child element was removed.
319: */
320: public boolean deleteExtension(String name, String namespace) {
321: Element childElement = getChildElement();
322: if (childElement == null) {
323: return false;
324: }
325: // Delete extensions in the child element
326: List extensions = childElement.elements(QName.get(name,
327: namespace));
328: if (!extensions.isEmpty()) {
329: childElement.remove((Element) extensions.get(0));
330: return true;
331: }
332: return false;
333: }
334:
335: /**
336: * Returns a deep copy of this IQ.
337: *
338: * @return a deep copy of this IQ.
339: */
340: public IQ createCopy() {
341: return new IQ(this );
342: }
343:
344: /**
345: * Convenience method to create a new {@link Type#result IQ.Type.result} IQ based
346: * on a {@link Type#get IQ.Type.get} or {@link Type#set IQ.Type.set} IQ. The new
347: * packet will be initialized with:<ul>
348: *
349: * <li>The sender set to the recipient of the originating IQ.
350: * <li>The recipient set to the sender of the originating IQ.
351: * <li>The type set to {@link Type#result IQ.Type.result}.
352: * <li>The id set to the id of the originating IQ.
353: * </ul>
354: *
355: * @param iq the {@link Type#get IQ.Type.get} or {@link Type#set IQ.Type.set} IQ packet.
356: * @throws IllegalArgumentException if the IQ packet does not have a type of
357: * {@link Type#get IQ.Type.get} or {@link Type#set IQ.Type.set}.
358: * @return a new {@link Type#result IQ.Type.result} IQ based on the originating IQ.
359: */
360: public static IQ createResultIQ(IQ iq) {
361: if (!(iq.getType() == Type.get || iq.getType() == Type.set)) {
362: throw new IllegalArgumentException(
363: "IQ must be of type 'set' or 'get'. Original IQ: "
364: + iq.toXML());
365: }
366: IQ result = new IQ(Type.result, iq.getID());
367: result.setFrom(iq.getTo());
368: result.setTo(iq.getFrom());
369: return result;
370: }
371:
372: /**
373: * Type-safe enumeration to represent the type of the IQ packet. The types are:
374: *
375: * <ul>
376: * <li>IQ.Type.get -- the IQ is a request for information or requirements.
377: * <li>IQ.Type.set -- the IQ provides required data, sets new values, or
378: * replaces existing values.
379: * <li>IQ.Type.result -- the IQ is a response to a successful get or set request.
380: * <li>IQ.Type.error -- an error has occurred regarding processing or delivery of a
381: * previously-sent get or set.
382: * </ul>
383: *
384: * If {@link #get IQ.Type.get} or {@link #set IQ.Type.set} is received the response
385: * must be {@link #result IQ.Type.result} or {@link #error IQ.Type.error}. The id of the
386: * originating {@link #get IQ.Type.get} of {@link #set IQ.Type.set} IQ must be preserved
387: * when sending {@link #result IQ.Type.result} or {@link #error IQ.Type.error}.
388: */
389: public enum Type {
390:
391: /**
392: * The IQ is a request for information or requirements.
393: */
394: get,
395:
396: /**
397: * The IQ provides required data, sets new values, or
398: * replaces existing values.
399: */
400: set,
401:
402: /**
403: * The IQ is a response to a successful get or set request.
404: */
405: result,
406:
407: /**
408: * An error has occurred regarding processing or delivery of a
409: * previously-sent get or set.
410: */
411: error;
412:
413: }
414: }
|