151a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson/*
251a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson * Copyright (C) 2010 The Android Open Source Project
351a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson *
451a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson * Licensed under the Apache License, Version 2.0 (the "License");
551a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson * you may not use this file except in compliance with the License.
651a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson * You may obtain a copy of the License at
751a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson *
851a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson *      http://www.apache.org/licenses/LICENSE-2.0
951a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson *
1051a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson * Unless required by applicable law or agreed to in writing, software
1151a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson * distributed under the License is distributed on an "AS IS" BASIS,
1251a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1351a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson * See the License for the specific language governing permissions and
1451a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson * limitations under the License.
1551a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson */
1651a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson
1751a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilsonpackage org.json;
1851a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson
1951a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilsonimport java.util.ArrayList;
2051a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilsonimport java.util.Arrays;
2151a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilsonimport java.util.List;
2251a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson
2351a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson// Note: this class was written without inspecting the non-free org.json sourcecode.
2451a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson
2551a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson/**
2695cf9534436835291927cb4bcb36842731319da8Jesse Wilson * Implements {@link JSONObject#toString} and {@link JSONArray#toString}. Most
27c78ca6700830460f671652ce3efd4cc2c0cf7a6aJesse Wilson * application developers should use those methods directly and disregard this
28c78ca6700830460f671652ce3efd4cc2c0cf7a6aJesse Wilson * API. For example:<pre>
29c78ca6700830460f671652ce3efd4cc2c0cf7a6aJesse Wilson * JSONObject object = ...
30c78ca6700830460f671652ce3efd4cc2c0cf7a6aJesse Wilson * String json = object.toString();</pre>
3151a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson *
32c78ca6700830460f671652ce3efd4cc2c0cf7a6aJesse Wilson * <p>Stringers only encode well-formed JSON strings. In particular:
33c78ca6700830460f671652ce3efd4cc2c0cf7a6aJesse Wilson * <ul>
34c78ca6700830460f671652ce3efd4cc2c0cf7a6aJesse Wilson *   <li>The stringer must have exactly one top-level array or object.
3595cf9534436835291927cb4bcb36842731319da8Jesse Wilson *   <li>Lexical scopes must be balanced: every call to {@link #array} must
3695cf9534436835291927cb4bcb36842731319da8Jesse Wilson *       have a matching call to {@link #endArray} and every call to {@link
3795cf9534436835291927cb4bcb36842731319da8Jesse Wilson *       #object} must have a matching call to {@link #endObject}.
38c78ca6700830460f671652ce3efd4cc2c0cf7a6aJesse Wilson *   <li>Arrays may not contain keys (property names).
39c78ca6700830460f671652ce3efd4cc2c0cf7a6aJesse Wilson *   <li>Objects must alternate keys (property names) and values.
40c78ca6700830460f671652ce3efd4cc2c0cf7a6aJesse Wilson *   <li>Values are inserted with either literal {@link #value(Object) value}
41c78ca6700830460f671652ce3efd4cc2c0cf7a6aJesse Wilson *       calls, or by nesting arrays or objects.
42c78ca6700830460f671652ce3efd4cc2c0cf7a6aJesse Wilson * </ul>
43c78ca6700830460f671652ce3efd4cc2c0cf7a6aJesse Wilson * Calls that would result in a malformed JSON string will fail with a
44c78ca6700830460f671652ce3efd4cc2c0cf7a6aJesse Wilson * {@link JSONException}.
45c78ca6700830460f671652ce3efd4cc2c0cf7a6aJesse Wilson *
46c78ca6700830460f671652ce3efd4cc2c0cf7a6aJesse Wilson * <p>This class provides no facility for pretty-printing (ie. indenting)
47c78ca6700830460f671652ce3efd4cc2c0cf7a6aJesse Wilson * output. To encode indented output, use {@link JSONObject#toString(int)} or
48c78ca6700830460f671652ce3efd4cc2c0cf7a6aJesse Wilson * {@link JSONArray#toString(int)}.
49c78ca6700830460f671652ce3efd4cc2c0cf7a6aJesse Wilson *
50c78ca6700830460f671652ce3efd4cc2c0cf7a6aJesse Wilson * <p>Some implementations of the API support at most 20 levels of nesting.
51c78ca6700830460f671652ce3efd4cc2c0cf7a6aJesse Wilson * Attempts to create more than 20 levels of nesting may fail with a {@link
52c78ca6700830460f671652ce3efd4cc2c0cf7a6aJesse Wilson * JSONException}.
53c78ca6700830460f671652ce3efd4cc2c0cf7a6aJesse Wilson *
54c78ca6700830460f671652ce3efd4cc2c0cf7a6aJesse Wilson * <p>Each stringer may be used to encode a single top level value. Instances of
55c78ca6700830460f671652ce3efd4cc2c0cf7a6aJesse Wilson * this class are not thread safe. Although this class is nonfinal, it was not
56c78ca6700830460f671652ce3efd4cc2c0cf7a6aJesse Wilson * designed for inheritance and should not be subclassed. In particular,
57661054f5a2f7f8f5f3ceffb97e803211b546e7fcJesse Wilson * self-use by overrideable methods is not specified. See <i>Effective Java</i>
58c78ca6700830460f671652ce3efd4cc2c0cf7a6aJesse Wilson * Item 17, "Design and Document or inheritance or else prohibit it" for further
59c78ca6700830460f671652ce3efd4cc2c0cf7a6aJesse Wilson * information.
6051a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson */
6151a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilsonpublic class JSONStringer {
6251a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson
6351a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson    /** The output data, containing at most one top-level array or object. */
6451a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson    final StringBuilder out = new StringBuilder();
6551a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson
6651a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson    /**
6751a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson     * Lexical scoping elements within this stringer, necessary to insert the
6851a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson     * appropriate separator characters (ie. commas and colons) and to detect
6951a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson     * nesting errors.
7051a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson     */
7151a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson    enum Scope {
7251a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson
7351a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson        /**
7451a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson         * An array with no elements requires no separators or newlines before
7551a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson         * it is closed.
7651a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson         */
7751a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson        EMPTY_ARRAY,
7851a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson
7951a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson        /**
8051a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson         * A array with at least one value requires a comma and newline before
8151a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson         * the next element.
8251a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson         */
8351a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson        NONEMPTY_ARRAY,
8451a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson
8551a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson        /**
8651a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson         * An object with no keys or values requires no separators or newlines
8751a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson         * before it is closed.
8851a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson         */
8951a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson        EMPTY_OBJECT,
9051a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson
9151a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson        /**
9251a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson         * An object whose most recent element is a key. The next element must
9351a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson         * be a value.
9451a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson         */
9551a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson        DANGLING_KEY,
9651a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson
9751a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson        /**
9851a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson         * An object with at least one name/value pair requires a comma and
9951a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson         * newline before the next element.
10051a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson         */
10151a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson        NONEMPTY_OBJECT,
10251a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson
10351a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson        /**
10451a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson         * A special bracketless array needed by JSONStringer.join() and
10551a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson         * JSONObject.quote() only. Not used for JSON encoding.
10651a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson         */
10751a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson        NULL,
10851a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson    }
10951a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson
11051a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson    /**
11151a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson     * Unlike the original implementation, this stack isn't limited to 20
11251a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson     * levels of nesting.
11351a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson     */
11451a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson    private final List<Scope> stack = new ArrayList<Scope>();
11551a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson
11651a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson    /**
11751a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson     * A string containing a full set of spaces for a single level of
11851a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson     * indentation, or null for no pretty printing.
11951a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson     */
12051a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson    private final String indent;
12151a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson
12251a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson    public JSONStringer() {
12351a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson        indent = null;
12451a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson    }
12551a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson
12651a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson    JSONStringer(int indentSpaces) {
12751a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson        char[] indentChars = new char[indentSpaces];
12851a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson        Arrays.fill(indentChars, ' ');
12951a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson        indent = new String(indentChars);
13051a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson    }
13151a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson
132c78ca6700830460f671652ce3efd4cc2c0cf7a6aJesse Wilson    /**
133c78ca6700830460f671652ce3efd4cc2c0cf7a6aJesse Wilson     * Begins encoding a new array. Each call to this method must be paired with
13495cf9534436835291927cb4bcb36842731319da8Jesse Wilson     * a call to {@link #endArray}.
135c78ca6700830460f671652ce3efd4cc2c0cf7a6aJesse Wilson     *
136c78ca6700830460f671652ce3efd4cc2c0cf7a6aJesse Wilson     * @return this stringer.
137c78ca6700830460f671652ce3efd4cc2c0cf7a6aJesse Wilson     */
13851a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson    public JSONStringer array() throws JSONException {
13951a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson        return open(Scope.EMPTY_ARRAY, "[");
14051a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson    }
14151a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson
142c78ca6700830460f671652ce3efd4cc2c0cf7a6aJesse Wilson    /**
143c78ca6700830460f671652ce3efd4cc2c0cf7a6aJesse Wilson     * Ends encoding the current array.
144c78ca6700830460f671652ce3efd4cc2c0cf7a6aJesse Wilson     *
145c78ca6700830460f671652ce3efd4cc2c0cf7a6aJesse Wilson     * @return this stringer.
146c78ca6700830460f671652ce3efd4cc2c0cf7a6aJesse Wilson     */
14751a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson    public JSONStringer endArray() throws JSONException {
14851a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson        return close(Scope.EMPTY_ARRAY, Scope.NONEMPTY_ARRAY, "]");
14951a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson    }
15051a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson
151c78ca6700830460f671652ce3efd4cc2c0cf7a6aJesse Wilson    /**
152c78ca6700830460f671652ce3efd4cc2c0cf7a6aJesse Wilson     * Begins encoding a new object. Each call to this method must be paired
15395cf9534436835291927cb4bcb36842731319da8Jesse Wilson     * with a call to {@link #endObject}.
154c78ca6700830460f671652ce3efd4cc2c0cf7a6aJesse Wilson     *
155c78ca6700830460f671652ce3efd4cc2c0cf7a6aJesse Wilson     * @return this stringer.
156c78ca6700830460f671652ce3efd4cc2c0cf7a6aJesse Wilson     */
15751a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson    public JSONStringer object() throws JSONException {
15851a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson        return open(Scope.EMPTY_OBJECT, "{");
15951a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson    }
16051a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson
161c78ca6700830460f671652ce3efd4cc2c0cf7a6aJesse Wilson    /**
162c78ca6700830460f671652ce3efd4cc2c0cf7a6aJesse Wilson     * Ends encoding the current object.
163c78ca6700830460f671652ce3efd4cc2c0cf7a6aJesse Wilson     *
164c78ca6700830460f671652ce3efd4cc2c0cf7a6aJesse Wilson     * @return this stringer.
165c78ca6700830460f671652ce3efd4cc2c0cf7a6aJesse Wilson     */
16651a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson    public JSONStringer endObject() throws JSONException {
16751a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson        return close(Scope.EMPTY_OBJECT, Scope.NONEMPTY_OBJECT, "}");
16851a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson    }
16951a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson
17051a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson    /**
17151a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson     * Enters a new scope by appending any necessary whitespace and the given
17251a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson     * bracket.
17351a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson     */
17451a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson    JSONStringer open(Scope empty, String openBracket) throws JSONException {
17551a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson        if (stack.isEmpty() && out.length() > 0) {
17651a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson            throw new JSONException("Nesting problem: multiple top-level roots");
17751a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson        }
17851a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson        beforeValue();
17951a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson        stack.add(empty);
18051a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson        out.append(openBracket);
18151a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson        return this;
18251a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson    }
18351a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson
18451a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson    /**
18551a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson     * Closes the current scope by appending any necessary whitespace and the
18651a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson     * given bracket.
18751a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson     */
18851a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson    JSONStringer close(Scope empty, Scope nonempty, String closeBracket) throws JSONException {
18951a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson        Scope context = peek();
19051a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson        if (context != nonempty && context != empty) {
19151a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson            throw new JSONException("Nesting problem");
19251a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson        }
19351a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson
19451a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson        stack.remove(stack.size() - 1);
19551a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson        if (context == nonempty) {
19651a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson            newline();
19751a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson        }
19851a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson        out.append(closeBracket);
19951a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson        return this;
20051a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson    }
20151a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson
20251a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson    /**
20351a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson     * Returns the value on the top of the stack.
20451a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson     */
20551a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson    private Scope peek() throws JSONException {
20651a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson        if (stack.isEmpty()) {
20751a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson            throw new JSONException("Nesting problem");
20851a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson        }
20951a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson        return stack.get(stack.size() - 1);
21051a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson    }
21151a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson
21251a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson    /**
21351a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson     * Replace the value on the top of the stack with the given value.
21451a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson     */
21551a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson    private void replaceTop(Scope topOfStack) {
21651a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson        stack.set(stack.size() - 1, topOfStack);
21751a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson    }
21851a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson
219c78ca6700830460f671652ce3efd4cc2c0cf7a6aJesse Wilson    /**
220c78ca6700830460f671652ce3efd4cc2c0cf7a6aJesse Wilson     * Encodes {@code value}.
221c78ca6700830460f671652ce3efd4cc2c0cf7a6aJesse Wilson     *
222c78ca6700830460f671652ce3efd4cc2c0cf7a6aJesse Wilson     * @param value a {@link JSONObject}, {@link JSONArray}, String, Boolean,
223c78ca6700830460f671652ce3efd4cc2c0cf7a6aJesse Wilson     *     Integer, Long, Double or null. May not be {@link Double#isNaN() NaNs}
224c78ca6700830460f671652ce3efd4cc2c0cf7a6aJesse Wilson     *     or {@link Double#isInfinite() infinities}.
225c78ca6700830460f671652ce3efd4cc2c0cf7a6aJesse Wilson     * @return this stringer.
226c78ca6700830460f671652ce3efd4cc2c0cf7a6aJesse Wilson     */
22751a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson    public JSONStringer value(Object value) throws JSONException {
22851a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson        if (stack.isEmpty()) {
22951a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson            throw new JSONException("Nesting problem");
23051a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson        }
23151a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson
23251a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson        if (value instanceof JSONArray) {
23351a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson            ((JSONArray) value).writeTo(this);
23451a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson            return this;
23551a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson
23651a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson        } else if (value instanceof JSONObject) {
23751a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson            ((JSONObject) value).writeTo(this);
23851a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson            return this;
23951a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson        }
24051a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson
24151a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson        beforeValue();
24251a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson
24351a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson        if (value == null
24451a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson                || value instanceof Boolean
24551a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson                || value == JSONObject.NULL) {
24651a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson            out.append(value);
24751a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson
24851a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson        } else if (value instanceof Number) {
24951a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson            out.append(JSONObject.numberToString((Number) value));
25051a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson
25151a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson        } else {
25251a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson            string(value.toString());
25351a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson        }
25451a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson
25551a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson        return this;
25651a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson    }
25751a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson
258c78ca6700830460f671652ce3efd4cc2c0cf7a6aJesse Wilson    /**
259c78ca6700830460f671652ce3efd4cc2c0cf7a6aJesse Wilson     * Encodes {@code value} to this stringer.
260c78ca6700830460f671652ce3efd4cc2c0cf7a6aJesse Wilson     *
261c78ca6700830460f671652ce3efd4cc2c0cf7a6aJesse Wilson     * @return this stringer.
262c78ca6700830460f671652ce3efd4cc2c0cf7a6aJesse Wilson     */
26351a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson    public JSONStringer value(boolean value) throws JSONException {
26451a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson        if (stack.isEmpty()) {
26551a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson            throw new JSONException("Nesting problem");
26651a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson        }
26751a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson        beforeValue();
26851a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson        out.append(value);
26951a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson        return this;
27051a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson    }
27151a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson
272c78ca6700830460f671652ce3efd4cc2c0cf7a6aJesse Wilson    /**
273c78ca6700830460f671652ce3efd4cc2c0cf7a6aJesse Wilson     * Encodes {@code value} to this stringer.
274c78ca6700830460f671652ce3efd4cc2c0cf7a6aJesse Wilson     *
275c78ca6700830460f671652ce3efd4cc2c0cf7a6aJesse Wilson     * @param value a finite value. May not be {@link Double#isNaN() NaNs} or
276c78ca6700830460f671652ce3efd4cc2c0cf7a6aJesse Wilson     *     {@link Double#isInfinite() infinities}.
277c78ca6700830460f671652ce3efd4cc2c0cf7a6aJesse Wilson     * @return this stringer.
278c78ca6700830460f671652ce3efd4cc2c0cf7a6aJesse Wilson     */
27951a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson    public JSONStringer value(double value) throws JSONException {
28051a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson        if (stack.isEmpty()) {
28151a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson            throw new JSONException("Nesting problem");
28251a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson        }
28351a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson        beforeValue();
28451a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson        out.append(JSONObject.numberToString(value));
28551a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson        return this;
28651a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson    }
28751a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson
288c78ca6700830460f671652ce3efd4cc2c0cf7a6aJesse Wilson    /**
289c78ca6700830460f671652ce3efd4cc2c0cf7a6aJesse Wilson     * Encodes {@code value} to this stringer.
290c78ca6700830460f671652ce3efd4cc2c0cf7a6aJesse Wilson     *
291c78ca6700830460f671652ce3efd4cc2c0cf7a6aJesse Wilson     * @return this stringer.
292c78ca6700830460f671652ce3efd4cc2c0cf7a6aJesse Wilson     */
29351a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson    public JSONStringer value(long value) throws JSONException {
29451a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson        if (stack.isEmpty()) {
29551a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson            throw new JSONException("Nesting problem");
29651a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson        }
29751a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson        beforeValue();
29851a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson        out.append(value);
29951a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson        return this;
30051a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson    }
30151a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson
30251a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson    private void string(String value) {
30351a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson        out.append("\"");
30451a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson        for (int i = 0, length = value.length(); i < length; i++) {
30551a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson            char c = value.charAt(i);
30651a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson
30751a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson            /*
30851a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson             * From RFC 4627, "All Unicode characters may be placed within the
30951a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson             * quotation marks except for the characters that must be escaped:
31051a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson             * quotation mark, reverse solidus, and the control characters
31151a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson             * (U+0000 through U+001F)."
31251a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson             */
31351a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson            switch (c) {
31451a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson                case '"':
31551a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson                case '\\':
31651a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson                case '/':
31751a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson                    out.append('\\').append(c);
31851a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson                    break;
31951a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson
32051a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson                case '\t':
32151a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson                    out.append("\\t");
32251a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson                    break;
32351a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson
32451a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson                case '\b':
32551a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson                    out.append("\\b");
32651a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson                    break;
32751a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson
32851a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson                case '\n':
32951a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson                    out.append("\\n");
33051a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson                    break;
33151a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson
33251a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson                case '\r':
33351a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson                    out.append("\\r");
33451a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson                    break;
33551a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson
33651a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson                case '\f':
33751a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson                    out.append("\\f");
33851a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson                    break;
33951a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson
34051a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson                default:
34151a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson                    if (c <= 0x1F) {
34251a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson                        out.append(String.format("\\u%04x", (int) c));
34351a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson                    } else {
34451a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson                        out.append(c);
34551a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson                    }
34651a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson                    break;
34751a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson            }
34851a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson
34951a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson        }
35051a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson        out.append("\"");
35151a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson    }
35251a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson
35351a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson    private void newline() {
35451a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson        if (indent == null) {
35551a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson            return;
35651a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson        }
35751a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson
35851a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson        out.append("\n");
35951a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson        for (int i = 0; i < stack.size(); i++) {
36051a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson            out.append(indent);
36151a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson        }
36251a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson    }
36351a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson
364c78ca6700830460f671652ce3efd4cc2c0cf7a6aJesse Wilson    /**
365c78ca6700830460f671652ce3efd4cc2c0cf7a6aJesse Wilson     * Encodes the key (property name) to this stringer.
366c78ca6700830460f671652ce3efd4cc2c0cf7a6aJesse Wilson     *
367c78ca6700830460f671652ce3efd4cc2c0cf7a6aJesse Wilson     * @param name the name of the forthcoming value. May not be null.
368c78ca6700830460f671652ce3efd4cc2c0cf7a6aJesse Wilson     * @return this stringer.
369c78ca6700830460f671652ce3efd4cc2c0cf7a6aJesse Wilson     */
37051a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson    public JSONStringer key(String name) throws JSONException {
37151a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson        if (name == null) {
37251a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson            throw new JSONException("Names must be non-null");
37351a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson        }
37451a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson        beforeKey();
37551a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson        string(name);
37651a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson        return this;
37751a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson    }
37851a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson
37951a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson    /**
38051a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson     * Inserts any necessary separators and whitespace before a name. Also
38151a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson     * adjusts the stack to expect the key's value.
38251a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson     */
38351a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson    private void beforeKey() throws JSONException {
38451a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson        Scope context = peek();
38551a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson        if (context == Scope.NONEMPTY_OBJECT) { // first in object
38651a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson            out.append(',');
38751a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson        } else if (context != Scope.EMPTY_OBJECT) { // not in an object!
38851a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson            throw new JSONException("Nesting problem");
38951a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson        }
39051a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson        newline();
39151a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson        replaceTop(Scope.DANGLING_KEY);
39251a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson    }
39351a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson
39451a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson    /**
39551a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson     * Inserts any necessary separators and whitespace before a literal value,
39651a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson     * inline array, or inline object. Also adjusts the stack to expect either a
39751a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson     * closing bracket or another element.
39851a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson     */
39951a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson    private void beforeValue() throws JSONException {
40051a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson        if (stack.isEmpty()) {
40151a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson            return;
40251a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson        }
40351a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson
40451a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson        Scope context = peek();
40551a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson        if (context == Scope.EMPTY_ARRAY) { // first in array
40651a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson            replaceTop(Scope.NONEMPTY_ARRAY);
40751a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson            newline();
40851a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson        } else if (context == Scope.NONEMPTY_ARRAY) { // another in array
40951a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson            out.append(',');
41051a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson            newline();
41151a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson        } else if (context == Scope.DANGLING_KEY) { // value for key
41251a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson            out.append(indent == null ? ":" : ": ");
41351a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson            replaceTop(Scope.NONEMPTY_OBJECT);
41451a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson        } else if (context != Scope.NULL) {
41551a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson            throw new JSONException("Nesting problem");
41651a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson        }
41751a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson    }
41851a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson
41951a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson    /**
420c78ca6700830460f671652ce3efd4cc2c0cf7a6aJesse Wilson     * Returns the encoded JSON string.
421c78ca6700830460f671652ce3efd4cc2c0cf7a6aJesse Wilson     *
422c78ca6700830460f671652ce3efd4cc2c0cf7a6aJesse Wilson     * <p>If invoked with unterminated arrays or unclosed objects, this method's
423c78ca6700830460f671652ce3efd4cc2c0cf7a6aJesse Wilson     * return value is undefined.
424c78ca6700830460f671652ce3efd4cc2c0cf7a6aJesse Wilson     *
425c78ca6700830460f671652ce3efd4cc2c0cf7a6aJesse Wilson     * <p><strong>Warning:</strong> although it contradicts the general contract
426c78ca6700830460f671652ce3efd4cc2c0cf7a6aJesse Wilson     * of {@link Object#toString}, this method returns null if the stringer
427c78ca6700830460f671652ce3efd4cc2c0cf7a6aJesse Wilson     * contains no data.
42851a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson     */
42951a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson    @Override public String toString() {
43051a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson        return out.length() == 0 ? null : out.toString();
43151a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson    }
43251a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson}
433