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