geode-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From u..@apache.org
Subject [6/8] geode git commit: GEODE-2142: Spotless
Date Fri, 17 Feb 2017 21:01:05 GMT
http://git-wip-us.apache.org/repos/asf/geode/blob/2e3a88f7/geode-json/src/main/java/org/json/JSONTokener.java
----------------------------------------------------------------------
diff --git a/geode-json/src/main/java/org/json/JSONTokener.java b/geode-json/src/main/java/org/json/JSONTokener.java
index 59eb4bc..21c9fb0 100755
--- a/geode-json/src/main/java/org/json/JSONTokener.java
+++ b/geode-json/src/main/java/org/json/JSONTokener.java
@@ -1,17 +1,15 @@
 /*
  * Copyright (C) 2010 The Android Open Source Project
  *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
except
+ * in compliance with the License. You may obtain a copy of the License at
  *
- *      http://www.apache.org/licenses/LICENSE-2.0
+ * http://www.apache.org/licenses/LICENSE-2.0
  *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
+ * Unless required by applicable law or agreed to in writing, software distributed under
the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
express
+ * or implied. See the License for the specific language governing permissions and limitations
under
+ * the License.
  */
 
 package org.json;
@@ -22,637 +20,620 @@ import java.io.IOException;
 import java.io.Reader;
 
 /**
- * Parses a JSON (<a href="http://www.ietf.org/rfc/rfc4627.txt">RFC 4627</a>)
- * encoded string into the corresponding object. Most clients of
- * this class will use only need the {@link #JSONTokener(String) constructor}
- * and {@link #nextValue} method. Example usage: <pre>
- * String json = "{"
- *         + "  \"query\": \"Pizza\", "
- *         + "  \"locations\": [ 94043, 90210 ] "
- *         + "}";
+ * Parses a JSON (<a href="http://www.ietf.org/rfc/rfc4627.txt">RFC 4627</a>)
encoded string into
+ * the corresponding object. Most clients of this class will use only need the
+ * {@link #JSONTokener(String) constructor} and {@link #nextValue} method. Example usage:
+ * 
+ * <pre>
+ * String json = "{" + "  \"query\": \"Pizza\", " + "  \"locations\": [ 94043, 90210 ] "
+ "}";
  *
  * JSONObject object = (JSONObject) new JSONTokener(json).nextValue();
  * String query = object.getString("query");
- * JSONArray locations = object.getJSONArray("locations");</pre>
+ * JSONArray locations = object.getJSONArray("locations");
+ * </pre>
  *
- * <p>For best interoperability and performance use JSON that complies with
- * RFC 4627, such as that generated by {@link JSONStringer}. For legacy reasons
- * this parser is lenient, so a successful parse does not indicate that the
- * input string was valid JSON. All of the following syntax errors will be
- * ignored:
+ * <p>
+ * For best interoperability and performance use JSON that complies with RFC 4627, such as
that
+ * generated by {@link JSONStringer}. For legacy reasons this parser is lenient, so a successful
+ * parse does not indicate that the input string was valid JSON. All of the following syntax
errors
+ * will be ignored:
  * <ul>
- * <li>End of line comments starting with {@code //} or {@code #} and ending
- * with a newline character.
- * <li>C-style comments starting with {@code /*} and ending with
- * {@code *}{@code /}. Such comments may not be nested.
+ * <li>End of line comments starting with {@code //} or {@code #} and ending with a
newline
+ * character.
+ * <li>C-style comments starting with {@code /*} and ending with {@code *}{@code /}.
Such comments
+ * may not be nested.
  * <li>Strings that are unquoted or {@code 'single quoted'}.
  * <li>Hexadecimal integers prefixed with {@code 0x} or {@code 0X}.
  * <li>Octal integers prefixed with {@code 0}.
  * <li>Array elements separated by {@code ;}.
- * <li>Unnecessary array separators. These are interpreted as if null was the
- * omitted value.
+ * <li>Unnecessary array separators. These are interpreted as if null was the omitted
value.
  * <li>Key-value pairs separated by {@code =} or {@code =>}.
  * <li>Key-value pairs separated by {@code ;}.
  * </ul>
  *
- * <p>Each tokener may be used to parse a single JSON string. Instances of this
- * class are not thread safe. Although this class is nonfinal, it was not
- * designed for inheritance and should not be subclassed. In particular,
- * self-use by overrideable methods is not specified. See <i>Effective Java</i>
- * Item 17, "Design and Document or inheritance or else prohibit it" for further
+ * <p>
+ * Each tokener may be used to parse a single JSON string. Instances of this class are not
thread
+ * safe. Although this class is nonfinal, it was not designed for inheritance and should
not be
+ * subclassed. In particular, self-use by overrideable methods is not specified. See <i>Effective
+ * Java</i> Item 17, "Design and Document or inheritance or else prohibit it" for further
  * information.
  */
 public class JSONTokener {
 
-    /**
-     * The input JSON.
-     */
-    private final String in;
-
-    /**
-     * The index of the next character to be returned by {@link #next}. When
-     * the input is exhausted, this equals the input's length.
-     */
-    private int pos;
-
-    /**
-     * @param in JSON encoded string. Null is not permitted and will yield a
-     *           tokener that throws {@code NullPointerExceptions} when methods are
-     *           called.
-     */
-    public JSONTokener(String in) {
-        // consume an optional byte order mark (BOM) if it exists
-        if (in != null && in.startsWith("\ufeff")) {
-            in = in.substring(1);
-        }
-        this.in = in;
+  /**
+   * The input JSON.
+   */
+  private final String in;
+
+  /**
+   * The index of the next character to be returned by {@link #next}. When the input is exhausted,
+   * this equals the input's length.
+   */
+  private int pos;
+
+  /**
+   * @param in JSON encoded string. Null is not permitted and will yield a tokener that throws
+   *        {@code NullPointerExceptions} when methods are called.
+   */
+  public JSONTokener(String in) {
+    // consume an optional byte order mark (BOM) if it exists
+    if (in != null && in.startsWith("\ufeff")) {
+      in = in.substring(1);
     }
-
-    public JSONTokener(Reader input) throws IOException {
-        StringBuilder s = new StringBuilder();
-        char[] readBuf = new char[102400];
-        int n = input.read(readBuf);
-        while (n >= 0) {
-            s.append(readBuf, 0, n);
-            n = input.read(readBuf);
-        }
-        in = s.toString();
-        pos = 0;
+    this.in = in;
+  }
+
+  public JSONTokener(Reader input) throws IOException {
+    StringBuilder s = new StringBuilder();
+    char[] readBuf = new char[102400];
+    int n = input.read(readBuf);
+    while (n >= 0) {
+      s.append(readBuf, 0, n);
+      n = input.read(readBuf);
     }
-
-    /**
-     * Returns the next value from the input.
-     *
-     * @return a {@link JSONObject}, {@link JSONArray}, String, Boolean,
-     * Integer, Long, Double or {@link JSONObject#NULL}.
-     * @throws JSONException if the input is malformed.
-     */
-    public Object nextValue() throws JSONException {
-        int c = nextCleanInternal();
-        switch (c) {
-            case -1:
-                throw syntaxError("End of input");
-
-            case '{':
-                return readObject();
-
-            case '[':
-                return readArray();
-
-            case '\'':
-            case '"':
-                return nextString((char) c);
+    in = s.toString();
+    pos = 0;
+  }
+
+  /**
+   * Returns the next value from the input.
+   *
+   * @return a {@link JSONObject}, {@link JSONArray}, String, Boolean, Integer, Long, Double
or
+   *         {@link JSONObject#NULL}.
+   * @throws JSONException if the input is malformed.
+   */
+  public Object nextValue() throws JSONException {
+    int c = nextCleanInternal();
+    switch (c) {
+      case -1:
+        throw syntaxError("End of input");
+
+      case '{':
+        return readObject();
+
+      case '[':
+        return readArray();
+
+      case '\'':
+      case '"':
+        return nextString((char) c);
+
+      default:
+        pos--;
+        return readLiteral();
+    }
+  }
+
+  private int nextCleanInternal() throws JSONException {
+    while (pos < in.length()) {
+      int c = in.charAt(pos++);
+      switch (c) {
+        case '\t':
+        case ' ':
+        case '\n':
+        case '\r':
+          continue;
+
+        case '/':
+          if (pos == in.length()) {
+            return c;
+          }
+
+          char peek = in.charAt(pos);
+          switch (peek) {
+            case '*':
+              // skip a /* c-style comment */
+              pos++;
+              int commentEnd = in.indexOf("*/", pos);
+              if (commentEnd == -1) {
+                throw syntaxError("Unterminated comment");
+              }
+              pos = commentEnd + 2;
+              continue;
+
+            case '/':
+              // skip a // end-of-line comment
+              pos++;
+              skipToEndOfLine();
+              continue;
 
             default:
-                pos--;
-                return readLiteral();
-        }
+              return c;
+          }
+
+        case '#':
+          /*
+           * Skip a # hash end-of-line comment. The JSON RFC doesn't specify this behavior,
but it's
+           * required to parse existing documents. See http://b/2571423.
+           */
+          skipToEndOfLine();
+          continue;
+
+        default:
+          return c;
+      }
     }
 
-    private int nextCleanInternal() throws JSONException {
-        while (pos < in.length()) {
-            int c = in.charAt(pos++);
-            switch (c) {
-                case '\t':
-                case ' ':
-                case '\n':
-                case '\r':
-                    continue;
-
-                case '/':
-                    if (pos == in.length()) {
-                        return c;
-                    }
-
-                    char peek = in.charAt(pos);
-                    switch (peek) {
-                        case '*':
-                            // skip a /* c-style comment */
-                            pos++;
-                            int commentEnd = in.indexOf("*/", pos);
-                            if (commentEnd == -1) {
-                                throw syntaxError("Unterminated comment");
-                            }
-                            pos = commentEnd + 2;
-                            continue;
-
-                        case '/':
-                            // skip a // end-of-line comment
-                            pos++;
-                            skipToEndOfLine();
-                            continue;
-
-                        default:
-                            return c;
-                    }
-
-                case '#':
-                    /*
-                     * Skip a # hash end-of-line comment. The JSON RFC doesn't
-                     * specify this behavior, but it's required to parse
-                     * existing documents. See http://b/2571423.
-                     */
-                    skipToEndOfLine();
-                    continue;
-
-                default:
-                    return c;
-            }
-        }
-
-        return -1;
+    return -1;
+  }
+
+  /**
+   * Advances the position until after the next newline character. If the line is terminated
by
+   * "\r\n", the '\n' must be consumed as whitespace by the caller.
+   */
+  private void skipToEndOfLine() {
+    for (; pos < in.length(); pos++) {
+      char c = in.charAt(pos);
+      if (c == '\r' || c == '\n') {
+        pos++;
+        break;
+      }
     }
-
-    /**
-     * Advances the position until after the next newline character. If the line
-     * is terminated by "\r\n", the '\n' must be consumed as whitespace by the
-     * caller.
+  }
+
+  /**
+   * Returns the string up to but not including {@code quote}, unescaping any character escape
+   * sequences encountered along the way. The opening quote should have already been read.
This
+   * consumes the closing quote, but does not include it in the returned string.
+   *
+   * @param quote either ' or ".
+   * @return The unescaped string.
+   * @throws JSONException if the string isn't terminated by a closing quote correctly.
+   */
+  public String nextString(char quote) throws JSONException {
+    /*
+     * For strings that are free of escape sequences, we can just extract the result as a
substring
+     * of the input. But if we encounter an escape sequence, we need to use a StringBuilder
to
+     * compose the result.
      */
-    private void skipToEndOfLine() {
-        for (; pos < in.length(); pos++) {
-            char c = in.charAt(pos);
-            if (c == '\r' || c == '\n') {
-                pos++;
-                break;
-            }
+    StringBuilder builder = null;
+
+    /* the index of the first character not yet appended to the builder. */
+    int start = pos;
+
+    while (pos < in.length()) {
+      int c = in.charAt(pos++);
+      if (c == quote) {
+        if (builder == null) {
+          // a new string avoids leaking memory
+          // noinspection RedundantStringConstructorCall
+          return new String(in.substring(start, pos - 1));
+        } else {
+          builder.append(in, start, pos - 1);
+          return builder.toString();
         }
-    }
+      }
 
-    /**
-     * Returns the string up to but not including {@code quote}, unescaping any
-     * character escape sequences encountered along the way. The opening quote
-     * should have already been read. This consumes the closing quote, but does
-     * not include it in the returned string.
-     *
-     * @param quote either ' or ".
-     * @return The unescaped string.
-     * @throws JSONException if the string isn't terminated by a closing quote correctly.
-     */
-    public String nextString(char quote) throws JSONException {
-        /*
-         * For strings that are free of escape sequences, we can just extract
-         * the result as a substring of the input. But if we encounter an escape
-         * sequence, we need to use a StringBuilder to compose the result.
-         */
-        StringBuilder builder = null;
-
-        /* the index of the first character not yet appended to the builder. */
-        int start = pos;
-
-        while (pos < in.length()) {
-            int c = in.charAt(pos++);
-            if (c == quote) {
-                if (builder == null) {
-                    // a new string avoids leaking memory
-                    //noinspection RedundantStringConstructorCall
-                    return new String(in.substring(start, pos - 1));
-                } else {
-                    builder.append(in, start, pos - 1);
-                    return builder.toString();
-                }
-            }
-
-            if (c == '\\') {
-                if (pos == in.length()) {
-                    throw syntaxError("Unterminated escape sequence");
-                }
-                if (builder == null) {
-                    builder = new StringBuilder();
-                }
-                builder.append(in, start, pos - 1);
-                builder.append(readEscapeCharacter());
-                start = pos;
-            }
+      if (c == '\\') {
+        if (pos == in.length()) {
+          throw syntaxError("Unterminated escape sequence");
         }
-
-        throw syntaxError("Unterminated string");
-    }
-
-    /**
-     * Unescapes the character identified by the character or characters that
-     * immediately follow a backslash. The backslash '\' should have already
-     * been read. This supports both unicode escapes "u000A" and two-character
-     * escapes "\n".
-     */
-    private char readEscapeCharacter() throws JSONException {
-        char escaped = in.charAt(pos++);
-        switch (escaped) {
-            case 'u':
-                if (pos + 4 > in.length()) {
-                    throw syntaxError("Unterminated escape sequence");
-                }
-                String hex = in.substring(pos, pos + 4);
-                pos += 4;
-                try {
-                    return (char) Integer.parseInt(hex, 16);
-                } catch (NumberFormatException nfe) {
-                    throw syntaxError("Invalid escape sequence: " + hex);
-                }
-
-            case 't':
-                return '\t';
-
-            case 'b':
-                return '\b';
-
-            case 'n':
-                return '\n';
-
-            case 'r':
-                return '\r';
-
-            case 'f':
-                return '\f';
-
-            case '\'':
-            case '"':
-            case '\\':
-            default:
-                return escaped;
+        if (builder == null) {
+          builder = new StringBuilder();
         }
+        builder.append(in, start, pos - 1);
+        builder.append(readEscapeCharacter());
+        start = pos;
+      }
     }
 
-    /**
-     * Reads a null, boolean, numeric or unquoted string literal value. Numeric
-     * values will be returned as an Integer, Long, or Double, in that order of
-     * preference.
-     */
-    private Object readLiteral() throws JSONException {
-        String literal = nextToInternal("{}[]/\\:,=;# \t\f");
-
-        if (literal.length() == 0) {
-            throw syntaxError("Expected literal value");
-        } else if ("null".equalsIgnoreCase(literal)) {
-            return JSONObject.NULL;
-        } else if ("true".equalsIgnoreCase(literal)) {
-            return Boolean.TRUE;
-        } else if ("false".equalsIgnoreCase(literal)) {
-            return Boolean.FALSE;
-        }
-
-        /* try to parse as an integral type... */
-        if (literal.indexOf('.') == -1) {
-            int base = 10;
-            String number = literal;
-            if (number.startsWith("0x") || number.startsWith("0X")) {
-                number = number.substring(2);
-                base = 16;
-            } else if (number.startsWith("0") && number.length() > 1) {
-                number = number.substring(1);
-                base = 8;
-            }
-            try {
-                long longValue = Long.parseLong(number, base);
-                if (longValue <= Integer.MAX_VALUE && longValue >= Integer.MIN_VALUE)
{
-                    return (int) longValue;
-                } else {
-                    return longValue;
-                }
-            } catch (NumberFormatException e) {
-                /*
-                 * This only happens for integral numbers greater than
-                 * Long.MAX_VALUE, numbers in exponential form (5e-10) and
-                 * unquoted strings. Fall through to try floating point.
-                 */
-            }
+    throw syntaxError("Unterminated string");
+  }
+
+  /**
+   * Unescapes the character identified by the character or characters that immediately follow
a
+   * backslash. The backslash '\' should have already been read. This supports both unicode
escapes
+   * "u000A" and two-character escapes "\n".
+   */
+  private char readEscapeCharacter() throws JSONException {
+    char escaped = in.charAt(pos++);
+    switch (escaped) {
+      case 'u':
+        if (pos + 4 > in.length()) {
+          throw syntaxError("Unterminated escape sequence");
         }
-
-        /* ...next try to parse as a floating point... */
+        String hex = in.substring(pos, pos + 4);
+        pos += 4;
         try {
-            return Double.valueOf(literal);
-        } catch (NumberFormatException ignored) {
+          return (char) Integer.parseInt(hex, 16);
+        } catch (NumberFormatException nfe) {
+          throw syntaxError("Invalid escape sequence: " + hex);
         }
 
-        /* ... finally give up. We have an unquoted string */
-        //noinspection RedundantStringConstructorCall
-        return new String(literal); // a new string avoids leaking memory
-    }
+      case 't':
+        return '\t';
 
-    /**
-     * Returns the string up to but not including any of the given characters or
-     * a newline character. This does not consume the excluded character.
-     */
-    private String nextToInternal(String excluded) {
-        int start = pos;
-        for (; pos < in.length(); pos++) {
-            char c = in.charAt(pos);
-            if (c == '\r' || c == '\n' || excluded.indexOf(c) != -1) {
-                return in.substring(start, pos);
-            }
-        }
-        return in.substring(start);
-    }
+      case 'b':
+        return '\b';
 
-    /**
-     * Reads a sequence of key/value pairs and the trailing closing brace '}' of
-     * an object. The opening brace '{' should have already been read.
-     */
-    private JSONObject readObject() throws JSONException {
-        JSONObject result = new JSONObject();
-
-        /* Peek to see if this is the empty object. */
-        int first = nextCleanInternal();
-        if (first == '}') {
-            return result;
-        } else if (first != -1) {
-            pos--;
-        }
+      case 'n':
+        return '\n';
 
-        while (true) {
-            Object name = nextValue();
-            if (!(name instanceof String)) {
-                if (name == null) {
-                    throw syntaxError("Names cannot be null");
-                } else {
-                    throw syntaxError("Names must be strings, but " + name
-                            + " is of type " + name.getClass().getName());
-                }
-            }
-
-            /*
-             * Expect the name/value separator to be either a colon ':', an
-             * equals sign '=', or an arrow "=>". The last two are bogus but we
-             * include them because that's what the original implementation did.
-             */
-            int separator = nextCleanInternal();
-            if (separator != ':' && separator != '=') {
-                throw syntaxError("Expected ':' after " + name);
-            }
-            if (pos < in.length() && in.charAt(pos) == '>') {
-                pos++;
-            }
-
-            result.put((String) name, nextValue());
-
-            switch (nextCleanInternal()) {
-                case '}':
-                    return result;
-                case ';':
-                case ',':
-                    continue;
-                default:
-                    throw syntaxError("Unterminated object");
-            }
-        }
-    }
+      case 'r':
+        return '\r';
 
-    /**
-     * Reads a sequence of values and the trailing closing brace ']' of an
-     * array. The opening brace '[' should have already been read. Note that
-     * "[]" yields an empty array, but "[,]" returns a two-element array
-     * equivalent to "[null,null]".
-     */
-    private JSONArray readArray() throws JSONException {
-        JSONArray result = new JSONArray();
-
-        /* to cover input that ends with ",]". */
-        boolean hasTrailingSeparator = false;
-
-        while (true) {
-            switch (nextCleanInternal()) {
-                case -1:
-                    throw syntaxError("Unterminated array");
-                case ']':
-                    if (hasTrailingSeparator) {
-                        result.put(null);
-                    }
-                    return result;
-                case ',':
-                case ';':
-                    /* A separator without a value first means "null". */
-                    result.put(null);
-                    hasTrailingSeparator = true;
-                    continue;
-                default:
-                    pos--;
-            }
-
-            result.put(nextValue());
-
-            switch (nextCleanInternal()) {
-                case ']':
-                    return result;
-                case ',':
-                case ';':
-                    hasTrailingSeparator = true;
-                    continue;
-                default:
-                    throw syntaxError("Unterminated array");
-            }
-        }
-    }
+      case 'f':
+        return '\f';
 
-    /**
-     * Returns an exception containing the given message plus the current
-     * position and the entire input string.
-     *
-     * @param message The message we want to include.
-     * @return An exception that we can throw.
-     */
-    public JSONException syntaxError(String message) {
-        return new JSONException(message + this);
+      case '\'':
+      case '"':
+      case '\\':
+      default:
+        return escaped;
     }
-
-    /**
-     * Returns the current position and the entire input string.
-     */
-    @Override
-    public String toString() {
-        // consistent with the original implementation
-        return " at character " + pos + " of " + in;
+  }
+
+  /**
+   * Reads a null, boolean, numeric or unquoted string literal value. Numeric values will
be
+   * returned as an Integer, Long, or Double, in that order of preference.
+   */
+  private Object readLiteral() throws JSONException {
+    String literal = nextToInternal("{}[]/\\:,=;# \t\f");
+
+    if (literal.length() == 0) {
+      throw syntaxError("Expected literal value");
+    } else if ("null".equalsIgnoreCase(literal)) {
+      return JSONObject.NULL;
+    } else if ("true".equalsIgnoreCase(literal)) {
+      return Boolean.TRUE;
+    } else if ("false".equalsIgnoreCase(literal)) {
+      return Boolean.FALSE;
     }
 
-    /*
-     * Legacy APIs.
-     *
-     * None of the methods below are on the critical path of parsing JSON
-     * documents. They exist only because they were exposed by the original
-     * implementation and may be used by some clients.
-     */
-
-    /**
-     * Returns true until the input has been exhausted.
-     *
-     * @return true if more input exists.
-     */
-    public boolean more() {
-        return pos < in.length();
+    /* try to parse as an integral type... */
+    if (literal.indexOf('.') == -1) {
+      int base = 10;
+      String number = literal;
+      if (number.startsWith("0x") || number.startsWith("0X")) {
+        number = number.substring(2);
+        base = 16;
+      } else if (number.startsWith("0") && number.length() > 1) {
+        number = number.substring(1);
+        base = 8;
+      }
+      try {
+        long longValue = Long.parseLong(number, base);
+        if (longValue <= Integer.MAX_VALUE && longValue >= Integer.MIN_VALUE)
{
+          return (int) longValue;
+        } else {
+          return longValue;
+        }
+      } catch (NumberFormatException e) {
+        /*
+         * This only happens for integral numbers greater than Long.MAX_VALUE, numbers in
+         * exponential form (5e-10) and unquoted strings. Fall through to try floating point.
+         */
+      }
     }
 
-    /**
-     * Returns the next available character, or the null character '\0' if all
-     * input has been exhausted. The return value of this method is ambiguous
-     * for JSON strings that contain the character '\0'.
-     *
-     * @return the next character.
-     */
-    public char next() {
-        return pos < in.length() ? in.charAt(pos++) : '\0';
+    /* ...next try to parse as a floating point... */
+    try {
+      return Double.valueOf(literal);
+    } catch (NumberFormatException ignored) {
     }
 
-    /**
-     * Returns the next available character if it equals {@code c}. Otherwise an
-     * exception is thrown.
-     *
-     * @param c The character we are looking for.
-     * @return the next character.
-     * @throws JSONException If the next character isn't {@code c}
-     */
-    public char next(char c) throws JSONException {
-        char result = next();
-        if (result != c) {
-            throw syntaxError("Expected " + c + " but was " + result);
-        }
-        return result;
+    /* ... finally give up. We have an unquoted string */
+    // noinspection RedundantStringConstructorCall
+    return new String(literal); // a new string avoids leaking memory
+  }
+
+  /**
+   * Returns the string up to but not including any of the given characters or a newline
character.
+   * This does not consume the excluded character.
+   */
+  private String nextToInternal(String excluded) {
+    int start = pos;
+    for (; pos < in.length(); pos++) {
+      char c = in.charAt(pos);
+      if (c == '\r' || c == '\n' || excluded.indexOf(c) != -1) {
+        return in.substring(start, pos);
+      }
     }
-
-    /**
-     * Returns the next character that is not whitespace and does not belong to
-     * a comment. If the input is exhausted before such a character can be
-     * found, the null character '\0' is returned. The return value of this
-     * method is ambiguous for JSON strings that contain the character '\0'.
-     *
-     * @return The next non-whitespace character.
-     * @throws JSONException Should not be possible.
-     */
-    public char nextClean() throws JSONException {
-        int nextCleanInt = nextCleanInternal();
-        return nextCleanInt == -1 ? '\0' : (char) nextCleanInt;
+    return in.substring(start);
+  }
+
+  /**
+   * Reads a sequence of key/value pairs and the trailing closing brace '}' of an object.
The
+   * opening brace '{' should have already been read.
+   */
+  private JSONObject readObject() throws JSONException {
+    JSONObject result = new JSONObject();
+
+    /* Peek to see if this is the empty object. */
+    int first = nextCleanInternal();
+    if (first == '}') {
+      return result;
+    } else if (first != -1) {
+      pos--;
     }
 
-    /**
-     * Returns the next {@code length} characters of the input.
-     *
-     * <p>The returned string shares its backing character array with this
-     * tokener's input string. If a reference to the returned string may be held
-     * indefinitely, you should use {@code new String(result)} to copy it first
-     * to avoid memory leaks.
-     *
-     * @param length The desired number of characters to return.
-     * @return The next few characters.
-     * @throws JSONException if the remaining input is not long enough to
-     *                       satisfy this request.
-     */
-    public String next(int length) throws JSONException {
-        if (pos + length > in.length()) {
-            throw syntaxError(length + " is out of bounds");
+    while (true) {
+      Object name = nextValue();
+      if (!(name instanceof String)) {
+        if (name == null) {
+          throw syntaxError("Names cannot be null");
+        } else {
+          throw syntaxError(
+              "Names must be strings, but " + name + " is of type " + name.getClass().getName());
         }
-        String result = in.substring(pos, pos + length);
-        pos += length;
-        return result;
+      }
+
+      /*
+       * Expect the name/value separator to be either a colon ':', an equals sign '=', or
an arrow
+       * "=>". The last two are bogus but we include them because that's what the original
+       * implementation did.
+       */
+      int separator = nextCleanInternal();
+      if (separator != ':' && separator != '=') {
+        throw syntaxError("Expected ':' after " + name);
+      }
+      if (pos < in.length() && in.charAt(pos) == '>') {
+        pos++;
+      }
+
+      result.put((String) name, nextValue());
+
+      switch (nextCleanInternal()) {
+        case '}':
+          return result;
+        case ';':
+        case ',':
+          continue;
+        default:
+          throw syntaxError("Unterminated object");
+      }
     }
-
-    /**
-     * Returns the {@link String#trim trimmed} string holding the characters up
-     * to but not including the first of:
-     * <ul>
-     * <li>any character in {@code excluded}
-     * <li>a newline character '\n'
-     * <li>a carriage return '\r'
-     * </ul>
-     *
-     * <p>The returned string shares its backing character array with this
-     * tokener's input string. If a reference to the returned string may be held
-     * indefinitely, you should use {@code new String(result)} to copy it first
-     * to avoid memory leaks.
-     *
-     * @param excluded The limiting string where the search should stop.
-     * @return a possibly-empty string
-     */
-    public String nextTo(String excluded) {
-        if (excluded == null) {
-            throw new NullPointerException("excluded == null");
-        }
-        return nextToInternal(excluded).trim();
+  }
+
+  /**
+   * Reads a sequence of values and the trailing closing brace ']' of an array. The opening
brace
+   * '[' should have already been read. Note that "[]" yields an empty array, but "[,]" returns
a
+   * two-element array equivalent to "[null,null]".
+   */
+  private JSONArray readArray() throws JSONException {
+    JSONArray result = new JSONArray();
+
+    /* to cover input that ends with ",]". */
+    boolean hasTrailingSeparator = false;
+
+    while (true) {
+      switch (nextCleanInternal()) {
+        case -1:
+          throw syntaxError("Unterminated array");
+        case ']':
+          if (hasTrailingSeparator) {
+            result.put(null);
+          }
+          return result;
+        case ',':
+        case ';':
+          /* A separator without a value first means "null". */
+          result.put(null);
+          hasTrailingSeparator = true;
+          continue;
+        default:
+          pos--;
+      }
+
+      result.put(nextValue());
+
+      switch (nextCleanInternal()) {
+        case ']':
+          return result;
+        case ',':
+        case ';':
+          hasTrailingSeparator = true;
+          continue;
+        default:
+          throw syntaxError("Unterminated array");
+      }
     }
-
-    /**
-     * Equivalent to {@code nextTo(String.valueOf(excluded))}.
-     *
-     * @param excluded The limiting character.
-     * @return a possibly-empty string
-     */
-    public String nextTo(char excluded) {
-        return nextToInternal(String.valueOf(excluded)).trim();
+  }
+
+  /**
+   * Returns an exception containing the given message plus the current position and the
entire
+   * input string.
+   *
+   * @param message The message we want to include.
+   * @return An exception that we can throw.
+   */
+  public JSONException syntaxError(String message) {
+    return new JSONException(message + this);
+  }
+
+  /**
+   * Returns the current position and the entire input string.
+   */
+  @Override
+  public String toString() {
+    // consistent with the original implementation
+    return " at character " + pos + " of " + in;
+  }
+
+  /*
+   * Legacy APIs.
+   *
+   * None of the methods below are on the critical path of parsing JSON documents. They exist
only
+   * because they were exposed by the original implementation and may be used by some clients.
+   */
+
+  /**
+   * Returns true until the input has been exhausted.
+   *
+   * @return true if more input exists.
+   */
+  public boolean more() {
+    return pos < in.length();
+  }
+
+  /**
+   * Returns the next available character, or the null character '\0' if all input has been
+   * exhausted. The return value of this method is ambiguous for JSON strings that contain
the
+   * character '\0'.
+   *
+   * @return the next character.
+   */
+  public char next() {
+    return pos < in.length() ? in.charAt(pos++) : '\0';
+  }
+
+  /**
+   * Returns the next available character if it equals {@code c}. Otherwise an exception
is thrown.
+   *
+   * @param c The character we are looking for.
+   * @return the next character.
+   * @throws JSONException If the next character isn't {@code c}
+   */
+  public char next(char c) throws JSONException {
+    char result = next();
+    if (result != c) {
+      throw syntaxError("Expected " + c + " but was " + result);
     }
-
-    /**
-     * Advances past all input up to and including the next occurrence of
-     * {@code thru}. If the remaining input doesn't contain {@code thru}, the
-     * input is exhausted.
-     *
-     * @param thru The string to skip over.
-     */
-    public void skipPast(String thru) {
-        int thruStart = in.indexOf(thru, pos);
-        pos = thruStart == -1 ? in.length() : (thruStart + thru.length());
+    return result;
+  }
+
+  /**
+   * Returns the next character that is not whitespace and does not belong to a comment.
If the
+   * input is exhausted before such a character can be found, the null character '\0' is
returned.
+   * The return value of this method is ambiguous for JSON strings that contain the character
'\0'.
+   *
+   * @return The next non-whitespace character.
+   * @throws JSONException Should not be possible.
+   */
+  public char nextClean() throws JSONException {
+    int nextCleanInt = nextCleanInternal();
+    return nextCleanInt == -1 ? '\0' : (char) nextCleanInt;
+  }
+
+  /**
+   * Returns the next {@code length} characters of the input.
+   *
+   * <p>
+   * The returned string shares its backing character array with this tokener's input string.
If a
+   * reference to the returned string may be held indefinitely, you should use
+   * {@code new String(result)} to copy it first to avoid memory leaks.
+   *
+   * @param length The desired number of characters to return.
+   * @return The next few characters.
+   * @throws JSONException if the remaining input is not long enough to satisfy this request.
+   */
+  public String next(int length) throws JSONException {
+    if (pos + length > in.length()) {
+      throw syntaxError(length + " is out of bounds");
     }
-
-    /**
-     * Advances past all input up to but not including the next occurrence of
-     * {@code to}. If the remaining input doesn't contain {@code to}, the input
-     * is unchanged.
-     *
-     * @param to The character we want to skip to.
-     * @return The value of {@code to} or null.
-     */
-    public char skipTo(char to) {
-        int index = in.indexOf(to, pos);
-        if (index != -1) {
-            pos = index;
-            return to;
-        } else {
-            return '\0';
-        }
+    String result = in.substring(pos, pos + length);
+    pos += length;
+    return result;
+  }
+
+  /**
+   * Returns the {@link String#trim trimmed} string holding the characters up to but not
including
+   * the first of:
+   * <ul>
+   * <li>any character in {@code excluded}
+   * <li>a newline character '\n'
+   * <li>a carriage return '\r'
+   * </ul>
+   *
+   * <p>
+   * The returned string shares its backing character array with this tokener's input string.
If a
+   * reference to the returned string may be held indefinitely, you should use
+   * {@code new String(result)} to copy it first to avoid memory leaks.
+   *
+   * @param excluded The limiting string where the search should stop.
+   * @return a possibly-empty string
+   */
+  public String nextTo(String excluded) {
+    if (excluded == null) {
+      throw new NullPointerException("excluded == null");
     }
-
-    /**
-     * Unreads the most recent character of input. If no input characters have
-     * been read, the input is unchanged.
-     */
-    public void back() {
-        if (--pos == -1) {
-            pos = 0;
-        }
+    return nextToInternal(excluded).trim();
+  }
+
+  /**
+   * Equivalent to {@code nextTo(String.valueOf(excluded))}.
+   *
+   * @param excluded The limiting character.
+   * @return a possibly-empty string
+   */
+  public String nextTo(char excluded) {
+    return nextToInternal(String.valueOf(excluded)).trim();
+  }
+
+  /**
+   * Advances past all input up to and including the next occurrence of {@code thru}. If
the
+   * remaining input doesn't contain {@code thru}, the input is exhausted.
+   *
+   * @param thru The string to skip over.
+   */
+  public void skipPast(String thru) {
+    int thruStart = in.indexOf(thru, pos);
+    pos = thruStart == -1 ? in.length() : (thruStart + thru.length());
+  }
+
+  /**
+   * Advances past all input up to but not including the next occurrence of {@code to}. If
the
+   * remaining input doesn't contain {@code to}, the input is unchanged.
+   *
+   * @param to The character we want to skip to.
+   * @return The value of {@code to} or null.
+   */
+  public char skipTo(char to) {
+    int index = in.indexOf(to, pos);
+    if (index != -1) {
+      pos = index;
+      return to;
+    } else {
+      return '\0';
     }
-
-    /**
-     * Returns the integer [0..15] value for the given hex character, or -1
-     * for non-hex input.
-     *
-     * @param hex a character in the ranges [0-9], [A-F] or [a-f]. Any other
-     *            character will yield a -1 result.
-     * @return The decoded integer.
-     */
-    public static int dehexchar(char hex) {
-        if (hex >= '0' && hex <= '9') {
-            return hex - '0';
-        } else if (hex >= 'A' && hex <= 'F') {
-            return hex - 'A' + 10;
-        } else if (hex >= 'a' && hex <= 'f') {
-            return hex - 'a' + 10;
-        } else {
-            return -1;
-        }
+  }
+
+  /**
+   * Unreads the most recent character of input. If no input characters have been read, the
input is
+   * unchanged.
+   */
+  public void back() {
+    if (--pos == -1) {
+      pos = 0;
+    }
+  }
+
+  /**
+   * Returns the integer [0..15] value for the given hex character, or -1 for non-hex input.
+   *
+   * @param hex a character in the ranges [0-9], [A-F] or [a-f]. Any other character will
yield a -1
+   *        result.
+   * @return The decoded integer.
+   */
+  public static int dehexchar(char hex) {
+    if (hex >= '0' && hex <= '9') {
+      return hex - '0';
+    } else if (hex >= 'A' && hex <= 'F') {
+      return hex - 'A' + 10;
+    } else if (hex >= 'a' && hex <= 'f') {
+      return hex - 'a' + 10;
+    } else {
+      return -1;
     }
+  }
 }


Mime
View raw message