JSONTokener.java
1 /* 2 * Copyright (c) 2002 JSON.org (now "Public Domain") 3 * This is NOT property of REAndroid 4 * This package is renamed from org.json.* to avoid class conflict when used on android platforms 5 */ 6 package com.reandroid.json; 7 8 import java.io.BufferedReader; 9 import java.io.IOException; 10 import java.io.InputStream; 11 import java.io.InputStreamReader; 12 import java.io.Reader; 13 import java.io.StringReader; 14 15 public class JSONTokener { 16 /** current read character position on the current line. */ 17 private long character; 18 /** flag to indicate if the end of the input has been found. */ 19 private boolean eof; 20 /** current read index of the input. */ 21 private long index; 22 /** current line of the input. */ 23 private long line; 24 /** previous character read from the input. */ 25 private char previous; 26 /** Reader for the input. */ 27 private final Reader reader; 28 /** flag to indicate that a previous character was requested. */ 29 private boolean usePrevious; 30 /** the number of characters read in the previous line. */ 31 private long characterPreviousLine; 32 public JSONTokener(Reader reader) { 33 this.reader = reader.markSupported() 34 ? reader 35 : new BufferedReader(reader); 36 this.eof = false; 37 this.usePrevious = false; 38 this.previous = 0; 39 this.index = 0; 40 this.character = 1; 41 this.characterPreviousLine = 0; 42 this.line = 1; 43 } 44 public JSONTokener(InputStream inputStream) { 45 this(new InputStreamReader(inputStream)); 46 } 47 public JSONTokener(String s) { 48 this(new StringReader(s)); 49 } 50 public void back() throws JSONException { 51 if (this.usePrevious || this.index <= 0) { 52 throw new JSONException("Stepping back two steps is not supported"); 53 } 54 this.decrementIndexes(); 55 this.usePrevious = true; 56 this.eof = false; 57 } 58 59 private void decrementIndexes() { 60 this.index--; 61 if(this.previous=='\r' || this.previous == '\n') { 62 this.line--; 63 this.character=this.characterPreviousLine ; 64 } else if(this.character > 0){ 65 this.character--; 66 } 67 } 68 69 public static int dehexchar(char c) { 70 if (c >= '0' && c <= '9') { 71 return c - '0'; 72 } 73 if (c >= 'A' && c <= 'F') { 74 return c - ('A' - 10); 75 } 76 if (c >= 'a' && c <= 'f') { 77 return c - ('a' - 10); 78 } 79 return -1; 80 } 81 82 public boolean end() { 83 return this.eof && !this.usePrevious; 84 } 85 public boolean more() throws JSONException { 86 if(this.usePrevious) { 87 return true; 88 } 89 try { 90 this.reader.mark(1); 91 } catch (IOException e) { 92 throw new JSONException("Unable to preserve stream position", e); 93 } 94 try { 95 // -1 is EOF, but next() can not consume the null character '\0' 96 if(this.reader.read() <= 0) { 97 this.eof = true; 98 return false; 99 } 100 this.reader.reset(); 101 } catch (IOException e) { 102 throw new JSONException("Unable to read the next character from the stream", e); 103 } 104 return true; 105 } 106 public char next() throws JSONException { 107 int c; 108 if (this.usePrevious) { 109 this.usePrevious = false; 110 c = this.previous; 111 } else { 112 try { 113 c = this.reader.read(); 114 } catch (IOException exception) { 115 throw new JSONException(exception); 116 } 117 } 118 if (c <= 0) { // End of stream 119 this.eof = true; 120 return 0; 121 } 122 this.incrementIndexes(c); 123 this.previous = (char) c; 124 return this.previous; 125 } 126 127 private void incrementIndexes(int c) { 128 if(c > 0) { 129 this.index++; 130 if(c=='\r') { 131 this.line++; 132 this.characterPreviousLine = this.character; 133 this.character=0; 134 }else if (c=='\n') { 135 if(this.previous != '\r') { 136 this.line++; 137 this.characterPreviousLine = this.character; 138 } 139 this.character=0; 140 } else { 141 this.character++; 142 } 143 } 144 } 145 146 public char next(char c) throws JSONException { 147 char n = this.next(); 148 if (n != c) { 149 if(n > 0) { 150 throw this.syntaxError("Expected '" + c + "' and instead saw '" + 151 n + "'"); 152 } 153 throw this.syntaxError("Expected '" + c + "' and instead saw ''"); 154 } 155 return n; 156 } 157 public String next(int n) throws JSONException { 158 if (n == 0) { 159 return ""; 160 } 161 162 char[] chars = new char[n]; 163 int pos = 0; 164 165 while (pos < n) { 166 chars[pos] = this.next(); 167 if (this.end()) { 168 throw this.syntaxError("Substring bounds error"); 169 } 170 pos += 1; 171 } 172 return new String(chars); 173 } 174 public char nextClean() throws JSONException { 175 for (;;) { 176 char c = this.next(); 177 if (c == 0 || c > ' ') { 178 return c; 179 } 180 } 181 } 182 public String nextString(char quote) throws JSONException { 183 char c; 184 StringBuilder sb = new StringBuilder(); 185 for (;;) { 186 c = this.next(); 187 switch (c) { 188 case 0: 189 case '\n': 190 case '\r': 191 throw this.syntaxError("Unterminated string"); 192 case '\\': 193 c = this.next(); 194 switch (c) { 195 case 'b': 196 sb.append('\b'); 197 break; 198 case 't': 199 sb.append('\t'); 200 break; 201 case 'n': 202 sb.append('\n'); 203 break; 204 case 'f': 205 sb.append('\f'); 206 break; 207 case 'r': 208 sb.append('\r'); 209 break; 210 case 'u': 211 try { 212 sb.append((char)Integer.parseInt(this.next(4), 16)); 213 } catch (NumberFormatException e) { 214 throw this.syntaxError("Illegal escape.", e); 215 } 216 break; 217 case '"': 218 case '\'': 219 case '\\': 220 case '/': 221 sb.append(c); 222 break; 223 default: 224 throw this.syntaxError("Illegal escape."); 225 } 226 break; 227 default: 228 if (c == quote) { 229 return sb.toString(); 230 } 231 sb.append(c); 232 } 233 } 234 } 235 public String nextTo(char delimiter) throws JSONException { 236 StringBuilder sb = new StringBuilder(); 237 for (;;) { 238 char c = this.next(); 239 if (c == delimiter || c == 0 || c == '\n' || c == '\r') { 240 if (c != 0) { 241 this.back(); 242 } 243 return sb.toString().trim(); 244 } 245 sb.append(c); 246 } 247 } 248 public String nextTo(String delimiters) throws JSONException { 249 char c; 250 StringBuilder sb = new StringBuilder(); 251 for (;;) { 252 c = this.next(); 253 if (delimiters.indexOf(c) >= 0 || c == 0 || 254 c == '\n' || c == '\r') { 255 if (c != 0) { 256 this.back(); 257 } 258 return sb.toString().trim(); 259 } 260 sb.append(c); 261 } 262 } 263 public Object nextValue() throws JSONException { 264 char c = this.nextClean(); 265 String string; 266 267 switch (c) { 268 case '"': 269 case '\'': 270 string = this.nextString(c); 271 byte[] bytes = JsonUtil.parseBase64(string); 272 if(bytes == null){ 273 return string; 274 } 275 return bytes; 276 case '{': 277 this.back(); 278 return new JSONObject(this); 279 case '[': 280 this.back(); 281 return new JSONArray(this); 282 } 283 284 /* 285 * Handle unquoted text. This could be the values true, false, or 286 * null, or it can be a number. An implementation (such as this one) 287 * is allowed to also accept non-standard forms. 288 * 289 * Accumulate characters until we reach the end of the text or a 290 * formatting character. 291 */ 292 293 StringBuilder sb = new StringBuilder(); 294 while (c >= ' ' && ",:]}/\\\"[{;=#".indexOf(c) < 0) { 295 sb.append(c); 296 c = this.next(); 297 } 298 if (!this.eof) { 299 this.back(); 300 } 301 302 string = sb.toString().trim(); 303 if ("".equals(string)) { 304 throw this.syntaxError("Missing value"); 305 } 306 return JSONObject.stringToValue(string); 307 } 308 public char skipTo(char to) throws JSONException { 309 char c; 310 try { 311 long startIndex = this.index; 312 long startCharacter = this.character; 313 long startLine = this.line; 314 this.reader.mark(1000000); 315 do { 316 c = this.next(); 317 if (c == 0) { 318 // in some readers, reset() may throw an exception if 319 // the remaining portion of the input is greater than 320 // the mark size (1,000,000 above). 321 this.reader.reset(); 322 this.index = startIndex; 323 this.character = startCharacter; 324 this.line = startLine; 325 return 0; 326 } 327 } while (c != to); 328 this.reader.mark(1); 329 } catch (IOException exception) { 330 throw new JSONException(exception); 331 } 332 this.back(); 333 return c; 334 } 335 336 public JSONException syntaxError(String message) { 337 return new JSONException(message + this.toString()); 338 } 339 340 public JSONException syntaxError(String message, Throwable causedBy) { 341 return new JSONException(message + this.toString(), causedBy); 342 } 343 344 @Override 345 public String toString() { 346 return " at " + this.index + " [character " + this.character + " line " + 347 this.line + "]"; 348 } 349 }