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