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