1/**
2 * $RCSfile$
3 * $Revision$
4 * $Date$
5 *
6 * Copyright 2003-2007 Jive Software.
7 *
8 * All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
9 * you may not use this file except in compliance with the License.
10 * You may obtain a copy of the License at
11 *
12 *     http://www.apache.org/licenses/LICENSE-2.0
13 *
14 * Unless required by applicable law or agreed to in writing, software
15 * distributed under the License is distributed on an "AS IS" BASIS,
16 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17 * See the License for the specific language governing permissions and
18 * limitations under the License.
19 */
20
21package org.jivesoftware.smack.packet;
22
23import org.jivesoftware.smack.util.StringUtils;
24
25/**
26 * The base IQ (Info/Query) packet. IQ packets are used to get and set information
27 * on the server, including authentication, roster operations, and creating
28 * accounts. Each IQ packet has a specific type that indicates what type of action
29 * is being taken: "get", "set", "result", or "error".<p>
30 *
31 * IQ packets can contain a single child element that exists in a specific XML
32 * namespace. The combination of the element name and namespace determines what
33 * type of IQ packet it is. Some example IQ subpacket snippets:<ul>
34 *
35 *  <li>&lt;query xmlns="jabber:iq:auth"&gt; -- an authentication IQ.
36 *  <li>&lt;query xmlns="jabber:iq:private"&gt; -- a private storage IQ.
37 *  <li>&lt;pubsub xmlns="http://jabber.org/protocol/pubsub"&gt; -- a pubsub IQ.
38 * </ul>
39 *
40 * @author Matt Tucker
41 */
42public abstract class IQ extends Packet {
43
44    private Type type = Type.GET;
45
46    public IQ() {
47        super();
48    }
49
50    public IQ(IQ iq) {
51        super(iq);
52        type = iq.getType();
53    }
54    /**
55     * Returns the type of the IQ packet.
56     *
57     * @return the type of the IQ packet.
58     */
59    public Type getType() {
60        return type;
61    }
62
63    /**
64     * Sets the type of the IQ packet.
65     *
66     * @param type the type of the IQ packet.
67     */
68    public void setType(Type type) {
69        if (type == null) {
70            this.type = Type.GET;
71        }
72        else {
73            this.type = type;
74        }
75    }
76
77    public String toXML() {
78        StringBuilder buf = new StringBuilder();
79        buf.append("<iq ");
80        if (getPacketID() != null) {
81            buf.append("id=\"" + getPacketID() + "\" ");
82        }
83        if (getTo() != null) {
84            buf.append("to=\"").append(StringUtils.escapeForXML(getTo())).append("\" ");
85        }
86        if (getFrom() != null) {
87            buf.append("from=\"").append(StringUtils.escapeForXML(getFrom())).append("\" ");
88        }
89        if (type == null) {
90            buf.append("type=\"get\">");
91        }
92        else {
93            buf.append("type=\"").append(getType()).append("\">");
94        }
95        // Add the query section if there is one.
96        String queryXML = getChildElementXML();
97        if (queryXML != null) {
98            buf.append(queryXML);
99        }
100        // Add the error sub-packet, if there is one.
101        XMPPError error = getError();
102        if (error != null) {
103            buf.append(error.toXML());
104        }
105        buf.append("</iq>");
106        return buf.toString();
107    }
108
109    /**
110     * Returns the sub-element XML section of the IQ packet, or <tt>null</tt> if there
111     * isn't one. Packet extensions <b>must</b> be included, if any are defined.<p>
112     *
113     * Extensions of this class must override this method.
114     *
115     * @return the child element section of the IQ XML.
116     */
117    public abstract String getChildElementXML();
118
119    /**
120     * Convenience method to create a new empty {@link Type#RESULT IQ.Type.RESULT}
121     * IQ based on a {@link Type#GET IQ.Type.GET} or {@link Type#SET IQ.Type.SET}
122     * IQ. The new packet will be initialized with:<ul>
123     *      <li>The sender set to the recipient of the originating IQ.
124     *      <li>The recipient set to the sender of the originating IQ.
125     *      <li>The type set to {@link Type#RESULT IQ.Type.RESULT}.
126     *      <li>The id set to the id of the originating IQ.
127     *      <li>No child element of the IQ element.
128     * </ul>
129     *
130     * @param iq the {@link Type#GET IQ.Type.GET} or {@link Type#SET IQ.Type.SET} IQ packet.
131     * @throws IllegalArgumentException if the IQ packet does not have a type of
132     *      {@link Type#GET IQ.Type.GET} or {@link Type#SET IQ.Type.SET}.
133     * @return a new {@link Type#RESULT IQ.Type.RESULT} IQ based on the originating IQ.
134     */
135    public static IQ createResultIQ(final IQ request) {
136        if (!(request.getType() == Type.GET || request.getType() == Type.SET)) {
137            throw new IllegalArgumentException(
138                    "IQ must be of type 'set' or 'get'. Original IQ: " + request.toXML());
139        }
140        final IQ result = new IQ() {
141            public String getChildElementXML() {
142                return null;
143            }
144        };
145        result.setType(Type.RESULT);
146        result.setPacketID(request.getPacketID());
147        result.setFrom(request.getTo());
148        result.setTo(request.getFrom());
149        return result;
150    }
151
152    /**
153     * Convenience method to create a new {@link Type#ERROR IQ.Type.ERROR} IQ
154     * based on a {@link Type#GET IQ.Type.GET} or {@link Type#SET IQ.Type.SET}
155     * IQ. The new packet will be initialized with:<ul>
156     *      <li>The sender set to the recipient of the originating IQ.
157     *      <li>The recipient set to the sender of the originating IQ.
158     *      <li>The type set to {@link Type#ERROR IQ.Type.ERROR}.
159     *      <li>The id set to the id of the originating IQ.
160     *      <li>The child element contained in the associated originating IQ.
161     *      <li>The provided {@link XMPPError XMPPError}.
162     * </ul>
163     *
164     * @param iq the {@link Type#GET IQ.Type.GET} or {@link Type#SET IQ.Type.SET} IQ packet.
165     * @param error the error to associate with the created IQ packet.
166     * @throws IllegalArgumentException if the IQ packet does not have a type of
167     *      {@link Type#GET IQ.Type.GET} or {@link Type#SET IQ.Type.SET}.
168     * @return a new {@link Type#ERROR IQ.Type.ERROR} IQ based on the originating IQ.
169     */
170    public static IQ createErrorResponse(final IQ request, final XMPPError error) {
171        if (!(request.getType() == Type.GET || request.getType() == Type.SET)) {
172            throw new IllegalArgumentException(
173                    "IQ must be of type 'set' or 'get'. Original IQ: " + request.toXML());
174        }
175        final IQ result = new IQ() {
176            public String getChildElementXML() {
177                return request.getChildElementXML();
178            }
179        };
180        result.setType(Type.ERROR);
181        result.setPacketID(request.getPacketID());
182        result.setFrom(request.getTo());
183        result.setTo(request.getFrom());
184        result.setError(error);
185        return result;
186    }
187
188    /**
189     * A class to represent the type of the IQ packet. The types are:
190     *
191     * <ul>
192     *      <li>IQ.Type.GET
193     *      <li>IQ.Type.SET
194     *      <li>IQ.Type.RESULT
195     *      <li>IQ.Type.ERROR
196     * </ul>
197     */
198    public static class Type {
199
200        public static final Type GET = new Type("get");
201        public static final Type SET = new Type("set");
202        public static final Type RESULT = new Type("result");
203        public static final Type ERROR = new Type("error");
204
205        /**
206         * Converts a String into the corresponding types. Valid String values
207         * that can be converted to types are: "get", "set", "result", and "error".
208         *
209         * @param type the String value to covert.
210         * @return the corresponding Type.
211         */
212        public static Type fromString(String type) {
213            if (type == null) {
214                return null;
215            }
216            type = type.toLowerCase();
217            if (GET.toString().equals(type)) {
218                return GET;
219            }
220            else if (SET.toString().equals(type)) {
221                return SET;
222            }
223            else if (ERROR.toString().equals(type)) {
224                return ERROR;
225            }
226            else if (RESULT.toString().equals(type)) {
227                return RESULT;
228            }
229            else {
230                return null;
231            }
232        }
233
234        private String value;
235
236        private Type(String value) {
237            this.value = value;
238        }
239
240        public String toString() {
241            return value;
242        }
243    }
244}
245