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.io.ObjectInputStream; 026import java.util.*; 027 028import com.restfb.json.JsonObject.Member; 029 030/** 031 * Represents a JSON object, a set of name/value pairs, where the names are strings and the values are JSON values. 032 * <p> 033 * Members can be added using the <code>add(String, ...)</code> methods which accept instances of {@link JsonValue}, 034 * strings, primitive numbers, and boolean values. To modify certain values of an object, use the 035 * <code>set(String, ...)</code> methods. Please note that the <code>add</code> methods are faster than <code>set</code> 036 * as they do not search for existing members. On the other hand, the <code>add</code> methods do not prevent adding 037 * multiple members with the same name. Duplicate names are discouraged but not prohibited by JSON. 038 * </p> 039 * <p> 040 * Members can be accessed by their name using {@link #get(String)}. A list of all names can be obtained from the method 041 * {@link #names()}. This class also supports iterating over the members in document order using an {@link #iterator()} 042 * or an enhanced for loop: 043 * </p> 044 * 045 * <pre> 046 * for (Member member : jsonObject) { 047 * String name = member.getName(); 048 * JsonValue value = member.getValue(); 049 * ... 050 * } 051 * </pre> 052 * <p> 053 * Even though JSON objects are unordered by definition, instances of this class preserve the order of members to allow 054 * processing in document order and to guarantee a predictable output. 055 * </p> 056 * <p> 057 * Note that this class is <strong>not thread-safe</strong>. If multiple threads access a <code>JsonObject</code> 058 * instance concurrently, while at least one of these threads modifies the contents of this object, access to the 059 * instance must be synchronized externally. Failure to do so may lead to an inconsistent state. 060 * </p> 061 * <p> 062 * This class is <strong>not supposed to be extended</strong> by clients. 063 * </p> 064 */ 065@SuppressWarnings("serial") // use default serial UID 066public class JsonObject extends JsonValue implements Iterable<Member> { 067 068 private final List<String> names; 069 private final List<JsonValue> values; 070 private transient HashIndexTable table; 071 072 /** 073 * Creates a new empty JsonObject. 074 */ 075 public JsonObject() { 076 names = new ArrayList<>(); 077 values = new ArrayList<>(); 078 table = new HashIndexTable(); 079 } 080 081 /** 082 * Creates a new JsonObject, initialized with the contents of the specified JSON object. 083 * 084 * @param object 085 * the JSON object to get the initial contents from, must not be <code>null</code> 086 */ 087 public JsonObject(JsonObject object) { 088 this(object, false); 089 } 090 091 private JsonObject(JsonObject object, boolean unmodifiable) { 092 Objects.requireNonNull(object, OBJECT_IS_NULL); 093 if (unmodifiable) { 094 names = Collections.unmodifiableList(object.names); 095 values = Collections.unmodifiableList(object.values); 096 } else { 097 names = new ArrayList<>(object.names); 098 values = new ArrayList<>(object.values); 099 } 100 table = new HashIndexTable(); 101 updateHashIndex(); 102 } 103 104 /** 105 * Returns an unmodifiable JsonObject for the specified one. This method allows to provide read-only access to a 106 * JsonObject. 107 * <p> 108 * The returned JsonObject is backed by the given object and reflect changes that happen to it. Attempts to modify the 109 * returned JsonObject result in an <code>UnsupportedOperationException</code>. 110 * </p> 111 * 112 * @param object 113 * the JsonObject for which an unmodifiable JsonObject is to be returned 114 * @return an unmodifiable view of the specified JsonObject 115 */ 116 public static JsonObject unmodifiableObject(JsonObject object) { 117 return new JsonObject(object, true); 118 } 119 120 /** 121 * Appends a new member to the end of this object, with the specified name and the JSON representation of the 122 * specified <code>int</code> value. 123 * <p> 124 * This method <strong>does not prevent duplicate names</strong>. Calling this method with a name that already exists 125 * in the object will append another member with the same name. In order to replace existing members, use the method 126 * <code>set(name, value)</code> instead. However, <strong> <em>add</em> is much faster than <em>set</em></strong> 127 * (because it does not need to search for existing members). Therefore <em>add</em> should be preferred when 128 * constructing new objects. 129 * </p> 130 * 131 * @param name 132 * the name of the member to add 133 * @param value 134 * the value of the member to add 135 * @return the object itself, to enable method chaining 136 */ 137 public JsonObject add(String name, int value) { 138 add(name, Json.value(value)); 139 return this; 140 } 141 142 /** 143 * Appends a new member to the end of this object, with the specified name and the JSON representation of the 144 * specified <code>long</code> value. 145 * <p> 146 * This method <strong>does not prevent duplicate names</strong>. Calling this method with a name that already exists 147 * in the object will append another member with the same name. In order to replace existing members, use the method 148 * <code>set(name, value)</code> instead. However, <strong> <em>add</em> is much faster than <em>set</em></strong> 149 * (because it does not need to search for existing members). Therefore <em>add</em> should be preferred when 150 * constructing new objects. 151 * </p> 152 * 153 * @param name 154 * the name of the member to add 155 * @param value 156 * the value of the member to add 157 * @return the object itself, to enable method chaining 158 */ 159 public JsonObject add(String name, long value) { 160 add(name, Json.value(value)); 161 return this; 162 } 163 164 /** 165 * Appends a new member to the end of this object, with the specified name and the JSON representation of the 166 * specified <code>float</code> value. 167 * <p> 168 * This method <strong>does not prevent duplicate names</strong>. Calling this method with a name that already exists 169 * in the object will append another member with the same name. In order to replace existing members, use the method 170 * <code>set(name, value)</code> instead. However, <strong> <em>add</em> is much faster than <em>set</em></strong> 171 * (because it does not need to search for existing members). Therefore <em>add</em> should be preferred when 172 * constructing new objects. 173 * </p> 174 * 175 * @param name 176 * the name of the member to add 177 * @param value 178 * the value of the member to add 179 * @return the object itself, to enable method chaining 180 */ 181 public JsonObject add(String name, float value) { 182 add(name, Json.value(value)); 183 return this; 184 } 185 186 /** 187 * Appends a new member to the end of this object, with the specified name and the JSON representation of the 188 * specified <code>double</code> value. 189 * <p> 190 * This method <strong>does not prevent duplicate names</strong>. Calling this method with a name that already exists 191 * in the object will append another member with the same name. In order to replace existing members, use the method 192 * <code>set(name, value)</code> instead. However, <strong> <em>add</em> is much faster than <em>set</em></strong> 193 * (because it does not need to search for existing members). Therefore <em>add</em> should be preferred when 194 * constructing new objects. 195 * </p> 196 * 197 * @param name 198 * the name of the member to add 199 * @param value 200 * the value of the member to add 201 * @return the object itself, to enable method chaining 202 */ 203 public JsonObject add(String name, double value) { 204 add(name, Json.value(value)); 205 return this; 206 } 207 208 /** 209 * Appends a new member to the end of this object, with the specified name and the JSON representation of the 210 * specified <code>boolean</code> value. 211 * <p> 212 * This method <strong>does not prevent duplicate names</strong>. Calling this method with a name that already exists 213 * in the object will append another member with the same name. In order to replace existing members, use the method 214 * <code>set(name, value)</code> instead. However, <strong> <em>add</em> is much faster than <em>set</em></strong> 215 * (because it does not need to search for existing members). Therefore <em>add</em> should be preferred when 216 * constructing new objects. 217 * </p> 218 * 219 * @param name 220 * the name of the member to add 221 * @param value 222 * the value of the member to add 223 * @return the object itself, to enable method chaining 224 */ 225 public JsonObject add(String name, boolean value) { 226 add(name, Json.value(value)); 227 return this; 228 } 229 230 /** 231 * Appends a new member to the end of this object, with the specified name and the JSON representation of the 232 * specified string. 233 * <p> 234 * This method <strong>does not prevent duplicate names</strong>. Calling this method with a name that already exists 235 * in the object will append another member with the same name. In order to replace existing members, use the method 236 * <code>set(name, value)</code> instead. However, <strong> <em>add</em> is much faster than <em>set</em></strong> 237 * (because it does not need to search for existing members). Therefore <em>add</em> should be preferred when 238 * constructing new objects. 239 * </p> 240 * 241 * @param name 242 * the name of the member to add 243 * @param value 244 * the value of the member to add 245 * @return the object itself, to enable method chaining 246 */ 247 public JsonObject add(String name, String value) { 248 add(name, Json.value(value)); 249 return this; 250 } 251 252 /** 253 * Appends a new member to the end of this object, with the specified name and the specified JSON value. 254 * <p> 255 * This method <strong>does not prevent duplicate names</strong>. Calling this method with a name that already exists 256 * in the object will append another member with the same name. In order to replace existing members, use the method 257 * <code>set(name, value)</code> instead. However, <strong> <em>add</em> is much faster than <em>set</em></strong> 258 * (because it does not need to search for existing members). Therefore <em>add</em> should be preferred when 259 * constructing new objects. 260 * </p> 261 * 262 * @param name 263 * the name of the member to add 264 * @param value 265 * the value of the member to add, must not be <code>null</code> 266 * @return the object itself, to enable method chaining 267 */ 268 public JsonObject add(String name, JsonValue value) { 269 Objects.requireNonNull(name, NAME_IS_NULL); 270 Objects.requireNonNull(value, VALUE_IS_NULL); 271 table.add(name, names.size()); 272 names.add(name); 273 values.add(value); 274 return this; 275 } 276 277 /** 278 * Sets the value of the member with the specified name to the JSON representation of the specified <code>int</code> 279 * value. If this object does not contain a member with this name, a new member is added at the end of the object. If 280 * this object contains multiple members with this name, only the last one is changed. 281 * <p> 282 * This method should <strong>only be used to modify existing objects</strong>. To fill a new object with members, the 283 * method <code>add(name, value)</code> should be preferred which is much faster (as it does not need to search for 284 * existing members). 285 * </p> 286 * 287 * @param name 288 * the name of the member to replace 289 * @param value 290 * the value to set to the member 291 * @return the object itself, to enable method chaining 292 */ 293 public JsonObject set(String name, int value) { 294 set(name, Json.value(value)); 295 return this; 296 } 297 298 /** 299 * Sets the value of the member with the specified name to the JSON representation of the specified <code>long</code> 300 * value. If this object does not contain a member with this name, a new member is added at the end of the object. If 301 * this object contains multiple members with this name, only the last one is changed. 302 * <p> 303 * This method should <strong>only be used to modify existing objects</strong>. To fill a new object with members, the 304 * method <code>add(name, value)</code> should be preferred which is much faster (as it does not need to search for 305 * existing members). 306 * </p> 307 * 308 * @param name 309 * the name of the member to replace 310 * @param value 311 * the value to set to the member 312 * @return the object itself, to enable method chaining 313 */ 314 public JsonObject set(String name, long value) { 315 set(name, Json.value(value)); 316 return this; 317 } 318 319 /** 320 * Sets the value of the member with the specified name to the JSON representation of the specified <code>float</code> 321 * value. If this object does not contain a member with this name, a new member is added at the end of the object. If 322 * this object contains multiple members with this name, only the last one is changed. 323 * <p> 324 * This method should <strong>only be used to modify existing objects</strong>. To fill a new object with members, the 325 * method <code>add(name, value)</code> should be preferred which is much faster (as it does not need to search for 326 * existing members). 327 * </p> 328 * 329 * @param name 330 * the name of the member to add 331 * @param value 332 * the value of the member to add 333 * @return the object itself, to enable method chaining 334 */ 335 public JsonObject set(String name, float value) { 336 set(name, Json.value(value)); 337 return this; 338 } 339 340 /** 341 * Sets the value of the member with the specified name to the JSON representation of the specified 342 * <code>double</code> value. If this object does not contain a member with this name, a new member is added at the 343 * end of the object. If this object contains multiple members with this name, only the last one is changed. 344 * <p> 345 * This method should <strong>only be used to modify existing objects</strong>. To fill a new object with members, the 346 * method <code>add(name, value)</code> should be preferred which is much faster (as it does not need to search for 347 * existing members). 348 * </p> 349 * 350 * @param name 351 * the name of the member to add 352 * @param value 353 * the value of the member to add 354 * @return the object itself, to enable method chaining 355 */ 356 public JsonObject set(String name, double value) { 357 set(name, Json.value(value)); 358 return this; 359 } 360 361 /** 362 * Sets the value of the member with the specified name to the JSON representation of the specified 363 * <code>boolean</code> value. If this object does not contain a member with this name, a new member is added at the 364 * end of the object. If this object contains multiple members with this name, only the last one is changed. 365 * <p> 366 * This method should <strong>only be used to modify existing objects</strong>. To fill a new object with members, the 367 * method <code>add(name, value)</code> should be preferred which is much faster (as it does not need to search for 368 * existing members). 369 * </p> 370 * 371 * @param name 372 * the name of the member to add 373 * @param value 374 * the value of the member to add 375 * @return the object itself, to enable method chaining 376 */ 377 public JsonObject set(String name, boolean value) { 378 set(name, Json.value(value)); 379 return this; 380 } 381 382 /** 383 * Sets the value of the member with the specified name to the JSON representation of the specified string. If this 384 * object does not contain a member with this name, a new member is added at the end of the object. If this object 385 * contains multiple members with this name, only the last one is changed. 386 * <p> 387 * This method should <strong>only be used to modify existing objects</strong>. To fill a new object with members, the 388 * method <code>add(name, value)</code> should be preferred which is much faster (as it does not need to search for 389 * existing members). 390 * </p> 391 * 392 * @param name 393 * the name of the member to add 394 * @param value 395 * the value of the member to add 396 * @return the object itself, to enable method chaining 397 */ 398 public JsonObject set(String name, String value) { 399 set(name, Json.value(value)); 400 return this; 401 } 402 403 /** 404 * Sets the value of the member with the specified name to the specified JSON value. If this object does not contain a 405 * member with this name, a new member is added at the end of the object. If this object contains multiple members 406 * with this name, only the last one is changed. 407 * <p> 408 * This method should <strong>only be used to modify existing objects</strong>. To fill a new object with members, the 409 * method <code>add(name, value)</code> should be preferred which is much faster (as it does not need to search for 410 * existing members). 411 * </p> 412 * 413 * @param name 414 * the name of the member to add 415 * @param value 416 * the value of the member to add, must not be <code>null</code> 417 * @return the object itself, to enable method chaining 418 */ 419 public JsonObject set(String name, JsonValue value) { 420 Objects.requireNonNull(name, NAME_IS_NULL); 421 Objects.requireNonNull(value, VALUE_IS_NULL); 422 int index = indexOf(name); 423 if (index != -1) { 424 values.set(index, value); 425 } else { 426 table.add(name, names.size()); 427 names.add(name); 428 values.add(value); 429 } 430 return this; 431 } 432 433 /** 434 * Removes a member with the specified name from this object. If this object contains multiple members with the given 435 * name, only the last one is removed. If this object does not contain a member with the specified name, the object is 436 * not modified. 437 * 438 * @param name 439 * the name of the member to remove 440 * @return the object itself, to enable method chaining 441 */ 442 public JsonObject remove(String name) { 443 Objects.requireNonNull(name, NAME_IS_NULL); 444 int index = indexOf(name); 445 if (index != -1) { 446 table.remove(index); 447 names.remove(index); 448 values.remove(index); 449 } 450 return this; 451 } 452 453 /** 454 * Checks if a specified member is present as a child of this object. This will not test if 455 * this object contains the literal <code>null</code>, {@link JsonValue#isNull()} should be used 456 * for this purpose. 457 * 458 * @param name 459 * the name of the member to check for 460 * @return whether or not the member is present 461 */ 462 public boolean contains(String name) { 463 return names.contains(name); 464 } 465 466 /** 467 * Copies all members of the specified object into this object. When the specified object contains 468 * members with names that also exist in this object, the existing values in this object will be 469 * replaced by the corresponding values in the specified object. 470 * 471 * @param object 472 * the object to merge 473 * @return the object itself, to enable method chaining 474 */ 475 public JsonObject merge(JsonObject object) { 476 Objects.requireNonNull(object, OBJECT_IS_NULL); 477 for (Member member : object) { 478 this.set(member.name, member.value); 479 } 480 return this; 481 } 482 483 /** 484 * Returns the value of the member with the specified name in this object. If this object contains multiple members 485 * with the given name, this method will return the last one. 486 * 487 * @param name 488 * the name of the member whose value is to be returned 489 * @return the value of the last member with the specified name, or <code>null</code> if this object does not contain 490 * a member with that name 491 */ 492 public JsonValue get(String name) { 493 Objects.requireNonNull(name, NAME_IS_NULL); 494 int index = indexOf(name); 495 return index != -1 ? values.get(index) : null; 496 } 497 498 /** 499 * Returns the <code>int</code> value of the member with the specified name in this object. If this object does not 500 * contain a member with this name, the given default value is returned. If this object contains multiple members with 501 * the given name, the last one will be picked. If this member's value does not represent a JSON number or if it 502 * cannot be interpreted as Java <code>int</code>, an exception is thrown. 503 * 504 * @param name 505 * the name of the member whose value is to be returned 506 * @param defaultValue 507 * the value to be returned if the requested member is missing 508 * @return the value of the last member with the specified name, or the given default value if this object does not 509 * contain a member with that name 510 */ 511 public int getInt(String name, int defaultValue) { 512 JsonValue value = get(name); 513 return value != null ? value.asInt() : defaultValue; 514 } 515 516 /** 517 * Returns the <code>long</code> value of the member with the specified name in this object. If this object does not 518 * contain a member with this name, the given default value is returned. If this object contains multiple members with 519 * the given name, the last one will be picked. If this member's value does not represent a JSON number or if it 520 * cannot be interpreted as Java <code>long</code>, an exception is thrown. 521 * 522 * @param name 523 * the name of the member whose value is to be returned 524 * @param defaultValue 525 * the value to be returned if the requested member is missing 526 * @return the value of the last member with the specified name, or the given default value if this object does not 527 * contain a member with that name 528 */ 529 public long getLong(String name, long defaultValue) { 530 JsonValue value = get(name); 531 return value != null ? value.asLong() : defaultValue; 532 } 533 534 /** 535 * Returns the <code>float</code> value of the member with the specified name in this object. If this object does not 536 * contain a member with this name, the given default value is returned. If this object contains multiple members with 537 * the given name, the last one will be picked. If this member's value does not represent a JSON number or if it 538 * cannot be interpreted as Java <code>float</code>, an exception is thrown. 539 * 540 * @param name 541 * the name of the member whose value is to be returned 542 * @param defaultValue 543 * the value to be returned if the requested member is missing 544 * @return the value of the last member with the specified name, or the given default value if this object does not 545 * contain a member with that name 546 */ 547 public float getFloat(String name, float defaultValue) { 548 JsonValue value = get(name); 549 return value != null ? value.asFloat() : defaultValue; 550 } 551 552 /** 553 * Returns the <code>double</code> value of the member with the specified name in this object. If this object does not 554 * contain a member with this name, the given default value is returned. If this object contains multiple members with 555 * the given name, the last one will be picked. If this member's value does not represent a JSON number or if it 556 * cannot be interpreted as Java <code>double</code>, an exception is thrown. 557 * 558 * @param name 559 * the name of the member whose value is to be returned 560 * @param defaultValue 561 * the value to be returned if the requested member is missing 562 * @return the value of the last member with the specified name, or the given default value if this object does not 563 * contain a member with that name 564 */ 565 public double getDouble(String name, double defaultValue) { 566 JsonValue value = get(name); 567 return value != null ? value.asDouble() : defaultValue; 568 } 569 570 /** 571 * Returns the <code>boolean</code> value of the member with the specified name in this object. If this object does 572 * not contain a member with this name, the given default value is returned. If this object contains multiple members 573 * with the given name, the last one will be picked. If this member's value does not represent a JSON 574 * <code>true</code> or <code>false</code> value, an exception is thrown. 575 * 576 * @param name 577 * the name of the member whose value is to be returned 578 * @param defaultValue 579 * the value to be returned if the requested member is missing 580 * @return the value of the last member with the specified name, or the given default value if this object does not 581 * contain a member with that name 582 */ 583 public boolean getBoolean(String name, boolean defaultValue) { 584 JsonValue value = get(name); 585 return value != null ? value.asBoolean() : defaultValue; 586 } 587 588 /** 589 * Returns the <code>String</code> value of the member with the specified name in this object. If this object does not 590 * contain a member with this name, the given default value is returned. If this object contains multiple members with 591 * the given name, the last one is picked. If this member's value does not represent a JSON string, an exception is 592 * thrown. 593 * 594 * @param name 595 * the name of the member whose value is to be returned 596 * @param defaultValue 597 * the value to be returned if the requested member is missing 598 * @return the value of the last member with the specified name, or the given default value if this object does not 599 * contain a member with that name 600 */ 601 public String getString(String name, String defaultValue) { 602 JsonValue value = get(name); 603 return value != null ? value.asString() : defaultValue; 604 } 605 606 /** 607 * Returns the number of members (name/value pairs) in this object. 608 * 609 * @return the number of members in this object 610 */ 611 public int size() { 612 return names.size(); 613 } 614 615 /** 616 * Returns <code>true</code> if this object contains no members. 617 * 618 * @return <code>true</code> if this object contains no members 619 */ 620 public boolean isEmpty() { 621 return names.isEmpty(); 622 } 623 624 /** 625 * Returns a list of the names in this object in document order. The returned list is backed by this object and will 626 * reflect subsequent changes. It cannot be used to modify this object. Attempts to modify the returned list will 627 * result in an exception. 628 * 629 * @return a list of the names in this object 630 */ 631 public List<String> names() { 632 return Collections.unmodifiableList(names); 633 } 634 635 /** 636 * Returns an iterator over the members of this object in document order. The returned iterator cannot be used to 637 * modify this object. 638 * 639 * @return an iterator over the members of this object 640 */ 641 public Iterator<Member> iterator() { 642 final Iterator<String> namesIterator = names.iterator(); 643 final Iterator<JsonValue> valuesIterator = values.iterator(); 644 return new Iterator<JsonObject.Member>() { 645 646 @Override 647 public boolean hasNext() { 648 return namesIterator.hasNext(); 649 } 650 651 @Override 652 public Member next() { 653 String name = namesIterator.next(); 654 JsonValue value = valuesIterator.next(); 655 return new Member(name, value); 656 } 657 658 @Override 659 public void remove() { 660 throw new UnsupportedOperationException(); 661 } 662 663 }; 664 } 665 666 @Override 667 void write(JsonWriter writer) throws IOException { 668 writer.writeObjectOpen(); 669 Iterator<String> namesIterator = names.iterator(); 670 Iterator<JsonValue> valuesIterator = values.iterator(); 671 if (namesIterator.hasNext()) { 672 writer.writeMemberName(namesIterator.next()); 673 writer.writeMemberSeparator(); 674 valuesIterator.next().write(writer); 675 while (namesIterator.hasNext()) { 676 writer.writeObjectSeparator(); 677 writer.writeMemberName(namesIterator.next()); 678 writer.writeMemberSeparator(); 679 valuesIterator.next().write(writer); 680 } 681 } 682 writer.writeObjectClose(); 683 } 684 685 @Override 686 public boolean isObject() { 687 return true; 688 } 689 690 @Override 691 public JsonObject asObject() { 692 return this; 693 } 694 695 @Override 696 public int hashCode() { 697 int result = 1; 698 result = 31 * result + names.hashCode(); 699 result = 31 * result + values.hashCode(); 700 return result; 701 } 702 703 @Override 704 public boolean equals(Object obj) { 705 if (this == obj) { 706 return true; 707 } 708 if (obj == null) { 709 return false; 710 } 711 if (getClass() != obj.getClass()) { 712 return false; 713 } 714 JsonObject other = (JsonObject) obj; 715 return names.equals(other.names) && values.equals(other.values); 716 } 717 718 int indexOf(String name) { 719 int index = table.get(name); 720 if (index != -1 && name.equals(names.get(index))) { 721 return index; 722 } 723 return names.lastIndexOf(name); 724 } 725 726 private synchronized void readObject(ObjectInputStream inputStream) throws IOException, ClassNotFoundException { 727 inputStream.defaultReadObject(); 728 table = new HashIndexTable(); 729 updateHashIndex(); 730 } 731 732 private void updateHashIndex() { 733 int size = names.size(); 734 for (int i = 0; i < size; i++) { 735 table.add(names.get(i), i); 736 } 737 } 738 739 /** 740 * Represents a member of a JSON object, a pair of a name and a value. 741 */ 742 public static class Member { 743 744 private final String name; 745 private final JsonValue value; 746 747 Member(String name, JsonValue value) { 748 this.name = name; 749 this.value = value; 750 } 751 752 /** 753 * Returns the name of this member. 754 * 755 * @return the name of this member, never <code>null</code> 756 */ 757 public String getName() { 758 return name; 759 } 760 761 /** 762 * Returns the value of this member. 763 * 764 * @return the value of this member, never <code>null</code> 765 */ 766 public JsonValue getValue() { 767 return value; 768 } 769 770 @Override 771 public int hashCode() { 772 int result = 1; 773 result = 31 * result + name.hashCode(); 774 result = 31 * result + value.hashCode(); 775 return result; 776 } 777 778 /** 779 * Indicates whether a given object is "equal to" this JsonObject. An object is considered equal 780 * if it is also a <code>JsonObject</code> and both objects contain the same members <em>in 781 * the same order</em>. 782 * <p> 783 * If two JsonObjects are equal, they will also produce the same JSON output. 784 * </p> 785 * 786 * @param object 787 * the object to be compared with this JsonObject 788 * @return <tt>true</tt> if the specified object is equal to this JsonObject, <code>false</code> 789 * otherwise 790 */ 791 @Override 792 public boolean equals(Object object) { 793 if (this == object) { 794 return true; 795 } 796 if (object == null) { 797 return false; 798 } 799 if (getClass() != object.getClass()) { 800 return false; 801 } 802 Member other = (Member)object; 803 return name.equals(other.name) && value.equals(other.value); 804 } 805 806 } 807 808 static class HashIndexTable { 809 810 private final byte[] hashTable = new byte[32]; // must be a power of two 811 812 public HashIndexTable() { 813 // nothing to do here 814 } 815 816 public HashIndexTable(HashIndexTable original) { 817 System.arraycopy(original.hashTable, 0, hashTable, 0, hashTable.length); 818 } 819 820 void add(String name, int index) { 821 int slot = hashSlotFor(name); 822 if (index < 0xff) { 823 // increment by 1, 0 stands for empty 824 hashTable[slot] = (byte) (index + 1); 825 } else { 826 hashTable[slot] = 0; 827 } 828 } 829 830 void remove(int index) { 831 for (int i = 0; i < hashTable.length; i++) { 832 if (hashTable[i] == index + 1) { 833 hashTable[i] = 0; 834 } else if (hashTable[i] > index + 1) { 835 hashTable[i]--; 836 } 837 } 838 } 839 840 int get(Object name) { 841 int slot = hashSlotFor(name); 842 // subtract 1, 0 stands for empty 843 return (hashTable[slot] & 0xff) - 1; 844 } 845 846 private int hashSlotFor(Object element) { 847 return element.hashCode() & hashTable.length - 1; 848 } 849 850 } 851 852}