001/******************************************************************************* 002 * Copyright (c) 2013, 2015 EclipseSource. 003 * 004 * Permission is hereby granted, free of charge, to any person obtaining a copy 005 * of this software and associated documentation files (the "Software"), to deal 006 * in the Software without restriction, including without limitation the rights 007 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 008 * copies of the Software, and to permit persons to whom the Software is 009 * furnished to do so, subject to the following conditions: 010 * 011 * The above copyright notice and this permission notice shall be included in all 012 * copies or substantial portions of the Software. 013 * 014 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 015 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 016 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 017 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 018 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 019 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 020 * SOFTWARE. 021 ******************************************************************************/ 022package com.restfb.json; 023 024import java.io.IOException; 025import java.util.*; 026import java.util.stream.Stream; 027 028/** 029 * Represents a JSON array, an ordered collection of JSON values. 030 * <p> 031 * Elements can be added using the <code>add(...)</code> methods which accept instances of {@link JsonValue}, strings, 032 * primitive numbers, and boolean values. To replace an element of an array, use the <code>set(int, ...)</code> methods. 033 * </p> 034 * <p> 035 * Elements can be accessed by their index using {@link #get(int)}. This class also supports iterating over the elements 036 * in document order using an {@link #iterator()} or an enhanced for loop: 037 * </p> 038 * 039 * <pre> 040 * for (JsonValue value : jsonArray) { 041 * ... 042 * } 043 * </pre> 044 * <p> 045 * An equivalent {@link List} can be obtained from the method {@link #values()}. 046 * </p> 047 * <p> 048 * Note that this class is <strong>not thread-safe</strong>. If multiple threads access a <code>JsonArray</code> 049 * instance concurrently, while at least one of these threads modifies the contents of this array, access to the 050 * instance must be synchronized externally. Failure to do so may lead to an inconsistent state. 051 * </p> 052 * <p> 053 * This class is <strong>not supposed to be extended</strong> by clients. 054 * </p> 055 */ 056@SuppressWarnings("serial") // use default serial UID 057public class JsonArray extends JsonValue implements Iterable<JsonValue> { 058 059 private final List<JsonValue> values; 060 061 /** 062 * Creates a new empty JsonArray. 063 */ 064 public JsonArray() { 065 values = new ArrayList<>(); 066 } 067 068 /** 069 * Creates a new JsonArray with the contents of the specified JSON array. 070 * 071 * @param array 072 * the JsonArray to get the initial contents from, must not be <code>null</code> 073 */ 074 public JsonArray(JsonArray array) { 075 this(array, false); 076 } 077 078 private JsonArray(JsonArray array, boolean unmodifiable) { 079 Objects.requireNonNull(array, ARRAY_IS_NULL); 080 if (unmodifiable) { 081 values = Collections.unmodifiableList(array.values); 082 } else { 083 values = new ArrayList<>(array.values); 084 } 085 } 086 087 /** 088 * Returns an unmodifiable wrapper for the specified JsonArray. This method allows to provide read-only access to a 089 * JsonArray. 090 * <p> 091 * The returned JsonArray is backed by the given array and reflects subsequent changes. Attempts to modify the 092 * returned JsonArray result in an <code>UnsupportedOperationException</code>. 093 * </p> 094 * 095 * @param array 096 * the JsonArray for which an unmodifiable JsonArray is to be returned 097 * @return an unmodifiable view of the specified JsonArray 098 */ 099 public static JsonArray unmodifiableArray(JsonArray array) { 100 return new JsonArray(array, true); 101 } 102 103 /** 104 * Appends the JSON representation of the specified <code>int</code> value to the end of this array. 105 * 106 * @param value 107 * the value to add to the array 108 * @return the array itself, to enable method chaining 109 */ 110 public JsonArray add(int value) { 111 values.add(Json.value(value)); 112 return this; 113 } 114 115 /** 116 * Appends the JSON representation of the specified <code>long</code> value to the end of this array. 117 * 118 * @param value 119 * the value to add to the array 120 * @return the array itself, to enable method chaining 121 */ 122 public JsonArray add(long value) { 123 values.add(Json.value(value)); 124 return this; 125 } 126 127 /** 128 * Appends the JSON representation of the specified <code>float</code> value to the end of this array. 129 * 130 * @param value 131 * the value to add to the array 132 * @return the array itself, to enable method chaining 133 */ 134 public JsonArray add(float value) { 135 values.add(Json.value(value)); 136 return this; 137 } 138 139 /** 140 * Appends the JSON representation of the specified <code>double</code> value to the end of this array. 141 * 142 * @param value 143 * the value to add to the array 144 * @return the array itself, to enable method chaining 145 */ 146 public JsonArray add(double value) { 147 values.add(Json.value(value)); 148 return this; 149 } 150 151 /** 152 * Appends the JSON representation of the specified <code>boolean</code> value to the end of this array. 153 * 154 * @param value 155 * the value to add to the array 156 * @return the array itself, to enable method chaining 157 */ 158 public JsonArray add(boolean value) { 159 values.add(Json.value(value)); 160 return this; 161 } 162 163 /** 164 * Appends the JSON representation of the specified string to the end of this array. 165 * 166 * @param value 167 * the string to add to the array 168 * @return the array itself, to enable method chaining 169 */ 170 public JsonArray add(String value) { 171 values.add(Json.value(value)); 172 return this; 173 } 174 175 /** 176 * Appends the specified JSON value to the end of this array. 177 * 178 * @param value 179 * the JsonValue to add to the array, must not be <code>null</code> 180 * @return the array itself, to enable method chaining 181 */ 182 public JsonArray add(JsonValue value) { 183 Objects.requireNonNull(value, VALUE_IS_NULL); 184 values.add(value); 185 return this; 186 } 187 188 /** 189 * Replaces the element at the specified position in this array with the JSON representation of the specified 190 * <code>int</code> value. 191 * 192 * @param index 193 * the index of the array element to replace 194 * @param value 195 * the value to be stored at the specified array position 196 * @return the array itself, to enable method chaining 197 * @throws IndexOutOfBoundsException 198 * if the index is out of range, i.e. <code>index < 0</code> or <code>index >= size</code> 199 */ 200 public JsonArray set(int index, int value) { 201 values.set(index, Json.value(value)); 202 return this; 203 } 204 205 /** 206 * Replaces the element at the specified position in this array with the JSON representation of the specified 207 * <code>long</code> value. 208 * 209 * @param index 210 * the index of the array element to replace 211 * @param value 212 * the value to be stored at the specified array position 213 * @return the array itself, to enable method chaining 214 * @throws IndexOutOfBoundsException 215 * if the index is out of range, i.e. <code>index < 0</code> or <code>index >= size</code> 216 */ 217 public JsonArray set(int index, long value) { 218 values.set(index, Json.value(value)); 219 return this; 220 } 221 222 /** 223 * Replaces the element at the specified position in this array with the JSON representation of the specified 224 * <code>float</code> value. 225 * 226 * @param index 227 * the index of the array element to replace 228 * @param value 229 * the value to be stored at the specified array position 230 * @return the array itself, to enable method chaining 231 * @throws IndexOutOfBoundsException 232 * if the index is out of range, i.e. <code>index < 0</code> or <code>index >= size</code> 233 */ 234 public JsonArray set(int index, float value) { 235 values.set(index, Json.value(value)); 236 return this; 237 } 238 239 /** 240 * Replaces the element at the specified position in this array with the JSON representation of the specified 241 * <code>double</code> value. 242 * 243 * @param index 244 * the index of the array element to replace 245 * @param value 246 * the value to be stored at the specified array position 247 * @return the array itself, to enable method chaining 248 * @throws IndexOutOfBoundsException 249 * if the index is out of range, i.e. <code>index < 0</code> or <code>index >= size</code> 250 */ 251 public JsonArray set(int index, double value) { 252 values.set(index, Json.value(value)); 253 return this; 254 } 255 256 /** 257 * Replaces the element at the specified position in this array with the JSON representation of the specified 258 * <code>boolean</code> value. 259 * 260 * @param index 261 * the index of the array element to replace 262 * @param value 263 * the value to be stored at the specified array position 264 * @return the array itself, to enable method chaining 265 * @throws IndexOutOfBoundsException 266 * if the index is out of range, i.e. <code>index < 0</code> or <code>index >= size</code> 267 */ 268 public JsonArray set(int index, boolean value) { 269 values.set(index, Json.value(value)); 270 return this; 271 } 272 273 /** 274 * Replaces the element at the specified position in this array with the JSON representation of the specified string. 275 * 276 * @param index 277 * the index of the array element to replace 278 * @param value 279 * the string to be stored at the specified array position 280 * @return the array itself, to enable method chaining 281 * @throws IndexOutOfBoundsException 282 * if the index is out of range, i.e. <code>index < 0</code> or <code>index >= size</code> 283 */ 284 public JsonArray set(int index, String value) { 285 values.set(index, Json.value(value)); 286 return this; 287 } 288 289 /** 290 * Replaces the element at the specified position in this array with the specified JSON value. 291 * 292 * @param index 293 * the index of the array element to replace 294 * @param value 295 * the value to be stored at the specified array position, must not be <code>null</code> 296 * @return the array itself, to enable method chaining 297 * @throws IndexOutOfBoundsException 298 * if the index is out of range, i.e. <code>index < 0</code> or <code>index >= size</code> 299 */ 300 public JsonArray set(int index, JsonValue value) { 301 Objects.requireNonNull(value, VALUE_IS_NULL); 302 values.set(index, value); 303 return this; 304 } 305 306 /** 307 * Removes the element at the specified index from this array. 308 * 309 * @param index 310 * the index of the element to remove 311 * @return the array itself, to enable method chaining 312 * @throws IndexOutOfBoundsException 313 * if the index is out of range, i.e. <code>index < 0</code> or <code>index >= size</code> 314 */ 315 public JsonArray remove(int index) { 316 values.remove(index); 317 return this; 318 } 319 320 /** 321 * Returns the number of elements in this array. 322 * 323 * @return the number of elements in this array 324 */ 325 public int size() { 326 return values.size(); 327 } 328 329 /** 330 * Returns <code>true</code> if this array contains no elements. 331 * 332 * @return <code>true</code> if this array contains no elements 333 */ 334 public boolean isEmpty() { 335 return values.isEmpty(); 336 } 337 338 /** 339 * Returns the value of the element at the specified position in this array. 340 * 341 * @param index 342 * the index of the array element to return 343 * @return the value of the element at the specified position 344 * @throws IndexOutOfBoundsException 345 * if the index is out of range, i.e. <code>index < 0</code> or <code>index >= size</code> 346 */ 347 public JsonValue get(int index) { 348 return values.get(index); 349 } 350 351 /** 352 * Returns a list of the values in this array in document order. The returned list is backed by this array and will 353 * reflect subsequent changes. It cannot be used to modify this array. Attempts to modify the returned list will 354 * result in an exception. 355 * 356 * @return a list of the values in this array 357 */ 358 public List<JsonValue> values() { 359 return Collections.unmodifiableList(values); 360 } 361 362 public Stream<JsonValue> valueStream() { 363 return values().stream(); 364 } 365 366 /** 367 * Returns an iterator over the values of this array in document order. The returned iterator cannot be used to modify 368 * this array. 369 * 370 * @return an iterator over the values of this array 371 */ 372 public Iterator<JsonValue> iterator() { 373 final Iterator<JsonValue> iterator = values.iterator(); 374 return new Iterator<JsonValue>() { 375 376 @Override 377 public boolean hasNext() { 378 return iterator.hasNext(); 379 } 380 381 @Override 382 public JsonValue next() { 383 return iterator.next(); 384 } 385 386 @Override 387 public void remove() { 388 throw new UnsupportedOperationException(); 389 } 390 }; 391 } 392 393 @Override 394 void write(JsonWriter writer) throws IOException { 395 writer.writeArrayOpen(); 396 Iterator<JsonValue> iterator = iterator(); 397 if (iterator.hasNext()) { 398 iterator.next().write(writer); 399 while (iterator.hasNext()) { 400 writer.writeArraySeparator(); 401 iterator.next().write(writer); 402 } 403 } 404 writer.writeArrayClose(); 405 } 406 407 @Override 408 public boolean isArray() { 409 return true; 410 } 411 412 @Override 413 public JsonArray asArray() { 414 return this; 415 } 416 417 @Override 418 public int hashCode() { 419 return values.hashCode(); 420 } 421 422 423 /** 424 * Indicates whether a given object is "equal to" this JsonArray. An object is considered equal 425 * if it is also a <code>JsonArray</code> and both arrays contain the same list of values. 426 * <p> 427 * If two JsonArrays are equal, they will also produce the same JSON output. 428 * </p> 429 * 430 * @param object 431 * the object to be compared with this JsonArray 432 * @return <tt>true</tt> if the specified object is equal to this JsonArray, <code>false</code> 433 * otherwise 434 */ 435 @Override 436 public boolean equals(Object object) { 437 if (this == object) { 438 return true; 439 } 440 if (object == null) { 441 return false; 442 } 443 if (getClass() != object.getClass()) { 444 return false; 445 } 446 JsonArray other = (JsonArray) object; 447 return values.equals(other.values); 448 } 449 450}