PageRenderTime 66ms CodeModel.GetById 31ms app.highlight 28ms RepoModel.GetById 1ms app.codeStats 1ms

/src/org/json/JSONTokener.java

http://github.com/nddrylliog/ooc
Java | 426 lines | 267 code | 40 blank | 119 comment | 37 complexity | b8632d99ae2f358bcd394db11c989188 MD5 | raw file
  1package org.json;
  2
  3import java.io.BufferedReader;
  4import java.io.IOException;
  5import java.io.Reader;
  6import java.io.StringReader;
  7
  8/*
  9Copyright (c) 2002 JSON.org
 10
 11Permission is hereby granted, free of charge, to any person obtaining a copy
 12of this software and associated documentation files (the "Software"), to deal
 13in the Software without restriction, including without limitation the rights
 14to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 15copies of the Software, and to permit persons to whom the Software is
 16furnished to do so, subject to the following conditions:
 17
 18The above copyright notice and this permission notice shall be included in all
 19copies or substantial portions of the Software.
 20
 21The Software shall be used for Good, not Evil.
 22
 23THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 24IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 25FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 26AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 27LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 28OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 29SOFTWARE.
 30*/
 31
 32/**
 33 * A JSONTokener takes a source string and extracts characters and tokens from
 34 * it. It is used by the JSONObject and JSONArray constructors to parse
 35 * JSON source strings.
 36 * @author JSON.org
 37 * @version 2008-09-18
 38 */
 39public class JSONTokener {
 40
 41    private int index;
 42    private Reader reader;
 43    private char lastChar;
 44    private boolean useLastChar;
 45
 46
 47    /**
 48     * Construct a JSONTokener from a string.
 49     *
 50     * @param reader     A reader.
 51     */
 52    public JSONTokener(Reader reader) {
 53        this.reader = reader.markSupported() ? 
 54        		reader : new BufferedReader(reader);
 55        this.useLastChar = false;
 56        this.index = 0;
 57    }
 58
 59
 60    /**
 61     * Construct a JSONTokener from a string.
 62     *
 63     * @param s     A source string.
 64     */
 65    public JSONTokener(String s) {
 66        this(new StringReader(s));
 67    }
 68
 69
 70    /**
 71     * Back up one character. This provides a sort of lookahead capability,
 72     * so that you can test for a digit or letter before attempting to parse
 73     * the next number or identifier.
 74     */
 75    public void back() throws JSONException {
 76        if (useLastChar || index <= 0) {
 77            throw new JSONException("Stepping back two steps is not supported");
 78        }
 79        index -= 1;
 80        useLastChar = true;
 81    }
 82
 83
 84
 85    /**
 86     * Get the hex value of a character (base16).
 87     * @param c A character between '0' and '9' or between 'A' and 'F' or
 88     * between 'a' and 'f'.
 89     * @return  An int between 0 and 15, or -1 if c was not a hex digit.
 90     */
 91    public static int dehexchar(char c) {
 92        if (c >= '0' && c <= '9') {
 93            return c - '0';
 94        }
 95        if (c >= 'A' && c <= 'F') {
 96            return c - ('A' - 10);
 97        }
 98        if (c >= 'a' && c <= 'f') {
 99            return c - ('a' - 10);
100        }
101        return -1;
102    }
103
104
105    /**
106     * Determine if the source string still contains characters that next()
107     * can consume.
108     * @return true if not yet at the end of the source.
109     */
110    public boolean more() throws JSONException {
111        char nextChar = next();
112        if (nextChar == 0) {
113            return false;
114        } 
115        back();
116        return true;
117    }
118
119
120    /**
121     * Get the next character in the source string.
122     *
123     * @return The next character, or 0 if past the end of the source string.
124     */
125    public char next() throws JSONException {
126        if (this.useLastChar) {
127        	this.useLastChar = false;
128            if (this.lastChar != 0) {
129            	this.index += 1;
130            }
131            return this.lastChar;
132        } 
133        int c;
134        try {
135            c = this.reader.read();
136        } catch (IOException exc) {
137            throw new JSONException(exc);
138        }
139
140        if (c <= 0) { // End of stream
141        	this.lastChar = 0;
142            return 0;
143        } 
144    	this.index += 1;
145    	this.lastChar = (char) c;
146        return this.lastChar;
147    }
148
149
150    /**
151     * Consume the next character, and check that it matches a specified
152     * character.
153     * @param c The character to match.
154     * @return The character.
155     * @throws JSONException if the character does not match.
156     */
157    public char next(char c) throws JSONException {
158        char n = next();
159        if (n != c) {
160            throw syntaxError("Expected '" + c + "' and instead saw '" +
161                    n + "'");
162        }
163        return n;
164    }
165
166
167    /**
168     * Get the next n characters.
169     *
170     * @param n     The number of characters to take.
171     * @return      A string of n characters.
172     * @throws JSONException
173     *   Substring bounds error if there are not
174     *   n characters remaining in the source string.
175     */
176     public String next(int n) throws JSONException {
177         if (n == 0) {
178             return "";
179         }
180
181         char[] buffer = new char[n];
182         int pos = 0;
183
184         if (this.useLastChar) {
185        	 this.useLastChar = false;
186             buffer[0] = this.lastChar;
187             pos = 1;
188         }
189
190         try {
191             int len;
192             while ((pos < n) && ((len = reader.read(buffer, pos, n - pos)) != -1)) {
193                 pos += len;
194             }
195         } catch (IOException exc) {
196             throw new JSONException(exc);
197         }
198         this.index += pos;
199
200         if (pos < n) {
201             throw syntaxError("Substring bounds error");
202         }
203
204         this.lastChar = buffer[n - 1];
205         return new String(buffer);
206     }
207
208
209    /**
210     * Get the next char in the string, skipping whitespace.
211     * @throws JSONException
212     * @return  A character, or 0 if there are no more characters.
213     */
214    public char nextClean() throws JSONException {
215        for (;;) {
216            char c = next();
217            if (c == 0 || c > ' ') {
218                return c;
219            }
220        }
221    }
222
223
224    /**
225     * Return the characters up to the next close quote character.
226     * Backslash processing is done. The formal JSON format does not
227     * allow strings in single quotes, but an implementation is allowed to
228     * accept them.
229     * @param quote The quoting character, either
230     *      <code>"</code>&nbsp;<small>(double quote)</small> or
231     *      <code>'</code>&nbsp;<small>(single quote)</small>.
232     * @return      A String.
233     * @throws JSONException Unterminated string.
234     */
235    public String nextString(char quote) throws JSONException {
236        char c;
237        StringBuffer sb = new StringBuffer();
238        for (;;) {
239            c = next();
240            switch (c) {
241            case 0:
242            case '\n':
243            case '\r':
244                throw syntaxError("Unterminated string");
245            case '\\':
246                c = next();
247                switch (c) {
248                case 'b':
249                    sb.append('\b');
250                    break;
251                case 't':
252                    sb.append('\t');
253                    break;
254                case 'n':
255                    sb.append('\n');
256                    break;
257                case 'f':
258                    sb.append('\f');
259                    break;
260                case 'r':
261                    sb.append('\r');
262                    break;
263                case 'u':
264                    sb.append((char)Integer.parseInt(next(4), 16));
265                    break;
266                case '"':
267                case '\'':
268                case '\\':
269                case '/':
270                	sb.append(c);
271                	break;
272                default:
273                    throw syntaxError("Illegal escape.");
274                }
275                break;
276            default:
277                if (c == quote) {
278                    return sb.toString();
279                }
280                sb.append(c);
281            }
282        }
283    }
284
285
286    /**
287     * Get the text up but not including the specified character or the
288     * end of line, whichever comes first.
289     * @param  d A delimiter character.
290     * @return   A string.
291     */
292    public String nextTo(char d) throws JSONException {
293        StringBuffer sb = new StringBuffer();
294        for (;;) {
295            char c = next();
296            if (c == d || c == 0 || c == '\n' || c == '\r') {
297                if (c != 0) {
298                    back();
299                }
300                return sb.toString().trim();
301            }
302            sb.append(c);
303        }
304    }
305
306
307    /**
308     * Get the text up but not including one of the specified delimiter
309     * characters or the end of line, whichever comes first.
310     * @param delimiters A set of delimiter characters.
311     * @return A string, trimmed.
312     */
313    public String nextTo(String delimiters) throws JSONException {
314        char c;
315        StringBuffer sb = new StringBuffer();
316        for (;;) {
317            c = next();
318            if (delimiters.indexOf(c) >= 0 || c == 0 ||
319                    c == '\n' || c == '\r') {
320                if (c != 0) {
321                    back();
322                }
323                return sb.toString().trim();
324            }
325            sb.append(c);
326        }
327    }
328
329
330    /**
331     * Get the next value. The value can be a Boolean, Double, Integer,
332     * JSONArray, JSONObject, Long, or String, or the JSONObject.NULL object.
333     * @throws JSONException If syntax error.
334     *
335     * @return An object.
336     */
337    public Object nextValue() throws JSONException {
338        char c = nextClean();
339        String s;
340
341        switch (c) {
342            case '"':
343            case '\'':
344                return nextString(c);
345            case '{':
346                back();
347                return new JSONObject(this);
348            case '[':
349            case '(':
350                back();
351                return new JSONArray(this);
352        }
353
354        /*
355         * Handle unquoted text. This could be the values true, false, or
356         * null, or it can be a number. An implementation (such as this one)
357         * is allowed to also accept non-standard forms.
358         *
359         * Accumulate characters until we reach the end of the text or a
360         * formatting character.
361         */
362
363        StringBuffer sb = new StringBuffer();
364        while (c >= ' ' && ",:]}/\\\"[{;=#".indexOf(c) < 0) {
365            sb.append(c);
366            c = next();
367        }
368        back();
369
370        s = sb.toString().trim();
371        if (s.equals("")) {
372            throw syntaxError("Missing value");
373        }
374        return JSONObject.stringToValue(s);
375    }
376
377
378    /**
379     * Skip characters until the next character is the requested character.
380     * If the requested character is not found, no characters are skipped.
381     * @param to A character to skip to.
382     * @return The requested character, or zero if the requested character
383     * is not found.
384     */
385    public char skipTo(char to) throws JSONException {
386        char c;
387        try {
388            int startIndex = this.index;
389            reader.mark(Integer.MAX_VALUE);
390            do {
391                c = next();
392                if (c == 0) {
393                    reader.reset();
394                    this.index = startIndex;
395                    return c;
396                }
397            } while (c != to);
398        } catch (IOException exc) {
399            throw new JSONException(exc);
400        }
401
402        back();
403        return c;
404    }
405
406    /**
407     * Make a JSONException to signal a syntax error.
408     *
409     * @param message The error message.
410     * @return  A JSONException object, suitable for throwing
411     */
412    public JSONException syntaxError(String message) {
413        return new JSONException(message + toString());
414    }
415
416
417    /**
418     * Make a printable string of this JSONTokener.
419     *
420     * @return " at character [this.index]"
421     */
422    @Override
423	public String toString() {
424        return " at character " + index;
425    }
426}