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