Essentials/EssentialsUpdate/src/f00f/net/irc/martyr/commands/ModeCommand.java

238 lines
6.2 KiB
Java

package f00f.net.irc.martyr.commands;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.StringTokenizer;
import f00f.net.irc.martyr.CommandRegister;
import f00f.net.irc.martyr.InCommand;
import f00f.net.irc.martyr.Mode;
import f00f.net.irc.martyr.OutCommand;
import f00f.net.irc.martyr.State;
import f00f.net.irc.martyr.clientstate.Channel;
import f00f.net.irc.martyr.clientstate.ClientState;
import java.util.logging.Logger;
/**
* Defines MODE command. Since the MODE command is of two distinct
* types, this class is really more of a command mini-factory. It
* determines which type of command it is, either a UserModeCommand or
* a ChannelModeCommand.
*
*/
public class ModeCommand implements InCommand, OutCommand
{
static Logger log = Logger.getLogger(ModeCommand.class.getName());
public static final String IDENTIFIER = "MODE";
private String source;
/** For use as a factory */
public ModeCommand()
{
}
public Iterator getAttributeKeys()
{
return new LinkedList().iterator();
}
public String getAttribute( String key )
{
return null;
}
public static void registerMode( Map<Character,Mode> modes, Mode mode )
{
Character modeChar = mode.getChar();
if( modes.get( modeChar ) != null )
{
log.severe("ModeCommand: Warning: Two modes with same letter: " +
modes.get( modeChar ) + " and " + mode);
}
modes.put( modeChar, mode );
}
public State getState()
{
return State.REGISTERED;
}
public void selfRegister( CommandRegister reg )
{
reg.addCommand( IDENTIFIER, this );
}
public String getIrcIdentifier()
{
return IDENTIFIER;
}
// Example
// <pre>:repp_!bdamm@dammfine.com MODE #bytesex +oo z * repp_telnet</pre>
public InCommand parse( String prefix, String identifier, String params )
{
// there are two kinds of modes. Either a channel mode, or a user
// mode. We need to figure out which we are dealing with, and
// return that.
// TODO: Research: Should we specify delimiters other than whitespace?
StringTokenizer tokens = new StringTokenizer( params );
String str = tokens.nextToken();
//log.debug("ModeCommand: Prefix: " + prefix + " str: " + str
// + " total: " + params);
// Malformed command.
if( str == null )
return null;
// Should we check to see if the string is really a channel
// that we know about?
if( Channel.isChannel( str ) )
{
return new ChannelModeCommand( prefix, str, tokens );
}
else
{
return new UserModeCommand( prefix, str, tokens );
}
}
/**
* Should not be called, as ModeCommand doesn't actually represent a
* command. Use UserModeCommand or ChannelModeCommand instead.
*/
public String render()
{
throw new IllegalStateException("Don't try to send ModeCommand!");
}
public void setSourceString( String source )
{
this.source = source;
}
public String getSourceString()
{
return source;
}
/**
* Does nothing, as this is a factory command.
*/
public boolean updateClientState( ClientState cs )
{
// Nothing here, move on.
return false;
}
public String toString()
{
return "ModeCommand";
}
/** Takes a mode string, such as: '+ooo A B C' or '+o A +o B' or even
* '+o-o A B' and returns a List containing Mode objects that
* correspond to the modes specified.
*
* @param modes is a Map of Character to Mode objects.
* @param tokens is the sequence of tokens making up the parameters of
* the command.
* @return List of modes
*/
public List<Mode> parseModes( Map<Character,Mode> modes, StringTokenizer tokens )
{
LinkedList<Mode> results = new LinkedList<Mode>();
while( true )
{
if( tokens.hasMoreTokens() )
{
parseOneModeSet( modes, tokens, results );
}
else
{
return results;
}
}
}
/**
* Parses one group of modes. '+ooo A B C' and not '+o A +o B'. It
* will parse the first group it finds and will ignore the rest.
*
* @param modes Map of character to Mode objects.
* @param tokens Sequence of tokens making up the parameters of the command.
* @param results List of Mode results to be filled in
*/
private void parseOneModeSet( Map<Character,Mode> modes, StringTokenizer tokens, List<Mode> results )
{
// A list of modes that we have.
LinkedList<Mode> localModes = new LinkedList<Mode>();
Mode.Sign sign = Mode.Sign.NOSIGN;
String chars = tokens.nextToken();
int stop = chars.length();
for( int i = 0; i < stop; ++i )
{
char lookingAt = chars.charAt( i );
if( lookingAt == '+' )
sign = Mode.Sign.POSITIVE;
else if( lookingAt == '-' )
sign = Mode.Sign.NEGATIVE;
else if( lookingAt == ':' )
// This is to get around a bug in some ircds
continue;
else
{
// A real mode character!
Mode mode = modes.get( lookingAt );
if( mode == null )
{
//TODO: Is there some way we can figure out if the mode
// we don't know anything about needs a parameter?
// Things get messy if it does need a parameter, and we
// don't eat the string.
//log.severe("ModeCommand: Unknown mode: " + lookingAt);
}
else
{
mode = mode.newInstance();
mode.setSign( sign );
localModes.add( mode );
}
}
}
// Now we know what modes are specified, and whether they are
// positive or negative. Now we need to fill in the parameters for
// any that require parameters, and place the results in the result
// list.
for (Mode localMode : localModes) {
/*
* What we do if the server doesn't pass us a parameter
* for a mode is rather undefined - except that we don't
* want to run off the end of the tokens. So we just
* ignore it. The problem is that we don't always know
* when a server is going to send us a parameter or not.
* We can only hope that servers don't send ambiguous
* masks followed by more modes instead of a parameter.
*/
if (localMode != null && localMode.requiresParam() && tokens.hasMoreTokens()) {
localMode.setParam(tokens.nextToken());
}
results.add(localMode);
}
}
}