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.*; 024import java.util.concurrent.CopyOnWriteArraySet; 025 026/** 027 * Represents XMPP roster packets. 028 * 029 * @author Matt Tucker 030 */ 031public class RosterPacket extends IQ { 032 033 private final List<Item> rosterItems = new ArrayList<Item>(); 034 private String rosterVersion; 035 036 /** 037 * Adds a roster item to the packet. 038 * 039 * @param item a roster item. 040 */ 041 public void addRosterItem(Item item) { 042 synchronized (rosterItems) { 043 rosterItems.add(item); 044 } 045 } 046 047 /** 048 * Returns the number of roster items in this roster packet. 049 * 050 * @return the number of roster items. 051 */ 052 public int getRosterItemCount() { 053 synchronized (rosterItems) { 054 return rosterItems.size(); 055 } 056 } 057 058 /** 059 * Returns an unmodifiable collection for the roster items in the packet. 060 * 061 * @return an unmodifiable collection for the roster items in the packet. 062 */ 063 public Collection<Item> getRosterItems() { 064 synchronized (rosterItems) { 065 return Collections.unmodifiableList(new ArrayList<Item>(rosterItems)); 066 } 067 } 068 069 public CharSequence getChildElementXML() { 070 XmlStringBuilder buf = new XmlStringBuilder(); 071 buf.halfOpenElement("query"); 072 buf.xmlnsAttribute("jabber:iq:roster"); 073 buf.optAttribute("ver", rosterVersion); 074 buf.rightAngelBracket(); 075 076 synchronized (rosterItems) { 077 for (Item entry : rosterItems) { 078 buf.append(entry.toXML()); 079 } 080 } 081 buf.closeElement("query"); 082 return buf; 083 } 084 085 public String getVersion() { 086 return rosterVersion; 087 } 088 089 public void setVersion(String version) { 090 rosterVersion = version; 091 } 092 093 /** 094 * A roster item, which consists of a JID, their name, the type of subscription, and 095 * the groups the roster item belongs to. 096 */ 097 public static class Item { 098 099 private String user; 100 private String name; 101 private ItemType itemType; 102 private ItemStatus itemStatus; 103 private final Set<String> groupNames; 104 105 /** 106 * Creates a new roster item. 107 * 108 * @param user the user. 109 * @param name the user's name. 110 */ 111 public Item(String user, String name) { 112 this.user = user.toLowerCase(Locale.US); 113 this.name = name; 114 itemType = null; 115 itemStatus = null; 116 groupNames = new CopyOnWriteArraySet<String>(); 117 } 118 119 /** 120 * Returns the user. 121 * 122 * @return the user. 123 */ 124 public String getUser() { 125 return user; 126 } 127 128 /** 129 * Returns the user's name. 130 * 131 * @return the user's name. 132 */ 133 public String getName() { 134 return name; 135 } 136 137 /** 138 * Sets the user's name. 139 * 140 * @param name the user's name. 141 */ 142 public void setName(String name) { 143 this.name = name; 144 } 145 146 /** 147 * Returns the roster item type. 148 * 149 * @return the roster item type. 150 */ 151 public ItemType getItemType() { 152 return itemType; 153 } 154 155 /** 156 * Sets the roster item type. 157 * 158 * @param itemType the roster item type. 159 */ 160 public void setItemType(ItemType itemType) { 161 this.itemType = itemType; 162 } 163 164 /** 165 * Returns the roster item status. 166 * 167 * @return the roster item status. 168 */ 169 public ItemStatus getItemStatus() { 170 return itemStatus; 171 } 172 173 /** 174 * Sets the roster item status. 175 * 176 * @param itemStatus the roster item status. 177 */ 178 public void setItemStatus(ItemStatus itemStatus) { 179 this.itemStatus = itemStatus; 180 } 181 182 /** 183 * Returns an unmodifiable set of the group names that the roster item 184 * belongs to. 185 * 186 * @return an unmodifiable set of the group names. 187 */ 188 public Set<String> getGroupNames() { 189 return Collections.unmodifiableSet(groupNames); 190 } 191 192 /** 193 * Adds a group name. 194 * 195 * @param groupName the group name. 196 */ 197 public void addGroupName(String groupName) { 198 groupNames.add(groupName); 199 } 200 201 /** 202 * Removes a group name. 203 * 204 * @param groupName the group name. 205 */ 206 public void removeGroupName(String groupName) { 207 groupNames.remove(groupName); 208 } 209 210 public String toXML() { 211 StringBuilder buf = new StringBuilder(); 212 buf.append("<item jid=\"").append(StringUtils.escapeForXML(user)).append("\""); 213 if (name != null) { 214 buf.append(" name=\"").append(StringUtils.escapeForXML(name)).append("\""); 215 } 216 if (itemType != null) { 217 buf.append(" subscription=\"").append(itemType).append("\""); 218 } 219 if (itemStatus != null) { 220 buf.append(" ask=\"").append(itemStatus).append("\""); 221 } 222 buf.append(">"); 223 for (String groupName : groupNames) { 224 buf.append("<group>").append(StringUtils.escapeForXML(groupName)).append("</group>"); 225 } 226 buf.append("</item>"); 227 return buf.toString(); 228 } 229 230 @Override 231 public int hashCode() { 232 final int prime = 31; 233 int result = 1; 234 result = prime * result + ((groupNames == null) ? 0 : groupNames.hashCode()); 235 result = prime * result + ((itemStatus == null) ? 0 : itemStatus.hashCode()); 236 result = prime * result + ((itemType == null) ? 0 : itemType.hashCode()); 237 result = prime * result + ((name == null) ? 0 : name.hashCode()); 238 result = prime * result + ((user == null) ? 0 : user.hashCode()); 239 return result; 240 } 241 242 @Override 243 public boolean equals(Object obj) { 244 if (this == obj) 245 return true; 246 if (obj == null) 247 return false; 248 if (getClass() != obj.getClass()) 249 return false; 250 Item other = (Item) obj; 251 if (groupNames == null) { 252 if (other.groupNames != null) 253 return false; 254 } 255 else if (!groupNames.equals(other.groupNames)) 256 return false; 257 if (itemStatus != other.itemStatus) 258 return false; 259 if (itemType != other.itemType) 260 return false; 261 if (name == null) { 262 if (other.name != null) 263 return false; 264 } 265 else if (!name.equals(other.name)) 266 return false; 267 if (user == null) { 268 if (other.user != null) 269 return false; 270 } 271 else if (!user.equals(other.user)) 272 return false; 273 return true; 274 } 275 276 } 277 278 /** 279 * The subscription status of a roster item. An optional element that indicates 280 * the subscription status if a change request is pending. 281 */ 282 public static enum ItemStatus { 283 /** 284 * Request to subscribe 285 */ 286 subscribe, 287 288 /** 289 * Request to unsubscribe 290 */ 291 unsubscribe; 292 293 public static final ItemStatus SUBSCRIPTION_PENDING = subscribe; 294 public static final ItemStatus UNSUBSCRIPTION_PENDING = unsubscribe; 295 296 public static ItemStatus fromString(String s) { 297 if (s == null) { 298 return null; 299 } 300 try { 301 return ItemStatus.valueOf(s); 302 } 303 catch (IllegalArgumentException e) { 304 return null; 305 } 306 } 307 } 308 309 public static enum ItemType { 310 311 /** 312 * The user and subscriber have no interest in each other's presence. 313 */ 314 none, 315 316 /** 317 * The user is interested in receiving presence updates from the subscriber. 318 */ 319 to, 320 321 /** 322 * The subscriber is interested in receiving presence updates from the user. 323 */ 324 from, 325 326 /** 327 * The user and subscriber have a mutual interest in each other's presence. 328 */ 329 both, 330 331 /** 332 * The user wishes to stop receiving presence updates from the subscriber. 333 */ 334 remove 335 } 336}