Statistics
| Branch: | Tag: | Revision:

root / src / org / json / JSONWriter.java @ 4bf05cad

History | View | Annotate | Download (10 kB)

1
package org.json;
2

    
3
import java.io.IOException;
4
import java.io.Writer;
5

    
6
/*
7
Copyright (c) 2006 JSON.org
8

9
Permission is hereby granted, free of charge, to any person obtaining a copy
10
of this software and associated documentation files (the "Software"), to deal
11
in the Software without restriction, including without limitation the rights
12
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13
copies of the Software, and to permit persons to whom the Software is
14
furnished to do so, subject to the following conditions:
15

16
The above copyright notice and this permission notice shall be included in all
17
copies or substantial portions of the Software.
18

19
The Software shall be used for Good, not Evil.
20

21
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
22
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
24
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
26
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
27
SOFTWARE.
28
*/
29

    
30
/**
31
 * JSONWriter provides a quick and convenient way of producing JSON text.
32
 * The texts produced strictly conform to JSON syntax rules. No whitespace is
33
 * added, so the results are ready for transmission or storage. Each instance of
34
 * JSONWriter can produce one JSON text.
35
 * <p>
36
 * A JSONWriter instance provides a <code>value</code> method for appending
37
 * values to the
38
 * text, and a <code>key</code>
39
 * method for adding keys before values in objects. There are <code>array</code>
40
 * and <code>endArray</code> methods that make and bound array values, and
41
 * <code>object</code> and <code>endObject</code> methods which make and bound
42
 * object values. All of these methods return the JSONWriter instance,
43
 * permitting a cascade style. For example, <pre>
44
 * new JSONWriter(myWriter)
45
 *     .object()
46
 *         .key("JSON")
47
 *         .value("Hello, World!")
48
 *     .endObject();</pre> which writes <pre>
49
 * {"JSON":"Hello, World!"}</pre>
50
 * <p>
51
 * The first method called must be <code>array</code> or <code>object</code>.
52
 * There are no methods for adding commas or colons. JSONWriter adds them for
53
 * you. Objects and arrays can be nested up to 20 levels deep.
54
 * <p>
55
 * This can sometimes be easier than using a JSONObject to build a string.
56
 * @author JSON.org
57
 * @version 2008-09-18
58
 */
