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