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