1/*
2 * Copyright (C) 2010 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package org.json;
18
19import java.util.ArrayList;
20import java.util.Collection;
21import java.util.List;
22
23// Note: this class was written without inspecting the non-free org.json sourcecode.
24
25/**
26 * A dense indexed sequence of values. Values may be any mix of
27 * {@link JSONObject JSONObjects}, other {@link JSONArray JSONArrays}, Strings,
28 * Booleans, Integers, Longs, Doubles, {@code null} or {@link JSONObject#NULL}.
29 * Values may not be {@link Double#isNaN() NaNs}, {@link Double#isInfinite()
30 * infinities}, or of any type not listed here.
31 *
32 * <p>{@code JSONArray} has the same type coercion behavior and
33 * optional/mandatory accessors as {@link JSONObject}. See that class'
34 * documentation for details.
35 *
36 * <p><strong>Warning:</strong> this class represents null in two incompatible
37 * ways: the standard Java {@code null} reference, and the sentinel value {@link
38 * JSONObject#NULL}. In particular, {@code get} fails if the requested index
39 * holds the null reference, but succeeds if it holds {@code JSONObject.NULL}.
40 *
41 * <p>Instances of this class are not thread safe. Although this class is
42 * nonfinal, it was not designed for inheritance and should not be subclassed.
43 * In particular, self-use by overridable methods is not specified. See
44 * <i>Effective Java</i> Item 17, "Design and Document or inheritance or else
45 * prohibit it" for further information.
46 */
47public class JSONArray {
48
49    private final List<Object> values;
50
51    /**
52     * Creates a {@code JSONArray} with no values.
53     */
54    public JSONArray() {
55        values = new ArrayList<Object>();
56    }
57
58    /**
59     * Creates a new {@code JSONArray} by copying all values from the given
60     * collection.
61     *
62     * @param copyFrom a collection whose values are of supported types.
63     *     Unsupported values are not permitted and will yield an array in an
64     *     inconsistent state.
65     */
66    /* Accept a raw type for API compatibility */
67    public JSONArray(Collection copyFrom) {
68        this();
69        Collection<?> copyFromTyped = (Collection<?>) copyFrom;
70        values.addAll(copyFromTyped);
71    }
72
73    /**
74     * Creates a new {@code JSONArray} with values from the next array in the
75     * tokener.
76     *
77     * @param readFrom a tokener whose nextValue() method will yield a
78     *     {@code JSONArray}.
79     * @throws JSONException if the parse fails or doesn't yield a
80     *     {@code JSONArray}.
81     */
82    public JSONArray(JSONTokener readFrom) throws JSONException {
83        /*
84         * Getting the parser to populate this could get tricky. Instead, just
85         * parse to temporary JSONArray and then steal the data from that.
86         */
87        Object object = readFrom.nextValue();
88        if (object instanceof JSONArray) {
89            values = ((JSONArray) object).values;
90        } else {
91            throw JSON.typeMismatch(object, "JSONArray");
92        }
93    }
94
95    /**
96     * Creates a new {@code JSONArray} with values from the JSON string.
97     *
98     * @param json a JSON-encoded string containing an array.
99     * @throws JSONException if the parse fails or doesn't yield a {@code
100     *     JSONArray}.
101     */
102    public JSONArray(String json) throws JSONException {
103        this(new JSONTokener(json));
104    }
105
106    /**
107     * Returns the number of values in this array.
108     */
109    public int length() {
110        return values.size();
111    }
112
113    /**
114     * Appends {@code value} to the end of this array.
115     *
116     * @return this array.
117     */
118    public JSONArray put(boolean value) {
119        values.add(value);
120        return this;
121    }
122
123    /**
124     * Appends {@code value} to the end of this array.
125     *
126     * @param value a finite value. May not be {@link Double#isNaN() NaNs} or
127     *     {@link Double#isInfinite() infinities}.
128     * @return this array.
129     */
130    public JSONArray put(double value) throws JSONException {
131        values.add(JSON.checkDouble(value));
132        return this;
133    }
134
135    /**
136     * Appends {@code value} to the end of this array.
137     *
138     * @return this array.
139     */
140    public JSONArray put(int value) {
141        values.add(value);
142        return this;
143    }
144
145    /**
146     * Appends {@code value} to the end of this array.
147     *
148     * @return this array.
149     */
150    public JSONArray put(long value) {
151        values.add(value);
152        return this;
153    }
154
155    /**
156     * Appends {@code value} to the end of this array.
157     *
158     * @param value a {@link JSONObject}, {@link JSONArray}, String, Boolean,
159     *     Integer, Long, Double, {@link JSONObject#NULL}, or {@code null}. May
160     *     not be {@link Double#isNaN() NaNs} or {@link Double#isInfinite()
161     *     infinities}. Unsupported values are not permitted and will cause the
162     *     array to be in an inconsistent state.
163     * @return this array.
164     */
165    public JSONArray put(Object value) {
166        values.add(value);
167        return this;
168    }
169
170    /**
171     * Sets the value at {@code index} to {@code value}, null padding this array
172     * to the required length if necessary. If a value already exists at {@code
173     * index}, it will be replaced.
174     *
175     * @return this array.
176     */
177    public JSONArray put(int index, boolean value) throws JSONException {
178        return put(index, (Boolean) value);
179    }
180
181    /**
182     * Sets the value at {@code index} to {@code value}, null padding this array
183     * to the required length if necessary. If a value already exists at {@code
184     * index}, it will be replaced.
185     *
186     * @param value a finite value. May not be {@link Double#isNaN() NaNs} or
187     *     {@link Double#isInfinite() infinities}.
188     * @return this array.
189     */
190    public JSONArray put(int index, double value) throws JSONException {
191        return put(index, (Double) value);
192    }
193
194    /**
195     * Sets the value at {@code index} to {@code value}, null padding this array
196     * to the required length if necessary. If a value already exists at {@code
197     * index}, it will be replaced.
198     *
199     * @return this array.
200     */
201    public JSONArray put(int index, int value) throws JSONException {
202        return put(index, (Integer) value);
203    }
204
205    /**
206     * Sets the value at {@code index} to {@code value}, null padding this array
207     * to the required length if necessary. If a value already exists at {@code
208     * index}, it will be replaced.
209     *
210     * @return this array.
211     */
212    public JSONArray put(int index, long value) throws JSONException {
213        return put(index, (Long) value);
214    }
215
216    /**
217     * Sets the value at {@code index} to {@code value}, null padding this array
218     * to the required length if necessary. If a value already exists at {@code
219     * index}, it will be replaced.
220     *
221     * @param value a {@link JSONObject}, {@link JSONArray}, String, Boolean,
222     *     Integer, Long, Double, {@link JSONObject#NULL}, or {@code null}. May
223     *     not be {@link Double#isNaN() NaNs} or {@link Double#isInfinite()
224     *     infinities}.
225     * @return this array.
226     */
227    public JSONArray put(int index, Object value) throws JSONException {
228        if (value instanceof Number) {
229            // deviate from the original by checking all Numbers, not just floats & doubles
230            JSON.checkDouble(((Number) value).doubleValue());
231        }
232        while (values.size() <= index) {
233            values.add(null);
234        }
235        values.set(index, value);
236        return this;
237    }
238
239    /**
240     * Returns true if this array has no value at {@code index}, or if its value
241     * is the {@code null} reference or {@link JSONObject#NULL}.
242     */
243    public boolean isNull(int index) {
244        Object value = opt(index);
245        return value == null || value == JSONObject.NULL;
246    }
247
248    /**
249     * Returns the value at {@code index}.
250     *
251     * @throws JSONException if this array has no value at {@code index}, or if
252     *     that value is the {@code null} reference. This method returns
253     *     normally if the value is {@code JSONObject#NULL}.
254     */
255    public Object get(int index) throws JSONException {
256        try {
257            Object value = values.get(index);
258            if (value == null) {
259                throw new JSONException("Value at " + index + " is null.");
260            }
261            return value;
262        } catch (IndexOutOfBoundsException e) {
263            throw new JSONException("Index " + index + " out of range [0.." + values.size() + ")");
264        }
265    }
266
267    /**
268     * Returns the value at {@code index}, or null if the array has no value
269     * at {@code index}.
270     */
271    public Object opt(int index) {
272        if (index < 0 || index >= values.size()) {
273            return null;
274        }
275        return values.get(index);
276    }
277
278    /**
279     * Returns the value at {@code index} if it exists and is a boolean or can
280     * be coerced to a boolean.
281     *
282     * @throws JSONException if the value at {@code index} doesn't exist or
283     *     cannot be coerced to a boolean.
284     */
285    public boolean getBoolean(int index) throws JSONException {
286        Object object = get(index);
287        Boolean result = JSON.toBoolean(object);
288        if (result == null) {
289            throw JSON.typeMismatch(index, object, "boolean");
290        }
291        return result;
292    }
293
294    /**
295     * Returns the value at {@code index} if it exists and is a boolean or can
296     * be coerced to a boolean. Returns false otherwise.
297     */
298    public boolean optBoolean(int index) {
299        return optBoolean(index, false);
300    }
301
302    /**
303     * Returns the value at {@code index} if it exists and is a boolean or can
304     * be coerced to a boolean. Returns {@code fallback} otherwise.
305     */
306    public boolean optBoolean(int index, boolean fallback) {
307        Object object = opt(index);
308        Boolean result = JSON.toBoolean(object);
309        return result != null ? result : fallback;
310    }
311
312    /**
313     * Returns the value at {@code index} if it exists and is a double or can
314     * be coerced to a double.
315     *
316     * @throws JSONException if the value at {@code index} doesn't exist or
317     *     cannot be coerced to a double.
318     */
319    public double getDouble(int index) throws JSONException {
320        Object object = get(index);
321        Double result = JSON.toDouble(object);
322        if (result == null) {
323            throw JSON.typeMismatch(index, object, "double");
324        }
325        return result;
326    }
327
328    /**
329     * Returns the value at {@code index} if it exists and is a double or can
330     * be coerced to a double. Returns {@code NaN} otherwise.
331     */
332    public double optDouble(int index) {
333        return optDouble(index, Double.NaN);
334    }
335
336    /**
337     * Returns the value at {@code index} if it exists and is a double or can
338     * be coerced to a double. Returns {@code fallback} otherwise.
339     */
340    public double optDouble(int index, double fallback) {
341        Object object = opt(index);
342        Double result = JSON.toDouble(object);
343        return result != null ? result : fallback;
344    }
345
346    /**
347     * Returns the value at {@code index} if it exists and is an int or
348     * can be coerced to an int.
349     *
350     * @throws JSONException if the value at {@code index} doesn't exist or
351     *     cannot be coerced to a int.
352     */
353    public int getInt(int index) throws JSONException {
354        Object object = get(index);
355        Integer result = JSON.toInteger(object);
356        if (result == null) {
357            throw JSON.typeMismatch(index, object, "int");
358        }
359        return result;
360    }
361
362    /**
363     * Returns the value at {@code index} if it exists and is an int or
364     * can be coerced to an int. Returns 0 otherwise.
365     */
366    public int optInt(int index) {
367        return optInt(index, 0);
368    }
369
370    /**
371     * Returns the value at {@code index} if it exists and is an int or
372     * can be coerced to an int. Returns {@code fallback} otherwise.
373     */
374    public int optInt(int index, int fallback) {
375        Object object = opt(index);
376        Integer result = JSON.toInteger(object);
377        return result != null ? result : fallback;
378    }
379
380    /**
381     * Returns the value at {@code index} if it exists and is a long or
382     * can be coerced to a long.
383     *
384     * @throws JSONException if the value at {@code index} doesn't exist or
385     *     cannot be coerced to a long.
386     */
387    public long getLong(int index) throws JSONException {
388        Object object = get(index);
389        Long result = JSON.toLong(object);
390        if (result == null) {
391            throw JSON.typeMismatch(index, object, "long");
392        }
393        return result;
394    }
395
396    /**
397     * Returns the value at {@code index} if it exists and is a long or
398     * can be coerced to a long. Returns 0 otherwise.
399     */
400    public long optLong(int index) {
401        return optLong(index, 0L);
402    }
403
404    /**
405     * Returns the value at {@code index} if it exists and is a long or
406     * can be coerced to a long. Returns {@code fallback} otherwise.
407     */
408    public long optLong(int index, long fallback) {
409        Object object = opt(index);
410        Long result = JSON.toLong(object);
411        return result != null ? result : fallback;
412    }
413
414    /**
415     * Returns the value at {@code index} if it exists, coercing it if
416     * necessary.
417     *
418     * @throws JSONException if no such value exists.
419     */
420    public String getString(int index) throws JSONException {
421        Object object = get(index);
422        String result = JSON.toString(object);
423        if (result == null) {
424            throw JSON.typeMismatch(index, object, "String");
425        }
426        return result;
427    }
428
429    /**
430     * Returns the value at {@code index} if it exists, coercing it if
431     * necessary. Returns the empty string if no such value exists.
432     */
433    public String optString(int index) {
434        return optString(index, "");
435    }
436
437    /**
438     * Returns the value at {@code index} if it exists, coercing it if
439     * necessary. Returns {@code fallback} if no such value exists.
440     */
441    public String optString(int index, String fallback) {
442        Object object = opt(index);
443        String result = JSON.toString(object);
444        return result != null ? result : fallback;
445    }
446
447    /**
448     * Returns the value at {@code index} if it exists and is a {@code
449     * JSONArray}.
450     *
451     * @throws JSONException if the value doesn't exist or is not a {@code
452     *     JSONArray}.
453     */
454    public JSONArray getJSONArray(int index) throws JSONException {
455        Object object = get(index);
456        if (object instanceof JSONArray) {
457            return (JSONArray) object;
458        } else {
459            throw JSON.typeMismatch(index, object, "JSONArray");
460        }
461    }
462
463    /**
464     * Returns the value at {@code index} if it exists and is a {@code
465     * JSONArray}. Returns null otherwise.
466     */
467    public JSONArray optJSONArray(int index) {
468        Object object = opt(index);
469        return object instanceof JSONArray ? (JSONArray) object : null;
470    }
471
472    /**
473     * Returns the value at {@code index} if it exists and is a {@code
474     * JSONObject}.
475     *
476     * @throws JSONException if the value doesn't exist or is not a {@code
477     *     JSONObject}.
478     */
479    public JSONObject getJSONObject(int index) throws JSONException {
480        Object object = get(index);
481        if (object instanceof JSONObject) {
482            return (JSONObject) object;
483        } else {
484            throw JSON.typeMismatch(index, object, "JSONObject");
485        }
486    }
487
488    /**
489     * Returns the value at {@code index} if it exists and is a {@code
490     * JSONObject}. Returns null otherwise.
491     */
492    public JSONObject optJSONObject(int index) {
493        Object object = opt(index);
494        return object instanceof JSONObject ? (JSONObject) object : null;
495    }
496
497    /**
498     * Returns a new object whose values are the values in this array, and whose
499     * names are the values in {@code names}. Names and values are paired up by
500     * index from 0 through to the shorter array's length. Names that are not
501     * strings will be coerced to strings. This method returns null if either
502     * array is empty.
503     */
504    public JSONObject toJSONObject(JSONArray names) throws JSONException {
505        JSONObject result = new JSONObject();
506        int length = Math.min(names.length(), values.size());
507        if (length == 0) {
508            return null;
509        }
510        for (int i = 0; i < length; i++) {
511            String name = JSON.toString(names.opt(i));
512            result.put(name, opt(i));
513        }
514        return result;
515    }
516
517    /**
518     * Returns a new string by alternating this array's values with {@code
519     * separator}. This array's string values are quoted and have their special
520     * characters escaped. For example, the array containing the strings '12"
521     * pizza', 'taco' and 'soda' joined on '+' returns this:
522     * <pre>"12\" pizza"+"taco"+"soda"</pre>
523     */
524    public String join(String separator) throws JSONException {
525        JSONStringer stringer = new JSONStringer();
526        stringer.open(JSONStringer.Scope.NULL, "");
527        for (int i = 0, size = values.size(); i < size; i++) {
528            if (i > 0) {
529                stringer.out.append(separator);
530            }
531            stringer.value(values.get(i));
532        }
533        stringer.close(JSONStringer.Scope.NULL, JSONStringer.Scope.NULL, "");
534        return stringer.out.toString();
535    }
536
537    /**
538     * Encodes this array as a compact JSON string, such as:
539     * <pre>[94043,90210]</pre>
540     */
541    @Override public String toString() {
542        try {
543            JSONStringer stringer = new JSONStringer();
544            writeTo(stringer);
545            return stringer.toString();
546        } catch (JSONException e) {
547            return null;
548        }
549    }
550
551    /**
552     * Encodes this array as a human readable JSON string for debugging, such
553     * as:
554     * <pre>
555     * [
556     *     94043,
557     *     90210
558     * ]</pre>
559     *
560     * @param indentSpaces the number of spaces to indent for each level of
561     *     nesting.
562     */
563    public String toString(int indentSpaces) throws JSONException {
564        JSONStringer stringer = new JSONStringer(indentSpaces);
565        writeTo(stringer);
566        return stringer.toString();
567    }
568
569    void writeTo(JSONStringer stringer) throws JSONException {
570        stringer.array();
571        for (Object value : values) {
572            stringer.value(value);
573        }
574        stringer.endArray();
575    }
576
577    @Override public boolean equals(Object o) {
578        return o instanceof JSONArray && ((JSONArray) o).values.equals(values);
579    }
580
581    @Override public int hashCode() {
582        // diverge from the original, which doesn't implement hashCode
583        return values.hashCode();
584    }
585}
586