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