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.LinkedHashSet; 022import java.util.List; 023import java.util.Set; 024 025import org.jivesoftware.smack.Manager; 026import org.jivesoftware.smack.SmackException.NoResponseException; 027import org.jivesoftware.smack.SmackException.NotConnectedException; 028import org.jivesoftware.smack.XMPPConnection; 029import org.jivesoftware.smack.XMPPException.XMPPErrorException; 030import org.jivesoftware.smack.packet.IQ; 031import org.jivesoftware.smack.roster.packet.RosterPacket; 032 033import org.jxmpp.jid.Jid; 034 035/** 036 * A group of roster entries. 037 * 038 * @see Roster#getGroup(String) 039 * @author Matt Tucker 040 */ 041public class RosterGroup extends Manager { 042 043 private final String name; 044 private final Set<RosterEntry> entries; 045 046 /** 047 * Creates a new roster group instance. 048 * 049 * @param name the name of the group. 050 * @param connection the connection the group belongs to. 051 */ 052 RosterGroup(String name, XMPPConnection connection) { 053 super(connection); 054 this.name = name; 055 entries = new LinkedHashSet<>(); 056 } 057 058 /** 059 * Returns the name of the group. 060 * 061 * @return the name of the group. 062 */ 063 public String getName() { 064 return name; 065 } 066 067 /** 068 * Sets the name of the group. Changing the group's name is like moving all the group entries 069 * of the group to a new group specified by the new name. Since this group won't have entries 070 * it will be removed from the roster. This means that all the references to this object will 071 * be invalid and will need to be updated to the new group specified by the new name. 072 * 073 * @param name the name of the group. 074 * @throws NotConnectedException if the XMPP connection is not connected. 075 * @throws XMPPErrorException if there was an XMPP error returned. 076 * @throws NoResponseException if there was no response from the remote entity. 077 * @throws InterruptedException if the calling thread was interrupted. 078 */ 079 public void setName(String name) throws NotConnectedException, NoResponseException, XMPPErrorException, InterruptedException { 080 synchronized (entries) { 081 for (RosterEntry entry : entries) { 082 RosterPacket packet = new RosterPacket(); 083 packet.setType(IQ.Type.set); 084 RosterPacket.Item item = RosterEntry.toRosterItem(entry); 085 item.removeGroupName(this.name); 086 item.addGroupName(name); 087 packet.addRosterItem(item); 088 connection().sendIqRequestAndWaitForResponse(packet); 089 } 090 } 091 } 092 093 /** 094 * Returns the number of entries in the group. 095 * 096 * @return the number of entries in the group. 097 */ 098 public int getEntryCount() { 099 synchronized (entries) { 100 return entries.size(); 101 } 102 } 103 104 /** 105 * Returns an copied list of all entries in the group. 106 * 107 * @return all entries in the group. 108 */ 109 public List<RosterEntry> getEntries() { 110 synchronized (entries) { 111 return new ArrayList<>(entries); 112 } 113 } 114 115 /** 116 * Returns the roster entry associated with the given XMPP address or 117 * <code>null</code> if the user is not an entry in the group. 118 * 119 * @param user the XMPP address of the user (eg "jsmith@example.com"). 120 * @return the roster entry or <code>null</code> if it does not exist in the group. 121 */ 122 public RosterEntry getEntry(Jid user) { 123 if (user == null) { 124 return null; 125 } 126 // Roster entries never include a resource so remove the resource 127 // if it's a part of the XMPP address. 128 user = user.asBareJid(); 129 synchronized (entries) { 130 for (RosterEntry entry : entries) { 131 if (entry.getJid().equals(user)) { 132 return entry; 133 } 134 } 135 } 136 return null; 137 } 138 139 /** 140 * Returns true if the specified entry is part of this group. 141 * 142 * @param entry a roster entry. 143 * @return true if the entry is part of this group. 144 */ 145 public boolean contains(RosterEntry entry) { 146 synchronized (entries) { 147 return entries.contains(entry); 148 } 149 } 150 151 /** 152 * Returns true if the specified XMPP address is an entry in this group. 153 * 154 * @param user the XMPP address of the user. 155 * @return true if the XMPP address is an entry in this group. 156 */ 157 public boolean contains(Jid user) { 158 return getEntry(user) != null; 159 } 160 161 /** 162 * Adds a roster entry to this group. If the entry was unfiled then it will be removed from 163 * the unfiled list and will be added to this group. 164 * Note that this is a synchronous call -- Smack must wait for the server 165 * to receive the updated roster. 166 * 167 * @param entry a roster entry. 168 * @throws XMPPErrorException if an error occurred while trying to add the entry to the group. 169 * @throws NoResponseException if there was no response from the server. 170 * @throws NotConnectedException if the XMPP connection is not connected. 171 * @throws InterruptedException if the calling thread was interrupted. 172 */ 173 public void addEntry(RosterEntry entry) throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException { 174 // Only add the entry if it isn't already in the list. 175 synchronized (entries) { 176 if (!entries.contains(entry)) { 177 RosterPacket packet = new RosterPacket(); 178 packet.setType(IQ.Type.set); 179 RosterPacket.Item item = RosterEntry.toRosterItem(entry); 180 item.addGroupName(getName()); 181 packet.addRosterItem(item); 182 // Wait up to a certain number of seconds for a reply from the server. 183 connection().sendIqRequestAndWaitForResponse(packet); 184 } 185 } 186 } 187 188 /** 189 * Removes a roster entry from this group. If the entry does not belong to any other group 190 * then it will be considered as unfiled, therefore it will be added to the list of unfiled 191 * entries. 192 * Note that this is a synchronous call -- Smack must wait for the server 193 * to receive the updated roster. 194 * 195 * @param entry a roster entry. 196 * @throws XMPPErrorException if an error occurred while trying to remove the entry from the group. 197 * @throws NoResponseException if there was no response from the server. 198 * @throws NotConnectedException if the XMPP connection is not connected. 199 * @throws InterruptedException if the calling thread was interrupted. 200 */ 201 public void removeEntry(RosterEntry entry) throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException { 202 // Only remove the entry if it's in the entry list. 203 // Remove the entry locally, if we wait for RosterPacketListenerProcess>>Packet(Packet) 204 // to take place the entry will exist in the group until a packet is received from the 205 // server. 206 synchronized (entries) { 207 if (entries.contains(entry)) { 208 RosterPacket packet = new RosterPacket(); 209 packet.setType(IQ.Type.set); 210 RosterPacket.Item item = RosterEntry.toRosterItem(entry); 211 item.removeGroupName(this.getName()); 212 packet.addRosterItem(item); 213 // Wait up to a certain number of seconds for a reply from the server. 214 connection().sendIqRequestAndWaitForResponse(packet); 215 } 216 } 217 } 218 219 void addEntryLocal(RosterEntry entry) { 220 // Update the entry if it is already in the list 221 synchronized (entries) { 222 entries.remove(entry); 223 entries.add(entry); 224 } 225 } 226 227 void removeEntryLocal(RosterEntry entry) { 228 // Only remove the entry if it's in the entry list. 229 synchronized (entries) { 230 if (entries.contains(entry)) { 231 entries.remove(entry); 232 } 233 } 234 } 235}