From 3e520e7e9f087ac14926f4fdd13cbb1a764e6316 Mon Sep 17 00:00:00 2001 From: Mark Hughes Date: Sat, 29 Apr 2017 12:47:17 +1000 Subject: [PATCH] Gson 2.8.0 --- .../xlib/gson/DefaultDateTypeAdapter.java | 19 +- .../xlib/gson/ExclusionStrategy.java | 4 +- .../xlib/gson/FieldAttributes.java | 2 +- .../xlib/gson/FieldNamingPolicy.java | 25 +- .../xlib/gson/FieldNamingStrategy.java | 2 +- .../massivecore/xlib/gson/Gson.java | 223 +++++++---- .../massivecore/xlib/gson/GsonBuilder.java | 42 ++- .../xlib/gson/InstanceCreator.java | 2 +- .../massivecore/xlib/gson/JsonArray.java | 38 +- .../xlib/gson/JsonDeserializationContext.java | 2 +- .../xlib/gson/JsonDeserializer.java | 2 +- .../massivecore/xlib/gson/JsonObject.java | 11 +- .../xlib/gson/JsonSerializationContext.java | 4 +- .../massivecore/xlib/gson/JsonSerializer.java | 4 +- .../xlib/gson/LongSerializationPolicy.java | 4 +- .../massivecore/xlib/gson/TypeAdapter.java | 18 +- .../xlib/gson/TypeAdapterFactory.java | 2 +- .../xlib/gson/annotations/Expose.java | 19 +- .../xlib/gson/annotations/JsonAdapter.java | 6 +- .../xlib/gson/annotations/SerializedName.java | 59 ++- .../xlib/gson/annotations/Since.java | 15 +- .../xlib/gson/annotations/Until.java | 15 +- .../gson/internal/$Gson$Preconditions.java | 4 + .../xlib/gson/internal/$Gson$Types.java | 23 +- .../gson/internal/ConstructorConstructor.java | 60 +-- .../xlib/gson/internal/Excluder.java | 8 +- .../gson/internal/LazilyParsedNumber.java | 20 +- .../xlib/gson/internal/LinkedHashTreeMap.java | 13 +- .../xlib/gson/internal/LinkedTreeMap.java | 14 +- .../xlib/gson/internal/ObjectConstructor.java | 2 +- .../xlib/gson/internal/Primitives.java | 4 +- .../xlib/gson/internal/Streams.java | 12 +- .../xlib/gson/internal/UnsafeAllocator.java | 19 + .../gson/internal/bind/ArrayTypeAdapter.java | 6 +- .../bind/CollectionTypeAdapterFactory.java | 11 +- .../gson/internal/bind/DateTypeAdapter.java | 17 +- ...onAdapterAnnotationTypeAdapterFactory.java | 51 ++- .../gson/internal/bind/JsonTreeReader.java | 144 +++++-- .../gson/internal/bind/JsonTreeWriter.java | 14 +- .../internal/bind/MapTypeAdapterFactory.java | 33 +- .../gson/internal/bind/ObjectTypeAdapter.java | 4 +- .../bind/ReflectiveTypeAdapterFactory.java | 98 ++--- .../internal/bind/SqlDateTypeAdapter.java | 2 +- .../gson/internal/bind/TimeTypeAdapter.java | 2 +- .../{ => internal/bind}/TreeTypeAdapter.java | 36 +- .../xlib/gson/internal/bind/TypeAdapters.java | 156 ++++++-- .../gson/internal/bind/util/ISO8601Utils.java | 352 ++++++++++++++++++ .../xlib/gson/reflect/TypeToken.java | 15 + .../xlib/gson/stream/JsonReader.java | 122 +++--- .../xlib/gson/stream/JsonWriter.java | 71 ++-- 50 files changed, 1280 insertions(+), 551 deletions(-) rename src/com/massivecraft/massivecore/xlib/gson/{ => internal/bind}/TreeTypeAdapter.java (79%) create mode 100644 src/com/massivecraft/massivecore/xlib/gson/internal/bind/util/ISO8601Utils.java diff --git a/src/com/massivecraft/massivecore/xlib/gson/DefaultDateTypeAdapter.java b/src/com/massivecraft/massivecore/xlib/gson/DefaultDateTypeAdapter.java index 42135081..c18043b7 100644 --- a/src/com/massivecraft/massivecore/xlib/gson/DefaultDateTypeAdapter.java +++ b/src/com/massivecraft/massivecore/xlib/gson/DefaultDateTypeAdapter.java @@ -16,14 +16,16 @@ package com.massivecraft.massivecore.xlib.gson; +import com.massivecraft.massivecore.xlib.gson.internal.bind.util.ISO8601Utils; + import java.lang.reflect.Type; import java.sql.Timestamp; import java.text.DateFormat; import java.text.ParseException; +import java.text.ParsePosition; import java.text.SimpleDateFormat; import java.util.Date; import java.util.Locale; -import java.util.TimeZone; /** * This type adapter supports three subclasses of date: Date, Timestamp, and @@ -38,7 +40,6 @@ final class DefaultDateTypeAdapter implements JsonSerializer, JsonDeserial private final DateFormat enUsFormat; private final DateFormat localFormat; - private final DateFormat iso8601Format; DefaultDateTypeAdapter() { this(DateFormat.getDateTimeInstance(DateFormat.DEFAULT, DateFormat.DEFAULT, Locale.US), @@ -61,12 +62,11 @@ final class DefaultDateTypeAdapter implements JsonSerializer, JsonDeserial DefaultDateTypeAdapter(DateFormat enUsFormat, DateFormat localFormat) { this.enUsFormat = enUsFormat; this.localFormat = localFormat; - this.iso8601Format = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'", Locale.US); - this.iso8601Format.setTimeZone(TimeZone.getTimeZone("UTC")); } // These methods need to be synchronized since JDK DateFormat classes are not thread-safe // See issue 162 + @Override public JsonElement serialize(Date src, Type typeOfSrc, JsonSerializationContext context) { synchronized (localFormat) { String dateFormatAsString = enUsFormat.format(src); @@ -74,6 +74,7 @@ final class DefaultDateTypeAdapter implements JsonSerializer, JsonDeserial } } + @Override public Date deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException { if (!(json instanceof JsonPrimitive)) { @@ -94,15 +95,13 @@ final class DefaultDateTypeAdapter implements JsonSerializer, JsonDeserial private Date deserializeToDate(JsonElement json) { synchronized (localFormat) { try { - return localFormat.parse(json.getAsString()); - } catch (ParseException ignored) { - } + return localFormat.parse(json.getAsString()); + } catch (ParseException ignored) {} try { return enUsFormat.parse(json.getAsString()); - } catch (ParseException ignored) { - } + } catch (ParseException ignored) {} try { - return iso8601Format.parse(json.getAsString()); + return ISO8601Utils.parse(json.getAsString(), new ParsePosition(0)); } catch (ParseException e) { throw new JsonSyntaxException(json.getAsString(), e); } diff --git a/src/com/massivecraft/massivecore/xlib/gson/ExclusionStrategy.java b/src/com/massivecraft/massivecore/xlib/gson/ExclusionStrategy.java index 150dbd6b..db2563f3 100644 --- a/src/com/massivecraft/massivecore/xlib/gson/ExclusionStrategy.java +++ b/src/com/massivecraft/massivecore/xlib/gson/ExclusionStrategy.java @@ -99,11 +99,11 @@ public interface ExclusionStrategy { * @param f the field object that is under test * @return true if the field should be ignored; otherwise false */ - boolean shouldSkipField(FieldAttributes f); + public boolean shouldSkipField(FieldAttributes f); /** * @param clazz the class object that is under test * @return true if the class should be ignored; otherwise false */ - boolean shouldSkipClass(Class clazz); + public boolean shouldSkipClass(Class clazz); } diff --git a/src/com/massivecraft/massivecore/xlib/gson/FieldAttributes.java b/src/com/massivecraft/massivecore/xlib/gson/FieldAttributes.java index d81de7dd..d02d699c 100644 --- a/src/com/massivecraft/massivecore/xlib/gson/FieldAttributes.java +++ b/src/com/massivecraft/massivecore/xlib/gson/FieldAttributes.java @@ -69,7 +69,7 @@ public final class FieldAttributes { * private List<String> red; * } * - * Type listParmeterizedType = new TypeToken<List<String>>() {}.getType(); + * Type listParameterizedType = new TypeToken<List<String>>() {}.getType(); * * *

