1 // GtpCommand.java
2 
3 package net.sf.gogui.gtp;
4 
5 import java.util.Locale;
6 import net.sf.gogui.go.GoColor;
7 import static net.sf.gogui.go.GoColor.BLACK;
8 import static net.sf.gogui.go.GoColor.WHITE;
9 import net.sf.gogui.go.GoPoint;
10 import net.sf.gogui.go.InvalidPointException;
11 import net.sf.gogui.go.PointList;
12 import net.sf.gogui.util.StringUtil;
13 
14 /** GTP command.
15     Handles parsing the command line and storing the response to the command.
16     Arguments containing whitespaces can be quoted with double quotes (").
17     The responses are allowed to contain consecutive new lines.
18     They will be replaced in GtpEngine.mainLoop() by lines containing a single
19     space to form a valid GTP response. */
20 public class GtpCommand
21 {
22     /** Construct command from command line.
23         @param line The full command line including ID. */
GtpCommand(String line)24     public GtpCommand(String line)
25     {
26         StringBuilder buffer = preprocessLine(line);
27         assert ! line.trim().equals("");
28         String[] array = StringUtil.splitArguments(buffer.toString());
29         assert array.length > 0;
30         int commandIndex = 0;
31         try
32         {
33             m_id = Integer.parseInt(array[0]);
34             m_hasId = true;
35             m_line = buffer.substring(array[0].length()).trim();
36             commandIndex = 1;
37         }
38         catch (NumberFormatException e)
39         {
40             m_hasId = false;
41             m_id = -1;
42             m_line = buffer.toString();
43         }
44         m_response = new StringBuilder();
45         if (commandIndex >= array.length)
46         {
47             m_command = "";
48             m_arg = null;
49             return;
50         }
51         m_command = array[commandIndex];
52         int nuArg = array.length - commandIndex - 1;
53         m_arg = new String[nuArg];
54         for (int i = 0; i < nuArg; ++i)
55             m_arg[i] = array[commandIndex + i + 1];
56     }
57 
58     /** Check that command has no arguments.
59         @throws GtpError If command has any arguments. */
checkArgNone()60     public void checkArgNone() throws GtpError
61     {
62         checkNuArg(0);
63     }
64 
65     /** Check that command has n arguments.
66         @throws GtpError If command has not n arguments. */
checkNuArg(int n)67     public void checkNuArg(int n) throws GtpError
68     {
69         if (getNuArg() != n)
70         {
71             if (n == 0)
72                 throw new GtpError("no arguments allowed");
73             if (n == 1)
74                 throw new GtpError("need argument");
75             throw new GtpError("need " + n + " arguments");
76         }
77     }
78 
79     /** Check that command has not more than n arguments.
80         @throws GtpError If command has more than n arguments. */
checkNuArgLessEqual(int n)81     public void checkNuArgLessEqual(int n) throws GtpError
82     {
83         if (getNuArg() > n)
84             throw new GtpError("too many arguments");
85     }
86 
87     /** Check if command has an ID.
88         @return true, if command has an ID. */
hasId()89     public boolean hasId()
90     {
91         return m_hasId;
92     }
93 
getArg()94     public String getArg() throws GtpError
95     {
96         checkNuArg(1);
97         return getArg(0);
98     }
99 
100     /** Get argument.
101         @param i The index of the argument (starting with zero).
102         @return The argument.
103         @throws GtpError If command has not enough arguments. */
getArg(int i)104     public String getArg(int i) throws GtpError
105     {
106         if (i >= getNuArg())
107             throw new GtpError("missing argument " + (i + 1));
108         return m_arg[i];
109     }
110 
111     /** Get argument line.
112         Get a string containing all arguments (the command line without
113         ID and command; leading and trailing whitespaces trimmed).
114         @return The argument line. */
getArgLine()115     public String getArgLine()
116     {
117         int pos = m_line.indexOf(m_command) + m_command.length();
118         return m_line.substring(pos).trim();
119     }
120 
121     /** Get single color argument.
122         Valid color strings are "b", "w", "black", "white" and the
123         corresponding uppercase strings.
124         @return The color.
125         @throws GtpError If command has not exactly one argument or argument
126         is not a color. */
getColorArg()127     public GoColor getColorArg() throws GtpError
128     {
129         checkNuArg(1);
130         return getColorArg(0);
131     }
132 
133     /** Get color argument.
134         Valid color strings are "b", "w", "black", "white" and the
135         corresponding uppercase strings.
136         @param i The index of the argument (starting with zero).
137         @return The color.
138         @throws GtpError If command has not enough arguments or argument is
139         not a color. */
getColorArg(int i)140     public GoColor getColorArg(int i) throws GtpError
141     {
142         String arg = getArg(i).toLowerCase(Locale.ENGLISH);
143         if (arg.equals("b") || arg.equals("black"))
144             return BLACK;
145         if (arg.equals("w") || arg.equals("white"))
146             return WHITE;
147         throw new GtpError("argument " + (i + 1) + " must be black or white");
148     }
149 
150     /** Get command.
151         @return The command string (command line without ID and arguments,
152         leading and trailing whitespaces trimmed). */
getCommand()153     public String getCommand()
154     {
155         return m_command;
156     }
157 
158     /** Get single floating point number argument.
159         @return The color.
160         @throws GtpError If command has not exactly one argument or argument
161         is not a floating point number. */
getDoubleArg()162     public double getDoubleArg() throws GtpError
163     {
164         checkNuArg(1);
165         return getDoubleArg(0);
166     }
167 
168     /** Get floating point number argument.
169         @param i The index of the argument (starting with zero).
170         @return The color.
171         @throws GtpError If command has not enough arguments or argument is
172         not a floating point number. */
getDoubleArg(int i)173     public double getDoubleArg(int i) throws GtpError
174     {
175         String arg = getArg(i);
176         try
177         {
178             return Double.parseDouble(arg);
179         }
180         catch (NumberFormatException e)
181         {
182             throw new GtpError("argument " + (i + 1) + " must be float");
183         }
184     }
185 
186     /** Get single integer argument.
187         @return The color.
188         @throws GtpError If command has not exactly one argument or argument
189         is not an integer. */
getIntArg()190     public int getIntArg() throws GtpError
191     {
192         checkNuArg(1);
193         return getIntArg(0);
194     }
195 
196     /** Get integer argument.
197         @param i The index of the argument (starting with zero).
198         @return The color.
199         @throws GtpError If command has not enough arguments or argument is
200         not an integer. */
getIntArg(int i)201     public int getIntArg(int i) throws GtpError
202     {
203         String arg = getArg(i);
204         try
205         {
206             return Integer.parseInt(arg);
207         }
208         catch (NumberFormatException e)
209         {
210             throw new GtpError("argument " + (i + 1) + " must be integer");
211         }
212     }
213 
214     /** Get integer argument in a range.
215         @param i The index of the argument (starting with zero).
216         @param min Minimum allowed value.
217         @param max Maximum allowed value.
218         @return The color.
219         @throws GtpError If command has not enough arguments or argument is
220         not an integer in the allowed range. */
getIntArg(int i, int min, int max)221     public int getIntArg(int i, int min, int max) throws GtpError
222     {
223         int n = getIntArg(i);
224         if (n < min)
225             throw new GtpError("argument " + (i + 1)
226                                + " must be greater/equal " + min);
227         if (n > max)
228             throw new GtpError("argument " + (i + 1)
229                                + " must be less/equal " + max);
230         return n;
231     }
232 
233     /** Get point argument.
234         Valid point strings are as in GtpUtil.parsePoint (uppercase or
235         lowercase coordinates, e.g. "A1", or "pass").
236         @param i The index of the argument (starting with zero).
237         @param boardSize The board size (points will be checked to be within
238         this board size).
239         @return The point.
240         @throws GtpError If command has not enough arguments or argument is
241         not a valid point. */
getPointArg(int i, int boardSize)242     public GoPoint getPointArg(int i, int boardSize) throws GtpError
243     {
244         try
245         {
246             return GoPoint.parsePoint(getArg(i), boardSize);
247         }
248         catch (InvalidPointException e)
249         {
250             throw new GtpError("argument " + (i + 1) + " is not a point");
251         }
252     }
253 
254     /** Get point arguments.
255         Valid point strings are as in GtpUtil.parsePoint (uppercase or
256         lowercase coordinates, e.g. "A1", or "pass").
257         All arguments will be parsed as points.
258         @param boardSize The board size (points will be checked to be within
259         this board size).
260         @return Point list containg the points.
261         @throws GtpError If at least one argument is not a valid point. */
getPointListArg(int boardSize)262     public PointList getPointListArg(int boardSize) throws GtpError
263     {
264         PointList pointList = new PointList();
265         for (int i = 0; i < getNuArg(); ++i)
266             pointList.add(getPointArg(i, boardSize));
267         return pointList;
268     }
269 
270     /** Full command line without ID.
271         @return The command line without ID. */
getLine()272     public String getLine()
273     {
274         return m_line;
275     }
276 
277     /** Get number of arguments.
278         @return The number of arguments. */
getNuArg()279     public int getNuArg()
280     {
281         return m_arg.length;
282     }
283 
284     /** Get string buffer for construction the response.
285         The response to the command can be constructed by appending to this
286         string buffer. */
getResponse()287     public StringBuilder getResponse()
288     {
289         return m_response;
290     }
291 
292     /** Get command ID.
293         It is allowed to call this function if command has no ID, but the
294         returned value is undefined.
295         @return The command ID. */
getId()296     public int getId()
297     {
298         return m_id;
299     }
300 
301     /** Check if command is quit command.
302         DEPRECATED: Fix GtpEngine to use only GtpEngine.m_quit
303         @return true, if command name is "quit". */
isQuit()304     public boolean isQuit()
305     {
306         return m_line.trim().equals("quit");
307     }
308 
309     /** Set the response.
310         Clears the string buffer containg the response and sets it to the
311         given string.
312         @param response The string containing the new response. */
setResponse(String response)313     public void setResponse(String response)
314     {
315         m_response.setLength(0);
316         m_response.append(response);
317     }
318 
319     private boolean m_hasId;
320 
321     private int m_id;
322 
323     private String m_line;
324 
325     private final String m_command;
326 
327     private final String[] m_arg;
328 
329     private final StringBuilder m_response;
330 
331     /** Preprocess command line.
332         Replaces control characters by spaces, removes redundant spaces
333         and appended comment. */
preprocessLine(String line)334     private static StringBuilder preprocessLine(String line)
335     {
336         int len = line.length();
337         StringBuilder buffer = new StringBuilder(len);
338         boolean wasLastSpace = false;
339         for (int i = 0; i < len; ++i)
340         {
341             char c = line.charAt(i);
342             if (c == '#')
343                 break;
344             if (Character.isISOControl(c))
345                 continue;
346             if (Character.isWhitespace(c))
347             {
348                 if (! wasLastSpace)
349                 {
350                     buffer.append(' ');
351                     wasLastSpace = true;
352                 }
353             }
354             else
355             {
356                 buffer.append(c);
357                 wasLastSpace = false;
358             }
359         }
360         return buffer;
361     }
362 }
363