001package org.json;
002
003/*
004 Copyright (c) 2002 JSON.org
005
006 Permission is hereby granted, free of charge, to any person obtaining a copy
007 of this software and associated documentation files (the "Software"), to deal
008 in the Software without restriction, including without limitation the rights
009 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
010 copies of the Software, and to permit persons to whom the Software is
011 furnished to do so, subject to the following conditions:
012
013 The above copyright notice and this permission notice shall be included in all
014 copies or substantial portions of the Software.
015
016 The Software shall be used for Good, not Evil.
017
018 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
019 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
020 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
021 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
022 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
023 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
024 SOFTWARE.
025 */
026
027import java.io.IOException;
028import java.io.StringWriter;
029import java.io.Writer;
030import java.lang.reflect.Array;
031import java.math.BigDecimal;
032import java.math.BigInteger;
033import java.util.*;
034
035/**
036 * A JSONArray is an ordered sequence of values. Its external text form is a
037 * string wrapped in square brackets with commas separating the values. The
038 * internal form is an object having <code>get</code> and <code>opt</code>
039 * methods for accessing the values by index, and <code>put</code> methods for
040 * adding or replacing values. The values can be any of these types:
041 * <code>Boolean</code>, <code>JSONArray</code>, <code>JSONObject</code>,
042 * <code>Number</code>, <code>String</code>, or the
043 * <code>JSONObject.NULL object</code>.
044 * <p>
045 * The constructor can convert a JSON text into a Java object. The
046 * <code>toString</code> method converts to JSON text.
047 * <p>
048 * A <code>get</code> method returns a value if one can be found, and throws an
049 * exception if one cannot be found. An <code>opt</code> method returns a
050 * default value instead of throwing an exception, and so is useful for
051 * obtaining optional values.
052 * <p>
053 * The generic <code>get()</code> and <code>opt()</code> methods return an
054 * object which you can cast or query for type. There are also typed
055 * <code>get</code> and <code>opt</code> methods that do type checking and type
056 * coercion for you.
057 * <p>
058 * The texts produced by the <code>toString</code> methods strictly conform to
059 * JSON syntax rules. The constructors are more forgiving in the texts they will
060 * accept:
061 * <ul>
062 * <li>An extra <code>,</code>&nbsp;<small>(comma)</small> may appear just
063 * before the closing bracket.</li>
064 * <li>The <code>null</code> value will be inserted when there is <code>,</code>
065 * &nbsp;<small>(comma)</small> elision.</li>
066 * <li>Strings may be quoted with <code>'</code>&nbsp;<small>(single
067 * quote)</small>.</li>
068 * <li>Strings do not need to be quoted at all if they do not begin with a quote
069 * or single quote, and if they do not contain leading or trailing spaces, and
070 * if they do not contain any of these characters:
071 * <code>{ } [ ] / \ : , #</code> and if they do not look like numbers and
072 * if they are not the reserved words <code>true</code>, <code>false</code>, or
073 * <code>null</code>.</li>
074 * </ul>
075 *
076 * @author JSON.org
077 * @version 2016-03-05
078 */
079public class JSONArray implements Iterable<Object> {
080
081    /**
082     * The arrayList where the JSONArray's properties are kept.
083     */
084    private final ArrayList<Object> myArrayList;
085
086    /**
087     * Construct an empty JSONArray.
088     */
089    public JSONArray() {
090        this.myArrayList = new ArrayList<Object>();
091    }
092
093    /**
094     * Construct a JSONArray from a JSONTokener.
095     *
096     * @param x
097     *            A JSONTokener
098     * @throws JSONException
099     *             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            x.back();
108            for (;;) {
109                if (x.nextClean() == ',') {
110                    x.back();
111                    this.myArrayList.add(JSONObject.NULL);
112                } else {
113                    x.back();
114                    this.myArrayList.add(x.nextValue());
115                }
116                switch (x.nextClean()) {
117                case ',':
118                    if (x.nextClean() == ']') {
119                        return;
120                    }
121                    x.back();
122                    break;
123                case ']':
124                    return;
125                default:
126                    throw x.syntaxError("Expected a ',' or ']'");
127                }
128            }
129        }
130    }
131
132    /**
133     * Construct a JSONArray from a source JSON text.
134     *
135     * @param source
136     *            A string that begins with <code>[</code>&nbsp;<small>(left
137     *            bracket)</small> and ends with <code>]</code>
138     *            &nbsp;<small>(right bracket)</small>.
139     * @throws JSONException
140     *             If there is a syntax error.
141     */
142    public JSONArray(String source) throws JSONException {
143        this(new JSONTokener(source));
144    }
145
146    /**
147     * Construct a JSONArray from a Collection.
148     *
149     * @param collection
150     *            A Collection.
151     */
152    public JSONArray(Collection<?> collection) {
153        this.myArrayList = new ArrayList<Object>();
154        if (collection != null) {
155                for (Object o: collection){
156                        this.myArrayList.add(JSONObject.wrap(o));
157                }
158        }
159    }
160
161    /**
162     * Construct a JSONArray from an array
163     *
164     * @throws JSONException
165     *             If not an array.
166     */
167    public JSONArray(Object array) throws JSONException {
168        this();
169        if (array.getClass().isArray()) {
170            int length = Array.getLength(array);
171            for (int i = 0; i < length; i += 1) {
172                this.put(JSONObject.wrap(Array.get(array, i)));
173            }
174        } else {
175            throw new JSONException(
176                    "JSONArray initial value should be a string or collection or array.");
177        }
178    }
179
180    @Override
181    public Iterator<Object> iterator() {
182        return myArrayList.iterator();
183    }
184
185    /**
186     * Get the object value associated with an index.
187     *
188     * @param index
189     *            The index must be between 0 and length() - 1.
190     * @return An object value.
191     * @throws JSONException
192     *             If there is no value for the index.
193     */
194    public Object get(int index) throws JSONException {
195        Object object = this.opt(index);
196        if (object == null) {
197            throw new JSONException("JSONArray[" + index + "] not found.");
198        }
199        return object;
200    }
201
202    /**
203     * Get the boolean value associated with an index. The string values "true"
204     * and "false" are converted to boolean.
205     *
206     * @param index
207     *            The index must be between 0 and length() - 1.
208     * @return The truth.
209     * @throws JSONException
210     *             If there is no value for the index or if the value is not
211     *             convertible to boolean.
212     */
213    public boolean getBoolean(int index) throws JSONException {
214        Object object = this.get(index);
215        if (object.equals(Boolean.FALSE)
216                || (object instanceof String && ((String) object)
217                        .equalsIgnoreCase("false"))) {
218            return false;
219        } else if (object.equals(Boolean.TRUE)
220                || (object instanceof String && ((String) object)
221                        .equalsIgnoreCase("true"))) {
222            return true;
223        }
224        throw new JSONException("JSONArray[" + index + "] is not a boolean.");
225    }
226
227    /**
228     * Get the double value associated with an index.
229     *
230     * @param index
231     *            The index must be between 0 and length() - 1.
232     * @return The value.
233     * @throws JSONException
234     *             If the key is not found or if the value cannot be converted
235     *             to a number.
236     */
237    public double getDouble(int index) throws JSONException {
238        Object object = this.get(index);
239        try {
240            return object instanceof Number ? ((Number) object).doubleValue()
241                    : Double.parseDouble((String) object);
242        } catch (Exception e) {
243            throw new JSONException("JSONArray[" + index + "] is not a number.");
244        }
245    }
246
247    /**
248    * Get the enum value associated with an index.
249    * 
250    * @param clazz
251    *            The type of enum to retrieve.
252    * @param index
253    *            The index must be between 0 and length() - 1.
254    * @return The enum value at the index location
255    * @throws JSONException
256    *            if the key is not found or if the value cannot be converted
257    *            to an enum.
258    */
259    public <E extends Enum<E>> E getEnum(Class<E> clazz, int index) throws JSONException {
260        E val = optEnum(clazz, index);
261        if(val==null) {
262            // JSONException should really take a throwable argument.
263            // If it did, I would re-implement this with the Enum.valueOf
264            // method and place any thrown exception in the JSONException
265            throw new JSONException("JSONObject[" + JSONObject.quote(Integer.toString(index))
266                    + "] is not an enum of type " + JSONObject.quote(clazz.getSimpleName())
267                    + ".");
268        }
269        return val;
270    }
271
272    /**
273     * Get the BigDecimal value associated with an index.
274     *
275     * @param index
276     *            The index must be between 0 and length() - 1.
277     * @return The value.
278     * @throws JSONException
279     *             If the key is not found or if the value cannot be converted
280     *             to a BigDecimal.
281     */
282    public BigDecimal getBigDecimal (int index) throws JSONException {
283        Object object = this.get(index);
284        try {
285            return new BigDecimal(object.toString());
286        } catch (Exception e) {
287            throw new JSONException("JSONArray[" + index +
288                    "] could not convert to BigDecimal.");
289        }
290    }
291
292    /**
293     * Get the BigInteger value associated with an index.
294     *
295     * @param index
296     *            The index must be between 0 and length() - 1.
297     * @return The value.
298     * @throws JSONException
299     *             If the key is not found or if the value cannot be converted
300     *             to a BigInteger.
301     */
302    public BigInteger getBigInteger (int index) throws JSONException {
303        Object object = this.get(index);
304        try {
305            return new BigInteger(object.toString());
306        } catch (Exception e) {
307            throw new JSONException("JSONArray[" + index +
308                    "] could not convert to BigInteger.");
309        }
310    }
311
312    /**
313     * Get the int value associated with an index.
314     *
315     * @param index
316     *            The index must be between 0 and length() - 1.
317     * @return The value.
318     * @throws JSONException
319     *             If the key is not found or if the value is not a number.
320     */
321    public int getInt(int index) throws JSONException {
322        Object object = this.get(index);
323        try {
324            return object instanceof Number ? ((Number) object).intValue()
325                    : Integer.parseInt((String) object);
326        } catch (Exception e) {
327            throw new JSONException("JSONArray[" + index + "] is not a number.");
328        }
329    }
330
331    /**
332     * Get the JSONArray associated with an index.
333     *
334     * @param index
335     *            The index must be between 0 and length() - 1.
336     * @return A JSONArray value.
337     * @throws JSONException
338     *             If there is no value for the index. or if the value is not a
339     *             JSONArray
340     */
341    public JSONArray getJSONArray(int index) throws JSONException {
342        Object object = this.get(index);
343        if (object instanceof JSONArray) {
344            return (JSONArray) object;
345        }
346        throw new JSONException("JSONArray[" + index + "] is not a JSONArray.");
347    }
348
349    /**
350     * Get the JSONObject associated with an index.
351     *
352     * @param index
353     *            subscript
354     * @return A JSONObject value.
355     * @throws JSONException
356     *             If there is no value for the index or if the value is not a
357     *             JSONObject
358     */
359    public JSONObject getJSONObject(int index) throws JSONException {
360        Object object = this.get(index);
361        if (object instanceof JSONObject) {
362            return (JSONObject) object;
363        }
364        throw new JSONException("JSONArray[" + index + "] is not a JSONObject.");
365    }
366
367    /**
368     * Get the long value associated with an index.
369     *
370     * @param index
371     *            The index must be between 0 and length() - 1.
372     * @return The value.
373     * @throws JSONException
374     *             If the key is not found or if the value cannot be converted
375     *             to a number.
376     */
377    public long getLong(int index) throws JSONException {
378        Object object = this.get(index);
379        try {
380            return object instanceof Number ? ((Number) object).longValue()
381                    : Long.parseLong((String) object);
382        } catch (Exception e) {
383            throw new JSONException("JSONArray[" + index + "] is not a number.");
384        }
385    }
386
387    /**
388     * Get the string associated with an index.
389     *
390     * @param index
391     *            The index must be between 0 and length() - 1.
392     * @return A string value.
393     * @throws JSONException
394     *             If there is no string value for the index.
395     */
396    public String getString(int index) throws JSONException {
397        Object object = this.get(index);
398        if (object instanceof String) {
399            return (String) object;
400        }
401        throw new JSONException("JSONArray[" + index + "] not a string.");
402    }
403
404    /**
405     * Determine if the value is null.
406     *
407     * @param index
408     *            The index must be between 0 and length() - 1.
409     * @return true if the value at the index is null, or if there is no value.
410     */
411    public boolean isNull(int index) {
412        return JSONObject.NULL.equals(this.opt(index));
413    }
414
415    /**
416     * Make a string from the contents of this JSONArray. The
417     * <code>separator</code> string is inserted between each element. Warning:
418     * This method assumes that the data structure is acyclical.
419     *
420     * @param separator
421     *            A string that will be inserted between the elements.
422     * @return a string.
423     * @throws JSONException
424     *             If the array contains an invalid number.
425     */
426    public String join(String separator) throws JSONException {
427        int len = this.length();
428        StringBuilder sb = new StringBuilder();
429
430        for (int i = 0; i < len; i += 1) {
431            if (i > 0) {
432                sb.append(separator);
433            }
434            sb.append(JSONObject.valueToString(this.myArrayList.get(i)));
435        }
436        return sb.toString();
437    }
438
439    /**
440     * Get the number of elements in the JSONArray, included nulls.
441     *
442     * @return The length (or size).
443     */
444    public int length() {
445        return this.myArrayList.size();
446    }
447
448    /**
449     * Get the optional object value associated with an index.
450     *
451     * @param index
452     *            The index must be between 0 and length() - 1.
453     * @return An object value, or null if there is no object at that index.
454     */
455    public Object opt(int index) {
456        return (index < 0 || index >= this.length()) ? null : this.myArrayList
457                .get(index);
458    }
459
460    /**
461     * Get the optional boolean value associated with an index. It returns false
462     * if there is no value at that index, or if the value is not Boolean.TRUE
463     * or the String "true".
464     *
465     * @param index
466     *            The index must be between 0 and length() - 1.
467     * @return The truth.
468     */
469    public boolean optBoolean(int index) {
470        return this.optBoolean(index, false);
471    }
472
473    /**
474     * Get the optional boolean value associated with an index. It returns the
475     * defaultValue if there is no value at that index or if it is not a Boolean
476     * or the String "true" or "false" (case insensitive).
477     *
478     * @param index
479     *            The index must be between 0 and length() - 1.
480     * @param defaultValue
481     *            A boolean default.
482     * @return The truth.
483     */
484    public boolean optBoolean(int index, boolean defaultValue) {
485        try {
486            return this.getBoolean(index);
487        } catch (Exception e) {
488            return defaultValue;
489        }
490    }
491
492    /**
493     * Get the optional double value associated with an index. NaN is returned
494     * if there is no value for the index, or if the value is not a number and
495     * cannot be converted to a number.
496     *
497     * @param index
498     *            The index must be between 0 and length() - 1.
499     * @return The value.
500     */
501    public double optDouble(int index) {
502        return this.optDouble(index, Double.NaN);
503    }
504
505    /**
506     * Get the optional double value associated with an index. The defaultValue
507     * is returned if there is no value for the index, or if the value is not a
508     * number and cannot be converted to a number.
509     *
510     * @param index
511     *            subscript
512     * @param defaultValue
513     *            The default value.
514     * @return The value.
515     */
516    public double optDouble(int index, double defaultValue) {
517        try {
518            return this.getDouble(index);
519        } catch (Exception e) {
520            return defaultValue;
521        }
522    }
523
524    /**
525     * Get the optional int value associated with an index. Zero is returned if
526     * there is no value for the index, or if the value is not a number and
527     * cannot be converted to a number.
528     *
529     * @param index
530     *            The index must be between 0 and length() - 1.
531     * @return The value.
532     */
533    public int optInt(int index) {
534        return this.optInt(index, 0);
535    }
536
537    /**
538     * Get the optional int value associated with an index. The defaultValue is
539     * returned if there is no value for the index, or if the value is not a
540     * number and cannot be converted to a number.
541     *
542     * @param index
543     *            The index must be between 0 and length() - 1.
544     * @param defaultValue
545     *            The default value.
546     * @return The value.
547     */
548    public int optInt(int index, int defaultValue) {
549        try {
550            return this.getInt(index);
551        } catch (Exception e) {
552            return defaultValue;
553        }
554    }
555
556    /**
557     * Get the enum value associated with a key.
558     * 
559     * @param clazz
560     *            The type of enum to retrieve.
561     * @param index
562     *            The index must be between 0 and length() - 1.
563     * @return The enum value at the index location or null if not found
564     */
565    public <E extends Enum<E>> E optEnum(Class<E> clazz, int index) {
566        return this.optEnum(clazz, index, null);
567    }
568
569    /**
570     * Get the enum value associated with a key.
571     * 
572     * @param clazz
573     *            The type of enum to retrieve.
574     * @param index
575     *            The index must be between 0 and length() - 1.
576     * @param defaultValue
577     *            The default in case the value is not found
578     * @return The enum value at the index location or defaultValue if
579     *            the value is not found or cannot be assigned to clazz
580     */
581    public <E extends Enum<E>> E optEnum(Class<E> clazz, int index, E defaultValue) {
582        try {
583            Object val = this.opt(index);
584            if (JSONObject.NULL.equals(val)) {
585                return defaultValue;
586            }
587            if (clazz.isAssignableFrom(val.getClass())) {
588                // we just checked it!
589                @SuppressWarnings("unchecked")
590                E myE = (E) val;
591                return myE;
592            }
593            return Enum.valueOf(clazz, val.toString());
594        } catch (IllegalArgumentException e) {
595            return defaultValue;
596        } catch (NullPointerException e) {
597            return defaultValue;
598        }
599    }
600
601
602    /**
603     * Get the optional BigInteger value associated with an index. The 
604     * defaultValue is returned if there is no value for the index, or if the 
605     * value is not a number and cannot be converted to a number.
606     *
607     * @param index
608     *            The index must be between 0 and length() - 1.
609     * @param defaultValue
610     *            The default value.
611     * @return The value.
612     */
613    public BigInteger optBigInteger(int index, BigInteger defaultValue) {
614        try {
615            return this.getBigInteger(index);
616        } catch (Exception e) {
617            return defaultValue;
618        }
619    }
620
621    /**
622     * Get the optional BigDecimal value associated with an index. The 
623     * defaultValue is returned if there is no value for the index, or if the 
624     * value is not a number and cannot be converted to a number.
625     *
626     * @param index
627     *            The index must be between 0 and length() - 1.
628     * @param defaultValue
629     *            The default value.
630     * @return The value.
631     */
632    public BigDecimal optBigDecimal(int index, BigDecimal defaultValue) {
633        try {
634            return this.getBigDecimal(index);
635        } catch (Exception e) {
636            return defaultValue;
637        }
638    }
639
640    /**
641     * Get the optional JSONArray associated with an index.
642     *
643     * @param index
644     *            subscript
645     * @return A JSONArray value, or null if the index has no value, or if the
646     *         value is not a JSONArray.
647     */
648    public JSONArray optJSONArray(int index) {
649        Object o = this.opt(index);
650        return o instanceof JSONArray ? (JSONArray) o : null;
651    }
652
653    /**
654     * Get the optional JSONObject associated with an index. Null is returned if
655     * the key is not found, or null if the index has no value, or if the value
656     * is not a JSONObject.
657     *
658     * @param index
659     *            The index must be between 0 and length() - 1.
660     * @return A JSONObject value.
661     */
662    public JSONObject optJSONObject(int index) {
663        Object o = this.opt(index);
664        return o instanceof JSONObject ? (JSONObject) o : null;
665    }
666
667    /**
668     * Get the optional long value associated with an index. Zero is returned if
669     * there is no value for the index, or if the value is not a number and
670     * cannot be converted to a number.
671     *
672     * @param index
673     *            The index must be between 0 and length() - 1.
674     * @return The value.
675     */
676    public long optLong(int index) {
677        return this.optLong(index, 0);
678    }
679
680    /**
681     * Get the optional long value associated with an index. The defaultValue is
682     * returned if there is no value for the index, or if the value is not a
683     * number and cannot be converted to a number.
684     *
685     * @param index
686     *            The index must be between 0 and length() - 1.
687     * @param defaultValue
688     *            The default value.
689     * @return The value.
690     */
691    public long optLong(int index, long defaultValue) {
692        try {
693            return this.getLong(index);
694        } catch (Exception e) {
695            return defaultValue;
696        }
697    }
698
699    /**
700     * Get the optional string value associated with an index. It returns an
701     * empty string if there is no value at that index. If the value is not a
702     * string and is not null, then it is coverted to a string.
703     *
704     * @param index
705     *            The index must be between 0 and length() - 1.
706     * @return A String value.
707     */
708    public String optString(int index) {
709        return this.optString(index, "");
710    }
711
712    /**
713     * Get the optional string associated with an index. The defaultValue is
714     * returned if the key is not found.
715     *
716     * @param index
717     *            The index must be between 0 and length() - 1.
718     * @param defaultValue
719     *            The default value.
720     * @return A String value.
721     */
722    public String optString(int index, String defaultValue) {
723        Object object = this.opt(index);
724        return JSONObject.NULL.equals(object) ? defaultValue : object
725                .toString();
726    }
727
728    /**
729     * Append a boolean value. This increases the array's length by one.
730     *
731     * @param value
732     *            A boolean value.
733     * @return this.
734     */
735    public JSONArray put(boolean value) {
736        this.put(value ? Boolean.TRUE : Boolean.FALSE);
737        return this;
738    }
739
740    /**
741     * Put a value in the JSONArray, where the value will be a JSONArray which
742     * is produced from a Collection.
743     *
744     * @param value
745     *            A Collection value.
746     * @return this.
747     */
748    public JSONArray put(Collection<?> value) {
749        this.put(new JSONArray(value));
750        return this;
751    }
752
753    /**
754     * Append a double value. This increases the array's length by one.
755     *
756     * @param value
757     *            A double value.
758     * @throws JSONException
759     *             if the value is not finite.
760     * @return this.
761     */
762    public JSONArray put(double value) throws JSONException {
763        Double d = new Double(value);
764        JSONObject.testValidity(d);
765        this.put(d);
766        return this;
767    }
768
769    /**
770     * Append an int value. This increases the array's length by one.
771     *
772     * @param value
773     *            An int value.
774     * @return this.
775     */
776    public JSONArray put(int value) {
777        this.put(new Integer(value));
778        return this;
779    }
780
781    /**
782     * Append an long value. This increases the array's length by one.
783     *
784     * @param value
785     *            A long value.
786     * @return this.
787     */
788    public JSONArray put(long value) {
789        this.put(new Long(value));
790        return this;
791    }
792
793    /**
794     * Put a value in the JSONArray, where the value will be a JSONObject which
795     * is produced from a Map.
796     *
797     * @param value
798     *            A Map value.
799     * @return this.
800     */
801    public JSONArray put(Map<?, ?> value) {
802        this.put(new JSONObject(value));
803        return this;
804    }
805
806    /**
807     * Append an object value. This increases the array's length by one.
808     *
809     * @param value
810     *            An object value. The value should be a Boolean, Double,
811     *            Integer, JSONArray, JSONObject, Long, or String, or the
812     *            JSONObject.NULL object.
813     * @return this.
814     */
815    public JSONArray put(Object value) {
816        this.myArrayList.add(value);
817        return this;
818    }
819
820    /**
821     * Put or replace a boolean value in the JSONArray. If the index is greater
822     * than the length of the JSONArray, then null elements will be added as
823     * necessary to pad it out.
824     *
825     * @param index
826     *            The subscript.
827     * @param value
828     *            A boolean value.
829     * @return this.
830     * @throws JSONException
831     *             If the index is negative.
832     */
833    public JSONArray put(int index, boolean value) throws JSONException {
834        this.put(index, value ? Boolean.TRUE : Boolean.FALSE);
835        return this;
836    }
837
838    /**
839     * Put a value in the JSONArray, where the value will be a JSONArray which
840     * is produced from a Collection.
841     *
842     * @param index
843     *            The subscript.
844     * @param value
845     *            A Collection value.
846     * @return this.
847     * @throws JSONException
848     *             If the index is negative or if the value is not finite.
849     */
850    public JSONArray put(int index, Collection<?> value) throws JSONException {
851        this.put(index, new JSONArray(value));
852        return this;
853    }
854
855    /**
856     * Put or replace a double value. If the index is greater than the length of
857     * the JSONArray, then null elements will be added as necessary to pad it
858     * out.
859     *
860     * @param index
861     *            The subscript.
862     * @param value
863     *            A double value.
864     * @return this.
865     * @throws JSONException
866     *             If the index is negative or if the value is not finite.
867     */
868    public JSONArray put(int index, double value) throws JSONException {
869        this.put(index, new Double(value));
870        return this;
871    }
872
873    /**
874     * Put or replace an int value. If the index is greater than the length of
875     * the JSONArray, then null elements will be added as necessary to pad it
876     * out.
877     *
878     * @param index
879     *            The subscript.
880     * @param value
881     *            An int value.
882     * @return this.
883     * @throws JSONException
884     *             If the index is negative.
885     */
886    public JSONArray put(int index, int value) throws JSONException {
887        this.put(index, new Integer(value));
888        return this;
889    }
890
891    /**
892     * Put or replace a long value. If the index is greater than the length of
893     * the JSONArray, then null elements will be added as necessary to pad it
894     * out.
895     *
896     * @param index
897     *            The subscript.
898     * @param value
899     *            A long value.
900     * @return this.
901     * @throws JSONException
902     *             If the index is negative.
903     */
904    public JSONArray put(int index, long value) throws JSONException {
905        this.put(index, new Long(value));
906        return this;
907    }
908
909    /**
910     * Put a value in the JSONArray, where the value will be a JSONObject that
911     * is produced from a Map.
912     *
913     * @param index
914     *            The subscript.
915     * @param value
916     *            The Map value.
917     * @return this.
918     * @throws JSONException
919     *             If the index is negative or if the the value is an invalid
920     *             number.
921     */
922    public JSONArray put(int index, Map<?, ?> value) throws JSONException {
923        this.put(index, new JSONObject(value));
924        return this;
925    }
926
927    /**
928     * Put or replace an object value in the JSONArray. If the index is greater
929     * than the length of the JSONArray, then null elements will be added as
930     * necessary to pad it out.
931     *
932     * @param index
933     *            The subscript.
934     * @param value
935     *            The value to put into the array. The value should be a
936     *            Boolean, Double, Integer, JSONArray, JSONObject, Long, or
937     *            String, or the JSONObject.NULL object.
938     * @return this.
939     * @throws JSONException
940     *             If the index is negative or if the the value is an invalid
941     *             number.
942     */
943    public JSONArray put(int index, Object value) throws JSONException {
944        JSONObject.testValidity(value);
945        if (index < 0) {
946            throw new JSONException("JSONArray[" + index + "] not found.");
947        }
948        if (index < this.length()) {
949            this.myArrayList.set(index, value);
950        } else {
951            while (index != this.length()) {
952                this.put(JSONObject.NULL);
953            }
954            this.put(value);
955        }
956        return this;
957    }
958
959    /**
960     * Remove an index and close the hole.
961     *
962     * @param index
963     *            The index of the element to be removed.
964     * @return The value that was associated with the index, or null if there
965     *         was no value.
966     */
967    public Object remove(int index) {
968        return index >= 0 && index < this.length()
969            ? this.myArrayList.remove(index)
970            : null;
971    }
972
973    /**
974     * Determine if two JSONArrays are similar.
975     * They must contain similar sequences.
976     *
977     * @param other The other JSONArray
978     * @return true if they are equal
979     */
980    public boolean similar(Object other) {
981        if (!(other instanceof JSONArray)) {
982            return false;
983        }
984        int len = this.length();
985        if (len != ((JSONArray)other).length()) {
986            return false;
987        }
988        for (int i = 0; i < len; i += 1) {
989            Object valueThis = this.get(i);
990            Object valueOther = ((JSONArray)other).get(i);
991            if (valueThis instanceof JSONObject) {
992                if (!((JSONObject)valueThis).similar(valueOther)) {
993                    return false;
994                }
995            } else if (valueThis instanceof JSONArray) {
996                if (!((JSONArray)valueThis).similar(valueOther)) {
997                    return false;
998                }
999            } else if (!valueThis.equals(valueOther)) {
1000                return false;
1001            }
1002        }
1003        return true;
1004    }
1005
1006    /**
1007     * Produce a JSONObject by combining a JSONArray of names with the values of
1008     * this JSONArray.
1009     *
1010     * @param names
1011     *            A JSONArray containing a list of key strings. These will be
1012     *            paired with the values.
1013     * @return A JSONObject, or null if there are no names or if this JSONArray
1014     *         has no values.
1015     * @throws JSONException
1016     *             If any of the names are null.
1017     */
1018    public JSONObject toJSONObject(JSONArray names) throws JSONException {
1019        if (names == null || names.length() == 0 || this.length() == 0) {
1020            return null;
1021        }
1022        JSONObject jo = new JSONObject();
1023        for (int i = 0; i < names.length(); i += 1) {
1024            jo.put(names.getString(i), this.opt(i));
1025        }
1026        return jo;
1027    }
1028
1029    /**
1030     * Make a JSON text of this JSONArray. For compactness, no unnecessary
1031     * whitespace is added. If it is not possible to produce a syntactically
1032     * correct JSON text then null will be returned instead. This could occur if
1033     * the array contains an invalid number.
1034     * <p>
1035     * Warning: This method assumes that the data structure is acyclical.
1036     *
1037     * @return a printable, displayable, transmittable representation of the
1038     *         array.
1039     */
1040    public String toString() {
1041        try {
1042            return this.toString(0);
1043        } catch (Exception e) {
1044            return null;
1045        }
1046    }
1047
1048    /**
1049     * Make a prettyprinted JSON text of this JSONArray. Warning: This method
1050     * assumes that the data structure is acyclical.
1051     *
1052     * @param indentFactor
1053     *            The number of spaces to add to each level of indentation.
1054     * @return a printable, displayable, transmittable representation of the
1055     *         object, beginning with <code>[</code>&nbsp;<small>(left
1056     *         bracket)</small> and ending with <code>]</code>
1057     *         &nbsp;<small>(right bracket)</small>.
1058     * @throws JSONException
1059     */
1060    public String toString(int indentFactor) throws JSONException {
1061        StringWriter sw = new StringWriter();
1062        synchronized (sw.getBuffer()) {
1063            return this.write(sw, indentFactor, 0).toString();
1064        }
1065    }
1066
1067    /**
1068     * Write the contents of the JSONArray as JSON text to a writer. For
1069     * compactness, no whitespace is added.
1070     * <p>
1071     * Warning: This method assumes that the data structure is acyclical.
1072     *
1073     * @return The writer.
1074     * @throws JSONException
1075     */
1076    public Writer write(Writer writer) throws JSONException {
1077        return this.write(writer, 0, 0);
1078    }
1079
1080    /**
1081     * Write the contents of the JSONArray as JSON text to a writer. For
1082     * compactness, no whitespace is added.
1083     * <p>
1084     * Warning: This method assumes that the data structure is acyclical.
1085     *
1086     * @param writer
1087     *            Writes the serialized JSON
1088     * @param indentFactor
1089     *            The number of spaces to add to each level of indentation.
1090     * @param indent
1091     *            The indention of the top level.
1092     * @return The writer.
1093     * @throws JSONException
1094     */
1095    public Writer write(Writer writer, int indentFactor, int indent)
1096            throws JSONException {
1097        try {
1098            boolean commanate = false;
1099            int length = this.length();
1100            writer.write('[');
1101
1102            if (length == 1) {
1103                JSONObject.writeValue(writer, this.myArrayList.get(0),
1104                        indentFactor, indent);
1105            } else if (length != 0) {
1106                final int newindent = indent + indentFactor;
1107
1108                for (int i = 0; i < length; i += 1) {
1109                    if (commanate) {
1110                        writer.write(',');
1111                    }
1112                    if (indentFactor > 0) {
1113                        writer.write('\n');
1114                    }
1115                    JSONObject.indent(writer, newindent);
1116                    JSONObject.writeValue(writer, this.myArrayList.get(i),
1117                            indentFactor, newindent);
1118                    commanate = true;
1119                }
1120                if (indentFactor > 0) {
1121                    writer.write('\n');
1122                }
1123                JSONObject.indent(writer, indent);
1124            }
1125            writer.write(']');
1126            return writer;
1127        } catch (IOException e) {
1128            throw new JSONException(e);
1129        }
1130    }
1131
1132    /**
1133     * Returns a java.util.List containing all of the elements in this array.
1134     * If an element in the array is a JSONArray or JSONObject it will also
1135     * be converted.
1136     * <p>
1137     * Warning: This method assumes that the data structure is acyclical.
1138     *
1139     * @return a java.util.List containing the elements of this array
1140     */
1141    public List<Object> toList() {
1142        List<Object> results = new ArrayList<Object>(this.myArrayList.size());
1143        for (Object element : this.myArrayList) {
1144            if (element == null || JSONObject.NULL.equals(element)) {
1145                results.add(null);
1146            } else if (element instanceof JSONArray) {
1147                results.add(((JSONArray) element).toList());
1148            } else if (element instanceof JSONObject) {
1149                results.add(((JSONObject) element).toMap());
1150            } else {
1151                results.add(element);
1152            }
1153        }
1154        return results;
1155    }
1156}