/** * $RCSfile$ * $Revision$ * $Date$ * * Copyright 2003-2004 Jive Software. * * All rights reserved. Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.jivesoftware.smack; import org.jivesoftware.smack.packet.*; import org.jivesoftware.smack.filter.*; import org.jivesoftware.smack.util.StringUtils; import java.util.*; import java.util.concurrent.ConcurrentHashMap; /** * Represents a user's roster, which is the collection of users a person receives * presence updates for. Roster items are categorized into groups for easier management.
* * Others users may attempt to subscribe to this user using a subscription request. Three * modes are supported for handling these requests:
* * If using the manual mode, a PacketListener should be registered that * listens for Presence packets that have a type of * {@link org.jivesoftware.smack.packet.Presence.Type#subscribe}. * * @return the subscription mode. */ public int getSubscriptionMode() { return subscriptionMode; } /** * Sets the subscription processing mode, which dictates what action * Smack will take when subscription requests from other users are made. * The default subscription mode is {@link #SUBSCRIPTION_ACCEPT_ALL}.
* * If using the manual mode, a PacketListener should be registered that * listens for Presence packets that have a type of * {@link org.jivesoftware.smack.packet.Presence.Type#subscribe}. * * @param subscriptionMode the subscription mode. */ public void setSubscriptionMode(int subscriptionMode) { if (subscriptionMode != SUBSCRIPTION_ACCEPT_ALL && subscriptionMode != SUBSCRIPTION_REJECT_ALL && subscriptionMode != SUBSCRIPTION_MANUAL) { throw new IllegalArgumentException("Invalid mode."); } this.subscriptionMode = subscriptionMode; } /** * Reloads the entire roster from the server. This is an asynchronous operation, * which means the method will return immediately, and the roster will be * reloaded at a later point when the server responds to the reload request. */ public void reload() { connection.sendPacket(new RosterPacket()); } /** * Adds a listener to this roster. The listener will be fired anytime one or more * changes to the roster are pushed from the server. * * @param rosterListener a roster listener. */ public void addRosterListener(RosterListener rosterListener) { synchronized (rosterListeners) { if (!rosterListeners.contains(rosterListener)) { rosterListeners.add(rosterListener); } } } /** * Removes a listener from this roster. The listener will be fired anytime one or more * changes to the roster are pushed from the server. * * @param rosterListener a roster listener. */ public void removeRosterListener(RosterListener rosterListener) { synchronized (rosterListeners) { rosterListeners.remove(rosterListener); } } /** * Creates a new group.
*
* Note: you must add at least one entry to the group for the group to be kept
* after a logout/login. This is due to the way that XMPP stores group information.
*
* @param name the name of the group.
* @return a new group.
*/
public RosterGroup createGroup(String name) {
synchronized (groups) {
if (groups.containsKey(name)) {
throw new IllegalArgumentException("Group with name " + name + " alread exists.");
}
RosterGroup group = new RosterGroup(name, connection);
groups.put(name, group);
return group;
}
}
/**
* Creates a new roster entry and presence subscription. The server will asynchronously
* update the roster with the subscription status.
*
* @param user the user. (e.g. johndoe@jabber.org)
* @param name the nickname of the user.
* @param groups the list of group names the entry will belong to, or null if the
* the roster entry won't belong to a group.
*/
public void createEntry(String user, String name, String [] groups) throws XMPPException {
// Create and send roster entry creation packet.
RosterPacket rosterPacket = new RosterPacket();
rosterPacket.setType(IQ.Type.SET);
RosterPacket.Item item = new RosterPacket.Item(user, name);
if (groups != null) {
for (int i=0; i
*
* If the user has several presences (one for each resource) then answer the presence
* with the highest priority.
*
* Note that presence information is received asynchronously. So, just after logging
* in to the server, presence values for users in the roster might be null
* even if they are actually online. In other words, the value returned by this
* method should only be treated as a snapshot in time, and may not accurately reflect
* other user's presence instant by instant. If you need to track presence over time,
* such as when showing a visual representation of the roster, consider using a
* {@link RosterListener}.
*
* @param user a fully qualified xmpp ID. The address could be in any valid format (e.g.
* "domain/resource", "user@domain" or "user@domain/resource").
* @return the user's current presence, or null if the user is unavailable
* or if no presence information is available..
*/
public Presence getPresence(String user) {
String key = getPresenceMapKey(user);
Map userPresences = (Map) presenceMap.get(key);
if (userPresences == null) {
return null;
}
else {
// Find the resource with the highest priority
// Might be changed to use the resource with the highest availability instead.
Iterator it = userPresences.keySet().iterator();
Presence p;
Presence presence = null;
while (it.hasNext()) {
p = (Presence) userPresences.get(it.next());
if (presence == null) {
presence = p;
}
else {
if (p.getPriority() > presence.getPriority()) {
presence = p;
}
}
}
return presence;
}
}
/**
* Returns the presence info for a particular user's resource, or null if the user
* is unavailable (offline) or if no presence information is available, such as
* when you are not subscribed to the user's presence updates.
*
* @param userResource a fully qualified xmpp ID including a resource.
* @return the user's current presence, or null if the user is unavailable
* or if no presence information is available.
*/
public Presence getPresenceResource(String userResource) {
String key = getPresenceMapKey(userResource);
String resource = StringUtils.parseResource(userResource);
Map userPresences = (Map)presenceMap.get(key);
if (userPresences == null) {
return null;
}
else {
return (Presence) userPresences.get(resource);
}
}
/**
* Returns an iterator (of Presence objects) for all the user's current presences
* or null if the user is unavailable (offline) or if no presence information
* is available, such as when you are not subscribed to the user's presence updates.
*
* @param user a fully qualified xmpp ID, e.g. jdoe@example.com
* @return an iterator (of Presence objects) for all the user's current presences,
* or null if the user is unavailable or if no presence information
* is available.
*/
public Iterator getPresences(String user) {
String key = getPresenceMapKey(user);
Map userPresences = (Map)presenceMap.get(key);
if (userPresences == null) {
return null;
}
else {
synchronized (userPresences) {
return new HashMap(userPresences).values().iterator();
}
}
}
/**
* Returns the key to use in the presenceMap for a fully qualified xmpp ID. The roster
* can contain any valid address format such us "domain/resource", "user@domain" or
* "user@domain/resource". If the roster contains an entry associated with the fully qualified
* xmpp ID then use the fully qualified xmpp ID as the key in presenceMap, otherwise use the
* bare address. Note: When the key in presenceMap is a fully qualified xmpp ID, the
* userPresences is useless since it will always contain one entry for the user.
*
* @param user the fully qualified xmpp ID, e.g. jdoe@example.com/Work.
* @return the key to use in the presenceMap for the fully qualified xmpp ID.
*/
private String getPresenceMapKey(String user) {
if (user == null) {
return null;
}
String key = user;
if (!contains(user)) {
key = StringUtils.parseBareAddress(user);
}
return key.toLowerCase();
}
/**
* Fires roster changed event to roster listeners indicating that the
* specified collections of contacts have been added, updated or deleted
* from the roster.
*
* @param addedEntries the collection of address of the added contacts.
* @param updatedEntries the collection of address of the updated contacts.
* @param deletedEntries the collection of address of the deleted contacts.
*/
private void fireRosterChangedEvent(Collection addedEntries, Collection updatedEntries,
Collection deletedEntries) {
RosterListener [] listeners;
synchronized (rosterListeners) {
listeners = new RosterListener[rosterListeners.size()];
rosterListeners.toArray(listeners);
}
for (int i=0; i