summaryrefslogtreecommitdiffstats
path: root/EssentialsUpdate/src/f00f/net/irc/martyr/commands/ModeCommand.java
blob: b3c3e19ef66383aee0283064aac41e302f43de53 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
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);
        }
    }
}