001package armyc2.c5isr.web.json.utilities; 002 003import java.io.BufferedReader; 004import java.io.IOException; 005import java.io.InputStream; 006import java.io.InputStreamReader; 007import java.io.Reader; 008import java.io.StringReader; 009 010/* 011Copyright (c) 2002 JSON.org 012 013Permission is hereby granted, free of charge, to any person obtaining a copy 014of this software and associated documentation files (the "Software"), to deal 015in the Software without restriction, including without limitation the rights 016to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 017copies of the Software, and to permit persons to whom the Software is 018furnished to do so, subject to the following conditions: 019 020The above copyright notice and this permission notice shall be included in all 021copies or substantial portions of the Software. 022 023The Software shall be used for Good, not Evil. 024 025THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 026IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 027FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 028AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 029LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 030OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 031SOFTWARE. 032*/ 033 034/** 035 * A JSONTokener takes a source string and extracts characters and tokens from 036 * it. It is used by the JSONObject and JSONArray constructors to parse 037 * JSON source strings. 038 * @author JSON.org 039 * @version 2010-12-24 040 */ 041public class JSONTokener { 042 043 private int character; 044 private boolean eof; 045 private int index; 046 private int line; 047 private char previous; 048 private Reader reader; 049 private boolean usePrevious; 050 051 052 /** 053 * Construct a JSONTokener from a Reader. 054 * 055 * @param reader A reader. 056 */ 057 public JSONTokener(Reader reader) { 058 this.reader = reader.markSupported() ? 059 reader : new BufferedReader(reader); 060 this.eof = false; 061 this.usePrevious = false; 062 this.previous = 0; 063 this.index = 0; 064 this.character = 1; 065 this.line = 1; 066 } 067 068 069 /** 070 * Construct a JSONTokener from an InputStream. 071 */ 072 public JSONTokener(InputStream inputStream) throws JSONException { 073 this(new InputStreamReader(inputStream)); 074 } 075 076 077 /** 078 * Construct a JSONTokener from a string. 079 * 080 * @param s A source string. 081 */ 082 public JSONTokener(String s) { 083 this(new StringReader(s)); 084 } 085 086 087 /** 088 * Back up one character. This provides a sort of lookahead capability, 089 * so that you can test for a digit or letter before attempting to parse 090 * the next number or identifier. 091 */ 092 public void back() throws JSONException { 093 if (usePrevious || index <= 0) { 094 throw new JSONException("Stepping back two steps is not supported"); 095 } 096 this.index -= 1; 097 this.character -= 1; 098 this.usePrevious = true; 099 this.eof = false; 100 } 101 102 103 /** 104 * Get the hex value of a character (base16). 105 * @param c A character between '0' and '9' or between 'A' and 'F' or 106 * between 'a' and 'f'. 107 * @return An int between 0 and 15, or -1 if c was not a hex digit. 108 */ 109 public static int dehexchar(char c) { 110 if (c >= '0' && c <= '9') { 111 return c - '0'; 112 } 113 if (c >= 'A' && c <= 'F') { 114 return c - ('A' - 10); 115 } 116 if (c >= 'a' && c <= 'f') { 117 return c - ('a' - 10); 118 } 119 return -1; 120 } 121 122 public boolean end() { 123 return eof && !usePrevious; 124 } 125 126 127 /** 128 * Determine if the source string still contains characters that next() 129 * can consume. 130 * @return true if not yet at the end of the source. 131 */ 132 public boolean more() throws JSONException { 133 next(); 134 if (end()) { 135 return false; 136 } 137 back(); 138 return true; 139 } 140 141 142 /** 143 * Get the next character in the source string. 144 * 145 * @return The next character, or 0 if past the end of the source string. 146 */ 147 public char next() throws JSONException { 148 int c; 149 if (this.usePrevious) { 150 this.usePrevious = false; 151 c = this.previous; 152 } else { 153 try { 154 c = this.reader.read(); 155 } catch (IOException exception) { 156 throw new JSONException(exception); 157 } 158 159 if (c <= 0) { // End of stream 160 this.eof = true; 161 c = 0; 162 } 163 } 164 this.index += 1; 165 if (this.previous == '\r') { 166 this.line += 1; 167 this.character = c == '\n' ? 0 : 1; 168 } else if (c == '\n') { 169 this.line += 1; 170 this.character = 0; 171 } else { 172 this.character += 1; 173 } 174 this.previous = (char) c; 175 return this.previous; 176 } 177 178 179 /** 180 * Consume the next character, and check that it matches a specified 181 * character. 182 * @param c The character to match. 183 * @return The character. 184 * @throws JSONException if the character does not match. 185 */ 186 public char next(char c) throws JSONException { 187 char n = next(); 188 if (n != c) { 189 throw syntaxError("Expected '" + c + "' and instead saw '" + 190 n + "'"); 191 } 192 return n; 193 } 194 195 196 /** 197 * Get the next n characters. 198 * 199 * @param n The number of characters to take. 200 * @return A string of n characters. 201 * @throws JSONException 202 * Substring bounds error if there are not 203 * n characters remaining in the source string. 204 */ 205 public String next(int n) throws JSONException { 206 if (n == 0) { 207 return ""; 208 } 209 210 char[] chars = new char[n]; 211 int pos = 0; 212 213 while (pos < n) { 214 chars[pos] = next(); 215 if (end()) { 216 throw syntaxError("Substring bounds error"); 217 } 218 pos += 1; 219 } 220 return new String(chars); 221 } 222 223 224 /** 225 * Get the next char in the string, skipping whitespace. 226 * @throws JSONException 227 * @return A character, or 0 if there are no more characters. 228 */ 229 public char nextClean() throws JSONException { 230 for (;;) { 231 char c = next(); 232 if (c == 0 || c > ' ') { 233 return c; 234 } 235 } 236 } 237 238 239 /** 240 * Return the characters up to the next close quote character. 241 * Backslash processing is done. The formal JSON format does not 242 * allow strings in single quotes, but an implementation is allowed to 243 * accept them. 244 * @param quote The quoting character, either 245 * <code>"</code> <small>(double quote)</small> or 246 * <code>'</code> <small>(single quote)</small>. 247 * @return A String. 248 * @throws JSONException Unterminated string. 249 */ 250 public String nextString(char quote) throws JSONException { 251 char c; 252 StringBuffer sb = new StringBuffer(); 253 for (;;) { 254 c = next(); 255 switch (c) { 256 case 0: 257 case '\n': 258 case '\r': 259 throw syntaxError("Unterminated string"); 260 case '\\': 261 c = next(); 262 switch (c) { 263 case 'b': 264 sb.append('\b'); 265 break; 266 case 't': 267 sb.append('\t'); 268 break; 269 case 'n': 270 sb.append('\n'); 271 break; 272 case 'f': 273 sb.append('\f'); 274 break; 275 case 'r': 276 sb.append('\r'); 277 break; 278 case 'u': 279 sb.append((char)Integer.parseInt(next(4), 16)); 280 break; 281 case '"': 282 case '\'': 283 case '\\': 284 case '/': 285 sb.append(c); 286 break; 287 default: 288 throw syntaxError("Illegal escape."); 289 } 290 break; 291 default: 292 if (c == quote) { 293 return sb.toString(); 294 } 295 sb.append(c); 296 } 297 } 298 } 299 300 301 /** 302 * Get the text up but not including the specified character or the 303 * end of line, whichever comes first. 304 * @param delimiter A delimiter character. 305 * @return A string. 306 */ 307 public String nextTo(char delimiter) throws JSONException { 308 StringBuffer sb = new StringBuffer(); 309 for (;;) { 310 char c = next(); 311 if (c == delimiter || c == 0 || c == '\n' || c == '\r') { 312 if (c != 0) { 313 back(); 314 } 315 return sb.toString().trim(); 316 } 317 sb.append(c); 318 } 319 } 320 321 322 /** 323 * Get the text up but not including one of the specified delimiter 324 * characters or the end of line, whichever comes first. 325 * @param delimiters A set of delimiter characters. 326 * @return A string, trimmed. 327 */ 328 public String nextTo(String delimiters) throws JSONException { 329 char c; 330 StringBuffer sb = new StringBuffer(); 331 for (;;) { 332 c = next(); 333 if (delimiters.indexOf(c) >= 0 || c == 0 || 334 c == '\n' || c == '\r') { 335 if (c != 0) { 336 back(); 337 } 338 return sb.toString().trim(); 339 } 340 sb.append(c); 341 } 342 } 343 344 345 /** 346 * Get the next value. The value can be a Boolean, Double, Integer, 347 * JSONArray, JSONObject, Long, or String, or the JSONObject.NULL object. 348 * @throws JSONException If syntax error. 349 * 350 * @return An object. 351 */ 352 public Object nextValue() throws JSONException { 353 char c = nextClean(); 354 String string; 355 356 switch (c) { 357 case '"': 358 case '\'': 359 return nextString(c); 360 case '{': 361 back(); 362 return new JSONObject(this); 363 case '[': 364 back(); 365 return new JSONArray(this); 366 } 367 368 /* 369 * Handle unquoted text. This could be the values true, false, or 370 * null, or it can be a number. An implementation (such as this one) 371 * is allowed to also accept non-standard forms. 372 * 373 * Accumulate characters until we reach the end of the text or a 374 * formatting character. 375 */ 376 377 StringBuffer sb = new StringBuffer(); 378 while (c >= ' ' && ",:]}/\\\"[{;=#".indexOf(c) < 0) { 379 sb.append(c); 380 c = next(); 381 } 382 back(); 383 384 string = sb.toString().trim(); 385 if (string.equals("")) { 386 throw syntaxError("Missing value"); 387 } 388 return JSONObject.stringToValue(string); 389 } 390 391 392 /** 393 * Skip characters until the next character is the requested character. 394 * If the requested character is not found, no characters are skipped. 395 * @param to A character to skip to. 396 * @return The requested character, or zero if the requested character 397 * is not found. 398 */ 399 public char skipTo(char to) throws JSONException { 400 char c; 401 try { 402 int startIndex = this.index; 403 int startCharacter = this.character; 404 int startLine = this.line; 405 reader.mark(Integer.MAX_VALUE); 406 do { 407 c = next(); 408 if (c == 0) { 409 reader.reset(); 410 this.index = startIndex; 411 this.character = startCharacter; 412 this.line = startLine; 413 return c; 414 } 415 } while (c != to); 416 } catch (IOException exc) { 417 throw new JSONException(exc); 418 } 419 420 back(); 421 return c; 422 } 423 424 425 /** 426 * Make a JSONException to signal a syntax error. 427 * 428 * @param message The error message. 429 * @return A JSONException object, suitable for throwing 430 */ 431 public JSONException syntaxError(String message) { 432 return new JSONException(message + toString()); 433 } 434 435 436 /** 437 * Make a printable string of this JSONTokener. 438 * 439 * @return " at {index} [character {character} line {line}]" 440 */ 441 public String toString() { 442 return " at " + index + " [character " + this.character + " line " + 443 this.line + "]"; 444 } 445}