001
002
003/*
004Copyright (c) 2002 JSON.org
005
006Permission is hereby granted, free of charge, to any person obtaining a copy
007of this software and associated documentation files (the "Software"), to deal
008in the Software without restriction, including without limitation the rights
009to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
010copies of the Software, and to permit persons to whom the Software is
011furnished to do so, subject to the following conditions:
012
013The above copyright notice and this permission notice shall be included in all
014copies or substantial portions of the Software.
015
016The Software shall be used for Good, not Evil.
017
018THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
019IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
020FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
021AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
022LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
023OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
024SOFTWARE.
025*/
026package armyc2.c5isr.web.json.utilities;
027import java.io.IOException;
028import java.io.Writer;
029import java.lang.reflect.Array;
030import java.util.ArrayList;
031import java.util.Collection;
032import java.util.Iterator;
033import java.util.Map;
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
065 *     is <code>,</code>&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,
070 *     and if they do not contain any of these characters:
071 *     <code>{ } [ ] / \ : , = ; #</code> and if they do not look like numbers
072 *     and if they are not the reserved words <code>true</code>,
073 *     <code>false</code>, or <code>null</code>.</li>
074 * <li>Values can be separated by <code>;</code> <small>(semicolon)</small> as
075 *     well as by <code>,</code> <small>(comma)</small>.</li>
076 * <li>Numbers may have the 
077 *     <code>0x-</code> <small>(hex)</small> prefix.</li>
078 * </ul>
079
080 * @author JSON.org
081 * @version 2011-05-04
082 */
083public class JSONArray {
084
085
086    /**
087     * The arrayList where the JSONArray's properties are kept.
088     */
089    private ArrayList<Object> myArrayList;
090
091
092    /**
093     * Construct an empty JSONArray.
094     */
095    public JSONArray() {
096        this.myArrayList = new ArrayList<Object>();
097    }
098
099    /**
100     * Construct a JSONArray from a JSONTokener.
101     * @param x A JSONTokener
102     * @throws JSONException If there is a syntax error.
103     */
104    public JSONArray(JSONTokener x) throws JSONException {
105        this();
106        if (x.nextClean() != '[') {
107            throw x.syntaxError("A JSONArray text must start with '['");
108        }
109        if (x.nextClean() != ']') {
110                x.back();
111                for (;;) {
112                    if (x.nextClean() == ',') {
113                        x.back();
114                        this.myArrayList.add(JSONObject.NULL);
115                    } else {
116                        x.back();
117                        this.myArrayList.add(x.nextValue());
118                    }
119                    switch (x.nextClean()) {
120                    case ';':
121                    case ',':
122                        if (x.nextClean() == ']') {
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
137    /**
138     * Construct a JSONArray from a source JSON text.
139     * @param source     A string that begins with
140     * <code>[</code>&nbsp;<small>(left bracket)</small>
141     *  and ends with <code>]</code>&nbsp;<small>(right bracket)</small>.
142     *  @throws JSONException If there is a syntax error.
143     */
144    public JSONArray(String source) throws JSONException {
145        this(new JSONTokener(source));
146    }
147
148
149    /**
150     * Construct a JSONArray from a Collection.
151     * @param collection     A Collection.
152     */
153    public JSONArray(Collection<?> collection) {
154                this.myArrayList = new ArrayList<Object>();
155                if (collection != null) {
156                        Iterator<?> iter = collection.iterator();
157                        while (iter.hasNext()) {
158                this.myArrayList.add(JSONObject.wrap(iter.next()));  
159                        }
160                }
161    }
162
163    
164    /**
165     * Construct a JSONArray from an array
166     * @throws JSONException If not an array.
167     */
168    public JSONArray(Object array) throws JSONException {
169        this();
170        if (array.getClass().isArray()) {
171            int length = Array.getLength(array);
172            for (int i = 0; i < length; i += 1) {
173                this.put(JSONObject.wrap(Array.get(array, i)));
174            }
175        } else {
176            throw new JSONException(
177"JSONArray initial value should be a string or collection or array.");
178        }
179    }
180    
181    
182    /**
183     * Get the object value associated with an index.
184     * @param index
185     *  The index must be between 0 and length() - 1.
186     * @return An object value.
187     * @throws JSONException If there is no value for the index.
188     */
189    public Object get(int index) throws JSONException {
190        Object object = opt(index);
191        if (object == null) {
192            throw new JSONException("JSONArray[" + index + "] not found.");
193        }
194        return object;
195    }
196
197
198    /**
199     * Get the boolean value associated with an index.
200     * The string values "true" and "false" are converted to boolean.
201     *
202     * @param index The index must be between 0 and length() - 1.
203     * @return      The truth.
204     * @throws JSONException If there is no value for the index or if the
205     *  value is not convertible to boolean.
206     */
207    public boolean getBoolean(int index) throws JSONException {
208        Object object = get(index);
209        if (object.equals(Boolean.FALSE) ||
210                (object instanceof String &&
211                ((String)object).equalsIgnoreCase("false"))) {
212            return false;
213        } else if (object.equals(Boolean.TRUE) ||
214                (object instanceof String &&
215                ((String)object).equalsIgnoreCase("true"))) {
216            return true;
217        }
218        throw new JSONException("JSONArray[" + index + "] is not a boolean.");
219    }
220
221
222    /**
223     * Get the double value associated with an index.
224     *
225     * @param index The index must be between 0 and length() - 1.
226     * @return      The value.
227     * @throws   JSONException If the key is not found or if the value cannot
228     *  be converted to a number.
229     */
230    public double getDouble(int index) throws JSONException {
231        Object object = get(index);
232        try {
233            return object instanceof Number ?
234                ((Number)object).doubleValue() :
235                Double.parseDouble((String)object);
236        } catch (Exception e) {
237            throw new JSONException("JSONArray[" + index +
238                "] is not a number.");
239        }
240    }
241
242
243    /**
244     * Get the int value associated with an index.
245     *
246     * @param index The index must be between 0 and length() - 1.
247     * @return      The value.
248     * @throws   JSONException If the key is not found or if the value is not a number.
249     */
250    public int getInt(int index) throws JSONException {
251        Object object = get(index);
252        try {
253            return object instanceof Number ?
254                ((Number)object).intValue() :
255                Integer.parseInt((String)object);
256        } catch (Exception e) {
257            throw new JSONException("JSONArray[" + index +
258                "] is not a number.");
259        }
260    }
261
262
263    /**
264     * Get the JSONArray associated with an index.
265     * @param index The index must be between 0 and length() - 1.
266     * @return      A JSONArray value.
267     * @throws JSONException If there is no value for the index. or if the
268     * value is not a JSONArray
269     */
270    public JSONArray getJSONArray(int index) throws JSONException {
271        Object object = get(index);
272        if (object instanceof JSONArray) {
273            return (JSONArray)object;
274        }
275        throw new JSONException("JSONArray[" + index +
276                "] is not a JSONArray.");
277    }
278
279
280    /**
281     * Get the JSONObject associated with an index.
282     * @param index subscript
283     * @return      A JSONObject value.
284     * @throws JSONException If there is no value for the index or if the
285     * value is not a JSONObject
286     */
287    public JSONObject getJSONObject(int index) throws JSONException {
288        Object object = get(index);
289        if (object instanceof JSONObject) {
290            return (JSONObject)object;
291        }
292        throw new JSONException("JSONArray[" + index +
293            "] is not a JSONObject.");
294    }
295
296
297    /**
298     * Get the long value associated with an index.
299     *
300     * @param index The index must be between 0 and length() - 1.
301     * @return      The value.
302     * @throws   JSONException If the key is not found or if the value cannot
303     *  be converted to a number.
304     */
305    public long getLong(int index) throws JSONException {
306        Object object = get(index);
307        try {
308            return object instanceof Number ?
309                ((Number)object).longValue() :
310                Long.parseLong((String)object);
311        } catch (Exception e) {
312            throw new JSONException("JSONArray[" + index +
313                "] is not a number.");
314        }
315    }
316
317
318    /**
319     * Get the string associated with an index.
320     * @param index The index must be between 0 and length() - 1.
321     * @return      A string value.
322     * @throws JSONException If there is no string value for the index.
323     */
324    public String getString(int index) throws JSONException {
325        Object object = get(index);
326        if (object instanceof String) {
327            return (String)object;
328        }
329        throw new JSONException("JSONArray[" + index + "] not a string.");
330    }
331
332
333    /**
334     * Determine if the value is null.
335     * @param index The index must be between 0 and length() - 1.
336     * @return true if the value at the index is null, or if there is no value.
337     */
338    public boolean isNull(int index) {
339        return JSONObject.NULL.equals(opt(index));
340    }
341
342
343    /**
344     * Make a string from the contents of this JSONArray. The
345     * <code>separator</code> string is inserted between each element.
346     * Warning: This method assumes that the data structure is acyclical.
347     * @param separator A string that will be inserted between the elements.
348     * @return a string.
349     * @throws JSONException If the array contains an invalid number.
350     */
351    public String join(String separator) throws JSONException {
352        int len = length();
353        StringBuffer sb = new StringBuffer();
354
355        for (int i = 0; i < len; i += 1) {
356            if (i > 0) {
357                sb.append(separator);
358            }
359            sb.append(JSONObject.valueToString(this.myArrayList.get(i)));
360        }
361        return sb.toString();
362    }
363
364
365    /**
366     * Get the number of elements in the JSONArray, included nulls.
367     *
368     * @return The length (or size).
369     */
370    public int length() {
371        return this.myArrayList.size();
372    }
373
374
375    /**
376     * Get the optional object value associated with an index.
377     * @param index The index must be between 0 and length() - 1.
378     * @return      An object value, or null if there is no
379     *              object at that index.
380     */
381    public Object opt(int index) {
382        return (index < 0 || index >= length()) ?
383            null : this.myArrayList.get(index);
384    }
385
386
387    /**
388     * Get the optional boolean value associated with an index.
389     * It returns false if there is no value at that index,
390     * or if the value is not Boolean.TRUE or the String "true".
391     *
392     * @param index The index must be between 0 and length() - 1.
393     * @return      The truth.
394     */
395    public boolean optBoolean(int index)  {
396        return optBoolean(index, false);
397    }
398
399
400    /**
401     * Get the optional boolean value associated with an index.
402     * It returns the defaultValue if there is no value at that index or if
403     * it is not a Boolean or the String "true" or "false" (case insensitive).
404     *
405     * @param index The index must be between 0 and length() - 1.
406     * @param defaultValue     A boolean default.
407     * @return      The truth.
408     */
409    public boolean optBoolean(int index, boolean defaultValue)  {
410        try {
411            return getBoolean(index);
412        } catch (Exception e) {
413            return defaultValue;
414        }
415    }
416
417
418    /**
419     * Get the optional double value associated with an index.
420     * NaN is returned if there is no value for the index,
421     * or if the value is not a number and cannot be converted to a number.
422     *
423     * @param index The index must be between 0 and length() - 1.
424     * @return      The value.
425     */
426    public double optDouble(int index) {
427        return optDouble(index, Double.NaN);
428    }
429
430
431    /**
432     * Get the optional double value associated with an index.
433     * The defaultValue is returned if there is no value for the index,
434     * or if the value is not a number and cannot be converted to a number.
435     *
436     * @param index subscript
437     * @param defaultValue     The default value.
438     * @return      The value.
439     */
440    public double optDouble(int index, double defaultValue) {
441        try {
442            return getDouble(index);
443        } catch (Exception e) {
444            return defaultValue;
445        }
446    }
447
448
449    /**
450     * Get the optional int value associated with an index.
451     * Zero is returned if there is no value for the index,
452     * or if the value is not a number and cannot be converted to a number.
453     *
454     * @param index The index must be between 0 and length() - 1.
455     * @return      The value.
456     */
457    public int optInt(int index) {
458        return optInt(index, 0);
459    }
460
461
462    /**
463     * Get the optional int value associated with an index.
464     * The defaultValue is returned if there is no value for the index,
465     * or if the value is not a number and cannot be converted to a number.
466     * @param index The index must be between 0 and length() - 1.
467     * @param defaultValue     The default value.
468     * @return      The value.
469     */
470    public int optInt(int index, int defaultValue) {
471        try {
472            return getInt(index);
473        } catch (Exception e) {
474            return defaultValue;
475        }
476    }
477
478
479    /**
480     * Get the optional JSONArray associated with an index.
481     * @param index subscript
482     * @return      A JSONArray value, or null if the index has no value,
483     * or if the value is not a JSONArray.
484     */
485    public JSONArray optJSONArray(int index) {
486        Object o = opt(index);
487        return o instanceof JSONArray ? (JSONArray)o : null;
488    }
489
490
491    /**
492     * Get the optional JSONObject associated with an index.
493     * Null is returned if the key is not found, or null if the index has
494     * no value, or if the value is not a JSONObject.
495     *
496     * @param index The index must be between 0 and length() - 1.
497     * @return      A JSONObject value.
498     */
499    public JSONObject optJSONObject(int index) {
500        Object o = opt(index);
501        return o instanceof JSONObject ? (JSONObject)o : null;
502    }
503
504
505    /**
506     * Get the optional long value associated with an index.
507     * Zero is returned if there is no value for the index,
508     * or if the value is not a number and cannot be converted to a number.
509     *
510     * @param index The index must be between 0 and length() - 1.
511     * @return      The value.
512     */
513    public long optLong(int index) {
514        return optLong(index, 0);
515    }
516
517
518    /**
519     * Get the optional long value associated with an index.
520     * The defaultValue is returned if there is no value for the index,
521     * or if the value is not a number and cannot be converted to a number.
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 long optLong(int index, long defaultValue) {
527        try {
528            return getLong(index);
529        } catch (Exception e) {
530            return defaultValue;
531        }
532    }
533
534
535    /**
536     * Get the optional string value associated with an index. It returns an
537     * empty string if there is no value at that index. If the value
538     * is not a string and is not null, then it is coverted to a string.
539     *
540     * @param index The index must be between 0 and length() - 1.
541     * @return      A String value.
542     */
543    public String optString(int index) {
544        return optString(index, "");
545    }
546
547
548    /**
549     * Get the optional string associated with an index.
550     * The defaultValue is returned if the key is not found.
551     *
552     * @param index The index must be between 0 and length() - 1.
553     * @param defaultValue     The default value.
554     * @return      A String value.
555     */
556    public String optString(int index, String defaultValue) {
557        Object object = opt(index);
558        return object != null ? object.toString() : defaultValue;
559    }
560
561
562    /**
563     * Append a boolean value. This increases the array's length by one.
564     *
565     * @param value A boolean value.
566     * @return this.
567     */
568    public JSONArray put(boolean value) {
569        put(value ? Boolean.TRUE : Boolean.FALSE);
570        return this;
571    }
572
573
574    /**
575     * Put a value in the JSONArray, where the value will be a
576     * JSONArray which is produced from a Collection.
577     * @param value A Collection value.
578     * @return      this.
579     */
580    public JSONArray put(Collection<?> value) {
581        put(new JSONArray(value));
582        return this;
583    }
584
585
586    /**
587     * Append a double value. This increases the array's length by one.
588     *
589     * @param value A double value.
590     * @throws JSONException if the value is not finite.
591     * @return this.
592     */
593    public JSONArray put(double value) throws JSONException {
594        Double d = new Double(value);
595        JSONObject.testValidity(d);
596        put(d);
597        return this;
598    }
599
600
601    /**
602     * Append an int value. This increases the array's length by one.
603     *
604     * @param value An int value.
605     * @return this.
606     */
607    public JSONArray put(int value) {
608        put(new Integer(value));
609        return this;
610    }
611
612
613    /**
614     * Append an long value. This increases the array's length by one.
615     *
616     * @param value A long value.
617     * @return this.
618     */
619    public JSONArray put(long value) {
620        put(new Long(value));
621        return this;
622    }
623
624
625    /**
626     * Put a value in the JSONArray, where the value will be a
627     * JSONObject which is produced from a Map.
628     * @param value A Map value.
629     * @return      this.
630     */
631    public JSONArray put(Map<?, ?> value) {
632        put(new JSONObject(value));
633        return this;
634    }
635
636
637    /**
638     * Append an object value. This increases the array's length by one.
639     * @param value An object value.  The value should be a
640     *  Boolean, Double, Integer, JSONArray, JSONObject, Long, or String, or the
641     *  JSONObject.NULL object.
642     * @return this.
643     */
644    public JSONArray put(Object value) {
645        this.myArrayList.add(value);
646        return this;
647    }
648
649
650    /**
651     * Put or replace a boolean value in the JSONArray. If the index is greater
652     * than the length of the JSONArray, then null elements will be added as
653     * necessary to pad it out.
654     * @param index The subscript.
655     * @param value A boolean value.
656     * @return this.
657     * @throws JSONException If the index is negative.
658     */
659    public JSONArray put(int index, boolean value) throws JSONException {
660        put(index, value ? Boolean.TRUE : Boolean.FALSE);
661        return this;
662    }
663
664
665    /**
666     * Put a value in the JSONArray, where the value will be a
667     * JSONArray which is produced from a Collection.
668     * @param index The subscript.
669     * @param value A Collection value.
670     * @return      this.
671     * @throws JSONException If the index is negative or if the value is
672     * not finite.
673     */
674    public JSONArray put(int index, Collection<?> value) throws JSONException {
675        put(index, new JSONArray(value));
676        return this;
677    }
678
679
680    /**
681     * Put or replace a double value. If the index is greater than the length of
682     *  the JSONArray, then null elements will be added as necessary to pad
683     *  it out.
684     * @param index The subscript.
685     * @param value A double value.
686     * @return this.
687     * @throws JSONException If the index is negative or if the value is
688     * not finite.
689     */
690    public JSONArray put(int index, double value) throws JSONException {
691        put(index, new Double(value));
692        return this;
693    }
694
695
696    /**
697     * Put or replace an int value. If the index is greater than the length of
698     *  the JSONArray, then null elements will be added as necessary to pad
699     *  it out.
700     * @param index The subscript.
701     * @param value An int value.
702     * @return this.
703     * @throws JSONException If the index is negative.
704     */
705    public JSONArray put(int index, int value) throws JSONException {
706        put(index, new Integer(value));
707        return this;
708    }
709
710
711    /**
712     * Put or replace a long value. If the index is greater than the length of
713     *  the JSONArray, then null elements will be added as necessary to pad
714     *  it out.
715     * @param index The subscript.
716     * @param value A long value.
717     * @return this.
718     * @throws JSONException If the index is negative.
719     */
720    public JSONArray put(int index, long value) throws JSONException {
721        put(index, new Long(value));
722        return this;
723    }
724
725
726    /**
727     * Put a value in the JSONArray, where the value will be a
728     * JSONObject that is produced from a Map.
729     * @param index The subscript.
730     * @param value The Map value.
731     * @return      this.
732     * @throws JSONException If the index is negative or if the the value is
733     *  an invalid number.
734     */
735    public JSONArray put(int index, Map<?, ?> value) throws JSONException {
736        put(index, new JSONObject(value));
737        return this;
738    }
739
740
741    /**
742     * Put or replace an object value in the JSONArray. If the index is greater
743     *  than the length of the JSONArray, then null elements will be added as
744     *  necessary to pad it out.
745     * @param index The subscript.
746     * @param value The value to put into the array. The value should be a
747     *  Boolean, Double, Integer, JSONArray, JSONObject, Long, or String, or the
748     *  JSONObject.NULL object.
749     * @return this.
750     * @throws JSONException If the index is negative or if the the value is
751     *  an invalid number.
752     */
753    public JSONArray put(int index, Object value) throws JSONException {
754        JSONObject.testValidity(value);
755        if (index < 0) {
756            throw new JSONException("JSONArray[" + index + "] not found.");
757        }
758        if (index < length()) {
759            this.myArrayList.set(index, value);
760        } else {
761            while (index != length()) {
762                put(JSONObject.NULL);
763            }
764            put(value);
765        }
766        return this;
767    }
768    
769    
770    /**
771     * Remove an index and close the hole.
772     * @param index The index of the element to be removed.
773     * @return The value that was associated with the index,
774     * or null if there was no value.
775     */
776    public Object remove(int index) {
777        Object o = opt(index);
778        this.myArrayList.remove(index);
779        return o;
780    }
781
782
783    /**
784     * Produce a JSONObject by combining a JSONArray of names with the values
785     * of this JSONArray.
786     * @param names A JSONArray containing a list of key strings. These will be
787     * paired with the values.
788     * @return A JSONObject, or null if there are no names or if this JSONArray
789     * has no values.
790     * @throws JSONException If any of the names are null.
791     */
792    public JSONObject toJSONObject(JSONArray names) throws JSONException {
793        if (names == null || names.length() == 0 || length() == 0) {
794            return null;
795        }
796        JSONObject jo = new JSONObject();
797        for (int i = 0; i < names.length(); i += 1) {
798            jo.put(names.getString(i), this.opt(i));
799        }
800        return jo;
801    }
802
803
804    /**
805     * Make a JSON text of this JSONArray. For compactness, no
806     * unnecessary whitespace is added. If it is not possible to produce a
807     * syntactically correct JSON text then null will be returned instead. This
808     * could occur if the array contains an invalid number.
809     * <p>
810     * Warning: This method assumes that the data structure is acyclical.
811     *
812     * @return a printable, displayable, transmittable
813     *  representation of the array.
814     */
815    public String toString() {
816        try {
817            return '[' + join(",") + ']';
818        } catch (Exception e) {
819            return null;
820        }
821    }
822
823
824    /**
825     * Make a prettyprinted JSON text of this JSONArray.
826     * Warning: This method assumes that the data structure is acyclical.
827     * @param indentFactor The number of spaces to add to each level of
828     *  indentation.
829     * @return a printable, displayable, transmittable
830     *  representation of the object, beginning
831     *  with <code>[</code>&nbsp;<small>(left bracket)</small> and ending
832     *  with <code>]</code>&nbsp;<small>(right bracket)</small>.
833     * @throws JSONException
834     */
835    public String toString(int indentFactor) throws JSONException {
836        return toString(indentFactor, 0);
837    }
838
839
840    /**
841     * Make a prettyprinted JSON text of this JSONArray.
842     * Warning: This method assumes that the data structure is acyclical.
843     * @param indentFactor The number of spaces to add to each level of
844     *  indentation.
845     * @param indent The indention of the top level.
846     * @return a printable, displayable, transmittable
847     *  representation of the array.
848     * @throws JSONException
849     */
850    String toString(int indentFactor, int indent) throws JSONException {
851        int len = length();
852        if (len == 0) {
853            return "[]";
854        }
855        int i;
856        StringBuffer sb = new StringBuffer("[");
857        if (len == 1) {
858            sb.append(JSONObject.valueToString(this.myArrayList.get(0),
859                    indentFactor, indent));
860        } else {
861            int newindent = indent + indentFactor;
862            sb.append('\n');
863            for (i = 0; i < len; i += 1) {
864                if (i > 0) {
865                    sb.append(",\n");
866                }
867                for (int j = 0; j < newindent; j += 1) {
868                    sb.append(' ');
869                }
870                sb.append(JSONObject.valueToString(this.myArrayList.get(i),
871                        indentFactor, newindent));
872            }
873            sb.append('\n');
874            for (i = 0; i < indent; i += 1) {
875                sb.append(' ');
876            }
877        }
878        sb.append(']');
879        return sb.toString();
880    }
881
882
883    /**
884     * Write the contents of the JSONArray as JSON text to a writer.
885     * For compactness, no whitespace is added.
886     * <p>
887     * Warning: This method assumes that the data structure is acyclical.
888     *
889     * @return The writer.
890     * @throws JSONException
891     */
892    public Writer write(Writer writer) throws JSONException {
893        try {
894            boolean b = false;
895            int     len = length();
896
897            writer.write('[');
898
899            for (int i = 0; i < len; i += 1) {
900                if (b) {
901                    writer.write(',');
902                }
903                Object v = this.myArrayList.get(i);
904                if (v instanceof JSONObject) {
905                    ((JSONObject)v).write(writer);
906                } else if (v instanceof JSONArray) {
907                    ((JSONArray)v).write(writer);
908                } else {
909                    writer.write(JSONObject.valueToString(v));
910                }
911                b = true;
912            }
913            writer.write(']');
914            return writer;
915        } catch (IOException e) {
916           throw new JSONException(e);
917        }
918    }
919}