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
191ba417140554dbd57602a1f4b453fc42363cf394Jesse Wilsonimport java.io.Closeable;
2076d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilsonimport java.io.IOException;
2176d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilsonimport java.io.Writer;
2276d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilsonimport java.util.ArrayList;
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"));
733312b297600ec97f28f979dfbdf6c95e164a304fJesse Wilson *     writer.setIndent("  ");
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 */
1201ba417140554dbd57602a1f4b453fc42363cf394Jesse Wilsonpublic final class JsonWriter implements Closeable {
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
141eb97c0ddc063176c26065fc6855188edf0c16e03Jesse Wilson    private boolean lenient;
142eb97c0ddc063176c26065fc6855188edf0c16e03Jesse Wilson
14376d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson    /**
14476d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson     * Creates a new instance that writes a JSON-encoded stream to {@code out}.
14576d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson     * For best performance, ensure {@link Writer} is buffered; wrapping in
14676d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson     * {@link java.io.BufferedWriter BufferedWriter} if necessary.
14776d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson     */
14876d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson    public JsonWriter(Writer out) {
14976d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson        if (out == null) {
15076d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson            throw new NullPointerException("out == null");
15176d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson        }
15276d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson        this.out = out;
15376d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson    }
15476d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson
15576d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson    /**
1561ba417140554dbd57602a1f4b453fc42363cf394Jesse Wilson     * Sets the indentation string to be repeated for each level of indentation
1571ba417140554dbd57602a1f4b453fc42363cf394Jesse Wilson     * in the encoded document. If {@code indent.isEmpty()} the encoded document
1581ba417140554dbd57602a1f4b453fc42363cf394Jesse Wilson     * will be compact. Otherwise the encoded document will be more
1591ba417140554dbd57602a1f4b453fc42363cf394Jesse Wilson     * human-readable.
1601ba417140554dbd57602a1f4b453fc42363cf394Jesse Wilson     *
1611ba417140554dbd57602a1f4b453fc42363cf394Jesse Wilson     * @param indent a string containing only whitespace.
16276d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson     */
1631ba417140554dbd57602a1f4b453fc42363cf394Jesse Wilson    public void setIndent(String indent) {
1641ba417140554dbd57602a1f4b453fc42363cf394Jesse Wilson        if (indent.isEmpty()) {
16576d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson            this.indent = null;
16676d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson            this.separator = ":";
1671ba417140554dbd57602a1f4b453fc42363cf394Jesse Wilson        } else {
1681ba417140554dbd57602a1f4b453fc42363cf394Jesse Wilson            this.indent = indent;
1691ba417140554dbd57602a1f4b453fc42363cf394Jesse Wilson            this.separator = ": ";
17076d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson        }
17176d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson    }
17276d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson
17376d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson    /**
174eb97c0ddc063176c26065fc6855188edf0c16e03Jesse Wilson     * Configure this writer to relax its syntax rules. By default, this writer
175eb97c0ddc063176c26065fc6855188edf0c16e03Jesse Wilson     * only emits well-formed JSON as specified by <a
176eb97c0ddc063176c26065fc6855188edf0c16e03Jesse Wilson     * href="http://www.ietf.org/rfc/rfc4627.txt">RFC 4627</a>. Setting the writer
177eb97c0ddc063176c26065fc6855188edf0c16e03Jesse Wilson     * to lenient permits the following:
178eb97c0ddc063176c26065fc6855188edf0c16e03Jesse Wilson     * <ul>
179eb97c0ddc063176c26065fc6855188edf0c16e03Jesse Wilson     *   <li>Top-level values of any type. With strict writing, the top-level
180eb97c0ddc063176c26065fc6855188edf0c16e03Jesse Wilson     *       value must be an object or an array.
181eb97c0ddc063176c26065fc6855188edf0c16e03Jesse Wilson     *   <li>Numbers may be {@link Double#isNaN() NaNs} or {@link
182eb97c0ddc063176c26065fc6855188edf0c16e03Jesse Wilson     *       Double#isInfinite() infinities}.
183eb97c0ddc063176c26065fc6855188edf0c16e03Jesse Wilson     * </ul>
184eb97c0ddc063176c26065fc6855188edf0c16e03Jesse Wilson     */
185eb97c0ddc063176c26065fc6855188edf0c16e03Jesse Wilson    public void setLenient(boolean lenient) {
186eb97c0ddc063176c26065fc6855188edf0c16e03Jesse Wilson        this.lenient = lenient;
187eb97c0ddc063176c26065fc6855188edf0c16e03Jesse Wilson    }
188eb97c0ddc063176c26065fc6855188edf0c16e03Jesse Wilson
189eb97c0ddc063176c26065fc6855188edf0c16e03Jesse Wilson    /**
190eb97c0ddc063176c26065fc6855188edf0c16e03Jesse Wilson     * Returns true if this writer has relaxed syntax rules.
191eb97c0ddc063176c26065fc6855188edf0c16e03Jesse Wilson     */
192eb97c0ddc063176c26065fc6855188edf0c16e03Jesse Wilson    public boolean isLenient() {
193eb97c0ddc063176c26065fc6855188edf0c16e03Jesse Wilson        return lenient;
194eb97c0ddc063176c26065fc6855188edf0c16e03Jesse Wilson    }
195eb97c0ddc063176c26065fc6855188edf0c16e03Jesse Wilson
196eb97c0ddc063176c26065fc6855188edf0c16e03Jesse Wilson    /**
19776d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson     * Begins encoding a new array. Each call to this method must be paired with
19876d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson     * a call to {@link #endArray}.
19976d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson     *
20076d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson     * @return this writer.
20176d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson     */
20276d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson    public JsonWriter beginArray() throws IOException {
20376d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson        return open(JsonScope.EMPTY_ARRAY, "[");
20476d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson    }
20576d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson
20676d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson    /**
20776d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson     * Ends encoding the current array.
20876d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson     *
20976d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson     * @return this writer.
21076d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson     */
21176d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson    public JsonWriter endArray() throws IOException {
21276d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson        return close(JsonScope.EMPTY_ARRAY, JsonScope.NONEMPTY_ARRAY, "]");
21376d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson    }
21476d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson
21576d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson    /**
21676d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson     * Begins encoding a new object. Each call to this method must be paired
21776d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson     * with a call to {@link #endObject}.
21876d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson     *
21976d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson     * @return this writer.
22076d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson     */
22176d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson    public JsonWriter beginObject() throws IOException {
22276d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson        return open(JsonScope.EMPTY_OBJECT, "{");
22376d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson    }
22476d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson
22576d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson    /**
22676d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson     * Ends encoding the current object.
22776d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson     *
22876d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson     * @return this writer.
22976d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson     */
23076d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson    public JsonWriter endObject() throws IOException {
23176d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson        return close(JsonScope.EMPTY_OBJECT, JsonScope.NONEMPTY_OBJECT, "}");
23276d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson    }
23376d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson
23476d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson    /**
23576d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson     * Enters a new scope by appending any necessary whitespace and the given
23676d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson     * bracket.
23776d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson     */
23876d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson    private JsonWriter open(JsonScope empty, String openBracket) throws IOException {
23976d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson        beforeValue(true);
24076d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson        stack.add(empty);
24176d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson        out.write(openBracket);
24276d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson        return this;
24376d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson    }
24476d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson
24576d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson    /**
24676d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson     * Closes the current scope by appending any necessary whitespace and the
24776d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson     * given bracket.
24876d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson     */
24976d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson    private JsonWriter close(JsonScope empty, JsonScope nonempty, String closeBracket)
25076d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson            throws IOException {
25176d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson        JsonScope context = peek();
25276d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson        if (context != nonempty && context != empty) {
25376d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson            throw new IllegalStateException("Nesting problem: " + stack);
25476d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson        }
25576d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson
25676d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson        stack.remove(stack.size() - 1);
25776d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson        if (context == nonempty) {
25876d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson            newline();
25976d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson        }
26076d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson        out.write(closeBracket);
26176d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson        return this;
26276d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson    }
26376d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson
26476d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson    /**
26576d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson     * Returns the value on the top of the stack.
26676d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson     */
26776d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson    private JsonScope peek() {
26876d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson        return stack.get(stack.size() - 1);
26976d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson    }
27076d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson
27176d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson    /**
27276d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson     * Replace the value on the top of the stack with the given value.
27376d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson     */
27476d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson    private void replaceTop(JsonScope topOfStack) {
27576d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson        stack.set(stack.size() - 1, topOfStack);
27676d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson    }
27776d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson
27876d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson    /**
27976d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson     * Encodes the property name.
28076d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson     *
28176d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson     * @param name the name of the forthcoming value. May not be null.
28276d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson     * @return this writer.
28376d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson     */
28476d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson    public JsonWriter name(String name) throws IOException {
28576d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson        if (name == null) {
28676d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson            throw new NullPointerException("name == null");
28776d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson        }
28876d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson        beforeName();
28976d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson        string(name);
29076d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson        return this;
29176d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson    }
29276d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson
29376d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson    /**
29476d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson     * Encodes {@code value}.
29576d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson     *
29676d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson     * @param value the literal string value, or null to encode a null literal.
29776d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson     * @return this writer.
29876d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson     */
29976d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson    public JsonWriter value(String value) throws IOException {
30076d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson        if (value == null) {
30176d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson            return nullValue();
30276d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson        }
30376d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson        beforeValue(false);
30476d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson        string(value);
30576d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson        return this;
30676d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson    }
30776d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson
30876d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson    /**
30976d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson     * Encodes {@code null}.
31076d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson     *
31176d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson     * @return this writer.
31276d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson     */
31376d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson    public JsonWriter nullValue() throws IOException {
31476d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson        beforeValue(false);
31576d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson        out.write("null");
31676d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson        return this;
31776d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson    }
31876d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson
31976d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson    /**
32076d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson     * Encodes {@code value}.
32176d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson     *
32276d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson     * @return this writer.
32376d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson     */
32476d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson    public JsonWriter value(boolean value) throws IOException {
32576d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson        beforeValue(false);
32676d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson        out.write(value ? "true" : "false");
32776d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson        return this;
32876d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson    }
32976d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson
33076d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson    /**
33176d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson     * Encodes {@code value}.
33276d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson     *
33376d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson     * @param value a finite value. May not be {@link Double#isNaN() NaNs} or
334eb97c0ddc063176c26065fc6855188edf0c16e03Jesse Wilson     *     {@link Double#isInfinite() infinities} unless this writer is lenient.
33576d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson     * @return this writer.
33676d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson     */
33776d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson    public JsonWriter value(double value) throws IOException {
338eb97c0ddc063176c26065fc6855188edf0c16e03Jesse Wilson        if (!lenient && (Double.isNaN(value) || Double.isInfinite(value))) {
33976d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson            throw new IllegalArgumentException("Numeric values must be finite, but was " + value);
34076d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson        }
34176d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson        beforeValue(false);
34276d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson        out.append(Double.toString(value));
34376d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson        return this;
34476d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson    }
34576d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson
34676d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson    /**
34776d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson     * Encodes {@code value}.
34876d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson     *
34976d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson     * @return this writer.
35076d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson     */
35176d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson    public JsonWriter value(long value) throws IOException {
35276d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson        beforeValue(false);
35376d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson        out.write(Long.toString(value));
35476d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson        return this;
35576d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson    }
35676d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson
35776d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson    /**
358eb97c0ddc063176c26065fc6855188edf0c16e03Jesse Wilson     * Encodes {@code value}.
359eb97c0ddc063176c26065fc6855188edf0c16e03Jesse Wilson     *
360eb97c0ddc063176c26065fc6855188edf0c16e03Jesse Wilson     * @param value a finite value. May not be {@link Double#isNaN() NaNs} or
361eb97c0ddc063176c26065fc6855188edf0c16e03Jesse Wilson     *     {@link Double#isInfinite() infinities} unless this writer is lenient.
362eb97c0ddc063176c26065fc6855188edf0c16e03Jesse Wilson     * @return this writer.
363eb97c0ddc063176c26065fc6855188edf0c16e03Jesse Wilson     */
364eb97c0ddc063176c26065fc6855188edf0c16e03Jesse Wilson    public JsonWriter value(Number value) throws IOException {
365eb97c0ddc063176c26065fc6855188edf0c16e03Jesse Wilson        if (value == null) {
366eb97c0ddc063176c26065fc6855188edf0c16e03Jesse Wilson            return nullValue();
367eb97c0ddc063176c26065fc6855188edf0c16e03Jesse Wilson        }
368eb97c0ddc063176c26065fc6855188edf0c16e03Jesse Wilson
369eb97c0ddc063176c26065fc6855188edf0c16e03Jesse Wilson        String string = value.toString();
370eb97c0ddc063176c26065fc6855188edf0c16e03Jesse Wilson        if (!lenient &&
371eb97c0ddc063176c26065fc6855188edf0c16e03Jesse Wilson                (string.equals("-Infinity") || string.equals("Infinity") || string.equals("NaN"))) {
372eb97c0ddc063176c26065fc6855188edf0c16e03Jesse Wilson            throw new IllegalArgumentException("Numeric values must be finite, but was " + value);
373eb97c0ddc063176c26065fc6855188edf0c16e03Jesse Wilson        }
374eb97c0ddc063176c26065fc6855188edf0c16e03Jesse Wilson        beforeValue(false);
375eb97c0ddc063176c26065fc6855188edf0c16e03Jesse Wilson        out.append(string);
376eb97c0ddc063176c26065fc6855188edf0c16e03Jesse Wilson        return this;
377eb97c0ddc063176c26065fc6855188edf0c16e03Jesse Wilson    }
378eb97c0ddc063176c26065fc6855188edf0c16e03Jesse Wilson
379eb97c0ddc063176c26065fc6855188edf0c16e03Jesse Wilson    /**
38076d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson     * Ensures all buffered data is written to the underlying {@link Writer}
38176d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson     * and flushes that writer.
38276d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson     */
38376d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson    public void flush() throws IOException {
38476d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson        out.flush();
38576d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson    }
38676d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson
38776d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson    /**
38876d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson     * Flushes and closes this writer and the underlying {@link Writer}.
38976d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson     *
39076d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson     * @throws IOException if the JSON document is incomplete.
39176d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson     */
39276d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson    public void close() throws IOException {
39376d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson        out.close();
39476d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson
39576d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson        if (peek() != JsonScope.NONEMPTY_DOCUMENT) {
39676d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson            throw new IOException("Incomplete document");
39776d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson        }
39876d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson    }
39976d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson
40076d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson    private void string(String value) throws IOException {
40176d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson        out.write("\"");
40276d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson        for (int i = 0, length = value.length(); i < length; i++) {
40376d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson            char c = value.charAt(i);
40476d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson
40576d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson            /*
40676d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson             * From RFC 4627, "All Unicode characters may be placed within the
40776d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson             * quotation marks except for the characters that must be escaped:
40876d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson             * quotation mark, reverse solidus, and the control characters
40976d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson             * (U+0000 through U+001F)."
4100fc0edfd273f9d92b9f9fe4f49382b11104f55f0Jesse Wilson             *
4110fc0edfd273f9d92b9f9fe4f49382b11104f55f0Jesse Wilson             * We also escape '\u2028' and '\u2029', which JavaScript interprets
4120fc0edfd273f9d92b9f9fe4f49382b11104f55f0Jesse Wilson             * as newline characters. This prevents eval() from failing with a
4130fc0edfd273f9d92b9f9fe4f49382b11104f55f0Jesse Wilson             * syntax error.
4140fc0edfd273f9d92b9f9fe4f49382b11104f55f0Jesse Wilson             * http://code.google.com/p/google-gson/issues/detail?id=341
41576d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson             */
41676d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson            switch (c) {
41776d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson                case '"':
41876d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson                case '\\':
41976d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson                    out.write('\\');
42076d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson                    out.write(c);
42176d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson                    break;
42276d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson
42376d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson                case '\t':
42476d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson                    out.write("\\t");
42576d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson                    break;
42676d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson
42776d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson                case '\b':
42876d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson                    out.write("\\b");
42976d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson                    break;
43076d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson
43176d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson                case '\n':
43276d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson                    out.write("\\n");
43376d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson                    break;
43476d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson
43576d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson                case '\r':
43676d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson                    out.write("\\r");
43776d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson                    break;
43876d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson
43976d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson                case '\f':
44076d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson                    out.write("\\f");
44176d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson                    break;
44276d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson
4430fc0edfd273f9d92b9f9fe4f49382b11104f55f0Jesse Wilson                case '\u2028':
4440fc0edfd273f9d92b9f9fe4f49382b11104f55f0Jesse Wilson                case '\u2029':
4450fc0edfd273f9d92b9f9fe4f49382b11104f55f0Jesse Wilson                    out.write(String.format("\\u%04x", (int) c));
4460fc0edfd273f9d92b9f9fe4f49382b11104f55f0Jesse Wilson                    break;
4470fc0edfd273f9d92b9f9fe4f49382b11104f55f0Jesse Wilson
44876d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson                default:
44976d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson                    if (c <= 0x1F) {
45076d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson                        out.write(String.format("\\u%04x", (int) c));
45176d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson                    } else {
45276d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson                        out.write(c);
45376d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson                    }
45476d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson                    break;
45576d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson            }
45676d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson
45776d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson        }
45876d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson        out.write("\"");
45976d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson    }
46076d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson
46176d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson    private void newline() throws IOException {
46276d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson        if (indent == null) {
46376d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson            return;
46476d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson        }
46576d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson
46676d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson        out.write("\n");
46776d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson        for (int i = 1; i < stack.size(); i++) {
46876d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson            out.write(indent);
46976d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson        }
47076d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson    }
47176d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson
47276d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson    /**
47376d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson     * Inserts any necessary separators and whitespace before a name. Also
47476d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson     * adjusts the stack to expect the name's value.
47576d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson     */
47676d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson    private void beforeName() throws IOException {
47776d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson        JsonScope context = peek();
47876d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson        if (context == JsonScope.NONEMPTY_OBJECT) { // first in object
47976d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson            out.write(',');
48076d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson        } else if (context != JsonScope.EMPTY_OBJECT) { // not in an object!
48176d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson            throw new IllegalStateException("Nesting problem: " + stack);
48276d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson        }
48376d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson        newline();
48476d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson        replaceTop(JsonScope.DANGLING_NAME);
48576d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson    }
48676d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson
48776d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson    /**
48876d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson     * Inserts any necessary separators and whitespace before a literal value,
48976d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson     * inline array, or inline object. Also adjusts the stack to expect either a
49076d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson     * closing bracket or another element.
49176d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson     *
49276d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson     * @param root true if the value is a new array or object, the two values
49376d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson     *     permitted as top-level elements.
49476d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson     */
49576d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson    private void beforeValue(boolean root) throws IOException {
49676d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson        switch (peek()) {
49776d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson            case EMPTY_DOCUMENT: // first in document
498eb97c0ddc063176c26065fc6855188edf0c16e03Jesse Wilson                if (!lenient && !root) {
49976d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson                    throw new IllegalStateException(
50076d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson                            "JSON must start with an array or an object.");
50176d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson                }
50276d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson                replaceTop(JsonScope.NONEMPTY_DOCUMENT);
50376d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson                break;
50476d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson
50576d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson            case EMPTY_ARRAY: // first in array
50676d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson                replaceTop(JsonScope.NONEMPTY_ARRAY);
50776d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson                newline();
50876d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson                break;
50976d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson
51076d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson            case NONEMPTY_ARRAY: // another in array
51176d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson                out.append(',');
51276d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson                newline();
51376d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson                break;
51476d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson
51576d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson            case DANGLING_NAME: // value for name
51676d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson                out.append(separator);
51776d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson                replaceTop(JsonScope.NONEMPTY_OBJECT);
51876d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson                break;
51976d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson
52076d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson            case NONEMPTY_DOCUMENT:
52176d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson                throw new IllegalStateException(
52276d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson                        "JSON must have only one top-level value.");
52376d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson
52476d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson            default:
52576d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson                throw new IllegalStateException("Nesting problem: " + stack);
52676d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson        }
52776d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson    }
52876d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson}
529