mirror of https://github.com/sipwise/jitsi.git
Conflicts: lib/installer-exclude/libjitsi.jarmaven
commit
100b6b8876
Binary file not shown.
@ -0,0 +1,564 @@
|
||||
/*
|
||||
* Jitsi, the OpenSource Java VoIP and Instant Messaging client.
|
||||
*
|
||||
* Distributable under LGPL license.
|
||||
* See terms of license at gnu.org.
|
||||
*/
|
||||
package net.java.sip.communicator.impl.protocol.irc;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.concurrent.atomic.*;
|
||||
|
||||
import net.java.sip.communicator.util.*;
|
||||
|
||||
import com.ircclouds.irc.api.*;
|
||||
import com.ircclouds.irc.api.domain.*;
|
||||
import com.ircclouds.irc.api.domain.messages.*;
|
||||
import com.ircclouds.irc.api.state.*;
|
||||
|
||||
class BasicPollerPresenceWatcher
|
||||
implements PresenceWatcher
|
||||
{
|
||||
/**
|
||||
* Logger.
|
||||
*/
|
||||
private static final Logger LOGGER = Logger
|
||||
.getLogger(BasicPollerPresenceWatcher.class);
|
||||
|
||||
/**
|
||||
* Delay before starting the presence watcher task for the first time.
|
||||
*/
|
||||
private static final long INITIAL_PRESENCE_WATCHER_DELAY = 10000L;
|
||||
|
||||
/**
|
||||
* Period for the presence watcher timer.
|
||||
*/
|
||||
private static final long PRESENCE_WATCHER_PERIOD = 60000L;
|
||||
|
||||
/**
|
||||
* Instance of IRCAPi.
|
||||
*/
|
||||
private final IRCApi irc;
|
||||
|
||||
/**
|
||||
* Connection state instance.
|
||||
*/
|
||||
private final IIRCState connectionState;
|
||||
|
||||
/**
|
||||
* The persistent presence operation set used to issue presence updates.
|
||||
*/
|
||||
private final OperationSetPersistentPresenceIrcImpl operationSet;
|
||||
|
||||
/**
|
||||
* Synchronized set of nicks to watch for presence changes.
|
||||
*/
|
||||
private final Set<String> nickWatchList;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param irc the IRCApi instance
|
||||
* @param connectionState the connection state
|
||||
* @param operationSet the persistent presence operation set
|
||||
* @param nickWatchList SYNCHRONIZED the nick watch list
|
||||
* @param serverIdentity the server identity
|
||||
*/
|
||||
BasicPollerPresenceWatcher(final IRCApi irc,
|
||||
final IIRCState connectionState,
|
||||
final OperationSetPersistentPresenceIrcImpl operationSet,
|
||||
final Set<String> nickWatchList,
|
||||
final AtomicReference<String> serverIdentity)
|
||||
{
|
||||
if (irc == null)
|
||||
{
|
||||
throw new IllegalArgumentException("irc cannot be null");
|
||||
}
|
||||
this.irc = irc;
|
||||
if (connectionState == null)
|
||||
{
|
||||
throw new IllegalArgumentException("connectionState cannot be null");
|
||||
}
|
||||
this.connectionState = connectionState;
|
||||
if (operationSet == null)
|
||||
{
|
||||
throw new IllegalArgumentException("operationSet cannot be null");
|
||||
}
|
||||
this.operationSet = operationSet;
|
||||
if (nickWatchList == null)
|
||||
{
|
||||
throw new IllegalArgumentException("nickWatchList cannot be null");
|
||||
}
|
||||
this.nickWatchList = nickWatchList;
|
||||
setUpPresenceWatcher(serverIdentity);
|
||||
LOGGER.debug("Basic Poller presence watcher initialized.");
|
||||
}
|
||||
|
||||
/**
|
||||
* Set up a timer for watching the presence of nicks in the watch list.
|
||||
*/
|
||||
private void setUpPresenceWatcher(
|
||||
final AtomicReference<String> serverIdentity)
|
||||
{
|
||||
// FIFO query list to be shared between presence watcher task and
|
||||
// presence reply listener.
|
||||
final List<List<String>> queryList =
|
||||
Collections.synchronizedList(new LinkedList<List<String>>());
|
||||
final Timer presenceWatcher = new Timer();
|
||||
irc.addListener(new PresenceReplyListener(presenceWatcher, queryList));
|
||||
final PresenceWatcherTask task =
|
||||
new PresenceWatcherTask(this.nickWatchList, queryList,
|
||||
serverIdentity);
|
||||
presenceWatcher.schedule(task, INITIAL_PRESENCE_WATCHER_DELAY,
|
||||
PRESENCE_WATCHER_PERIOD);
|
||||
LOGGER.trace("Basic Poller presence watcher set up.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void add(String nick)
|
||||
{
|
||||
this.nickWatchList.add(nick);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove(String nick)
|
||||
{
|
||||
this.nickWatchList.remove(nick);
|
||||
}
|
||||
|
||||
/**
|
||||
* Task for watching nick presence.
|
||||
*
|
||||
* @author Danny van Heumen
|
||||
*/
|
||||
private final class PresenceWatcherTask extends TimerTask
|
||||
{
|
||||
/**
|
||||
* Static overhead for ISON response message.
|
||||
*
|
||||
* Additional 10 chars extra overhead as fail-safe, as I was not able to
|
||||
* find the exact number in the overhead computation.
|
||||
*/
|
||||
private static final int ISON_RESPONSE_STATIC_MESSAGE_OVERHEAD = 18;
|
||||
|
||||
/**
|
||||
* Set containing nicks that must be watched.
|
||||
*/
|
||||
private final Set<String> watchList;
|
||||
|
||||
/**
|
||||
* FIFO list storing each ISON query that is sent, for use when
|
||||
* responses return.
|
||||
*/
|
||||
private final List<List<String>> queryList;
|
||||
|
||||
/**
|
||||
* Reference to the current server identity.
|
||||
*/
|
||||
private final AtomicReference<String> serverIdentity;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param watchList the list of nicks to watch
|
||||
* @param queryList list containing list of nicks of each ISON query
|
||||
* @param serverIdentity container with the current server identity for
|
||||
* use in overhead calculation
|
||||
*/
|
||||
public PresenceWatcherTask(final Set<String> watchList,
|
||||
final List<List<String>> queryList,
|
||||
final AtomicReference<String> serverIdentity)
|
||||
{
|
||||
if (watchList == null)
|
||||
{
|
||||
throw new IllegalArgumentException("watchList cannot be null");
|
||||
}
|
||||
this.watchList = watchList;
|
||||
if (queryList == null)
|
||||
{
|
||||
throw new IllegalArgumentException("queryList cannot be null");
|
||||
}
|
||||
this.queryList = queryList;
|
||||
if (serverIdentity == null)
|
||||
{
|
||||
throw new IllegalArgumentException(
|
||||
"serverIdentity reference cannot be null");
|
||||
}
|
||||
this.serverIdentity = serverIdentity;
|
||||
}
|
||||
|
||||
/**
|
||||
* The implementation of the task.
|
||||
*/
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
if (this.watchList.isEmpty())
|
||||
{
|
||||
LOGGER.trace("Watch list is empty. Not querying for online "
|
||||
+ "presence.");
|
||||
return;
|
||||
}
|
||||
if (this.serverIdentity.get() == null)
|
||||
{
|
||||
LOGGER.trace("Server identity not available yet. Skipping "
|
||||
+ "this presence status query.");
|
||||
return;
|
||||
}
|
||||
LOGGER
|
||||
.trace("Watch list contains nicks: querying presence status.");
|
||||
final StringBuilder query = new StringBuilder();
|
||||
final LinkedList<String> list;
|
||||
synchronized (this.watchList)
|
||||
{
|
||||
list = new LinkedList<String>(this.watchList);
|
||||
}
|
||||
LinkedList<String> nicks = new LinkedList<String>();
|
||||
// The ISON reply contains the most overhead, so base the maximum
|
||||
// number of nicks limit on that.
|
||||
final int maxQueryLength =
|
||||
MessageManager.IRC_PROTOCOL_MAXIMUM_MESSAGE_SIZE - overhead();
|
||||
for (String nick : list)
|
||||
{
|
||||
if (query.length() + nick.length() >= maxQueryLength)
|
||||
{
|
||||
this.queryList.add(nicks);
|
||||
BasicPollerPresenceWatcher.this.irc
|
||||
.rawMessage(createQuery(query));
|
||||
// Initialize new data types
|
||||
query.delete(0, query.length());
|
||||
nicks = new LinkedList<String>();
|
||||
}
|
||||
query.append(nick);
|
||||
query.append(' ');
|
||||
nicks.add(nick);
|
||||
}
|
||||
if (query.length() > 0)
|
||||
{
|
||||
// Send remaining entries.
|
||||
this.queryList.add(nicks);
|
||||
BasicPollerPresenceWatcher.this.irc
|
||||
.rawMessage(createQuery(query));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an ISON query from the StringBuilder containing the list of
|
||||
* nicks.
|
||||
*
|
||||
* @param nicklist the list of nicks as a StringBuilder instance
|
||||
* @return returns the ISON query string
|
||||
*/
|
||||
private String createQuery(final StringBuilder nicklist)
|
||||
{
|
||||
return "ISON " + nicklist;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate overhead for ISON response message.
|
||||
*
|
||||
* @return returns amount of overhead in response message
|
||||
*/
|
||||
private int overhead()
|
||||
{
|
||||
return ISON_RESPONSE_STATIC_MESSAGE_OVERHEAD
|
||||
+ this.serverIdentity.get().length()
|
||||
+ BasicPollerPresenceWatcher.this.connectionState.getNickname()
|
||||
.length();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Presence reply listener.
|
||||
*
|
||||
* Listener that acts on various replies that give an indication of actual
|
||||
* presence or presence changes, such as RPL_ISON and ERR_NOSUCHNICKCHAN.
|
||||
*
|
||||
* @author Danny van Heumen
|
||||
*/
|
||||
private final class PresenceReplyListener
|
||||
extends AbstractIrcMessageListener
|
||||
{
|
||||
/**
|
||||
* Reply for ISON query.
|
||||
*/
|
||||
private static final int RPL_ISON = 303;
|
||||
|
||||
/**
|
||||
* Error reply in case nick does not exist on server.
|
||||
*/
|
||||
private static final int ERR_NOSUCHNICK = 401;
|
||||
|
||||
/**
|
||||
* Timer for presence watcher task.
|
||||
*/
|
||||
private final Timer timer;
|
||||
|
||||
/**
|
||||
* FIFO list containing list of nicks for each query.
|
||||
*/
|
||||
private final List<List<String>> queryList;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param timer Timer for presence watcher task
|
||||
* @param queryList List of executed queries with expected nicks lists.
|
||||
*/
|
||||
public PresenceReplyListener(final Timer timer,
|
||||
final List<List<String>> queryList)
|
||||
{
|
||||
super(BasicPollerPresenceWatcher.this.irc,
|
||||
BasicPollerPresenceWatcher.this.connectionState);
|
||||
if (timer == null)
|
||||
{
|
||||
throw new IllegalArgumentException("timer cannot be null");
|
||||
}
|
||||
this.timer = timer;
|
||||
if (queryList == null)
|
||||
{
|
||||
throw new IllegalArgumentException("queryList cannot be null");
|
||||
}
|
||||
this.queryList = queryList;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update nick watch list upon receiving a nick change message for a
|
||||
* nick that is on the watch list.
|
||||
*
|
||||
* NOTE: This nick change event could be handled earlier than the
|
||||
* handler that fires the contact rename event. This will result in a
|
||||
* missed presence update. However, since the nick change was just
|
||||
* announced, it is reasonable to assume that the user is still online.
|
||||
*
|
||||
* @param msg the nick message
|
||||
*/
|
||||
@Override
|
||||
public void onNickChange(final NickMessage msg)
|
||||
{
|
||||
if (msg == null || msg.getSource() == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
final String oldNick = msg.getSource().getNick();
|
||||
final String newNick = msg.getNewNick();
|
||||
if (oldNick == null || newNick == null)
|
||||
{
|
||||
LOGGER.error("Incomplete nick change message. Old nick: '"
|
||||
+ oldNick + "', new nick: '" + newNick + "'.");
|
||||
return;
|
||||
}
|
||||
synchronized (BasicPollerPresenceWatcher.this.nickWatchList)
|
||||
{
|
||||
if (BasicPollerPresenceWatcher.this.nickWatchList
|
||||
.contains(oldNick))
|
||||
{
|
||||
update(oldNick, IrcStatusEnum.OFFLINE);
|
||||
}
|
||||
if (BasicPollerPresenceWatcher.this.nickWatchList
|
||||
.contains(newNick))
|
||||
{
|
||||
update(newNick, IrcStatusEnum.ONLINE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Message handling for RPL_ISON message and other indicators.
|
||||
*
|
||||
* @param msg the message
|
||||
*/
|
||||
@Override
|
||||
public void onServerNumericMessage(final ServerNumericMessage msg)
|
||||
{
|
||||
if (msg == null || msg.getNumericCode() == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
switch (msg.getNumericCode())
|
||||
{
|
||||
case RPL_ISON:
|
||||
final String[] nicks = msg.getText().substring(1).split(" ");
|
||||
final List<String> offline;
|
||||
if (this.queryList.isEmpty())
|
||||
{
|
||||
// If no query list exists, we can only update nicks that
|
||||
// are online, since we do not know who we have actually
|
||||
// queried for.
|
||||
offline = new LinkedList<String>();
|
||||
}
|
||||
else
|
||||
{
|
||||
offline = this.queryList.remove(0);
|
||||
}
|
||||
for (String nick : nicks)
|
||||
{
|
||||
update(nick, IrcStatusEnum.ONLINE);
|
||||
offline.remove(nick);
|
||||
}
|
||||
for (String nick : offline)
|
||||
{
|
||||
update(nick, IrcStatusEnum.OFFLINE);
|
||||
}
|
||||
break;
|
||||
case ERR_NOSUCHNICK:
|
||||
final String errortext = msg.getText();
|
||||
final int idx = errortext.indexOf(' ');
|
||||
if (idx == -1)
|
||||
{
|
||||
LOGGER.info("ERR_NOSUCHNICK message does not have "
|
||||
+ "expected format.");
|
||||
return;
|
||||
}
|
||||
final String errNick = errortext.substring(0, idx);
|
||||
update(errNick, IrcStatusEnum.OFFLINE);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Upon receiving a private message from a user, conclude that the user
|
||||
* must then be online and update its presence status.
|
||||
*
|
||||
* @param msg the message
|
||||
*/
|
||||
@Override
|
||||
public void onUserPrivMessage(final UserPrivMsg msg)
|
||||
{
|
||||
if (msg == null || msg.getSource() == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
final IRCUser user = msg.getSource();
|
||||
update(user.getNick(), IrcStatusEnum.ONLINE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Upon receiving a notice from a user, conclude that the user
|
||||
* must then be online and update its presence status.
|
||||
*
|
||||
* @param msg the message
|
||||
*/
|
||||
@Override
|
||||
public void onUserNotice(final UserNotice msg)
|
||||
{
|
||||
if (msg == null || msg.getSource() == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
final IRCUser user = msg.getSource();
|
||||
update(user.getNick(), IrcStatusEnum.ONLINE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Upon receiving an action from a user, conclude that the user
|
||||
* must then be online and update its presence status.
|
||||
*
|
||||
* @param msg the message
|
||||
*/
|
||||
@Override
|
||||
public void onUserAction(final UserActionMsg msg)
|
||||
{
|
||||
if (msg == null || msg.getSource() == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
final IRCUser user = msg.getSource();
|
||||
update(user.getNick(), IrcStatusEnum.ONLINE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handler for channel join events.
|
||||
*/
|
||||
@Override
|
||||
public void onChannelJoin(final ChanJoinMessage msg)
|
||||
{
|
||||
if (msg == null || msg.getSource() == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
final String user = msg.getSource().getNick();
|
||||
update(user, IrcStatusEnum.ONLINE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handler for user quit events.
|
||||
*
|
||||
* @param msg the quit message
|
||||
*/
|
||||
@Override
|
||||
public void onUserQuit(final QuitMessage msg)
|
||||
{
|
||||
super.onUserQuit(msg);
|
||||
final String user = msg.getSource().getNick();
|
||||
if (user == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (localUser(user))
|
||||
{
|
||||
// Stop presence watcher task.
|
||||
this.timer.cancel();
|
||||
updateAll(IrcStatusEnum.OFFLINE);
|
||||
}
|
||||
else
|
||||
{
|
||||
update(user, IrcStatusEnum.OFFLINE);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* In case a fatal error occurs, remove the listener.
|
||||
*
|
||||
* @param msg the error message
|
||||
*/
|
||||
@Override
|
||||
public void onError(final ErrorMessage msg)
|
||||
{
|
||||
super.onError(msg);
|
||||
// Stop presence watcher task.
|
||||
this.timer.cancel();
|
||||
updateAll(IrcStatusEnum.OFFLINE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the status of a single nick.
|
||||
*
|
||||
* @param nick the nick to update
|
||||
* @param status the new status
|
||||
*/
|
||||
private void update(final String nick, final IrcStatusEnum status)
|
||||
{
|
||||
// User is some other user, so check if we are watching that nick.
|
||||
if (!BasicPollerPresenceWatcher.this.nickWatchList.contains(nick))
|
||||
{
|
||||
return;
|
||||
}
|
||||
BasicPollerPresenceWatcher.this.operationSet
|
||||
.updateNickContactPresence(nick, status);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the status of all contacts in the nick watch list.
|
||||
*
|
||||
* @param status the new status
|
||||
*/
|
||||
private void updateAll(final IrcStatusEnum status)
|
||||
{
|
||||
final LinkedList<String> list;
|
||||
synchronized (BasicPollerPresenceWatcher.this.nickWatchList)
|
||||
{
|
||||
list =
|
||||
new LinkedList<String>(
|
||||
BasicPollerPresenceWatcher.this.nickWatchList);
|
||||
}
|
||||
for (String nick : list)
|
||||
{
|
||||
BasicPollerPresenceWatcher.this.operationSet
|
||||
.updateNickContactPresence(nick, status);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,345 @@
|
||||
/*
|
||||
* Jitsi, the OpenSource Java VoIP and Instant Messaging client.
|
||||
*
|
||||
* Distributable under LGPL license.
|
||||
* See terms of license at gnu.org.
|
||||
*/
|
||||
package net.java.sip.communicator.impl.protocol.irc;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import net.java.sip.communicator.util.*;
|
||||
|
||||
import com.ircclouds.irc.api.*;
|
||||
import com.ircclouds.irc.api.domain.messages.*;
|
||||
import com.ircclouds.irc.api.state.*;
|
||||
|
||||
/**
|
||||
* MONITOR presence watcher.
|
||||
*
|
||||
* @author Danny van Heumen
|
||||
*/
|
||||
class MonitorPresenceWatcher
|
||||
implements PresenceWatcher
|
||||
{
|
||||
/**
|
||||
* Static overhead in message payload required for 'MONITOR +' command.
|
||||
*/
|
||||
private static final int MONITOR_ADD_CMD_STATIC_OVERHEAD = 10;
|
||||
|
||||
/**
|
||||
* Logger.
|
||||
*/
|
||||
private static final Logger LOGGER = Logger
|
||||
.getLogger(MonitorPresenceWatcher.class);
|
||||
|
||||
/**
|
||||
* IRCApi instance.
|
||||
*/
|
||||
private final IRCApi irc;
|
||||
|
||||
/**
|
||||
* IRC connection state.
|
||||
*/
|
||||
private final IIRCState connectionState;
|
||||
|
||||
/**
|
||||
* Complete nick watch list.
|
||||
*/
|
||||
private final Set<String> nickWatchList;
|
||||
|
||||
/**
|
||||
* List of monitored nicks.
|
||||
*
|
||||
* The instance is stored here only for access in order to remove a nick
|
||||
* from the list, since 'MONITOR - ' command does not reply so we cannot
|
||||
* manage list removals from the reply listener.
|
||||
*/
|
||||
private final Set<String> monitoredList;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param irc the IRCApi instance
|
||||
* @param connectionState the connection state
|
||||
* @param nickWatchList SYNCHRONIZED the nick watch list
|
||||
* @param monitored SYNCHRONIZED The shared collection which contains all
|
||||
* the nicks that are confirmed to be subscribed to the MONITOR
|
||||
* command.
|
||||
* @param operationSet the persistent presence operation set
|
||||
*/
|
||||
MonitorPresenceWatcher(final IRCApi irc, final IIRCState connectionState,
|
||||
final Set<String> nickWatchList, final Set<String> monitored,
|
||||
final OperationSetPersistentPresenceIrcImpl operationSet,
|
||||
final int maxListSize)
|
||||
{
|
||||
if (irc == null)
|
||||
{
|
||||
throw new IllegalArgumentException("irc cannot be null");
|
||||
}
|
||||
this.irc = irc;
|
||||
if (connectionState == null)
|
||||
{
|
||||
throw new IllegalArgumentException(
|
||||
"connectionState cannot be null");
|
||||
}
|
||||
this.connectionState = connectionState;
|
||||
if (nickWatchList == null)
|
||||
{
|
||||
throw new IllegalArgumentException("nickWatchList cannot be null");
|
||||
}
|
||||
this.nickWatchList = nickWatchList;
|
||||
if (monitored == null)
|
||||
{
|
||||
throw new IllegalArgumentException("monitored cannot be null");
|
||||
}
|
||||
this.monitoredList = monitored;
|
||||
this.irc.addListener(new MonitorReplyListener(this.monitoredList,
|
||||
operationSet));
|
||||
setUpMonitor(this.irc, this.nickWatchList, maxListSize);
|
||||
LOGGER.debug("MONITOR presence watcher initialized.");
|
||||
}
|
||||
|
||||
/**
|
||||
* Set up monitor based on the nick watch list in its current state.
|
||||
*
|
||||
* Created a static method as not to interfere too much with a state that is
|
||||
* still being initialized.
|
||||
*/
|
||||
private static void setUpMonitor(final IRCApi irc,
|
||||
final Collection<String> nickWatchList, final int maxListSize)
|
||||
{
|
||||
List<String> current;
|
||||
synchronized (nickWatchList)
|
||||
{
|
||||
current = new LinkedList<String>(nickWatchList);
|
||||
}
|
||||
if (current.size() > maxListSize)
|
||||
{
|
||||
// cut off list to maximum number of entries allowed by server
|
||||
current = current.subList(0, maxListSize);
|
||||
}
|
||||
final int maxLength = 510 - MONITOR_ADD_CMD_STATIC_OVERHEAD;
|
||||
final StringBuilder query = new StringBuilder();
|
||||
for (String nick : current)
|
||||
{
|
||||
if (query.length() + nick.length() + 1 > maxLength)
|
||||
{
|
||||
// full payload, send monitor query now
|
||||
irc.rawMessage("MONITOR + " + query);
|
||||
query.delete(0, query.length());
|
||||
}
|
||||
else if (query.length() > 0)
|
||||
{
|
||||
query.append(",");
|
||||
}
|
||||
query.append(nick);
|
||||
}
|
||||
if (query.length() > 0)
|
||||
{
|
||||
// send query for remaining nicks
|
||||
irc.rawMessage("MONITOR + " + query);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void add(final String nick)
|
||||
{
|
||||
LOGGER.trace("Adding nick '" + nick + "' to MONITOR watch list.");
|
||||
this.nickWatchList.add(nick);
|
||||
this.irc.rawMessage("MONITOR + " + nick);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove(final String nick)
|
||||
{
|
||||
LOGGER.trace("Removing nick '" + nick + "' from MONITOR watch list.");
|
||||
this.nickWatchList.remove(nick);
|
||||
this.irc.rawMessage("MONITOR - " + nick);
|
||||
// 'MONITOR - nick' command does not send confirmation, so immediately
|
||||
// remove nick from monitored list.
|
||||
this.monitoredList.remove(nick);
|
||||
}
|
||||
|
||||
/**
|
||||
* Listener for MONITOR replies.
|
||||
*
|
||||
* @author Danny van Heumen
|
||||
*/
|
||||
private final class MonitorReplyListener
|
||||
extends AbstractIrcMessageListener
|
||||
{
|
||||
/**
|
||||
* Numeric message id for ONLINE nick response.
|
||||
*/
|
||||
private static final int IRC_RPL_MONONLINE = 730;
|
||||
|
||||
/**
|
||||
* Numeric message id for OFFLINE nick response.
|
||||
*/
|
||||
private static final int IRC_RPL_MONOFFLINE = 731;
|
||||
|
||||
// /**
|
||||
// * Numeric message id for MONLIST entry.
|
||||
// */
|
||||
// private static final int IRC_RPL_MONLIST = 732;
|
||||
//
|
||||
// /**
|
||||
// * Numeric message id for ENDOFMONLIST.
|
||||
// */
|
||||
// private static final int IRC_RPL_ENDOFMONLIST = 733;
|
||||
|
||||
/**
|
||||
* Error message signaling full list. Nick list provided are all nicks
|
||||
* that failed to be added to the monitor list.
|
||||
*/
|
||||
private static final int IRC_ERR_MONLISTFULL = 734;
|
||||
|
||||
/**
|
||||
* Operation set persistent presence instance.
|
||||
*/
|
||||
private final OperationSetPersistentPresenceIrcImpl operationSet;
|
||||
|
||||
/**
|
||||
* Set of nicks that are confirmed to be monitored by the server.
|
||||
*/
|
||||
private final Set<String> monitoredNickList;
|
||||
|
||||
// TODO Update to act on onClientError once available.
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param monitored SYNCHRONIZED Collection of monitored nicks. This
|
||||
* collection will be updated with all nicks that are
|
||||
* confirmed to be subscribed by the MONITOR command.
|
||||
* @param operationSet the persistent presence opset used to update nick
|
||||
* presence statuses.
|
||||
*/
|
||||
public MonitorReplyListener(final Set<String> monitored,
|
||||
final OperationSetPersistentPresenceIrcImpl operationSet)
|
||||
{
|
||||
super(MonitorPresenceWatcher.this.irc,
|
||||
MonitorPresenceWatcher.this.connectionState);
|
||||
if (operationSet == null)
|
||||
{
|
||||
throw new IllegalArgumentException(
|
||||
"operationSet cannot be null");
|
||||
}
|
||||
this.operationSet = operationSet;
|
||||
if (monitored == null)
|
||||
{
|
||||
throw new IllegalArgumentException("monitored cannot be null");
|
||||
}
|
||||
this.monitoredNickList = monitored;
|
||||
}
|
||||
|
||||
/**
|
||||
* Numeric messages received in response to MONITOR commands or presence
|
||||
* updates.
|
||||
*/
|
||||
@Override
|
||||
public void onServerNumericMessage(final ServerNumericMessage msg)
|
||||
{
|
||||
final List<String> acknowledged;
|
||||
switch (msg.getNumericCode())
|
||||
{
|
||||
case IRC_RPL_MONONLINE:
|
||||
acknowledged = parseMonitorResponse(msg.getText());
|
||||
for (String nick : acknowledged)
|
||||
{
|
||||
update(nick, IrcStatusEnum.ONLINE);
|
||||
}
|
||||
monitoredNickList.addAll(acknowledged);
|
||||
break;
|
||||
case IRC_RPL_MONOFFLINE:
|
||||
acknowledged = parseMonitorResponse(msg.getText());
|
||||
for (String nick : acknowledged)
|
||||
{
|
||||
update(nick, IrcStatusEnum.OFFLINE);
|
||||
}
|
||||
monitoredNickList.addAll(acknowledged);
|
||||
break;
|
||||
case IRC_ERR_MONLISTFULL:
|
||||
LOGGER.debug("MONITOR list full. Nick was not added. "
|
||||
+ "Fall back Basic Poller will be used if it is enabled. ("
|
||||
+ msg.getText() + ")");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update all monitored nicks upon receiving a server-side QUIT message
|
||||
* for local user.
|
||||
*/
|
||||
@Override
|
||||
public void onUserQuit(QuitMessage msg)
|
||||
{
|
||||
super.onUserQuit(msg);
|
||||
if (localUser(msg.getSource().getNick())) {
|
||||
updateAll(IrcStatusEnum.OFFLINE);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update all monitored nicks upon receiving a server-side ERROR
|
||||
* response.
|
||||
*/
|
||||
@Override
|
||||
public void onError(ErrorMessage msg)
|
||||
{
|
||||
super.onError(msg);
|
||||
updateAll(IrcStatusEnum.OFFLINE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse response messages.
|
||||
*
|
||||
* @param message the message
|
||||
* @return Returns the list of targets extracted.
|
||||
*/
|
||||
private List<String> parseMonitorResponse(final String message)
|
||||
{
|
||||
// Note: this should support both targets consisting of only a nick,
|
||||
// and targets consisting of nick!ident@host formats. (And probably
|
||||
// any variation on this that is typically allowed in IRC.)
|
||||
final LinkedList<String> acknowledged = new LinkedList<String>();
|
||||
final String[] targets = message.substring(1).split(",");
|
||||
for (String target : targets)
|
||||
{
|
||||
String[] parts = target.trim().split("!");
|
||||
acknowledged.add(parts[0]);
|
||||
}
|
||||
return acknowledged;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update all monitored nicks to specified status.
|
||||
*
|
||||
* @param status the desired status
|
||||
*/
|
||||
private void updateAll(final IrcStatusEnum status)
|
||||
{
|
||||
final LinkedList<String> nicks;
|
||||
synchronized (monitoredNickList)
|
||||
{
|
||||
nicks = new LinkedList<String>(monitoredNickList);
|
||||
}
|
||||
for (String nick : nicks)
|
||||
{
|
||||
update(nick, status);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update specified nick to specified presence status.
|
||||
*
|
||||
* @param nick the target nick
|
||||
* @param status the current status
|
||||
*/
|
||||
private void update(final String nick, final IrcStatusEnum status)
|
||||
{
|
||||
this.operationSet.updateNickContactPresence(nick, status);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,32 @@
|
||||
/*
|
||||
* Jitsi, the OpenSource Java VoIP and Instant Messaging client.
|
||||
*
|
||||
* Distributable under LGPL license.
|
||||
* See terms of license at gnu.org.
|
||||
*/
|
||||
package net.java.sip.communicator.impl.protocol.irc;
|
||||
|
||||
/**
|
||||
* Interface of the presence watcher.
|
||||
*
|
||||
* This interface defines the requirements of a presence watcher. The watcher is
|
||||
* used to update contact presence for a selection of nicks.
|
||||
*
|
||||
* @author Danny van Heumen
|
||||
*/
|
||||
public interface PresenceWatcher
|
||||
{
|
||||
/**
|
||||
* Add a nick to the list.
|
||||
*
|
||||
* @param nick the nick
|
||||
*/
|
||||
void add(String nick);
|
||||
|
||||
/**
|
||||
* Remove a nick from the list.
|
||||
*
|
||||
* @param nick the nick
|
||||
*/
|
||||
void remove(String nick);
|
||||
}
|
||||
@ -0,0 +1,339 @@
|
||||
/*
|
||||
* Jitsi, the OpenSource Java VoIP and Instant Messaging client.
|
||||
*
|
||||
* Distributable under LGPL license.
|
||||
* See terms of license at gnu.org.
|
||||
*/
|
||||
package net.java.sip.communicator.impl.protocol.irc;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import net.java.sip.communicator.util.*;
|
||||
|
||||
import com.ircclouds.irc.api.*;
|
||||
import com.ircclouds.irc.api.domain.messages.*;
|
||||
import com.ircclouds.irc.api.state.*;
|
||||
|
||||
/**
|
||||
* WATCH presence watcher.
|
||||
*
|
||||
* @author Danny van Heumen
|
||||
*/
|
||||
class WatchPresenceWatcher
|
||||
implements PresenceWatcher
|
||||
{
|
||||
/**
|
||||
* Static overhead in message payload required for 'WATCH ' command.
|
||||
*/
|
||||
private static final int WATCH_ADD_CMD_STATIC_OVERHEAD = 6;
|
||||
|
||||
/**
|
||||
* Logger.
|
||||
*/
|
||||
private static final Logger LOGGER = Logger
|
||||
.getLogger(WatchPresenceWatcher.class);
|
||||
|
||||
/**
|
||||
* IRCApi instance.
|
||||
*/
|
||||
private final IRCApi irc;
|
||||
|
||||
/**
|
||||
* IRC connection state.
|
||||
*/
|
||||
private final IIRCState connectionState;
|
||||
|
||||
/**
|
||||
* Complete nick watch list.
|
||||
*/
|
||||
private final Set<String> nickWatchList;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param irc the IRCApi instance
|
||||
* @param connectionState the connection state
|
||||
* @param nickWatchList SYNCHRONIZED the nick watch list
|
||||
* @param monitored SYNCHRONIZED The shared collection which contains all
|
||||
* the nicks that are confirmed to be subscribed to the MONITOR
|
||||
* command.
|
||||
* @param operationSet the persistent presence operation set
|
||||
*/
|
||||
WatchPresenceWatcher(final IRCApi irc, final IIRCState connectionState,
|
||||
final Set<String> nickWatchList, final Set<String> monitored,
|
||||
final OperationSetPersistentPresenceIrcImpl operationSet,
|
||||
final int maxListSize)
|
||||
{
|
||||
if (irc == null)
|
||||
{
|
||||
throw new IllegalArgumentException("irc cannot be null");
|
||||
}
|
||||
this.irc = irc;
|
||||
if (connectionState == null)
|
||||
{
|
||||
throw new IllegalArgumentException(
|
||||
"connectionState cannot be null");
|
||||
}
|
||||
this.connectionState = connectionState;
|
||||
if (nickWatchList == null)
|
||||
{
|
||||
throw new IllegalArgumentException("nickWatchList cannot be null");
|
||||
}
|
||||
this.nickWatchList = nickWatchList;
|
||||
this.irc.addListener(new WatchReplyListener(monitored, operationSet));
|
||||
setUpWatch(this.irc, this.nickWatchList, maxListSize);
|
||||
LOGGER.debug("WATCH presence watcher initialized.");
|
||||
}
|
||||
|
||||
/**
|
||||
* Set up monitor based on the nick watch list in its current state.
|
||||
*
|
||||
* Created a static method as not to interfere too much with a state that is
|
||||
* still being initialized.
|
||||
*/
|
||||
private static void setUpWatch(final IRCApi irc,
|
||||
final Collection<String> nickWatchList, final int maxListSize)
|
||||
{
|
||||
List<String> current;
|
||||
synchronized (nickWatchList)
|
||||
{
|
||||
current = new LinkedList<String>(nickWatchList);
|
||||
}
|
||||
if (current.size() > maxListSize)
|
||||
{
|
||||
// cut off list to maximum number of entries allowed by server
|
||||
current = current.subList(0, maxListSize);
|
||||
}
|
||||
final int maxLength = 510 - WATCH_ADD_CMD_STATIC_OVERHEAD;
|
||||
final StringBuilder query = new StringBuilder();
|
||||
for (String nick : current)
|
||||
{
|
||||
if (query.length() + nick.length() + 2 > maxLength)
|
||||
{
|
||||
// full payload, send monitor query now
|
||||
irc.rawMessage("WATCH " + query);
|
||||
query.delete(0, query.length());
|
||||
}
|
||||
else if (query.length() > 0)
|
||||
{
|
||||
query.append(" ");
|
||||
}
|
||||
query.append('+').append(nick);
|
||||
}
|
||||
if (query.length() > 0)
|
||||
{
|
||||
// send query for remaining nicks
|
||||
irc.rawMessage("WATCH " + query);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void add(final String nick)
|
||||
{
|
||||
LOGGER.trace("Adding nick '" + nick + "' to WATCH watch list.");
|
||||
this.nickWatchList.add(nick);
|
||||
this.irc.rawMessage("WATCH +" + nick);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove(final String nick)
|
||||
{
|
||||
LOGGER.trace("Removing nick '" + nick + "' from WATCH watch list.");
|
||||
this.nickWatchList.remove(nick);
|
||||
this.irc.rawMessage("WATCH -" + nick);
|
||||
}
|
||||
|
||||
/**
|
||||
* Listener for WATCH replies.
|
||||
*
|
||||
* @author Danny van Heumen
|
||||
*/
|
||||
private final class WatchReplyListener
|
||||
extends AbstractIrcMessageListener
|
||||
{
|
||||
/**
|
||||
* Numeric message id for notification that user logged on.
|
||||
*/
|
||||
private static final int IRC_RPL_LOGON = 600;
|
||||
|
||||
/**
|
||||
* Numeric message id for notification that user logged off.
|
||||
*/
|
||||
private static final int IRC_RPL_LOGOFF = 601;
|
||||
|
||||
/**
|
||||
* Numeric message id for when nick is removed from WATCH list.
|
||||
*/
|
||||
private static final int IRC_RPL_WATCHOFF = 602;
|
||||
|
||||
/**
|
||||
* Numeric message id for ONLINE nick response.
|
||||
*/
|
||||
private static final int IRC_RPL_NOWON = 604;
|
||||
|
||||
/**
|
||||
* Numeric message id for OFFLINE nick response.
|
||||
*/
|
||||
private static final int IRC_RPL_NOWOFF = 605;
|
||||
|
||||
// /**
|
||||
// * Numeric message id for MONLIST entry.
|
||||
// */
|
||||
// private static final int IRC_RPL_MONLIST = 732;
|
||||
//
|
||||
// /**
|
||||
// * Numeric message id for ENDOFMONLIST.
|
||||
// */
|
||||
// private static final int IRC_RPL_ENDOFMONLIST = 733;
|
||||
|
||||
/**
|
||||
* Error message signaling full list. Nick list provided are all nicks
|
||||
* that failed to be added to the WATCH list.
|
||||
*/
|
||||
private static final int IRC_ERR_LISTFULL = 512;
|
||||
|
||||
/**
|
||||
* Operation set persistent presence instance.
|
||||
*/
|
||||
private final OperationSetPersistentPresenceIrcImpl operationSet;
|
||||
|
||||
/**
|
||||
* Set of nicks that are confirmed to be monitored by the server.
|
||||
*/
|
||||
private final Set<String> monitoredNickList;
|
||||
|
||||
// TODO Update to act on onClientError once available.
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param monitored SYNCHRONIZED Collection of monitored nicks. This
|
||||
* collection will be updated with all nicks that are
|
||||
* confirmed to be subscribed by the WATCH command.
|
||||
* @param operationSet the persistent presence opset used to update nick
|
||||
* presence statuses.
|
||||
*/
|
||||
public WatchReplyListener(final Set<String> monitored,
|
||||
final OperationSetPersistentPresenceIrcImpl operationSet)
|
||||
{
|
||||
super(WatchPresenceWatcher.this.irc,
|
||||
WatchPresenceWatcher.this.connectionState);
|
||||
if (operationSet == null)
|
||||
{
|
||||
throw new IllegalArgumentException(
|
||||
"operationSet cannot be null");
|
||||
}
|
||||
this.operationSet = operationSet;
|
||||
if (monitored == null)
|
||||
{
|
||||
throw new IllegalArgumentException("monitored cannot be null");
|
||||
}
|
||||
this.monitoredNickList = monitored;
|
||||
}
|
||||
|
||||
/**
|
||||
* Numeric messages received in response to WATCH commands or presence
|
||||
* updates.
|
||||
*/
|
||||
@Override
|
||||
public void onServerNumericMessage(final ServerNumericMessage msg)
|
||||
{
|
||||
final String nick;
|
||||
switch (msg.getNumericCode())
|
||||
{
|
||||
case IRC_RPL_NOWON:
|
||||
nick = parseWatchResponse(msg.getText());
|
||||
monitoredNickList.add(nick);
|
||||
update(nick, IrcStatusEnum.ONLINE);
|
||||
break;
|
||||
case IRC_RPL_LOGON:
|
||||
nick = parseWatchResponse(msg.getText());
|
||||
update(nick, IrcStatusEnum.ONLINE);
|
||||
break;
|
||||
case IRC_RPL_NOWOFF:
|
||||
nick = parseWatchResponse(msg.getText());
|
||||
monitoredNickList.add(nick);
|
||||
update(nick, IrcStatusEnum.OFFLINE);
|
||||
break;
|
||||
case IRC_RPL_LOGOFF:
|
||||
nick = parseWatchResponse(msg.getText());
|
||||
update(nick, IrcStatusEnum.OFFLINE);
|
||||
break;
|
||||
case IRC_RPL_WATCHOFF:
|
||||
nick = parseWatchResponse(msg.getText());
|
||||
monitoredNickList.remove(nick);
|
||||
break;
|
||||
case IRC_ERR_LISTFULL:
|
||||
LOGGER.debug("WATCH list is full. Nick was not added. "
|
||||
+ "Fall back Basic Poller will be used if it is enabled. ("
|
||||
+ msg.getText() + ")");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update all monitored nicks upon receiving a server-side QUIT message
|
||||
* for local user.
|
||||
*/
|
||||
@Override
|
||||
public void onUserQuit(QuitMessage msg)
|
||||
{
|
||||
super.onUserQuit(msg);
|
||||
if (localUser(msg.getSource().getNick())) {
|
||||
updateAll(IrcStatusEnum.OFFLINE);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update all monitored nicks upon receiving a server-side ERROR
|
||||
* response.
|
||||
*/
|
||||
@Override
|
||||
public void onError(ErrorMessage msg)
|
||||
{
|
||||
super.onError(msg);
|
||||
updateAll(IrcStatusEnum.OFFLINE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse response messages.
|
||||
*
|
||||
* @param message the message
|
||||
* @return Returns the list of targets extracted.
|
||||
*/
|
||||
private String parseWatchResponse(final String message)
|
||||
{
|
||||
final String[] parts = message.split(" ");
|
||||
return parts[0];
|
||||
}
|
||||
|
||||
/**
|
||||
* Update all monitored nicks to specified status.
|
||||
*
|
||||
* @param status the desired status
|
||||
*/
|
||||
private void updateAll(final IrcStatusEnum status)
|
||||
{
|
||||
final LinkedList<String> nicks;
|
||||
synchronized (monitoredNickList)
|
||||
{
|
||||
nicks = new LinkedList<String>(monitoredNickList);
|
||||
}
|
||||
for (String nick : nicks)
|
||||
{
|
||||
update(nick, status);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update specified nick to specified presence status.
|
||||
*
|
||||
* @param nick the target nick
|
||||
* @param status the current status
|
||||
*/
|
||||
private void update(final String nick, final IrcStatusEnum status)
|
||||
{
|
||||
this.operationSet.updateNickContactPresence(nick, status);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,207 @@
|
||||
/*
|
||||
* Jitsi, the OpenSource Java VoIP and Instant Messaging client.
|
||||
*
|
||||
* Distributable under LGPL license.
|
||||
* See terms of license at gnu.org.
|
||||
*/
|
||||
package net.java.sip.communicator.impl.protocol.irc.collection;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* Dynamic difference set.
|
||||
*
|
||||
* A custom implementation that constructs a (dynamic) difference set from 2
|
||||
* input instances. The first input instance is the 'source' which is used as
|
||||
* the base data set. The second input is the 'removals', which are then removed
|
||||
* from the source set. The dynamic set is computed each time a method call is
|
||||
* executed.
|
||||
*
|
||||
* This set is immutable. That is, the modifier methods are unsupported. Changes
|
||||
* that do happen are derived from changes in the 'source' set or the 'removals'
|
||||
* set.
|
||||
*
|
||||
* Keep in mind that any operation on this dynamic returns a result that is only
|
||||
* valid in the moment. Even calling two subsequent methods can result in
|
||||
* difference or conflicting results.
|
||||
*
|
||||
* @author Danny van Heumen
|
||||
*
|
||||
* @param <E> The type of element that is stored in the set.
|
||||
*/
|
||||
public class DynamicDifferenceSet<E>
|
||||
implements Set<E>
|
||||
{
|
||||
/**
|
||||
* SYNCHRONIZED The source or base data set. This set is the basis and contains all the
|
||||
* elements that can possibly be in the dynamic set.
|
||||
*/
|
||||
private final Set<E> source;
|
||||
|
||||
/**
|
||||
* SYNCHRONIZED The removals set contains elements that are removed during the
|
||||
* calculation of the difference set at the moment.
|
||||
*/
|
||||
private final Set<E> removals;
|
||||
|
||||
/**
|
||||
* Constructor for creating a difference set instance.
|
||||
*
|
||||
* @param source The source data set.
|
||||
* @param removals The removals data set which will be used to create the
|
||||
* difference.
|
||||
*/
|
||||
public DynamicDifferenceSet(final Set<E> source,
|
||||
final Set<E> removals)
|
||||
{
|
||||
if (source == null)
|
||||
{
|
||||
throw new IllegalArgumentException("source cannot be null");
|
||||
}
|
||||
this.source = source;
|
||||
if (removals == null)
|
||||
{
|
||||
throw new IllegalArgumentException("removals cannot be null");
|
||||
}
|
||||
this.removals = removals;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate the difference set based on the current state of the data.
|
||||
*
|
||||
* @return Returns the current difference set that is calculated on the fly.
|
||||
*/
|
||||
private Set<E> calculate()
|
||||
{
|
||||
final TreeSet<E> current;
|
||||
synchronized (source)
|
||||
{
|
||||
current = new TreeSet<E>(source);
|
||||
}
|
||||
synchronized (removals)
|
||||
{
|
||||
current.removeAll(removals);
|
||||
}
|
||||
return current;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the size of the difference set. (Keep in mind that these numbers are
|
||||
* only momentary and may be obsolete as soon as they are calculated.)
|
||||
*/
|
||||
@Override
|
||||
public int size()
|
||||
{
|
||||
return calculate().size();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the difference set of the moment is empty. (Keep in mind that
|
||||
* the result may be obsolete as soon as it is calculated.)
|
||||
*/
|
||||
@Override
|
||||
public boolean isEmpty()
|
||||
{
|
||||
return calculate().isEmpty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if an element is contained in the difference set.
|
||||
*/
|
||||
@Override
|
||||
public boolean contains(Object o)
|
||||
{
|
||||
return this.source.contains(o) && !this.removals.contains(o);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an iterator for the difference set of the moment.
|
||||
*/
|
||||
@Override
|
||||
public Iterator<E> iterator()
|
||||
{
|
||||
return calculate().iterator();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an array of the current difference set.
|
||||
*/
|
||||
@Override
|
||||
public Object[] toArray()
|
||||
{
|
||||
return calculate().toArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an array of the current difference set.
|
||||
*/
|
||||
@Override
|
||||
public <T> T[] toArray(T[] a)
|
||||
{
|
||||
return calculate().toArray(a);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adding elements to a difference set is unsupported.
|
||||
*/
|
||||
@Override
|
||||
public boolean add(E e)
|
||||
{
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
/**
|
||||
* Removing elements from a difference set is unsupported.
|
||||
*/
|
||||
@Override
|
||||
public boolean remove(Object o)
|
||||
{
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if all provided elements are contained in the difference set of the
|
||||
* moment.
|
||||
*/
|
||||
@Override
|
||||
public boolean containsAll(Collection<?> c)
|
||||
{
|
||||
return calculate().containsAll(c);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adding elements to a difference set is unsupported.
|
||||
*/
|
||||
@Override
|
||||
public boolean addAll(Collection<? extends E> c)
|
||||
{
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
/**
|
||||
* Removing elements from a difference set is unsupported.
|
||||
*/
|
||||
@Override
|
||||
public boolean removeAll(Collection<?> c)
|
||||
{
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
/**
|
||||
* Modifying a difference set is unsupported.
|
||||
*/
|
||||
@Override
|
||||
public boolean retainAll(Collection<?> c)
|
||||
{
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
/**
|
||||
* Modifying a difference set is unsupported.
|
||||
*/
|
||||
@Override
|
||||
public void clear()
|
||||
{
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,13 @@
|
||||
/*
|
||||
* Jitsi, the OpenSource Java VoIP and Instant Messaging client.
|
||||
*
|
||||
* Distributable under LGPL license.
|
||||
* See terms of license at gnu.org.
|
||||
*/
|
||||
/**
|
||||
* Set of dynamic collections that are used to manage the interaction between
|
||||
* various individual components.
|
||||
*
|
||||
* @author Danny van Heumen
|
||||
*/
|
||||
package net.java.sip.communicator.impl.protocol.irc.collection;
|
||||
Loading…
Reference in new issue