fbc1a0f737b69bb01d269f642ed13acbae48a12c
[jalview.git] / src / org / json / JSONArray.java
1 package org.json;
2
3 /*
4  Copyright (c) 2002 JSON.org
5
6  Permission is hereby granted, free of charge, to any person obtaining a copy
7  of this software and associated documentation files (the "Software"), to deal
8  in the Software without restriction, including without limitation the rights
9  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10  copies of the Software, and to permit persons to whom the Software is
11  furnished to do so, subject to the following conditions:
12
13  The above copyright notice and this permission notice shall be included in all
14  copies or substantial portions of the Software.
15
16  The Software shall be used for Good, not Evil.
17
18  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
24  SOFTWARE.
25  */
26
27 import java.io.IOException;
28 import java.io.StringWriter;
29 import java.io.Writer;
30 import java.lang.reflect.Array;
31 import java.math.BigDecimal;
32 import java.math.BigInteger;
33 import java.util.ArrayList;
34 import java.util.Collection;
35 import java.util.Iterator;
36 import java.util.List;
37 import java.util.Map;
38
39
40 /**
41  * A JSONArray is an ordered sequence of values. Its external text form is a
42  * string wrapped in square brackets with commas separating the values. The
43  * internal form is an object having <code>get</code> and <code>opt</code>
44  * methods for accessing the values by index, and <code>put</code> methods for
45  * adding or replacing values. The values can be any of these types:
46  * <code>Boolean</code>, <code>JSONArray</code>, <code>JSONObject</code>,
47  * <code>Number</code>, <code>String</code>, or the
48  * <code>JSONObject.NULL object</code>.
49  * <p>
50  * The constructor can convert a JSON text into a Java object. The
51  * <code>toString</code> method converts to JSON text.
52  * <p>
53  * A <code>get</code> method returns a value if one can be found, and throws an
54  * exception if one cannot be found. An <code>opt</code> method returns a
55  * default value instead of throwing an exception, and so is useful for
56  * obtaining optional values.
57  * <p>
58  * The generic <code>get()</code> and <code>opt()</code> methods return an
59  * object which you can cast or query for type. There are also typed
60  * <code>get</code> and <code>opt</code> methods that do type checking and type
61  * coercion for you.
62  * <p>
63  * The texts produced by the <code>toString</code> methods strictly conform to
64  * JSON syntax rules. The constructors are more forgiving in the texts they will
65  * accept:
66  * <ul>
67  * <li>An extra <code>,</code>&nbsp;<small>(comma)</small> may appear just
68  * before the closing bracket.</li>
69  * <li>The <code>null</code> value will be inserted when there is <code>,</code>
70  * &nbsp;<small>(comma)</small> elision.</li>
71  * <li>Strings may be quoted with <code>'</code>&nbsp;<small>(single
72  * quote)</small>.</li>
73  * <li>Strings do not need to be quoted at all if they do not begin with a quote
74  * or single quote, and if they do not contain leading or trailing spaces, and
75  * if they do not contain any of these characters:
76  * <code>{ } [ ] / \ : , #</code> and if they do not look like numbers and
77  * if they are not the reserved words <code>true</code>, <code>false</code>, or
78  * <code>null</code>.</li>
79  * </ul>
80  *
81  * @author JSON.org
82  * @version 2016-08/15
83  */
84 public class JSONArray implements Iterable<Object> {
85
86     /**
87      * The arrayList where the JSONArray's properties are kept.
88      */
89     private final ArrayList<Object> myArrayList;
90
91     /**
92      * Construct an empty JSONArray.
93      */
94     public JSONArray() {
95         this.myArrayList = new ArrayList<Object>();
96     }
97
98     /**
99      * Construct a JSONArray from a JSONTokener.
100      *
101      * @param x
102      *            A JSONTokener
103      * @throws JSONException
104      *             If there is a syntax error.
105      */
106     public JSONArray(JSONTokener x) throws JSONException {
107         this();
108         if (x.nextClean() != '[') {
109             throw x.syntaxError("A JSONArray text must start with '['");
110         }
111         
112         char nextChar = x.nextClean();
113         if (nextChar == 0) {
114             // array is unclosed. No ']' found, instead EOF
115             throw x.syntaxError("Expected a ',' or ']'");
116         }
117         if (nextChar != ']') {
118             x.back();
119             for (;;) {
120                 if (x.nextClean() == ',') {
121                     x.back();
122                     this.myArrayList.add(JSONObject.NULL);
123                 } else {
124                     x.back();
125                     this.myArrayList.add(x.nextValue());
126                 }
127                 switch (x.nextClean()) {
128                 case 0:
129                     // array is unclosed. No ']' found, instead EOF
130                     throw x.syntaxError("Expected a ',' or ']'");
131                 case ',':
132                     nextChar = x.nextClean();
133                     if (nextChar == 0) {
134                         // array is unclosed. No ']' found, instead EOF
135                         throw x.syntaxError("Expected a ',' or ']'");
136                     }
137                     if (nextChar == ']') {
138                         return;
139                     }
140                     x.back();
141                     break;
142                 case ']':
143                     return;
144                 default:
145                     throw x.syntaxError("Expected a ',' or ']'");
146                 }
147             }
148         }
149     }
150
151     /**
152      * Construct a JSONArray from a source JSON text.
153      *
154      * @param source
155      *            A string that begins with <code>[</code>&nbsp;<small>(left
156      *            bracket)</small> and ends with <code>]</code>
157      *            &nbsp;<small>(right bracket)</small>.
158      * @throws JSONException
159      *             If there is a syntax error.
160      */
161     public JSONArray(String source) throws JSONException {
162         this(new JSONTokener(source));
163     }
164
165     /**
166      * Construct a JSONArray from a Collection.
167      *
168      * @param collection
169      *            A Collection.
170      */
171     public JSONArray(Collection<?> collection) {
172         if (collection == null) {
173             this.myArrayList = new ArrayList<Object>();
174         } else {
175             this.myArrayList = new ArrayList<Object>(collection.size());
176                 for (Object o: collection){
177                         this.myArrayList.add(JSONObject.wrap(o));
178                 }
179         }
180     }
181
182     /**
183      * Construct a JSONArray from an array
184      *
185      * @throws JSONException
186      *             If not an array or if an array value is non-finite number.
187      */
188     public JSONArray(Object array) throws JSONException {
189         this();
190         if (array.getClass().isArray()) {
191             int length = Array.getLength(array);
192             this.myArrayList.ensureCapacity(length);
193             for (int i = 0; i < length; i += 1) {
194                 this.put(JSONObject.wrap(Array.get(array, i)));
195             }
196         } else {
197             throw new JSONException(
198                     "JSONArray initial value should be a string or collection or array.");
199         }
200     }
201
202     @Override
203     public Iterator<Object> iterator() {
204         return this.myArrayList.iterator();
205     }
206
207     /**
208      * Get the object value associated with an index.
209      *
210      * @param index
211      *            The index must be between 0 and length() - 1.
212      * @return An object value.
213      * @throws JSONException
214      *             If there is no value for the index.
215      */
216     public Object get(int index) throws JSONException {
217         Object object = this.opt(index);
218         if (object == null) {
219             throw new JSONException("JSONArray[" + index + "] not found.");
220         }
221         return object;
222     }
223
224     /**
225      * Get the boolean value associated with an index. The string values "true"
226      * and "false" are converted to boolean.
227      *
228      * @param index
229      *            The index must be between 0 and length() - 1.
230      * @return The truth.
231      * @throws JSONException
232      *             If there is no value for the index or if the value is not
233      *             convertible to boolean.
234      */
235     public boolean getBoolean(int index) throws JSONException {
236         Object object = this.get(index);
237         if (object.equals(Boolean.FALSE)
238                 || (object instanceof String && ((String) object)
239                         .equalsIgnoreCase("false"))) {
240             return false;
241         } else if (object.equals(Boolean.TRUE)
242                 || (object instanceof String && ((String) object)
243                         .equalsIgnoreCase("true"))) {
244             return true;
245         }
246         throw new JSONException("JSONArray[" + index + "] is not a boolean.");
247     }
248
249     /**
250      * Get the double value associated with an index.
251      *
252      * @param index
253      *            The index must be between 0 and length() - 1.
254      * @return The value.
255      * @throws JSONException
256      *             If the key is not found or if the value cannot be converted
257      *             to a number.
258      */
259     public double getDouble(int index) throws JSONException {
260         Object object = this.get(index);
261         try {
262             return object instanceof Number ? ((Number) object).doubleValue()
263                     : Double.parseDouble((String) object);
264         } catch (Exception e) {
265             throw new JSONException("JSONArray[" + index + "] is not a number.", e);
266         }
267     }
268
269     /**
270      * Get the float value associated with a key.
271      *
272      * @param index
273      *            The index must be between 0 and length() - 1.
274      * @return The numeric value.
275      * @throws JSONException
276      *             if the key is not found or if the value is not a Number
277      *             object and cannot be converted to a number.
278      */
279     public float getFloat(int index) throws JSONException {
280         Object object = this.get(index);
281         try {
282             return object instanceof Number ? ((Number) object).floatValue()
283                     : Float.parseFloat(object.toString());
284         } catch (Exception e) {
285             throw new JSONException("JSONArray[" + index
286                     + "] is not a number.", e);
287         }
288     }
289
290     /**
291      * Get the Number value associated with a key.
292      *
293      * @param index
294      *            The index must be between 0 and length() - 1.
295      * @return The numeric value.
296      * @throws JSONException
297      *             if the key is not found or if the value is not a Number
298      *             object and cannot be converted to a number.
299      */
300     public Number getNumber(int index) throws JSONException {
301         Object object = this.get(index);
302         try {
303             if (object instanceof Number) {
304                 return (Number)object;
305             }
306             return JSONObject.stringToNumber(object.toString());
307         } catch (Exception e) {
308             throw new JSONException("JSONArray[" + index + "] is not a number.", e);
309         }
310     }
311
312     /**
313     * Get the enum value associated with an index.
314     * 
315     * @param clazz
316     *            The type of enum to retrieve.
317     * @param index
318     *            The index must be between 0 and length() - 1.
319     * @return The enum value at the index location
320     * @throws JSONException
321     *            if the key is not found or if the value cannot be converted
322     *            to an enum.
323     */
324     public <E extends Enum<E>> E getEnum(Class<E> clazz, int index) throws JSONException {
325         E val = optEnum(clazz, index);
326         if(val==null) {
327             // JSONException should really take a throwable argument.
328             // If it did, I would re-implement this with the Enum.valueOf
329             // method and place any thrown exception in the JSONException
330             throw new JSONException("JSONArray[" + index + "] is not an enum of type "
331                     + JSONObject.quote(clazz.getSimpleName()) + ".");
332         }
333         return val;
334     }
335
336     /**
337      * Get the BigDecimal value associated with an index.
338      *
339      * @param index
340      *            The index must be between 0 and length() - 1.
341      * @return The value.
342      * @throws JSONException
343      *             If the key is not found or if the value cannot be converted
344      *             to a BigDecimal.
345      */
346     public BigDecimal getBigDecimal (int index) throws JSONException {
347         Object object = this.get(index);
348         try {
349             return new BigDecimal(object.toString());
350         } catch (Exception e) {
351             throw new JSONException("JSONArray[" + index +
352                     "] could not convert to BigDecimal.", e);
353         }
354     }
355
356     /**
357      * Get the BigInteger value associated with an index.
358      *
359      * @param index
360      *            The index must be between 0 and length() - 1.
361      * @return The value.
362      * @throws JSONException
363      *             If the key is not found or if the value cannot be converted
364      *             to a BigInteger.
365      */
366     public BigInteger getBigInteger (int index) throws JSONException {
367         Object object = this.get(index);
368         try {
369             return new BigInteger(object.toString());
370         } catch (Exception e) {
371             throw new JSONException("JSONArray[" + index +
372                     "] could not convert to BigInteger.", e);
373         }
374     }
375
376     /**
377      * Get the int value associated with an index.
378      *
379      * @param index
380      *            The index must be between 0 and length() - 1.
381      * @return The value.
382      * @throws JSONException
383      *             If the key is not found or if the value is not a number.
384      */
385     public int getInt(int index) throws JSONException {
386         Object object = this.get(index);
387         try {
388             return object instanceof Number ? ((Number) object).intValue()
389                     : Integer.parseInt((String) object);
390         } catch (Exception e) {
391             throw new JSONException("JSONArray[" + index + "] is not a number.", e);
392         }
393     }
394
395     /**
396      * Get the JSONArray associated with an index.
397      *
398      * @param index
399      *            The index must be between 0 and length() - 1.
400      * @return A JSONArray value.
401      * @throws JSONException
402      *             If there is no value for the index. or if the value is not a
403      *             JSONArray
404      */
405     public JSONArray getJSONArray(int index) throws JSONException {
406         Object object = this.get(index);
407         if (object instanceof JSONArray) {
408             return (JSONArray) object;
409         }
410         throw new JSONException("JSONArray[" + index + "] is not a JSONArray.");
411     }
412
413     /**
414      * Get the JSONObject associated with an index.
415      *
416      * @param index
417      *            subscript
418      * @return A JSONObject value.
419      * @throws JSONException
420      *             If there is no value for the index or if the value is not a
421      *             JSONObject
422      */
423     public JSONObject getJSONObject(int index) throws JSONException {
424         Object object = this.get(index);
425         if (object instanceof JSONObject) {
426             return (JSONObject) object;
427         }
428         throw new JSONException("JSONArray[" + index + "] is not a JSONObject.");
429     }
430
431     /**
432      * Get the long value associated with an index.
433      *
434      * @param index
435      *            The index must be between 0 and length() - 1.
436      * @return The value.
437      * @throws JSONException
438      *             If the key is not found or if the value cannot be converted
439      *             to a number.
440      */
441     public long getLong(int index) throws JSONException {
442         Object object = this.get(index);
443         try {
444             return object instanceof Number ? ((Number) object).longValue()
445                     : Long.parseLong((String) object);
446         } catch (Exception e) {
447             throw new JSONException("JSONArray[" + index + "] is not a number.", e);
448         }
449     }
450
451     /**
452      * Get the string associated with an index.
453      *
454      * @param index
455      *            The index must be between 0 and length() - 1.
456      * @return A string value.
457      * @throws JSONException
458      *             If there is no string value for the index.
459      */
460     public String getString(int index) throws JSONException {
461         Object object = this.get(index);
462         if (object instanceof String) {
463             return (String) object;
464         }
465         throw new JSONException("JSONArray[" + index + "] not a string.");
466     }
467
468     /**
469      * Determine if the value is <code>null</code>.
470      *
471      * @param index
472      *            The index must be between 0 and length() - 1.
473      * @return true if the value at the index is <code>null</code>, or if there is no value.
474      */
475     public boolean isNull(int index) {
476         return JSONObject.NULL.equals(this.opt(index));
477     }
478
479     /**
480      * Make a string from the contents of this JSONArray. The
481      * <code>separator</code> string is inserted between each element. Warning:
482      * This method assumes that the data structure is acyclical.
483      *
484      * @param separator
485      *            A string that will be inserted between the elements.
486      * @return a string.
487      * @throws JSONException
488      *             If the array contains an invalid number.
489      */
490     public String join(String separator) throws JSONException {
491         int len = this.length();
492         StringBuilder sb = new StringBuilder();
493
494         for (int i = 0; i < len; i += 1) {
495             if (i > 0) {
496                 sb.append(separator);
497             }
498             sb.append(JSONObject.valueToString(this.myArrayList.get(i)));
499         }
500         return sb.toString();
501     }
502
503     /**
504      * Get the number of elements in the JSONArray, included nulls.
505      *
506      * @return The length (or size).
507      */
508     public int length() {
509         return this.myArrayList.size();
510     }
511
512     /**
513      * Get the optional object value associated with an index.
514      *
515      * @param index
516      *            The index must be between 0 and length() - 1. If not, null is returned.
517      * @return An object value, or null if there is no object at that index.
518      */
519     public Object opt(int index) {
520         return (index < 0 || index >= this.length()) ? null : this.myArrayList
521                 .get(index);
522     }
523
524     /**
525      * Get the optional boolean value associated with an index. It returns false
526      * if there is no value at that index, or if the value is not Boolean.TRUE
527      * or the String "true".
528      *
529      * @param index
530      *            The index must be between 0 and length() - 1.
531      * @return The truth.
532      */
533     public boolean optBoolean(int index) {
534         return this.optBoolean(index, false);
535     }
536
537     /**
538      * Get the optional boolean value associated with an index. It returns the
539      * defaultValue if there is no value at that index or if it is not a Boolean
540      * or the String "true" or "false" (case insensitive).
541      *
542      * @param index
543      *            The index must be between 0 and length() - 1.
544      * @param defaultValue
545      *            A boolean default.
546      * @return The truth.
547      */
548     public boolean optBoolean(int index, boolean defaultValue) {
549         try {
550             return this.getBoolean(index);
551         } catch (Exception e) {
552             return defaultValue;
553         }
554     }
555
556     /**
557      * Get the optional double value associated with an index. NaN is returned
558      * if there is no value for the index, or if the value is not a number and
559      * cannot be converted to a number.
560      *
561      * @param index
562      *            The index must be between 0 and length() - 1.
563      * @return The value.
564      */
565     public double optDouble(int index) {
566         return this.optDouble(index, Double.NaN);
567     }
568
569     /**
570      * Get the optional double value associated with an index. The defaultValue
571      * is returned if there is no value for the index, or if the value is not a
572      * number and cannot be converted to a number.
573      *
574      * @param index
575      *            subscript
576      * @param defaultValue
577      *            The default value.
578      * @return The value.
579      */
580     public double optDouble(int index, double defaultValue) {
581         Object val = this.opt(index);
582         if (JSONObject.NULL.equals(val)) {
583             return defaultValue;
584         }
585         if (val instanceof Number){
586             return ((Number) val).doubleValue();
587         }
588         if (val instanceof String) {
589             try {
590                 return Double.parseDouble((String) val);
591             } catch (Exception e) {
592                 return defaultValue;
593             }
594         }
595         return defaultValue;
596     }
597
598     /**
599      * Get the optional float value associated with an index. NaN is returned
600      * if there is no value for the index, or if the value is not a number and
601      * cannot be converted to a number.
602      *
603      * @param index
604      *            The index must be between 0 and length() - 1.
605      * @return The value.
606      */
607     public float optFloat(int index) {
608         return this.optFloat(index, Float.NaN);
609     }
610
611     /**
612      * Get the optional float value associated with an index. The defaultValue
613      * is returned if there is no value for the index, or if the value is not a
614      * number and cannot be converted to a number.
615      *
616      * @param index
617      *            subscript
618      * @param defaultValue
619      *            The default value.
620      * @return The value.
621      */
622     public float optFloat(int index, float defaultValue) {
623         Object val = this.opt(index);
624         if (JSONObject.NULL.equals(val)) {
625             return defaultValue;
626         }
627         if (val instanceof Number){
628             return ((Number) val).floatValue();
629         }
630         if (val instanceof String) {
631             try {
632                 return Float.parseFloat((String) val);
633             } catch (Exception e) {
634                 return defaultValue;
635             }
636         }
637         return defaultValue;
638     }
639
640     /**
641      * Get the optional int value associated with an index. Zero is returned if
642      * there is no value for the index, or if the value is not a number and
643      * cannot be converted to a number.
644      *
645      * @param index
646      *            The index must be between 0 and length() - 1.
647      * @return The value.
648      */
649     public int optInt(int index) {
650         return this.optInt(index, 0);
651     }
652
653     /**
654      * Get the optional int value associated with an index. The defaultValue is
655      * returned if there is no value for the index, or if the value is not a
656      * number and cannot be converted to a number.
657      *
658      * @param index
659      *            The index must be between 0 and length() - 1.
660      * @param defaultValue
661      *            The default value.
662      * @return The value.
663      */
664     public int optInt(int index, int defaultValue) {
665         Object val = this.opt(index);
666         if (JSONObject.NULL.equals(val)) {
667             return defaultValue;
668         }
669         if (val instanceof Number){
670             return ((Number) val).intValue();
671         }
672         
673         if (val instanceof String) {
674             try {
675                 return new BigDecimal(val.toString()).intValue();
676             } catch (Exception e) {
677                 return defaultValue;
678             }
679         }
680         return defaultValue;
681     }
682
683     /**
684      * Get the enum value associated with a key.
685      * 
686      * @param clazz
687      *            The type of enum to retrieve.
688      * @param index
689      *            The index must be between 0 and length() - 1.
690      * @return The enum value at the index location or null if not found
691      */
692     public <E extends Enum<E>> E optEnum(Class<E> clazz, int index) {
693         return this.optEnum(clazz, index, null);
694     }
695
696     /**
697      * Get the enum value associated with a key.
698      * 
699      * @param clazz
700      *            The type of enum to retrieve.
701      * @param index
702      *            The index must be between 0 and length() - 1.
703      * @param defaultValue
704      *            The default in case the value is not found
705      * @return The enum value at the index location or defaultValue if
706      *            the value is not found or cannot be assigned to clazz
707      */
708     public <E extends Enum<E>> E optEnum(Class<E> clazz, int index, E defaultValue) {
709         try {
710             Object val = this.opt(index);
711             if (JSONObject.NULL.equals(val)) {
712                 return defaultValue;
713             }
714             if (clazz.isAssignableFrom(val.getClass())) {
715                 // we just checked it!
716                 @SuppressWarnings("unchecked")
717                 E myE = (E) val;
718                 return myE;
719             }
720             return Enum.valueOf(clazz, val.toString());
721         } catch (IllegalArgumentException e) {
722             return defaultValue;
723         } catch (NullPointerException e) {
724             return defaultValue;
725         }
726     }
727
728
729     /**
730      * Get the optional BigInteger value associated with an index. The 
731      * defaultValue is returned if there is no value for the index, or if the 
732      * value is not a number and cannot be converted to a number.
733      *
734      * @param index
735      *            The index must be between 0 and length() - 1.
736      * @param defaultValue
737      *            The default value.
738      * @return The value.
739      */
740     public BigInteger optBigInteger(int index, BigInteger defaultValue) {
741         Object val = this.opt(index);
742         if (JSONObject.NULL.equals(val)) {
743             return defaultValue;
744         }
745         if (val instanceof BigInteger){
746             return (BigInteger) val;
747         }
748         if (val instanceof BigDecimal){
749             return ((BigDecimal) val).toBigInteger();
750         }
751         if (val instanceof Double || val instanceof Float){
752             return new BigDecimal(((Number) val).doubleValue()).toBigInteger();
753         }
754         if (val instanceof Long || val instanceof Integer
755                 || val instanceof Short || val instanceof Byte){
756             return BigInteger.valueOf(((Number) val).longValue());
757         }
758         try {
759             final String valStr = val.toString();
760             if(JSONObject.isDecimalNotation(valStr)) {
761                 return new BigDecimal(valStr).toBigInteger();
762             }
763             return new BigInteger(valStr);
764         } catch (Exception e) {
765             return defaultValue;
766         }
767     }
768
769     /**
770      * Get the optional BigDecimal value associated with an index. The 
771      * defaultValue is returned if there is no value for the index, or if the 
772      * value is not a number and cannot be converted to a number.
773      *
774      * @param index
775      *            The index must be between 0 and length() - 1.
776      * @param defaultValue
777      *            The default value.
778      * @return The value.
779      */
780     public BigDecimal optBigDecimal(int index, BigDecimal defaultValue) {
781         Object val = this.opt(index);
782         if (JSONObject.NULL.equals(val)) {
783             return defaultValue;
784         }
785         if (val instanceof BigDecimal){
786             return (BigDecimal) val;
787         }
788         if (val instanceof BigInteger){
789             return new BigDecimal((BigInteger) val);
790         }
791         if (val instanceof Double || val instanceof Float){
792             return new BigDecimal(((Number) val).doubleValue());
793         }
794         if (val instanceof Long || val instanceof Integer
795                 || val instanceof Short || val instanceof Byte){
796             return new BigDecimal(((Number) val).longValue());
797         }
798         try {
799             return new BigDecimal(val.toString());
800         } catch (Exception e) {
801             return defaultValue;
802         }
803     }
804
805     /**
806      * Get the optional JSONArray associated with an index.
807      *
808      * @param index
809      *            subscript
810      * @return A JSONArray value, or null if the index has no value, or if the
811      *         value is not a JSONArray.
812      */
813     public JSONArray optJSONArray(int index) {
814         Object o = this.opt(index);
815         return o instanceof JSONArray ? (JSONArray) o : null;
816     }
817
818     /**
819      * Get the optional JSONObject associated with an index. Null is returned if
820      * the key is not found, or null if the index has no value, or if the value
821      * is not a JSONObject.
822      *
823      * @param index
824      *            The index must be between 0 and length() - 1.
825      * @return A JSONObject value.
826      */
827     public JSONObject optJSONObject(int index) {
828         Object o = this.opt(index);
829         return o instanceof JSONObject ? (JSONObject) o : null;
830     }
831
832     /**
833      * Get the optional long value associated with an index. Zero is returned if
834      * there is no value for the index, or if the value is not a number and
835      * cannot be converted to a number.
836      *
837      * @param index
838      *            The index must be between 0 and length() - 1.
839      * @return The value.
840      */
841     public long optLong(int index) {
842         return this.optLong(index, 0);
843     }
844
845     /**
846      * Get the optional long value associated with an index. The defaultValue is
847      * returned if there is no value for the index, or if the value is not a
848      * number and cannot be converted to a number.
849      *
850      * @param index
851      *            The index must be between 0 and length() - 1.
852      * @param defaultValue
853      *            The default value.
854      * @return The value.
855      */
856     public long optLong(int index, long defaultValue) {
857         Object val = this.opt(index);
858         if (JSONObject.NULL.equals(val)) {
859             return defaultValue;
860         }
861         if (val instanceof Number){
862             return ((Number) val).longValue();
863         }
864         
865         if (val instanceof String) {
866             try {
867                 return new BigDecimal(val.toString()).longValue();
868             } catch (Exception e) {
869                 return defaultValue;
870             }
871         }
872         return defaultValue;
873     }
874
875     /**
876      * Get an optional {@link Number} value associated with a key, or <code>null</code>
877      * if there is no such key or if the value is not a number. If the value is a string,
878      * an attempt will be made to evaluate it as a number ({@link BigDecimal}). This method
879      * would be used in cases where type coercion of the number value is unwanted.
880      *
881      * @param index
882      *            The index must be between 0 and length() - 1.
883      * @return An object which is the value.
884      */
885     public Number optNumber(int index) {
886         return this.optNumber(index, null);
887     }
888
889     /**
890      * Get an optional {@link Number} value associated with a key, or the default if there
891      * is no such key or if the value is not a number. If the value is a string,
892      * an attempt will be made to evaluate it as a number ({@link BigDecimal}). This method
893      * would be used in cases where type coercion of the number value is unwanted.
894      *
895      * @param index
896      *            The index must be between 0 and length() - 1.
897      * @param defaultValue
898      *            The default.
899      * @return An object which is the value.
900      */
901     public Number optNumber(int index, Number defaultValue) {
902         Object val = this.opt(index);
903         if (JSONObject.NULL.equals(val)) {
904             return defaultValue;
905         }
906         if (val instanceof Number){
907             return (Number) val;
908         }
909         
910         if (val instanceof String) {
911             try {
912                 return JSONObject.stringToNumber((String) val);
913             } catch (Exception e) {
914                 return defaultValue;
915             }
916         }
917         return defaultValue;
918     }
919
920     /**
921      * Get the optional string value associated with an index. It returns an
922      * empty string if there is no value at that index. If the value is not a
923      * string and is not null, then it is converted to a string.
924      *
925      * @param index
926      *            The index must be between 0 and length() - 1.
927      * @return A String value.
928      */
929     public String optString(int index) {
930         return this.optString(index, "");
931     }
932
933     /**
934      * Get the optional string associated with an index. The defaultValue is
935      * returned if the key is not found.
936      *
937      * @param index
938      *            The index must be between 0 and length() - 1.
939      * @param defaultValue
940      *            The default value.
941      * @return A String value.
942      */
943     public String optString(int index, String defaultValue) {
944         Object object = this.opt(index);
945         return JSONObject.NULL.equals(object) ? defaultValue : object
946                 .toString();
947     }
948
949     /**
950      * Append a boolean value. This increases the array's length by one.
951      *
952      * @param value
953      *            A boolean value.
954      * @return this.
955      */
956     public JSONArray put(boolean value) {
957         return this.put(value ? Boolean.TRUE : Boolean.FALSE);
958     }
959
960     /**
961      * Put a value in the JSONArray, where the value will be a JSONArray which
962      * is produced from a Collection.
963      *
964      * @param value
965      *            A Collection value.
966      * @return this.
967      * @throws JSONException
968      *            If the value is non-finite number.
969      */
970     public JSONArray put(Collection<?> value) {
971         return this.put(new JSONArray(value));
972     }
973
974     /**
975      * Append a double value. This increases the array's length by one.
976      *
977      * @param value
978      *            A double value.
979      * @return this.
980      * @throws JSONException
981      *             if the value is not finite.
982      */
983     public JSONArray put(double value) throws JSONException {
984         return this.put(Double.valueOf(value));
985     }
986     
987     /**
988      * Append a float value. This increases the array's length by one.
989      *
990      * @param value
991      *            A float value.
992      * @return this.
993      * @throws JSONException
994      *             if the value is not finite.
995      */
996     public JSONArray put(float value) throws JSONException {
997         return this.put(Float.valueOf(value));
998     }
999
1000     /**
1001      * Append an int value. This increases the array's length by one.
1002      *
1003      * @param value
1004      *            An int value.
1005      * @return this.
1006      */
1007     public JSONArray put(int value) {
1008         return this.put(Integer.valueOf(value));
1009     }
1010
1011     /**
1012      * Append an long value. This increases the array's length by one.
1013      *
1014      * @param value
1015      *            A long value.
1016      * @return this.
1017      */
1018     public JSONArray put(long value) {
1019         return this.put(Long.valueOf(value));
1020     }
1021
1022     /**
1023      * Put a value in the JSONArray, where the value will be a JSONObject which
1024      * is produced from a Map.
1025      *
1026      * @param value
1027      *            A Map value.
1028      * @return this.
1029      * @throws JSONException
1030      *            If a value in the map is non-finite number.
1031      * @throws NullPointerException
1032      *            If a key in the map is <code>null</code>
1033      */
1034     public JSONArray put(Map<?, ?> value) {
1035         return this.put(new JSONObject(value));
1036     }
1037
1038     /**
1039      * Append an object value. This increases the array's length by one.
1040      *
1041      * @param value
1042      *            An object value. The value should be a Boolean, Double,
1043      *            Integer, JSONArray, JSONObject, Long, or String, or the
1044      *            JSONObject.NULL object.
1045      * @return this.
1046      * @throws JSONException
1047      *            If the value is non-finite number.
1048      */
1049     public JSONArray put(Object value) {
1050         JSONObject.testValidity(value);
1051         this.myArrayList.add(value);
1052         return this;
1053     }
1054
1055     /**
1056      * Put or replace a boolean value in the JSONArray. If the index is greater
1057      * than the length of the JSONArray, then null elements will be added as
1058      * necessary to pad it out.
1059      *
1060      * @param index
1061      *            The subscript.
1062      * @param value
1063      *            A boolean value.
1064      * @return this.
1065      * @throws JSONException
1066      *             If the index is negative.
1067      */
1068     public JSONArray put(int index, boolean value) throws JSONException {
1069         return this.put(index, value ? Boolean.TRUE : Boolean.FALSE);
1070     }
1071
1072     /**
1073      * Put a value in the JSONArray, where the value will be a JSONArray which
1074      * is produced from a Collection.
1075      *
1076      * @param index
1077      *            The subscript.
1078      * @param value
1079      *            A Collection value.
1080      * @return this.
1081      * @throws JSONException
1082      *             If the index is negative or if the value is non-finite.
1083      */
1084     public JSONArray put(int index, Collection<?> value) throws JSONException {
1085         return this.put(index, new JSONArray(value));
1086     }
1087
1088     /**
1089      * Put or replace a double value. If the index is greater than the length of
1090      * the JSONArray, then null elements will be added as necessary to pad it
1091      * out.
1092      *
1093      * @param index
1094      *            The subscript.
1095      * @param value
1096      *            A double value.
1097      * @return this.
1098      * @throws JSONException
1099      *             If the index is negative or if the value is non-finite.
1100      */
1101     public JSONArray put(int index, double value) throws JSONException {
1102         return this.put(index, Double.valueOf(value));
1103     }
1104
1105     /**
1106      * Put or replace a float value. If the index is greater than the length of
1107      * the JSONArray, then null elements will be added as necessary to pad it
1108      * out.
1109      *
1110      * @param index
1111      *            The subscript.
1112      * @param value
1113      *            A float value.
1114      * @return this.
1115      * @throws JSONException
1116      *             If the index is negative or if the value is non-finite.
1117      */
1118     public JSONArray put(int index, float value) throws JSONException {
1119         return this.put(index, Float.valueOf(value));
1120     }
1121
1122     /**
1123      * Put or replace an int value. If the index is greater than the length of
1124      * the JSONArray, then null elements will be added as necessary to pad it
1125      * out.
1126      *
1127      * @param index
1128      *            The subscript.
1129      * @param value
1130      *            An int value.
1131      * @return this.
1132      * @throws JSONException
1133      *             If the index is negative.
1134      */
1135     public JSONArray put(int index, int value) throws JSONException {
1136         return this.put(index, Integer.valueOf(value));
1137     }
1138
1139     /**
1140      * Put or replace a long value. If the index is greater than the length of
1141      * the JSONArray, then null elements will be added as necessary to pad it
1142      * out.
1143      *
1144      * @param index
1145      *            The subscript.
1146      * @param value
1147      *            A long value.
1148      * @return this.
1149      * @throws JSONException
1150      *             If the index is negative.
1151      */
1152     public JSONArray put(int index, long value) throws JSONException {
1153         return this.put(index, Long.valueOf(value));
1154     }
1155
1156     /**
1157      * Put a value in the JSONArray, where the value will be a JSONObject that
1158      * is produced from a Map.
1159      *
1160      * @param index
1161      *            The subscript.
1162      * @param value
1163      *            The Map value.
1164      * @return this.
1165      * @throws JSONException
1166      *             If the index is negative or if the the value is an invalid
1167      *             number.
1168      * @throws NullPointerException
1169      *             If a key in the map is <code>null</code>
1170      */
1171     public JSONArray put(int index, Map<?, ?> value) throws JSONException {
1172         this.put(index, new JSONObject(value));
1173         return this;
1174     }
1175
1176     /**
1177      * Put or replace an object value in the JSONArray. If the index is greater
1178      * than the length of the JSONArray, then null elements will be added as
1179      * necessary to pad it out.
1180      *
1181      * @param index
1182      *            The subscript.
1183      * @param value
1184      *            The value to put into the array. The value should be a
1185      *            Boolean, Double, Integer, JSONArray, JSONObject, Long, or
1186      *            String, or the JSONObject.NULL object.
1187      * @return this.
1188      * @throws JSONException
1189      *             If the index is negative or if the the value is an invalid
1190      *             number.
1191      */
1192     public JSONArray put(int index, Object value) throws JSONException {
1193         if (index < 0) {
1194             throw new JSONException("JSONArray[" + index + "] not found.");
1195         }
1196         if (index < this.length()) {
1197             JSONObject.testValidity(value);
1198             this.myArrayList.set(index, value);
1199             return this;
1200         }
1201         if(index == this.length()){
1202             // simple append
1203             return this.put(value);
1204         }
1205         // if we are inserting past the length, we want to grow the array all at once
1206         // instead of incrementally.
1207         this.myArrayList.ensureCapacity(index + 1);
1208         while (index != this.length()) {
1209             // we don't need to test validity of NULL objects
1210             this.myArrayList.add(JSONObject.NULL);
1211         }
1212         return this.put(value);
1213     }
1214     
1215     /**
1216      * Creates a JSONPointer using an initialization string and tries to 
1217      * match it to an item within this JSONArray. For example, given a
1218      * JSONArray initialized with this document:
1219      * <pre>
1220      * [
1221      *     {"b":"c"}
1222      * ]
1223      * </pre>
1224      * and this JSONPointer string: 
1225      * <pre>
1226      * "/0/b"
1227      * </pre>
1228      * Then this method will return the String "c"
1229      * A JSONPointerException may be thrown from code called by this method.
1230      *
1231      * @param jsonPointer string that can be used to create a JSONPointer
1232      * @return the item matched by the JSONPointer, otherwise null
1233      */
1234     public Object query(String jsonPointer) {
1235         return query(new JSONPointer(jsonPointer));
1236     }
1237     
1238     /**
1239      * Uses a uaer initialized JSONPointer  and tries to 
1240      * match it to an item whithin this JSONArray. For example, given a
1241      * JSONArray initialized with this document:
1242      * <pre>
1243      * [
1244      *     {"b":"c"}
1245      * ]
1246      * </pre>
1247      * and this JSONPointer: 
1248      * <pre>
1249      * "/0/b"
1250      * </pre>
1251      * Then this method will return the String "c"
1252      * A JSONPointerException may be thrown from code called by this method.
1253      *
1254      * @param jsonPointer string that can be used to create a JSONPointer
1255      * @return the item matched by the JSONPointer, otherwise null
1256      */
1257     public Object query(JSONPointer jsonPointer) {
1258         return jsonPointer.queryFrom(this);
1259     }
1260     
1261     /**
1262      * Queries and returns a value from this object using {@code jsonPointer}, or
1263      * returns null if the query fails due to a missing key.
1264      * 
1265      * @param jsonPointer the string representation of the JSON pointer
1266      * @return the queried value or {@code null}
1267      * @throws IllegalArgumentException if {@code jsonPointer} has invalid syntax
1268      */
1269     public Object optQuery(String jsonPointer) {
1270         return optQuery(new JSONPointer(jsonPointer));
1271     }
1272     
1273     /**
1274      * Queries and returns a value from this object using {@code jsonPointer}, or
1275      * returns null if the query fails due to a missing key.
1276      * 
1277      * @param jsonPointer The JSON pointer
1278      * @return the queried value or {@code null}
1279      * @throws IllegalArgumentException if {@code jsonPointer} has invalid syntax
1280      */
1281     public Object optQuery(JSONPointer jsonPointer) {
1282         try {
1283             return jsonPointer.queryFrom(this);
1284         } catch (JSONPointerException e) {
1285             return null;
1286         }
1287     }
1288
1289     /**
1290      * Remove an index and close the hole.
1291      *
1292      * @param index
1293      *            The index of the element to be removed.
1294      * @return The value that was associated with the index, or null if there
1295      *         was no value.
1296      */
1297     public Object remove(int index) {
1298         return index >= 0 && index < this.length()
1299             ? this.myArrayList.remove(index)
1300             : null;
1301     }
1302
1303     /**
1304      * Determine if two JSONArrays are similar.
1305      * They must contain similar sequences.
1306      *
1307      * @param other The other JSONArray
1308      * @return true if they are equal
1309      */
1310     public boolean similar(Object other) {
1311         if (!(other instanceof JSONArray)) {
1312             return false;
1313         }
1314         int len = this.length();
1315         if (len != ((JSONArray)other).length()) {
1316             return false;
1317         }
1318         for (int i = 0; i < len; i += 1) {
1319             Object valueThis = this.myArrayList.get(i);
1320             Object valueOther = ((JSONArray)other).myArrayList.get(i);
1321             if(valueThis == valueOther) {
1322                 continue;
1323             }
1324             if(valueThis == null) {
1325                 return false;
1326             }
1327             if (valueThis instanceof JSONObject) {
1328                 if (!((JSONObject)valueThis).similar(valueOther)) {
1329                     return false;
1330                 }
1331             } else if (valueThis instanceof JSONArray) {
1332                 if (!((JSONArray)valueThis).similar(valueOther)) {
1333                     return false;
1334                 }
1335             } else if (!valueThis.equals(valueOther)) {
1336                 return false;
1337             }
1338         }
1339         return true;
1340     }
1341
1342     /**
1343      * Produce a JSONObject by combining a JSONArray of names with the values of
1344      * this JSONArray.
1345      *
1346      * @param names
1347      *            A JSONArray containing a list of key strings. These will be
1348      *            paired with the values.
1349      * @return A JSONObject, or null if there are no names or if this JSONArray
1350      *         has no values.
1351      * @throws JSONException
1352      *             If any of the names are null.
1353      */
1354     public JSONObject toJSONObject(JSONArray names) throws JSONException {
1355         if (names == null || names.isEmpty() || this.isEmpty()) {
1356             return null;
1357         }
1358         JSONObject jo = new JSONObject(names.length());
1359         for (int i = 0; i < names.length(); i += 1) {
1360             jo.put(names.getString(i), this.opt(i));
1361         }
1362         return jo;
1363     }
1364
1365     /**
1366      * Make a JSON text of this JSONArray. For compactness, no unnecessary
1367      * whitespace is added. If it is not possible to produce a syntactically
1368      * correct JSON text then null will be returned instead. This could occur if
1369      * the array contains an invalid number.
1370      * <p><b>
1371      * Warning: This method assumes that the data structure is acyclical.
1372      * </b>
1373      *
1374      * @return a printable, displayable, transmittable representation of the
1375      *         array.
1376      */
1377     @Override
1378     public String toString() {
1379         try {
1380             return this.toString(0);
1381         } catch (Exception e) {
1382             return null;
1383         }
1384     }
1385
1386     /**
1387      * Make a pretty-printed JSON text of this JSONArray.
1388      * 
1389      * <p>If <code>indentFactor > 0</code> and the {@link JSONArray} has only
1390      * one element, then the array will be output on a single line:
1391      * <pre>{@code [1]}</pre>
1392      * 
1393      * <p>If an array has 2 or more elements, then it will be output across
1394      * multiple lines: <pre>{@code
1395      * [
1396      * 1,
1397      * "value 2",
1398      * 3
1399      * ]
1400      * }</pre>
1401      * <p><b>
1402      * Warning: This method assumes that the data structure is acyclical.
1403      * </b>
1404      * 
1405      * @param indentFactor
1406      *            The number of spaces to add to each level of indentation.
1407      * @return a printable, displayable, transmittable representation of the
1408      *         object, beginning with <code>[</code>&nbsp;<small>(left
1409      *         bracket)</small> and ending with <code>]</code>
1410      *         &nbsp;<small>(right bracket)</small>.
1411      * @throws JSONException
1412      */
1413     public String toString(int indentFactor) throws JSONException {
1414         StringWriter sw = new StringWriter();
1415         synchronized (sw.getBuffer()) {
1416             return this.write(sw, indentFactor, 0).toString();
1417         }
1418     }
1419
1420     /**
1421      * Write the contents of the JSONArray as JSON text to a writer. For
1422      * compactness, no whitespace is added.
1423      * <p><b>
1424      * Warning: This method assumes that the data structure is acyclical.
1425      *</b>
1426      *
1427      * @return The writer.
1428      * @throws JSONException
1429      */
1430     public Writer write(Writer writer) throws JSONException {
1431         return this.write(writer, 0, 0);
1432     }
1433
1434     /**
1435      * Write the contents of the JSONArray as JSON text to a writer.
1436      * 
1437      * <p>If <code>indentFactor > 0</code> and the {@link JSONArray} has only
1438      * one element, then the array will be output on a single line:
1439      * <pre>{@code [1]}</pre>
1440      * 
1441      * <p>If an array has 2 or more elements, then it will be output across
1442      * multiple lines: <pre>{@code
1443      * [
1444      * 1,
1445      * "value 2",
1446      * 3
1447      * ]
1448      * }</pre>
1449      * <p><b>
1450      * Warning: This method assumes that the data structure is acyclical.
1451      * </b>
1452      *
1453      * @param writer
1454      *            Writes the serialized JSON
1455      * @param indentFactor
1456      *            The number of spaces to add to each level of indentation.
1457      * @param indent
1458      *            The indentation of the top level.
1459      * @return The writer.
1460      * @throws JSONException
1461      */
1462     public Writer write(Writer writer, int indentFactor, int indent)
1463             throws JSONException {
1464         try {
1465             boolean commanate = false;
1466             int length = this.length();
1467             writer.write('[');
1468
1469             if (length == 1) {
1470                 try {
1471                     JSONObject.writeValue(writer, this.myArrayList.get(0),
1472                             indentFactor, indent);
1473                 } catch (Exception e) {
1474                     throw new JSONException("Unable to write JSONArray value at index: 0", e);
1475                 }
1476             } else if (length != 0) {
1477                 final int newindent = indent + indentFactor;
1478
1479                 for (int i = 0; i < length; i += 1) {
1480                     if (commanate) {
1481                         writer.write(',');
1482                     }
1483                     if (indentFactor > 0) {
1484                         writer.write('\n');
1485                     }
1486                     JSONObject.indent(writer, newindent);
1487                     try {
1488                         JSONObject.writeValue(writer, this.myArrayList.get(i),
1489                                 indentFactor, newindent);
1490                     } catch (Exception e) {
1491                         throw new JSONException("Unable to write JSONArray value at index: " + i, e);
1492                     }
1493                     commanate = true;
1494                 }
1495                 if (indentFactor > 0) {
1496                     writer.write('\n');
1497                 }
1498                 JSONObject.indent(writer, indent);
1499             }
1500             writer.write(']');
1501             return writer;
1502         } catch (IOException e) {
1503             throw new JSONException(e);
1504         }
1505     }
1506
1507     /**
1508      * Returns a java.util.List containing all of the elements in this array.
1509      * If an element in the array is a JSONArray or JSONObject it will also
1510      * be converted.
1511      * <p>
1512      * Warning: This method assumes that the data structure is acyclical.
1513      *
1514      * @return a java.util.List containing the elements of this array
1515      */
1516     public List<Object> toList() {
1517         List<Object> results = new ArrayList<Object>(this.myArrayList.size());
1518         for (Object element : this.myArrayList) {
1519             if (element == null || JSONObject.NULL.equals(element)) {
1520                 results.add(null);
1521             } else if (element instanceof JSONArray) {
1522                 results.add(((JSONArray) element).toList());
1523             } else if (element instanceof JSONObject) {
1524                 results.add(((JSONObject) element).toMap());
1525             } else {
1526                 results.add(element);
1527             }
1528         }
1529         return results;
1530     }
1531
1532     /**
1533      * Check if JSONArray is empty.
1534      *
1535      * @return true if JSONArray is empty, otherwise false.
1536      */
1537     public boolean isEmpty() {
1538         return myArrayList.isEmpty();
1539     }
1540
1541 }