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 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 //
:repp_!bdamm@dammfine.com MODE #bytesex +oo z * repp_telnet
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 parseModes( Map modes, StringTokenizer tokens ) { LinkedList results = new LinkedList(); 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 modes, StringTokenizer tokens, List results ) { // A list of modes that we have. LinkedList localModes = new LinkedList(); 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); } } }