1/**
2 * $RCSfile$
3 * $Revision$
4 * $Date$
5 *
6 * Copyright 2003-2007 Jive Software.
7 *
8 * All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
9 * you may not use this file except in compliance with the License.
10 * You may obtain a copy of the License at
11 *
12 *     http://www.apache.org/licenses/LICENSE-2.0
13 *
14 * Unless required by applicable law or agreed to in writing, software
15 * distributed under the License is distributed on an "AS IS" BASIS,
16 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17 * See the License for the specific language governing permissions and
18 * limitations under the License.
19 */
20
21package org.jivesoftware.smack;
22
23import org.jivesoftware.smack.packet.IQ;
24import org.jivesoftware.smack.packet.RosterPacket;
25
26import java.util.*;
27
28/**
29 * Each user in your roster is represented by a roster entry, which contains the user's
30 * JID and a name or nickname you assign.
31 *
32 * @author Matt Tucker
33 */
34public class RosterEntry {
35
36    private String user;
37    private String name;
38    private RosterPacket.ItemType type;
39    private RosterPacket.ItemStatus status;
40    final private Roster roster;
41    final private Connection connection;
42
43    /**
44     * Creates a new roster entry.
45     *
46     * @param user the user.
47     * @param name the nickname for the entry.
48     * @param type the subscription type.
49     * @param status the subscription status (related to subscriptions pending to be approbed).
50     * @param connection a connection to the XMPP server.
51     */
52    RosterEntry(String user, String name, RosterPacket.ItemType type,
53                RosterPacket.ItemStatus status, Roster roster, Connection connection) {
54        this.user = user;
55        this.name = name;
56        this.type = type;
57        this.status = status;
58        this.roster = roster;
59        this.connection = connection;
60    }
61
62    /**
63     * Returns the JID of the user associated with this entry.
64     *
65     * @return the user associated with this entry.
66     */
67    public String getUser() {
68        return user;
69    }
70
71    /**
72     * Returns the name associated with this entry.
73     *
74     * @return the name.
75     */
76    public String getName() {
77        return name;
78    }
79
80    /**
81     * Sets the name associated with this entry.
82     *
83     * @param name the name.
84     */
85    public void setName(String name) {
86        // Do nothing if the name hasn't changed.
87        if (name != null && name.equals(this.name)) {
88            return;
89        }
90        this.name = name;
91        RosterPacket packet = new RosterPacket();
92        packet.setType(IQ.Type.SET);
93        packet.addRosterItem(toRosterItem(this));
94        connection.sendPacket(packet);
95    }
96
97    /**
98     * Updates the state of the entry with the new values.
99     *
100     * @param name the nickname for the entry.
101     * @param type the subscription type.
102     * @param status the subscription status (related to subscriptions pending to be approbed).
103     */
104    void updateState(String name, RosterPacket.ItemType type, RosterPacket.ItemStatus status) {
105        this.name = name;
106        this.type = type;
107        this.status = status;
108    }
109
110    /**
111     * Returns an unmodifiable collection of the roster groups that this entry belongs to.
112     *
113     * @return an iterator for the groups this entry belongs to.
114     */
115    public Collection<RosterGroup> getGroups() {
116        List<RosterGroup> results = new ArrayList<RosterGroup>();
117        // Loop through all roster groups and find the ones that contain this
118        // entry. This algorithm should be fine
119        for (RosterGroup group: roster.getGroups()) {
120            if (group.contains(this)) {
121                results.add(group);
122            }
123        }
124        return Collections.unmodifiableCollection(results);
125    }
126
127    /**
128     * Returns the roster subscription type of the entry. When the type is
129     * RosterPacket.ItemType.none or RosterPacket.ItemType.from,
130     * refer to {@link RosterEntry getStatus()} to see if a subscription request
131     * is pending.
132     *
133     * @return the type.
134     */
135    public RosterPacket.ItemType getType() {
136        return type;
137    }
138
139    /**
140     * Returns the roster subscription status of the entry. When the status is
141     * RosterPacket.ItemStatus.SUBSCRIPTION_PENDING, the contact has to answer the
142     * subscription request.
143     *
144     * @return the status.
145     */
146    public RosterPacket.ItemStatus getStatus() {
147        return status;
148    }
149
150    public String toString() {
151        StringBuilder buf = new StringBuilder();
152        if (name != null) {
153            buf.append(name).append(": ");
154        }
155        buf.append(user);
156        Collection<RosterGroup> groups = getGroups();
157        if (!groups.isEmpty()) {
158            buf.append(" [");
159            Iterator<RosterGroup> iter = groups.iterator();
160            RosterGroup group = iter.next();
161            buf.append(group.getName());
162            while (iter.hasNext()) {
163            buf.append(", ");
164                group = iter.next();
165                buf.append(group.getName());
166            }
167            buf.append("]");
168        }
169        return buf.toString();
170    }
171
172    public boolean equals(Object object) {
173        if (this == object) {
174            return true;
175        }
176        if (object != null && object instanceof RosterEntry) {
177            return user.equals(((RosterEntry)object).getUser());
178        }
179        else {
180            return false;
181        }
182    }
183
184    @Override
185    public int hashCode() {
186        return this.user.hashCode();
187    }
188
189    /**
190     * Indicates whether some other object is "equal to" this by comparing all members.
191     * <p>
192     * The {@link #equals(Object)} method returns <code>true</code> if the user JIDs are equal.
193     *
194     * @param obj the reference object with which to compare.
195     * @return <code>true</code> if this object is the same as the obj argument; <code>false</code>
196     *         otherwise.
197     */
198    public boolean equalsDeep(Object obj) {
199        if (this == obj)
200            return true;
201        if (obj == null)
202            return false;
203        if (getClass() != obj.getClass())
204            return false;
205        RosterEntry other = (RosterEntry) obj;
206        if (name == null) {
207            if (other.name != null)
208                return false;
209        }
210        else if (!name.equals(other.name))
211            return false;
212        if (status == null) {
213            if (other.status != null)
214                return false;
215        }
216        else if (!status.equals(other.status))
217            return false;
218        if (type == null) {
219            if (other.type != null)
220                return false;
221        }
222        else if (!type.equals(other.type))
223            return false;
224        if (user == null) {
225            if (other.user != null)
226                return false;
227        }
228        else if (!user.equals(other.user))
229            return false;
230        return true;
231    }
232
233    static RosterPacket.Item toRosterItem(RosterEntry entry) {
234        RosterPacket.Item item = new RosterPacket.Item(entry.getUser(), entry.getName());
235        item.setItemType(entry.getType());
236        item.setItemStatus(entry.getStatus());
237        // Set the correct group names for the item.
238        for (RosterGroup group : entry.getGroups()) {
239            item.addGroupName(group.getName());
240        }
241        return item;
242    }
243
244}