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 */ JSONArray()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 */ JSONArray(Collection copyFrom)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 */ JSONArray(JSONTokener readFrom)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 */ JSONArray(String json)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 */ JSONArray(Object array)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 */ length()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 */ put(boolean value)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 */ put(double value)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 */ put(int value)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 */ put(long value)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 */ put(Object value)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 */ checkedPut(Object value)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 */ put(int index, boolean value)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 */ put(int index, double value)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 */ put(int index, int value)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 */ put(int index, long value)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 */ put(int index, Object value)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 */ isNull(int index)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 */ get(int index)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 */ opt(int index)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 */ remove(int index)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 */ getBoolean(int index)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 */ optBoolean(int index)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 */ optBoolean(int index, boolean fallback)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 */ getDouble(int index)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 */ optDouble(int index)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 */ optDouble(int index, double fallback)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 */ getInt(int index)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 */ optInt(int index)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 */ optInt(int index, int fallback)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 */ getLong(int index)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 */ optLong(int index)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 */ optLong(int index, long fallback)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 */ getString(int index)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 */ optString(int index)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 */ optString(int index, String fallback)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 */ getJSONArray(int index)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 */ optJSONArray(int index)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 */ getJSONObject(int index)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 */ optJSONObject(int index)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 */ toJSONObject(JSONArray names)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 */ join(String separator)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 */ toString()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 */ toString(int indentSpaces)604 public String toString(int indentSpaces) throws JSONException { 605 JSONStringer stringer = new JSONStringer(indentSpaces); 606 writeTo(stringer); 607 return stringer.toString(); 608 } 609 writeTo(JSONStringer stringer)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 equals(Object o)618 @Override public boolean equals(Object o) { 619 return o instanceof JSONArray && ((JSONArray) o).values.equals(values); 620 } 621 hashCode()622 @Override public int hashCode() { 623 // diverge from the original, which doesn't implement hashCode 624 return values.hashCode(); 625 } 626 } 627