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