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><query xmlns="jabber:iq:auth"> -- an authentication IQ. 035 * <li><query xmlns="jabber:iq:private"> -- a private storage IQ. 036 * <li><pubsub xmlns="http://jabber.org/protocol/pubsub"> -- 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}