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