All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.eclipsesource.json.JsonObject Maven / Gradle / Ivy

There is a newer version: 0.9.5
Show newest version
/*******************************************************************************
 * Copyright (c) 2008, 2013 EclipseSource.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *    Ralf Sternberg - initial implementation and API
 ******************************************************************************/
package com.eclipsesource.json;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.Reader;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;

import com.eclipsesource.json.JsonObject.Member;


/**
 * Represents a JSON object, i.e. an unordered set of name/value pairs, where the names are strings
 * and the values are JSON values.
 * 

* Members can be added using the add(String, ...) methods which accept instances of * {@link JsonValue}, strings, primitive numbers, and boolean values. To modify certain values of an * object, use the set(String, ...) methods. Please note that the add * methods are faster than set as they do not search for existing members. On the other * hand, the add methods do not prevent adding multiple members with the same name. * Duplicate names are discouraged but not prohibited by JSON. *

*

* Members can be accessed by their name using {@link #get(String)}. A list of all names can be * obtained from the method {@link #names()}. This class also supports iterating over the members in * document order using an {@link #iterator()} or an enhanced for loop: *

* *
 * for( Member member : jsonObject ) {
 *   String name = member.getName();
 *   JsonValue value = member.getValue();
 *   ...
 * }
 * 
*

* Even though JSON objects are unordered by definition, instances of this class preserve the order * of members to allow processing in document order and to guarantee a predictable output. *

*

* Note that this class is not thread-safe. If multiple threads access a * JsonObject instance concurrently, while at least one of these threads modifies the * contents of this object, access to the instance must be synchronized externally. Failure to do so * may lead to an inconsistent state. *

*

* This class is not supposed to be extended by clients. *

*/ @SuppressWarnings( "serial" ) // use default serial UID public class JsonObject extends JsonValue implements Iterable { private final List names; private final List values; private transient HashIndexTable table; /** * Creates a new empty JsonObject. */ public JsonObject() { names = new ArrayList(); values = new ArrayList(); table = new HashIndexTable(); } /** * Creates a new JsonObject, initialized with the contents of the specified JSON object. * * @param object * the JSON object to get the initial contents from, must not be null */ public JsonObject( JsonObject object ) { this( object, false ); } private JsonObject( JsonObject object, boolean unmodifiable ) { if( object == null ) { throw new NullPointerException( "object is null" ); } if( unmodifiable ) { names = Collections.unmodifiableList( object.names ); values = Collections.unmodifiableList( object.values ); } else { names = new ArrayList( object.names ); values = new ArrayList( object.values ); } table = new HashIndexTable(); updateHashIndex(); } /** * Reads a JSON object from the given reader. *

* Characters are read in chunks and buffered internally, therefore wrapping an existing reader in * an additional BufferedReader does not improve reading * performance. *

* * @param reader * the reader to read the JSON object from * @return the JSON object that has been read * @throws IOException * if an I/O error occurs in the reader * @throws ParseException * if the input is not valid JSON * @throws UnsupportedOperationException * if the input does not contain a JSON object */ public static JsonObject readFrom( Reader reader ) throws IOException { return JsonValue.readFrom( reader ).asObject(); } /** * Reads a JSON object from the given string. * * @param string * the string that contains the JSON object * @return the JSON object that has been read * @throws ParseException * if the input is not valid JSON * @throws UnsupportedOperationException * if the input does not contain a JSON object */ public static JsonObject readFrom( String string ) { return JsonValue.readFrom( string ).asObject(); } /** * Returns an unmodifiable JsonObject for the specified one. This method allows to provide * read-only access to a JsonObject. *

* The returned JsonObject is backed by the given object and reflect changes that happen to it. * Attempts to modify the returned JsonObject result in an * UnsupportedOperationException. *

* * @param object * the JsonObject for which an unmodifiable JsonObject is to be returned * @return an unmodifiable view of the specified JsonObject */ public static JsonObject unmodifiableObject( JsonObject object ) { return new JsonObject( object, true ); } /** * Appends a new member to the end of this object, with the specified name and the JSON * representation of the specified int value. *

* This method does not prevent duplicate names. Calling this method with a name * that already exists in the object will append another member with the same name. In order to * replace existing members, use the method set(name, value) instead. However, * add is much faster than set (because it does not need to * search for existing members). Therefore add should be preferred when constructing new * objects. *

* * @param name * the name of the member to add * @param value * the value of the member to add * @return the object itself, to enable method chaining */ public JsonObject add( String name, int value ) { add( name, valueOf( value ) ); return this; } /** * Appends a new member to the end of this object, with the specified name and the JSON * representation of the specified long value. *

* This method does not prevent duplicate names. Calling this method with a name * that already exists in the object will append another member with the same name. In order to * replace existing members, use the method set(name, value) instead. However, * add is much faster than set (because it does not need to * search for existing members). Therefore add should be preferred when constructing new * objects. *

* * @param name * the name of the member to add * @param value * the value of the member to add * @return the object itself, to enable method chaining */ public JsonObject add( String name, long value ) { add( name, valueOf( value ) ); return this; } /** * Appends a new member to the end of this object, with the specified name and the JSON * representation of the specified float value. *

* This method does not prevent duplicate names. Calling this method with a name * that already exists in the object will append another member with the same name. In order to * replace existing members, use the method set(name, value) instead. However, * add is much faster than set (because it does not need to * search for existing members). Therefore add should be preferred when constructing new * objects. *

* * @param name * the name of the member to add * @param value * the value of the member to add * @return the object itself, to enable method chaining */ public JsonObject add( String name, float value ) { add( name, valueOf( value ) ); return this; } /** * Appends a new member to the end of this object, with the specified name and the JSON * representation of the specified double value. *

* This method does not prevent duplicate names. Calling this method with a name * that already exists in the object will append another member with the same name. In order to * replace existing members, use the method set(name, value) instead. However, * add is much faster than set (because it does not need to * search for existing members). Therefore add should be preferred when constructing new * objects. *

* * @param name * the name of the member to add * @param value * the value of the member to add * @return the object itself, to enable method chaining */ public JsonObject add( String name, double value ) { add( name, valueOf( value ) ); return this; } /** * Appends a new member to the end of this object, with the specified name and the JSON * representation of the specified boolean value. *

* This method does not prevent duplicate names. Calling this method with a name * that already exists in the object will append another member with the same name. In order to * replace existing members, use the method set(name, value) instead. However, * add is much faster than set (because it does not need to * search for existing members). Therefore add should be preferred when constructing new * objects. *

* * @param name * the name of the member to add * @param value * the value of the member to add * @return the object itself, to enable method chaining */ public JsonObject add( String name, boolean value ) { add( name, valueOf( value ) ); return this; } /** * Appends a new member to the end of this object, with the specified name and the JSON * representation of the specified string. *

* This method does not prevent duplicate names. Calling this method with a name * that already exists in the object will append another member with the same name. In order to * replace existing members, use the method set(name, value) instead. However, * add is much faster than set (because it does not need to * search for existing members). Therefore add should be preferred when constructing new * objects. *

* * @param name * the name of the member to add * @param value * the value of the member to add * @return the object itself, to enable method chaining */ public JsonObject add( String name, String value ) { add( name, valueOf( value ) ); return this; } /** * Appends a new member to the end of this object, with the specified name and the specified JSON * value. *

* This method does not prevent duplicate names. Calling this method with a name * that already exists in the object will append another member with the same name. In order to * replace existing members, use the method set(name, value) instead. However, * add is much faster than set (because it does not need to * search for existing members). Therefore add should be preferred when constructing new * objects. *

* * @param name * the name of the member to add * @param value * the value of the member to add, must not be null * @return the object itself, to enable method chaining */ public JsonObject add( String name, JsonValue value ) { if( name == null ) { throw new NullPointerException( "name is null" ); } if( value == null ) { throw new NullPointerException( "value is null" ); } table.add( name, names.size() ); names.add( name ); values.add( value ); return this; } /** * Sets the value of the member with the specified name to the JSON representation of the * specified int value. If this object does not contain a member with this name, a * new member is added at the end of the object. If this object contains multiple members with * this name, only the last one is changed. *

* This method should only be used to modify existing objects. To fill a new * object with members, the method add(name, value) should be preferred which is much * faster (as it does not need to search for existing members). *

* * @param name * the name of the member to replace * @param value * the value to set to the member * @return the object itself, to enable method chaining */ public JsonObject set( String name, int value ) { set( name, valueOf( value ) ); return this; } /** * Sets the value of the member with the specified name to the JSON representation of the * specified long value. If this object does not contain a member with this name, a * new member is added at the end of the object. If this object contains multiple members with * this name, only the last one is changed. *

* This method should only be used to modify existing objects. To fill a new * object with members, the method add(name, value) should be preferred which is much * faster (as it does not need to search for existing members). *

* * @param name * the name of the member to replace * @param value * the value to set to the member * @return the object itself, to enable method chaining */ public JsonObject set( String name, long value ) { set( name, valueOf( value ) ); return this; } /** * Sets the value of the member with the specified name to the JSON representation of the * specified float value. If this object does not contain a member with this name, a * new member is added at the end of the object. If this object contains multiple members with * this name, only the last one is changed. *

* This method should only be used to modify existing objects. To fill a new * object with members, the method add(name, value) should be preferred which is much * faster (as it does not need to search for existing members). *

* * @param name * the name of the member to add * @param value * the value of the member to add * @return the object itself, to enable method chaining */ public JsonObject set( String name, float value ) { set( name, valueOf( value ) ); return this; } /** * Sets the value of the member with the specified name to the JSON representation of the * specified double value. If this object does not contain a member with this name, a * new member is added at the end of the object. If this object contains multiple members with * this name, only the last one is changed. *

* This method should only be used to modify existing objects. To fill a new * object with members, the method add(name, value) should be preferred which is much * faster (as it does not need to search for existing members). *

* * @param name * the name of the member to add * @param value * the value of the member to add * @return the object itself, to enable method chaining */ public JsonObject set( String name, double value ) { set( name, valueOf( value ) ); return this; } /** * Sets the value of the member with the specified name to the JSON representation of the * specified boolean value. If this object does not contain a member with this name, * a new member is added at the end of the object. If this object contains multiple members with * this name, only the last one is changed. *

* This method should only be used to modify existing objects. To fill a new * object with members, the method add(name, value) should be preferred which is much * faster (as it does not need to search for existing members). *

* * @param name * the name of the member to add * @param value * the value of the member to add * @return the object itself, to enable method chaining */ public JsonObject set( String name, boolean value ) { set( name, valueOf( value ) ); return this; } /** * Sets the value of the member with the specified name to the JSON representation of the * specified string. If this object does not contain a member with this name, a new member is * added at the end of the object. If this object contains multiple members with this name, only * the last one is changed. *

* This method should only be used to modify existing objects. To fill a new * object with members, the method add(name, value) should be preferred which is much * faster (as it does not need to search for existing members). *

* * @param name * the name of the member to add * @param value * the value of the member to add * @return the object itself, to enable method chaining */ public JsonObject set( String name, String value ) { set( name, valueOf( value ) ); return this; } /** * Sets the value of the member with the specified name to the specified JSON value. If this * object does not contain a member with this name, a new member is added at the end of the * object. If this object contains multiple members with this name, only the last one is changed. *

* This method should only be used to modify existing objects. To fill a new * object with members, the method add(name, value) should be preferred which is much * faster (as it does not need to search for existing members). *

* * @param name * the name of the member to add * @param value * the value of the member to add, must not be null * @return the object itself, to enable method chaining */ public JsonObject set( String name, JsonValue value ) { if( name == null ) { throw new NullPointerException( "name is null" ); } if( value == null ) { throw new NullPointerException( "value is null" ); } int index = indexOf( name ); if( index != -1 ) { values.set( index, value ); } else { table.add( name, names.size() ); names.add( name ); values.add( value ); } return this; } /** * Removes a member with the specified name from this object. If this object contains multiple * members with the given name, only the last one is removed. If this object does not contain a * member with the specified name, the object is not modified. * * @param name * the name of the member to remove * @return the object itself, to enable method chaining */ public JsonObject remove( String name ) { if( name == null ) { throw new NullPointerException( "name is null" ); } int index = indexOf( name ); if( index != -1 ) { table.remove( index ); names.remove( index ); values.remove( index ); } return this; } /** * Returns the value of the member with the specified name in this object. If this object contains * multiple members with the given name, this method will return the last one. * * @param name * the name of the member whose value is to be returned * @return the value of the last member with the specified name, or null if this * object does not contain a member with that name */ public JsonValue get( String name ) { if( name == null ) { throw new NullPointerException( "name is null" ); } int index = indexOf( name ); return index != -1 ? values.get( index ) : null; } /** * Returns the number of members (i.e. name/value pairs) in this object. * * @return the number of members in this object */ public int size() { return names.size(); } /** * Returns true if this object contains no members. * * @return true if this object contains no members */ public boolean isEmpty() { return names.isEmpty(); } /** * Returns a list of the names in this object in document order. The returned list is backed by * this object and will reflect subsequent changes. It cannot be used to modify this object. * Attempts to modify the returned list will result in an exception. * * @returns a list of the names in this object */ public List names() { return Collections.unmodifiableList( names ); } /** * Returns an iterator over the members of this object in document order. The returned iterator * cannot be used to modify this object. * * @return an iterator over the members of this object */ public Iterator iterator() { final Iterator namesIterator = names.iterator(); final Iterator valuesIterator = values.iterator(); return new Iterator() { public boolean hasNext() { return namesIterator.hasNext(); } public Member next() { String name = namesIterator.next(); JsonValue value = valuesIterator.next(); return new Member( name, value ); } public void remove() { throw new UnsupportedOperationException(); } }; } @Override protected void write( JsonWriter writer ) throws IOException { writer.writeObject( this ); } @Override public boolean isObject() { return true; } @Override public JsonObject asObject() { return this; } @Override public int hashCode() { int result = 1; result = 31 * result + names.hashCode(); result = 31 * result + values.hashCode(); return result; } @Override public boolean equals( Object obj ) { if( this == obj ) { return true; } if( obj == null ) { return false; } if( getClass() != obj.getClass() ) { return false; } JsonObject other = (JsonObject)obj; return names.equals( other.names ) && values.equals( other.values ); } int indexOf( String name ) { int index = table.get( name ); if( index != -1 && name.equals( names.get( index ) ) ) { return index; } return names.lastIndexOf( name ); } private synchronized void readObject( ObjectInputStream inputStream ) throws IOException, ClassNotFoundException { inputStream.defaultReadObject(); table = new HashIndexTable(); updateHashIndex(); } private void updateHashIndex() { int size = names.size(); for( int i = 0; i < size; i++ ) { table.add( names.get( i ), i ); } } /** * Represents a member of a JSON object, i.e. a pair of name and value. */ public static class Member { private final String name; private final JsonValue value; Member( String name, JsonValue value ) { this.name = name; this.value = value; } /** * Returns the name of this member. * * @return the name of this member, never null */ public String getName() { return name; } /** * Returns the value of this member. * * @return the value of this member, never null */ public JsonValue getValue() { return value; } @Override public int hashCode() { int result = 1; result = 31 * result + name.hashCode(); result = 31 * result + value.hashCode(); return result; } @Override public boolean equals( Object obj ) { if( this == obj ) { return true; } if( obj == null ) { return false; } if( getClass() != obj.getClass() ) { return false; } Member other = (Member)obj; return name.equals( other.name ) && value.equals( other.value ); } } static class HashIndexTable { private final byte[] hashTable = new byte[32]; // must be a power of two public HashIndexTable() { } public HashIndexTable( HashIndexTable original ) { System.arraycopy( original.hashTable, 0, hashTable, 0, hashTable.length ); } void add( String name, int index ) { int slot = hashSlotFor( name ); if( index < 0xff ) { // increment by 1, 0 stands for empty hashTable[slot] = (byte)( index + 1 ); } else { hashTable[slot] = 0; } } void remove( int index ) { for( int i = 0; i < hashTable.length; i++ ) { if( hashTable[i] == index + 1 ) { hashTable[i] = 0; } else if( hashTable[i] > index + 1 ) { hashTable[i]--; } } } int get( Object name ) { int slot = hashSlotFor( name ); // subtract 1, 0 stands for empty return ( hashTable[slot] & 0xff ) - 1; } private int hashSlotFor( Object element ) { return element.hashCode() & hashTable.length - 1; } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy