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.smack.packet;
019
020import java.util.Locale;
021
022import org.jivesoftware.smack.util.XmlStringBuilder;
023
024/**
025 * The base IQ (Info/Query) packet. IQ packets are used to get and set information
026 * on the server, including authentication, roster operations, and creating
027 * accounts. Each IQ packet has a specific type that indicates what type of action
028 * is being taken: "get", "set", "result", or "error".<p>
029 *
030 * IQ packets can contain a single child element that exists in a specific XML
031 * namespace. The combination of the element name and namespace determines what
032 * type of IQ packet it is. Some example IQ subpacket snippets:<ul>
033 *
034 *  <li>&lt;query xmlns="jabber:iq:auth"&gt; -- an authentication IQ.
035 *  <li>&lt;query xmlns="jabber:iq:private"&gt; -- a private storage IQ.
036 *  <li>&lt;pubsub xmlns="http://jabber.org/protocol/pubsub"&gt; -- a pubsub IQ.
037 * </ul>
038 *
039 * @author Matt Tucker
040 */
041public abstract class IQ extends Packet {
042
043    private Type type = Type.GET;
044
045    public IQ() {
046        super();
047    }
048
049    public IQ(IQ iq) {
050        super(iq);
051        type = iq.getType();
052    }
053    /**
054     * Returns the type of the IQ packet.
055     *
056     * @return the type of the IQ packet.
057     */
058    public Type getType() {
059        return type;
060    }
061
062    /**
063     * Sets the type of the IQ packet.
064     *
065     * @param type the type of the IQ packet.
066     */
067    public void setType(Type type) {
068        if (type == null) {
069            this.type = Type.GET;
070        }
071        else {
072            this.type = type;
073        }
074    }
075
076    @Override
077    public CharSequence toXML() {
078        XmlStringBuilder buf = new XmlStringBuilder();
079        buf.halfOpenElement("iq");
080        addCommonAttributes(buf);
081        if (type == null) {
082            buf.attribute("type", "get");
083        }
084        else {
085            buf.attribute("type", type.toString());
086        }
087        buf.rightAngelBracket();
088        // Add the query section if there is one.
089        buf.optAppend(getChildElementXML());
090        // Add the error sub-packet, if there is one.
091        XMPPError error = getError();
092        if (error != null) {
093            buf.append(error.toXML());
094        }
095        buf.closeElement("iq");
096        return buf;
097    }
098
099    /**
100     * Returns the sub-element XML section of the IQ packet, or <tt>null</tt> if there
101     * isn't one. Packet extensions <b>must</b> be included, if any are defined.<p>
102     *
103     * Extensions of this class must override this method.
104     *
105     * @return the child element section of the IQ XML.
106     */
107    public abstract CharSequence getChildElementXML();
108
109    /**
110     * Convenience method to create a new empty {@link Type#RESULT IQ.Type.RESULT}
111     * IQ based on a {@link Type#GET IQ.Type.GET} or {@link Type#SET IQ.Type.SET}
112     * IQ. The new packet will be initialized with:<ul>
113     *      <li>The sender set to the recipient of the originating IQ.
114     *      <li>The recipient set to the sender of the originating IQ.
115     *      <li>The type set to {@link Type#RESULT IQ.Type.RESULT}.
116     *      <li>The id set to the id of the originating IQ.
117     *      <li>No child element of the IQ element.
118     * </ul>
119     *
120     * @param request the {@link Type#GET IQ.Type.GET} or {@link Type#SET IQ.Type.SET} IQ packet.
121     * @throws IllegalArgumentException if the IQ packet does not have a type of
122     *      {@link Type#GET IQ.Type.GET} or {@link Type#SET IQ.Type.SET}.
123     * @return a new {@link Type#RESULT IQ.Type.RESULT} IQ based on the originating IQ.
124     */
125    public static IQ createResultIQ(final IQ request) {
126        if (!(request.getType() == Type.GET || request.getType() == Type.SET)) {
127            throw new IllegalArgumentException(
128                    "IQ must be of type 'set' or 'get'. Original IQ: " + request.toXML());
129        }
130        final IQ result = new IQ() {
131            public String getChildElementXML() {
132                return null;
133            }
134        };
135        result.setType(Type.RESULT);
136        result.setPacketID(request.getPacketID());
137        result.setFrom(request.getTo());
138        result.setTo(request.getFrom());
139        return result;
140    }
141
142    /**
143     * Convenience method to create a new {@link Type#ERROR IQ.Type.ERROR} IQ
144     * based on a {@link Type#GET IQ.Type.GET} or {@link Type#SET IQ.Type.SET}
145     * IQ. The new packet will be initialized with:<ul>
146     *      <li>The sender set to the recipient of the originating IQ.
147     *      <li>The recipient set to the sender of the originating IQ.
148     *      <li>The type set to {@link Type#ERROR IQ.Type.ERROR}.
149     *      <li>The id set to the id of the originating IQ.
150     *      <li>The child element contained in the associated originating IQ.
151     *      <li>The provided {@link XMPPError XMPPError}.
152     * </ul>
153     *
154     * @param request the {@link Type#GET IQ.Type.GET} or {@link Type#SET IQ.Type.SET} IQ packet.
155     * @param error the error to associate with the created IQ packet.
156     * @throws IllegalArgumentException if the IQ packet does not have a type of
157     *      {@link Type#GET IQ.Type.GET} or {@link Type#SET IQ.Type.SET}.
158     * @return a new {@link Type#ERROR IQ.Type.ERROR} IQ based on the originating IQ.
159     */
160    public static IQ createErrorResponse(final IQ request, final XMPPError error) {
161        if (!(request.getType() == Type.GET || request.getType() == Type.SET)) {
162            throw new IllegalArgumentException(
163                    "IQ must be of type 'set' or 'get'. Original IQ: " + request.toXML());
164        }
165        final IQ result = new IQ() {
166            @Override
167            public CharSequence getChildElementXML() {
168                return request.getChildElementXML();
169            }
170        };
171        result.setType(Type.ERROR);
172        result.setPacketID(request.getPacketID());
173        result.setFrom(request.getTo());
174        result.setTo(request.getFrom());
175        result.setError(error);
176        return result;
177    }
178
179    /**
180     * A class to represent the type of the IQ packet. The types are:
181     *
182     * <ul>
183     *      <li>IQ.Type.GET
184     *      <li>IQ.Type.SET
185     *      <li>IQ.Type.RESULT
186     *      <li>IQ.Type.ERROR
187     * </ul>
188     */
189    public static class Type {
190
191        public static final Type GET = new Type("get");
192        public static final Type SET = new Type("set");
193        public static final Type RESULT = new Type("result");
194        public static final Type ERROR = new Type("error");
195
196        /**
197         * Converts a String into the corresponding types. Valid String values
198         * that can be converted to types are: "get", "set", "result", and "error".
199         *
200         * @param type the String value to covert.
201         * @return the corresponding Type.
202         */
203        public static Type fromString(String type) {
204            if (type == null) {
205                return null;
206            }
207            type = type.toLowerCase(Locale.US);
208            if (GET.toString().equals(type)) {
209                return GET;
210            }
211            else if (SET.toString().equals(type)) {
212                return SET;
213            }
214            else if (ERROR.toString().equals(type)) {
215                return ERROR;
216            }
217            else if (RESULT.toString().equals(type)) {
218                return RESULT;
219            }
220            else {
221                return null;
222            }
223        }
224
225        private String value;
226
227        private Type(String value) {
228            this.value = value;
229        }
230
231        public String toString() {
232            return value;
233        }
234    }
235}