JsonWriter.java revision 76d7e20a75d24afb2f5011a7646dbde8aaa52087
176d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson/*
276d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * Copyright (C) 2010 The Android Open Source Project
376d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson *
476d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * Licensed under the Apache License, Version 2.0 (the "License");
576d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * you may not use this file except in compliance with the License.
676d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * You may obtain a copy of the License at
776d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson *
876d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson *      http://www.apache.org/licenses/LICENSE-2.0
976d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson *
1076d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * Unless required by applicable law or agreed to in writing, software
1176d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * distributed under the License is distributed on an "AS IS" BASIS,
1276d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1376d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * See the License for the specific language governing permissions and
1476d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * limitations under the License.
1576d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson */
1676d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson
1776d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilsonpackage android.util;
1876d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson
1976d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilsonimport java.io.IOException;
2076d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilsonimport java.io.Writer;
2176d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilsonimport java.util.ArrayList;
2276d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilsonimport java.util.Arrays;
2376d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilsonimport java.util.List;
2476d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson
2576d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson/**
2676d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * Writes a JSON (<a href="http://www.ietf.org/rfc/rfc4627.txt">RFC 4627</a>)
2776d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * encoded value to a stream, one token at a time. The stream includes both
2876d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * literal values (strings, numbers, booleans and nulls) as well as the begin
2976d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * and end delimiters of objects and arrays.
3076d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson *
3176d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * <h3>Encoding JSON</h3>
3276d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * To encode your data as JSON, create a new {@code JsonWriter}. Each JSON
3376d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * document must contain one top-level array or object. Call methods on the
3476d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * writer as you walk the structure's contents, nesting arrays and objects as
3576d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * necessary:
3676d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * <ul>
3776d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson *   <li>To write <strong>arrays</strong>, first call {@link #beginArray()}.
3876d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson *       Write each of the array's elements with the appropriate {@link #value}
3976d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson *       methods or by nesting other arrays and objects. Finally close the array
4076d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson *       using {@link #endArray()}.
4176d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson *   <li>To write <strong>objects</strong>, first call {@link #beginObject()}.
4276d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson *       Write each of the object's properties by alternating calls to
4376d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson *       {@link #name} with the property's value. Write property values with the
4476d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson *       appropriate {@link #value} method or by nesting other objects or arrays.
4576d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson *       Finally close the object using {@link #endObject()}.
4676d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * </ul>
4776d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson *
4876d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * <h3>Example</h3>
4976d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * Suppose we'd like to encode a stream of messages such as the following: <pre> {@code
5076d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * [
5176d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson *   {
5276d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson *     "id": 912345678901,
5376d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson *     "text": "How do I write JSON on Android?",
5476d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson *     "geo": null,
5576d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson *     "user": {
5676d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson *       "name": "android_newb",
5776d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson *       "followers_count": 41
5876d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson *      }
5976d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson *   },
6076d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson *   {
6176d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson *     "id": 912345678902,
6276d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson *     "text": "@android_newb just use android.util.JsonWriter!",
6376d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson *     "geo": [50.454722, -104.606667],
6476d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson *     "user": {
6576d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson *       "name": "jesse",
6676d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson *       "followers_count": 2
6776d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson *     }
6876d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson *   }
6976d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * ]}</pre>
7076d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * This code encodes the above structure: <pre>   {@code
7176d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson *   public void writeJsonStream(OutputStream out, List<Message> messages) throws IOException {
7276d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson *     JsonWriter writer = new JsonWriter(new OutputStreamWriter(out, "UTF-8"));
7376d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson *     writer.setIndentSpaces(4);
7476d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson *     writeMessagesArray(writer, messages);
7576d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson *     writer.close();
7676d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson *   }
7776d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson *
7876d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson *   public void writeMessagesArray(JsonWriter writer, List<Message> messages) throws IOException {
7976d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson *     writer.beginArray();
8076d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson *     for (Message message : messages) {
8176d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson *       writeMessage(writer, message);
8276d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson *     }
8376d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson *     writer.endArray();
8476d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson *   }
8576d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson *
8676d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson *   public void writeMessage(JsonWriter writer, Message message) throws IOException {
8776d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson *     writer.beginObject();
8876d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson *     writer.name("id").value(message.getId());
8976d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson *     writer.name("text").value(message.getText());
9076d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson *     if (message.getGeo() != null) {
9176d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson *       writer.name("geo");
9276d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson *       writeDoublesArray(writer, message.getGeo());
9376d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson *     } else {
9476d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson *       writer.name("geo").nullValue();
9576d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson *     }
9676d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson *     writer.name("user");
9776d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson *     writeUser(writer, message.getUser());
9876d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson *     writer.endObject();
9976d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson *   }
10076d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson *
10176d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson *   public void writeUser(JsonWriter writer, User user) throws IOException {
10276d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson *     writer.beginObject();
10376d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson *     writer.name("name").value(user.getName());
10476d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson *     writer.name("followers_count").value(user.getFollowersCount());
10576d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson *     writer.endObject();
10676d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson *   }
10776d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson *
10876d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson *   public void writeDoublesArray(JsonWriter writer, List<Double> doubles) throws IOException {
10976d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson *     writer.beginArray();
11076d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson *     for (Double value : doubles) {
11176d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson *       writer.value(value);
11276d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson *     }
11376d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson *     writer.endArray();
11476d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson *   }}</pre>
11576d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson *
11676d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * <p>Each {@code JsonWriter} may be used to write a single JSON stream.
11776d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * Instances of this class are not thread safe. Calls that would result in a
11876d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * malformed JSON string will fail with an {@link IllegalStateException}.
11976d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson */
12076d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilsonpublic final class JsonWriter {
12176d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson
12276d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson    /** The output data, containing at most one top-level array or object. */
12376d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson    private final Writer out;
12476d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson
12576d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson    private final List<JsonScope> stack = new ArrayList<JsonScope>();
12676d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson    {
12776d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson        stack.add(JsonScope.EMPTY_DOCUMENT);
12876d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson    }
12976d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson
13076d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson    /**
13176d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson     * A string containing a full set of spaces for a single level of
13276d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson     * indentation, or null for no pretty printing.
13376d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson     */
13476d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson    private String indent;
13576d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson
13676d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson    /**
13776d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson     * The name/value separator; either ":" or ": ".
13876d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson     */
13976d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson    private String separator = ":";
14076d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson
14176d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson    /**
14276d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson     * Creates a new instance that writes a JSON-encoded stream to {@code out}.
14376d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson     * For best performance, ensure {@link Writer} is buffered; wrapping in
14476d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson     * {@link java.io.BufferedWriter BufferedWriter} if necessary.
14576d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson     */
14676d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson    public JsonWriter(Writer out) {
14776d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson        if (out == null) {
14876d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson            throw new NullPointerException("out == null");
14976d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson        }
15076d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson        this.out = out;
15176d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson    }
15276d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson
15376d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson    /**
15476d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson     * Sets the number of spaces to indent each line in the encoded document.
15576d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson     * If {@code indent == 0} the encoded document will be compact. If {@code
15676d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson     * indent > 0}, the encoded document will be more human-readable.
15776d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson     */
15876d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson    public void setIndentSpaces(int indent) {
15976d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson        if (indent < 0) {
16076d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson            throw new IllegalArgumentException("indent < 0");
16176d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson        }
16276d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson
16376d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson        if (indent > 0) {
16476d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson            char[] indentChars = new char[indent];
16576d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson            Arrays.fill(indentChars, ' ');
16676d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson            this.indent = new String(indentChars);
16776d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson            this.separator = ": ";
16876d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson        } else {
16976d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson            this.indent = null;
17076d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson            this.separator = ":";
17176d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson        }
17276d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson    }
17376d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson
17476d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson    /**
17576d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson     * Begins encoding a new array. Each call to this method must be paired with
17676d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson     * a call to {@link #endArray}.
17776d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson     *
17876d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson     * @return this writer.
17976d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson     */
18076d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson    public JsonWriter beginArray() throws IOException {
18176d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson        return open(JsonScope.EMPTY_ARRAY, "[");
18276d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson    }
18376d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson
18476d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson    /**
18576d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson     * Ends encoding the current array.
18676d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson     *
18776d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson     * @return this writer.
18876d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson     */
18976d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson    public JsonWriter endArray() throws IOException {
19076d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson        return close(JsonScope.EMPTY_ARRAY, JsonScope.NONEMPTY_ARRAY, "]");
19176d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson    }
19276d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson
19376d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson    /**
19476d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson     * Begins encoding a new object. Each call to this method must be paired
19576d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson     * with a call to {@link #endObject}.
19676d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson     *
19776d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson     * @return this writer.
19876d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson     */
19976d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson    public JsonWriter beginObject() throws IOException {
20076d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson        return open(JsonScope.EMPTY_OBJECT, "{");
20176d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson    }
20276d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson
20376d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson    /**
20476d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson     * Ends encoding the current object.
20576d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson     *
20676d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson     * @return this writer.
20776d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson     */
20876d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson    public JsonWriter endObject() throws IOException {
20976d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson        return close(JsonScope.EMPTY_OBJECT, JsonScope.NONEMPTY_OBJECT, "}");
21076d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson    }
21176d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson
21276d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson    /**
21376d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson     * Enters a new scope by appending any necessary whitespace and the given
21476d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson     * bracket.
21576d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson     */
21676d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson    private JsonWriter open(JsonScope empty, String openBracket) throws IOException {
21776d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson        beforeValue(true);
21876d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson        stack.add(empty);
21976d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson        out.write(openBracket);
22076d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson        return this;
22176d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson    }
22276d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson
22376d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson    /**
22476d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson     * Closes the current scope by appending any necessary whitespace and the
22576d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson     * given bracket.
22676d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson     */
22776d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson    private JsonWriter close(JsonScope empty, JsonScope nonempty, String closeBracket)
22876d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson            throws IOException {
22976d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson        JsonScope context = peek();
23076d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson        if (context != nonempty && context != empty) {
23176d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson            throw new IllegalStateException("Nesting problem: " + stack);
23276d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson        }
23376d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson
23476d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson        stack.remove(stack.size() - 1);
23576d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson        if (context == nonempty) {
23676d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson            newline();
23776d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson        }
23876d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson        out.write(closeBracket);
23976d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson        return this;
24076d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson    }
24176d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson
24276d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson    /**
24376d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson     * Returns the value on the top of the stack.
24476d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson     */
24576d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson    private JsonScope peek() {
24676d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson        return stack.get(stack.size() - 1);
24776d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson    }
24876d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson
24976d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson    /**
25076d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson     * Replace the value on the top of the stack with the given value.
25176d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson     */
25276d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson    private void replaceTop(JsonScope topOfStack) {
25376d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson        stack.set(stack.size() - 1, topOfStack);
25476d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson    }
25576d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson
25676d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson    /**
25776d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson     * Encodes the property name.
25876d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson     *
25976d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson     * @param name the name of the forthcoming value. May not be null.
26076d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson     * @return this writer.
26176d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson     */
26276d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson    public JsonWriter name(String name) throws IOException {
26376d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson        if (name == null) {
26476d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson            throw new NullPointerException("name == null");
26576d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson        }
26676d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson        beforeName();
26776d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson        string(name);
26876d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson        return this;
26976d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson    }
27076d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson
27176d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson    /**
27276d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson     * Encodes {@code value}.
27376d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson     *
27476d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson     * @param value the literal string value, or null to encode a null literal.
27576d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson     * @return this writer.
27676d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson     */
27776d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson    public JsonWriter value(String value) throws IOException {
27876d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson        if (value == null) {
27976d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson            return nullValue();
28076d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson        }
28176d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson        beforeValue(false);
28276d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson        string(value);
28376d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson        return this;
28476d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson    }
28576d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson
28676d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson    /**
28776d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson     * Encodes {@code null}.
28876d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson     *
28976d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson     * @return this writer.
29076d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson     */
29176d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson    public JsonWriter nullValue() throws IOException {
29276d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson        beforeValue(false);
29376d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson        out.write("null");
29476d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson        return this;
29576d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson    }
29676d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson
29776d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson    /**
29876d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson     * Encodes {@code value}.
29976d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson     *
30076d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson     * @return this writer.
30176d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson     */
30276d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson    public JsonWriter value(boolean value) throws IOException {
30376d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson        beforeValue(false);
30476d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson        out.write(value ? "true" : "false");
30576d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson        return this;
30676d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson    }
30776d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson
30876d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson    /**
30976d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson     * Encodes {@code value}.
31076d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson     *
31176d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson     * @param value a finite value. May not be {@link Double#isNaN() NaNs} or
31276d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson     *     {@link Double#isInfinite() infinities}.
31376d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson     * @return this writer.
31476d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson     */
31576d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson    public JsonWriter value(double value) throws IOException {
31676d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson        if (Double.isNaN(value) || Double.isInfinite(value)) {
31776d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson            throw new IllegalArgumentException("Numeric values must be finite, but was " + value);
31876d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson        }
31976d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson        beforeValue(false);
32076d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson        out.append(Double.toString(value));
32176d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson        return this;
32276d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson    }
32376d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson
32476d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson    /**
32576d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson     * Encodes {@code value}.
32676d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson     *
32776d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson     * @return this writer.
32876d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson     */
32976d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson    public JsonWriter value(long value) throws IOException {
33076d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson        beforeValue(false);
33176d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson        out.write(Long.toString(value));
33276d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson        return this;
33376d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson    }
33476d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson
33576d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson    /**
33676d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson     * Ensures all buffered data is written to the underlying {@link Writer}
33776d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson     * and flushes that writer.
33876d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson     */
33976d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson    public void flush() throws IOException {
34076d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson        out.flush();
34176d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson    }
34276d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson
34376d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson    /**
34476d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson     * Flushes and closes this writer and the underlying {@link Writer}.
34576d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson     *
34676d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson     * @throws IOException if the JSON document is incomplete.
34776d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson     */
34876d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson    public void close() throws IOException {
34976d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson        out.close();
35076d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson
35176d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson        if (peek() != JsonScope.NONEMPTY_DOCUMENT) {
35276d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson            throw new IOException("Incomplete document");
35376d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson        }
35476d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson    }
35576d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson
35676d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson    private void string(String value) throws IOException {
35776d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson        out.write("\"");
35876d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson        for (int i = 0, length = value.length(); i < length; i++) {
35976d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson            char c = value.charAt(i);
36076d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson
36176d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson            /*
36276d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson             * From RFC 4627, "All Unicode characters may be placed within the
36376d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson             * quotation marks except for the characters that must be escaped:
36476d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson             * quotation mark, reverse solidus, and the control characters
36576d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson             * (U+0000 through U+001F)."
36676d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson             */
36776d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson            switch (c) {
36876d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson                case '"':
36976d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson                case '\\':
37076d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson                case '/':
37176d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson                    out.write('\\');
37276d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson                    out.write(c);
37376d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson                    break;
37476d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson
37576d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson                case '\t':
37676d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson                    out.write("\\t");
37776d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson                    break;
37876d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson
37976d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson                case '\b':
38076d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson                    out.write("\\b");
38176d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson                    break;
38276d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson
38376d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson                case '\n':
38476d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson                    out.write("\\n");
38576d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson                    break;
38676d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson
38776d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson                case '\r':
38876d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson                    out.write("\\r");
38976d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson                    break;
39076d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson
39176d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson                case '\f':
39276d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson                    out.write("\\f");
39376d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson                    break;
39476d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson
39576d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson                default:
39676d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson                    if (c <= 0x1F) {
39776d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson                        out.write(String.format("\\u%04x", (int) c));
39876d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson                    } else {
39976d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson                        out.write(c);
40076d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson                    }
40176d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson                    break;
40276d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson            }
40376d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson
40476d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson        }
40576d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson        out.write("\"");
40676d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson    }
40776d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson
40876d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson    private void newline() throws IOException {
40976d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson        if (indent == null) {
41076d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson            return;
41176d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson        }
41276d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson
41376d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson        out.write("\n");
41476d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson        for (int i = 1; i < stack.size(); i++) {
41576d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson            out.write(indent);
41676d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson        }
41776d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson    }
41876d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson
41976d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson    /**
42076d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson     * Inserts any necessary separators and whitespace before a name. Also
42176d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson     * adjusts the stack to expect the name's value.
42276d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson     */
42376d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson    private void beforeName() throws IOException {
42476d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson        JsonScope context = peek();
42576d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson        if (context == JsonScope.NONEMPTY_OBJECT) { // first in object
42676d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson            out.write(',');
42776d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson        } else if (context != JsonScope.EMPTY_OBJECT) { // not in an object!
42876d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson            throw new IllegalStateException("Nesting problem: " + stack);
42976d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson        }
43076d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson        newline();
43176d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson        replaceTop(JsonScope.DANGLING_NAME);
43276d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson    }
43376d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson
43476d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson    /**
43576d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson     * Inserts any necessary separators and whitespace before a literal value,
43676d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson     * inline array, or inline object. Also adjusts the stack to expect either a
43776d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson     * closing bracket or another element.
43876d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson     *
43976d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson     * @param root true if the value is a new array or object, the two values
44076d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson     *     permitted as top-level elements.
44176d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson     */
44276d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson    private void beforeValue(boolean root) throws IOException {
44376d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson        switch (peek()) {
44476d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson            case EMPTY_DOCUMENT: // first in document
44576d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson                if (!root) {
44676d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson                    throw new IllegalStateException(
44776d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson                            "JSON must start with an array or an object.");
44876d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson                }
44976d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson                replaceTop(JsonScope.NONEMPTY_DOCUMENT);
45076d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson                break;
45176d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson
45276d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson            case EMPTY_ARRAY: // first in array
45376d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson                replaceTop(JsonScope.NONEMPTY_ARRAY);
45476d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson                newline();
45576d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson                break;
45676d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson
45776d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson            case NONEMPTY_ARRAY: // another in array
45876d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson                out.append(',');
45976d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson                newline();
46076d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson                break;
46176d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson
46276d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson            case DANGLING_NAME: // value for name
46376d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson                out.append(separator);
46476d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson                replaceTop(JsonScope.NONEMPTY_OBJECT);
46576d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson                break;
46676d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson
46776d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson            case NONEMPTY_DOCUMENT:
46876d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson                throw new IllegalStateException(
46976d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson                        "JSON must have only one top-level value.");
47076d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson
47176d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson            default:
47276d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson                throw new IllegalStateException("Nesting problem: " + stack);
47376d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson        }
47476d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson    }
47576d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson}
476