59
public class JSONWriter {
60
    private static final int maxdepth = 20;
61

    
62
    /**
63
     * The comma flag determines if a comma should be output before the next
64
     * value.
65
     */
66
    private boolean comma;
67

    
68
    /**
69
     * The current mode. Values:
70
     * 'a' (array),
71
     * 'd' (done),
72
     * 'i' (initial),
73
     * 'k' (key),
74
     * 'o' (object).
75
     */
76
    protected char mode;
77

    
78
    /**
79
     * The object/array stack.
80
     */
81
    private JSONObject stack[];
82

    
83
    /**
84
     * The stack top index. A value of 0 indicates that the stack is empty.
85
     */
86
    private int top;
87

    
88
    /**
89
     * The writer that will receive the output.
90
     */
91
    protected Writer writer;
92

    
93
    /**
94
     * Make a fresh JSONWriter. It can be used to build one JSON text.
95
     */
96
    public JSONWriter(Writer w) {
97
        this.comma = false;
98
        this.mode = 'i';
99
        this.stack = new JSONObject[maxdepth];
100
        this.top = 0;
101
        this.writer = w;
102
    }
103

    
104
    /**
105
     * Append a value.
106
     * @param s A string value.
107
     * @return this
108
     * @throws JSONException If the value is out of sequence.
109
     */
110
    private JSONWriter append(String s) throws JSONException {
111
        if (s == null) {
112
            throw new JSONException("Null pointer");
113
        }
114
        if (this.mode == 'o' || this.mode == 'a') {
115
            try {
116
                if (this.comma && this.mode == 'a') {
117
                    this.writer.write(',');
118
                }
119
                this.writer.write(s);
120
            } catch (IOException e) {
121
                throw new JSONException(e);
122
            }
123
            if (this.mode == 'o') {
124
                this.mode = 'k';
125
            }
126
            this.comma = true;
127
            return this;
128
        }
129
        throw new JSONException("Value out of sequence.");
130
    }
131

    
132
    /**
133
     * Begin appending a new array. All values until the balancing
134
     * <code>endArray</code> will be appended to this array. The
135
     * <code>endArray</code> method must be called to mark the array's end.
136
     * @return this
137
     * @throws JSONException If the nesting is too deep, or if the object is
138
     * started in the wrong place (for example as a key or after the end of the
139
     * outermost array or object).
140
     */
141
    public JSONWriter array() throws JSONException {
142
        if (this.mode == 'i' || this.mode == 'o' || this.mode == 'a') {
143
            this.push(null);
144
            this.append("[");
145
            this.comma = false;
146
            return this;
147
        }
148
        throw new JSONException("Misplaced array.");
149
    }
150

    
151
    /**
152
     * End something.
153
     * @param m Mode
154
     * @param c Closing character
155
     * @return this
156
     * @throws JSONException If unbalanced.
157
     */
158
    private JSONWriter end(char m, char c) throws JSONException {
159
        if (this.mode != m) {
160
            throw new JSONException(m == 'o' ? "Misplaced endObject." :
161
                "Misplaced endArray.");
162
        }
163
        this.pop(m);
164
        try {
165
            this.writer.write(c);
166
        } catch (IOException e) {
167
            throw new JSONException(e);
168
        }
169
        this.comma = true;
170
        return this;
171
    }
172

    
173
    /**
174
     * End an array. This method most be called to balance calls to
175
     * <code>array</code>.
176
     * @return this
177
     * @throws JSONException If incorrectly nested.
178
     */
179
    public JSONWriter endArray() throws JSONException {
180
        return this.end('a', ']');
181
    }
182

    
183
    /**
184
     * End an object. This method most be called to balance calls to
185
     * <code>object</code>.
186
     * @return this
187
     * @throws JSONException If incorrectly nested.
188
     */
189
    public JSONWriter endObject() throws JSONException {
190
        return this.end('k', '}');
191
    }
192

    
193
    /**
194
     * Append a key. The key will be associated with the next value. In an
195
     * object, every value must be preceded by a key.
196
     * @param s A key string.
197
     * @return this
198
     * @throws JSONException If the key is out of place. For example, keys
199
     *  do not belong in arrays or if the key is null.
200
     */
201
    public JSONWriter key(String s) throws JSONException {
202
        if (s == null) {
203
            throw new JSONException("Null key.");
204
        }
205
        if (this.mode == 'k') {
206
            try {
207
                if (this.comma) {
208
                    this.writer.write(',');
209
                }
210
                stack[top - 1].putOnce(s, Boolean.TRUE);
211
                this.writer.write(JSONObject.quote(s));
212
                this.writer.write(':');
213
                this.comma = false;
214
                this.mode = 'o';
215
                return this;
216
            } catch (IOException e) {
217
                throw new JSONException(e);
218
            }
219
        }
220
        throw new JSONException("Misplaced key.");
221
    }
222

    
223

    
224
    /**
225
     * Begin appending a new object. All keys and values until the balancing
226
     * <code>endObject</code> will be appended to this object. The
227
     * <code>endObject</code> method must be called to mark the object's end.
228
     * @return this
229
     * @throws JSONException If the nesting is too deep, or if the object is
230
     * started in the wrong place (for example as a key or after the end of the
231
     * outermost array or object).
232
     */
233
    public JSONWriter object() throws JSONException {
234
        if (this.mode == 'i') {
235
            this.mode = 'o';
236
        }
237
        if (this.mode == 'o' || this.mode == 'a') {
238
            this.append("{");
239
            this.push(new JSONObject());
240
            this.comma = false;
241
            return this;
242
        }
243
        throw new JSONException("Misplaced object.");
244

    
245
    }
246

    
247

    
248
    /**
249
     * Pop an array or object scope.
250
     * @param c The scope to close.
251
     * @throws JSONException If nesting is wrong.
252
     */
253
    private void pop(char c) throws JSONException {
254
        if (this.top <= 0) {
255
            throw new JSONException("Nesting error.");
256
        }
257
        char m = this.stack[this.top - 1] == null ? 'a' : 'k';
258
        if (m != c) {
259
            throw new JSONException("Nesting error.");
260
        }
261
        this.top -= 1;
262
        this.mode = this.top == 0 ? 'd' : this.stack[this.top - 1] == null ? 'a' : 'k';
263
    }
264

    
265
    /**
266
     * Push an array or object scope.
267
     * @param c The scope to open.
268
     * @throws JSONException If nesting is too deep.
269
     */
270
    private void push(JSONObject jo) throws JSONException {
271
        if (this.top >= maxdepth) {
272
            throw new JSONException("Nesting too deep.");
273
        }
274
        this.stack[this.top] = jo;
275
        this.mode = jo == null ? 'a' : 'k';
276
        this.top += 1;
277
    }
278

    
279

    
280
    /**
281
     * Append either the value <code>true</code> or the value
282
     * <code>false</code>.
283
     * @param b A boolean.
284
     * @return this
285
     * @throws JSONException
286
     */
287
    public JSONWriter value(boolean b) throws JSONException {
288
        return this.append(b ? "true" : "false");
289
    }
290

    
291
    /**
292
     * Append a double value.
293
     * @param d A double.
294
     * @return this
295
     * @throws JSONException If the number is not finite.
296
     */
297
    public JSONWriter value(double d) throws JSONException {
298
        return this.value(new Double(d));
299
    }
300

    
301
    /**
302
     * Append a long value.
303
     * @param l A long.
304
     * @return this
305
     * @throws JSONException
306
     */
307
    public JSONWriter value(long l) throws JSONException {
308
        return this.append(Long.toString(l));
309
    }
310

    
311

    
312
    /**
313
     * Append an object value.
314
     * @param o The object to append. It can be null, or a Boolean, Number,
315
     *   String, JSONObject, or JSONArray, or an object with a toJSONString()
316
     *   method.
317
     * @return this
318
     * @throws JSONException If the value is out of sequence.
319
     */
320
    public JSONWriter value(Object o) throws JSONException {
321
        return this.append(JSONObject.valueToString(o));
322
    }
323
}