/ app / src / main / java / com / reandroid / json / JSONTokener.java
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  }