1package org.json;
2
3/*
4Copyright (c) 2002 JSON.org
5
6Permission is hereby granted, free of charge, to any person obtaining a copy
7of this software and associated documentation files (the "Software"), to deal
8in the Software without restriction, including without limitation the rights
9to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10copies of the Software, and to permit persons to whom the Software is
11furnished to do so, subject to the following conditions:
12
13The above copyright notice and this permission notice shall be included in all
14copies or substantial portions of the Software.
15
16The Software shall be used for Good, not Evil.
17
18THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
24SOFTWARE.
25*/
26
27import java.util.ArrayList;
28import java.util.Collection;
29
30/**
31 * A JSONArray is an ordered sequence of values. Its external text form is a
32 * string wrapped in square brackets with commas separating the values. The
33 * internal form is an object having <code>get</code> and <code>opt</code>
34 * methods for accessing the values by index, and <code>put</code> methods for
35 * adding or replacing values. The values can be any of these types:
36 * <code>Boolean</code>, <code>JSONArray</code>, <code>JSONObject</code>,
37 * <code>Number</code>, <code>String</code>, or the
38 * <code>JSONObject.NULL object</code>.
39 * <p>
40 * The constructor can convert a JSON text into a Java object. The
41 * <code>toString</code> method converts to JSON text.
42 * <p>
43 * A <code>get</code> method returns a value if one can be found, and throws an
44 * exception if one cannot be found. An <code>opt</code> method returns a
45 * default value instead of throwing an exception, and so is useful for
46 * obtaining optional values.
47 * <p>
48 * The generic <code>get()</code> and <code>opt()</code> methods return an
49 * object which you can cast or query for type. There are also typed
50 * <code>get</code> and <code>opt</code> methods that do type checking and type
51 * coersion for you.
52 * <p>
53 * The texts produced by the <code>toString</code> methods strictly conform to
54 * JSON syntax rules. The constructors are more forgiving in the texts they will
55 * accept:
56 * <ul>
57 * <li>An extra <code>,</code>&nbsp;<small>(comma)</small> may appear just
58 *     before the closing bracket.</li>
59 * <li>The <code>null</code> value will be inserted when there
60 *     is <code>,</code>&nbsp;<small>(comma)</small> elision.</li>
61 * <li>Strings may be quoted with <code>'</code>&nbsp;<small>(single
62 *     quote)</small>.</li>
63 * <li>Strings do not need to be quoted at all if they do not begin with a quote
64 *     or single quote, and if they do not contain leading or trailing spaces,
65 *     and if they do not contain any of these characters:
66 *     <code>{ } [ ] / \ : , = ; #</code> and if they do not look like numbers
67 *     and if they are not the reserved words <code>true</code>,
68 *     <code>false</code>, or <code>null</code>.</li>
69 * <li>Values can be separated by <code>;</code> <small>(semicolon)</small> as
70 *     well as by <code>,</code> <small>(comma)</small>.</li>
71 * <li>Numbers may have the <code>0-</code> <small>(octal)</small> or
72 *     <code>0x-</code> <small>(hex)</small> prefix.</li>
73 * <li>Comments written in the slashshlash, slashstar, and hash conventions
74 *     will be ignored.</li>
75 * </ul>
76
77 * @author JSON.org
78 * @version 2
79 */
80public class JSONArray {
81
82
83    /**
84     * The arrayList where the JSONArray's properties are kept.
85     */
86    private ArrayList myArrayList;
87
88
89    /**
90     * Construct an empty JSONArray.
91     */
92    public JSONArray() {
93        this.myArrayList = new ArrayList();
94    }
95
96    /**
97     * Construct a JSONArray from a JSONTokener.
98     * @param x A JSONTokener
99     * @throws JSONException If there is a syntax error.
100     */
101    public JSONArray(JSONTokener x) throws JSONException {
102        this();
103        if (x.nextClean() != '[') {
104            throw x.syntaxError("A JSONArray text must start with '['");
105        }
106        if (x.nextClean() == ']') {
107            return;
108        }
109        x.back();
110        for (;;) {
111            if (x.nextClean() == ',') {
112                x.back();
113                this.myArrayList.add(null);
114            } else {
115                x.back();
116                this.myArrayList.add(x.nextValue());
117            }
118            switch (x.nextClean()) {
119            case ';':
120            case ',':
121                if (x.nextClean() == ']') {
122                    this.myArrayList.add(null);
123                    return;
124                }
125                x.back();
126                break;
127            case ']':
128                return;
129            default:
130                throw x.syntaxError("Expected a ',' or ']'");
131            }
132        }
133    }
134
135
136    public boolean equals(Object object) {
137        if (!(object instanceof JSONArray)) return false;
138        return myArrayList.equals(((JSONArray)object).myArrayList);
139    }
140
141
142    /**
143     * Construct a JSONArray from a source sJSON text.
144     * @param string     A string that begins with
145     * <code>[</code>&nbsp;<small>(left bracket)</small>
146     *  and ends with <code>]</code>&nbsp;<small>(right bracket)</small>.
147     *  @throws JSONException If there is a syntax error.
148     */
149    public JSONArray(String string) throws JSONException {
150        this(new JSONTokener(string));
151    }
152
153
154    /**
155     * Construct a JSONArray from a Collection.
156     * @param collection     A Collection.
157     */
158    public JSONArray(Collection collection) {
159        this.myArrayList = new ArrayList(collection);
160    }
161
162
163    /**
164     * Get the object value associated with an index.
165     * @param index
166     *  The index must be between 0 and length() - 1.
167     * @return An object value.
168     * @throws JSONException If there is no value for the index.
169     */
170    public Object get(int index) throws JSONException {
171        Object o = opt(index);
172        if (o == null) {
173            throw new JSONException("JSONArray[" + index + "] not found.");
174        }
175        return o;
176    }
177
178
179    /**
180     * Get the boolean value associated with an index.
181     * The string values "true" and "false" are converted to boolean.
182     *
183     * @param index The index must be between 0 and length() - 1.
184     * @return      The truth.
185     * @throws JSONException If there is no value for the index or if the
186     *  value is not convertable to boolean.
187     */
188    public boolean getBoolean(int index) throws JSONException {
189        Object o = get(index);
190        if (o.equals(Boolean.FALSE) ||
191                (o instanceof String &&
192                ((String)o).equalsIgnoreCase("false"))) {
193            return false;
194        } else if (o.equals(Boolean.TRUE) ||
195                (o instanceof String &&
196                ((String)o).equalsIgnoreCase("true"))) {
197            return true;
198        }
199        throw new JSONException("JSONArray[" + index + "] is not a Boolean.");
200    }
201
202
203    /**
204     * Get the double value associated with an index.
205     *
206     * @param index The index must be between 0 and length() - 1.
207     * @return      The value.
208     * @throws   JSONException If the key is not found or if the value cannot
209     *  be converted to a number.
210     */
211    public double getDouble(int index) throws JSONException {
212        Object o = get(index);
213        try {
214            return o instanceof Number ?
215                    ((Number)o).doubleValue() : Double.parseDouble((String)o);
216        } catch (Exception e) {
217            throw new JSONException("JSONArray[" + index +
218                "] is not a number.");
219        }
220    }
221
222
223    /**
224     * Get the int value associated with an index.
225     *
226     * @param index The index must be between 0 and length() - 1.
227     * @return      The value.
228     * @throws   JSONException If the key is not found or if the value cannot
229     *  be converted to a number.
230     *  if the value cannot be converted to a number.
231     */
232    public int getInt(int index) throws JSONException {
233        Object o = get(index);
234        return o instanceof Number ?
235                ((Number)o).intValue() : (int)getDouble(index);
236    }
237
238
239    /**
240     * Get the JSONArray associated with an index.
241     * @param index The index must be between 0 and length() - 1.
242     * @return      A JSONArray value.
243     * @throws JSONException If there is no value for the index. or if the
244     * value is not a JSONArray
245     */
246    public JSONArray getJSONArray(int index) throws JSONException {
247        Object o = get(index);
248        if (o instanceof JSONArray) {
249            return (JSONArray)o;
250        }
251        throw new JSONException("JSONArray[" + index +
252                "] is not a JSONArray.");
253    }
254
255
256    /**
257     * Get the JSONObject associated with an index.
258     * @param index subscript
259     * @return      A JSONObject value.
260     * @throws JSONException If there is no value for the index or if the
261     * value is not a JSONObject
262     */
263    public JSONObject getJSONObject(int index) throws JSONException {
264        Object o = get(index);
265        if (o instanceof JSONObject) {
266            return (JSONObject)o;
267        }
268        throw new JSONException("JSONArray[" + index +
269            "] is not a JSONObject.");
270    }
271
272
273    /**
274     * Get the long value associated with an index.
275     *
276     * @param index The index must be between 0 and length() - 1.
277     * @return      The value.
278     * @throws   JSONException If the key is not found or if the value cannot
279     *  be converted to a number.
280     */
281    public long getLong(int index) throws JSONException {
282        Object o = get(index);
283        return o instanceof Number ?
284                ((Number)o).longValue() : (long)getDouble(index);
285    }
286
287
288    /**
289     * Get the string associated with an index.
290     * @param index The index must be between 0 and length() - 1.
291     * @return      A string value.
292     * @throws JSONException If there is no value for the index.
293     */
294    public String getString(int index) throws JSONException {
295        return get(index).toString();
296    }
297
298
299    /**
300     * Determine if the value is null.
301     * @param index The index must be between 0 and length() - 1.
302     * @return true if the value at the index is null, or if there is no value.
303     */
304    public boolean isNull(int index) {
305        return JSONObject.NULL.equals(opt(index));
306    }
307
308
309    /**
310     * Make a string from the contents of this JSONArray. The
311     * <code>separator</code> string is inserted between each element.
312     * Warning: This method assumes that the data structure is acyclical.
313     * @param separator A string that will be inserted between the elements.
314     * @return a string.
315     * @throws JSONException If the array contains an invalid number.
316     */
317    public String join(String separator) throws JSONException {
318        int len = length();
319        StringBuilder sb = new StringBuilder();
320
321        for (int i = 0; i < len; i += 1) {
322            if (i > 0) {
323                sb.append(separator);
324            }
325            sb.append(JSONObject.valueToString(this.myArrayList.get(i)));
326        }
327        return sb.toString();
328    }
329
330
331    /**
332     * Get the number of elements in the JSONArray, included nulls.
333     *
334     * @return The length (or size).
335     */
336    public int length() {
337        return this.myArrayList.size();
338    }
339
340
341    /**
342     * Get the optional object value associated with an index.
343     * @param index The index must be between 0 and length() - 1.
344     * @return      An object value, or null if there is no
345     *              object at that index.
346     */
347    public Object opt(int index) {
348        return (index < 0 || index >= length()) ?
349            null : this.myArrayList.get(index);
350    }
351
352
353    /**
354     * Get the optional boolean value associated with an index.
355     * It returns false if there is no value at that index,
356     * or if the value is not Boolean.TRUE or the String "true".
357     *
358     * @param index The index must be between 0 and length() - 1.
359     * @return      The truth.
360     */
361    public boolean optBoolean(int index)  {
362        return optBoolean(index, false);
363    }
364
365
366    /**
367     * Get the optional boolean value associated with an index.
368     * It returns the defaultValue if there is no value at that index or if
369     * it is not a Boolean or the String "true" or "false" (case insensitive).
370     *
371     * @param index The index must be between 0 and length() - 1.
372     * @param defaultValue     A boolean default.
373     * @return      The truth.
374     */
375    public boolean optBoolean(int index, boolean defaultValue)  {
376        try {
377            return getBoolean(index);
378        } catch (Exception e) {
379            return defaultValue;
380        }
381    }
382
383
384    /**
385     * Get the optional double value associated with an index.
386     * NaN is returned if there is no value for the index,
387     * or if the value is not a number and cannot be converted to a number.
388     *
389     * @param index The index must be between 0 and length() - 1.
390     * @return      The value.
391     */
392    public double optDouble(int index) {
393        return optDouble(index, Double.NaN);
394    }
395
396
397    /**
398     * Get the optional double value associated with an index.
399     * The defaultValue is returned if there is no value for the index,
400     * or if the value is not a number and cannot be converted to a number.
401     *
402     * @param index subscript
403     * @param defaultValue     The default value.
404     * @return      The value.
405     */
406    public double optDouble(int index, double defaultValue) {
407        try {
408            return getDouble(index);
409        } catch (Exception e) {
410            return defaultValue;
411        }
412    }
413
414
415    /**
416     * Get the optional int value associated with an index.
417     * Zero is returned if there is no value for the index,
418     * or if the value is not a number and cannot be converted to a number.
419     *
420     * @param index The index must be between 0 and length() - 1.
421     * @return      The value.
422     */
423    public int optInt(int index) {
424        return optInt(index, 0);
425    }
426
427
428    /**
429     * Get the optional int value associated with an index.
430     * The defaultValue is returned if there is no value for the index,
431     * or if the value is not a number and cannot be converted to a number.
432     * @param index The index must be between 0 and length() - 1.
433     * @param defaultValue     The default value.
434     * @return      The value.
435     */
436    public int optInt(int index, int defaultValue) {
437        try {
438            return getInt(index);
439        } catch (Exception e) {
440            return defaultValue;
441        }
442    }
443
444
445    /**
446     * Get the optional JSONArray associated with an index.
447     * @param index subscript
448     * @return      A JSONArray value, or null if the index has no value,
449     * or if the value is not a JSONArray.
450     */
451    public JSONArray optJSONArray(int index) {
452        Object o = opt(index);
453        return o instanceof JSONArray ? (JSONArray)o : null;
454    }
455
456
457    /**
458     * Get the optional JSONObject associated with an index.
459     * Null is returned if the key is not found, or null if the index has
460     * no value, or if the value is not a JSONObject.
461     *
462     * @param index The index must be between 0 and length() - 1.
463     * @return      A JSONObject value.
464     */
465    public JSONObject optJSONObject(int index) {
466        Object o = opt(index);
467        return o instanceof JSONObject ? (JSONObject)o : null;
468    }
469
470
471    /**
472     * Get the optional long value associated with an index.
473     * Zero is returned if there is no value for the index,
474     * or if the value is not a number and cannot be converted to a number.
475     *
476     * @param index The index must be between 0 and length() - 1.
477     * @return      The value.
478     */
479    public long optLong(int index) {
480        return optLong(index, 0);
481    }
482
483
484    /**
485     * Get the optional long value associated with an index.
486     * The defaultValue is returned if there is no value for the index,
487     * or if the value is not a number and cannot be converted to a number.
488     * @param index The index must be between 0 and length() - 1.
489     * @param defaultValue     The default value.
490     * @return      The value.
491     */
492    public long optLong(int index, long defaultValue) {
493        try {
494            return getLong(index);
495        } catch (Exception e) {
496            return defaultValue;
497        }
498    }
499
500
501    /**
502     * Get the optional string value associated with an index. It returns an
503     * empty string if there is no value at that index. If the value
504     * is not a string and is not null, then it is coverted to a string.
505     *
506     * @param index The index must be between 0 and length() - 1.
507     * @return      A String value.
508     */
509    public String optString(int index) {
510        return optString(index, "");
511    }
512
513
514    /**
515     * Get the optional string associated with an index.
516     * The defaultValue is returned if the key is not found.
517     *
518     * @param index The index must be between 0 and length() - 1.
519     * @param defaultValue     The default value.
520     * @return      A String value.
521     */
522    public String optString(int index, String defaultValue) {
523        Object o = opt(index);
524        return o != null ? o.toString() : defaultValue;
525    }
526
527
528    /**
529     * Append a boolean value. This increases the array's length by one.
530     *
531     * @param value A boolean value.
532     * @return this.
533     */
534    public JSONArray put(boolean value) {
535        put(Boolean.valueOf(value));
536        return this;
537    }
538
539
540    /**
541     * Append a double value. This increases the array's length by one.
542     *
543     * @param value A double value.
544     * @throws JSONException if the value is not finite.
545     * @return this.
546     */
547    public JSONArray put(double value) throws JSONException {
548        Double d = new Double(value);
549        JSONObject.testValidity(d);
550        put(d);
551        return this;
552    }
553
554
555    /**
556     * Append an int value. This increases the array's length by one.
557     *
558     * @param value An int value.
559     * @return this.
560     */
561    public JSONArray put(int value) {
562        put(new Integer(value));
563        return this;
564    }
565
566
567    /**
568     * Append an long value. This increases the array's length by one.
569     *
570     * @param value A long value.
571     * @return this.
572     */
573    public JSONArray put(long value) {
574        put(new Long(value));
575        return this;
576    }
577
578
579    /**
580     * Append an object value. This increases the array's length by one.
581     * @param value An object value.  The value should be a
582     *  Boolean, Double, Integer, JSONArray, JSObject, Long, or String, or the
583     *  JSONObject.NULL object.
584     * @return this.
585     */
586    public JSONArray put(Object value) {
587        this.myArrayList.add(value);
588        return this;
589    }
590
591
592    /**
593     * Put or replace a boolean value in the JSONArray. If the index is greater
594     * than the length of the JSONArray, then null elements will be added as
595     * necessary to pad it out.
596     * @param index The subscript.
597     * @param value A boolean value.
598     * @return this.
599     * @throws JSONException If the index is negative.
600     */
601    public JSONArray put(int index, boolean value) throws JSONException {
602        put(index, Boolean.valueOf(value));
603        return this;
604    }
605
606
607    /**
608     * Put or replace a double value. If the index is greater than the length of
609     *  the JSONArray, then null elements will be added as necessary to pad
610     *  it out.
611     * @param index The subscript.
612     * @param value A double value.
613     * @return this.
614     * @throws JSONException If the index is negative or if the value is
615     * not finite.
616     */
617    public JSONArray put(int index, double value) throws JSONException {
618        put(index, new Double(value));
619        return this;
620    }
621
622
623    /**
624     * Put or replace an int value. If the index is greater than the length of
625     *  the JSONArray, then null elements will be added as necessary to pad
626     *  it out.
627     * @param index The subscript.
628     * @param value An int value.
629     * @return this.
630     * @throws JSONException If the index is negative.
631     */
632    public JSONArray put(int index, int value) throws JSONException {
633        put(index, new Integer(value));
634        return this;
635    }
636
637
638    /**
639     * Put or replace a long value. If the index is greater than the length of
640     *  the JSONArray, then null elements will be added as necessary to pad
641     *  it out.
642     * @param index The subscript.
643     * @param value A long value.
644     * @return this.
645     * @throws JSONException If the index is negative.
646     */
647    public JSONArray put(int index, long value) throws JSONException {
648        put(index, new Long(value));
649        return this;
650    }
651
652
653    /**
654     * Put or replace an object value in the JSONArray. If the index is greater
655     *  than the length of the JSONArray, then null elements will be added as
656     *  necessary to pad it out.
657     * @param index The subscript.
658     * @param value The value to put into the array.
659     * @return this.
660     * @throws JSONException If the index is negative or if the the value is
661     *     an invalid number.
662     */
663    public JSONArray put(int index, Object value) throws JSONException {
664        JSONObject.testValidity(value);
665        if (index < 0) {
666            throw new JSONException("JSONArray[" + index + "] not found.");
667        }
668        if (index < length()) {
669            this.myArrayList.set(index, value);
670        } else {
671            while (index != length()) {
672                put(null);
673            }
674            put(value);
675        }
676        return this;
677    }
678
679
680    /**
681     * Produce a JSONObject by combining a JSONArray of names with the values
682     * of this JSONArray.
683     * @param names A JSONArray containing a list of key strings. These will be
684     * paired with the values.
685     * @return A JSONObject, or null if there are no names or if this JSONArray
686     * has no values.
687     * @throws JSONException If any of the names are null.
688     */
689    public JSONObject toJSONObject(JSONArray names) throws JSONException {
690        if (names == null || names.length() == 0 || length() == 0) {
691            return null;
692        }
693        JSONObject jo = new JSONObject();
694        for (int i = 0; i < names.length(); i += 1) {
695            jo.put(names.getString(i), this.opt(i));
696        }
697        return jo;
698    }
699
700
701    /**
702     * Make an JSON text of this JSONArray. For compactness, no
703     * unnecessary whitespace is added. If it is not possible to produce a
704     * syntactically correct JSON text then null will be returned instead. This
705     * could occur if the array contains an invalid number.
706     * <p>
707     * Warning: This method assumes that the data structure is acyclical.
708     *
709     * @return a printable, displayable, transmittable
710     *  representation of the array.
711     */
712    public String toString() {
713        try {
714            return '[' + join(",") + ']';
715        } catch (Exception e) {
716            return null;
717        }
718    }
719
720
721    /**
722     * Make a prettyprinted JSON text of this JSONArray.
723     * Warning: This method assumes that the data structure is acyclical.
724     * @param indentFactor The number of spaces to add to each level of
725     *  indentation.
726     * @return a printable, displayable, transmittable
727     *  representation of the object, beginning
728     *  with <code>[</code>&nbsp;<small>(left bracket)</small> and ending
729     *  with <code>]</code>&nbsp;<small>(right bracket)</small>.
730     * @throws JSONException
731     */
732    public String toString(int indentFactor) throws JSONException {
733        return toString(indentFactor, 0);
734    }
735
736
737    /**
738     * Make a prettyprinted JSON text of this JSONArray.
739     * Warning: This method assumes that the data structure is acyclical.
740     * @param indentFactor The number of spaces to add to each level of
741     *  indentation.
742     * @param indent The indention of the top level.
743     * @return a printable, displayable, transmittable
744     *  representation of the array.
745     * @throws JSONException
746     */
747    String toString(int indentFactor, int indent) throws JSONException {
748        int len = length();
749        if (len == 0) {
750            return "[]";
751        }
752        int i;
753        StringBuilder sb = new StringBuilder("[");
754        if (len == 1) {
755            sb.append(JSONObject.valueToString(this.myArrayList.get(0),
756                    indentFactor, indent));
757        } else {
758            int newindent = indent + indentFactor;
759            sb.append('\n');
760            for (i = 0; i < len; i += 1) {
761                if (i > 0) {
762                    sb.append(",\n");
763                }
764                for (int j = 0; j < newindent; j += 1) {
765                    sb.append(' ');
766                }
767                sb.append(JSONObject.valueToString(this.myArrayList.get(i),
768                        indentFactor, newindent));
769            }
770            sb.append('\n');
771            for (i = 0; i < indent; i += 1) {
772                sb.append(' ');
773            }
774        }
775        sb.append(']');
776        return sb.toString();
777    }
778}
779