This method would return {@code String.class} for the {@code bar} field and diff --git a/src/com/massivecraft/massivecore/xlib/gson/FieldNamingPolicy.java b/src/com/massivecraft/massivecore/xlib/gson/FieldNamingPolicy.java index cc22426a..00366f61 100644 --- a/src/com/massivecraft/massivecore/xlib/gson/FieldNamingPolicy.java +++ b/src/com/massivecraft/massivecore/xlib/gson/FieldNamingPolicy.java @@ -17,11 +17,12 @@ package com.massivecraft.massivecore.xlib.gson; import java.lang.reflect.Field; +import java.util.Locale; /** * An enumeration that defines a few standard naming conventions for JSON field names. - * This enumeration should be used in conjunction with {@link com.massivecraft.massivecore.xlib.gson.GsonBuilder} - * to configure a {@link com.massivecraft.massivecore.xlib.gson.Gson} instance to properly translate Java field + * This enumeration should be used in conjunction with {@link GsonBuilder} + * to configure a {@link Gson} instance to properly translate Java field * names into the desired JSON field names. * * @author Inderjeet Singh @@ -34,7 +35,7 @@ public enum FieldNamingPolicy implements FieldNamingStrategy { * unchanged. */ IDENTITY() { - public String translateName(Field f) { + @Override public String translateName(Field f) { return f.getName(); } }, @@ -50,7 +51,7 @@ public enum FieldNamingPolicy implements FieldNamingStrategy { * */ UPPER_CAMEL_CASE() { - public String translateName(Field f) { + @Override public String translateName(Field f) { return upperCaseFirstLetter(f.getName()); } }, @@ -69,7 +70,7 @@ public enum FieldNamingPolicy implements FieldNamingStrategy { * @since 1.4 */ UPPER_CAMEL_CASE_WITH_SPACES() { - public String translateName(Field f) { + @Override public String translateName(Field f) { return upperCaseFirstLetter(separateCamelCase(f.getName(), " ")); } }, @@ -87,8 +88,8 @@ public enum FieldNamingPolicy implements FieldNamingStrategy { * */ LOWER_CASE_WITH_UNDERSCORES() { - public String translateName(Field f) { - return separateCamelCase(f.getName(), "_").toLowerCase(); + @Override public String translateName(Field f) { + return separateCamelCase(f.getName(), "_").toLowerCase(Locale.ENGLISH); } }, @@ -110,8 +111,8 @@ public enum FieldNamingPolicy implements FieldNamingStrategy { * @since 1.4 */ LOWER_CASE_WITH_DASHES() { - public String translateName(Field f) { - return separateCamelCase(f.getName(), "-").toLowerCase(); + @Override public String translateName(Field f) { + return separateCamelCase(f.getName(), "-").toLowerCase(Locale.ENGLISH); } }; @@ -119,7 +120,7 @@ public enum FieldNamingPolicy implements FieldNamingStrategy { * Converts the field name that uses camel-case define word separation into * separate words that are separated by the provided {@code separatorString}. */ - private static String separateCamelCase(String name, String separator) { + static String separateCamelCase(String name, String separator) { StringBuilder translation = new StringBuilder(); for (int i = 0; i < name.length(); i++) { char character = name.charAt(i); @@ -134,7 +135,7 @@ public enum FieldNamingPolicy implements FieldNamingStrategy { /** * Ensures the JSON field names begins with an upper case letter. */ - private static String upperCaseFirstLetter(String name) { + static String upperCaseFirstLetter(String name) { StringBuilder fieldNameBuilder = new StringBuilder(); int index = 0; char firstCharacter = name.charAt(index); @@ -165,4 +166,4 @@ public enum FieldNamingPolicy implements FieldNamingStrategy { ? firstCharacter + srcString.substring(indexOfSubstring) : String.valueOf(firstCharacter); } -} \ No newline at end of file +} diff --git a/src/com/massivecraft/massivecore/xlib/gson/FieldNamingStrategy.java b/src/com/massivecraft/massivecore/xlib/gson/FieldNamingStrategy.java index d6f745ab..6fda689f 100644 --- a/src/com/massivecraft/massivecore/xlib/gson/FieldNamingStrategy.java +++ b/src/com/massivecraft/massivecore/xlib/gson/FieldNamingStrategy.java @@ -36,5 +36,5 @@ public interface FieldNamingStrategy { * @return the translated field name. * @since 1.3 */ - String translateName(Field f); + public String translateName(Field f); } diff --git a/src/com/massivecraft/massivecore/xlib/gson/Gson.java b/src/com/massivecraft/massivecore/xlib/gson/Gson.java index 81b22e4e..f5625471 100644 --- a/src/com/massivecraft/massivecore/xlib/gson/Gson.java +++ b/src/com/massivecraft/massivecore/xlib/gson/Gson.java @@ -16,47 +16,33 @@ package com.massivecraft.massivecore.xlib.gson; +import com.massivecraft.massivecore.xlib.gson.annotations.Expose; +import com.massivecraft.massivecore.xlib.gson.annotations.Since; import com.massivecraft.massivecore.xlib.gson.internal.ConstructorConstructor; import com.massivecraft.massivecore.xlib.gson.internal.Excluder; import com.massivecraft.massivecore.xlib.gson.internal.Primitives; import com.massivecraft.massivecore.xlib.gson.internal.Streams; -import com.massivecraft.massivecore.xlib.gson.internal.bind.ArrayTypeAdapter; -import com.massivecraft.massivecore.xlib.gson.internal.bind.CollectionTypeAdapterFactory; -import com.massivecraft.massivecore.xlib.gson.internal.bind.DateTypeAdapter; -import com.massivecraft.massivecore.xlib.gson.internal.bind.JsonAdapterAnnotationTypeAdapterFactory; -import com.massivecraft.massivecore.xlib.gson.internal.bind.JsonTreeReader; -import com.massivecraft.massivecore.xlib.gson.internal.bind.JsonTreeWriter; -import com.massivecraft.massivecore.xlib.gson.internal.bind.MapTypeAdapterFactory; -import com.massivecraft.massivecore.xlib.gson.internal.bind.ObjectTypeAdapter; -import com.massivecraft.massivecore.xlib.gson.internal.bind.ReflectiveTypeAdapterFactory; -import com.massivecraft.massivecore.xlib.gson.internal.bind.SqlDateTypeAdapter; -import com.massivecraft.massivecore.xlib.gson.internal.bind.TimeTypeAdapter; -import com.massivecraft.massivecore.xlib.gson.internal.bind.TypeAdapters; +import com.massivecraft.massivecore.xlib.gson.internal.bind.*; import com.massivecraft.massivecore.xlib.gson.reflect.TypeToken; import com.massivecraft.massivecore.xlib.gson.stream.JsonReader; import com.massivecraft.massivecore.xlib.gson.stream.JsonToken; import com.massivecraft.massivecore.xlib.gson.stream.JsonWriter; import com.massivecraft.massivecore.xlib.gson.stream.MalformedJsonException; -import java.io.EOFException; -import java.io.IOException; -import java.io.Reader; -import java.io.StringReader; -import java.io.StringWriter; -import java.io.Writer; +import java.io.*; import java.lang.reflect.Type; import java.math.BigDecimal; import java.math.BigInteger; -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.atomic.AtomicLong; +import java.util.concurrent.atomic.AtomicLongArray; /** * This is the main class for using Gson. Gson is typically used by first constructing a * Gson instance and then invoking {@link #toJson(Object)} or {@link #fromJson(String, Class)} - * methods on it. + * methods on it. Gson instances are Thread-safe so you can reuse them freely across multiple + * threads. * *

You can create a Gson instance by invoking {@code new Gson()} if the default configuration * is all you need. You can also use {@link GsonBuilder} to build a Gson instance with various @@ -75,7 +61,7 @@ import java.util.Map; *

If the object that your are serializing/deserializing is a {@code ParameterizedType} * (i.e. contains at least one type parameter and may be an array) then you must use the * {@link #toJson(Object, Type)} or {@link #fromJson(String, Type)} method. Here is an - * example for serializing and deserialing a {@code ParameterizedType}: + * example for serializing and deserializing a {@code ParameterizedType}: * *

  * Type listType = new TypeToken<List<String>>() {}.getType();
@@ -90,7 +76,7 @@ import java.util.Map;
  * 

See the Gson User Guide * for a more complete set of examples.

* - * @see com.massivecraft.massivecore.xlib.gson.reflect.TypeToken + * @see TypeToken * * @author Inderjeet Singh * @author Joel Leitch @@ -98,7 +84,14 @@ import java.util.Map; */ public final class Gson { static final boolean DEFAULT_JSON_NON_EXECUTABLE = false; + static final boolean DEFAULT_LENIENT = false; + static final boolean DEFAULT_PRETTY_PRINT = false; + static final boolean DEFAULT_ESCAPE_HTML = true; + static final boolean DEFAULT_SERIALIZE_NULLS = false; + static final boolean DEFAULT_COMPLEX_MAP_KEYS = false; + static final boolean DEFAULT_SPECIALIZE_FLOAT_VALUES = false; + private static final TypeToken NULL_KEY_SURROGATE = new TypeToken() {}; private static final String JSON_NON_EXECUTABLE_PREFIX = ")]}'\n"; /** @@ -111,32 +104,19 @@ public final class Gson { private final ThreadLocal, FutureTypeAdapter>> calls = new ThreadLocal<>(); - private final Map, TypeAdapter> typeTokenCache - = Collections.synchronizedMap(new HashMap, TypeAdapter>()); + private final Map, TypeAdapter> typeTokenCache = new ConcurrentHashMap<>(); private final List factories; private final ConstructorConstructor constructorConstructor; + private final Excluder excluder; + private final FieldNamingStrategy fieldNamingStrategy; private final boolean serializeNulls; private final boolean htmlSafe; private final boolean generateNonExecutableJson; private final boolean prettyPrinting; - - final JsonDeserializationContext deserializationContext = new JsonDeserializationContext() { - @SuppressWarnings("unchecked") - public T deserialize(JsonElement json, Type typeOfT) throws JsonParseException { - return (T) fromJson(json, typeOfT); - } - }; - - final JsonSerializationContext serializationContext = new JsonSerializationContext() { - public JsonElement serialize(Object src) { - return toJsonTree(src); - } - public JsonElement serialize(Object src, Type typeOfSrc) { - return toJsonTree(src, typeOfSrc); - } - }; + private final boolean lenient; + private final JsonAdapterAnnotationTypeAdapterFactory jsonAdapterFactory; /** * Constructs a Gson object with default configuration. The default configuration has the @@ -158,10 +138,10 @@ public final class Gson { * ignores the millisecond portion of the date during serialization. You can change * this by invoking {@link GsonBuilder#setDateFormat(int)} or * {@link GsonBuilder#setDateFormat(String)}. - *
  • By default, Gson ignores the {@link com.massivecraft.massivecore.xlib.gson.annotations.Expose} annotation. + *
  • By default, Gson ignores the {@link Expose} annotation. * You can enable Gson to serialize/deserialize only those fields marked with this annotation * through {@link GsonBuilder#excludeFieldsWithoutExposeAnnotation()}.
  • - *
  • By default, Gson ignores the {@link com.massivecraft.massivecore.xlib.gson.annotations.Since} annotation. You + *
  • By default, Gson ignores the {@link Since} annotation. You * can enable Gson to use this annotation through {@link GsonBuilder#setVersion(double)}.
  • *
  • The default field naming policy for the output Json is same as in Java. So, a Java class * field versionNumber will be output as "versionNumber" in @@ -174,22 +154,26 @@ public final class Gson { */ public Gson() { this(Excluder.DEFAULT, FieldNamingPolicy.IDENTITY, - Collections.>emptyMap(), false, false, DEFAULT_JSON_NON_EXECUTABLE, - true, false, false, LongSerializationPolicy.DEFAULT, - Collections.emptyList()); + Collections.>emptyMap(), DEFAULT_SERIALIZE_NULLS, + DEFAULT_COMPLEX_MAP_KEYS, DEFAULT_JSON_NON_EXECUTABLE, DEFAULT_ESCAPE_HTML, + DEFAULT_PRETTY_PRINT, DEFAULT_LENIENT, DEFAULT_SPECIALIZE_FLOAT_VALUES, + LongSerializationPolicy.DEFAULT, Collections.emptyList()); } - Gson(final Excluder excluder, final FieldNamingStrategy fieldNamingPolicy, - final Map> instanceCreators, boolean serializeNulls, - boolean complexMapKeySerialization, boolean generateNonExecutableGson, boolean htmlSafe, - boolean prettyPrinting, boolean serializeSpecialFloatingPointValues, - LongSerializationPolicy longSerializationPolicy, - List typeAdapterFactories) { + Gson(final Excluder excluder, final FieldNamingStrategy fieldNamingStrategy, + final Map> instanceCreators, boolean serializeNulls, + boolean complexMapKeySerialization, boolean generateNonExecutableGson, boolean htmlSafe, + boolean prettyPrinting, boolean lenient, boolean serializeSpecialFloatingPointValues, + LongSerializationPolicy longSerializationPolicy, + List typeAdapterFactories) { this.constructorConstructor = new ConstructorConstructor(instanceCreators); + this.excluder = excluder; + this.fieldNamingStrategy = fieldNamingStrategy; this.serializeNulls = serializeNulls; this.generateNonExecutableJson = generateNonExecutableGson; this.htmlSafe = htmlSafe; this.prettyPrinting = prettyPrinting; + this.lenient = lenient; List factories = new ArrayList<>(); @@ -209,13 +193,18 @@ public final class Gson { factories.add(TypeAdapters.BOOLEAN_FACTORY); factories.add(TypeAdapters.BYTE_FACTORY); factories.add(TypeAdapters.SHORT_FACTORY); - factories.add(TypeAdapters.newFactory(long.class, Long.class, - longAdapter(longSerializationPolicy))); + TypeAdapter longAdapter = longAdapter(longSerializationPolicy); + factories.add(TypeAdapters.newFactory(long.class, Long.class, longAdapter)); factories.add(TypeAdapters.newFactory(double.class, Double.class, doubleAdapter(serializeSpecialFloatingPointValues))); factories.add(TypeAdapters.newFactory(float.class, Float.class, floatAdapter(serializeSpecialFloatingPointValues))); factories.add(TypeAdapters.NUMBER_FACTORY); + factories.add(TypeAdapters.ATOMIC_INTEGER_FACTORY); + factories.add(TypeAdapters.ATOMIC_BOOLEAN_FACTORY); + factories.add(TypeAdapters.newFactory(AtomicLong.class, atomicLongAdapter(longAdapter))); + factories.add(TypeAdapters.newFactory(AtomicLongArray.class, atomicLongArrayAdapter(longAdapter))); + factories.add(TypeAdapters.ATOMIC_INTEGER_ARRAY_FACTORY); factories.add(TypeAdapters.CHARACTER_FACTORY); factories.add(TypeAdapters.STRING_BUILDER_FACTORY); factories.add(TypeAdapters.STRING_BUFFER_FACTORY); @@ -224,6 +213,7 @@ public final class Gson { factories.add(TypeAdapters.URL_FACTORY); factories.add(TypeAdapters.URI_FACTORY); factories.add(TypeAdapters.UUID_FACTORY); + factories.add(TypeAdapters.CURRENCY_FACTORY); factories.add(TypeAdapters.LOCALE_FACTORY); factories.add(TypeAdapters.INET_ADDRESS_FACTORY); factories.add(TypeAdapters.BIT_SET_FACTORY); @@ -238,14 +228,31 @@ public final class Gson { // type adapters for composite and user-defined types factories.add(new CollectionTypeAdapterFactory(constructorConstructor)); factories.add(new MapTypeAdapterFactory(constructorConstructor, complexMapKeySerialization)); - factories.add(new JsonAdapterAnnotationTypeAdapterFactory(constructorConstructor)); + this.jsonAdapterFactory = new JsonAdapterAnnotationTypeAdapterFactory(constructorConstructor); + factories.add(jsonAdapterFactory); factories.add(TypeAdapters.ENUM_FACTORY); factories.add(new ReflectiveTypeAdapterFactory( - constructorConstructor, fieldNamingPolicy, excluder)); + constructorConstructor, fieldNamingStrategy, excluder, jsonAdapterFactory)); this.factories = Collections.unmodifiableList(factories); } + public Excluder excluder() { + return excluder; + } + + public FieldNamingStrategy fieldNamingStrategy() { + return fieldNamingStrategy; + } + + public boolean serializeNulls() { + return serializeNulls; + } + + public boolean htmlSafe() { + return htmlSafe; + } + private TypeAdapter doubleAdapter(boolean serializeSpecialFloatingPointValues) { if (serializeSpecialFloatingPointValues) { return TypeAdapters.DOUBLE; @@ -294,7 +301,7 @@ public final class Gson { }; } - private void checkValidFloatingPoint(double value) { + static void checkValidFloatingPoint(double value) { if (Double.isNaN(value) || Double.isInfinite(value)) { throw new IllegalArgumentException(value + " is not a valid double value as per JSON specification. To override this" @@ -302,7 +309,7 @@ public final class Gson { } } - private TypeAdapter longAdapter(LongSerializationPolicy longSerializationPolicy) { + private static TypeAdapter longAdapter(LongSerializationPolicy longSerializationPolicy) { if (longSerializationPolicy == LongSerializationPolicy.DEFAULT) { return TypeAdapters.LONG; } @@ -324,6 +331,45 @@ public final class Gson { }; } + private static TypeAdapter atomicLongAdapter(final TypeAdapter longAdapter) { + return new TypeAdapter() { + @Override public void write(JsonWriter out, AtomicLong value) throws IOException { + longAdapter.write(out, value.get()); + } + @Override public AtomicLong read(JsonReader in) throws IOException { + Number value = longAdapter.read(in); + return new AtomicLong(value.longValue()); + } + }.nullSafe(); + } + + private static TypeAdapter atomicLongArrayAdapter(final TypeAdapter longAdapter) { + return new TypeAdapter() { + @Override public void write(JsonWriter out, AtomicLongArray value) throws IOException { + out.beginArray(); + for (int i = 0, length = value.length(); i < length; i++) { + longAdapter.write(out, value.get(i)); + } + out.endArray(); + } + @Override public AtomicLongArray read(JsonReader in) throws IOException { + List list = new ArrayList<>(); + in.beginArray(); + while (in.hasNext()) { + long value = longAdapter.read(in).longValue(); + list.add(value); + } + in.endArray(); + int length = list.size(); + AtomicLongArray array = new AtomicLongArray(length); + for (int i = 0; i < length; ++i) { + array.set(i, list.get(i)); + } + return array; + } + }.nullSafe(); + } + /** * Returns the type adapter for {@code} type. * @@ -332,7 +378,7 @@ public final class Gson { */ @SuppressWarnings("unchecked") public TypeAdapter getAdapter(TypeToken type) { - TypeAdapter cached = typeTokenCache.get(type); + TypeAdapter cached = typeTokenCache.get(type == null ? NULL_KEY_SURROGATE : type); if (cached != null) { return (TypeAdapter) cached; } @@ -386,9 +432,9 @@ public final class Gson { * class StatsTypeAdapterFactory implements TypeAdapterFactory { * public int numReads = 0; * public int numWrites = 0; - * public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type) { - * final TypeAdapter<T> delegate = gson.getDelegateAdapter(this, type); - * return new TypeAdapter<T>() { + * public TypeAdapter create(Gson gson, TypeToken type) { + * final TypeAdapter delegate = gson.getDelegateAdapter(this, type); + * return new TypeAdapter() { * public void write(JsonWriter out, T value) throws IOException { * ++numWrites; * delegate.write(out, value); @@ -409,6 +455,10 @@ public final class Gson { * System.out.println("Num JSON reads" + stats.numReads); * System.out.println("Num JSON writes" + stats.numWrites); * } + * Note that this call will skip all factories registered before {@code skipPast}. In case of + * multiple TypeAdapterFactories registered it is up to the caller of this function to insure + * that the order of registration does not prevent this method from reaching a factory they + * would expect to reply from this call. * Note that since you can not override type adapter factories for String and Java primitive * types, our stats factory will not count the number of String or primitives that will be * read or written. @@ -420,12 +470,13 @@ public final class Gson { * @since 2.2 */ public TypeAdapter getDelegateAdapter(TypeAdapterFactory skipPast, TypeToken type) { - boolean skipPastFound = false; - // Skip past if and only if the specified factory is present in the factories. - // This is useful because the factories created through JsonAdapter annotations are not - // registered in this list. - if (!factories.contains(skipPast)) skipPastFound = true; + // Hack. If the skipPast factory isn't registered, assume the factory is being requested via + // our @JsonAdapter annotation. + if (!factories.contains(skipPast)) { + skipPast = jsonAdapterFactory; + } + boolean skipPastFound = false; for (TypeAdapterFactory factory : factories) { if (!skipPastFound) { if (factory == skipPast) { @@ -480,7 +531,7 @@ public final class Gson { * * @param src the object for which JSON representation is to be created * @param typeOfSrc The specific genericized type of src. You can obtain - * this type by using the {@link com.massivecraft.massivecore.xlib.gson.reflect.TypeToken} class. For example, + * this type by using the {@link TypeToken} class. For example, * to get the type for {@code Collection}, you should use: *
        * Type typeOfSrc = new TypeToken<Collection<Foo>>(){}.getType();
    @@ -522,7 +573,7 @@ public final class Gson {
        *
        * @param src the object for which JSON representation is to be created
        * @param typeOfSrc The specific genericized type of src. You can obtain
    -   * this type by using the {@link com.massivecraft.massivecore.xlib.gson.reflect.TypeToken} class. For example,
    +   * this type by using the {@link TypeToken} class. For example,
        * to get the type for {@code Collection}, you should use:
        * 
        * Type typeOfSrc = new TypeToken<Collection<Foo>>(){}.getType();
    @@ -564,7 +615,7 @@ public final class Gson {
        *
        * @param src the object for which JSON representation is to be created
        * @param typeOfSrc The specific genericized type of src. You can obtain
    -   * this type by using the {@link com.massivecraft.massivecore.xlib.gson.reflect.TypeToken} class. For example,
    +   * this type by using the {@link TypeToken} class. For example,
        * to get the type for {@code Collection}, you should use:
        * 
        * Type typeOfSrc = new TypeToken<Collection<Foo>>(){}.getType();
    @@ -633,15 +684,14 @@ public final class Gson {
           JsonWriter jsonWriter = newJsonWriter(Streams.writerForAppendable(writer));
           toJson(jsonElement, jsonWriter);
         } catch (IOException e) {
    -      throw new RuntimeException(e);
    +      throw new JsonIOException(e);
         }
       }
     
       /**
    -   * Returns a new JSON writer configured for this GSON and with the non-execute
    -   * prefix if that is configured.
    +   * Returns a new JSON writer configured for the settings on this Gson instance.
        */
    -  private JsonWriter newJsonWriter(Writer writer) throws IOException {
    +  public JsonWriter newJsonWriter(Writer writer) throws IOException {
         if (generateNonExecutableJson) {
           writer.write(JSON_NON_EXECUTABLE_PREFIX);
         }
    @@ -653,6 +703,15 @@ public final class Gson {
         return jsonWriter;
       }
     
    +  /**
    +   * Returns a new JSON reader configured for the settings on this Gson instance.
    +   */
    +  public JsonReader newJsonReader(Reader reader) {
    +    JsonReader jsonReader = new JsonReader(reader);
    +    jsonReader.setLenient(lenient);
    +    return jsonReader;
    +  }
    +
       /**
        * Writes the JSON for {@code jsonElement} to {@code writer}.
        * @throws JsonIOException if there was a problem writing to the writer
    @@ -706,7 +765,7 @@ public final class Gson {
        * @param  the type of the desired object
        * @param json the string from which the object is to be deserialized
        * @param typeOfT The specific genericized type of src. You can obtain this type by using the
    -   * {@link com.massivecraft.massivecore.xlib.gson.reflect.TypeToken} class. For example, to get the type for
    +   * {@link TypeToken} class. For example, to get the type for
        * {@code Collection}, you should use:
        * 
        * Type typeOfT = new TypeToken<Collection<Foo>>(){}.getType();
    @@ -744,7 +803,7 @@ public final class Gson {
        * @since 1.2
        */
       public  T fromJson(Reader json, Class classOfT) throws JsonSyntaxException, JsonIOException {
    -    JsonReader jsonReader = new JsonReader(json);
    +    JsonReader jsonReader = newJsonReader(json);
         Object object = fromJson(jsonReader, classOfT);
         assertFullConsumption(object, jsonReader);
         return Primitives.wrap(classOfT).cast(object);
    @@ -759,7 +818,7 @@ public final class Gson {
        * @param  the type of the desired object
        * @param json the reader producing Json from which the object is to be deserialized
        * @param typeOfT The specific genericized type of src. You can obtain this type by using the
    -   * {@link com.massivecraft.massivecore.xlib.gson.reflect.TypeToken} class. For example, to get the type for
    +   * {@link TypeToken} class. For example, to get the type for
        * {@code Collection}, you should use:
        * 
        * Type typeOfT = new TypeToken<Collection<Foo>>(){}.getType();
    @@ -771,7 +830,7 @@ public final class Gson {
        */
       @SuppressWarnings("unchecked")
       public  T fromJson(Reader json, Type typeOfT) throws JsonIOException, JsonSyntaxException {
    -    JsonReader jsonReader = new JsonReader(json);
    +    JsonReader jsonReader = newJsonReader(json);
         T object = (T) fromJson(jsonReader, typeOfT);
         assertFullConsumption(object, jsonReader);
         return object;
    @@ -858,7 +917,7 @@ public final class Gson {
        * @param json the root of the parse tree of {@link JsonElement}s from which the object is to
        * be deserialized
        * @param typeOfT The specific genericized type of src. You can obtain this type by using the
    -   * {@link com.massivecraft.massivecore.xlib.gson.reflect.TypeToken} class. For example, to get the type for
    +   * {@link TypeToken} class. For example, to get the type for
        * {@code Collection}, you should use:
        * 
        * Type typeOfT = new TypeToken<Collection<Foo>>(){}.getType();
    diff --git a/src/com/massivecraft/massivecore/xlib/gson/GsonBuilder.java b/src/com/massivecraft/massivecore/xlib/gson/GsonBuilder.java
    index d1727108..ad0db77a 100644
    --- a/src/com/massivecraft/massivecore/xlib/gson/GsonBuilder.java
    +++ b/src/com/massivecraft/massivecore/xlib/gson/GsonBuilder.java
    @@ -16,20 +16,18 @@
     
     package com.massivecraft.massivecore.xlib.gson;
     
    +import com.massivecraft.massivecore.xlib.gson.annotations.Expose;
     import com.massivecraft.massivecore.xlib.gson.internal.$Gson$Preconditions;
     import com.massivecraft.massivecore.xlib.gson.internal.Excluder;
    +import com.massivecraft.massivecore.xlib.gson.internal.bind.TreeTypeAdapter;
     import com.massivecraft.massivecore.xlib.gson.internal.bind.TypeAdapters;
     import com.massivecraft.massivecore.xlib.gson.reflect.TypeToken;
    +import com.massivecraft.massivecore.xlib.gson.stream.JsonReader;
     
     import java.lang.reflect.Type;
     import java.sql.Timestamp;
     import java.text.DateFormat;
    -import java.util.ArrayList;
    -import java.util.Collections;
    -import java.util.Date;
    -import java.util.HashMap;
    -import java.util.List;
    -import java.util.Map;
    +import java.util.*;
     
     /**
      * 

    Use this builder to construct a {@link Gson} instance when you need to set configuration @@ -74,15 +72,16 @@ public final class GsonBuilder { private final List factories = new ArrayList<>(); /** tree-style hierarchy factories. These come after factories for backwards compatibility. */ private final List hierarchyFactories = new ArrayList<>(); - private boolean serializeNulls; + private boolean serializeNulls = Gson.DEFAULT_SERIALIZE_NULLS; private String datePattern; private int dateStyle = DateFormat.DEFAULT; private int timeStyle = DateFormat.DEFAULT; - private boolean complexMapKeySerialization; - private boolean serializeSpecialFloatingPointValues; - private boolean escapeHtmlChars = true; - private boolean prettyPrinting; - private boolean generateNonExecutableJson; + private boolean complexMapKeySerialization = Gson.DEFAULT_COMPLEX_MAP_KEYS; + private boolean serializeSpecialFloatingPointValues = Gson.DEFAULT_SPECIALIZE_FLOAT_VALUES; + private boolean escapeHtmlChars = Gson.DEFAULT_ESCAPE_HTML; + private boolean prettyPrinting = Gson.DEFAULT_PRETTY_PRINT; + private boolean generateNonExecutableJson = Gson.DEFAULT_JSON_NON_EXECUTABLE; + private boolean lenient = Gson.DEFAULT_LENIENT; /** * Creates a GsonBuilder instance that can be used to build Gson with various configuration @@ -137,7 +136,7 @@ public final class GsonBuilder { /** * Configures Gson to exclude all fields from consideration for serialization or deserialization - * that do not have the {@link com.massivecraft.massivecore.xlib.gson.annotations.Expose} annotation. + * that do not have the {@link Expose} annotation. * * @return a reference to this {@code GsonBuilder} object to fulfill the "Builder" pattern */ @@ -293,7 +292,7 @@ public final class GsonBuilder { * Configures Gson to apply a set of exclusion strategies during both serialization and * deserialization. Each of the {@code strategies} will be applied as a disjunction rule. * This means that if one of the {@code strategies} suggests that a field (or class) should be - * skipped then that field (or object) is skipped during serializaiton/deserialization. + * skipped then that field (or object) is skipped during serialization/deserialization. * * @param strategies the set of strategy object to apply during object (de)serialization. * @return a reference to this {@code GsonBuilder} object to fulfill the "Builder" pattern @@ -351,6 +350,19 @@ public final class GsonBuilder { return this; } + /** + * By default, Gson is strict and only accepts JSON as specified by + * RFC 4627. This option makes the parser + * liberal in what it accepts. + * + * @return a reference to this {@code GsonBuilder} object to fulfill the "Builder" pattern + * @see JsonReader#setLenient(boolean) + */ + public GsonBuilder setLenient() { + lenient = true; + return this; + } + /** * By default, Gson escapes HTML characters such as < > etc. Use this option to configure * Gson to pass-through HTML characters as is. @@ -544,7 +556,7 @@ public final class GsonBuilder { return new Gson(excluder, fieldNamingPolicy, instanceCreators, serializeNulls, complexMapKeySerialization, - generateNonExecutableJson, escapeHtmlChars, prettyPrinting, + generateNonExecutableJson, escapeHtmlChars, prettyPrinting, lenient, serializeSpecialFloatingPointValues, longSerializationPolicy, factories); } diff --git a/src/com/massivecraft/massivecore/xlib/gson/InstanceCreator.java b/src/com/massivecraft/massivecore/xlib/gson/InstanceCreator.java index 926b9339..cb9d25e5 100644 --- a/src/com/massivecraft/massivecore/xlib/gson/InstanceCreator.java +++ b/src/com/massivecraft/massivecore/xlib/gson/InstanceCreator.java @@ -88,5 +88,5 @@ public interface InstanceCreator { * @param type the parameterized T represented as a {@link Type}. * @return a default object instance of type T. */ - T createInstance(Type type); + public T createInstance(Type type); } diff --git a/src/com/massivecraft/massivecore/xlib/gson/JsonArray.java b/src/com/massivecraft/massivecore/xlib/gson/JsonArray.java index f0a15896..d300a8f7 100644 --- a/src/com/massivecraft/massivecore/xlib/gson/JsonArray.java +++ b/src/com/massivecraft/massivecore/xlib/gson/JsonArray.java @@ -49,6 +49,42 @@ public final class JsonArray extends JsonElement implements Iterable T deserialize(JsonElement json, Type typeOfT) throws JsonParseException; + public T deserialize(JsonElement json, Type typeOfT) throws JsonParseException; } \ No newline at end of file diff --git a/src/com/massivecraft/massivecore/xlib/gson/JsonDeserializer.java b/src/com/massivecraft/massivecore/xlib/gson/JsonDeserializer.java index d1b92ad3..b4e1d6e1 100644 --- a/src/com/massivecraft/massivecore/xlib/gson/JsonDeserializer.java +++ b/src/com/massivecraft/massivecore/xlib/gson/JsonDeserializer.java @@ -86,6 +86,6 @@ public interface JsonDeserializer { * @return a deserialized object of the specified type typeOfT which is a subclass of {@code T} * @throws JsonParseException if json is not in the expected format of {@code typeofT} */ - T deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) + public T deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException; } diff --git a/src/com/massivecraft/massivecore/xlib/gson/JsonObject.java b/src/com/massivecraft/massivecore/xlib/gson/JsonObject.java index d9a2e931..2420fcfd 100644 --- a/src/com/massivecraft/massivecore/xlib/gson/JsonObject.java +++ b/src/com/massivecraft/massivecore/xlib/gson/JsonObject.java @@ -31,7 +31,7 @@ import java.util.Set; */ public final class JsonObject extends JsonElement { private final LinkedTreeMap members = - new LinkedTreeMap<>(); + new LinkedTreeMap<>(); @Override JsonObject deepCopy() { @@ -132,6 +132,15 @@ public final class JsonObject extends JsonElement { return members.entrySet(); } + /** + * Returns the number of key/value pairs in the object. + * + * @return the number of key/value pairs in the object. + */ + public int size() { + return members.size(); + } + /** * Convenience method to check if a member with the specified name is present in this object. * diff --git a/src/com/massivecraft/massivecore/xlib/gson/JsonSerializationContext.java b/src/com/massivecraft/massivecore/xlib/gson/JsonSerializationContext.java index f4671253..ed310f08 100644 --- a/src/com/massivecraft/massivecore/xlib/gson/JsonSerializationContext.java +++ b/src/com/massivecraft/massivecore/xlib/gson/JsonSerializationContext.java @@ -33,7 +33,7 @@ public interface JsonSerializationContext { * @param src the object that needs to be serialized. * @return a tree of {@link JsonElement}s corresponding to the serialized form of {@code src}. */ - JsonElement serialize(Object src); + public JsonElement serialize(Object src); /** * Invokes default serialization on the specified object passing the specific type information. @@ -45,5 +45,5 @@ public interface JsonSerializationContext { * @param typeOfSrc the actual genericized type of src object. * @return a tree of {@link JsonElement}s corresponding to the serialized form of {@code src}. */ - JsonElement serialize(Object src, Type typeOfSrc); + public JsonElement serialize(Object src, Type typeOfSrc); } diff --git a/src/com/massivecraft/massivecore/xlib/gson/JsonSerializer.java b/src/com/massivecraft/massivecore/xlib/gson/JsonSerializer.java index 2f4d78e3..54faa562 100644 --- a/src/com/massivecraft/massivecore/xlib/gson/JsonSerializer.java +++ b/src/com/massivecraft/massivecore/xlib/gson/JsonSerializer.java @@ -21,7 +21,7 @@ import java.lang.reflect.Type; /** * Interface representing a custom serializer for Json. You should write a custom serializer, if * you are not happy with the default serialization done by Gson. You will also need to register - * this serializer through {@link com.massivecraft.massivecore.xlib.gson.GsonBuilder#registerTypeAdapter(Type, Object)}. + * this serializer through {@link GsonBuilder#registerTypeAdapter(Type, Object)}. * *

    Let us look at example where defining a serializer will be useful. The {@code Id} class * defined below has two fields: {@code clazz} and {@code value}.

    @@ -85,5 +85,5 @@ public interface JsonSerializer { * @param typeOfSrc the actual type (fully genericized version) of the source object. * @return a JsonElement corresponding to the specified object. */ - JsonElement serialize(T src, Type typeOfSrc, JsonSerializationContext context); + public JsonElement serialize(T src, Type typeOfSrc, JsonSerializationContext context); } diff --git a/src/com/massivecraft/massivecore/xlib/gson/LongSerializationPolicy.java b/src/com/massivecraft/massivecore/xlib/gson/LongSerializationPolicy.java index b82bf501..1c43b929 100644 --- a/src/com/massivecraft/massivecore/xlib/gson/LongSerializationPolicy.java +++ b/src/com/massivecraft/massivecore/xlib/gson/LongSerializationPolicy.java @@ -32,7 +32,7 @@ public enum LongSerializationPolicy { * {@code {"f":123}}. */ DEFAULT() { - public JsonElement serialize(Long value) { + @Override public JsonElement serialize(Long value) { return new JsonPrimitive(value); } }, @@ -43,7 +43,7 @@ public enum LongSerializationPolicy { * {@code {"f":"123"}}. */ STRING() { - public JsonElement serialize(Long value) { + @Override public JsonElement serialize(Long value) { return new JsonPrimitive(String.valueOf(value)); } }; diff --git a/src/com/massivecraft/massivecore/xlib/gson/TypeAdapter.java b/src/com/massivecraft/massivecore/xlib/gson/TypeAdapter.java index b49f9dde..dca170e8 100644 --- a/src/com/massivecraft/massivecore/xlib/gson/TypeAdapter.java +++ b/src/com/massivecraft/massivecore/xlib/gson/TypeAdapter.java @@ -22,11 +22,7 @@ import com.massivecraft.massivecore.xlib.gson.stream.JsonReader; import com.massivecraft.massivecore.xlib.gson.stream.JsonToken; import com.massivecraft.massivecore.xlib.gson.stream.JsonWriter; -import java.io.IOException; -import java.io.Reader; -import java.io.StringReader; -import java.io.StringWriter; -import java.io.Writer; +import java.io.*; /** * Converts Java objects to and from JSON. @@ -132,7 +128,7 @@ public abstract class TypeAdapter { * Unlike Gson's similar {@link Gson#toJson(JsonElement, Appendable) toJson} * method, this write is strict. Create a {@link * JsonWriter#setLenient(boolean) lenient} {@code JsonWriter} and call - * {@link #write(com.massivecraft.massivecore.xlib.gson.stream.JsonWriter, Object)} for lenient + * {@link #write(JsonWriter, Object)} for lenient * writing. * * @param value the Java object to convert. May be null. @@ -206,15 +202,19 @@ public abstract class TypeAdapter { * Converts {@code value} to a JSON document. Unlike Gson's similar {@link * Gson#toJson(Object) toJson} method, this write is strict. Create a {@link * JsonWriter#setLenient(boolean) lenient} {@code JsonWriter} and call - * {@link #write(com.massivecraft.massivecore.xlib.gson.stream.JsonWriter, Object)} for lenient + * {@link #write(JsonWriter, Object)} for lenient * writing. * * @param value the Java object to convert. May be null. * @since 2.2 */ - public final String toJson(T value) throws IOException { + public final String toJson(T value) { StringWriter stringWriter = new StringWriter(); - toJson(stringWriter, value); + try { + toJson(stringWriter, value); + } catch (IOException e) { + throw new AssertionError(e); // No I/O writing to a StringWriter. + } return stringWriter.toString(); } diff --git a/src/com/massivecraft/massivecore/xlib/gson/TypeAdapterFactory.java b/src/com/massivecraft/massivecore/xlib/gson/TypeAdapterFactory.java index f0bf420f..6d531938 100644 --- a/src/com/massivecraft/massivecore/xlib/gson/TypeAdapterFactory.java +++ b/src/com/massivecraft/massivecore/xlib/gson/TypeAdapterFactory.java @@ -80,7 +80,7 @@ import com.massivecraft.massivecore.xlib.gson.reflect.TypeToken; * mapping from lowercase name to enum value is computed eagerly. * *

    As with type adapters, factories must be registered with a {@link - * com.massivecraft.massivecore.xlib.gson.GsonBuilder} for them to take effect:

       {@code
    + * GsonBuilder} for them to take effect: 
       {@code
      *
      *  GsonBuilder builder = new GsonBuilder();
      *  builder.registerTypeAdapterFactory(new LowercaseEnumTypeAdapterFactory());
    diff --git a/src/com/massivecraft/massivecore/xlib/gson/annotations/Expose.java b/src/com/massivecraft/massivecore/xlib/gson/annotations/Expose.java
    index 0e29f08a..18bba08b 100644
    --- a/src/com/massivecraft/massivecore/xlib/gson/annotations/Expose.java
    +++ b/src/com/massivecraft/massivecore/xlib/gson/annotations/Expose.java
    @@ -16,18 +16,18 @@
     
     package com.massivecraft.massivecore.xlib.gson.annotations;
     
    -import java.lang.annotation.ElementType;
    -import java.lang.annotation.Retention;
    -import java.lang.annotation.RetentionPolicy;
    -import java.lang.annotation.Target;
    +import com.massivecraft.massivecore.xlib.gson.Gson;
    +import com.massivecraft.massivecore.xlib.gson.GsonBuilder;
    +
    +import java.lang.annotation.*;
     
     /**
      * An annotation that indicates this member should be exposed for JSON
      * serialization or deserialization.
      *
    - * 

    This annotation has no effect unless you build {@link com.massivecraft.massivecore.xlib.gson.Gson} - * with a {@link com.massivecraft.massivecore.xlib.gson.GsonBuilder} and invoke - * {@link com.massivecraft.massivecore.xlib.gson.GsonBuilder#excludeFieldsWithoutExposeAnnotation()} + *

    This annotation has no effect unless you build {@link Gson} + * with a {@link GsonBuilder} and invoke + * {@link GsonBuilder#excludeFieldsWithoutExposeAnnotation()} * method.

    * *

    Here is an example of how this annotation is meant to be used: @@ -57,6 +57,7 @@ import java.lang.annotation.Target; * @author Inderjeet Singh * @author Joel Leitch */ +@Documented @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.FIELD) public @interface Expose { @@ -67,7 +68,7 @@ public @interface Expose { * serialized output. Defaults to {@code true}. * @since 1.4 */ - boolean serialize() default true; + public boolean serialize() default true; /** * If {@code true}, the field marked with this annotation is deserialized from the JSON. @@ -75,5 +76,5 @@ public @interface Expose { * Defaults to {@code true}. * @since 1.4 */ - boolean deserialize() default true; + public boolean deserialize() default true; } diff --git a/src/com/massivecraft/massivecore/xlib/gson/annotations/JsonAdapter.java b/src/com/massivecraft/massivecore/xlib/gson/annotations/JsonAdapter.java index 16bb7d7e..b7e4e02e 100644 --- a/src/com/massivecraft/massivecore/xlib/gson/annotations/JsonAdapter.java +++ b/src/com/massivecraft/massivecore/xlib/gson/annotations/JsonAdapter.java @@ -16,6 +16,7 @@ package com.massivecraft.massivecore.xlib.gson.annotations; +import com.massivecraft.massivecore.xlib.gson.GsonBuilder; import com.massivecraft.massivecore.xlib.gson.TypeAdapter; import com.massivecraft.massivecore.xlib.gson.TypeAdapterFactory; @@ -73,7 +74,7 @@ import java.lang.annotation.Target; *

    * * It's possible to specify different type adapters on a field, that - * field's type, and in the {@link com.massivecraft.massivecore.xlib.gson.GsonBuilder}. Field + * field's type, and in the {@link GsonBuilder}. Field * annotations take precedence over {@code GsonBuilder}-registered type * adapters, which in turn take precedence over annotated types. * @@ -95,4 +96,7 @@ public @interface JsonAdapter { /** Either a {@link TypeAdapter} or {@link TypeAdapterFactory}. */ Class value(); + /** false, to be able to handle {@code null} values within the adapter, default value is true. */ + boolean nullSafe() default true; + } diff --git a/src/com/massivecraft/massivecore/xlib/gson/annotations/SerializedName.java b/src/com/massivecraft/massivecore/xlib/gson/annotations/SerializedName.java index 57049e2a..aa0edcbf 100644 --- a/src/com/massivecraft/massivecore/xlib/gson/annotations/SerializedName.java +++ b/src/com/massivecraft/massivecore/xlib/gson/annotations/SerializedName.java @@ -16,30 +16,33 @@ package com.massivecraft.massivecore.xlib.gson.annotations; -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; +import com.massivecraft.massivecore.xlib.gson.FieldNamingPolicy; +import com.massivecraft.massivecore.xlib.gson.Gson; +import com.massivecraft.massivecore.xlib.gson.GsonBuilder; + +import java.lang.annotation.*; /** * An annotation that indicates this member should be serialized to JSON with * the provided name value as its field name. * - *

    This annotation will override any {@link com.massivecraft.massivecore.xlib.gson.FieldNamingPolicy}, including - * the default field naming policy, that may have been set on the {@link com.massivecraft.massivecore.xlib.gson.Gson} + *

    This annotation will override any {@link FieldNamingPolicy}, including + * the default field naming policy, that may have been set on the {@link Gson} * instance. A different naming policy can set using the {@code GsonBuilder} class. See - * {@link com.massivecraft.massivecore.xlib.gson.GsonBuilder#setFieldNamingPolicy(com.massivecraft.massivecore.xlib.gson.FieldNamingPolicy)} + * {@link GsonBuilder#setFieldNamingPolicy(FieldNamingPolicy)} * for more information.

    * *

    Here is an example of how this annotation is meant to be used:

    *
    - * public class SomeClassWithFields {
    - *   @SerializedName("name") private final String someField;
    - *   private final String someOtherField;
    + * public class MyClass {
    + *   @SerializedName("name") String a;
    + *   @SerializedName(value="name1", alternate={"name2", "name3"}) String b;
    + *   String c;
      *
    - *   public SomeClassWithFields(String a, String b) {
    - *     this.someField = a;
    - *     this.someOtherField = b;
    + *   public MyClass(String a, String b, String c) {
    + *     this.a = a;
    + *     this.b = b;
    + *     this.c = c;
      *   }
      * }
      * 
    @@ -47,28 +50,44 @@ import java.lang.annotation.Target; *

    The following shows the output that is generated when serializing an instance of the * above example class:

    *
    - * SomeClassWithFields objectToSerialize = new SomeClassWithFields("a", "b");
    + * MyClass target = new MyClass("v1", "v2", "v3");
      * Gson gson = new Gson();
    - * String jsonRepresentation = gson.toJson(objectToSerialize);
    - * System.out.println(jsonRepresentation);
    + * String json = gson.toJson(target);
    + * System.out.println(json);
      *
      * ===== OUTPUT =====
    - * {"name":"a","someOtherField":"b"}
    + * {"name":"v1","name1":"v2","c":"v3"}
      * 
    * *

    NOTE: The value you specify in this annotation must be a valid JSON field name.

    + * While deserializing, all values specified in the annotation will be deserialized into the field. + * For example: + *
    + *   MyClass target = gson.fromJson("{'name1':'v1'}", MyClass.class);
    + *   assertEquals("v1", target.b);
    + *   target = gson.fromJson("{'name2':'v2'}", MyClass.class);
    + *   assertEquals("v2", target.b);
    + *   target = gson.fromJson("{'name3':'v3'}", MyClass.class);
    + *   assertEquals("v3", target.b);
    + * 
    + * Note that MyClass.b is now deserialized from either name1, name2 or name3. * - * @see com.massivecraft.massivecore.xlib.gson.FieldNamingPolicy + * @see FieldNamingPolicy * * @author Inderjeet Singh * @author Joel Leitch */ +@Documented @Retention(RetentionPolicy.RUNTIME) -@Target(ElementType.FIELD) +@Target({ElementType.FIELD, ElementType.METHOD}) public @interface SerializedName { /** - * @return the desired name of the field when it is serialized + * @return the desired name of the field when it is serialized or deserialized */ String value(); + /** + * @return the alternative names of the field when it is deserialized + */ + String[] alternate() default {}; } diff --git a/src/com/massivecraft/massivecore/xlib/gson/annotations/Since.java b/src/com/massivecraft/massivecore/xlib/gson/annotations/Since.java index b2a3005a..99be8cc1 100644 --- a/src/com/massivecraft/massivecore/xlib/gson/annotations/Since.java +++ b/src/com/massivecraft/massivecore/xlib/gson/annotations/Since.java @@ -16,19 +16,19 @@ package com.massivecraft.massivecore.xlib.gson.annotations; -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; +import com.massivecraft.massivecore.xlib.gson.Gson; +import com.massivecraft.massivecore.xlib.gson.GsonBuilder; + +import java.lang.annotation.*; /** * An annotation that indicates the version number since a member or a type has been present. * This annotation is useful to manage versioning of your Json classes for a web-service. * *

    - * This annotation has no effect unless you build {@link com.massivecraft.massivecore.xlib.gson.Gson} with a - * {@link com.massivecraft.massivecore.xlib.gson.GsonBuilder} and invoke - * {@link com.massivecraft.massivecore.xlib.gson.GsonBuilder#setVersion(double)} method. + * This annotation has no effect unless you build {@link Gson} with a + * {@link GsonBuilder} and invoke + * {@link GsonBuilder#setVersion(double)} method. * *

    Here is an example of how this annotation is meant to be used:

    *
    @@ -50,6 +50,7 @@ import java.lang.annotation.Target;
      * @author Inderjeet Singh
      * @author Joel Leitch
      */
    +@Documented
     @Retention(RetentionPolicy.RUNTIME)
     @Target({ElementType.FIELD, ElementType.TYPE})
     public @interface Since {
    diff --git a/src/com/massivecraft/massivecore/xlib/gson/annotations/Until.java b/src/com/massivecraft/massivecore/xlib/gson/annotations/Until.java
    index 9686f1ee..8ae2c133 100644
    --- a/src/com/massivecraft/massivecore/xlib/gson/annotations/Until.java
    +++ b/src/com/massivecraft/massivecore/xlib/gson/annotations/Until.java
    @@ -16,10 +16,10 @@
     
     package com.massivecraft.massivecore.xlib.gson.annotations;
     
    -import java.lang.annotation.ElementType;
    -import java.lang.annotation.Retention;
    -import java.lang.annotation.RetentionPolicy;
    -import java.lang.annotation.Target;
    +import com.massivecraft.massivecore.xlib.gson.Gson;
    +import com.massivecraft.massivecore.xlib.gson.GsonBuilder;
    +
    +import java.lang.annotation.*;
     
     /**
      * An annotation that indicates the version number until a member or a type should be present.
    @@ -28,9 +28,9 @@ import java.lang.annotation.Target;
      * is useful to manage versioning of your JSON classes for a web-service.
      *
      * 

    - * This annotation has no effect unless you build {@link com.massivecraft.massivecore.xlib.gson.Gson} with a - * {@link com.massivecraft.massivecore.xlib.gson.GsonBuilder} and invoke - * {@link com.massivecraft.massivecore.xlib.gson.GsonBuilder#setVersion(double)} method. + * This annotation has no effect unless you build {@link Gson} with a + * {@link GsonBuilder} and invoke + * {@link GsonBuilder#setVersion(double)} method. * *

    Here is an example of how this annotation is meant to be used:

    *
    @@ -54,6 +54,7 @@ import java.lang.annotation.Target;
      * @author Joel Leitch
      * @since 1.3
      */
    +@Documented
     @Retention(RetentionPolicy.RUNTIME)
     @Target({ElementType.FIELD, ElementType.TYPE})
     public @interface Until {
    diff --git a/src/com/massivecraft/massivecore/xlib/gson/internal/$Gson$Preconditions.java b/src/com/massivecraft/massivecore/xlib/gson/internal/$Gson$Preconditions.java
    index 795b9ddc..dcea08d7 100644
    --- a/src/com/massivecraft/massivecore/xlib/gson/internal/$Gson$Preconditions.java
    +++ b/src/com/massivecraft/massivecore/xlib/gson/internal/$Gson$Preconditions.java
    @@ -30,6 +30,10 @@ package com.massivecraft.massivecore.xlib.gson.internal;
      * @author Joel Leitch
      */
     public final class $Gson$Preconditions {
    +  private $Gson$Preconditions() {
    +    throw new UnsupportedOperationException();
    +  }
    +
       public static  T checkNotNull(T obj) {
         if (obj == null) {
           throw new NullPointerException();
    diff --git a/src/com/massivecraft/massivecore/xlib/gson/internal/$Gson$Types.java b/src/com/massivecraft/massivecore/xlib/gson/internal/$Gson$Types.java
    index 0534f3c0..b192889c 100644
    --- a/src/com/massivecraft/massivecore/xlib/gson/internal/$Gson$Types.java
    +++ b/src/com/massivecraft/massivecore/xlib/gson/internal/$Gson$Types.java
    @@ -17,19 +17,8 @@
     package com.massivecraft.massivecore.xlib.gson.internal;
     
     import java.io.Serializable;
    -import java.lang.reflect.Array;
    -import java.lang.reflect.GenericArrayType;
    -import java.lang.reflect.GenericDeclaration;
    -import java.lang.reflect.Modifier;
    -import java.lang.reflect.ParameterizedType;
    -import java.lang.reflect.Type;
    -import java.lang.reflect.TypeVariable;
    -import java.lang.reflect.WildcardType;
    -import java.util.Arrays;
    -import java.util.Collection;
    -import java.util.Map;
    -import java.util.NoSuchElementException;
    -import java.util.Properties;
    +import java.lang.reflect.*;
    +import java.util.*;
     
     import static com.massivecraft.massivecore.xlib.gson.internal.$Gson$Preconditions.checkArgument;
     import static com.massivecraft.massivecore.xlib.gson.internal.$Gson$Preconditions.checkNotNull;
    @@ -43,7 +32,9 @@ import static com.massivecraft.massivecore.xlib.gson.internal.$Gson$Precondition
     public final class $Gson$Types {
       static final Type[] EMPTY_TYPE_ARRAY = new Type[] {};
     
    -  private $Gson$Types() {}
    +  private $Gson$Types() {
    +    throw new UnsupportedOperationException();
    +  }
     
       /**
        * Returns a new parameterized type, applying {@code typeArguments} to
    @@ -210,7 +201,7 @@ public final class $Gson$Types {
         }
       }
     
    -  private static int hashCodeOrZero(Object o) {
    +  static int hashCodeOrZero(Object o) {
         return o != null ? o.hashCode() : 0;
       }
     
    @@ -428,7 +419,7 @@ public final class $Gson$Types {
             : null;
       }
     
    -  private static void checkNotPrimitive(Type type) {
    +  static void checkNotPrimitive(Type type) {
         checkArgument(!(type instanceof Class) || !((Class) type).isPrimitive());
       }
     
    diff --git a/src/com/massivecraft/massivecore/xlib/gson/internal/ConstructorConstructor.java b/src/com/massivecraft/massivecore/xlib/gson/internal/ConstructorConstructor.java
    index 79ca0e1b..e76118af 100644
    --- a/src/com/massivecraft/massivecore/xlib/gson/internal/ConstructorConstructor.java
    +++ b/src/com/massivecraft/massivecore/xlib/gson/internal/ConstructorConstructor.java
    @@ -24,19 +24,11 @@ import java.lang.reflect.Constructor;
     import java.lang.reflect.InvocationTargetException;
     import java.lang.reflect.ParameterizedType;
     import java.lang.reflect.Type;
    -import java.util.ArrayList;
    -import java.util.Collection;
    -import java.util.EnumSet;
    -import java.util.LinkedHashMap;
    -import java.util.LinkedHashSet;
    -import java.util.LinkedList;
    -import java.util.Map;
    -import java.util.Queue;
    -import java.util.Set;
    -import java.util.SortedMap;
    -import java.util.SortedSet;
    -import java.util.TreeMap;
    -import java.util.TreeSet;
    +import java.util.*;
    +import java.util.concurrent.ConcurrentHashMap;
    +import java.util.concurrent.ConcurrentMap;
    +import java.util.concurrent.ConcurrentNavigableMap;
    +import java.util.concurrent.ConcurrentSkipListMap;
     
     /**
      * Returns a function that can construct an instance of a requested type.
    @@ -58,7 +50,7 @@ public final class ConstructorConstructor {
         final InstanceCreator typeCreator = (InstanceCreator) instanceCreators.get(type);
         if (typeCreator != null) {
           return new ObjectConstructor() {
    -        public T construct() {
    +        @Override public T construct() {
               return typeCreator.createInstance(type);
             }
           };
    @@ -70,7 +62,7 @@ public final class ConstructorConstructor {
             (InstanceCreator) instanceCreators.get(rawType);
         if (rawTypeCreator != null) {
           return new ObjectConstructor() {
    -        public T construct() {
    +        @Override public T construct() {
               return rawTypeCreator.createInstance(type);
             }
           };
    @@ -98,7 +90,7 @@ public final class ConstructorConstructor {
           }
           return new ObjectConstructor() {
             @SuppressWarnings("unchecked") // T is the same raw type as is requested
    -        public T construct() {
    +        @Override public T construct() {
               try {
                 Object[] args = null;
                 return (T) constructor.newInstance(args);
    @@ -122,7 +114,7 @@ public final class ConstructorConstructor {
     
       /**
        * Constructors for common interface types like Map and List and their
    -   * subytpes.
    +   * subtypes.
        */
       @SuppressWarnings("unchecked") // use runtime checks to guarantee that 'T' is what it is
       private  ObjectConstructor newDefaultImplementationConstructor(
    @@ -130,14 +122,14 @@ public final class ConstructorConstructor {
         if (Collection.class.isAssignableFrom(rawType)) {
           if (SortedSet.class.isAssignableFrom(rawType)) {
             return new ObjectConstructor() {
    -          public T construct() {
    +          @Override public T construct() {
                 return (T) new TreeSet<>();
               }
             };
           } else if (EnumSet.class.isAssignableFrom(rawType)) {
             return new ObjectConstructor() {
               @SuppressWarnings("rawtypes")
    -          public T construct() {
    +          @Override public T construct() {
                 if (type instanceof ParameterizedType) {
                   Type elementType = ((ParameterizedType) type).getActualTypeArguments()[0];
                   if (elementType instanceof Class) {
    @@ -152,19 +144,19 @@ public final class ConstructorConstructor {
             };
           } else if (Set.class.isAssignableFrom(rawType)) {
             return new ObjectConstructor() {
    -          public T construct() {
    +          @Override public T construct() {
                 return (T) new LinkedHashSet<>();
               }
             };
           } else if (Queue.class.isAssignableFrom(rawType)) {
             return new ObjectConstructor() {
    -          public T construct() {
    -            return (T) new LinkedList<>();
    +          @Override public T construct() {
    +            return (T) new ArrayDeque<>();
               }
             };
           } else {
             return new ObjectConstructor() {
    -          public T construct() {
    +          @Override public T construct() {
                 return (T) new ArrayList<>();
               }
             };
    @@ -172,22 +164,34 @@ public final class ConstructorConstructor {
         }
     
         if (Map.class.isAssignableFrom(rawType)) {
    -      if (SortedMap.class.isAssignableFrom(rawType)) {
    +      if (ConcurrentNavigableMap.class.isAssignableFrom(rawType)) {
             return new ObjectConstructor() {
    -          public T construct() {
    +          @Override public T construct() {
    +            return (T) new ConcurrentSkipListMap<>();
    +          }
    +        };
    +      } else if (ConcurrentMap.class.isAssignableFrom(rawType)) {
    +        return new ObjectConstructor() {
    +          @Override public T construct() {
    +            return (T) new ConcurrentHashMap<>();
    +          }
    +        };
    +      } else if (SortedMap.class.isAssignableFrom(rawType)) {
    +        return new ObjectConstructor() {
    +          @Override public T construct() {
                 return (T) new TreeMap<>();
               }
             };
           } else if (type instanceof ParameterizedType && !(String.class.isAssignableFrom(
               TypeToken.get(((ParameterizedType) type).getActualTypeArguments()[0]).getRawType()))) {
             return new ObjectConstructor() {
    -          public T construct() {
    +          @Override public T construct() {
                 return (T) new LinkedHashMap<>();
               }
             };
           } else {
             return new ObjectConstructor() {
    -          public T construct() {
    +          @Override public T construct() {
                 return (T) new LinkedTreeMap();
               }
             };
    @@ -202,7 +206,7 @@ public final class ConstructorConstructor {
         return new ObjectConstructor() {
           private final UnsafeAllocator unsafeAllocator = UnsafeAllocator.create();
           @SuppressWarnings("unchecked")
    -      public T construct() {
    +      @Override public T construct() {
             try {
               Object newInstance = unsafeAllocator.newInstance(rawType);
               return (T) newInstance;
    diff --git a/src/com/massivecraft/massivecore/xlib/gson/internal/Excluder.java b/src/com/massivecraft/massivecore/xlib/gson/internal/Excluder.java
    index 6fed8243..17099b61 100644
    --- a/src/com/massivecraft/massivecore/xlib/gson/internal/Excluder.java
    +++ b/src/com/massivecraft/massivecore/xlib/gson/internal/Excluder.java
    @@ -16,11 +16,7 @@
     
     package com.massivecraft.massivecore.xlib.gson.internal;
     
    -import com.massivecraft.massivecore.xlib.gson.ExclusionStrategy;
    -import com.massivecraft.massivecore.xlib.gson.FieldAttributes;
    -import com.massivecraft.massivecore.xlib.gson.Gson;
    -import com.massivecraft.massivecore.xlib.gson.TypeAdapter;
    -import com.massivecraft.massivecore.xlib.gson.TypeAdapterFactory;
    +import com.massivecraft.massivecore.xlib.gson.*;
     import com.massivecraft.massivecore.xlib.gson.annotations.Expose;
     import com.massivecraft.massivecore.xlib.gson.annotations.Since;
     import com.massivecraft.massivecore.xlib.gson.annotations.Until;
    @@ -63,7 +59,7 @@ public final class Excluder implements TypeAdapterFactory, Cloneable {
         try {
           return (Excluder) super.clone();
         } catch (CloneNotSupportedException e) {
    -      throw new AssertionError();
    +      throw new AssertionError(e);
         }
       }
     
    diff --git a/src/com/massivecraft/massivecore/xlib/gson/internal/LazilyParsedNumber.java b/src/com/massivecraft/massivecore/xlib/gson/internal/LazilyParsedNumber.java
    index 3bd3558a..a71936d8 100644
    --- a/src/com/massivecraft/massivecore/xlib/gson/internal/LazilyParsedNumber.java
    +++ b/src/com/massivecraft/massivecore/xlib/gson/internal/LazilyParsedNumber.java
    @@ -26,6 +26,7 @@ import java.math.BigDecimal;
     public final class LazilyParsedNumber extends Number {
       private final String value;
     
    +  /** @param value must not be null */
       public LazilyParsedNumber(String value) {
         this.value = value;
       }
    @@ -75,4 +76,21 @@ public final class LazilyParsedNumber extends Number {
       private Object writeReplace() throws ObjectStreamException {
         return new BigDecimal(value);
       }
    -}
    \ No newline at end of file
    +
    +  @Override
    +  public int hashCode() {
    +    return value.hashCode();
    +  }
    +
    +  @Override
    +  public boolean equals(Object obj) {
    +    if (this == obj) {
    +      return true;
    +    }
    +    if (obj instanceof LazilyParsedNumber) {
    +      LazilyParsedNumber other = (LazilyParsedNumber) obj;
    +      return value == other.value || value.equals(other.value);
    +    }
    +    return false;
    +  }
    +}
    diff --git a/src/com/massivecraft/massivecore/xlib/gson/internal/LinkedHashTreeMap.java b/src/com/massivecraft/massivecore/xlib/gson/internal/LinkedHashTreeMap.java
    index 6f24e57e..37623373 100644
    --- a/src/com/massivecraft/massivecore/xlib/gson/internal/LinkedHashTreeMap.java
    +++ b/src/com/massivecraft/massivecore/xlib/gson/internal/LinkedHashTreeMap.java
    @@ -19,15 +19,7 @@ package com.massivecraft.massivecore.xlib.gson.internal;
     
     import java.io.ObjectStreamException;
     import java.io.Serializable;
    -import java.util.AbstractMap;
    -import java.util.AbstractSet;
    -import java.util.Arrays;
    -import java.util.Comparator;
    -import java.util.ConcurrentModificationException;
    -import java.util.Iterator;
    -import java.util.LinkedHashMap;
    -import java.util.NoSuchElementException;
    -import java.util.Set;
    +import java.util.*;
     
     /**
      * A map of comparable keys to values. Unlike {@code TreeMap}, this class uses
    @@ -762,6 +754,9 @@ public final class LinkedHashTreeMap extends AbstractMap implements
         Node lastReturned = null;
         int expectedModCount = modCount;
     
    +    LinkedTreeMapIterator() {
    +    }
    +
         public final boolean hasNext() {
           return next != header;
         }
    diff --git a/src/com/massivecraft/massivecore/xlib/gson/internal/LinkedTreeMap.java b/src/com/massivecraft/massivecore/xlib/gson/internal/LinkedTreeMap.java
    index d6895708..9030b453 100644
    --- a/src/com/massivecraft/massivecore/xlib/gson/internal/LinkedTreeMap.java
    +++ b/src/com/massivecraft/massivecore/xlib/gson/internal/LinkedTreeMap.java
    @@ -19,14 +19,7 @@ package com.massivecraft.massivecore.xlib.gson.internal;
     
     import java.io.ObjectStreamException;
     import java.io.Serializable;
    -import java.util.AbstractMap;
    -import java.util.AbstractSet;
    -import java.util.Comparator;
    -import java.util.ConcurrentModificationException;
    -import java.util.Iterator;
    -import java.util.LinkedHashMap;
    -import java.util.NoSuchElementException;
    -import java.util.Set;
    +import java.util.*;
     
     /**
      * A map of comparable keys to values. Unlike {@code TreeMap}, this class uses
    @@ -528,6 +521,9 @@ public final class LinkedTreeMap extends AbstractMap implements Seri
         Node lastReturned = null;
         int expectedModCount = modCount;
     
    +    LinkedTreeMapIterator() {
    +    }
    +
         public final boolean hasNext() {
           return next != header;
         }
    @@ -624,4 +620,4 @@ public final class LinkedTreeMap extends AbstractMap implements Seri
       private Object writeReplace() throws ObjectStreamException {
         return new LinkedHashMap<>(this);
       }
    -}
    \ No newline at end of file
    +}
    diff --git a/src/com/massivecraft/massivecore/xlib/gson/internal/ObjectConstructor.java b/src/com/massivecraft/massivecore/xlib/gson/internal/ObjectConstructor.java
    index 64c64cd7..a6978aab 100644
    --- a/src/com/massivecraft/massivecore/xlib/gson/internal/ObjectConstructor.java
    +++ b/src/com/massivecraft/massivecore/xlib/gson/internal/ObjectConstructor.java
    @@ -29,5 +29,5 @@ public interface ObjectConstructor {
       /**
        * Returns a new instance.
        */
    -  T construct();
    +  public T construct();
     }
    \ No newline at end of file
    diff --git a/src/com/massivecraft/massivecore/xlib/gson/internal/Primitives.java b/src/com/massivecraft/massivecore/xlib/gson/internal/Primitives.java
    index 6a13f443..bba7873c 100644
    --- a/src/com/massivecraft/massivecore/xlib/gson/internal/Primitives.java
    +++ b/src/com/massivecraft/massivecore/xlib/gson/internal/Primitives.java
    @@ -29,7 +29,9 @@ import java.util.Map;
      * @author Kevin Bourrillion
      */
     public final class Primitives {
    -  private Primitives() {}
    +  private Primitives() {
    +    throw new UnsupportedOperationException();
    +  }
     
       /** A map from primitive types to their corresponding wrapper types. */
       private static final Map, Class> PRIMITIVE_TO_WRAPPER_TYPE;
    diff --git a/src/com/massivecraft/massivecore/xlib/gson/internal/Streams.java b/src/com/massivecraft/massivecore/xlib/gson/internal/Streams.java
    index a9706a52..ce92e295 100644
    --- a/src/com/massivecraft/massivecore/xlib/gson/internal/Streams.java
    +++ b/src/com/massivecraft/massivecore/xlib/gson/internal/Streams.java
    @@ -16,11 +16,7 @@
     
     package com.massivecraft.massivecore.xlib.gson.internal;
     
    -import com.massivecraft.massivecore.xlib.gson.JsonElement;
    -import com.massivecraft.massivecore.xlib.gson.JsonIOException;
    -import com.massivecraft.massivecore.xlib.gson.JsonNull;
    -import com.massivecraft.massivecore.xlib.gson.JsonParseException;
    -import com.massivecraft.massivecore.xlib.gson.JsonSyntaxException;
    +import com.massivecraft.massivecore.xlib.gson.*;
     import com.massivecraft.massivecore.xlib.gson.internal.bind.TypeAdapters;
     import com.massivecraft.massivecore.xlib.gson.stream.JsonReader;
     import com.massivecraft.massivecore.xlib.gson.stream.JsonWriter;
    @@ -34,6 +30,10 @@ import java.io.Writer;
      * Reads and writes GSON parse trees over streams.
      */
     public final class Streams {
    +  private Streams() {
    +    throw new UnsupportedOperationException();
    +  }
    +
       /**
        * Takes a reader in any state and returns the next value as a JsonElement.
        */
    @@ -81,7 +81,7 @@ public final class Streams {
         private final Appendable appendable;
         private final CurrentWrite currentWrite = new CurrentWrite();
     
    -    private AppendableWriter(Appendable appendable) {
    +    AppendableWriter(Appendable appendable) {
           this.appendable = appendable;
         }
     
    diff --git a/src/com/massivecraft/massivecore/xlib/gson/internal/UnsafeAllocator.java b/src/com/massivecraft/massivecore/xlib/gson/internal/UnsafeAllocator.java
    index 99c35e5d..db9decdd 100644
    --- a/src/com/massivecraft/massivecore/xlib/gson/internal/UnsafeAllocator.java
    +++ b/src/com/massivecraft/massivecore/xlib/gson/internal/UnsafeAllocator.java
    @@ -20,6 +20,7 @@ import java.io.ObjectInputStream;
     import java.io.ObjectStreamClass;
     import java.lang.reflect.Field;
     import java.lang.reflect.Method;
    +import java.lang.reflect.Modifier;
     
     /**
      * Do sneaky things to allocate objects without invoking their constructors.
    @@ -45,6 +46,7 @@ public abstract class UnsafeAllocator {
             @Override
             @SuppressWarnings("unchecked")
             public  T newInstance(Class c) throws Exception {
    +          assertInstantiable(c);
               return (T) allocateInstance.invoke(unsafe, c);
             }
           };
    @@ -68,6 +70,7 @@ public abstract class UnsafeAllocator {
             @Override
             @SuppressWarnings("unchecked")
             public  T newInstance(Class c) throws Exception {
    +          assertInstantiable(c);
               return (T) newInstance.invoke(null, c, constructorId);
             }
           };
    @@ -87,6 +90,7 @@ public abstract class UnsafeAllocator {
             @Override
             @SuppressWarnings("unchecked")
             public  T newInstance(Class c) throws Exception {
    +          assertInstantiable(c);
               return (T) newInstance.invoke(null, c, Object.class);
             }
           };
    @@ -101,4 +105,19 @@ public abstract class UnsafeAllocator {
           }
         };
       }
    +
    +  /**
    +   * Check if the class can be instantiated by unsafe allocator. If the instance has interface or abstract modifiers
    +   * throw an {@link java.lang.UnsupportedOperationException}
    +   * @param c instance of the class to be checked
    +   */
    +  private static void assertInstantiable(Class c) {
    +    int modifiers = c.getModifiers();
    +    if (Modifier.isInterface(modifiers)) {
    +      throw new UnsupportedOperationException("Interface can't be instantiated! Interface name: " + c.getName());
    +    }
    +    if (Modifier.isAbstract(modifiers)) {
    +      throw new UnsupportedOperationException("Abstract class can't be instantiated! Class name: " + c.getName());
    +    }
    +  }
     }
    diff --git a/src/com/massivecraft/massivecore/xlib/gson/internal/bind/ArrayTypeAdapter.java b/src/com/massivecraft/massivecore/xlib/gson/internal/bind/ArrayTypeAdapter.java
    index d4b7bac0..c7353336 100644
    --- a/src/com/massivecraft/massivecore/xlib/gson/internal/bind/ArrayTypeAdapter.java
    +++ b/src/com/massivecraft/massivecore/xlib/gson/internal/bind/ArrayTypeAdapter.java
    @@ -38,7 +38,7 @@ import java.util.List;
     public final class ArrayTypeAdapter extends TypeAdapter {
       public static final TypeAdapterFactory FACTORY = new TypeAdapterFactory() {
         @SuppressWarnings({"unchecked", "rawtypes"})
    -    public  TypeAdapter create(Gson gson, TypeToken typeToken) {
    +    @Override public  TypeAdapter create(Gson gson, TypeToken typeToken) {
           Type type = typeToken.getType();
           if (!(type instanceof GenericArrayType || type instanceof Class && ((Class) type).isArray())) {
             return null;
    @@ -56,11 +56,11 @@ public final class ArrayTypeAdapter extends TypeAdapter {
     
       public ArrayTypeAdapter(Gson context, TypeAdapter componentTypeAdapter, Class componentType) {
         this.componentTypeAdapter =
    -		new TypeAdapterRuntimeTypeWrapper<>(context, componentTypeAdapter, componentType);
    +            new TypeAdapterRuntimeTypeWrapper<>(context, componentTypeAdapter, componentType);
         this.componentType = componentType;
       }
     
    -  public Object read(JsonReader in) throws IOException {
    +  @Override public Object read(JsonReader in) throws IOException {
         if (in.peek() == JsonToken.NULL) {
           in.nextNull();
           return null;
    diff --git a/src/com/massivecraft/massivecore/xlib/gson/internal/bind/CollectionTypeAdapterFactory.java b/src/com/massivecraft/massivecore/xlib/gson/internal/bind/CollectionTypeAdapterFactory.java
    index 0a063495..45291dad 100644
    --- a/src/com/massivecraft/massivecore/xlib/gson/internal/bind/CollectionTypeAdapterFactory.java
    +++ b/src/com/massivecraft/massivecore/xlib/gson/internal/bind/CollectionTypeAdapterFactory.java
    @@ -41,6 +41,7 @@ public final class CollectionTypeAdapterFactory implements TypeAdapterFactory {
         this.constructorConstructor = constructorConstructor;
       }
     
    +  @Override
       public  TypeAdapter create(Gson gson, TypeToken typeToken) {
         Type type = typeToken.getType();
     
    @@ -63,14 +64,14 @@ public final class CollectionTypeAdapterFactory implements TypeAdapterFactory {
         private final ObjectConstructor> constructor;
     
         public Adapter(Gson context, Type elementType,
    -        TypeAdapter elementTypeAdapter,
    -        ObjectConstructor> constructor) {
    +                   TypeAdapter elementTypeAdapter,
    +                   ObjectConstructor> constructor) {
           this.elementTypeAdapter =
    -		  new TypeAdapterRuntimeTypeWrapper<>(context, elementTypeAdapter, elementType);
    +              new TypeAdapterRuntimeTypeWrapper<>(context, elementTypeAdapter, elementType);
           this.constructor = constructor;
         }
     
    -    public Collection read(JsonReader in) throws IOException {
    +    @Override public Collection read(JsonReader in) throws IOException {
           if (in.peek() == JsonToken.NULL) {
             in.nextNull();
             return null;
    @@ -86,7 +87,7 @@ public final class CollectionTypeAdapterFactory implements TypeAdapterFactory {
           return collection;
         }
     
    -    public void write(JsonWriter out, Collection collection) throws IOException {
    +    @Override public void write(JsonWriter out, Collection collection) throws IOException {
           if (collection == null) {
             out.nullValue();
             return;
    diff --git a/src/com/massivecraft/massivecore/xlib/gson/internal/bind/DateTypeAdapter.java b/src/com/massivecraft/massivecore/xlib/gson/internal/bind/DateTypeAdapter.java
    index d3488175..d57ea2e5 100644
    --- a/src/com/massivecraft/massivecore/xlib/gson/internal/bind/DateTypeAdapter.java
    +++ b/src/com/massivecraft/massivecore/xlib/gson/internal/bind/DateTypeAdapter.java
    @@ -20,6 +20,7 @@ import com.massivecraft.massivecore.xlib.gson.Gson;
     import com.massivecraft.massivecore.xlib.gson.JsonSyntaxException;
     import com.massivecraft.massivecore.xlib.gson.TypeAdapter;
     import com.massivecraft.massivecore.xlib.gson.TypeAdapterFactory;
    +import com.massivecraft.massivecore.xlib.gson.internal.bind.util.ISO8601Utils;
     import com.massivecraft.massivecore.xlib.gson.reflect.TypeToken;
     import com.massivecraft.massivecore.xlib.gson.stream.JsonReader;
     import com.massivecraft.massivecore.xlib.gson.stream.JsonToken;
    @@ -28,10 +29,9 @@ import com.massivecraft.massivecore.xlib.gson.stream.JsonWriter;
     import java.io.IOException;
     import java.text.DateFormat;
     import java.text.ParseException;
    -import java.text.SimpleDateFormat;
    +import java.text.ParsePosition;
     import java.util.Date;
     import java.util.Locale;
    -import java.util.TimeZone;
     
     /**
      * Adapter for Date. Although this class appears stateless, it is not.
    @@ -42,7 +42,7 @@ import java.util.TimeZone;
     public final class DateTypeAdapter extends TypeAdapter {
       public static final TypeAdapterFactory FACTORY = new TypeAdapterFactory() {
         @SuppressWarnings("unchecked") // we use a runtime check to make sure the 'T's equal
    -    public  TypeAdapter create(Gson gson, TypeToken typeToken) {
    +    @Override public  TypeAdapter create(Gson gson, TypeToken typeToken) {
           return typeToken.getRawType() == Date.class ? (TypeAdapter) new DateTypeAdapter() : null;
         }
       };
    @@ -51,13 +51,6 @@ public final class DateTypeAdapter extends TypeAdapter {
           = DateFormat.getDateTimeInstance(DateFormat.DEFAULT, DateFormat.DEFAULT, Locale.US);
       private final DateFormat localFormat
           = DateFormat.getDateTimeInstance(DateFormat.DEFAULT, DateFormat.DEFAULT);
    -  private final DateFormat iso8601Format = buildIso8601Format();
    -
    -  private static DateFormat buildIso8601Format() {
    -    DateFormat iso8601Format = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'", Locale.US);
    -    iso8601Format.setTimeZone(TimeZone.getTimeZone("UTC"));
    -    return iso8601Format;
    -  }
     
       @Override public Date read(JsonReader in) throws IOException {
         if (in.peek() == JsonToken.NULL) {
    @@ -77,7 +70,7 @@ public final class DateTypeAdapter extends TypeAdapter {
         } catch (ParseException ignored) {
         }
         try {
    -      return iso8601Format.parse(json);
    +    	return ISO8601Utils.parse(json, new ParsePosition(0));
         } catch (ParseException e) {
           throw new JsonSyntaxException(json, e);
         }
    @@ -91,4 +84,6 @@ public final class DateTypeAdapter extends TypeAdapter {
         String dateFormatAsString = enUsFormat.format(value);
         out.value(dateFormatAsString);
       }
    +  
    +  
     }
    diff --git a/src/com/massivecraft/massivecore/xlib/gson/internal/bind/JsonAdapterAnnotationTypeAdapterFactory.java b/src/com/massivecraft/massivecore/xlib/gson/internal/bind/JsonAdapterAnnotationTypeAdapterFactory.java
    index 23b4af02..08e8ffa2 100644
    --- a/src/com/massivecraft/massivecore/xlib/gson/internal/bind/JsonAdapterAnnotationTypeAdapterFactory.java
    +++ b/src/com/massivecraft/massivecore/xlib/gson/internal/bind/JsonAdapterAnnotationTypeAdapterFactory.java
    @@ -16,9 +16,7 @@
     
     package com.massivecraft.massivecore.xlib.gson.internal.bind;
     
    -import com.massivecraft.massivecore.xlib.gson.Gson;
    -import com.massivecraft.massivecore.xlib.gson.TypeAdapter;
    -import com.massivecraft.massivecore.xlib.gson.TypeAdapterFactory;
    +import com.massivecraft.massivecore.xlib.gson.*;
     import com.massivecraft.massivecore.xlib.gson.annotations.JsonAdapter;
     import com.massivecraft.massivecore.xlib.gson.internal.ConstructorConstructor;
     import com.massivecraft.massivecore.xlib.gson.reflect.TypeToken;
    @@ -30,7 +28,6 @@ import com.massivecraft.massivecore.xlib.gson.reflect.TypeToken;
      * @since 2.3
      */
     public final class JsonAdapterAnnotationTypeAdapterFactory implements TypeAdapterFactory {
    -
       private final ConstructorConstructor constructorConstructor;
     
       public JsonAdapterAnnotationTypeAdapterFactory(ConstructorConstructor constructorConstructor) {
    @@ -38,30 +35,44 @@ public final class JsonAdapterAnnotationTypeAdapterFactory implements TypeAdapte
       }
     
       @SuppressWarnings("unchecked")
    +  @Override
       public  TypeAdapter create(Gson gson, TypeToken targetType) {
    -    JsonAdapter annotation = targetType.getRawType().getAnnotation(JsonAdapter.class);
    +    Class rawType = targetType.getRawType();
    +    JsonAdapter annotation = rawType.getAnnotation(JsonAdapter.class);
         if (annotation == null) {
           return null;
         }
         return (TypeAdapter) getTypeAdapter(constructorConstructor, gson, targetType, annotation);
       }
     
    -  @SuppressWarnings("unchecked") // Casts guarded by conditionals.
    -  static TypeAdapter getTypeAdapter(ConstructorConstructor constructorConstructor, Gson gson,
    -      TypeToken fieldType, JsonAdapter annotation) {
    -    Class value = annotation.value();
    -    if (TypeAdapter.class.isAssignableFrom(value)) {
    -          Class> typeAdapter = (Class>) value;
    -      return constructorConstructor.get(TypeToken.get(typeAdapter)).construct();
    -    }
    -    if (TypeAdapterFactory.class.isAssignableFrom(value)) {
    -          Class typeAdapterFactory = (Class) value;
    -      return constructorConstructor.get(TypeToken.get(typeAdapterFactory))
    -          .construct()
    -          .create(gson, fieldType);
    +  @SuppressWarnings({ "unchecked", "rawtypes" }) // Casts guarded by conditionals.
    +  TypeAdapter getTypeAdapter(ConstructorConstructor constructorConstructor, Gson gson,
    +      TypeToken type, JsonAdapter annotation) {
    +    Object instance = constructorConstructor.get(TypeToken.get(annotation.value())).construct();
    +
    +    TypeAdapter typeAdapter;
    +    if (instance instanceof TypeAdapter) {
    +      typeAdapter = (TypeAdapter) instance;
    +    } else if (instance instanceof TypeAdapterFactory) {
    +      typeAdapter = ((TypeAdapterFactory) instance).create(gson, type);
    +    } else if (instance instanceof JsonSerializer || instance instanceof JsonDeserializer) {
    +      JsonSerializer serializer = instance instanceof JsonSerializer
    +          ? (JsonSerializer) instance
    +          : null;
    +      JsonDeserializer deserializer = instance instanceof JsonDeserializer
    +          ? (JsonDeserializer) instance
    +          : null;
    +      typeAdapter = new TreeTypeAdapter(serializer, deserializer, gson, type, null);
    +    } else {
    +      throw new IllegalArgumentException(
    +          "@JsonAdapter value must be TypeAdapter, TypeAdapterFactory, "
    +              + "JsonSerializer or JsonDeserializer reference.");
         }
     
    -    throw new IllegalArgumentException(
    -        "@JsonAdapter value must be TypeAdapter or TypeAdapterFactory reference.");
    +    if (typeAdapter != null && annotation.nullSafe()) {
    +      typeAdapter = typeAdapter.nullSafe();
    +    }
    +
    +    return typeAdapter;
       }
     }
    diff --git a/src/com/massivecraft/massivecore/xlib/gson/internal/bind/JsonTreeReader.java b/src/com/massivecraft/massivecore/xlib/gson/internal/bind/JsonTreeReader.java
    index 38bcdb29..3fa7fc85 100644
    --- a/src/com/massivecraft/massivecore/xlib/gson/internal/bind/JsonTreeReader.java
    +++ b/src/com/massivecraft/massivecore/xlib/gson/internal/bind/JsonTreeReader.java
    @@ -16,19 +16,13 @@
     
     package com.massivecraft.massivecore.xlib.gson.internal.bind;
     
    -import com.massivecraft.massivecore.xlib.gson.JsonArray;
    -import com.massivecraft.massivecore.xlib.gson.JsonElement;
    -import com.massivecraft.massivecore.xlib.gson.JsonNull;
    -import com.massivecraft.massivecore.xlib.gson.JsonObject;
    -import com.massivecraft.massivecore.xlib.gson.JsonPrimitive;
    +import com.massivecraft.massivecore.xlib.gson.*;
     import com.massivecraft.massivecore.xlib.gson.stream.JsonReader;
     import com.massivecraft.massivecore.xlib.gson.stream.JsonToken;
     
     import java.io.IOException;
     import java.io.Reader;
    -import java.util.ArrayList;
     import java.util.Iterator;
    -import java.util.List;
     import java.util.Map;
     
     /**
    @@ -48,35 +42,57 @@ public final class JsonTreeReader extends JsonReader {
       };
       private static final Object SENTINEL_CLOSED = new Object();
     
    -  private final List stack = new ArrayList<>();
    +  /*
    +   * The nesting stack. Using a manual array rather than an ArrayList saves 20%.
    +   */
    +  private Object[] stack = new Object[32];
    +  private int stackSize = 0;
    +
    +  /*
    +   * The path members. It corresponds directly to stack: At indices where the
    +   * stack contains an object (EMPTY_OBJECT, DANGLING_NAME or NONEMPTY_OBJECT),
    +   * pathNames contains the name at this scope. Where it contains an array
    +   * (EMPTY_ARRAY, NONEMPTY_ARRAY) pathIndices contains the current index in
    +   * that array. Otherwise the value is undefined, and we take advantage of that
    +   * by incrementing pathIndices when doing so isn't useful.
    +   */
    +  private String[] pathNames = new String[32];
    +  private int[] pathIndices = new int[32];
     
       public JsonTreeReader(JsonElement element) {
         super(UNREADABLE_READER);
    -    stack.add(element);
    +    push(element);
       }
     
       @Override public void beginArray() throws IOException {
         expect(JsonToken.BEGIN_ARRAY);
         JsonArray array = (JsonArray) peekStack();
    -    stack.add(array.iterator());
    +    push(array.iterator());
    +    pathIndices[stackSize - 1] = 0;
       }
     
       @Override public void endArray() throws IOException {
         expect(JsonToken.END_ARRAY);
         popStack(); // empty iterator
         popStack(); // array
    +    if (stackSize > 0) {
    +      pathIndices[stackSize - 1]++;
    +    }
       }
     
       @Override public void beginObject() throws IOException {
         expect(JsonToken.BEGIN_OBJECT);
         JsonObject object = (JsonObject) peekStack();
    -    stack.add(object.entrySet().iterator());
    +    push(object.entrySet().iterator());
       }
     
       @Override public void endObject() throws IOException {
         expect(JsonToken.END_OBJECT);
         popStack(); // empty iterator
         popStack(); // object
    +    if (stackSize > 0) {
    +      pathIndices[stackSize - 1]++;
    +    }
       }
     
       @Override public boolean hasNext() throws IOException {
    @@ -85,19 +101,19 @@ public final class JsonTreeReader extends JsonReader {
       }
     
       @Override public JsonToken peek() throws IOException {
    -    if (stack.isEmpty()) {
    +    if (stackSize == 0) {
           return JsonToken.END_DOCUMENT;
         }
     
         Object o = peekStack();
         if (o instanceof Iterator) {
    -      boolean isObject = stack.get(stack.size() - 2) instanceof JsonObject;
    +      boolean isObject = stack[stackSize - 2] instanceof JsonObject;
           Iterator iterator = (Iterator) o;
           if (iterator.hasNext()) {
             if (isObject) {
               return JsonToken.NAME;
             } else {
    -          stack.add(iterator.next());
    +          push(iterator.next());
               return peek();
             }
           } else {
    @@ -128,16 +144,19 @@ public final class JsonTreeReader extends JsonReader {
       }
     
       private Object peekStack() {
    -    return stack.get(stack.size() - 1);
    +    return stack[stackSize - 1];
       }
     
       private Object popStack() {
    -    return stack.remove(stack.size() - 1);
    +    Object result = stack[--stackSize];
    +    stack[stackSize] = null;
    +    return result;
       }
     
       private void expect(JsonToken expected) throws IOException {
         if (peek() != expected) {
    -      throw new IllegalStateException("Expected " + expected + " but was " + peek());
    +      throw new IllegalStateException(
    +          "Expected " + expected + " but was " + peek() + locationString());
         }
       }
     
    @@ -145,72 +164,101 @@ public final class JsonTreeReader extends JsonReader {
         expect(JsonToken.NAME);
         Iterator i = (Iterator) peekStack();
         Map.Entry entry = (Map.Entry) i.next();
    -    stack.add(entry.getValue());
    -    return (String) entry.getKey();
    +    String result = (String) entry.getKey();
    +    pathNames[stackSize - 1] = result;
    +    push(entry.getValue());
    +    return result;
       }
     
       @Override public String nextString() throws IOException {
         JsonToken token = peek();
         if (token != JsonToken.STRING && token != JsonToken.NUMBER) {
    -      throw new IllegalStateException("Expected " + JsonToken.STRING + " but was " + token);
    +      throw new IllegalStateException(
    +          "Expected " + JsonToken.STRING + " but was " + token + locationString());
         }
    -    return ((JsonPrimitive) popStack()).getAsString();
    +    String result = ((JsonPrimitive) popStack()).getAsString();
    +    if (stackSize > 0) {
    +      pathIndices[stackSize - 1]++;
    +    }
    +    return result;
       }
     
       @Override public boolean nextBoolean() throws IOException {
         expect(JsonToken.BOOLEAN);
    -    return ((JsonPrimitive) popStack()).getAsBoolean();
    +    boolean result = ((JsonPrimitive) popStack()).getAsBoolean();
    +    if (stackSize > 0) {
    +      pathIndices[stackSize - 1]++;
    +    }
    +    return result;
       }
     
       @Override public void nextNull() throws IOException {
         expect(JsonToken.NULL);
         popStack();
    +    if (stackSize > 0) {
    +      pathIndices[stackSize - 1]++;
    +    }
       }
     
       @Override public double nextDouble() throws IOException {
         JsonToken token = peek();
         if (token != JsonToken.NUMBER && token != JsonToken.STRING) {
    -      throw new IllegalStateException("Expected " + JsonToken.NUMBER + " but was " + token);
    +      throw new IllegalStateException(
    +          "Expected " + JsonToken.NUMBER + " but was " + token + locationString());
         }
         double result = ((JsonPrimitive) peekStack()).getAsDouble();
         if (!isLenient() && (Double.isNaN(result) || Double.isInfinite(result))) {
           throw new NumberFormatException("JSON forbids NaN and infinities: " + result);
         }
         popStack();
    +    if (stackSize > 0) {
    +      pathIndices[stackSize - 1]++;
    +    }
         return result;
       }
     
       @Override public long nextLong() throws IOException {
         JsonToken token = peek();
         if (token != JsonToken.NUMBER && token != JsonToken.STRING) {
    -      throw new IllegalStateException("Expected " + JsonToken.NUMBER + " but was " + token);
    +      throw new IllegalStateException(
    +          "Expected " + JsonToken.NUMBER + " but was " + token + locationString());
         }
         long result = ((JsonPrimitive) peekStack()).getAsLong();
         popStack();
    +    if (stackSize > 0) {
    +      pathIndices[stackSize - 1]++;
    +    }
         return result;
       }
     
       @Override public int nextInt() throws IOException {
         JsonToken token = peek();
         if (token != JsonToken.NUMBER && token != JsonToken.STRING) {
    -      throw new IllegalStateException("Expected " + JsonToken.NUMBER + " but was " + token);
    +      throw new IllegalStateException(
    +          "Expected " + JsonToken.NUMBER + " but was " + token + locationString());
         }
         int result = ((JsonPrimitive) peekStack()).getAsInt();
         popStack();
    +    if (stackSize > 0) {
    +      pathIndices[stackSize - 1]++;
    +    }
         return result;
       }
     
       @Override public void close() throws IOException {
    -    stack.clear();
    -    stack.add(SENTINEL_CLOSED);
    +    stack = new Object[] { SENTINEL_CLOSED };
    +    stackSize = 1;
       }
     
       @Override public void skipValue() throws IOException {
         if (peek() == JsonToken.NAME) {
           nextName();
    +      pathNames[stackSize - 2] = "null";
         } else {
           popStack();
    +      pathNames[stackSize - 1] = "null";
         }
    +    pathIndices[stackSize - 1]++;
       }
     
       @Override public String toString() {
    @@ -221,7 +269,45 @@ public final class JsonTreeReader extends JsonReader {
         expect(JsonToken.NAME);
         Iterator i = (Iterator) peekStack();
         Map.Entry entry = (Map.Entry) i.next();
    -    stack.add(entry.getValue());
    -    stack.add(new JsonPrimitive((String)entry.getKey()));
    +    push(entry.getValue());
    +    push(new JsonPrimitive((String) entry.getKey()));
    +  }
    +
    +  private void push(Object newTop) {
    +    if (stackSize == stack.length) {
    +      Object[] newStack = new Object[stackSize * 2];
    +      int[] newPathIndices = new int[stackSize * 2];
    +      String[] newPathNames = new String[stackSize * 2];
    +      System.arraycopy(stack, 0, newStack, 0, stackSize);
    +      System.arraycopy(pathIndices, 0, newPathIndices, 0, stackSize);
    +      System.arraycopy(pathNames, 0, newPathNames, 0, stackSize);
    +      stack = newStack;
    +      pathIndices = newPathIndices;
    +      pathNames = newPathNames;
    +    }
    +    stack[stackSize++] = newTop;
    +  }
    +
    +  @Override public String getPath() {
    +    StringBuilder result = new StringBuilder().append('$');
    +    for (int i = 0; i < stackSize; i++) {
    +      if (stack[i] instanceof JsonArray) {
    +        if (stack[++i] instanceof Iterator) {
    +          result.append('[').append(pathIndices[i]).append(']');
    +        }
    +      } else if (stack[i] instanceof JsonObject) {
    +        if (stack[++i] instanceof Iterator) {
    +          result.append('.');
    +          if (pathNames[i] != null) {
    +            result.append(pathNames[i]);
    +          }
    +        }
    +      }
    +    }
    +    return result.toString();
    +  }
    +
    +  private String locationString() {
    +    return " at path " + getPath();
       }
     }
    diff --git a/src/com/massivecraft/massivecore/xlib/gson/internal/bind/JsonTreeWriter.java b/src/com/massivecraft/massivecore/xlib/gson/internal/bind/JsonTreeWriter.java
    index b38fd09d..8547368b 100644
    --- a/src/com/massivecraft/massivecore/xlib/gson/internal/bind/JsonTreeWriter.java
    +++ b/src/com/massivecraft/massivecore/xlib/gson/internal/bind/JsonTreeWriter.java
    @@ -16,11 +16,7 @@
     
     package com.massivecraft.massivecore.xlib.gson.internal.bind;
     
    -import com.massivecraft.massivecore.xlib.gson.JsonArray;
    -import com.massivecraft.massivecore.xlib.gson.JsonElement;
    -import com.massivecraft.massivecore.xlib.gson.JsonNull;
    -import com.massivecraft.massivecore.xlib.gson.JsonObject;
    -import com.massivecraft.massivecore.xlib.gson.JsonPrimitive;
    +import com.massivecraft.massivecore.xlib.gson.*;
     import com.massivecraft.massivecore.xlib.gson.stream.JsonWriter;
     
     import java.io.IOException;
    @@ -160,6 +156,14 @@ public final class JsonTreeWriter extends JsonWriter {
         return this;
       }
     
    +  @Override public JsonWriter value(Boolean value) throws IOException {
    +    if (value == null) {
    +      return nullValue();
    +    }
    +    put(new JsonPrimitive(value));
    +    return this;
    +  }
    +
       @Override public JsonWriter value(double value) throws IOException {
         if (!isLenient() && (Double.isNaN(value) || Double.isInfinite(value))) {
           throw new IllegalArgumentException("JSON forbids NaN and infinities: " + value);
    diff --git a/src/com/massivecraft/massivecore/xlib/gson/internal/bind/MapTypeAdapterFactory.java b/src/com/massivecraft/massivecore/xlib/gson/internal/bind/MapTypeAdapterFactory.java
    index d56e41a2..8890de63 100644
    --- a/src/com/massivecraft/massivecore/xlib/gson/internal/bind/MapTypeAdapterFactory.java
    +++ b/src/com/massivecraft/massivecore/xlib/gson/internal/bind/MapTypeAdapterFactory.java
    @@ -16,17 +16,8 @@
     
     package com.massivecraft.massivecore.xlib.gson.internal.bind;
     
    -import com.massivecraft.massivecore.xlib.gson.Gson;
    -import com.massivecraft.massivecore.xlib.gson.JsonElement;
    -import com.massivecraft.massivecore.xlib.gson.JsonPrimitive;
    -import com.massivecraft.massivecore.xlib.gson.JsonSyntaxException;
    -import com.massivecraft.massivecore.xlib.gson.TypeAdapter;
    -import com.massivecraft.massivecore.xlib.gson.TypeAdapterFactory;
    -import com.massivecraft.massivecore.xlib.gson.internal.$Gson$Types;
    -import com.massivecraft.massivecore.xlib.gson.internal.ConstructorConstructor;
    -import com.massivecraft.massivecore.xlib.gson.internal.JsonReaderInternalAccess;
    -import com.massivecraft.massivecore.xlib.gson.internal.ObjectConstructor;
    -import com.massivecraft.massivecore.xlib.gson.internal.Streams;
    +import com.massivecraft.massivecore.xlib.gson.*;
    +import com.massivecraft.massivecore.xlib.gson.internal.*;
     import com.massivecraft.massivecore.xlib.gson.reflect.TypeToken;
     import com.massivecraft.massivecore.xlib.gson.stream.JsonReader;
     import com.massivecraft.massivecore.xlib.gson.stream.JsonToken;
    @@ -61,7 +52,7 @@ import java.util.Map;
      * But GSON is unable to deserialize this value because the JSON string name is
      * just the {@link Object#toString() toString()} of the map key. Attempting to
      * convert the above JSON to an object fails with a parse exception:
    - * 
    com.google.gson.JsonParseException: Expecting object found: "(5,6)"
    + * 
    JsonParseException: Expecting object found: "(5,6)"
      *   at com.google.gson.JsonObjectDeserializationVisitor.visitFieldUsingCustomHandler
      *   at com.google.gson.ObjectNavigator.navigateClassFields
      *   ...
    @@ -105,15 +96,15 @@ import java.util.Map; */ public final class MapTypeAdapterFactory implements TypeAdapterFactory { private final ConstructorConstructor constructorConstructor; - private final boolean complexMapKeySerialization; + final boolean complexMapKeySerialization; public MapTypeAdapterFactory(ConstructorConstructor constructorConstructor, - boolean complexMapKeySerialization) { + boolean complexMapKeySerialization) { this.constructorConstructor = constructorConstructor; this.complexMapKeySerialization = complexMapKeySerialization; } - public TypeAdapter create(Gson gson, TypeToken typeToken) { + @Override public TypeAdapter create(Gson gson, TypeToken typeToken) { Type type = typeToken.getType(); Class rawType = typeToken.getRawType(); @@ -149,16 +140,16 @@ public final class MapTypeAdapterFactory implements TypeAdapterFactory { private final ObjectConstructor> constructor; public Adapter(Gson context, Type keyType, TypeAdapter keyTypeAdapter, - Type valueType, TypeAdapter valueTypeAdapter, - ObjectConstructor> constructor) { + Type valueType, TypeAdapter valueTypeAdapter, + ObjectConstructor> constructor) { this.keyTypeAdapter = - new TypeAdapterRuntimeTypeWrapper<>(context, keyTypeAdapter, keyType); + new TypeAdapterRuntimeTypeWrapper<>(context, keyTypeAdapter, keyType); this.valueTypeAdapter = - new TypeAdapterRuntimeTypeWrapper<>(context, valueTypeAdapter, valueType); + new TypeAdapterRuntimeTypeWrapper<>(context, valueTypeAdapter, valueType); this.constructor = constructor; } - public Map read(JsonReader in) throws IOException { + @Override public Map read(JsonReader in) throws IOException { JsonToken peek = in.peek(); if (peek == JsonToken.NULL) { in.nextNull(); @@ -196,7 +187,7 @@ public final class MapTypeAdapterFactory implements TypeAdapterFactory { return map; } - public void write(JsonWriter out, Map map) throws IOException { + @Override public void write(JsonWriter out, Map map) throws IOException { if (map == null) { out.nullValue(); return; diff --git a/src/com/massivecraft/massivecore/xlib/gson/internal/bind/ObjectTypeAdapter.java b/src/com/massivecraft/massivecore/xlib/gson/internal/bind/ObjectTypeAdapter.java index c40b6b43..5883b6ae 100644 --- a/src/com/massivecraft/massivecore/xlib/gson/internal/bind/ObjectTypeAdapter.java +++ b/src/com/massivecraft/massivecore/xlib/gson/internal/bind/ObjectTypeAdapter.java @@ -37,7 +37,7 @@ import java.util.Map; public final class ObjectTypeAdapter extends TypeAdapter { public static final TypeAdapterFactory FACTORY = new TypeAdapterFactory() { @SuppressWarnings("unchecked") - public TypeAdapter create(Gson gson, TypeToken type) { + @Override public TypeAdapter create(Gson gson, TypeToken type) { if (type.getRawType() == Object.class) { return (TypeAdapter) new ObjectTypeAdapter(gson); } @@ -47,7 +47,7 @@ public final class ObjectTypeAdapter extends TypeAdapter { private final Gson gson; - private ObjectTypeAdapter(Gson gson) { + ObjectTypeAdapter(Gson gson) { this.gson = gson; } diff --git a/src/com/massivecraft/massivecore/xlib/gson/internal/bind/ReflectiveTypeAdapterFactory.java b/src/com/massivecraft/massivecore/xlib/gson/internal/bind/ReflectiveTypeAdapterFactory.java index 23583642..9d430a29 100644 --- a/src/com/massivecraft/massivecore/xlib/gson/internal/bind/ReflectiveTypeAdapterFactory.java +++ b/src/com/massivecraft/massivecore/xlib/gson/internal/bind/ReflectiveTypeAdapterFactory.java @@ -16,18 +16,10 @@ package com.massivecraft.massivecore.xlib.gson.internal.bind; -import com.massivecraft.massivecore.xlib.gson.FieldNamingStrategy; -import com.massivecraft.massivecore.xlib.gson.Gson; -import com.massivecraft.massivecore.xlib.gson.JsonSyntaxException; -import com.massivecraft.massivecore.xlib.gson.TypeAdapter; -import com.massivecraft.massivecore.xlib.gson.TypeAdapterFactory; +import com.massivecraft.massivecore.xlib.gson.*; import com.massivecraft.massivecore.xlib.gson.annotations.JsonAdapter; import com.massivecraft.massivecore.xlib.gson.annotations.SerializedName; -import com.massivecraft.massivecore.xlib.gson.internal.$Gson$Types; -import com.massivecraft.massivecore.xlib.gson.internal.ConstructorConstructor; -import com.massivecraft.massivecore.xlib.gson.internal.Excluder; -import com.massivecraft.massivecore.xlib.gson.internal.ObjectConstructor; -import com.massivecraft.massivecore.xlib.gson.internal.Primitives; +import com.massivecraft.massivecore.xlib.gson.internal.*; import com.massivecraft.massivecore.xlib.gson.reflect.TypeToken; import com.massivecraft.massivecore.xlib.gson.stream.JsonReader; import com.massivecraft.massivecore.xlib.gson.stream.JsonToken; @@ -36,10 +28,7 @@ import com.massivecraft.massivecore.xlib.gson.stream.JsonWriter; import java.io.IOException; import java.lang.reflect.Field; import java.lang.reflect.Type; -import java.util.LinkedHashMap; -import java.util.Map; - -import static com.massivecraft.massivecore.xlib.gson.internal.bind.JsonAdapterAnnotationTypeAdapterFactory.getTypeAdapter; +import java.util.*; /** * Type adapter that reflects over the fields and methods of a class. @@ -48,12 +37,15 @@ public final class ReflectiveTypeAdapterFactory implements TypeAdapterFactory { private final ConstructorConstructor constructorConstructor; private final FieldNamingStrategy fieldNamingPolicy; private final Excluder excluder; + private final JsonAdapterAnnotationTypeAdapterFactory jsonAdapterFactory; public ReflectiveTypeAdapterFactory(ConstructorConstructor constructorConstructor, - FieldNamingStrategy fieldNamingPolicy, Excluder excluder) { + FieldNamingStrategy fieldNamingPolicy, Excluder excluder, + JsonAdapterAnnotationTypeAdapterFactory jsonAdapterFactory) { this.constructorConstructor = constructorConstructor; this.fieldNamingPolicy = fieldNamingPolicy; this.excluder = excluder; + this.jsonAdapterFactory = jsonAdapterFactory; } public boolean excludeField(Field f, boolean serialize) { @@ -64,16 +56,29 @@ public final class ReflectiveTypeAdapterFactory implements TypeAdapterFactory { return !excluder.excludeClass(f.getType(), serialize) && !excluder.excludeField(f, serialize); } - private String getFieldName(Field f) { - return getFieldName(fieldNamingPolicy, f); + /** first element holds the default name */ + private List getFieldNames(Field f) { + SerializedName annotation = f.getAnnotation(SerializedName.class); + if (annotation == null) { + String name = fieldNamingPolicy.translateName(f); + return Collections.singletonList(name); + } + + String serializedName = annotation.value(); + String[] alternates = annotation.alternate(); + if (alternates.length == 0) { + return Collections.singletonList(serializedName); + } + + List fieldNames = new ArrayList<>(alternates.length + 1); + fieldNames.add(serializedName); + for (String alternate : alternates) { + fieldNames.add(alternate); + } + return fieldNames; } - static String getFieldName(FieldNamingStrategy fieldNamingPolicy, Field f) { - SerializedName serializedName = f.getAnnotation(SerializedName.class); - return serializedName == null ? fieldNamingPolicy.translateName(f) : serializedName.value(); - } - - public TypeAdapter create(Gson gson, final TypeToken type) { + @Override public TypeAdapter create(Gson gson, final TypeToken type) { Class raw = type.getRawType(); if (!Object.class.isAssignableFrom(raw)) { @@ -85,18 +90,27 @@ public final class ReflectiveTypeAdapterFactory implements TypeAdapterFactory { } private ReflectiveTypeAdapterFactory.BoundField createBoundField( - final Gson context, final Field field, final String name, - final TypeToken fieldType, boolean serialize, boolean deserialize) { + final Gson context, final Field field, final String name, + final TypeToken fieldType, boolean serialize, boolean deserialize) { final boolean isPrimitive = Primitives.isPrimitive(fieldType.getRawType()); // special casing primitives here saves ~5% on Android... + JsonAdapter annotation = field.getAnnotation(JsonAdapter.class); + TypeAdapter mapped = null; + if (annotation != null) { + mapped = jsonAdapterFactory.getTypeAdapter( + constructorConstructor, context, fieldType, annotation); + } + final boolean jsonAdapterPresent = mapped != null; + if (mapped == null) mapped = context.getAdapter(fieldType); + + final TypeAdapter typeAdapter = mapped; return new ReflectiveTypeAdapterFactory.BoundField(name, serialize, deserialize) { - final TypeAdapter typeAdapter = getFieldAdapter(context, field, fieldType); @SuppressWarnings({"unchecked", "rawtypes"}) // the type adapter and field type always agree @Override void write(JsonWriter writer, Object value) throws IOException, IllegalAccessException { Object fieldValue = field.get(value); - TypeAdapter t = - new TypeAdapterRuntimeTypeWrapper(context, this.typeAdapter, fieldType.getType()); + TypeAdapter t = jsonAdapterPresent ? typeAdapter + : new TypeAdapterRuntimeTypeWrapper(context, typeAdapter, fieldType.getType()); t.write(writer, fieldValue); } @Override void read(JsonReader reader, Object value) @@ -106,7 +120,7 @@ public final class ReflectiveTypeAdapterFactory implements TypeAdapterFactory { field.set(value, fieldValue); } } - public boolean writeField(Object value) throws IOException, IllegalAccessException { + @Override public boolean writeField(Object value) throws IOException, IllegalAccessException { if (!serialized) return false; Object fieldValue = field.get(value); return fieldValue != value; // avoid recursion for example for Throwable.cause @@ -114,15 +128,6 @@ public final class ReflectiveTypeAdapterFactory implements TypeAdapterFactory { }; } - private TypeAdapter getFieldAdapter(Gson gson, Field field, TypeToken fieldType) { - JsonAdapter annotation = field.getAnnotation(JsonAdapter.class); - if (annotation != null) { - TypeAdapter adapter = getTypeAdapter(constructorConstructor, gson, fieldType, annotation); - if (adapter != null) return adapter; - } - return gson.getAdapter(fieldType); - } - private Map getBoundFields(Gson context, TypeToken type, Class raw) { Map result = new LinkedHashMap<>(); if (raw.isInterface()) { @@ -140,9 +145,16 @@ public final class ReflectiveTypeAdapterFactory implements TypeAdapterFactory { } field.setAccessible(true); Type fieldType = $Gson$Types.resolve(type.getType(), raw, field.getGenericType()); - BoundField boundField = createBoundField(context, field, getFieldName(field), - TypeToken.get(fieldType), serialize, deserialize); - BoundField previous = result.put(boundField.name, boundField); + List fieldNames = getFieldNames(field); + BoundField previous = null; + for (int i = 0; i < fieldNames.size(); ++i) { + String name = fieldNames.get(i); + if (i != 0) serialize = false; // only serialize the default name + BoundField boundField = createBoundField(context, field, name, + TypeToken.get(fieldType), serialize, deserialize); + BoundField replaced = result.put(name, boundField); + if (previous == null) previous = replaced; + } if (previous != null) { throw new IllegalArgumentException(declaredType + " declares multiple JSON fields named " + previous.name); @@ -173,7 +185,7 @@ public final class ReflectiveTypeAdapterFactory implements TypeAdapterFactory { private final ObjectConstructor constructor; private final Map boundFields; - private Adapter(ObjectConstructor constructor, Map boundFields) { + Adapter(ObjectConstructor constructor, Map boundFields) { this.constructor = constructor; this.boundFields = boundFields; } @@ -221,7 +233,7 @@ public final class ReflectiveTypeAdapterFactory implements TypeAdapterFactory { } } } catch (IllegalAccessException e) { - throw new AssertionError(); + throw new AssertionError(e); } out.endObject(); } diff --git a/src/com/massivecraft/massivecore/xlib/gson/internal/bind/SqlDateTypeAdapter.java b/src/com/massivecraft/massivecore/xlib/gson/internal/bind/SqlDateTypeAdapter.java index 3200b14d..ebb7438c 100644 --- a/src/com/massivecraft/massivecore/xlib/gson/internal/bind/SqlDateTypeAdapter.java +++ b/src/com/massivecraft/massivecore/xlib/gson/internal/bind/SqlDateTypeAdapter.java @@ -39,7 +39,7 @@ import java.text.SimpleDateFormat; public final class SqlDateTypeAdapter extends TypeAdapter { public static final TypeAdapterFactory FACTORY = new TypeAdapterFactory() { @SuppressWarnings("unchecked") // we use a runtime check to make sure the 'T's equal - public TypeAdapter create(Gson gson, TypeToken typeToken) { + @Override public TypeAdapter create(Gson gson, TypeToken typeToken) { return typeToken.getRawType() == java.sql.Date.class ? (TypeAdapter) new SqlDateTypeAdapter() : null; } diff --git a/src/com/massivecraft/massivecore/xlib/gson/internal/bind/TimeTypeAdapter.java b/src/com/massivecraft/massivecore/xlib/gson/internal/bind/TimeTypeAdapter.java index eb492973..7fa54ade 100644 --- a/src/com/massivecraft/massivecore/xlib/gson/internal/bind/TimeTypeAdapter.java +++ b/src/com/massivecraft/massivecore/xlib/gson/internal/bind/TimeTypeAdapter.java @@ -41,7 +41,7 @@ import java.util.Date; public final class TimeTypeAdapter extends TypeAdapter