Source code: org/metacosm/framework/parser/Parser.java
1 /*
2 Metacosm, an object-oriented network game framework
3 Copyright (C) 1999-2001 Metacosm Development Team
4
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 */
19
20 package org.metacosm.framework.parser;
21
22 import org.metacosm.framework.command.PlayerCommand;
23 import org.metacosm.framework.command.ControllerCommand;
24 import org.metacosm.framework.server.Configuration;
25 import org.metacosm.framework.server.Player;
26 import java.util.ArrayList;
27 import java.util.StringTokenizer;
28
29 /**
30 * The Parser is used to parse (!) input streams from players.
31 * It's also a Singleton OOD pattern.
32 *
33 * The following grammar is accepted:
34 * <verb>
35 * input::= pseudocmd ([;] pseudocmd)*
36 * pseudocmd::= (white)* command (white)*
37 * white::= [space]
38 * command::= cmdname (pseudoparam)* | (white)*
39 * pseudoparam::= white (white)* param
40 * param ::= [alphanum] ([alphanum])* | ['] [alphanum] ([alphanum])* [']
41 *
42 * Terminal symbol: [space] (space), [;] (commands delimiter), ['] (string delimiter], [alphanum] (others autorized characters)
43 * </verb>
44 */
45 final public class Parser {
46 // Class methods
47 /**
48 * @return the unique instance of the class.
49 */
50 public static Parser getInstance() {
51 if (parser == null) {
52 parser = new Parser();
53 }
54 return parser;
55 }
56
57 /**
58 * Parses and executes the first command found in a string.
59 * An exception is thrown if something goes wrong.
60 * @return the end of the string
61 */
62 public String parse(Player player, String input) {
63 if (player == null || input == null) {
64 throw new java.lang.IllegalArgumentException();
65 }
66 String cmdName = null;
67 boolean playerCmd = false;
68
69 // TODO RUFFY is it an alias?
70
71 StringTokenizer st = new StringTokenizer(input,delimiters,true);
72 String part = junkWhites( st);
73
74 // Find cmdName
75 if (part != null && !part.equals(" ")) {
76 if (part.equals(str_delim) || part.equals(cmd_delim)) {
77 throw new ExpectedCommandException();
78 } else {
79 cmdName = new String(part);
80 }
81 } else {
82 // no command
83 return null;
84 }
85
86 playerCmd = isPlayerCommand( cmdName);
87
88 // Parameters or not?
89 ArrayList params = new ArrayList();
90 if (st.hasMoreTokens() && ! (part = st.nextToken()).equals(cmd_delim)) {
91 // Should find a white or a command delimiter
92 if (! part.equals(" ")) {
93 throw new UnexpectedStringException();
94 }
95
96 part = junkWhites( st);
97
98 // Find parameters
99 String param = null;
100 boolean endParam = (part==null);
101 while (! endParam) {
102 if (part.equals(str_delim)) {
103 if (param != null) {
104 // string in the middle of the param;
105 throw new UnexpectedStringException();
106 }
107 param = new String(str_delim);
108 part = null;
109 // find next string delimiter
110 while (st.hasMoreTokens() && !(part = st.nextToken()).equals(str_delim)) {
111 param += part;
112 }
113 if (part != null && param.equals(str_delim)) {
114 throw new EmptyStringException();
115 } else if ( part == null || !part.equals(str_delim)) {
116 throw new UnfinishedStringException();
117 } else {
118 if (st.hasMoreTokens()) {
119 part = st.nextToken();
120 if (part.equals(str_delim)) {
121 throw new UnexpectedStringException();
122 } else if (!part.equals(cmd_delim) && !part.equals(" ")) {
123 throw new InvalidParameterException();
124 } else if (part.equals(cmd_delim)) {
125 endParam = true;
126 }
127 }
128 // removes the first character (str_delim)
129 params.add(param.substring(1));
130 param = null;
131 }
132 } else if (part.equals(cmd_delim)) {
133 endParam = true;
134 if (param != null) {
135 params.add(param);
136 param = null;
137 }
138 } else if (part.equals(" ")) {
139 if (param != null) {
140 params.add(param);
141 param = null;
142 }
143 } else {
144 param = new String(part);
145 }
146 if (st.hasMoreTokens() && !endParam) {
147 part = st.nextToken();
148 } else {
149 endParam = true;
150 if (param != null) {
151 params.add(param);
152 }
153 }
154 }
155 }
156
157 // Execute command(cmdName, params)
158 // TODO RUFFY log all cmds
159 if (playerCmd) {
160 PlayerCommand pc = CommandsManager.getInstance().getPlayerCommand(cmdName);
161 player.execute(pc, params);
162 } else {
163 ControllerCommand cc = CommandsManager.getInstance().getControllerCommand(cmdName);
164 player.execute(cc, params);
165 }
166
167 // Return rest of input string
168 try {
169 part = st.nextToken("");
170 } catch( java.util.NoSuchElementException e) {
171 part = null;
172 }
173
174 return part;
175 }
176
177 /**
178 * Junks leading whites.
179 */
180 private String junkWhites( StringTokenizer st) {
181 boolean ok = true;
182 String part = null;
183 while (st.hasMoreTokens() && ok) {
184 part = st.nextToken();
185 ok = part.equals(" ");
186 }
187 return part;
188 }
189
190 /**
191 * Tests if a command is a PlayerCommand or a ControllerCommand.
192 * Throws exception in other cases.
193 */
194 boolean isPlayerCommand( String cmdName) {
195 if (CommandsManager.getInstance().containsPlayerCommand(cmdName)) {
196 return true;
197 } else if (CommandsManager.getInstance().containsControllerCommand(cmdName)) {
198 return false;
199 } else {
200 throw new UnknownCommandException();
201 }
202 }
203
204 /**
205 * The constructor is private because of singleton pattern.
206 * Sets the command and string delimiters from configuration file.
207 * Throws exceptions if incorrect delimiters.
208 */
209 private Parser() {
210 Configuration configuration = Configuration.getInstance();
211 cmd_delim = configuration.getString(Configuration.PARSER_COMMANDS_DELIMITER);
212 str_delim = configuration.getString(Configuration.PARSER_STRING_DELIMITER);
213 if (cmd_delim.equals(" ")) {
214 System.out.println(configuration.getString(Configuration.PARSER_BAD_COMMAND_DELIMITER));
215 throw new BadCommandDelimiterException();
216 } else if (str_delim.equals( " ")) {
217 System.out.println(configuration.getString(Configuration.PARSER_BAD_STRING_DELIMITER));
218 throw new BadStringDelimiterException();
219 } else if (cmd_delim.equals(str_delim)) {
220 System.out.println(configuration.getString(Configuration.PARSER_BAD_STRING_AND_COMMAND_DELIMITERS));
221 throw new BadStringAndCommandDelimitersException();
222 } else {
223 delimiters = " " + cmd_delim + str_delim;
224 }
225 }
226
227 /**
228 * The instance of the class.
229 */
230 private static Parser parser;
231
232 /**
233 * Characters used to separe tokens
234 * Default is space, <i>cmd_delim</i> and <i>str_delim</i>.
235 * @see cmd_delim
236 * @see str_delim
237 */
238 private static String delimiters;
239
240 /**
241 * Character used to delimit a string
242 */
243 private static String str_delim;
244
245 /**
246 * Character used to separe commands
247 */
248 private static String cmd_delim;
249 }