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 this object contains the 455 * literal <code>null</code>, {@link JsonValue#isNull()} should be used for this purpose. 456 * 457 * @param name 458 * the name of the member to check for 459 * @return whether or not the member is present 460 */ 461 public boolean contains(String name) { 462 return names.contains(name); 463 } 464 465 /** 466 * Copies all members of the specified object into this object. When the specified object contains members with names 467 * that also exist in this object, the existing values in this object will be replaced by the corresponding values in 468 * the specified object. 469 * 470 * @param object 471 * the object to merge 472 * @return the object itself, to enable method chaining 473 */ 474 public JsonObject merge(JsonObject object) { 475 Objects.requireNonNull(object, OBJECT_IS_NULL); 476 for (Member member : object) { 477 this.set(member.name, member.value); 478 } 479 return this; 480 } 481 482 /** 483 * Returns the value of the member with the specified name in this object. If this object contains multiple members 484 * with the given name, this method will return the last one. 485 * 486 * @param name 487 * the name of the member whose value is to be returned 488 * @return the value of the last member with the specified name, or <code>null</code> if this object does not contain 489 * a member with that name 490 */ 491 public JsonValue get(String name) { 492 Objects.requireNonNull(name, NAME_IS_NULL); 493 int index = indexOf(name); 494 return index != -1 ? values.get(index) : null; 495 } 496 497 /** 498 * Returns the <code>int</code> value of the member with the specified name in this object. If this object does not 499 * contain a member with this name, the given default value is returned. If this object contains multiple members with 500 * the given name, the last one will be picked. If this member's value does not represent a JSON number or if it 501 * cannot be interpreted as Java <code>int</code>, an exception is thrown. 502 * 503 * @param name 504 * the name of the member whose value is to be returned 505 * @param defaultValue 506 * the value to be returned if the requested member is missing 507 * @return the value of the last member with the specified name, or the given default value if this object does not 508 * contain a member with that name 509 */ 510 public int getInt(String name, int defaultValue) { 511 JsonValue value = get(name); 512 return value != null ? value.asInt() : defaultValue; 513 } 514 515 /** 516 * Returns the <code>long</code> value of the member with the specified name in this object. If this object does not 517 * contain a member with this name, the given default value is returned. If this object contains multiple members with 518 * the given name, the last one will be picked. If this member's value does not represent a JSON number or if it 519 * cannot be interpreted as Java <code>long</code>, an exception is thrown. 520 * 521 * @param name 522 * the name of the member whose value is to be returned 523 * @param defaultValue 524 * the value to be returned if the requested member is missing 525 * @return the value of the last member with the specified name, or the given default value if this object does not 526 * contain a member with that name 527 */ 528 public long getLong(String name, long defaultValue) { 529 JsonValue value = get(name); 530 return value != null ? value.asLong() : defaultValue; 531 } 532 533 /** 534 * Returns the <code>float</code> value of the member with the specified name in this object. If this object does not 535 * contain a member with this name, the given default value is returned. If this object contains multiple members with 536 * the given name, the last one will be picked. If this member's value does not represent a JSON number or if it 537 * cannot be interpreted as Java <code>float</code>, an exception is thrown. 538 * 539 * @param name 540 * the name of the member whose value is to be returned 541 * @param defaultValue 542 * the value to be returned if the requested member is missing 543 * @return the value of the last member with the specified name, or the given default value if this object does not 544 * contain a member with that name 545 */ 546 public float getFloat(String name, float defaultValue) { 547 JsonValue value = get(name); 548 return value != null ? value.asFloat() : defaultValue; 549 } 550 551 /** 552 * Returns the <code>double</code> value of the member with the specified name in this object. If this object does not 553 * contain a member with this name, the given default value is returned. If this object contains multiple members with 554 * the given name, the last one will be picked. If this member's value does not represent a JSON number or if it 555 * cannot be interpreted as Java <code>double</code>, an exception is thrown. 556 * 557 * @param name 558 * the name of the member whose value is to be returned 559 * @param defaultValue 560 * the value to be returned if the requested member is missing 561 * @return the value of the last member with the specified name, or the given default value if this object does not 562 * contain a member with that name 563 */ 564 public double getDouble(String name, double defaultValue) { 565 JsonValue value = get(name); 566 return value != null ? value.asDouble() : defaultValue; 567 } 568 569 /** 570 * Returns the <code>boolean</code> value of the member with the specified name in this object. If this object does 571 * not contain a member with this name, the given default value is returned. If this object contains multiple members 572 * with the given name, the last one will be picked. If this member's value does not represent a JSON 573 * <code>true</code> or <code>false</code> value, an exception is thrown. 574 * 575 * @param name 576 * the name of the member whose value is to be returned 577 * @param defaultValue 578 * the value to be returned if the requested member is missing 579 * @return the value of the last member with the specified name, or the given default value if this object does not 580 * contain a member with that name 581 */ 582 public boolean getBoolean(String name, boolean defaultValue) { 583 JsonValue value = get(name); 584 return value != null ? value.asBoolean() : defaultValue; 585 } 586 587 /** 588 * Returns the <code>String</code> value of the member with the specified name in this object. If this object does not 589 * contain a member with this name, the given default value is returned. If this object contains multiple members with 590 * the given name, the last one is picked. If this member's value does not represent a JSON string, an exception is 591 * thrown. 592 * 593 * @param name 594 * the name of the member whose value is to be returned 595 * @param defaultValue 596 * the value to be returned if the requested member is missing 597 * @return the value of the last member with the specified name, or the given default value if this object does not 598 * contain a member with that name 599 */ 600 public String getString(String name, String defaultValue) { 601 JsonValue value = get(name); 602 return value != null ? value.asString() : defaultValue; 603 } 604 605 /** 606 * Returns the number of members (name/value pairs) in this object. 607 * 608 * @return the number of members in this object 609 */ 610 public int size() { 611 return names.size(); 612 } 613 614 /** 615 * Returns <code>true</code> if this object contains no members. 616 * 617 * @return <code>true</code> if this object contains no members 618 */ 619 public boolean isEmpty() { 620 return names.isEmpty(); 621 } 622 623 /** 624 * Returns a list of the names in this object in document order. The returned list is backed by this object and will 625 * reflect subsequent changes. It cannot be used to modify this object. Attempts to modify the returned list will 626 * result in an exception. 627 * 628 * @return a list of the names in this object 629 */ 630 public List<String> names() { 631 return Collections.unmodifiableList(names); 632 } 633 634 /** 635 * Returns an iterator over the members of this object in document order. The returned iterator cannot be used to 636 * modify this object. 637 * 638 * @return an iterator over the members of this object 639 */ 640 public Iterator<Member> iterator() { 641 final Iterator<String> namesIterator = names.iterator(); 642 final Iterator<JsonValue> valuesIterator = values.iterator(); 643 return new Iterator<JsonObject.Member>() { 644 645 @Override 646 public boolean hasNext() { 647 return namesIterator.hasNext(); 648 } 649 650 @Override 651 public Member next() { 652 String name = namesIterator.next(); 653 JsonValue value = valuesIterator.next(); 654 return new Member(name, value); 655 } 656 657 @Override 658 public void remove() { 659 throw new UnsupportedOperationException(); 660 } 661 662 }; 663 } 664 665 @Override 666 void write(JsonWriter writer) throws IOException { 667 writer.writeObjectOpen(); 668 Iterator<String> namesIterator = names.iterator(); 669 Iterator<JsonValue> valuesIterator = values.iterator(); 670 if (namesIterator.hasNext()) { 671 writer.writeMemberName(namesIterator.next()); 672 writer.writeMemberSeparator(); 673 valuesIterator.next().write(writer); 674 while (namesIterator.hasNext()) { 675 writer.writeObjectSeparator(); 676 writer.writeMemberName(namesIterator.next()); 677 writer.writeMemberSeparator(); 678 valuesIterator.next().write(writer); 679 } 680 } 681 writer.writeObjectClose(); 682 } 683 684 @Override 685 public boolean isObject() { 686 return true; 687 } 688 689 @Override 690 public JsonObject asObject() { 691 return this; 692 } 693 694 @Override 695 public int hashCode() { 696 int result = 1; 697 result = 31 * result + names.hashCode(); 698 result = 31 * result + values.hashCode(); 699 return result; 700 } 701 702 @Override 703 public boolean equals(Object obj) { 704 if (this == obj) { 705 return true; 706 } 707 if (obj == null) { 708 return false; 709 } 710 if (getClass() != obj.getClass()) { 711 return false; 712 } 713 JsonObject other = (JsonObject) obj; 714 return names.equals(other.names) && values.equals(other.values); 715 } 716 717 int indexOf(String name) { 718 int index = table.get(name); 719 if (index != -1 && name.equals(names.get(index))) { 720 return index; 721 } 722 return names.lastIndexOf(name); 723 } 724 725 private synchronized void readObject(ObjectInputStream inputStream) throws IOException, ClassNotFoundException { 726 inputStream.defaultReadObject(); 727 table = new HashIndexTable(); 728 updateHashIndex(); 729 } 730 731 private void updateHashIndex() { 732 int size = names.size(); 733 for (int i = 0; i < size; i++) { 734 table.add(names.get(i), i); 735 } 736 } 737 738 /** 739 * Represents a member of a JSON object, a pair of a name and a value. 740 */ 741 public static class Member { 742 743 private final String name; 744 private final JsonValue value; 745 746 Member(String name, JsonValue value) { 747 this.name = name; 748 this.value = value; 749 } 750 751 /** 752 * Returns the name of this member. 753 * 754 * @return the name of this member, never <code>null</code> 755 */ 756 public String getName() { 757 return name; 758 } 759 760 /** 761 * Returns the value of this member. 762 * 763 * @return the value of this member, never <code>null</code> 764 */ 765 public JsonValue getValue() { 766 return value; 767 } 768 769 @Override 770 public int hashCode() { 771 int result = 1; 772 result = 31 * result + name.hashCode(); 773 result = 31 * result + value.hashCode(); 774 return result; 775 } 776 777 /** 778 * Indicates whether a given object is "equal to" this JsonObject. An object is considered equal if it is also a 779 * <code>JsonObject</code> and both objects contain the same members <em>in the same order</em>. 780 * <p> 781 * If two JsonObjects are equal, they will also produce the same JSON output. 782 * </p> 783 * 784 * @param object 785 * the object to be compared with this JsonObject 786 * @return <tt>true</tt> if the specified object is equal to this JsonObject, <code>false</code> otherwise 787 */ 788 @Override 789 public boolean equals(Object object) { 790 if (this == object) { 791 return true; 792 } 793 if (object == null) { 794 return false; 795 } 796 if (getClass() != object.getClass()) { 797 return false; 798 } 799 Member other = (Member) object; 800 return name.equals(other.name) && value.equals(other.value); 801 } 802 803 } 804 805 static class HashIndexTable { 806 807 private final byte[] hashTable = new byte[32]; // must be a power of two 808 809 public HashIndexTable() { 810 // nothing to do here 811 } 812 813 public HashIndexTable(HashIndexTable original) { 814 System.arraycopy(original.hashTable, 0, hashTable, 0, hashTable.length); 815 } 816 817 void add(String name, int index) { 818 int slot = hashSlotFor(name); 819 if (index < 0xff) { 820 // increment by 1, 0 stands for empty 821 hashTable[slot] = (byte) (index + 1); 822 } else { 823 hashTable[slot] = 0; 824 } 825 } 826 827 void remove(int index) { 828 for (int i = 0; i < hashTable.length; i++) { 829 if (hashTable[i] == index + 1) { 830 hashTable[i] = 0; 831 } else if (hashTable[i] > index + 1) { 832 hashTable[i]--; 833 } 834 } 835 } 836 837 int get(Object name) { 838 int slot = hashSlotFor(name); 839 // subtract 1, 0 stands for empty 840 return (hashTable[slot] & 0xff) - 1; 841 } 842 843 private int hashSlotFor(Object element) { 844 return element.hashCode() & hashTable.length - 1; 845 } 846 847 } 848 849}