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.roster; 019 020import java.util.ArrayList; 021import java.util.Collection; 022import java.util.Iterator; 023import java.util.List; 024 025import org.jivesoftware.smack.Manager; 026import org.jivesoftware.smack.SmackException.NoResponseException; 027import org.jivesoftware.smack.XMPPConnection; 028import org.jivesoftware.smack.SmackException.NotConnectedException; 029import org.jivesoftware.smack.XMPPException.XMPPErrorException; 030import org.jivesoftware.smack.packet.IQ; 031import org.jivesoftware.smack.roster.packet.RosterPacket; 032 033 034/** 035 * Each user in your roster is represented by a roster entry, which contains the user's 036 * JID and a name or nickname you assign. 037 * 038 * @author Matt Tucker 039 */ 040public final class RosterEntry extends Manager { 041 042 /** 043 * The JID of the entity/user. 044 */ 045 private final String user; 046 047 private String name; 048 private RosterPacket.ItemType type; 049 private RosterPacket.ItemStatus status; 050 final private Roster roster; 051 052 /** 053 * Creates a new roster entry. 054 * 055 * @param user the user. 056 * @param name the nickname for the entry. 057 * @param type the subscription type. 058 * @param status the subscription status (related to subscriptions pending to be approbed). 059 * @param connection a connection to the XMPP server. 060 */ 061 RosterEntry(String user, String name, RosterPacket.ItemType type, 062 RosterPacket.ItemStatus status, Roster roster, XMPPConnection connection) { 063 super(connection); 064 this.user = user; 065 this.name = name; 066 this.type = type; 067 this.status = status; 068 this.roster = roster; 069 } 070 071 /** 072 * Returns the JID of the user associated with this entry. 073 * 074 * @return the user associated with this entry. 075 */ 076 public String getUser() { 077 return user; 078 } 079 080 /** 081 * Returns the name associated with this entry. 082 * 083 * @return the name. 084 */ 085 public String getName() { 086 return name; 087 } 088 089 /** 090 * Sets the name associated with this entry. 091 * 092 * @param name the name. 093 * @throws NotConnectedException 094 * @throws XMPPErrorException 095 * @throws NoResponseException 096 */ 097 public synchronized void setName(String name) throws NotConnectedException, NoResponseException, XMPPErrorException { 098 // Do nothing if the name hasn't changed. 099 if (name != null && name.equals(this.name)) { 100 return; 101 } 102 103 RosterPacket packet = new RosterPacket(); 104 packet.setType(IQ.Type.set); 105 106 // Create a new roster item with the current RosterEntry and the *new* name. Note that we can't set the name of 107 // RosterEntry right away, as otherwise the updated event wont get fired, because equalsDeep would return true. 108 packet.addRosterItem(toRosterItem(this, name)); 109 connection().createPacketCollectorAndSend(packet).nextResultOrThrow(); 110 111 // We have received a result response to the IQ set, the name was successfully changed 112 this.name = name; 113 } 114 115 /** 116 * Updates the state of the entry with the new values. 117 * 118 * @param name the nickname for the entry. 119 * @param type the subscription type. 120 * @param status the subscription status (related to subscriptions pending to be approbed). 121 */ 122 void updateState(String name, RosterPacket.ItemType type, RosterPacket.ItemStatus status) { 123 this.name = name; 124 this.type = type; 125 this.status = status; 126 } 127 128 /** 129 * Returns an copied list of the roster groups that this entry belongs to. 130 * 131 * @return an iterator for the groups this entry belongs to. 132 */ 133 public List<RosterGroup> getGroups() { 134 List<RosterGroup> results = new ArrayList<RosterGroup>(); 135 // Loop through all roster groups and find the ones that contain this 136 // entry. This algorithm should be fine 137 for (RosterGroup group: roster.getGroups()) { 138 if (group.contains(this)) { 139 results.add(group); 140 } 141 } 142 return results; 143 } 144 145 /** 146 * Returns the roster subscription type of the entry. When the type is 147 * RosterPacket.ItemType.none or RosterPacket.ItemType.from, 148 * refer to {@link RosterEntry getStatus()} to see if a subscription request 149 * is pending. 150 * 151 * @return the type. 152 */ 153 public RosterPacket.ItemType getType() { 154 return type; 155 } 156 157 /** 158 * Returns the roster subscription status of the entry. When the status is 159 * RosterPacket.ItemStatus.SUBSCRIPTION_PENDING, the contact has to answer the 160 * subscription request. 161 * 162 * @return the status. 163 */ 164 public RosterPacket.ItemStatus getStatus() { 165 return status; 166 } 167 168 public String toString() { 169 StringBuilder buf = new StringBuilder(); 170 if (name != null) { 171 buf.append(name).append(": "); 172 } 173 buf.append(user); 174 Collection<RosterGroup> groups = getGroups(); 175 if (!groups.isEmpty()) { 176 buf.append(" ["); 177 Iterator<RosterGroup> iter = groups.iterator(); 178 RosterGroup group = iter.next(); 179 buf.append(group.getName()); 180 while (iter.hasNext()) { 181 buf.append(", "); 182 group = iter.next(); 183 buf.append(group.getName()); 184 } 185 buf.append("]"); 186 } 187 return buf.toString(); 188 } 189 190 @Override 191 public int hashCode() { 192 return (user == null ? 0 : user.hashCode()); 193 } 194 195 public boolean equals(Object object) { 196 if (this == object) { 197 return true; 198 } 199 if (object != null && object instanceof RosterEntry) { 200 return user.equals(((RosterEntry)object).getUser()); 201 } 202 else { 203 return false; 204 } 205 } 206 207 /** 208 * Indicates whether some other object is "equal to" this by comparing all members. 209 * <p> 210 * The {@link #equals(Object)} method returns <code>true</code> if the user JIDs are equal. 211 * 212 * @param obj the reference object with which to compare. 213 * @return <code>true</code> if this object is the same as the obj argument; <code>false</code> 214 * otherwise. 215 */ 216 public boolean equalsDeep(Object obj) { 217 if (this == obj) 218 return true; 219 if (obj == null) 220 return false; 221 if (getClass() != obj.getClass()) 222 return false; 223 RosterEntry other = (RosterEntry) obj; 224 if (name == null) { 225 if (other.name != null) 226 return false; 227 } 228 else if (!name.equals(other.name)) 229 return false; 230 if (status == null) { 231 if (other.status != null) 232 return false; 233 } 234 else if (!status.equals(other.status)) 235 return false; 236 if (type == null) { 237 if (other.type != null) 238 return false; 239 } 240 else if (!type.equals(other.type)) 241 return false; 242 if (user == null) { 243 if (other.user != null) 244 return false; 245 } 246 else if (!user.equals(other.user)) 247 return false; 248 return true; 249 } 250 251 static RosterPacket.Item toRosterItem(RosterEntry entry) { 252 return toRosterItem(entry, entry.getName()); 253 } 254 255 private static RosterPacket.Item toRosterItem(RosterEntry entry, String name) { 256 RosterPacket.Item item = new RosterPacket.Item(entry.getUser(), name); 257 item.setItemType(entry.getType()); 258 item.setItemStatus(entry.getStatus()); 259 // Set the correct group names for the item. 260 for (RosterGroup group : entry.getGroups()) { 261 item.addGroupName(group.getName()); 262 } 263 return item; 264 } 265 266}