Home | History | Annotate | Download | only in json
      1 /*
      2  * Copyright (C) 2010 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package org.json;
     18 
     19 import java.lang.reflect.Array;
     20 import java.util.ArrayList;
     21 import java.util.Collection;
     22 import java.util.Iterator;
     23 import java.util.List;
     24 
     25 // Note: this class was written without inspecting the non-free org.json sourcecode.
     26 
     27 /**
     28  * A dense indexed sequence of values. Values may be any mix of
     29  * {@link JSONObject JSONObjects}, other {@link JSONArray JSONArrays}, Strings,
     30  * Booleans, Integers, Longs, Doubles, {@code null} or {@link JSONObject#NULL}.
     31  * Values may not be {@link Double#isNaN() NaNs}, {@link Double#isInfinite()
     32  * infinities}, or of any type not listed here.
     33  *
     34  * <p>{@code JSONArray} has the same type coercion behavior and
     35  * optional/mandatory accessors as {@link JSONObject}. See that class'
     36  * documentation for details.
     37  *
     38  * <p><strong>Warning:</strong> this class represents null in two incompatible
     39  * ways: the standard Java {@code null} reference, and the sentinel value {@link
     40  * JSONObject#NULL}. In particular, {@code get} fails if the requested index
     41  * holds the null reference, but succeeds if it holds {@code JSONObject.NULL}.
     42  *
     43  * <p>Instances of this class are not thread safe. Although this class is
     44  * nonfinal, it was not designed for inheritance and should not be subclassed.
     45  * In particular, self-use by overridable methods is not specified. See
     46  * <i>Effective Java</i> Item 17, "Design and Document or inheritance or else
     47  * prohibit it" for further information.
     48  */
     49 public class JSONArray {
     50 
     51     private final List<Object> values;
     52 
     53     /**
     54      * Creates a {@code JSONArray} with no values.
     55      */
     56     public JSONArray() {
     57         values = new ArrayList<Object>();
     58     }
     59 
     60     /**
     61      * Creates a new {@code JSONArray} by copying all values from the given
     62      * collection.
     63      *
     64      * @param copyFrom a collection whose values are of supported types.
     65      *     Unsupported values are not permitted and will yield an array in an
     66      *     inconsistent state.
     67      */
     68     /* Accept a raw type for API compatibility */
     69     public JSONArray(Collection copyFrom) {
     70         this();
     71         if (copyFrom != null) {
     72             for (Iterator it = copyFrom.iterator(); it.hasNext();) {
     73                 put(JSONObject.wrap(it.next()));
     74             }
     75         }
     76     }
     77 
     78     /**
     79      * Creates a new {@code JSONArray} with values from the next array in the
     80      * tokener.
     81      *
     82      * @param readFrom a tokener whose nextValue() method will yield a
     83      *     {@code JSONArray}.
     84      * @throws JSONException if the parse fails or doesn't yield a
     85      *     {@code JSONArray}.
     86      */
     87     public JSONArray(JSONTokener readFrom) throws JSONException {
     88         /*
     89          * Getting the parser to populate this could get tricky. Instead, just
     90          * parse to temporary JSONArray and then steal the data from that.
     91          */
     92         Object object = readFrom.nextValue();
     93         if (object instanceof JSONArray) {
     94             values = ((JSONArray) object).values;
     95         } else {
     96             throw JSON.typeMismatch(object, "JSONArray");
     97         }
     98     }
     99 
    100     /**
    101      * Creates a new {@code JSONArray} with values from the JSON string.
    102      *
    103      * @param json a JSON-encoded string containing an array.
    104      * @throws JSONException if the parse fails or doesn't yield a {@code
    105      *     JSONArray}.
    106      */
    107     public JSONArray(String json) throws JSONException {
    108         this(new JSONTokener(json));
    109     }
    110 
    111     /**
    112      * Creates a new {@code JSONArray} with values from the given primitive array.
    113      */
    114     public JSONArray(Object array) throws JSONException {
    115         if (!array.getClass().isArray()) {
    116             throw new JSONException("Not a primitive array: " + array.getClass());
    117         }
    118         final int length = Array.getLength(array);
    119         values = new ArrayList<Object>(length);
    120         for (int i = 0; i < length; ++i) {
    121             put(JSONObject.wrap(Array.get(array, i)));
    122         }
    123     }
    124 
    125     /**
    126      * Returns the number of values in this array.
    127      */
    128     public int length() {
    129         return values.size();
    130     }
    131 
    132     /**
    133      * Appends {@code value} to the end of this array.
    134      *
    135      * @return this array.
    136      */
    137     public JSONArray put(boolean value) {
    138         values.add(value);
    139         return this;
    140     }
    141 
    142     /**
    143      * Appends {@code value} to the end of this array.
    144      *
    145      * @param value a finite value. May not be {@link Double#isNaN() NaNs} or
    146      *     {@link Double#isInfinite() infinities}.
    147      * @return this array.
    148      */
    149     public JSONArray put(double value) throws JSONException {
    150         values.add(JSON.checkDouble(value));
    151         return this;
    152     }
    153 
    154     /**
    155      * Appends {@code value} to the end of this array.
    156      *
    157      * @return this array.
    158      */
    159     public JSONArray put(int value) {
    160         values.add(value);
    161         return this;
    162     }
    163 
    164     /**
    165      * Appends {@code value} to the end of this array.
    166      *
    167      * @return this array.
    168      */
    169     public JSONArray put(long value) {
    170         values.add(value);
    171         return this;
    172     }
    173 
    174     /**
    175      * Appends {@code value} to the end of this array.
    176      *
    177      * @param value a {@link JSONObject}, {@link JSONArray}, String, Boolean,
    178      *     Integer, Long, Double, {@link JSONObject#NULL}, or {@code null}. May
    179      *     not be {@link Double#isNaN() NaNs} or {@link Double#isInfinite()
    180      *     infinities}. Unsupported values are not permitted and will cause the
    181      *     array to be in an inconsistent state.
    182      * @return this array.
    183      */
    184     public JSONArray put(Object value) {
    185         values.add(value);
    186         return this;
    187     }
    188 
    189     /**
    190      * Same as {@link #put}, with added validity checks.
    191      */
    192     void checkedPut(Object value) throws JSONException {
    193         if (value instanceof Number) {
    194             JSON.checkDouble(((Number) value).doubleValue());
    195         }
    196 
    197         put(value);
    198     }
    199 
    200     /**
    201      * Sets the value at {@code index} to {@code value}, null padding this array
    202      * to the required length if necessary. If a value already exists at {@code
    203      * index}, it will be replaced.
    204      *
    205      * @return this array.
    206      */
    207     public JSONArray put(int index, boolean value) throws JSONException {
    208         return put(index, (Boolean) value);
    209     }
    210 
    211     /**
    212      * Sets the value at {@code index} to {@code value}, null padding this array
    213      * to the required length if necessary. If a value already exists at {@code
    214      * index}, it will be replaced.
    215      *
    216      * @param value a finite value. May not be {@link Double#isNaN() NaNs} or
    217      *     {@link Double#isInfinite() infinities}.
    218      * @return this array.
    219      */
    220     public JSONArray put(int index, double value) throws JSONException {
    221         return put(index, (Double) value);
    222     }
    223 
    224     /**
    225      * Sets the value at {@code index} to {@code value}, null padding this array
    226      * to the required length if necessary. If a value already exists at {@code
    227      * index}, it will be replaced.
    228      *
    229      * @return this array.
    230      */
    231     public JSONArray put(int index, int value) throws JSONException {
    232         return put(index, (Integer) value);
    233     }
    234 
    235     /**
    236      * Sets the value at {@code index} to {@code value}, null padding this array
    237      * to the required length if necessary. If a value already exists at {@code
    238      * index}, it will be replaced.
    239      *
    240      * @return this array.
    241      */
    242     public JSONArray put(int index, long value) throws JSONException {
    243         return put(index, (Long) value);
    244     }
    245 
    246     /**
    247      * Sets the value at {@code index} to {@code value}, null padding this array
    248      * to the required length if necessary. If a value already exists at {@code
    249      * index}, it will be replaced.
    250      *
    251      * @param value a {@link JSONObject}, {@link JSONArray}, String, Boolean,
    252      *     Integer, Long, Double, {@link JSONObject#NULL}, or {@code null}. May
    253      *     not be {@link Double#isNaN() NaNs} or {@link Double#isInfinite()
    254      *     infinities}.
    255      * @return this array.
    256      */
    257     public JSONArray put(int index, Object value) throws JSONException {
    258         if (value instanceof Number) {
    259             // deviate from the original by checking all Numbers, not just floats & doubles
    260             JSON.checkDouble(((Number) value).doubleValue());
    261         }
    262         while (values.size() <= index) {
    263             values.add(null);
    264         }
    265         values.set(index, value);
    266         return this;
    267     }
    268 
    269     /**
    270      * Returns true if this array has no value at {@code index}, or if its value
    271      * is the {@code null} reference or {@link JSONObject#NULL}.
    272      */
    273     public boolean isNull(int index) {
    274         Object value = opt(index);
    275         return value == null || value == JSONObject.NULL;
    276     }
    277 
    278     /**
    279      * Returns the value at {@code index}.
    280      *
    281      * @throws JSONException if this array has no value at {@code index}, or if
    282      *     that value is the {@code null} reference. This method returns
    283      *     normally if the value is {@code JSONObject#NULL}.
    284      */
    285     public Object get(int index) throws JSONException {
    286         try {
    287             Object value = values.get(index);
    288             if (value == null) {
    289                 throw new JSONException("Value at " + index + " is null.");
    290             }
    291             return value;
    292         } catch (IndexOutOfBoundsException e) {
    293             throw new JSONException("Index " + index + " out of range [0.." + values.size() + ")");
    294         }
    295     }
    296 
    297     /**
    298      * Returns the value at {@code index}, or null if the array has no value
    299      * at {@code index}.
    300      */
    301     public Object opt(int index) {
    302         if (index < 0 || index >= values.size()) {
    303             return null;
    304         }
    305         return values.get(index);
    306     }
    307 
    308     /**
    309      * Removes and returns the value at {@code index}, or null if the array has no value
    310      * at {@code index}.
    311      */
    312     public Object remove(int index) {
    313         if (index < 0 || index >= values.size()) {
    314             return null;
    315         }
    316         return values.remove(index);
    317     }
    318 
    319     /**
    320      * Returns the value at {@code index} if it exists and is a boolean or can
    321      * be coerced to a boolean.
    322      *
    323      * @throws JSONException if the value at {@code index} doesn't exist or
    324      *     cannot be coerced to a boolean.
    325      */
    326     public boolean getBoolean(int index) throws JSONException {
    327         Object object = get(index);
    328         Boolean result = JSON.toBoolean(object);
    329         if (result == null) {
    330             throw JSON.typeMismatch(index, object, "boolean");
    331         }
    332         return result;
    333     }
    334 
    335     /**
    336      * Returns the value at {@code index} if it exists and is a boolean or can
    337      * be coerced to a boolean. Returns false otherwise.
    338      */
    339     public boolean optBoolean(int index) {
    340         return optBoolean(index, false);
    341     }
    342 
    343     /**
    344      * Returns the value at {@code index} if it exists and is a boolean or can
    345      * be coerced to a boolean. Returns {@code fallback} otherwise.
    346      */
    347     public boolean optBoolean(int index, boolean fallback) {
    348         Object object = opt(index);
    349         Boolean result = JSON.toBoolean(object);
    350         return result != null ? result : fallback;
    351     }
    352 
    353     /**
    354      * Returns the value at {@code index} if it exists and is a double or can
    355      * be coerced to a double.
    356      *
    357      * @throws JSONException if the value at {@code index} doesn't exist or
    358      *     cannot be coerced to a double.
    359      */
    360     public double getDouble(int index) throws JSONException {
    361         Object object = get(index);
    362         Double result = JSON.toDouble(object);
    363         if (result == null) {
    364             throw JSON.typeMismatch(index, object, "double");
    365         }
    366         return result;
    367     }
    368 
    369     /**
    370      * Returns the value at {@code index} if it exists and is a double or can
    371      * be coerced to a double. Returns {@code NaN} otherwise.
    372      */
    373     public double optDouble(int index) {
    374         return optDouble(index, Double.NaN);
    375     }
    376 
    377     /**
    378      * Returns the value at {@code index} if it exists and is a double or can
    379      * be coerced to a double. Returns {@code fallback} otherwise.
    380      */
    381     public double optDouble(int index, double fallback) {
    382         Object object = opt(index);
    383         Double result = JSON.toDouble(object);
    384         return result != null ? result : fallback;
    385     }
    386 
    387     /**
    388      * Returns the value at {@code index} if it exists and is an int or
    389      * can be coerced to an int.
    390      *
    391      * @throws JSONException if the value at {@code index} doesn't exist or
    392      *     cannot be coerced to a int.
    393      */
    394     public int getInt(int index) throws JSONException {
    395         Object object = get(index);
    396         Integer result = JSON.toInteger(object);
    397         if (result == null) {
    398             throw JSON.typeMismatch(index, object, "int");
    399         }
    400         return result;
    401     }
    402 
    403     /**
    404      * Returns the value at {@code index} if it exists and is an int or
    405      * can be coerced to an int. Returns 0 otherwise.
    406      */
    407     public int optInt(int index) {
    408         return optInt(index, 0);
    409     }
    410 
    411     /**
    412      * Returns the value at {@code index} if it exists and is an int or
    413      * can be coerced to an int. Returns {@code fallback} otherwise.
    414      */
    415     public int optInt(int index, int fallback) {
    416         Object object = opt(index);
    417         Integer result = JSON.toInteger(object);
    418         return result != null ? result : fallback;
    419     }
    420 
    421     /**
    422      * Returns the value at {@code index} if it exists and is a long or
    423      * can be coerced to a long.
    424      *
    425      * @throws JSONException if the value at {@code index} doesn't exist or
    426      *     cannot be coerced to a long.
    427      */
    428     public long getLong(int index) throws JSONException {
    429         Object object = get(index);
    430         Long result = JSON.toLong(object);
    431         if (result == null) {
    432             throw JSON.typeMismatch(index, object, "long");
    433         }
    434         return result;
    435     }
    436 
    437     /**
    438      * Returns the value at {@code index} if it exists and is a long or
    439      * can be coerced to a long. Returns 0 otherwise.
    440      */
    441     public long optLong(int index) {
    442         return optLong(index, 0L);
    443     }
    444 
    445     /**
    446      * Returns the value at {@code index} if it exists and is a long or
    447      * can be coerced to a long. Returns {@code fallback} otherwise.
    448      */
    449     public long optLong(int index, long fallback) {
    450         Object object = opt(index);
    451         Long result = JSON.toLong(object);
    452         return result != null ? result : fallback;
    453     }
    454 
    455     /**
    456      * Returns the value at {@code index} if it exists, coercing it if
    457      * necessary.
    458      *
    459      * @throws JSONException if no such value exists.
    460      */
    461     public String getString(int index) throws JSONException {
    462         Object object = get(index);
    463         String result = JSON.toString(object);
    464         if (result == null) {
    465             throw JSON.typeMismatch(index, object, "String");
    466         }
    467         return result;
    468     }
    469 
    470     /**
    471      * Returns the value at {@code index} if it exists, coercing it if
    472      * necessary. Returns the empty string if no such value exists.
    473      */
    474     public String optString(int index) {
    475         return optString(index, "");
    476     }
    477 
    478     /**
    479      * Returns the value at {@code index} if it exists, coercing it if
    480      * necessary. Returns {@code fallback} if no such value exists.
    481      */
    482     public String optString(int index, String fallback) {
    483         Object object = opt(index);
    484         String result = JSON.toString(object);
    485         return result != null ? result : fallback;
    486     }
    487 
    488     /**
    489      * Returns the value at {@code index} if it exists and is a {@code
    490      * JSONArray}.
    491      *
    492      * @throws JSONException if the value doesn't exist or is not a {@code
    493      *     JSONArray}.
    494      */
    495     public JSONArray getJSONArray(int index) throws JSONException {
    496         Object object = get(index);
    497         if (object instanceof JSONArray) {
    498             return (JSONArray) object;
    499         } else {
    500             throw JSON.typeMismatch(index, object, "JSONArray");
    501         }
    502     }
    503 
    504     /**
    505      * Returns the value at {@code index} if it exists and is a {@code
    506      * JSONArray}. Returns null otherwise.
    507      */
    508     public JSONArray optJSONArray(int index) {
    509         Object object = opt(index);
    510         return object instanceof JSONArray ? (JSONArray) object : null;
    511     }
    512 
    513     /**
    514      * Returns the value at {@code index} if it exists and is a {@code
    515      * JSONObject}.
    516      *
    517      * @throws JSONException if the value doesn't exist or is not a {@code
    518      *     JSONObject}.
    519      */
    520     public JSONObject getJSONObject(int index) throws JSONException {
    521         Object object = get(index);
    522         if (object instanceof JSONObject) {
    523             return (JSONObject) object;
    524         } else {
    525             throw JSON.typeMismatch(index, object, "JSONObject");
    526         }
    527     }
    528 
    529     /**
    530      * Returns the value at {@code index} if it exists and is a {@code
    531      * JSONObject}. Returns null otherwise.
    532      */
    533     public JSONObject optJSONObject(int index) {
    534         Object object = opt(index);
    535         return object instanceof JSONObject ? (JSONObject) object : null;
    536     }
    537 
    538     /**
    539      * Returns a new object whose values are the values in this array, and whose
    540      * names are the values in {@code names}. Names and values are paired up by
    541      * index from 0 through to the shorter array's length. Names that are not
    542      * strings will be coerced to strings. This method returns null if either
    543      * array is empty.
    544      */
    545     public JSONObject toJSONObject(JSONArray names) throws JSONException {
    546         JSONObject result = new JSONObject();
    547         int length = Math.min(names.length(), values.size());
    548         if (length == 0) {
    549             return null;
    550         }
    551         for (int i = 0; i < length; i++) {
    552             String name = JSON.toString(names.opt(i));
    553             result.put(name, opt(i));
    554         }
    555         return result;
    556     }
    557 
    558     /**
    559      * Returns a new string by alternating this array's values with {@code
    560      * separator}. This array's string values are quoted and have their special
    561      * characters escaped. For example, the array containing the strings '12"
    562      * pizza', 'taco' and 'soda' joined on '+' returns this:
    563      * <pre>"12\" pizza"+"taco"+"soda"</pre>
    564      */
    565     public String join(String separator) throws JSONException {
    566         JSONStringer stringer = new JSONStringer();
    567         stringer.open(JSONStringer.Scope.NULL, "");
    568         for (int i = 0, size = values.size(); i < size; i++) {
    569             if (i > 0) {
    570                 stringer.out.append(separator);
    571             }
    572             stringer.value(values.get(i));
    573         }
    574         stringer.close(JSONStringer.Scope.NULL, JSONStringer.Scope.NULL, "");
    575         return stringer.out.toString();
    576     }
    577 
    578     /**
    579      * Encodes this array as a compact JSON string, such as:
    580      * <pre>[94043,90210]</pre>
    581      */
    582     @Override public String toString() {
    583         try {
    584             JSONStringer stringer = new JSONStringer();
    585             writeTo(stringer);
    586             return stringer.toString();
    587         } catch (JSONException e) {
    588             return null;
    589         }
    590     }
    591 
    592     /**
    593      * Encodes this array as a human readable JSON string for debugging, such
    594      * as:
    595      * <pre>
    596      * [
    597      *     94043,
    598      *     90210
    599      * ]</pre>
    600      *
    601      * @param indentSpaces the number of spaces to indent for each level of
    602      *     nesting.
    603      */
    604     public String toString(int indentSpaces) throws JSONException {
    605         JSONStringer stringer = new JSONStringer(indentSpaces);
    606         writeTo(stringer);
    607         return stringer.toString();
    608     }
    609 
    610     void writeTo(JSONStringer stringer) throws JSONException {
    611         stringer.array();
    612         for (Object value : values) {
    613             stringer.value(value);
    614         }
    615         stringer.endArray();
    616     }
    617 
    618     @Override public boolean equals(Object o) {
    619         return o instanceof JSONArray && ((JSONArray) o).values.equals(values);
    620     }
    621 
    622     @Override public int hashCode() {
    623         // diverge from the original, which doesn't implement hashCode
    624         return values.hashCode();
    625     }
    626 }
    627