![JAR search and dependency download from the Maven repository](/logo.png)
org.jivesoftware.openfire.group.ConcurrentGroupList Maven / Gradle / Ivy
The newest version!
package org.jivesoftware.openfire.group;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArrayList;
import org.xmpp.packet.JID;
/**
* This list specifies additional methods that understand groups among
* the items in the list.
*
* @author Tom Evans
*/
public class ConcurrentGroupList extends CopyOnWriteArrayList implements GroupAwareList {
private static final long serialVersionUID = 7735849143650412115L;
// This set is used to optimize group operations within this list.
// We only populate this set when it's needed to dereference the
// groups in the base list, but once it exists we keep it in sync
// via the various add/remove operations.
// NOTE: added volatile keyword for double-check idiom (lazy instantiation)
private volatile transient Set knownGroupNamesInList;
public ConcurrentGroupList() {
super();
}
public ConcurrentGroupList(Collection extends T> c) {
super(c);
}
/**
* Returns true if the list contains the given JID. If the JID
* is not found in the list, search the list for groups and look
* for the JID in each of the corresponding groups.
*
* @param value The target, presumably a JID
* @return True if the target is in the list, or in any groups in the list
*/
@Override
public boolean includes(Object value) {
boolean found = false;
if (contains(value)) {
found = true;
} else if (value instanceof JID) {
JID target = (JID) value;
Iterator iterator = getGroups().iterator();
while (!found && iterator.hasNext()) {
found = iterator.next().isUser(target);
}
}
return found;
}
/**
* Returns the groups that are implied (resolvable) from the items in the list.
*
* @return A Set containing the groups in the list
*/
@Override
public Set getGroups() {
Set result = new HashSet<>();
for (String groupName : getKnownGroupNamesInList()) {
result.add(Group.resolveFrom(groupName));
}
return result;
}
/**
* Accessor uses the "double-check idiom" (j2se 5.0+) for proper lazy instantiation.
* Additionally, the set is not cached until there is at least one group in the list.
*
* @return the known group names among the items in the list
*/
private Set getKnownGroupNamesInList() {
Set result = knownGroupNamesInList;
if (result == null) {
synchronized(this) {
result = knownGroupNamesInList;
if (result == null) {
result = new HashSet<>();
// add all the groups into the group set
Iterator iterator = iterator();
while (iterator.hasNext()) {
T listItem = iterator.next();
Group group = Group.resolveFrom(listItem);
if (group != null) {
result.add(group.getName());
};
}
knownGroupNamesInList = result.isEmpty() ? null : result;
}
}
}
return result;
}
/**
* This method is called from several of the mutators to keep
* the group set in sync with the full list.
*
* @param item The item to be added or removed if it is in the group set
* @param addOrRemove True to add, false to remove
* @return true if the given item is a group
*/
private synchronized boolean syncGroups(Object item, boolean addOrRemove) {
boolean result = false;
// only sync if the group list has been instantiated
if (knownGroupNamesInList != null) {
Group group = Group.resolveFrom(item);
if (group != null) {
result = true;
if (addOrRemove == ADD) {
knownGroupNamesInList.add(group.getName());
} else if (addOrRemove == REMOVE) {
knownGroupNamesInList.remove(group.getName());
if (knownGroupNamesInList.isEmpty()) {
knownGroupNamesInList = null;
}
}
}
}
return result;
}
// below are overrides for the various mutators
@Override
public T set(int index, T element) {
T result = super.set(index, element);
syncGroups(element, ADD);
return result;
}
@Override
public boolean add(T e) {
boolean result = super.add(e);
syncGroups(e, ADD);
return result;
}
@Override
public void add(int index, T element) {
super.add(index, element);
syncGroups(element, ADD);
}
@Override
public T remove(int index) {
T result = super.remove(index);
syncGroups(result, REMOVE);
return result;
}
@Override
public boolean remove(Object o) {
boolean removed = super.remove(o);
if (removed) {
syncGroups(o, REMOVE);
}
return removed;
}
@Override
public boolean addIfAbsent(T e) {
boolean added = super.addIfAbsent(e);
if (added) {
syncGroups(e, ADD);
}
return added;
}
@Override
public boolean removeAll(Collection> c) {
boolean changed = super.removeAll(c);
if (changed) {
// drop the transient set, will be rebuilt when/if needed
synchronized(this) {
knownGroupNamesInList = null;
}
}
return changed;
}
@Override
public boolean retainAll(Collection> c) {
boolean changed = super.retainAll(c);
if (changed) {
// drop the transient set, will be rebuilt when/if needed
synchronized(this) {
knownGroupNamesInList = null;
}
}
return changed;
}
@Override
public int addAllAbsent(Collection extends T> c) {
int added = super.addAllAbsent(c);
if (added > 0) {
// drop the transient set, will be rebuilt when/if needed
synchronized(this) {
knownGroupNamesInList = null;
}
}
return added;
}
@Override
public void clear() {
super.clear();
synchronized(this) {
knownGroupNamesInList = null;
}
}
@Override
public boolean addAll(Collection extends T> c) {
boolean changed = super.addAll(c);
if (changed) {
// drop the transient set, will be rebuilt when/if needed
synchronized(this) {
knownGroupNamesInList = null;
}
}
return changed;
}
@Override
public boolean addAll(int index, Collection extends T> c) {
boolean changed = super.addAll(index, c);
if (changed) {
// drop the transient set, will be rebuilt when/if needed
synchronized(this) {
knownGroupNamesInList = null;
}
}
return changed;
}
private static final boolean ADD = true;
private static final boolean REMOVE = false;
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy