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

info.freelibrary.json.JsonObject Maven / Gradle / Ivy

There is a newer version: 0.10.2
Show newest version

package info.freelibrary.json;

import static info.freelibrary.util.Constants.EMPTY;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
import java.util.NoSuchElementException;
import java.util.Objects;
import java.util.Optional;
import java.util.OptionalDouble;
import java.util.OptionalInt;
import java.util.OptionalLong;

import info.freelibrary.json.JsonObject.Property;

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

* Properties 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 properties. On the other hand, the add methods do not prevent adding * multiple properties with the same name. Duplicate names are discouraged but not prohibited by JSON. *

*

* Properties 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 properties in document order using an * {@link #iterator()} or an enhanced for loop: *

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

* Even though JSON objects are unordered by definition, instances of this class preserve the order of properties 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. *

*/ public final class JsonObject extends JsonValue implements Iterable { /** * The serialVersionUID for JsonObject. */ private static final long serialVersionUID = -7137647142842370219L; /** * A list of property names. */ private final List myNames; /** * A list of property values. */ private final List myValues; /** * A hash code table. */ private transient HashCodeTable myTable; /** * Creates a new empty JsonObject. */ public JsonObject() { myNames = new ArrayList<>(); myValues = new ArrayList<>(); myTable = new HashCodeTable(); } /** * Creates a new JsonObject, initialized with the contents of the specified JSON object. * * @param aObject the JSON object to get the initial contents from, must not be null */ public JsonObject(final JsonObject aObject) { this(aObject, false); } /** * Creates a new JsonObject that can be unmodifiable, if desired. * * @param aObject A JSON object * @param aUnmodifiable Whether the newly created JsonObject should be unmodifiable */ private JsonObject(final JsonObject aObject, final boolean aUnmodifiable) { Objects.requireNonNull(aObject, "object is null"); if (aUnmodifiable) { myNames = Collections.unmodifiableList(aObject.myNames); myValues = Collections.unmodifiableList(aObject.myValues); } else { myNames = new ArrayList<>(aObject.myNames); myValues = new ArrayList<>(aObject.myValues); } myTable = new HashCodeTable(); updateHashIndex(); } /** * Returns an unmodifiable JsonObject for the specified one. This method allows 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 aObject the JsonObject for which an unmodifiable JsonObject is to be returned * @return an unmodifiable view of the specified JsonObject */ public static JsonObject unmodifiableObject(final JsonObject aObject) { return new JsonObject(aObject, true); } /** * Appends a new property 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 property with the same name. In order to replace existing properties, * use the method set(name, value) instead. However, add is much faster than * set (because it does not need to search for existing properties). Therefore add should * be preferred when constructing new objects. *

* * @param aName The name of the property to add * @param aValue The value of the property to add * @return The object itself, to enable method chaining */ public JsonObject add(final String aName, final int aValue) { return add(aName, Json.value(aValue)); } /** * Appends a new property 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 property with the same name. In order to replace existing properties, * use the method set(name, value) instead. However, add is much faster than * set (because it does not need to search for existing properties). Therefore add should * be preferred when constructing new objects. *

* * @param aName The name of the property to add * @param aValue The value of the property to add * @return The object itself, to enable method chaining */ public JsonObject add(final String aName, final long aValue) { return add(aName, Json.value(aValue)); } /** * Appends a new property 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 property with the same name. In order to replace existing properties, * use the method set(name, value) instead. However, add is much faster than * set (because it does not need to search for existing properties). Therefore add should * be preferred when constructing new objects. *

* * @param aName The name of the property to add * @param aValue The value of the property to add * @return The object itself, to enable method chaining */ public JsonObject add(final String aName, final float aValue) { return add(aName, Json.value(aValue)); } /** * Appends a new property 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 property with the same name. In order to replace existing properties, * use the method set(name, value) instead. However, add is much faster than * set (because it does not need to search for existing properties). Therefore add should * be preferred when constructing new objects. *

* * @param aName The name of the property to add * @param aValue The value of the property to add * @return The object itself, to enable method chaining */ public JsonObject add(final String aName, final double aValue) { return add(aName, Json.value(aValue)); } /** * Appends a new property 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 property with the same name. In order to replace existing properties, * use the method set(name, value) instead. However, add is much faster than * set (because it does not need to search for existing properties). Therefore add should * be preferred when constructing new objects. *

* * @param aName The name of the property to add * @param aValue The value of the property to add * @return The object itself, to enable method chaining */ public JsonObject add(final String aName, final boolean aValue) { return add(aName, Json.value(aValue)); } /** * Appends a new property 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 property with the same name. In order to replace existing properties, * use the method set(name, value) instead. However, add is much faster than * set (because it does not need to search for existing properties). Therefore add should * be preferred when constructing new objects. *

* * @param aName The name of the property to add * @param aValue The value of the property to add * @return The object itself, to enable method chaining */ public JsonObject add(final String aName, final String aValue) { return add(aName, Json.value(aValue)); } /** * Appends a new property to the end of this object, with the specified name and supplied JSON object. *

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

* * @param aName The name of the property to add * @param aObject The value of the property to add * @return The object itself, to enable method chaining */ public JsonObject add(final String aName, final JsonObject aObject) { return add(aName, (JsonValue) aObject); } /** * Appends a new property to the end of this object, with the specified name and supplied JSON array. *

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

* * @param aName The name of the property to add * @param aArray The value of the property to add * @return The object itself, to enable method chaining */ public JsonObject add(final String aName, final JsonArray aArray) { return add(aName, (JsonValue) aArray); } /** * Appends a new property 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 property with the same name. In order to replace existing properties, * use the method set(name, value) instead. However, add is much faster than * set (because it does not need to search for existing properties). Therefore add should * be preferred when constructing new objects. *

* * @param aName The name of the property to add * @param aValue The value of the property to add, must not be null * @return The object itself, to enable method chaining */ protected JsonObject add(final String aName, final JsonValue aValue) { Objects.requireNonNull(aName, "name is null"); Objects.requireNonNull(aValue, "value is null"); myTable.add(aName, myNames.size()); myNames.add(aName); myValues.add(aValue); return this; } /** * Sets the value of the property with the specified name to the JSON representation of the specified * int value. If this object does not contain a property with this name, a new property is added at the * end of the object. If this object contains multiple properties 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 * properties, the method add(name, value) should be preferred which is much faster (as it does not * need to search for existing properties). *

* * @param aName The name of the property to replace * @param aValue The value to set to the property * @return The object itself, to enable method chaining */ public JsonObject set(final String aName, final int aValue) { return set(aName, Json.value(aValue)); } /** * Sets the value of the property with the specified name to the JSON representation of the specified * long value. If this object does not contain a property with this name, a new property is added at * the end of the object. If this object contains multiple properties 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 * properties, the method add(name, value) should be preferred which is much faster (as it does not * need to search for existing properties). *

* * @param aName The name of the property to replace * @param aValue The value to set to the property * @return The object itself, to enable method chaining */ public JsonObject set(final String aName, final long aValue) { return set(aName, Json.value(aValue)); } /** * Sets the value of the property with the specified name to the JSON representation of the specified * float value. If this object does not contain a property with this name, a new property is added at * the end of the object. If this object contains multiple properties 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 * properties, the method add(name, value) should be preferred which is much faster (as it does not * need to search for existing properties). *

* * @param aName The name of the property to add * @param aValue The value of the property to add * @return The object itself, to enable method chaining */ public JsonObject set(final String aName, final float aValue) { return set(aName, Json.value(aValue)); } /** * Sets the value of the property with the specified name to the JSON representation of the specified * double value. If this object does not contain a property with this name, a new property is added at * the end of the object. If this object contains multiple properties 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 * properties, the method add(name, value) should be preferred which is much faster (as it does not * need to search for existing properties). *

* * @param aName The name of the property to add * @param aValue The value of the property to add * @return The object itself, to enable method chaining */ public JsonObject set(final String aName, final double aValue) { set(aName, Json.value(aValue)); return this; } /** * Sets the value of the property with the specified name to the JSON representation of the specified * boolean value. If this object does not contain a property with this name, a new property is added at * the end of the object. If this object contains multiple properties 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 * properties, the method add(name, value) should be preferred which is much faster (as it does not * need to search for existing properties). *

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

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

* * @param aName The name of the property to add * @param aValue The value of the property to add * @return The object itself, to enable method chaining */ public JsonObject set(final String aName, final String aValue) { return set(aName, Json.value(aValue)); } /** * Sets the value of the property with the specified name to the specified JSON value. If this object does not * contain a property with this name, a new property is added at the end of the object. If this object contains * multiple properties 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 * properties, the method add(name, value) should be preferred which is much faster (as it does not * need to search for existing properties). *

* * @param aName The name of the property to add * @param aValue The value of the property to add, must not be null * @return The object itself, to enable method chaining */ public JsonObject set(final String aName, final JsonValue aValue) { Objects.requireNonNull(aName, "name is null"); Objects.requireNonNull(aValue, "value is null"); final int index = indexOf(aName); if (index != -1) { myValues.set(index, aValue); } else { myTable.add(aName, myNames.size()); myNames.add(aName); myValues.add(aValue); } return this; } /** * Removes a property with the specified name from this object. If this object contains multiple properties with the * given name, only the last one is removed. If this object does not contain a property with the specified name, the * object is not modified. * * @param aName The name of the property to remove * @return The object itself, to enable method chaining */ public JsonObject remove(final String aName) { Objects.requireNonNull(aName, "name is null"); final int index = indexOf(aName); if (index != -1) { myTable.remove(index); myNames.remove(index); myValues.remove(index); } return this; } /** * Checks if a specified property is present as a child of this object. This will not test if this object contains * the literal null, {@link JsonValue#isNull()} should be used for this purpose. * * @param aName The name of the property to check for * @return Whether or not the property is present */ public boolean contains(final String aName) { return myNames.contains(aName); } /** * Copies all properties of the specified object into this object. When the specified object contains properties * with names that also exist in this object, the existing values in this object will be replaced by the * corresponding values in the specified object. * * @param aObject The object to merge * @return The object itself, to enable method chaining */ public JsonObject merge(final JsonObject aObject) { Objects.requireNonNull(aObject, "object is null"); for (final Property property : aObject) { set(property.myName, property.myValue); } return this; } /** * Returns the value of the property with the specified name in this object. If this object contains multiple * properties with the given name, this method will return the last one. * * @param aName The name of the property whose value is to be returned * @return An Optional with the value of the last property with the specified name, or an empty Optional if this * object does not contain a property with that name */ public Optional get(final String aName) { return Optional.ofNullable(getJsonValue(aName)); } /** * Returns an optional JsonObject property value that's associated with the supplied property name. If this object * contains multiple properties with the given name, this method will return the last one. If it doesn't have any * properties with the given name, an empty Optional is returned. * * @param aName The name of the JsonObject property to be returned * @return An Optional with the value of the last property with the specified name, or an empty Optional if this * object does not contain a property with that name */ public Optional getJsonObject(final String aName) { final JsonValue value = getJsonValue(aName); return value != null ? Optional.of(value.asObject()) : Optional.empty(); } /** * Returns the JsonObject property value that's associated with the supplied property name. If this object doesn't * contain a property with the supplied name, the passed in default value is returned instead. * * @param aName The name of the JsonObject property to be returned * @param aDefaultValue The default value returned if the JsonObject doesn't have a property with the supplied name * @return The requested JsonObject or the supplied default value */ public JsonObject getJsonObject(final String aName, final JsonObject aDefaultValue) { final JsonValue value = getJsonValue(aName); return value != null ? value.asObject() : aDefaultValue; } /** * Returns an optional JsonArray property value that's associated with the supplied property name. If this object * contains multiple properties with the given name, this method will return the last one. If it doesn't have any * properties with the given name, an empty Optional is returned. * * @param aName The name of the JsonArray property to be returned * @return An Optional with the value of the last property with the specified name, or an empty Optional if this * object does not contain a property with that name */ public Optional getJsonArray(final String aName) { final JsonValue value = getJsonValue(aName); return value != null ? Optional.of(value.asArray()) : Optional.empty(); } /** * Returns the JsonArray property value that's associated with the supplied property name. If this object doesn't * contain a property with the supplied name, the pass in default value is returned instead. * * @param aName The name of the JsonArray property to be returned * @param aDefaultValue The default value returned if the JsonObject doesn't have a property with the supplied name * @return The requested JsonArray or the supplied default value */ public JsonArray getJsonArray(final String aName, final JsonArray aDefaultValue) { final JsonValue value = getJsonValue(aName); return value != null ? value.asArray() : aDefaultValue; } /** * Returns the int value of the property with the specified name in this object. If this object does * not contain a property with this name, the given default value is returned. If this object contains multiple * properties with the given name, the last one will be picked. If this property's value does not represent a JSON * number or if it cannot be interpreted as Java int, an exception is thrown. * * @param aName The name of the property whose value is to be returned * @param aDefaultValue The value to be returned if the requested property is missing * @return The value of the last property with the specified name, or the given default value if this object does * not contain a property with that name */ public int getInt(final String aName, final int aDefaultValue) { final JsonValue value = getJsonValue(aName); return value != null ? value.asInt() : aDefaultValue; } /** * Returns the optional int value of the property with the specified name in this object. If this * object does not contain a property with this name, an empty optional is returned. If this object contains * multiple properties with the given name, the last one will be picked. If this property's value does not represent * a JSON number or if it cannot be interpreted as Java int, an exception is thrown. * * @param aName The name of the property whose value is to be returned * @return The value of the last property with the specified name, or an empty optional if this object does not * contain a property with that name */ public OptionalInt getInt(final String aName) { final JsonValue value = getJsonValue(aName); return value != null ? OptionalInt.of(value.asInt()) : OptionalInt.empty(); } /** * Returns the long value of the property with the specified name in this object. If this object does * not contain a property with this name, the given default value is returned. If this object contains multiple * properties with the given name, the last one will be picked. If this property's value does not represent a JSON * number or if it cannot be interpreted as Java long, an exception is thrown. * * @param aName The name of the property whose value is to be returned * @param aDefaultValue The value to be returned if the requested property is missing * @return The value of the last property with the specified name, or the given default value if this object does * not contain a property with that name */ public long getLong(final String aName, final long aDefaultValue) { final JsonValue value = getJsonValue(aName); return value != null ? value.asLong() : aDefaultValue; } /** * Returns the optional long value of the property with the specified name in this object. If this * object does not contain a property with this name, an empty optional is returned. If this object contains * multiple properties with the given name, the last one will be picked. If this property's value does not represent * a JSON number or if it cannot be interpreted as Java long, an exception is thrown. * * @param aName The name of the property whose value is to be returned * @return The value of the last property with the specified name, or an empty optional if this object does not * contain a property with that name */ public OptionalLong getLong(final String aName) { final JsonValue value = getJsonValue(aName); return value != null ? OptionalLong.of(value.asLong()) : OptionalLong.empty(); } /** * Returns the float value of the property with the specified name in this object. If this object does * not contain a property with this name, the given default value is returned. If this object contains multiple * properties with the given name, the last one will be picked. If this property's value does not represent a JSON * number or if it cannot be interpreted as Java float, an exception is thrown. * * @param aName The name of the property whose value is to be returned * @param aDefaultValue The value to be returned if the requested property is missing * @return The value of the last property with the specified name, or the given default value if this object does * not contain a property with that name */ public float getFloat(final String aName, final float aDefaultValue) { final JsonValue value = getJsonValue(aName); return value != null ? value.asFloat() : aDefaultValue; } /** * Returns the optional float value of the property with the specified name in this object. If this * object does not contain a property with this name, an empty optional is returned. If this object contains * multiple properties with the given name, the last one will be picked. If this property's value does not represent * a JSON number or if it cannot be interpreted as Java float, an exception is thrown. * * @param aName The name of the property whose value is to be returned * @return The value of the last property with the specified name, or an empty optional if this object does not * contain a property with that name */ public Optional getFloat(final String aName) { final JsonValue value = getJsonValue(aName); return value != null ? Optional.of(value.asFloat()) : Optional.empty(); } /** * Returns the double value of the property with the specified name in this object. If this object does * not contain a property with this name, the given default value is returned. If this object contains multiple * properties with the given name, the last one will be picked. If this property's value does not represent a JSON * number or if it cannot be interpreted as Java double, an exception is thrown. * * @param aName The name of the property whose value is to be returned * @param aDefaultValue the value to be returned if the requested property is missing * @return The value of the last property with the specified name, or the given default value if this object does * not contain a property with that name */ public double getDouble(final String aName, final double aDefaultValue) { final JsonValue value = getJsonValue(aName); return value != null ? value.asDouble() : aDefaultValue; } /** * Returns the optional double value of the property with the specified name in this object. If this * object does not contain a property with this name, an empty optional is returned. If this object contains * multiple properties with the given name, the last one will be picked. If this property's value does not represent * a JSON number or if it cannot be interpreted as Java double, an exception is thrown. * * @param aName The name of the property whose value is to be returned * @return The value of the last property with the specified name, or an empty optional if this object does not * contain a property with that name */ public OptionalDouble getDouble(final String aName) { final JsonValue value = getJsonValue(aName); return value != null ? OptionalDouble.of(value.asDouble()) : OptionalDouble.empty(); } /** * Returns the boolean value of the property with the specified name in this object. If this object * does not contain a property with this name, the given default value is returned. If this object contains multiple * properties with the given name, the last one will be picked. If this property's value does not represent a JSON * true or false value, an exception is thrown. * * @param aName The name of the property whose value is to be returned * @param aDefaultValue The value to be returned if the requested property is missing * @return The value of the last property with the specified name, or the given default value if this object does * not contain a property with that name */ public boolean getBoolean(final String aName, final boolean aDefaultValue) { final JsonValue value = getJsonValue(aName); return value != null ? value.asBoolean() : aDefaultValue; } /** * Returns the optional boolean value of the property with the specified name in this object. If this * object does not contain a property with this name, an empty optional is returned. If this object contains * multiple properties with the given name, the last one will be picked. If this property's value does not represent * a JSON number or if it cannot be interpreted as Java boolean, an exception is thrown. * * @param aName The name of the property whose value is to be returned * @return The value of the last property with the specified name, or an empty optional if this object does not * contain a property with that name */ public Optional getBoolean(final String aName) { final JsonValue value = getJsonValue(aName); return value != null ? Optional.of(value.asBoolean()) : Optional.empty(); } /** * Returns the String value of the property with the specified name in this object. If this object does * not contain a property with this name, the given default value is returned. If this object contains multiple * properties with the given name, the last one is picked. If this property's value does not represent a JSON * string, an exception is thrown. * * @param aName The name of the property whose value is to be returned * @param aDefaultValue The value to be returned if the requested property is missing * @return The value of the last property with the specified name, or the given default value if this object does * not contain a property with that name */ public String getString(final String aName, final String aDefaultValue) { final JsonValue value = getJsonValue(aName); return value != null ? value.asString() : aDefaultValue; } /** * Returns the optional String value of the property with the specified name in this object. If this * object does not contain a property with this name, an empty optional is returned. If this object contains * multiple properties with the given name, the last one will be picked. If this property's value does not represent * a JSON string, an exception is thrown. * * @param aName The name of the property whose value is to be returned * @return The value of the last property with the specified name, or an empty optional if this object does not * contain a property with that name */ public Optional getString(final String aName) { final JsonValue value = getJsonValue(aName); return value != null ? Optional.of(value.asString()) : Optional.empty(); } /** * Returns the number of properties (name/value pairs) in this object. * * @return The number of properties in this object */ public int size() { return myNames.size(); } /** * Returns true if this object contains no properties. * * @return True if this object contains no properties; else, false */ public boolean isEmpty() { return myNames.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. * * @return A list of the names in this object */ public List names() { return Collections.unmodifiableList(myNames); } /** * Returns an iterator over the properties of this object in document order. The returned iterator cannot be used to * modify this object. * * @return An iterator over the properties of this object */ @Override public ListIterator iterator() { final ListIterator namesIterator = myNames.listIterator(); final ListIterator valuesIterator = myValues.listIterator(); return new ListIterator<>() { @Override public boolean hasNext() { return namesIterator.hasNext(); } @Override public Property next() { if (!hasNext()) { throw new NoSuchElementException("JsonObject doesn't have another property"); } return new Property(namesIterator.next(), valuesIterator.next()); } @Override public void remove() { throw new UnsupportedOperationException(); } @Override public void add(final Property aProperty) { throw new UnsupportedOperationException(); } @Override public boolean hasPrevious() { return namesIterator.hasPrevious(); } @Override public int nextIndex() { return namesIterator.nextIndex(); } @Override public Property previous() { return new Property(namesIterator.previous(), valuesIterator.next()); } @Override public int previousIndex() { return namesIterator.previousIndex(); } @Override public void set(final Property arg0) { throw new UnsupportedOperationException(); } }; } @Override void write(final JsonWriter aWriter) throws IOException { aWriter.writeObjectOpen(); if (aWriter instanceof SortingWriter) { final Iterator iterator = sortedIterator(); if (iterator.hasNext()) { Property property = iterator.next(); aWriter.writeMemberName(property.getName()); aWriter.writeMemberSeparator(); property.getValue().write(aWriter); while (iterator.hasNext()) { property = iterator.next(); aWriter.writeObjectSeparator(); aWriter.writeMemberName(property.getName()); aWriter.writeMemberSeparator(); property.getValue().write(aWriter); } } } else { final Iterator namesIterator = myNames.iterator(); final Iterator valuesIterator = myValues.iterator(); if (namesIterator.hasNext()) { aWriter.writeMemberName(namesIterator.next()); aWriter.writeMemberSeparator(); valuesIterator.next().write(aWriter); while (namesIterator.hasNext()) { aWriter.writeObjectSeparator(); aWriter.writeMemberName(namesIterator.next()); aWriter.writeMemberSeparator(); valuesIterator.next().write(aWriter); } } } aWriter.writeObjectClose(); } @Override public boolean isObject() { return true; } @Override public JsonObject asObject() { return this; } @Override public int hashCode() { int result = 1; result = 31 * result + myNames.hashCode(); return 31 * result + myValues.hashCode(); } @Override public boolean equals(final Object aObject) { final JsonObject jsonObject; if (this == aObject) { return true; } if (aObject == null || getClass() != aObject.getClass()) { return false; } jsonObject = (JsonObject) aObject; return myNames.equals(jsonObject.myNames) && myValues.equals(jsonObject.myValues); } @Override public boolean equals(final JsonValue aValue, final JsonOptions aConfig) { return ValueUtils.equals(EMPTY, this, aValue, aConfig); } /** * Gets the index of the supplied property name. * * @param aName A property name * @return The index position of the last entry with the supplied name */ int indexOf(final String aName) { final int index = myTable.get(aName); if (index != -1 && aName.equals(myNames.get(index))) { return index; } return myNames.lastIndexOf(aName); } /** * Iterates through the JsonObject's contents in a sorted fashion. * * @return An iterator that iterates over the JsonObject's sorted contents */ Iterator sortedIterator() { final List names = new ArrayList<>(myNames); // Sort the JsonObject's property names Collections.sort(names); /** * The sorted iterator. */ return new Iterator<>() { /** * The sorted iterator's index position. */ private int myCurrentIndex; @Override public boolean hasNext() { return myCurrentIndex < names.size(); } @Override public Property next() { final String name = names.get(myCurrentIndex++); return new Property(name, myValues.get(myNames.indexOf(name))); } }; } /** * Reads the object input stream. * * @param aInputStream An object stream * @throws IOException If there is trouble reading the object stream * @throws ClassNotFoundException If the class of the object stream cannot be found */ private synchronized void readObject(final ObjectInputStream aInputStream) throws IOException, ClassNotFoundException { aInputStream.defaultReadObject(); myTable = new HashCodeTable(); updateHashIndex(); } /** * Updates the object's hash index. */ private void updateHashIndex() { final int size = myNames.size(); for (int index = 0; index < size; index++) { myTable.add(myNames.get(index), index); } } /** * Returns the value of the property with the specified name in this object. If this object contains multiple * properties with the given name, this method will return the last one. * * @param aName The name of the property whose value is to be returned * @return The value of the last property with the specified name, or null if this object does not * contain a property with that name */ private JsonValue getJsonValue(final String aName) { final int index = indexOf(Objects.requireNonNull(aName, "name is null")); return index != -1 ? myValues.get(index) : null; } /** * Represents a JSON object property, i.e. a name and value. */ public static class Property { /** * The name of the JSON object's property. */ private final String myName; /** * The value of the JSON object's property. */ private final JsonValue myValue; /** * Creates a new JSON object property. * * @param aName A property name * @param aValue A property string value */ Property(final String aName, final String aValue) { this(aName, Json.value(aValue)); } /** * Creates a new JSON object property. * * @param aName A property name * @param aValue A property integer value */ Property(final String aName, final int aValue) { this(aName, Json.value(aValue)); } /** * Creates a new JSON object property. * * @param aName A property name * @param aValue A property long value */ Property(final String aName, final long aValue) { this(aName, Json.value(aValue)); } /** * Creates a new JSON object property. * * @param aName A property name * @param aValue A property float value */ Property(final String aName, final float aValue) { this(aName, Json.value(aValue)); } /** * Creates a new JSON object property. * * @param aName A property name * @param aValue A property double value */ Property(final String aName, final double aValue) { this(aName, Json.value(aValue)); } /** * Creates a new JSON object property. * * @param aName A property name * @param aValue A property boolean value */ Property(final String aName, final boolean aValue) { this(aName, Json.value(aValue)); } /** * Creates a new JSON object property. * * @param aName A property name * @param aValue A property JSON array value */ Property(final String aName, final JsonArray aValue) { this(aName, (JsonValue) aValue); } /** * Creates a new JSON object property. * * @param aName A property name * @param aValue A property JSON object value */ Property(final String aName, final JsonObject aValue) { this(aName, (JsonValue) aValue); } /** * Creates a new JSON object property. * * @param aName A property name * @param aValue A property value */ private Property(final String aName, final JsonValue aValue) { myName = aName; myValue = aValue; } /** * Returns the name of this property. * * @return the name of this property, never null */ public String getName() { return myName; } /** * Returns the value of this property if it's a string. If the value isn't a string, an * UnsupportedOperationException is thrown. * * @return The string value of this property * @throws UnsupportedOperationException If the property's value isn't a string */ public String getString() { return myValue.asString(); } /** * Returns the value of this property if it's an integer. If the value isn't an integer, an * UnsupportedOperationException is thrown. * * @return The integer value of this property * @throws UnsupportedOperationException If the property's value isn't an integer */ public int getInt() { return myValue.asInt(); } /** * Returns the value of this property if it's a long. If the value isn't a long, an * UnsupportedOperationException is thrown. * * @return The long value of this property * @throws UnsupportedOperationException If the property's value isn't a long */ public long getLong() { return myValue.asLong(); } /** * Returns the value of this property if it's a float. If the value isn't a float, an * UnsupportedOperationException is thrown. * * @return The float value of this property * @throws UnsupportedOperationException If the property's value isn't a float */ public float getFloat() { return myValue.asFloat(); } /** * Returns the value of this property if it's a double. If the value isn't a double, an * UnsupportedOperationException is thrown. * * @return The double value of this property * @throws UnsupportedOperationException If the property's value isn't a double */ public double getDouble() { return myValue.asDouble(); } /** * Returns the value of this property if it's a boolean. If the value isn't a boolean, an * UnsupportedOperationException is thrown. * * @return The boolean value of this property * @throws UnsupportedOperationException If the property's value isn't a boolean */ public boolean getBoolean() { return myValue.asBoolean(); } /** * Returns the value of this property if it's a JsonObject. If the value isn't a JsonObject, an * UnsupportedOperationException is thrown. * * @return The JsonObject value of this property * @throws UnsupportedOperationException If the property's value isn't a JsonObject */ public JsonObject getObject() { return myValue.asObject(); } /** * Returns the value of this property if it's a JsonArray. If the value isn't a JsonArray, an * UnsupportedOperationException is thrown. * * @return The JsonArray value of this property * @throws UnsupportedOperationException If the property's value isn't a JsonArray */ public JsonArray getArray() { return myValue.asArray(); } /** * Returns the value of this property. * * @return the value of this property, never null */ public JsonValue getValue() { return myValue; } @Override public int hashCode() { int result = 1; result = 31 * result + myName.hashCode(); return 31 * result + myValue.hashCode(); } /** * Indicates whether a given object is "equal to" this JsonObject. An object is considered equal if it is also a * JsonObject and both objects contain the same properties in the same order. *

* If two JsonObjects are equal, they will also produce the same JSON output. *

* * @param aObject the object to be compared with this JsonObject * @return True if the specified object is equal to this JsonObject; else, false */ @Override public boolean equals(final Object aObject) { final Property jsonObject; if (this == aObject) { return true; } if (aObject == null || getClass() != aObject.getClass()) { return false; } jsonObject = (Property) aObject; return myName.equals(jsonObject.myName) && myValue.equals(jsonObject.myValue); } } /** * A hash code table. */ static class HashCodeTable { /** * The internal index table. */ private final byte[] myIndexTable = new byte[32]; // Must be a power of two /** * Creates a new hash code table. */ HashCodeTable() { } /** * Creates a new hash code table from the supplied one. * * @param aHashCodeTable A hash code table */ HashCodeTable(final HashCodeTable aHashCodeTable) { System.arraycopy(aHashCodeTable.myIndexTable, 0, myIndexTable, 0, myIndexTable.length); } /** * Add a new name to the hash code table. * * @param aName A name * @param aIndex An index position */ void add(final String aName, final int aIndex) { final int slot = hashSlotFor(aName); if (aIndex < 0xff) { myIndexTable[slot] = (byte) (aIndex + 1); // Increment by 1 } else { myIndexTable[slot] = 0; // 0 stands for empty } } /** * Removes an entry from the hash code table. * * @param aIndex The index position of the entry to remove */ void remove(final int aIndex) { for (int index = 0; index < myIndexTable.length; index++) { if ((myIndexTable[index] & 0xff) == aIndex + 1) { myIndexTable[index] = 0; } else if ((myIndexTable[index] & 0xff) > aIndex + 1) { myIndexTable[index]--; } } } /** * Gets the index position of the supplied entry name. * * @param aName The entry name for which to get the index position * @return The index position of the supplied entry name */ int get(final Object aName) { return (myIndexTable[hashSlotFor(aName)] & 0xff) - 1; // Decrement by 1; 0 stands for empty } /** * Gets the hash code table slot for the supplied entry. * * @param aEntry An index table entry. * @return A hash slot */ private int hashSlotFor(final Object aEntry) { return aEntry.hashCode() & myIndexTable.length - 1; } } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy