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 org.jivesoftware.smack.util.StringUtils; 021import org.jivesoftware.smack.util.XmlStringBuilder; 022 023import java.util.ArrayList; 024import java.util.Collection; 025import java.util.Collections; 026import java.util.List; 027import java.util.Locale; 028import java.util.concurrent.CopyOnWriteArrayList; 029 030/** 031 * Base class for XMPP packets. Every packet has a unique ID (which is automatically 032 * generated, but can be overridden). Optionally, the "to" and "from" fields can be set. 033 * 034 * @author Matt Tucker 035 */ 036public abstract class Packet { 037 038 protected static final String DEFAULT_LANGUAGE = 039 java.util.Locale.getDefault().getLanguage().toLowerCase(Locale.US); 040 041 private static String DEFAULT_XML_NS = null; 042 043 /** 044 * Constant used as packetID to indicate that a packet has no id. To indicate that a packet 045 * has no id set this constant as the packet's id. When the packet is asked for its id the 046 * answer will be <tt>null</tt>. 047 */ 048 public static final String ID_NOT_AVAILABLE = "ID_NOT_AVAILABLE"; 049 050 /** 051 * A prefix helps to make sure that ID's are unique across mutliple instances. 052 */ 053 private static String prefix = StringUtils.randomString(5) + "-"; 054 055 /** 056 * Keeps track of the current increment, which is appended to the prefix to 057 * forum a unique ID. 058 */ 059 private static long id = 0; 060 061 private String xmlns = DEFAULT_XML_NS; 062 063 /** 064 * Returns the next unique id. Each id made up of a short alphanumeric 065 * prefix along with a unique numeric value. 066 * 067 * @return the next id. 068 */ 069 public static synchronized String nextID() { 070 return prefix + Long.toString(id++); 071 } 072 073 public static void setDefaultXmlns(String defaultXmlns) { 074 DEFAULT_XML_NS = defaultXmlns; 075 } 076 077 private String packetID = null; 078 private String to = null; 079 private String from = null; 080 private final List<PacketExtension> packetExtensions 081 = new CopyOnWriteArrayList<PacketExtension>(); 082 083 private XMPPError error = null; 084 085 public Packet() { 086 } 087 088 public Packet(Packet p) { 089 packetID = p.getPacketID(); 090 to = p.getTo(); 091 from = p.getFrom(); 092 xmlns = p.xmlns; 093 error = p.error; 094 095 // Copy extensions 096 for (PacketExtension pe : p.getExtensions()) { 097 addExtension(pe); 098 } 099 } 100 101 /** 102 * Returns the unique ID of the packet. The returned value could be <tt>null</tt> when 103 * ID_NOT_AVAILABLE was set as the packet's id. 104 * 105 * @return the packet's unique ID or <tt>null</tt> if the packet's id is not available. 106 */ 107 public String getPacketID() { 108 if (ID_NOT_AVAILABLE.equals(packetID)) { 109 return null; 110 } 111 112 if (packetID == null) { 113 packetID = nextID(); 114 } 115 return packetID; 116 } 117 118 /** 119 * Sets the unique ID of the packet. To indicate that a packet has no id 120 * pass the constant ID_NOT_AVAILABLE as the packet's id value. 121 * 122 * @param packetID the unique ID for the packet. 123 */ 124 public void setPacketID(String packetID) { 125 this.packetID = packetID; 126 } 127 128 /** 129 * Returns who the packet is being sent "to", or <tt>null</tt> if 130 * the value is not set. The XMPP protocol often makes the "to" 131 * attribute optional, so it does not always need to be set.<p> 132 * 133 * The StringUtils class provides several useful methods for dealing with 134 * XMPP addresses such as parsing the 135 * {@link StringUtils#parseBareAddress(String) bare address}, 136 * {@link StringUtils#parseName(String) user name}, 137 * {@link StringUtils#parseServer(String) server}, and 138 * {@link StringUtils#parseResource(String) resource}. 139 * 140 * @return who the packet is being sent to, or <tt>null</tt> if the 141 * value has not been set. 142 */ 143 public String getTo() { 144 return to; 145 } 146 147 /** 148 * Sets who the packet is being sent "to". The XMPP protocol often makes 149 * the "to" attribute optional, so it does not always need to be set. 150 * 151 * @param to who the packet is being sent to. 152 */ 153 public void setTo(String to) { 154 this.to = to; 155 } 156 157 /** 158 * Returns who the packet is being sent "from" or <tt>null</tt> if 159 * the value is not set. The XMPP protocol often makes the "from" 160 * attribute optional, so it does not always need to be set.<p> 161 * 162 * The StringUtils class provides several useful methods for dealing with 163 * XMPP addresses such as parsing the 164 * {@link StringUtils#parseBareAddress(String) bare address}, 165 * {@link StringUtils#parseName(String) user name}, 166 * {@link StringUtils#parseServer(String) server}, and 167 * {@link StringUtils#parseResource(String) resource}. 168 * 169 * @return who the packet is being sent from, or <tt>null</tt> if the 170 * value has not been set. 171 */ 172 public String getFrom() { 173 return from; 174 } 175 176 /** 177 * Sets who the packet is being sent "from". The XMPP protocol often 178 * makes the "from" attribute optional, so it does not always need to 179 * be set. 180 * 181 * @param from who the packet is being sent to. 182 */ 183 public void setFrom(String from) { 184 this.from = from; 185 } 186 187 /** 188 * Returns the error associated with this packet, or <tt>null</tt> if there are 189 * no errors. 190 * 191 * @return the error sub-packet or <tt>null</tt> if there isn't an error. 192 */ 193 public XMPPError getError() { 194 return error; 195 } 196 197 /** 198 * Sets the error for this packet. 199 * 200 * @param error the error to associate with this packet. 201 */ 202 public void setError(XMPPError error) { 203 this.error = error; 204 } 205 206 /** 207 * Returns an unmodifiable collection of the packet extensions attached to the packet. 208 * 209 * @return the packet extensions. 210 */ 211 public synchronized Collection<PacketExtension> getExtensions() { 212 if (packetExtensions == null) { 213 return Collections.emptyList(); 214 } 215 return Collections.unmodifiableList(new ArrayList<PacketExtension>(packetExtensions)); 216 } 217 218 /** 219 * Returns the first extension of this packet that has the given namespace. 220 * 221 * @param namespace the namespace of the extension that is desired. 222 * @return the packet extension with the given namespace. 223 */ 224 public PacketExtension getExtension(String namespace) { 225 return getExtension(null, namespace); 226 } 227 228 /** 229 * Returns the first packet extension that matches the specified element name and 230 * namespace, or <tt>null</tt> if it doesn't exist. If the provided elementName is null, 231 * only the namespace is matched. Packet extensions are 232 * are arbitrary XML sub-documents in standard XMPP packets. By default, a 233 * DefaultPacketExtension instance will be returned for each extension. However, 234 * PacketExtensionProvider instances can be registered with the 235 * {@link org.jivesoftware.smack.provider.ProviderManager ProviderManager} 236 * class to handle custom parsing. In that case, the type of the Object 237 * will be determined by the provider. 238 * 239 * @param elementName the XML element name of the packet extension. (May be null) 240 * @param namespace the XML element namespace of the packet extension. 241 * @return the extension, or <tt>null</tt> if it doesn't exist. 242 */ 243 @SuppressWarnings("unchecked") 244 public <PE extends PacketExtension> PE getExtension(String elementName, String namespace) { 245 if (namespace == null) { 246 return null; 247 } 248 for (PacketExtension ext : packetExtensions) { 249 if ((elementName == null || elementName.equals(ext.getElementName())) 250 && namespace.equals(ext.getNamespace())) 251 { 252 return (PE) ext; 253 } 254 } 255 return null; 256 } 257 258 /** 259 * Adds a packet extension to the packet. Does nothing if extension is null. 260 * 261 * @param extension a packet extension. 262 */ 263 public void addExtension(PacketExtension extension) { 264 if (extension == null) return; 265 packetExtensions.add(extension); 266 } 267 268 /** 269 * Adds a collection of packet extensions to the packet. Does nothing if extensions is null. 270 * 271 * @param extensions a collection of packet extensions 272 */ 273 public void addExtensions(Collection<PacketExtension> extensions) { 274 if (extensions == null) return; 275 packetExtensions.addAll(extensions); 276 } 277 278 /** 279 * Removes a packet extension from the packet. 280 * 281 * @param extension the packet extension to remove. 282 */ 283 public void removeExtension(PacketExtension extension) { 284 packetExtensions.remove(extension); 285 } 286 287 /** 288 * Returns the packet as XML. Every concrete extension of Packet must implement 289 * this method. In addition to writing out packet-specific data, every sub-class 290 * should also write out the error and the extensions data if they are defined. 291 * 292 * @return the XML format of the packet as a String. 293 */ 294 public abstract CharSequence toXML(); 295 296 /** 297 * Returns the extension sub-packets (including properties data) as an XML 298 * String, or the Empty String if there are no packet extensions. 299 * 300 * @return the extension sub-packets as XML or the Empty String if there 301 * are no packet extensions. 302 */ 303 protected synchronized CharSequence getExtensionsXML() { 304 XmlStringBuilder xml = new XmlStringBuilder(); 305 // Add in all standard extension sub-packets. 306 for (PacketExtension extension : getExtensions()) { 307 xml.append(extension.toXML()); 308 } 309 return xml; 310 } 311 312 public String getXmlns() { 313 return this.xmlns; 314 } 315 316 /** 317 * Returns the default language used for all messages containing localized content. 318 * 319 * @return the default language 320 */ 321 public static String getDefaultLanguage() { 322 return DEFAULT_LANGUAGE; 323 } 324 325 @Override 326 public boolean equals(Object o) { 327 if (this == o) return true; 328 if (o == null || getClass() != o.getClass()) return false; 329 330 Packet packet = (Packet) o; 331 332 if (error != null ? !error.equals(packet.error) : packet.error != null) { return false; } 333 if (from != null ? !from.equals(packet.from) : packet.from != null) { return false; } 334 if (!packetExtensions.equals(packet.packetExtensions)) { return false; } 335 if (packetID != null ? !packetID.equals(packet.packetID) : packet.packetID != null) { 336 return false; 337 } 338 if (to != null ? !to.equals(packet.to) : packet.to != null) { return false; } 339 return !(xmlns != null ? !xmlns.equals(packet.xmlns) : packet.xmlns != null); 340 } 341 342 @Override 343 public int hashCode() { 344 int result; 345 result = (xmlns != null ? xmlns.hashCode() : 0); 346 result = 31 * result + (packetID != null ? packetID.hashCode() : 0); 347 result = 31 * result + (to != null ? to.hashCode() : 0); 348 result = 31 * result + (from != null ? from.hashCode() : 0); 349 result = 31 * result + packetExtensions.hashCode(); 350 result = 31 * result + (error != null ? error.hashCode() : 0); 351 return result; 352 } 353 354 @Override 355 public String toString() { 356 return toXML().toString(); 357 } 358 359 /** 360 * Add to, from and id attributes 361 * 362 * @param xml 363 */ 364 protected void addCommonAttributes(XmlStringBuilder xml) { 365 xml.optAttribute("id", getPacketID()); 366 xml.optAttribute("to", getTo()); 367 xml.optAttribute("from", getFrom()); 368 } 369}