Gson 2.8.0

This commit is contained in:
Mark Hughes 2017-04-29 12:47:17 +10:00 committed by Olof Larsson
parent b7ddb71f04
commit 3e520e7e9f
50 changed files with 1280 additions and 551 deletions

View File

@ -16,14 +16,16 @@
package com.massivecraft.massivecore.xlib.gson; package com.massivecraft.massivecore.xlib.gson;
import com.massivecraft.massivecore.xlib.gson.internal.bind.util.ISO8601Utils;
import java.lang.reflect.Type; import java.lang.reflect.Type;
import java.sql.Timestamp; import java.sql.Timestamp;
import java.text.DateFormat; import java.text.DateFormat;
import java.text.ParseException; import java.text.ParseException;
import java.text.ParsePosition;
import java.text.SimpleDateFormat; import java.text.SimpleDateFormat;
import java.util.Date; import java.util.Date;
import java.util.Locale; import java.util.Locale;
import java.util.TimeZone;
/** /**
* This type adapter supports three subclasses of date: Date, Timestamp, and * This type adapter supports three subclasses of date: Date, Timestamp, and
@ -38,7 +40,6 @@ final class DefaultDateTypeAdapter implements JsonSerializer<Date>, JsonDeserial
private final DateFormat enUsFormat; private final DateFormat enUsFormat;
private final DateFormat localFormat; private final DateFormat localFormat;
private final DateFormat iso8601Format;
DefaultDateTypeAdapter() { DefaultDateTypeAdapter() {
this(DateFormat.getDateTimeInstance(DateFormat.DEFAULT, DateFormat.DEFAULT, Locale.US), this(DateFormat.getDateTimeInstance(DateFormat.DEFAULT, DateFormat.DEFAULT, Locale.US),
@ -61,12 +62,11 @@ final class DefaultDateTypeAdapter implements JsonSerializer<Date>, JsonDeserial
DefaultDateTypeAdapter(DateFormat enUsFormat, DateFormat localFormat) { DefaultDateTypeAdapter(DateFormat enUsFormat, DateFormat localFormat) {
this.enUsFormat = enUsFormat; this.enUsFormat = enUsFormat;
this.localFormat = localFormat; 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 // These methods need to be synchronized since JDK DateFormat classes are not thread-safe
// See issue 162 // See issue 162
@Override
public JsonElement serialize(Date src, Type typeOfSrc, JsonSerializationContext context) { public JsonElement serialize(Date src, Type typeOfSrc, JsonSerializationContext context) {
synchronized (localFormat) { synchronized (localFormat) {
String dateFormatAsString = enUsFormat.format(src); String dateFormatAsString = enUsFormat.format(src);
@ -74,6 +74,7 @@ final class DefaultDateTypeAdapter implements JsonSerializer<Date>, JsonDeserial
} }
} }
@Override
public Date deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) public Date deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
throws JsonParseException { throws JsonParseException {
if (!(json instanceof JsonPrimitive)) { if (!(json instanceof JsonPrimitive)) {
@ -95,14 +96,12 @@ final class DefaultDateTypeAdapter implements JsonSerializer<Date>, JsonDeserial
synchronized (localFormat) { synchronized (localFormat) {
try { try {
return localFormat.parse(json.getAsString()); return localFormat.parse(json.getAsString());
} catch (ParseException ignored) { } catch (ParseException ignored) {}
}
try { try {
return enUsFormat.parse(json.getAsString()); return enUsFormat.parse(json.getAsString());
} catch (ParseException ignored) { } catch (ParseException ignored) {}
}
try { try {
return iso8601Format.parse(json.getAsString()); return ISO8601Utils.parse(json.getAsString(), new ParsePosition(0));
} catch (ParseException e) { } catch (ParseException e) {
throw new JsonSyntaxException(json.getAsString(), e); throw new JsonSyntaxException(json.getAsString(), e);
} }

View File

@ -99,11 +99,11 @@ public interface ExclusionStrategy {
* @param f the field object that is under test * @param f the field object that is under test
* @return true if the field should be ignored; otherwise false * @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 * @param clazz the class object that is under test
* @return true if the class should be ignored; otherwise false * @return true if the class should be ignored; otherwise false
*/ */
boolean shouldSkipClass(Class<?> clazz); public boolean shouldSkipClass(Class<?> clazz);
} }

View File

@ -69,7 +69,7 @@ public final class FieldAttributes {
* private List&lt;String&gt; red; * private List&lt;String&gt; red;
* } * }
* *
* Type listParmeterizedType = new TypeToken&lt;List&lt;String&gt;&gt;() {}.getType(); * Type listParameterizedType = new TypeToken&lt;List&lt;String&gt;&gt;() {}.getType();
* </pre> * </pre>
* *
* <p>This method would return {@code String.class} for the {@code bar} field and * <p>This method would return {@code String.class} for the {@code bar} field and

View File

@ -17,11 +17,12 @@
package com.massivecraft.massivecore.xlib.gson; package com.massivecraft.massivecore.xlib.gson;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.util.Locale;
/** /**
* An enumeration that defines a few standard naming conventions for JSON field names. * 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} * This enumeration should be used in conjunction with {@link GsonBuilder}
* to configure a {@link com.massivecraft.massivecore.xlib.gson.Gson} instance to properly translate Java field * to configure a {@link Gson} instance to properly translate Java field
* names into the desired JSON field names. * names into the desired JSON field names.
* *
* @author Inderjeet Singh * @author Inderjeet Singh
@ -34,7 +35,7 @@ public enum FieldNamingPolicy implements FieldNamingStrategy {
* unchanged. * unchanged.
*/ */
IDENTITY() { IDENTITY() {
public String translateName(Field f) { @Override public String translateName(Field f) {
return f.getName(); return f.getName();
} }
}, },
@ -50,7 +51,7 @@ public enum FieldNamingPolicy implements FieldNamingStrategy {
* </ul> * </ul>
*/ */
UPPER_CAMEL_CASE() { UPPER_CAMEL_CASE() {
public String translateName(Field f) { @Override public String translateName(Field f) {
return upperCaseFirstLetter(f.getName()); return upperCaseFirstLetter(f.getName());
} }
}, },
@ -69,7 +70,7 @@ public enum FieldNamingPolicy implements FieldNamingStrategy {
* @since 1.4 * @since 1.4
*/ */
UPPER_CAMEL_CASE_WITH_SPACES() { UPPER_CAMEL_CASE_WITH_SPACES() {
public String translateName(Field f) { @Override public String translateName(Field f) {
return upperCaseFirstLetter(separateCamelCase(f.getName(), " ")); return upperCaseFirstLetter(separateCamelCase(f.getName(), " "));
} }
}, },
@ -87,8 +88,8 @@ public enum FieldNamingPolicy implements FieldNamingStrategy {
* </ul> * </ul>
*/ */
LOWER_CASE_WITH_UNDERSCORES() { LOWER_CASE_WITH_UNDERSCORES() {
public String translateName(Field f) { @Override public String translateName(Field f) {
return separateCamelCase(f.getName(), "_").toLowerCase(); return separateCamelCase(f.getName(), "_").toLowerCase(Locale.ENGLISH);
} }
}, },
@ -110,8 +111,8 @@ public enum FieldNamingPolicy implements FieldNamingStrategy {
* @since 1.4 * @since 1.4
*/ */
LOWER_CASE_WITH_DASHES() { LOWER_CASE_WITH_DASHES() {
public String translateName(Field f) { @Override public String translateName(Field f) {
return separateCamelCase(f.getName(), "-").toLowerCase(); 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 * Converts the field name that uses camel-case define word separation into
* separate words that are separated by the provided {@code separatorString}. * 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(); StringBuilder translation = new StringBuilder();
for (int i = 0; i < name.length(); i++) { for (int i = 0; i < name.length(); i++) {
char character = name.charAt(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. * 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(); StringBuilder fieldNameBuilder = new StringBuilder();
int index = 0; int index = 0;
char firstCharacter = name.charAt(index); char firstCharacter = name.charAt(index);

View File

@ -36,5 +36,5 @@ public interface FieldNamingStrategy {
* @return the translated field name. * @return the translated field name.
* @since 1.3 * @since 1.3
*/ */
String translateName(Field f); public String translateName(Field f);
} }

View File

@ -16,47 +16,33 @@
package com.massivecraft.massivecore.xlib.gson; 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.ConstructorConstructor;
import com.massivecraft.massivecore.xlib.gson.internal.Excluder; import com.massivecraft.massivecore.xlib.gson.internal.Excluder;
import com.massivecraft.massivecore.xlib.gson.internal.Primitives; import com.massivecraft.massivecore.xlib.gson.internal.Primitives;
import com.massivecraft.massivecore.xlib.gson.internal.Streams; 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.*;
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.reflect.TypeToken; import com.massivecraft.massivecore.xlib.gson.reflect.TypeToken;
import com.massivecraft.massivecore.xlib.gson.stream.JsonReader; import com.massivecraft.massivecore.xlib.gson.stream.JsonReader;
import com.massivecraft.massivecore.xlib.gson.stream.JsonToken; import com.massivecraft.massivecore.xlib.gson.stream.JsonToken;
import com.massivecraft.massivecore.xlib.gson.stream.JsonWriter; import com.massivecraft.massivecore.xlib.gson.stream.JsonWriter;
import com.massivecraft.massivecore.xlib.gson.stream.MalformedJsonException; import com.massivecraft.massivecore.xlib.gson.stream.MalformedJsonException;
import java.io.EOFException; import java.io.*;
import java.io.IOException;
import java.io.Reader;
import java.io.StringReader;
import java.io.StringWriter;
import java.io.Writer;
import java.lang.reflect.Type; import java.lang.reflect.Type;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.math.BigInteger; import java.math.BigInteger;
import java.util.ArrayList; import java.util.*;
import java.util.Collections; import java.util.concurrent.ConcurrentHashMap;
import java.util.HashMap; import java.util.concurrent.atomic.AtomicLong;
import java.util.List; import java.util.concurrent.atomic.AtomicLongArray;
import java.util.Map;
/** /**
* This is the main class for using Gson. Gson is typically used by first constructing a * 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)} * 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.
* *
* <p>You can create a Gson instance by invoking {@code new Gson()} if the default configuration * <p>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 * 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;
* <p>If the object that your are serializing/deserializing is a {@code ParameterizedType} * <p>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 * (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 * {@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}:
* *
* <pre> * <pre>
* Type listType = new TypeToken&lt;List&lt;String&gt;&gt;() {}.getType(); * Type listType = new TypeToken&lt;List&lt;String&gt;&gt;() {}.getType();
@ -90,7 +76,7 @@ import java.util.Map;
* <p>See the <a href="https://sites.google.com/site/gson/gson-user-guide">Gson User Guide</a> * <p>See the <a href="https://sites.google.com/site/gson/gson-user-guide">Gson User Guide</a>
* for a more complete set of examples.</p> * for a more complete set of examples.</p>
* *
* @see com.massivecraft.massivecore.xlib.gson.reflect.TypeToken * @see TypeToken
* *
* @author Inderjeet Singh * @author Inderjeet Singh
* @author Joel Leitch * @author Joel Leitch
@ -98,7 +84,14 @@ import java.util.Map;
*/ */
public final class Gson { public final class Gson {
static final boolean DEFAULT_JSON_NON_EXECUTABLE = false; 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<Object>() {};
private static final String JSON_NON_EXECUTABLE_PREFIX = ")]}'\n"; private static final String JSON_NON_EXECUTABLE_PREFIX = ")]}'\n";
/** /**
@ -111,32 +104,19 @@ public final class Gson {
private final ThreadLocal<Map<TypeToken<?>, FutureTypeAdapter<?>>> calls private final ThreadLocal<Map<TypeToken<?>, FutureTypeAdapter<?>>> calls
= new ThreadLocal<>(); = new ThreadLocal<>();
private final Map<TypeToken<?>, TypeAdapter<?>> typeTokenCache private final Map<TypeToken<?>, TypeAdapter<?>> typeTokenCache = new ConcurrentHashMap<>();
= Collections.synchronizedMap(new HashMap<TypeToken<?>, TypeAdapter<?>>());
private final List<TypeAdapterFactory> factories; private final List<TypeAdapterFactory> factories;
private final ConstructorConstructor constructorConstructor; private final ConstructorConstructor constructorConstructor;
private final Excluder excluder;
private final FieldNamingStrategy fieldNamingStrategy;
private final boolean serializeNulls; private final boolean serializeNulls;
private final boolean htmlSafe; private final boolean htmlSafe;
private final boolean generateNonExecutableJson; private final boolean generateNonExecutableJson;
private final boolean prettyPrinting; private final boolean prettyPrinting;
private final boolean lenient;
final JsonDeserializationContext deserializationContext = new JsonDeserializationContext() { private final JsonAdapterAnnotationTypeAdapterFactory jsonAdapterFactory;
@SuppressWarnings("unchecked")
public <T> 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);
}
};
/** /**
* Constructs a Gson object with default configuration. The default configuration has the * 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 * ignores the millisecond portion of the date during serialization. You can change
* this by invoking {@link GsonBuilder#setDateFormat(int)} or * this by invoking {@link GsonBuilder#setDateFormat(int)} or
* {@link GsonBuilder#setDateFormat(String)}. </li> * {@link GsonBuilder#setDateFormat(String)}. </li>
* <li>By default, Gson ignores the {@link com.massivecraft.massivecore.xlib.gson.annotations.Expose} annotation. * <li>By default, Gson ignores the {@link Expose} annotation.
* You can enable Gson to serialize/deserialize only those fields marked with this annotation * You can enable Gson to serialize/deserialize only those fields marked with this annotation
* through {@link GsonBuilder#excludeFieldsWithoutExposeAnnotation()}. </li> * through {@link GsonBuilder#excludeFieldsWithoutExposeAnnotation()}. </li>
* <li>By default, Gson ignores the {@link com.massivecraft.massivecore.xlib.gson.annotations.Since} annotation. You * <li>By default, Gson ignores the {@link Since} annotation. You
* can enable Gson to use this annotation through {@link GsonBuilder#setVersion(double)}.</li> * can enable Gson to use this annotation through {@link GsonBuilder#setVersion(double)}.</li>
* <li>The default field naming policy for the output Json is same as in Java. So, a Java class * <li>The default field naming policy for the output Json is same as in Java. So, a Java class
* field <code>versionNumber</code> will be output as <code>&quot;versionNumber&quot;</code> in * field <code>versionNumber</code> will be output as <code>&quot;versionNumber&quot;</code> in
@ -174,22 +154,26 @@ public final class Gson {
*/ */
public Gson() { public Gson() {
this(Excluder.DEFAULT, FieldNamingPolicy.IDENTITY, this(Excluder.DEFAULT, FieldNamingPolicy.IDENTITY,
Collections.<Type, InstanceCreator<?>>emptyMap(), false, false, DEFAULT_JSON_NON_EXECUTABLE, Collections.<Type, InstanceCreator<?>>emptyMap(), DEFAULT_SERIALIZE_NULLS,
true, false, false, LongSerializationPolicy.DEFAULT, DEFAULT_COMPLEX_MAP_KEYS, DEFAULT_JSON_NON_EXECUTABLE, DEFAULT_ESCAPE_HTML,
Collections.<TypeAdapterFactory>emptyList()); DEFAULT_PRETTY_PRINT, DEFAULT_LENIENT, DEFAULT_SPECIALIZE_FLOAT_VALUES,
LongSerializationPolicy.DEFAULT, Collections.<TypeAdapterFactory>emptyList());
} }
Gson(final Excluder excluder, final FieldNamingStrategy fieldNamingPolicy, Gson(final Excluder excluder, final FieldNamingStrategy fieldNamingStrategy,
final Map<Type, InstanceCreator<?>> instanceCreators, boolean serializeNulls, final Map<Type, InstanceCreator<?>> instanceCreators, boolean serializeNulls,
boolean complexMapKeySerialization, boolean generateNonExecutableGson, boolean htmlSafe, boolean complexMapKeySerialization, boolean generateNonExecutableGson, boolean htmlSafe,
boolean prettyPrinting, boolean serializeSpecialFloatingPointValues, boolean prettyPrinting, boolean lenient, boolean serializeSpecialFloatingPointValues,
LongSerializationPolicy longSerializationPolicy, LongSerializationPolicy longSerializationPolicy,
List<TypeAdapterFactory> typeAdapterFactories) { List<TypeAdapterFactory> typeAdapterFactories) {
this.constructorConstructor = new ConstructorConstructor(instanceCreators); this.constructorConstructor = new ConstructorConstructor(instanceCreators);
this.excluder = excluder;
this.fieldNamingStrategy = fieldNamingStrategy;
this.serializeNulls = serializeNulls; this.serializeNulls = serializeNulls;
this.generateNonExecutableJson = generateNonExecutableGson; this.generateNonExecutableJson = generateNonExecutableGson;
this.htmlSafe = htmlSafe; this.htmlSafe = htmlSafe;
this.prettyPrinting = prettyPrinting; this.prettyPrinting = prettyPrinting;
this.lenient = lenient;
List<TypeAdapterFactory> factories = new ArrayList<>(); List<TypeAdapterFactory> factories = new ArrayList<>();
@ -209,13 +193,18 @@ public final class Gson {
factories.add(TypeAdapters.BOOLEAN_FACTORY); factories.add(TypeAdapters.BOOLEAN_FACTORY);
factories.add(TypeAdapters.BYTE_FACTORY); factories.add(TypeAdapters.BYTE_FACTORY);
factories.add(TypeAdapters.SHORT_FACTORY); factories.add(TypeAdapters.SHORT_FACTORY);
factories.add(TypeAdapters.newFactory(long.class, Long.class, TypeAdapter<Number> longAdapter = longAdapter(longSerializationPolicy);
longAdapter(longSerializationPolicy))); factories.add(TypeAdapters.newFactory(long.class, Long.class, longAdapter));
factories.add(TypeAdapters.newFactory(double.class, Double.class, factories.add(TypeAdapters.newFactory(double.class, Double.class,
doubleAdapter(serializeSpecialFloatingPointValues))); doubleAdapter(serializeSpecialFloatingPointValues)));
factories.add(TypeAdapters.newFactory(float.class, Float.class, factories.add(TypeAdapters.newFactory(float.class, Float.class,
floatAdapter(serializeSpecialFloatingPointValues))); floatAdapter(serializeSpecialFloatingPointValues)));
factories.add(TypeAdapters.NUMBER_FACTORY); 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.CHARACTER_FACTORY);
factories.add(TypeAdapters.STRING_BUILDER_FACTORY); factories.add(TypeAdapters.STRING_BUILDER_FACTORY);
factories.add(TypeAdapters.STRING_BUFFER_FACTORY); factories.add(TypeAdapters.STRING_BUFFER_FACTORY);
@ -224,6 +213,7 @@ public final class Gson {
factories.add(TypeAdapters.URL_FACTORY); factories.add(TypeAdapters.URL_FACTORY);
factories.add(TypeAdapters.URI_FACTORY); factories.add(TypeAdapters.URI_FACTORY);
factories.add(TypeAdapters.UUID_FACTORY); factories.add(TypeAdapters.UUID_FACTORY);
factories.add(TypeAdapters.CURRENCY_FACTORY);
factories.add(TypeAdapters.LOCALE_FACTORY); factories.add(TypeAdapters.LOCALE_FACTORY);
factories.add(TypeAdapters.INET_ADDRESS_FACTORY); factories.add(TypeAdapters.INET_ADDRESS_FACTORY);
factories.add(TypeAdapters.BIT_SET_FACTORY); factories.add(TypeAdapters.BIT_SET_FACTORY);
@ -238,14 +228,31 @@ public final class Gson {
// type adapters for composite and user-defined types // type adapters for composite and user-defined types
factories.add(new CollectionTypeAdapterFactory(constructorConstructor)); factories.add(new CollectionTypeAdapterFactory(constructorConstructor));
factories.add(new MapTypeAdapterFactory(constructorConstructor, complexMapKeySerialization)); 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(TypeAdapters.ENUM_FACTORY);
factories.add(new ReflectiveTypeAdapterFactory( factories.add(new ReflectiveTypeAdapterFactory(
constructorConstructor, fieldNamingPolicy, excluder)); constructorConstructor, fieldNamingStrategy, excluder, jsonAdapterFactory));
this.factories = Collections.unmodifiableList(factories); 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<Number> doubleAdapter(boolean serializeSpecialFloatingPointValues) { private TypeAdapter<Number> doubleAdapter(boolean serializeSpecialFloatingPointValues) {
if (serializeSpecialFloatingPointValues) { if (serializeSpecialFloatingPointValues) {
return TypeAdapters.DOUBLE; 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)) { if (Double.isNaN(value) || Double.isInfinite(value)) {
throw new IllegalArgumentException(value throw new IllegalArgumentException(value
+ " is not a valid double value as per JSON specification. To override this" + " is not a valid double value as per JSON specification. To override this"
@ -302,7 +309,7 @@ public final class Gson {
} }
} }
private TypeAdapter<Number> longAdapter(LongSerializationPolicy longSerializationPolicy) { private static TypeAdapter<Number> longAdapter(LongSerializationPolicy longSerializationPolicy) {
if (longSerializationPolicy == LongSerializationPolicy.DEFAULT) { if (longSerializationPolicy == LongSerializationPolicy.DEFAULT) {
return TypeAdapters.LONG; return TypeAdapters.LONG;
} }
@ -324,6 +331,45 @@ public final class Gson {
}; };
} }
private static TypeAdapter<AtomicLong> atomicLongAdapter(final TypeAdapter<Number> longAdapter) {
return new TypeAdapter<AtomicLong>() {
@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<AtomicLongArray> atomicLongArrayAdapter(final TypeAdapter<Number> longAdapter) {
return new TypeAdapter<AtomicLongArray>() {
@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<Long> 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. * Returns the type adapter for {@code} type.
* *
@ -332,7 +378,7 @@ public final class Gson {
*/ */
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public <T> TypeAdapter<T> getAdapter(TypeToken<T> type) { public <T> TypeAdapter<T> getAdapter(TypeToken<T> type) {
TypeAdapter<?> cached = typeTokenCache.get(type); TypeAdapter<?> cached = typeTokenCache.get(type == null ? NULL_KEY_SURROGATE : type);
if (cached != null) { if (cached != null) {
return (TypeAdapter<T>) cached; return (TypeAdapter<T>) cached;
} }
@ -386,9 +432,9 @@ public final class Gson {
* class StatsTypeAdapterFactory implements TypeAdapterFactory { * class StatsTypeAdapterFactory implements TypeAdapterFactory {
* public int numReads = 0; * public int numReads = 0;
* public int numWrites = 0; * public int numWrites = 0;
* public &lt;T&gt; TypeAdapter&lt;T&gt; create(Gson gson, TypeToken&lt;T&gt; type) { * public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type) {
* final TypeAdapter&lt;T&gt; delegate = gson.getDelegateAdapter(this, type); * final TypeAdapter<T> delegate = gson.getDelegateAdapter(this, type);
* return new TypeAdapter&lt;T&gt;() { * return new TypeAdapter<T>() {
* public void write(JsonWriter out, T value) throws IOException { * public void write(JsonWriter out, T value) throws IOException {
* ++numWrites; * ++numWrites;
* delegate.write(out, value); * 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 reads" + stats.numReads);
* System.out.println("Num JSON writes" + stats.numWrites); * System.out.println("Num JSON writes" + stats.numWrites);
* }</pre> * }</pre>
* 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 * 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 * types, our stats factory will not count the number of String or primitives that will be
* read or written. * read or written.
@ -420,12 +470,13 @@ public final class Gson {
* @since 2.2 * @since 2.2
*/ */
public <T> TypeAdapter<T> getDelegateAdapter(TypeAdapterFactory skipPast, TypeToken<T> type) { public <T> TypeAdapter<T> getDelegateAdapter(TypeAdapterFactory skipPast, TypeToken<T> type) {
boolean skipPastFound = false; // Hack. If the skipPast factory isn't registered, assume the factory is being requested via
// Skip past if and only if the specified factory is present in the factories. // our @JsonAdapter annotation.
// This is useful because the factories created through JsonAdapter annotations are not if (!factories.contains(skipPast)) {
// registered in this list. skipPast = jsonAdapterFactory;
if (!factories.contains(skipPast)) skipPastFound = true; }
boolean skipPastFound = false;
for (TypeAdapterFactory factory : factories) { for (TypeAdapterFactory factory : factories) {
if (!skipPastFound) { if (!skipPastFound) {
if (factory == skipPast) { if (factory == skipPast) {
@ -480,7 +531,7 @@ public final class Gson {
* *
* @param src the object for which JSON representation is to be created * @param src the object for which JSON representation is to be created
* @param typeOfSrc The specific genericized type of src. You can obtain * @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<Foo>}, you should use: * to get the type for {@code Collection<Foo>}, you should use:
* <pre> * <pre>
* Type typeOfSrc = new TypeToken&lt;Collection&lt;Foo&gt;&gt;(){}.getType(); * Type typeOfSrc = new TypeToken&lt;Collection&lt;Foo&gt;&gt;(){}.getType();
@ -522,7 +573,7 @@ public final class Gson {
* *
* @param src the object for which JSON representation is to be created * @param src the object for which JSON representation is to be created
* @param typeOfSrc The specific genericized type of src. You can obtain * @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<Foo>}, you should use: * to get the type for {@code Collection<Foo>}, you should use:
* <pre> * <pre>
* Type typeOfSrc = new TypeToken&lt;Collection&lt;Foo&gt;&gt;(){}.getType(); * Type typeOfSrc = new TypeToken&lt;Collection&lt;Foo&gt;&gt;(){}.getType();
@ -564,7 +615,7 @@ public final class Gson {
* *
* @param src the object for which JSON representation is to be created * @param src the object for which JSON representation is to be created
* @param typeOfSrc The specific genericized type of src. You can obtain * @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<Foo>}, you should use: * to get the type for {@code Collection<Foo>}, you should use:
* <pre> * <pre>
* Type typeOfSrc = new TypeToken&lt;Collection&lt;Foo&gt;&gt;(){}.getType(); * Type typeOfSrc = new TypeToken&lt;Collection&lt;Foo&gt;&gt;(){}.getType();
@ -633,15 +684,14 @@ public final class Gson {
JsonWriter jsonWriter = newJsonWriter(Streams.writerForAppendable(writer)); JsonWriter jsonWriter = newJsonWriter(Streams.writerForAppendable(writer));
toJson(jsonElement, jsonWriter); toJson(jsonElement, jsonWriter);
} catch (IOException e) { } 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 * Returns a new JSON writer configured for the settings on this Gson instance.
* prefix if that is configured.
*/ */
private JsonWriter newJsonWriter(Writer writer) throws IOException { public JsonWriter newJsonWriter(Writer writer) throws IOException {
if (generateNonExecutableJson) { if (generateNonExecutableJson) {
writer.write(JSON_NON_EXECUTABLE_PREFIX); writer.write(JSON_NON_EXECUTABLE_PREFIX);
} }
@ -653,6 +703,15 @@ public final class Gson {
return jsonWriter; 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}. * Writes the JSON for {@code jsonElement} to {@code writer}.
* @throws JsonIOException if there was a problem writing to the writer * @throws JsonIOException if there was a problem writing to the writer
@ -706,7 +765,7 @@ public final class Gson {
* @param <T> the type of the desired object * @param <T> the type of the desired object
* @param json the string from which the object is to be deserialized * @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 * @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<Foo>}, you should use: * {@code Collection<Foo>}, you should use:
* <pre> * <pre>
* Type typeOfT = new TypeToken&lt;Collection&lt;Foo&gt;&gt;(){}.getType(); * Type typeOfT = new TypeToken&lt;Collection&lt;Foo&gt;&gt;(){}.getType();
@ -759,7 +818,7 @@ public final class Gson {
* @param <T> the type of the desired object * @param <T> the type of the desired object
* @param json the reader producing Json from which the object is to be deserialized * @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 * @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<Foo>}, you should use: * {@code Collection<Foo>}, you should use:
* <pre> * <pre>
* Type typeOfT = new TypeToken&lt;Collection&lt;Foo&gt;&gt;(){}.getType(); * Type typeOfT = new TypeToken&lt;Collection&lt;Foo&gt;&gt;(){}.getType();
@ -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 * @param json the root of the parse tree of {@link JsonElement}s from which the object is to
* be deserialized * be deserialized
* @param typeOfT The specific genericized type of src. You can obtain this type by using the * @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<Foo>}, you should use: * {@code Collection<Foo>}, you should use:
* <pre> * <pre>
* Type typeOfT = new TypeToken&lt;Collection&lt;Foo&gt;&gt;(){}.getType(); * Type typeOfT = new TypeToken&lt;Collection&lt;Foo&gt;&gt;(){}.getType();

View File

@ -16,20 +16,18 @@
package com.massivecraft.massivecore.xlib.gson; 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.$Gson$Preconditions;
import com.massivecraft.massivecore.xlib.gson.internal.Excluder; 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.internal.bind.TypeAdapters;
import com.massivecraft.massivecore.xlib.gson.reflect.TypeToken; import com.massivecraft.massivecore.xlib.gson.reflect.TypeToken;
import com.massivecraft.massivecore.xlib.gson.stream.JsonReader;
import java.lang.reflect.Type; import java.lang.reflect.Type;
import java.sql.Timestamp; import java.sql.Timestamp;
import java.text.DateFormat; import java.text.DateFormat;
import java.util.ArrayList; import java.util.*;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/** /**
* <p>Use this builder to construct a {@link Gson} instance when you need to set configuration * <p>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<TypeAdapterFactory> factories = new ArrayList<>(); private final List<TypeAdapterFactory> factories = new ArrayList<>();
/** tree-style hierarchy factories. These come after factories for backwards compatibility. */ /** tree-style hierarchy factories. These come after factories for backwards compatibility. */
private final List<TypeAdapterFactory> hierarchyFactories = new ArrayList<>(); private final List<TypeAdapterFactory> hierarchyFactories = new ArrayList<>();
private boolean serializeNulls; private boolean serializeNulls = Gson.DEFAULT_SERIALIZE_NULLS;
private String datePattern; private String datePattern;
private int dateStyle = DateFormat.DEFAULT; private int dateStyle = DateFormat.DEFAULT;
private int timeStyle = DateFormat.DEFAULT; private int timeStyle = DateFormat.DEFAULT;
private boolean complexMapKeySerialization; private boolean complexMapKeySerialization = Gson.DEFAULT_COMPLEX_MAP_KEYS;
private boolean serializeSpecialFloatingPointValues; private boolean serializeSpecialFloatingPointValues = Gson.DEFAULT_SPECIALIZE_FLOAT_VALUES;
private boolean escapeHtmlChars = true; private boolean escapeHtmlChars = Gson.DEFAULT_ESCAPE_HTML;
private boolean prettyPrinting; private boolean prettyPrinting = Gson.DEFAULT_PRETTY_PRINT;
private boolean generateNonExecutableJson; 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 * 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 * 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 * @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 * 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. * 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 * 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. * @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 * @return a reference to this {@code GsonBuilder} object to fulfill the "Builder" pattern
@ -351,6 +350,19 @@ public final class GsonBuilder {
return this; return this;
} }
/**
* By default, Gson is strict and only accepts JSON as specified by
* <a href="http://www.ietf.org/rfc/rfc4627.txt">RFC 4627</a>. 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 &lt; &gt; etc. Use this option to configure * By default, Gson escapes HTML characters such as &lt; &gt; etc. Use this option to configure
* Gson to pass-through HTML characters as is. * Gson to pass-through HTML characters as is.
@ -544,7 +556,7 @@ public final class GsonBuilder {
return new Gson(excluder, fieldNamingPolicy, instanceCreators, return new Gson(excluder, fieldNamingPolicy, instanceCreators,
serializeNulls, complexMapKeySerialization, serializeNulls, complexMapKeySerialization,
generateNonExecutableJson, escapeHtmlChars, prettyPrinting, generateNonExecutableJson, escapeHtmlChars, prettyPrinting, lenient,
serializeSpecialFloatingPointValues, longSerializationPolicy, factories); serializeSpecialFloatingPointValues, longSerializationPolicy, factories);
} }

View File

@ -88,5 +88,5 @@ public interface InstanceCreator<T> {
* @param type the parameterized T represented as a {@link Type}. * @param type the parameterized T represented as a {@link Type}.
* @return a default object instance of type T. * @return a default object instance of type T.
*/ */
T createInstance(Type type); public T createInstance(Type type);
} }

View File

@ -49,6 +49,42 @@ public final class JsonArray extends JsonElement implements Iterable<JsonElement
return result; return result;
} }
/**
* Adds the specified boolean to self.
*
* @param bool the boolean that needs to be added to the array.
*/
public void add(Boolean bool) {
elements.add(bool == null ? JsonNull.INSTANCE : new JsonPrimitive(bool));
}
/**
* Adds the specified character to self.
*
* @param character the character that needs to be added to the array.
*/
public void add(Character character) {
elements.add(character == null ? JsonNull.INSTANCE : new JsonPrimitive(character));
}
/**
* Adds the specified number to self.
*
* @param number the number that needs to be added to the array.
*/
public void add(Number number) {
elements.add(number == null ? JsonNull.INSTANCE : new JsonPrimitive(number));
}
/**
* Adds the specified string to self.
*
* @param string the string that needs to be added to the array.
*/
public void add(String string) {
elements.add(string == null ? JsonNull.INSTANCE : new JsonPrimitive(string));
}
/** /**
* Adds the specified element to self. * Adds the specified element to self.
* *
@ -126,7 +162,7 @@ public final class JsonArray extends JsonElement implements Iterable<JsonElement
} }
/** /**
* Returns an iterator to navigate the elemetns of the array. Since the array is an ordered list, * Returns an iterator to navigate the elements of the array. Since the array is an ordered list,
* the iterator navigates the elements in the order they were inserted. * the iterator navigates the elements in the order they were inserted.
* *
* @return an iterator to navigate the elements of the array. * @return an iterator to navigate the elements of the array.

View File

@ -40,5 +40,5 @@ public interface JsonDeserializationContext {
* @return An object of type typeOfT. * @return An object of type typeOfT.
* @throws JsonParseException if the parse tree does not contain expected data. * @throws JsonParseException if the parse tree does not contain expected data.
*/ */
<T> T deserialize(JsonElement json, Type typeOfT) throws JsonParseException; public <T> T deserialize(JsonElement json, Type typeOfT) throws JsonParseException;
} }

View File

@ -86,6 +86,6 @@ public interface JsonDeserializer<T> {
* @return a deserialized object of the specified type typeOfT which is a subclass of {@code T} * @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} * @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; throws JsonParseException;
} }

View File

@ -132,6 +132,15 @@ public final class JsonObject extends JsonElement {
return members.entrySet(); 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. * Convenience method to check if a member with the specified name is present in this object.
* *

View File

@ -33,7 +33,7 @@ public interface JsonSerializationContext {
* @param src the object that needs to be serialized. * @param src the object that needs to be serialized.
* @return a tree of {@link JsonElement}s corresponding to the serialized form of {@code src}. * @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. * 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. * @param typeOfSrc the actual genericized type of src object.
* @return a tree of {@link JsonElement}s corresponding to the serialized form of {@code src}. * @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);
} }

View File

@ -21,7 +21,7 @@ import java.lang.reflect.Type;
/** /**
* Interface representing a custom serializer for Json. You should write a custom serializer, if * 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 * 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)}.
* *
* <p>Let us look at example where defining a serializer will be useful. The {@code Id} class * <p>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}.</p> * defined below has two fields: {@code clazz} and {@code value}.</p>
@ -85,5 +85,5 @@ public interface JsonSerializer<T> {
* @param typeOfSrc the actual type (fully genericized version) of the source object. * @param typeOfSrc the actual type (fully genericized version) of the source object.
* @return a JsonElement corresponding to the specified 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);
} }

View File

@ -32,7 +32,7 @@ public enum LongSerializationPolicy {
* {@code {"f":123}}. * {@code {"f":123}}.
*/ */
DEFAULT() { DEFAULT() {
public JsonElement serialize(Long value) { @Override public JsonElement serialize(Long value) {
return new JsonPrimitive(value); return new JsonPrimitive(value);
} }
}, },
@ -43,7 +43,7 @@ public enum LongSerializationPolicy {
* {@code {"f":"123"}}. * {@code {"f":"123"}}.
*/ */
STRING() { STRING() {
public JsonElement serialize(Long value) { @Override public JsonElement serialize(Long value) {
return new JsonPrimitive(String.valueOf(value)); return new JsonPrimitive(String.valueOf(value));
} }
}; };

View File

@ -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.JsonToken;
import com.massivecraft.massivecore.xlib.gson.stream.JsonWriter; import com.massivecraft.massivecore.xlib.gson.stream.JsonWriter;
import java.io.IOException; import java.io.*;
import java.io.Reader;
import java.io.StringReader;
import java.io.StringWriter;
import java.io.Writer;
/** /**
* Converts Java objects to and from JSON. * Converts Java objects to and from JSON.
@ -132,7 +128,7 @@ public abstract class TypeAdapter<T> {
* Unlike Gson's similar {@link Gson#toJson(JsonElement, Appendable) toJson} * Unlike Gson's similar {@link Gson#toJson(JsonElement, Appendable) toJson}
* method, this write is strict. Create a {@link * method, this write is strict. Create a {@link
* JsonWriter#setLenient(boolean) lenient} {@code JsonWriter} and call * 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. * writing.
* *
* @param value the Java object to convert. May be null. * @param value the Java object to convert. May be null.
@ -206,15 +202,19 @@ public abstract class TypeAdapter<T> {
* Converts {@code value} to a JSON document. Unlike Gson's similar {@link * Converts {@code value} to a JSON document. Unlike Gson's similar {@link
* Gson#toJson(Object) toJson} method, this write is strict. Create a {@link * Gson#toJson(Object) toJson} method, this write is strict. Create a {@link
* JsonWriter#setLenient(boolean) lenient} {@code JsonWriter} and call * 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. * writing.
* *
* @param value the Java object to convert. May be null. * @param value the Java object to convert. May be null.
* @since 2.2 * @since 2.2
*/ */
public final String toJson(T value) throws IOException { public final String toJson(T value) {
StringWriter stringWriter = new StringWriter(); StringWriter stringWriter = new StringWriter();
try {
toJson(stringWriter, value); toJson(stringWriter, value);
} catch (IOException e) {
throw new AssertionError(e); // No I/O writing to a StringWriter.
}
return stringWriter.toString(); return stringWriter.toString();
} }

View File

@ -80,7 +80,7 @@ import com.massivecraft.massivecore.xlib.gson.reflect.TypeToken;
* mapping from lowercase name to enum value is computed eagerly. * mapping from lowercase name to enum value is computed eagerly.
* *
* <p>As with type adapters, factories must be <i>registered</i> with a {@link * <p>As with type adapters, factories must be <i>registered</i> with a {@link
* com.massivecraft.massivecore.xlib.gson.GsonBuilder} for them to take effect: <pre> {@code * GsonBuilder} for them to take effect: <pre> {@code
* *
* GsonBuilder builder = new GsonBuilder(); * GsonBuilder builder = new GsonBuilder();
* builder.registerTypeAdapterFactory(new LowercaseEnumTypeAdapterFactory()); * builder.registerTypeAdapterFactory(new LowercaseEnumTypeAdapterFactory());

View File

@ -16,18 +16,18 @@
package com.massivecraft.massivecore.xlib.gson.annotations; package com.massivecraft.massivecore.xlib.gson.annotations;
import java.lang.annotation.ElementType; import com.massivecraft.massivecore.xlib.gson.Gson;
import java.lang.annotation.Retention; import com.massivecraft.massivecore.xlib.gson.GsonBuilder;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target; import java.lang.annotation.*;
/** /**
* An annotation that indicates this member should be exposed for JSON * An annotation that indicates this member should be exposed for JSON
* serialization or deserialization. * serialization or deserialization.
* *
* <p>This annotation has no effect unless you build {@link com.massivecraft.massivecore.xlib.gson.Gson} * <p>This annotation has no effect unless you build {@link Gson}
* with a {@link com.massivecraft.massivecore.xlib.gson.GsonBuilder} and invoke * with a {@link GsonBuilder} and invoke
* {@link com.massivecraft.massivecore.xlib.gson.GsonBuilder#excludeFieldsWithoutExposeAnnotation()} * {@link GsonBuilder#excludeFieldsWithoutExposeAnnotation()}
* method.</p> * method.</p>
* *
* <p>Here is an example of how this annotation is meant to be used: * <p>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 Inderjeet Singh
* @author Joel Leitch * @author Joel Leitch
*/ */
@Documented
@Retention(RetentionPolicy.RUNTIME) @Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD) @Target(ElementType.FIELD)
public @interface Expose { public @interface Expose {
@ -67,7 +68,7 @@ public @interface Expose {
* serialized output. Defaults to {@code true}. * serialized output. Defaults to {@code true}.
* @since 1.4 * @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. * 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}. * Defaults to {@code true}.
* @since 1.4 * @since 1.4
*/ */
boolean deserialize() default true; public boolean deserialize() default true;
} }

View File

@ -16,6 +16,7 @@
package com.massivecraft.massivecore.xlib.gson.annotations; 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.TypeAdapter;
import com.massivecraft.massivecore.xlib.gson.TypeAdapterFactory; import com.massivecraft.massivecore.xlib.gson.TypeAdapterFactory;
@ -73,7 +74,7 @@ import java.lang.annotation.Target;
* </pre> * </pre>
* *
* It's possible to specify different type adapters on a field, that * 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 * annotations take precedence over {@code GsonBuilder}-registered type
* adapters, which in turn take precedence over annotated types. * adapters, which in turn take precedence over annotated types.
* *
@ -95,4 +96,7 @@ public @interface JsonAdapter {
/** Either a {@link TypeAdapter} or {@link TypeAdapterFactory}. */ /** Either a {@link TypeAdapter} or {@link TypeAdapterFactory}. */
Class<?> value(); Class<?> value();
/** false, to be able to handle {@code null} values within the adapter, default value is true. */
boolean nullSafe() default true;
} }

View File

@ -16,30 +16,33 @@
package com.massivecraft.massivecore.xlib.gson.annotations; package com.massivecraft.massivecore.xlib.gson.annotations;
import java.lang.annotation.ElementType; import com.massivecraft.massivecore.xlib.gson.FieldNamingPolicy;
import java.lang.annotation.Retention; import com.massivecraft.massivecore.xlib.gson.Gson;
import java.lang.annotation.RetentionPolicy; import com.massivecraft.massivecore.xlib.gson.GsonBuilder;
import java.lang.annotation.Target;
import java.lang.annotation.*;
/** /**
* An annotation that indicates this member should be serialized to JSON with * An annotation that indicates this member should be serialized to JSON with
* the provided name value as its field name. * the provided name value as its field name.
* *
* <p>This annotation will override any {@link com.massivecraft.massivecore.xlib.gson.FieldNamingPolicy}, including * <p>This annotation will override any {@link FieldNamingPolicy}, including
* the default field naming policy, that may have been set on the {@link com.massivecraft.massivecore.xlib.gson.Gson} * 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 * 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.</p> * for more information.</p>
* *
* <p>Here is an example of how this annotation is meant to be used:</p> * <p>Here is an example of how this annotation is meant to be used:</p>
* <pre> * <pre>
* public class SomeClassWithFields { * public class MyClass {
* &#64SerializedName("name") private final String someField; * &#64SerializedName("name") String a;
* private final String someOtherField; * &#64SerializedName(value="name1", alternate={"name2", "name3"}) String b;
* String c;
* *
* public SomeClassWithFields(String a, String b) { * public MyClass(String a, String b, String c) {
* this.someField = a; * this.a = a;
* this.someOtherField = b; * this.b = b;
* this.c = c;
* } * }
* } * }
* </pre> * </pre>
@ -47,28 +50,44 @@ import java.lang.annotation.Target;
* <p>The following shows the output that is generated when serializing an instance of the * <p>The following shows the output that is generated when serializing an instance of the
* above example class:</p> * above example class:</p>
* <pre> * <pre>
* SomeClassWithFields objectToSerialize = new SomeClassWithFields("a", "b"); * MyClass target = new MyClass("v1", "v2", "v3");
* Gson gson = new Gson(); * Gson gson = new Gson();
* String jsonRepresentation = gson.toJson(objectToSerialize); * String json = gson.toJson(target);
* System.out.println(jsonRepresentation); * System.out.println(json);
* *
* ===== OUTPUT ===== * ===== OUTPUT =====
* {"name":"a","someOtherField":"b"} * {"name":"v1","name1":"v2","c":"v3"}
* </pre> * </pre>
* *
* <p>NOTE: The value you specify in this annotation must be a valid JSON field name.</p> * <p>NOTE: The value you specify in this annotation must be a valid JSON field name.</p>
* While deserializing, all values specified in the annotation will be deserialized into the field.
* For example:
* <pre>
* 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);
* </pre>
* 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 Inderjeet Singh
* @author Joel Leitch * @author Joel Leitch
*/ */
@Documented
@Retention(RetentionPolicy.RUNTIME) @Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD) @Target({ElementType.FIELD, ElementType.METHOD})
public @interface SerializedName { 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(); String value();
/**
* @return the alternative names of the field when it is deserialized
*/
String[] alternate() default {};
} }

View File

@ -16,19 +16,19 @@
package com.massivecraft.massivecore.xlib.gson.annotations; package com.massivecraft.massivecore.xlib.gson.annotations;
import java.lang.annotation.ElementType; import com.massivecraft.massivecore.xlib.gson.Gson;
import java.lang.annotation.Retention; import com.massivecraft.massivecore.xlib.gson.GsonBuilder;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target; import java.lang.annotation.*;
/** /**
* An annotation that indicates the version number since a member or a type has been present. * 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 is useful to manage versioning of your Json classes for a web-service.
* *
* <p> * <p>
* This annotation has no effect unless you build {@link com.massivecraft.massivecore.xlib.gson.Gson} with a * This annotation has no effect unless you build {@link Gson} with a
* {@link com.massivecraft.massivecore.xlib.gson.GsonBuilder} and invoke * {@link GsonBuilder} and invoke
* {@link com.massivecraft.massivecore.xlib.gson.GsonBuilder#setVersion(double)} method. * {@link GsonBuilder#setVersion(double)} method.
* *
* <p>Here is an example of how this annotation is meant to be used:</p> * <p>Here is an example of how this annotation is meant to be used:</p>
* <pre> * <pre>
@ -50,6 +50,7 @@ import java.lang.annotation.Target;
* @author Inderjeet Singh * @author Inderjeet Singh
* @author Joel Leitch * @author Joel Leitch
*/ */
@Documented
@Retention(RetentionPolicy.RUNTIME) @Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD, ElementType.TYPE}) @Target({ElementType.FIELD, ElementType.TYPE})
public @interface Since { public @interface Since {

View File

@ -16,10 +16,10 @@
package com.massivecraft.massivecore.xlib.gson.annotations; package com.massivecraft.massivecore.xlib.gson.annotations;
import java.lang.annotation.ElementType; import com.massivecraft.massivecore.xlib.gson.Gson;
import java.lang.annotation.Retention; import com.massivecraft.massivecore.xlib.gson.GsonBuilder;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target; import java.lang.annotation.*;
/** /**
* An annotation that indicates the version number until a member or a type should be present. * 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. * is useful to manage versioning of your JSON classes for a web-service.
* *
* <p> * <p>
* This annotation has no effect unless you build {@link com.massivecraft.massivecore.xlib.gson.Gson} with a * This annotation has no effect unless you build {@link Gson} with a
* {@link com.massivecraft.massivecore.xlib.gson.GsonBuilder} and invoke * {@link GsonBuilder} and invoke
* {@link com.massivecraft.massivecore.xlib.gson.GsonBuilder#setVersion(double)} method. * {@link GsonBuilder#setVersion(double)} method.
* *
* <p>Here is an example of how this annotation is meant to be used:</p> * <p>Here is an example of how this annotation is meant to be used:</p>
* <pre> * <pre>
@ -54,6 +54,7 @@ import java.lang.annotation.Target;
* @author Joel Leitch * @author Joel Leitch
* @since 1.3 * @since 1.3
*/ */
@Documented
@Retention(RetentionPolicy.RUNTIME) @Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD, ElementType.TYPE}) @Target({ElementType.FIELD, ElementType.TYPE})
public @interface Until { public @interface Until {

View File

@ -30,6 +30,10 @@ package com.massivecraft.massivecore.xlib.gson.internal;
* @author Joel Leitch * @author Joel Leitch
*/ */
public final class $Gson$Preconditions { public final class $Gson$Preconditions {
private $Gson$Preconditions() {
throw new UnsupportedOperationException();
}
public static <T> T checkNotNull(T obj) { public static <T> T checkNotNull(T obj) {
if (obj == null) { if (obj == null) {
throw new NullPointerException(); throw new NullPointerException();

View File

@ -17,19 +17,8 @@
package com.massivecraft.massivecore.xlib.gson.internal; package com.massivecraft.massivecore.xlib.gson.internal;
import java.io.Serializable; import java.io.Serializable;
import java.lang.reflect.Array; import java.lang.reflect.*;
import java.lang.reflect.GenericArrayType; import java.util.*;
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 static com.massivecraft.massivecore.xlib.gson.internal.$Gson$Preconditions.checkArgument; import static com.massivecraft.massivecore.xlib.gson.internal.$Gson$Preconditions.checkArgument;
import static com.massivecraft.massivecore.xlib.gson.internal.$Gson$Preconditions.checkNotNull; 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 { public final class $Gson$Types {
static final Type[] EMPTY_TYPE_ARRAY = new Type[] {}; 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 * 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; return o != null ? o.hashCode() : 0;
} }
@ -428,7 +419,7 @@ public final class $Gson$Types {
: null; : null;
} }
private static void checkNotPrimitive(Type type) { static void checkNotPrimitive(Type type) {
checkArgument(!(type instanceof Class<?>) || !((Class<?>) type).isPrimitive()); checkArgument(!(type instanceof Class<?>) || !((Class<?>) type).isPrimitive());
} }

View File

@ -24,19 +24,11 @@ import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException; import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.ParameterizedType; import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type; import java.lang.reflect.Type;
import java.util.ArrayList; import java.util.*;
import java.util.Collection; import java.util.concurrent.ConcurrentHashMap;
import java.util.EnumSet; import java.util.concurrent.ConcurrentMap;
import java.util.LinkedHashMap; import java.util.concurrent.ConcurrentNavigableMap;
import java.util.LinkedHashSet; import java.util.concurrent.ConcurrentSkipListMap;
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;
/** /**
* Returns a function that can construct an instance of a requested type. * Returns a function that can construct an instance of a requested type.
@ -58,7 +50,7 @@ public final class ConstructorConstructor {
final InstanceCreator<T> typeCreator = (InstanceCreator<T>) instanceCreators.get(type); final InstanceCreator<T> typeCreator = (InstanceCreator<T>) instanceCreators.get(type);
if (typeCreator != null) { if (typeCreator != null) {
return new ObjectConstructor<T>() { return new ObjectConstructor<T>() {
public T construct() { @Override public T construct() {
return typeCreator.createInstance(type); return typeCreator.createInstance(type);
} }
}; };
@ -70,7 +62,7 @@ public final class ConstructorConstructor {
(InstanceCreator<T>) instanceCreators.get(rawType); (InstanceCreator<T>) instanceCreators.get(rawType);
if (rawTypeCreator != null) { if (rawTypeCreator != null) {
return new ObjectConstructor<T>() { return new ObjectConstructor<T>() {
public T construct() { @Override public T construct() {
return rawTypeCreator.createInstance(type); return rawTypeCreator.createInstance(type);
} }
}; };
@ -98,7 +90,7 @@ public final class ConstructorConstructor {
} }
return new ObjectConstructor<T>() { return new ObjectConstructor<T>() {
@SuppressWarnings("unchecked") // T is the same raw type as is requested @SuppressWarnings("unchecked") // T is the same raw type as is requested
public T construct() { @Override public T construct() {
try { try {
Object[] args = null; Object[] args = null;
return (T) constructor.newInstance(args); return (T) constructor.newInstance(args);
@ -122,7 +114,7 @@ public final class ConstructorConstructor {
/** /**
* Constructors for common interface types like Map and List and their * 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 @SuppressWarnings("unchecked") // use runtime checks to guarantee that 'T' is what it is
private <T> ObjectConstructor<T> newDefaultImplementationConstructor( private <T> ObjectConstructor<T> newDefaultImplementationConstructor(
@ -130,14 +122,14 @@ public final class ConstructorConstructor {
if (Collection.class.isAssignableFrom(rawType)) { if (Collection.class.isAssignableFrom(rawType)) {
if (SortedSet.class.isAssignableFrom(rawType)) { if (SortedSet.class.isAssignableFrom(rawType)) {
return new ObjectConstructor<T>() { return new ObjectConstructor<T>() {
public T construct() { @Override public T construct() {
return (T) new TreeSet<>(); return (T) new TreeSet<>();
} }
}; };
} else if (EnumSet.class.isAssignableFrom(rawType)) { } else if (EnumSet.class.isAssignableFrom(rawType)) {
return new ObjectConstructor<T>() { return new ObjectConstructor<T>() {
@SuppressWarnings("rawtypes") @SuppressWarnings("rawtypes")
public T construct() { @Override public T construct() {
if (type instanceof ParameterizedType) { if (type instanceof ParameterizedType) {
Type elementType = ((ParameterizedType) type).getActualTypeArguments()[0]; Type elementType = ((ParameterizedType) type).getActualTypeArguments()[0];
if (elementType instanceof Class) { if (elementType instanceof Class) {
@ -152,19 +144,19 @@ public final class ConstructorConstructor {
}; };
} else if (Set.class.isAssignableFrom(rawType)) { } else if (Set.class.isAssignableFrom(rawType)) {
return new ObjectConstructor<T>() { return new ObjectConstructor<T>() {
public T construct() { @Override public T construct() {
return (T) new LinkedHashSet<>(); return (T) new LinkedHashSet<>();
} }
}; };
} else if (Queue.class.isAssignableFrom(rawType)) { } else if (Queue.class.isAssignableFrom(rawType)) {
return new ObjectConstructor<T>() { return new ObjectConstructor<T>() {
public T construct() { @Override public T construct() {
return (T) new LinkedList<>(); return (T) new ArrayDeque<>();
} }
}; };
} else { } else {
return new ObjectConstructor<T>() { return new ObjectConstructor<T>() {
public T construct() { @Override public T construct() {
return (T) new ArrayList<>(); return (T) new ArrayList<>();
} }
}; };
@ -172,22 +164,34 @@ public final class ConstructorConstructor {
} }
if (Map.class.isAssignableFrom(rawType)) { if (Map.class.isAssignableFrom(rawType)) {
if (SortedMap.class.isAssignableFrom(rawType)) { if (ConcurrentNavigableMap.class.isAssignableFrom(rawType)) {
return new ObjectConstructor<T>() { return new ObjectConstructor<T>() {
public T construct() { @Override public T construct() {
return (T) new ConcurrentSkipListMap<>();
}
};
} else if (ConcurrentMap.class.isAssignableFrom(rawType)) {
return new ObjectConstructor<T>() {
@Override public T construct() {
return (T) new ConcurrentHashMap<>();
}
};
} else if (SortedMap.class.isAssignableFrom(rawType)) {
return new ObjectConstructor<T>() {
@Override public T construct() {
return (T) new TreeMap<>(); return (T) new TreeMap<>();
} }
}; };
} else if (type instanceof ParameterizedType && !(String.class.isAssignableFrom( } else if (type instanceof ParameterizedType && !(String.class.isAssignableFrom(
TypeToken.get(((ParameterizedType) type).getActualTypeArguments()[0]).getRawType()))) { TypeToken.get(((ParameterizedType) type).getActualTypeArguments()[0]).getRawType()))) {
return new ObjectConstructor<T>() { return new ObjectConstructor<T>() {
public T construct() { @Override public T construct() {
return (T) new LinkedHashMap<>(); return (T) new LinkedHashMap<>();
} }
}; };
} else { } else {
return new ObjectConstructor<T>() { return new ObjectConstructor<T>() {
public T construct() { @Override public T construct() {
return (T) new LinkedTreeMap<String, Object>(); return (T) new LinkedTreeMap<String, Object>();
} }
}; };
@ -202,7 +206,7 @@ public final class ConstructorConstructor {
return new ObjectConstructor<T>() { return new ObjectConstructor<T>() {
private final UnsafeAllocator unsafeAllocator = UnsafeAllocator.create(); private final UnsafeAllocator unsafeAllocator = UnsafeAllocator.create();
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public T construct() { @Override public T construct() {
try { try {
Object newInstance = unsafeAllocator.newInstance(rawType); Object newInstance = unsafeAllocator.newInstance(rawType);
return (T) newInstance; return (T) newInstance;

View File

@ -16,11 +16,7 @@
package com.massivecraft.massivecore.xlib.gson.internal; package com.massivecraft.massivecore.xlib.gson.internal;
import com.massivecraft.massivecore.xlib.gson.ExclusionStrategy; import com.massivecraft.massivecore.xlib.gson.*;
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.annotations.Expose; import com.massivecraft.massivecore.xlib.gson.annotations.Expose;
import com.massivecraft.massivecore.xlib.gson.annotations.Since; import com.massivecraft.massivecore.xlib.gson.annotations.Since;
import com.massivecraft.massivecore.xlib.gson.annotations.Until; import com.massivecraft.massivecore.xlib.gson.annotations.Until;
@ -63,7 +59,7 @@ public final class Excluder implements TypeAdapterFactory, Cloneable {
try { try {
return (Excluder) super.clone(); return (Excluder) super.clone();
} catch (CloneNotSupportedException e) { } catch (CloneNotSupportedException e) {
throw new AssertionError(); throw new AssertionError(e);
} }
} }

View File

@ -26,6 +26,7 @@ import java.math.BigDecimal;
public final class LazilyParsedNumber extends Number { public final class LazilyParsedNumber extends Number {
private final String value; private final String value;
/** @param value must not be null */
public LazilyParsedNumber(String value) { public LazilyParsedNumber(String value) {
this.value = value; this.value = value;
} }
@ -75,4 +76,21 @@ public final class LazilyParsedNumber extends Number {
private Object writeReplace() throws ObjectStreamException { private Object writeReplace() throws ObjectStreamException {
return new BigDecimal(value); return new BigDecimal(value);
} }
@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;
}
} }

View File

@ -19,15 +19,7 @@ package com.massivecraft.massivecore.xlib.gson.internal;
import java.io.ObjectStreamException; import java.io.ObjectStreamException;
import java.io.Serializable; import java.io.Serializable;
import java.util.AbstractMap; import java.util.*;
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;
/** /**
* A map of comparable keys to values. Unlike {@code TreeMap}, this class uses * A map of comparable keys to values. Unlike {@code TreeMap}, this class uses
@ -762,6 +754,9 @@ public final class LinkedHashTreeMap<K, V> extends AbstractMap<K, V> implements
Node<K, V> lastReturned = null; Node<K, V> lastReturned = null;
int expectedModCount = modCount; int expectedModCount = modCount;
LinkedTreeMapIterator() {
}
public final boolean hasNext() { public final boolean hasNext() {
return next != header; return next != header;
} }

View File

@ -19,14 +19,7 @@ package com.massivecraft.massivecore.xlib.gson.internal;
import java.io.ObjectStreamException; import java.io.ObjectStreamException;
import java.io.Serializable; import java.io.Serializable;
import java.util.AbstractMap; import java.util.*;
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;
/** /**
* A map of comparable keys to values. Unlike {@code TreeMap}, this class uses * A map of comparable keys to values. Unlike {@code TreeMap}, this class uses
@ -528,6 +521,9 @@ public final class LinkedTreeMap<K, V> extends AbstractMap<K, V> implements Seri
Node<K, V> lastReturned = null; Node<K, V> lastReturned = null;
int expectedModCount = modCount; int expectedModCount = modCount;
LinkedTreeMapIterator() {
}
public final boolean hasNext() { public final boolean hasNext() {
return next != header; return next != header;
} }

View File

@ -29,5 +29,5 @@ public interface ObjectConstructor<T> {
/** /**
* Returns a new instance. * Returns a new instance.
*/ */
T construct(); public T construct();
} }

View File

@ -29,7 +29,9 @@ import java.util.Map;
* @author Kevin Bourrillion * @author Kevin Bourrillion
*/ */
public final class Primitives { public final class Primitives {
private Primitives() {} private Primitives() {
throw new UnsupportedOperationException();
}
/** A map from primitive types to their corresponding wrapper types. */ /** A map from primitive types to their corresponding wrapper types. */
private static final Map<Class<?>, Class<?>> PRIMITIVE_TO_WRAPPER_TYPE; private static final Map<Class<?>, Class<?>> PRIMITIVE_TO_WRAPPER_TYPE;

View File

@ -16,11 +16,7 @@
package com.massivecraft.massivecore.xlib.gson.internal; package com.massivecraft.massivecore.xlib.gson.internal;
import com.massivecraft.massivecore.xlib.gson.JsonElement; import com.massivecraft.massivecore.xlib.gson.*;
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.internal.bind.TypeAdapters; import com.massivecraft.massivecore.xlib.gson.internal.bind.TypeAdapters;
import com.massivecraft.massivecore.xlib.gson.stream.JsonReader; import com.massivecraft.massivecore.xlib.gson.stream.JsonReader;
import com.massivecraft.massivecore.xlib.gson.stream.JsonWriter; import com.massivecraft.massivecore.xlib.gson.stream.JsonWriter;
@ -34,6 +30,10 @@ import java.io.Writer;
* Reads and writes GSON parse trees over streams. * Reads and writes GSON parse trees over streams.
*/ */
public final class Streams { public final class Streams {
private Streams() {
throw new UnsupportedOperationException();
}
/** /**
* Takes a reader in any state and returns the next value as a JsonElement. * 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 Appendable appendable;
private final CurrentWrite currentWrite = new CurrentWrite(); private final CurrentWrite currentWrite = new CurrentWrite();
private AppendableWriter(Appendable appendable) { AppendableWriter(Appendable appendable) {
this.appendable = appendable; this.appendable = appendable;
} }

View File

@ -20,6 +20,7 @@ import java.io.ObjectInputStream;
import java.io.ObjectStreamClass; import java.io.ObjectStreamClass;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
/** /**
* Do sneaky things to allocate objects without invoking their constructors. * Do sneaky things to allocate objects without invoking their constructors.
@ -45,6 +46,7 @@ public abstract class UnsafeAllocator {
@Override @Override
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public <T> T newInstance(Class<T> c) throws Exception { public <T> T newInstance(Class<T> c) throws Exception {
assertInstantiable(c);
return (T) allocateInstance.invoke(unsafe, c); return (T) allocateInstance.invoke(unsafe, c);
} }
}; };
@ -68,6 +70,7 @@ public abstract class UnsafeAllocator {
@Override @Override
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public <T> T newInstance(Class<T> c) throws Exception { public <T> T newInstance(Class<T> c) throws Exception {
assertInstantiable(c);
return (T) newInstance.invoke(null, c, constructorId); return (T) newInstance.invoke(null, c, constructorId);
} }
}; };
@ -87,6 +90,7 @@ public abstract class UnsafeAllocator {
@Override @Override
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public <T> T newInstance(Class<T> c) throws Exception { public <T> T newInstance(Class<T> c) throws Exception {
assertInstantiable(c);
return (T) newInstance.invoke(null, c, Object.class); 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());
}
}
} }

View File

@ -38,7 +38,7 @@ import java.util.List;
public final class ArrayTypeAdapter<E> extends TypeAdapter<Object> { public final class ArrayTypeAdapter<E> extends TypeAdapter<Object> {
public static final TypeAdapterFactory FACTORY = new TypeAdapterFactory() { public static final TypeAdapterFactory FACTORY = new TypeAdapterFactory() {
@SuppressWarnings({"unchecked", "rawtypes"}) @SuppressWarnings({"unchecked", "rawtypes"})
public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> typeToken) { @Override public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> typeToken) {
Type type = typeToken.getType(); Type type = typeToken.getType();
if (!(type instanceof GenericArrayType || type instanceof Class && ((Class<?>) type).isArray())) { if (!(type instanceof GenericArrayType || type instanceof Class && ((Class<?>) type).isArray())) {
return null; return null;
@ -60,7 +60,7 @@ public final class ArrayTypeAdapter<E> extends TypeAdapter<Object> {
this.componentType = componentType; this.componentType = componentType;
} }
public Object read(JsonReader in) throws IOException { @Override public Object read(JsonReader in) throws IOException {
if (in.peek() == JsonToken.NULL) { if (in.peek() == JsonToken.NULL) {
in.nextNull(); in.nextNull();
return null; return null;

View File

@ -41,6 +41,7 @@ public final class CollectionTypeAdapterFactory implements TypeAdapterFactory {
this.constructorConstructor = constructorConstructor; this.constructorConstructor = constructorConstructor;
} }
@Override
public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> typeToken) { public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> typeToken) {
Type type = typeToken.getType(); Type type = typeToken.getType();
@ -70,7 +71,7 @@ public final class CollectionTypeAdapterFactory implements TypeAdapterFactory {
this.constructor = constructor; this.constructor = constructor;
} }
public Collection<E> read(JsonReader in) throws IOException { @Override public Collection<E> read(JsonReader in) throws IOException {
if (in.peek() == JsonToken.NULL) { if (in.peek() == JsonToken.NULL) {
in.nextNull(); in.nextNull();
return null; return null;
@ -86,7 +87,7 @@ public final class CollectionTypeAdapterFactory implements TypeAdapterFactory {
return collection; return collection;
} }
public void write(JsonWriter out, Collection<E> collection) throws IOException { @Override public void write(JsonWriter out, Collection<E> collection) throws IOException {
if (collection == null) { if (collection == null) {
out.nullValue(); out.nullValue();
return; return;

View File

@ -20,6 +20,7 @@ import com.massivecraft.massivecore.xlib.gson.Gson;
import com.massivecraft.massivecore.xlib.gson.JsonSyntaxException; import com.massivecraft.massivecore.xlib.gson.JsonSyntaxException;
import com.massivecraft.massivecore.xlib.gson.TypeAdapter; import com.massivecraft.massivecore.xlib.gson.TypeAdapter;
import com.massivecraft.massivecore.xlib.gson.TypeAdapterFactory; 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.reflect.TypeToken;
import com.massivecraft.massivecore.xlib.gson.stream.JsonReader; import com.massivecraft.massivecore.xlib.gson.stream.JsonReader;
import com.massivecraft.massivecore.xlib.gson.stream.JsonToken; 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.io.IOException;
import java.text.DateFormat; import java.text.DateFormat;
import java.text.ParseException; import java.text.ParseException;
import java.text.SimpleDateFormat; import java.text.ParsePosition;
import java.util.Date; import java.util.Date;
import java.util.Locale; import java.util.Locale;
import java.util.TimeZone;
/** /**
* Adapter for Date. Although this class appears stateless, it is not. * 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<Date> { public final class DateTypeAdapter extends TypeAdapter<Date> {
public static final TypeAdapterFactory FACTORY = new TypeAdapterFactory() { public static final TypeAdapterFactory FACTORY = new TypeAdapterFactory() {
@SuppressWarnings("unchecked") // we use a runtime check to make sure the 'T's equal @SuppressWarnings("unchecked") // we use a runtime check to make sure the 'T's equal
public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> typeToken) { @Override public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> typeToken) {
return typeToken.getRawType() == Date.class ? (TypeAdapter<T>) new DateTypeAdapter() : null; return typeToken.getRawType() == Date.class ? (TypeAdapter<T>) new DateTypeAdapter() : null;
} }
}; };
@ -51,13 +51,6 @@ public final class DateTypeAdapter extends TypeAdapter<Date> {
= DateFormat.getDateTimeInstance(DateFormat.DEFAULT, DateFormat.DEFAULT, Locale.US); = DateFormat.getDateTimeInstance(DateFormat.DEFAULT, DateFormat.DEFAULT, Locale.US);
private final DateFormat localFormat private final DateFormat localFormat
= DateFormat.getDateTimeInstance(DateFormat.DEFAULT, DateFormat.DEFAULT); = 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 { @Override public Date read(JsonReader in) throws IOException {
if (in.peek() == JsonToken.NULL) { if (in.peek() == JsonToken.NULL) {
@ -77,7 +70,7 @@ public final class DateTypeAdapter extends TypeAdapter<Date> {
} catch (ParseException ignored) { } catch (ParseException ignored) {
} }
try { try {
return iso8601Format.parse(json); return ISO8601Utils.parse(json, new ParsePosition(0));
} catch (ParseException e) { } catch (ParseException e) {
throw new JsonSyntaxException(json, e); throw new JsonSyntaxException(json, e);
} }
@ -91,4 +84,6 @@ public final class DateTypeAdapter extends TypeAdapter<Date> {
String dateFormatAsString = enUsFormat.format(value); String dateFormatAsString = enUsFormat.format(value);
out.value(dateFormatAsString); out.value(dateFormatAsString);
} }
} }

View File

@ -16,9 +16,7 @@
package com.massivecraft.massivecore.xlib.gson.internal.bind; package com.massivecraft.massivecore.xlib.gson.internal.bind;
import com.massivecraft.massivecore.xlib.gson.Gson; import com.massivecraft.massivecore.xlib.gson.*;
import com.massivecraft.massivecore.xlib.gson.TypeAdapter;
import com.massivecraft.massivecore.xlib.gson.TypeAdapterFactory;
import com.massivecraft.massivecore.xlib.gson.annotations.JsonAdapter; import com.massivecraft.massivecore.xlib.gson.annotations.JsonAdapter;
import com.massivecraft.massivecore.xlib.gson.internal.ConstructorConstructor; import com.massivecraft.massivecore.xlib.gson.internal.ConstructorConstructor;
import com.massivecraft.massivecore.xlib.gson.reflect.TypeToken; import com.massivecraft.massivecore.xlib.gson.reflect.TypeToken;
@ -30,7 +28,6 @@ import com.massivecraft.massivecore.xlib.gson.reflect.TypeToken;
* @since 2.3 * @since 2.3
*/ */
public final class JsonAdapterAnnotationTypeAdapterFactory implements TypeAdapterFactory { public final class JsonAdapterAnnotationTypeAdapterFactory implements TypeAdapterFactory {
private final ConstructorConstructor constructorConstructor; private final ConstructorConstructor constructorConstructor;
public JsonAdapterAnnotationTypeAdapterFactory(ConstructorConstructor constructorConstructor) { public JsonAdapterAnnotationTypeAdapterFactory(ConstructorConstructor constructorConstructor) {
@ -38,30 +35,44 @@ public final class JsonAdapterAnnotationTypeAdapterFactory implements TypeAdapte
} }
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
@Override
public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> targetType) { public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> targetType) {
JsonAdapter annotation = targetType.getRawType().getAnnotation(JsonAdapter.class); Class<? super T> rawType = targetType.getRawType();
JsonAdapter annotation = rawType.getAnnotation(JsonAdapter.class);
if (annotation == null) { if (annotation == null) {
return null; return null;
} }
return (TypeAdapter<T>) getTypeAdapter(constructorConstructor, gson, targetType, annotation); return (TypeAdapter<T>) getTypeAdapter(constructorConstructor, gson, targetType, annotation);
} }
@SuppressWarnings("unchecked") // Casts guarded by conditionals. @SuppressWarnings({ "unchecked", "rawtypes" }) // Casts guarded by conditionals.
static TypeAdapter<?> getTypeAdapter(ConstructorConstructor constructorConstructor, Gson gson, TypeAdapter<?> getTypeAdapter(ConstructorConstructor constructorConstructor, Gson gson,
TypeToken<?> fieldType, JsonAdapter annotation) { TypeToken<?> type, JsonAdapter annotation) {
Class<?> value = annotation.value(); Object instance = constructorConstructor.get(TypeToken.get(annotation.value())).construct();
if (TypeAdapter.class.isAssignableFrom(value)) {
Class<TypeAdapter<?>> typeAdapter = (Class<TypeAdapter<?>>) value; TypeAdapter<?> typeAdapter;
return constructorConstructor.get(TypeToken.get(typeAdapter)).construct(); if (instance instanceof TypeAdapter) {
} typeAdapter = (TypeAdapter<?>) instance;
if (TypeAdapterFactory.class.isAssignableFrom(value)) { } else if (instance instanceof TypeAdapterFactory) {
Class<TypeAdapterFactory> typeAdapterFactory = (Class<TypeAdapterFactory>) value; typeAdapter = ((TypeAdapterFactory) instance).create(gson, type);
return constructorConstructor.get(TypeToken.get(typeAdapterFactory)) } else if (instance instanceof JsonSerializer || instance instanceof JsonDeserializer) {
.construct() JsonSerializer<?> serializer = instance instanceof JsonSerializer
.create(gson, fieldType); ? (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( if (typeAdapter != null && annotation.nullSafe()) {
"@JsonAdapter value must be TypeAdapter or TypeAdapterFactory reference."); typeAdapter = typeAdapter.nullSafe();
}
return typeAdapter;
} }
} }

View File

@ -16,19 +16,13 @@
package com.massivecraft.massivecore.xlib.gson.internal.bind; package com.massivecraft.massivecore.xlib.gson.internal.bind;
import com.massivecraft.massivecore.xlib.gson.JsonArray; import com.massivecraft.massivecore.xlib.gson.*;
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.stream.JsonReader; import com.massivecraft.massivecore.xlib.gson.stream.JsonReader;
import com.massivecraft.massivecore.xlib.gson.stream.JsonToken; import com.massivecraft.massivecore.xlib.gson.stream.JsonToken;
import java.io.IOException; import java.io.IOException;
import java.io.Reader; import java.io.Reader;
import java.util.ArrayList;
import java.util.Iterator; import java.util.Iterator;
import java.util.List;
import java.util.Map; import java.util.Map;
/** /**
@ -48,35 +42,57 @@ public final class JsonTreeReader extends JsonReader {
}; };
private static final Object SENTINEL_CLOSED = new Object(); private static final Object SENTINEL_CLOSED = new Object();
private final List<Object> 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) { public JsonTreeReader(JsonElement element) {
super(UNREADABLE_READER); super(UNREADABLE_READER);
stack.add(element); push(element);
} }
@Override public void beginArray() throws IOException { @Override public void beginArray() throws IOException {
expect(JsonToken.BEGIN_ARRAY); expect(JsonToken.BEGIN_ARRAY);
JsonArray array = (JsonArray) peekStack(); JsonArray array = (JsonArray) peekStack();
stack.add(array.iterator()); push(array.iterator());
pathIndices[stackSize - 1] = 0;
} }
@Override public void endArray() throws IOException { @Override public void endArray() throws IOException {
expect(JsonToken.END_ARRAY); expect(JsonToken.END_ARRAY);
popStack(); // empty iterator popStack(); // empty iterator
popStack(); // array popStack(); // array
if (stackSize > 0) {
pathIndices[stackSize - 1]++;
}
} }
@Override public void beginObject() throws IOException { @Override public void beginObject() throws IOException {
expect(JsonToken.BEGIN_OBJECT); expect(JsonToken.BEGIN_OBJECT);
JsonObject object = (JsonObject) peekStack(); JsonObject object = (JsonObject) peekStack();
stack.add(object.entrySet().iterator()); push(object.entrySet().iterator());
} }
@Override public void endObject() throws IOException { @Override public void endObject() throws IOException {
expect(JsonToken.END_OBJECT); expect(JsonToken.END_OBJECT);
popStack(); // empty iterator popStack(); // empty iterator
popStack(); // object popStack(); // object
if (stackSize > 0) {
pathIndices[stackSize - 1]++;
}
} }
@Override public boolean hasNext() throws IOException { @Override public boolean hasNext() throws IOException {
@ -85,19 +101,19 @@ public final class JsonTreeReader extends JsonReader {
} }
@Override public JsonToken peek() throws IOException { @Override public JsonToken peek() throws IOException {
if (stack.isEmpty()) { if (stackSize == 0) {
return JsonToken.END_DOCUMENT; return JsonToken.END_DOCUMENT;
} }
Object o = peekStack(); Object o = peekStack();
if (o instanceof Iterator) { if (o instanceof Iterator) {
boolean isObject = stack.get(stack.size() - 2) instanceof JsonObject; boolean isObject = stack[stackSize - 2] instanceof JsonObject;
Iterator<?> iterator = (Iterator<?>) o; Iterator<?> iterator = (Iterator<?>) o;
if (iterator.hasNext()) { if (iterator.hasNext()) {
if (isObject) { if (isObject) {
return JsonToken.NAME; return JsonToken.NAME;
} else { } else {
stack.add(iterator.next()); push(iterator.next());
return peek(); return peek();
} }
} else { } else {
@ -128,16 +144,19 @@ public final class JsonTreeReader extends JsonReader {
} }
private Object peekStack() { private Object peekStack() {
return stack.get(stack.size() - 1); return stack[stackSize - 1];
} }
private Object popStack() { 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 { private void expect(JsonToken expected) throws IOException {
if (peek() != expected) { 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); expect(JsonToken.NAME);
Iterator<?> i = (Iterator<?>) peekStack(); Iterator<?> i = (Iterator<?>) peekStack();
Map.Entry<?, ?> entry = (Map.Entry<?, ?>) i.next(); Map.Entry<?, ?> entry = (Map.Entry<?, ?>) i.next();
stack.add(entry.getValue()); String result = (String) entry.getKey();
return (String) entry.getKey(); pathNames[stackSize - 1] = result;
push(entry.getValue());
return result;
} }
@Override public String nextString() throws IOException { @Override public String nextString() throws IOException {
JsonToken token = peek(); JsonToken token = peek();
if (token != JsonToken.STRING && token != JsonToken.NUMBER) { 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 { @Override public boolean nextBoolean() throws IOException {
expect(JsonToken.BOOLEAN); 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 { @Override public void nextNull() throws IOException {
expect(JsonToken.NULL); expect(JsonToken.NULL);
popStack(); popStack();
if (stackSize > 0) {
pathIndices[stackSize - 1]++;
}
} }
@Override public double nextDouble() throws IOException { @Override public double nextDouble() throws IOException {
JsonToken token = peek(); JsonToken token = peek();
if (token != JsonToken.NUMBER && token != JsonToken.STRING) { 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(); double result = ((JsonPrimitive) peekStack()).getAsDouble();
if (!isLenient() && (Double.isNaN(result) || Double.isInfinite(result))) { if (!isLenient() && (Double.isNaN(result) || Double.isInfinite(result))) {
throw new NumberFormatException("JSON forbids NaN and infinities: " + result); throw new NumberFormatException("JSON forbids NaN and infinities: " + result);
} }
popStack(); popStack();
if (stackSize > 0) {
pathIndices[stackSize - 1]++;
}
return result; return result;
} }
@Override public long nextLong() throws IOException { @Override public long nextLong() throws IOException {
JsonToken token = peek(); JsonToken token = peek();
if (token != JsonToken.NUMBER && token != JsonToken.STRING) { 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(); long result = ((JsonPrimitive) peekStack()).getAsLong();
popStack(); popStack();
if (stackSize > 0) {
pathIndices[stackSize - 1]++;
}
return result; return result;
} }
@Override public int nextInt() throws IOException { @Override public int nextInt() throws IOException {
JsonToken token = peek(); JsonToken token = peek();
if (token != JsonToken.NUMBER && token != JsonToken.STRING) { 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(); int result = ((JsonPrimitive) peekStack()).getAsInt();
popStack(); popStack();
if (stackSize > 0) {
pathIndices[stackSize - 1]++;
}
return result; return result;
} }
@Override public void close() throws IOException { @Override public void close() throws IOException {
stack.clear(); stack = new Object[] { SENTINEL_CLOSED };
stack.add(SENTINEL_CLOSED); stackSize = 1;
} }
@Override public void skipValue() throws IOException { @Override public void skipValue() throws IOException {
if (peek() == JsonToken.NAME) { if (peek() == JsonToken.NAME) {
nextName(); nextName();
pathNames[stackSize - 2] = "null";
} else { } else {
popStack(); popStack();
pathNames[stackSize - 1] = "null";
} }
pathIndices[stackSize - 1]++;
} }
@Override public String toString() { @Override public String toString() {
@ -221,7 +269,45 @@ public final class JsonTreeReader extends JsonReader {
expect(JsonToken.NAME); expect(JsonToken.NAME);
Iterator<?> i = (Iterator<?>) peekStack(); Iterator<?> i = (Iterator<?>) peekStack();
Map.Entry<?, ?> entry = (Map.Entry<?, ?>) i.next(); Map.Entry<?, ?> entry = (Map.Entry<?, ?>) i.next();
stack.add(entry.getValue()); push(entry.getValue());
stack.add(new JsonPrimitive((String)entry.getKey())); 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();
} }
} }

View File

@ -16,11 +16,7 @@
package com.massivecraft.massivecore.xlib.gson.internal.bind; package com.massivecraft.massivecore.xlib.gson.internal.bind;
import com.massivecraft.massivecore.xlib.gson.JsonArray; import com.massivecraft.massivecore.xlib.gson.*;
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.stream.JsonWriter; import com.massivecraft.massivecore.xlib.gson.stream.JsonWriter;
import java.io.IOException; import java.io.IOException;
@ -160,6 +156,14 @@ public final class JsonTreeWriter extends JsonWriter {
return this; 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 { @Override public JsonWriter value(double value) throws IOException {
if (!isLenient() && (Double.isNaN(value) || Double.isInfinite(value))) { if (!isLenient() && (Double.isNaN(value) || Double.isInfinite(value))) {
throw new IllegalArgumentException("JSON forbids NaN and infinities: " + value); throw new IllegalArgumentException("JSON forbids NaN and infinities: " + value);

View File

@ -16,17 +16,8 @@
package com.massivecraft.massivecore.xlib.gson.internal.bind; package com.massivecraft.massivecore.xlib.gson.internal.bind;
import com.massivecraft.massivecore.xlib.gson.Gson; import com.massivecraft.massivecore.xlib.gson.*;
import com.massivecraft.massivecore.xlib.gson.JsonElement; import com.massivecraft.massivecore.xlib.gson.internal.*;
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.reflect.TypeToken; import com.massivecraft.massivecore.xlib.gson.reflect.TypeToken;
import com.massivecraft.massivecore.xlib.gson.stream.JsonReader; import com.massivecraft.massivecore.xlib.gson.stream.JsonReader;
import com.massivecraft.massivecore.xlib.gson.stream.JsonToken; 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 * 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 * just the {@link Object#toString() toString()} of the map key. Attempting to
* convert the above JSON to an object fails with a parse exception: * convert the above JSON to an object fails with a parse exception:
* <pre>com.google.gson.JsonParseException: Expecting object found: "(5,6)" * <pre>JsonParseException: Expecting object found: "(5,6)"
* at com.google.gson.JsonObjectDeserializationVisitor.visitFieldUsingCustomHandler * at com.google.gson.JsonObjectDeserializationVisitor.visitFieldUsingCustomHandler
* at com.google.gson.ObjectNavigator.navigateClassFields * at com.google.gson.ObjectNavigator.navigateClassFields
* ...</pre> * ...</pre>
@ -105,7 +96,7 @@ import java.util.Map;
*/ */
public final class MapTypeAdapterFactory implements TypeAdapterFactory { public final class MapTypeAdapterFactory implements TypeAdapterFactory {
private final ConstructorConstructor constructorConstructor; private final ConstructorConstructor constructorConstructor;
private final boolean complexMapKeySerialization; final boolean complexMapKeySerialization;
public MapTypeAdapterFactory(ConstructorConstructor constructorConstructor, public MapTypeAdapterFactory(ConstructorConstructor constructorConstructor,
boolean complexMapKeySerialization) { boolean complexMapKeySerialization) {
@ -113,7 +104,7 @@ public final class MapTypeAdapterFactory implements TypeAdapterFactory {
this.complexMapKeySerialization = complexMapKeySerialization; this.complexMapKeySerialization = complexMapKeySerialization;
} }
public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> typeToken) { @Override public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> typeToken) {
Type type = typeToken.getType(); Type type = typeToken.getType();
Class<? super T> rawType = typeToken.getRawType(); Class<? super T> rawType = typeToken.getRawType();
@ -158,7 +149,7 @@ public final class MapTypeAdapterFactory implements TypeAdapterFactory {
this.constructor = constructor; this.constructor = constructor;
} }
public Map<K, V> read(JsonReader in) throws IOException { @Override public Map<K, V> read(JsonReader in) throws IOException {
JsonToken peek = in.peek(); JsonToken peek = in.peek();
if (peek == JsonToken.NULL) { if (peek == JsonToken.NULL) {
in.nextNull(); in.nextNull();
@ -196,7 +187,7 @@ public final class MapTypeAdapterFactory implements TypeAdapterFactory {
return map; return map;
} }
public void write(JsonWriter out, Map<K, V> map) throws IOException { @Override public void write(JsonWriter out, Map<K, V> map) throws IOException {
if (map == null) { if (map == null) {
out.nullValue(); out.nullValue();
return; return;

View File

@ -37,7 +37,7 @@ import java.util.Map;
public final class ObjectTypeAdapter extends TypeAdapter<Object> { public final class ObjectTypeAdapter extends TypeAdapter<Object> {
public static final TypeAdapterFactory FACTORY = new TypeAdapterFactory() { public static final TypeAdapterFactory FACTORY = new TypeAdapterFactory() {
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type) { @Override public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type) {
if (type.getRawType() == Object.class) { if (type.getRawType() == Object.class) {
return (TypeAdapter<T>) new ObjectTypeAdapter(gson); return (TypeAdapter<T>) new ObjectTypeAdapter(gson);
} }
@ -47,7 +47,7 @@ public final class ObjectTypeAdapter extends TypeAdapter<Object> {
private final Gson gson; private final Gson gson;
private ObjectTypeAdapter(Gson gson) { ObjectTypeAdapter(Gson gson) {
this.gson = gson; this.gson = gson;
} }

View File

@ -16,18 +16,10 @@
package com.massivecraft.massivecore.xlib.gson.internal.bind; package com.massivecraft.massivecore.xlib.gson.internal.bind;
import com.massivecraft.massivecore.xlib.gson.FieldNamingStrategy; import com.massivecraft.massivecore.xlib.gson.*;
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.annotations.JsonAdapter; import com.massivecraft.massivecore.xlib.gson.annotations.JsonAdapter;
import com.massivecraft.massivecore.xlib.gson.annotations.SerializedName; import com.massivecraft.massivecore.xlib.gson.annotations.SerializedName;
import com.massivecraft.massivecore.xlib.gson.internal.$Gson$Types; import com.massivecraft.massivecore.xlib.gson.internal.*;
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.reflect.TypeToken; import com.massivecraft.massivecore.xlib.gson.reflect.TypeToken;
import com.massivecraft.massivecore.xlib.gson.stream.JsonReader; import com.massivecraft.massivecore.xlib.gson.stream.JsonReader;
import com.massivecraft.massivecore.xlib.gson.stream.JsonToken; 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.io.IOException;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.lang.reflect.Type; import java.lang.reflect.Type;
import java.util.LinkedHashMap; import java.util.*;
import java.util.Map;
import static com.massivecraft.massivecore.xlib.gson.internal.bind.JsonAdapterAnnotationTypeAdapterFactory.getTypeAdapter;
/** /**
* Type adapter that reflects over the fields and methods of a class. * 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 ConstructorConstructor constructorConstructor;
private final FieldNamingStrategy fieldNamingPolicy; private final FieldNamingStrategy fieldNamingPolicy;
private final Excluder excluder; private final Excluder excluder;
private final JsonAdapterAnnotationTypeAdapterFactory jsonAdapterFactory;
public ReflectiveTypeAdapterFactory(ConstructorConstructor constructorConstructor, public ReflectiveTypeAdapterFactory(ConstructorConstructor constructorConstructor,
FieldNamingStrategy fieldNamingPolicy, Excluder excluder) { FieldNamingStrategy fieldNamingPolicy, Excluder excluder,
JsonAdapterAnnotationTypeAdapterFactory jsonAdapterFactory) {
this.constructorConstructor = constructorConstructor; this.constructorConstructor = constructorConstructor;
this.fieldNamingPolicy = fieldNamingPolicy; this.fieldNamingPolicy = fieldNamingPolicy;
this.excluder = excluder; this.excluder = excluder;
this.jsonAdapterFactory = jsonAdapterFactory;
} }
public boolean excludeField(Field f, boolean serialize) { 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); return !excluder.excludeClass(f.getType(), serialize) && !excluder.excludeField(f, serialize);
} }
private String getFieldName(Field f) { /** first element holds the default name */
return getFieldName(fieldNamingPolicy, f); private List<String> getFieldNames(Field f) {
SerializedName annotation = f.getAnnotation(SerializedName.class);
if (annotation == null) {
String name = fieldNamingPolicy.translateName(f);
return Collections.singletonList(name);
} }
static String getFieldName(FieldNamingStrategy fieldNamingPolicy, Field f) { String serializedName = annotation.value();
SerializedName serializedName = f.getAnnotation(SerializedName.class); String[] alternates = annotation.alternate();
return serializedName == null ? fieldNamingPolicy.translateName(f) : serializedName.value(); if (alternates.length == 0) {
return Collections.singletonList(serializedName);
} }
public <T> TypeAdapter<T> create(Gson gson, final TypeToken<T> type) { List<String> fieldNames = new ArrayList<>(alternates.length + 1);
fieldNames.add(serializedName);
for (String alternate : alternates) {
fieldNames.add(alternate);
}
return fieldNames;
}
@Override public <T> TypeAdapter<T> create(Gson gson, final TypeToken<T> type) {
Class<? super T> raw = type.getRawType(); Class<? super T> raw = type.getRawType();
if (!Object.class.isAssignableFrom(raw)) { if (!Object.class.isAssignableFrom(raw)) {
@ -89,14 +94,23 @@ public final class ReflectiveTypeAdapterFactory implements TypeAdapterFactory {
final TypeToken<?> fieldType, boolean serialize, boolean deserialize) { final TypeToken<?> fieldType, boolean serialize, boolean deserialize) {
final boolean isPrimitive = Primitives.isPrimitive(fieldType.getRawType()); final boolean isPrimitive = Primitives.isPrimitive(fieldType.getRawType());
// special casing primitives here saves ~5% on Android... // 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) { 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 @SuppressWarnings({"unchecked", "rawtypes"}) // the type adapter and field type always agree
@Override void write(JsonWriter writer, Object value) @Override void write(JsonWriter writer, Object value)
throws IOException, IllegalAccessException { throws IOException, IllegalAccessException {
Object fieldValue = field.get(value); Object fieldValue = field.get(value);
TypeAdapter t = TypeAdapter t = jsonAdapterPresent ? typeAdapter
new TypeAdapterRuntimeTypeWrapper(context, this.typeAdapter, fieldType.getType()); : new TypeAdapterRuntimeTypeWrapper(context, typeAdapter, fieldType.getType());
t.write(writer, fieldValue); t.write(writer, fieldValue);
} }
@Override void read(JsonReader reader, Object value) @Override void read(JsonReader reader, Object value)
@ -106,7 +120,7 @@ public final class ReflectiveTypeAdapterFactory implements TypeAdapterFactory {
field.set(value, fieldValue); 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; if (!serialized) return false;
Object fieldValue = field.get(value); Object fieldValue = field.get(value);
return fieldValue != value; // avoid recursion for example for Throwable.cause 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<String, BoundField> getBoundFields(Gson context, TypeToken<?> type, Class<?> raw) { private Map<String, BoundField> getBoundFields(Gson context, TypeToken<?> type, Class<?> raw) {
Map<String, BoundField> result = new LinkedHashMap<>(); Map<String, BoundField> result = new LinkedHashMap<>();
if (raw.isInterface()) { if (raw.isInterface()) {
@ -140,9 +145,16 @@ public final class ReflectiveTypeAdapterFactory implements TypeAdapterFactory {
} }
field.setAccessible(true); field.setAccessible(true);
Type fieldType = $Gson$Types.resolve(type.getType(), raw, field.getGenericType()); Type fieldType = $Gson$Types.resolve(type.getType(), raw, field.getGenericType());
BoundField boundField = createBoundField(context, field, getFieldName(field), List<String> 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); TypeToken.get(fieldType), serialize, deserialize);
BoundField previous = result.put(boundField.name, boundField); BoundField replaced = result.put(name, boundField);
if (previous == null) previous = replaced;
}
if (previous != null) { if (previous != null) {
throw new IllegalArgumentException(declaredType throw new IllegalArgumentException(declaredType
+ " declares multiple JSON fields named " + previous.name); + " declares multiple JSON fields named " + previous.name);
@ -173,7 +185,7 @@ public final class ReflectiveTypeAdapterFactory implements TypeAdapterFactory {
private final ObjectConstructor<T> constructor; private final ObjectConstructor<T> constructor;
private final Map<String, BoundField> boundFields; private final Map<String, BoundField> boundFields;
private Adapter(ObjectConstructor<T> constructor, Map<String, BoundField> boundFields) { Adapter(ObjectConstructor<T> constructor, Map<String, BoundField> boundFields) {
this.constructor = constructor; this.constructor = constructor;
this.boundFields = boundFields; this.boundFields = boundFields;
} }
@ -221,7 +233,7 @@ public final class ReflectiveTypeAdapterFactory implements TypeAdapterFactory {
} }
} }
} catch (IllegalAccessException e) { } catch (IllegalAccessException e) {
throw new AssertionError(); throw new AssertionError(e);
} }
out.endObject(); out.endObject();
} }

View File

@ -39,7 +39,7 @@ import java.text.SimpleDateFormat;
public final class SqlDateTypeAdapter extends TypeAdapter<java.sql.Date> { public final class SqlDateTypeAdapter extends TypeAdapter<java.sql.Date> {
public static final TypeAdapterFactory FACTORY = new TypeAdapterFactory() { public static final TypeAdapterFactory FACTORY = new TypeAdapterFactory() {
@SuppressWarnings("unchecked") // we use a runtime check to make sure the 'T's equal @SuppressWarnings("unchecked") // we use a runtime check to make sure the 'T's equal
public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> typeToken) { @Override public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> typeToken) {
return typeToken.getRawType() == java.sql.Date.class return typeToken.getRawType() == java.sql.Date.class
? (TypeAdapter<T>) new SqlDateTypeAdapter() : null; ? (TypeAdapter<T>) new SqlDateTypeAdapter() : null;
} }

View File

@ -41,7 +41,7 @@ import java.util.Date;
public final class TimeTypeAdapter extends TypeAdapter<Time> { public final class TimeTypeAdapter extends TypeAdapter<Time> {
public static final TypeAdapterFactory FACTORY = new TypeAdapterFactory() { public static final TypeAdapterFactory FACTORY = new TypeAdapterFactory() {
@SuppressWarnings("unchecked") // we use a runtime check to make sure the 'T's equal @SuppressWarnings("unchecked") // we use a runtime check to make sure the 'T's equal
public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> typeToken) { @Override public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> typeToken) {
return typeToken.getRawType() == Time.class ? (TypeAdapter<T>) new TimeTypeAdapter() : null; return typeToken.getRawType() == Time.class ? (TypeAdapter<T>) new TimeTypeAdapter() : null;
} }
}; };

View File

@ -14,8 +14,9 @@
* limitations under the License. * limitations under the License.
*/ */
package com.massivecraft.massivecore.xlib.gson; package com.massivecraft.massivecore.xlib.gson.internal.bind;
import com.massivecraft.massivecore.xlib.gson.*;
import com.massivecraft.massivecore.xlib.gson.internal.$Gson$Preconditions; import com.massivecraft.massivecore.xlib.gson.internal.$Gson$Preconditions;
import com.massivecraft.massivecore.xlib.gson.internal.Streams; import com.massivecraft.massivecore.xlib.gson.internal.Streams;
import com.massivecraft.massivecore.xlib.gson.reflect.TypeToken; import com.massivecraft.massivecore.xlib.gson.reflect.TypeToken;
@ -23,23 +24,25 @@ import com.massivecraft.massivecore.xlib.gson.stream.JsonReader;
import com.massivecraft.massivecore.xlib.gson.stream.JsonWriter; import com.massivecraft.massivecore.xlib.gson.stream.JsonWriter;
import java.io.IOException; import java.io.IOException;
import java.lang.reflect.Type;
/** /**
* Adapts a Gson 1.x tree-style adapter as a streaming TypeAdapter. Since the * Adapts a Gson 1.x tree-style adapter as a streaming TypeAdapter. Since the
* tree adapter may be serialization-only or deserialization-only, this class * tree adapter may be serialization-only or deserialization-only, this class
* has a facility to lookup a delegate type adapter on demand. * has a facility to lookup a delegate type adapter on demand.
*/ */
final class TreeTypeAdapter<T> extends TypeAdapter<T> { public final class TreeTypeAdapter<T> extends TypeAdapter<T> {
private final JsonSerializer<T> serializer; private final JsonSerializer<T> serializer;
private final JsonDeserializer<T> deserializer; private final JsonDeserializer<T> deserializer;
private final Gson gson; private final Gson gson;
private final TypeToken<T> typeToken; private final TypeToken<T> typeToken;
private final TypeAdapterFactory skipPast; private final TypeAdapterFactory skipPast;
private final GsonContextImpl context = new GsonContextImpl();
/** The delegate is lazily created because it may not be needed, and creating it may fail. */ /** The delegate is lazily created because it may not be needed, and creating it may fail. */
private TypeAdapter<T> delegate; private TypeAdapter<T> delegate;
private TreeTypeAdapter(JsonSerializer<T> serializer, JsonDeserializer<T> deserializer, public TreeTypeAdapter(JsonSerializer<T> serializer, JsonDeserializer<T> deserializer,
Gson gson, TypeToken<T> typeToken, TypeAdapterFactory skipPast) { Gson gson, TypeToken<T> typeToken, TypeAdapterFactory skipPast) {
this.serializer = serializer; this.serializer = serializer;
this.deserializer = deserializer; this.deserializer = deserializer;
@ -56,7 +59,7 @@ final class TreeTypeAdapter<T> extends TypeAdapter<T> {
if (value.isJsonNull()) { if (value.isJsonNull()) {
return null; return null;
} }
return deserializer.deserialize(value, typeToken.getType(), gson.deserializationContext); return deserializer.deserialize(value, typeToken.getType(), context);
} }
@Override public void write(JsonWriter out, T value) throws IOException { @Override public void write(JsonWriter out, T value) throws IOException {
@ -68,7 +71,7 @@ final class TreeTypeAdapter<T> extends TypeAdapter<T> {
out.nullValue(); out.nullValue();
return; return;
} }
JsonElement tree = serializer.serialize(value, typeToken.getType(), gson.serializationContext); JsonElement tree = serializer.serialize(value, typeToken.getType(), context);
Streams.write(tree, out); Streams.write(tree, out);
} }
@ -106,14 +109,14 @@ final class TreeTypeAdapter<T> extends TypeAdapter<T> {
return new SingleTypeFactory(typeAdapter, null, false, hierarchyType); return new SingleTypeFactory(typeAdapter, null, false, hierarchyType);
} }
private static class SingleTypeFactory implements TypeAdapterFactory { private static final class SingleTypeFactory implements TypeAdapterFactory {
private final TypeToken<?> exactType; private final TypeToken<?> exactType;
private final boolean matchRawType; private final boolean matchRawType;
private final Class<?> hierarchyType; private final Class<?> hierarchyType;
private final JsonSerializer<?> serializer; private final JsonSerializer<?> serializer;
private final JsonDeserializer<?> deserializer; private final JsonDeserializer<?> deserializer;
private SingleTypeFactory(Object typeAdapter, TypeToken<?> exactType, boolean matchRawType, SingleTypeFactory(Object typeAdapter, TypeToken<?> exactType, boolean matchRawType,
Class<?> hierarchyType) { Class<?> hierarchyType) {
serializer = typeAdapter instanceof JsonSerializer serializer = typeAdapter instanceof JsonSerializer
? (JsonSerializer<?>) typeAdapter ? (JsonSerializer<?>) typeAdapter
@ -128,15 +131,28 @@ final class TreeTypeAdapter<T> extends TypeAdapter<T> {
} }
@SuppressWarnings("unchecked") // guarded by typeToken.equals() call @SuppressWarnings("unchecked") // guarded by typeToken.equals() call
@Override
public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type) { public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type) {
boolean matches = exactType != null boolean matches = exactType != null
? exactType.equals(type) || matchRawType && exactType.getType() == type.getRawType() ? exactType.equals(type) || matchRawType && exactType.getType() == type.getRawType()
: hierarchyType.isAssignableFrom(type.getRawType()); : hierarchyType.isAssignableFrom(type.getRawType());
return matches return matches
? new TreeTypeAdapter<>((JsonSerializer<T>) serializer, ? new TreeTypeAdapter<>((JsonSerializer<T>) serializer,
(JsonDeserializer<T>) deserializer, gson, type, this (JsonDeserializer<T>) deserializer, gson, type, this)
)
: null; : null;
} }
} }
private final class GsonContextImpl implements JsonSerializationContext, JsonDeserializationContext {
@Override public JsonElement serialize(Object src) {
return gson.toJsonTree(src);
}
@Override public JsonElement serialize(Object src, Type typeOfSrc) {
return gson.toJsonTree(src, typeOfSrc);
}
@SuppressWarnings("unchecked")
@Override public <R> R deserialize(JsonElement json, Type typeOfT) throws JsonParseException {
return (R) gson.fromJson(json, typeOfT);
}
};
} }

View File

@ -16,16 +16,7 @@
package com.massivecraft.massivecore.xlib.gson.internal.bind; package com.massivecraft.massivecore.xlib.gson.internal.bind;
import com.massivecraft.massivecore.xlib.gson.Gson; import com.massivecraft.massivecore.xlib.gson.*;
import com.massivecraft.massivecore.xlib.gson.JsonArray;
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.JsonObject;
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.annotations.SerializedName; import com.massivecraft.massivecore.xlib.gson.annotations.SerializedName;
import com.massivecraft.massivecore.xlib.gson.internal.LazilyParsedNumber; import com.massivecraft.massivecore.xlib.gson.internal.LazilyParsedNumber;
import com.massivecraft.massivecore.xlib.gson.reflect.TypeToken; import com.massivecraft.massivecore.xlib.gson.reflect.TypeToken;
@ -41,21 +32,18 @@ import java.net.URI;
import java.net.URISyntaxException; import java.net.URISyntaxException;
import java.net.URL; import java.net.URL;
import java.sql.Timestamp; import java.sql.Timestamp;
import java.util.BitSet; import java.util.*;
import java.util.Calendar; import java.util.concurrent.atomic.AtomicBoolean;
import java.util.Date; import java.util.concurrent.atomic.AtomicInteger;
import java.util.GregorianCalendar; import java.util.concurrent.atomic.AtomicIntegerArray;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import java.util.StringTokenizer;
import java.util.UUID;
/** /**
* Type adapters for basic types. * Type adapters for basic types.
*/ */
public final class TypeAdapters { public final class TypeAdapters {
private TypeAdapters() {} private TypeAdapters() {
throw new UnsupportedOperationException();
}
@SuppressWarnings("rawtypes") @SuppressWarnings("rawtypes")
public static final TypeAdapter<Class> CLASS = new TypeAdapter<Class>() { public static final TypeAdapter<Class> CLASS = new TypeAdapter<Class>() {
@ -82,7 +70,7 @@ public final class TypeAdapters {
public static final TypeAdapterFactory CLASS_FACTORY = newFactory(Class.class, CLASS); public static final TypeAdapterFactory CLASS_FACTORY = newFactory(Class.class, CLASS);
public static final TypeAdapter<BitSet> BIT_SET = new TypeAdapter<BitSet>() { public static final TypeAdapter<BitSet> BIT_SET = new TypeAdapter<BitSet>() {
public BitSet read(JsonReader in) throws IOException { @Override public BitSet read(JsonReader in) throws IOException {
if (in.peek() == JsonToken.NULL) { if (in.peek() == JsonToken.NULL) {
in.nextNull(); in.nextNull();
return null; return null;
@ -123,7 +111,7 @@ public final class TypeAdapters {
return bitset; return bitset;
} }
public void write(JsonWriter out, BitSet src) throws IOException { @Override public void write(JsonWriter out, BitSet src) throws IOException {
if (src == null) { if (src == null) {
out.nullValue(); out.nullValue();
return; return;
@ -154,10 +142,6 @@ public final class TypeAdapters {
} }
@Override @Override
public void write(JsonWriter out, Boolean value) throws IOException { public void write(JsonWriter out, Boolean value) throws IOException {
if (value == null) {
out.nullValue();
return;
}
out.value(value); out.value(value);
} }
}; };
@ -246,10 +230,66 @@ public final class TypeAdapters {
out.value(value); out.value(value);
} }
}; };
public static final TypeAdapterFactory INTEGER_FACTORY public static final TypeAdapterFactory INTEGER_FACTORY
= newFactory(int.class, Integer.class, INTEGER); = newFactory(int.class, Integer.class, INTEGER);
public static final TypeAdapter<AtomicInteger> ATOMIC_INTEGER = new TypeAdapter<AtomicInteger>() {
@Override public AtomicInteger read(JsonReader in) throws IOException {
try {
return new AtomicInteger(in.nextInt());
} catch (NumberFormatException e) {
throw new JsonSyntaxException(e);
}
}
@Override public void write(JsonWriter out, AtomicInteger value) throws IOException {
out.value(value.get());
}
}.nullSafe();
public static final TypeAdapterFactory ATOMIC_INTEGER_FACTORY =
newFactory(AtomicInteger.class, TypeAdapters.ATOMIC_INTEGER);
public static final TypeAdapter<AtomicBoolean> ATOMIC_BOOLEAN = new TypeAdapter<AtomicBoolean>() {
@Override public AtomicBoolean read(JsonReader in) throws IOException {
return new AtomicBoolean(in.nextBoolean());
}
@Override public void write(JsonWriter out, AtomicBoolean value) throws IOException {
out.value(value.get());
}
}.nullSafe();
public static final TypeAdapterFactory ATOMIC_BOOLEAN_FACTORY =
newFactory(AtomicBoolean.class, TypeAdapters.ATOMIC_BOOLEAN);
public static final TypeAdapter<AtomicIntegerArray> ATOMIC_INTEGER_ARRAY = new TypeAdapter<AtomicIntegerArray>() {
@Override public AtomicIntegerArray read(JsonReader in) throws IOException {
List<Integer> list = new ArrayList<>();
in.beginArray();
while (in.hasNext()) {
try {
int integer = in.nextInt();
list.add(integer);
} catch (NumberFormatException e) {
throw new JsonSyntaxException(e);
}
}
in.endArray();
int length = list.size();
AtomicIntegerArray array = new AtomicIntegerArray(length);
for (int i = 0; i < length; ++i) {
array.set(i, list.get(i));
}
return array;
}
@Override public void write(JsonWriter out, AtomicIntegerArray value) throws IOException {
out.beginArray();
for (int i = 0, length = value.length(); i < length; i++) {
out.value(value.get(i));
}
out.endArray();
}
}.nullSafe();
public static final TypeAdapterFactory ATOMIC_INTEGER_ARRAY_FACTORY =
newFactory(AtomicIntegerArray.class, TypeAdapters.ATOMIC_INTEGER_ARRAY);
public static final TypeAdapter<Number> LONG = new TypeAdapter<Number>() { public static final TypeAdapter<Number> LONG = new TypeAdapter<Number>() {
@Override @Override
public Number read(JsonReader in) throws IOException { public Number read(JsonReader in) throws IOException {
@ -513,9 +553,21 @@ public final class TypeAdapters {
public static final TypeAdapterFactory UUID_FACTORY = newFactory(UUID.class, UUID); public static final TypeAdapterFactory UUID_FACTORY = newFactory(UUID.class, UUID);
public static final TypeAdapter<Currency> CURRENCY = new TypeAdapter<Currency>() {
@Override
public Currency read(JsonReader in) throws IOException {
return Currency.getInstance(in.nextString());
}
@Override
public void write(JsonWriter out, Currency value) throws IOException {
out.value(value.getCurrencyCode());
}
}.nullSafe();
public static final TypeAdapterFactory CURRENCY_FACTORY = newFactory(Currency.class, CURRENCY);
public static final TypeAdapterFactory TIMESTAMP_FACTORY = new TypeAdapterFactory() { public static final TypeAdapterFactory TIMESTAMP_FACTORY = new TypeAdapterFactory() {
@SuppressWarnings("unchecked") // we use a runtime check to make sure the 'T's equal @SuppressWarnings("unchecked") // we use a runtime check to make sure the 'T's equal
public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> typeToken) { @Override public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> typeToken) {
if (typeToken.getRawType() != Timestamp.class) { if (typeToken.getRawType() != Timestamp.class) {
return null; return null;
} }
@ -725,15 +777,18 @@ public final class TypeAdapters {
SerializedName annotation = classOfT.getField(name).getAnnotation(SerializedName.class); SerializedName annotation = classOfT.getField(name).getAnnotation(SerializedName.class);
if (annotation != null) { if (annotation != null) {
name = annotation.value(); name = annotation.value();
for (String alternate : annotation.alternate()) {
nameToConstant.put(alternate, constant);
}
} }
nameToConstant.put(name, constant); nameToConstant.put(name, constant);
constantToName.put(constant, name); constantToName.put(constant, name);
} }
} catch (NoSuchFieldException e) { } catch (NoSuchFieldException e) {
throw new AssertionError(); throw new AssertionError(e);
} }
} }
public T read(JsonReader in) throws IOException { @Override public T read(JsonReader in) throws IOException {
if (in.peek() == JsonToken.NULL) { if (in.peek() == JsonToken.NULL) {
in.nextNull(); in.nextNull();
return null; return null;
@ -741,14 +796,14 @@ public final class TypeAdapters {
return nameToConstant.get(in.nextString()); return nameToConstant.get(in.nextString());
} }
public void write(JsonWriter out, T value) throws IOException { @Override public void write(JsonWriter out, T value) throws IOException {
out.value(value == null ? null : constantToName.get(value)); out.value(value == null ? null : constantToName.get(value));
} }
} }
public static final TypeAdapterFactory ENUM_FACTORY = new TypeAdapterFactory() { public static final TypeAdapterFactory ENUM_FACTORY = new TypeAdapterFactory() {
@SuppressWarnings({"rawtypes", "unchecked"}) @SuppressWarnings({"rawtypes", "unchecked"})
public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> typeToken) { @Override public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> typeToken) {
Class<? super T> rawType = typeToken.getRawType(); Class<? super T> rawType = typeToken.getRawType();
if (!Enum.class.isAssignableFrom(rawType) || rawType == Enum.class) { if (!Enum.class.isAssignableFrom(rawType) || rawType == Enum.class) {
return null; return null;
@ -764,7 +819,7 @@ public final class TypeAdapters {
final TypeToken<TT> type, final TypeAdapter<TT> typeAdapter) { final TypeToken<TT> type, final TypeAdapter<TT> typeAdapter) {
return new TypeAdapterFactory() { return new TypeAdapterFactory() {
@SuppressWarnings("unchecked") // we use a runtime check to make sure the 'T's equal @SuppressWarnings("unchecked") // we use a runtime check to make sure the 'T's equal
public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> typeToken) { @Override public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> typeToken) {
return typeToken.equals(type) ? (TypeAdapter<T>) typeAdapter : null; return typeToken.equals(type) ? (TypeAdapter<T>) typeAdapter : null;
} }
}; };
@ -774,7 +829,7 @@ public final class TypeAdapters {
final Class<TT> type, final TypeAdapter<TT> typeAdapter) { final Class<TT> type, final TypeAdapter<TT> typeAdapter) {
return new TypeAdapterFactory() { return new TypeAdapterFactory() {
@SuppressWarnings("unchecked") // we use a runtime check to make sure the 'T's equal @SuppressWarnings("unchecked") // we use a runtime check to make sure the 'T's equal
public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> typeToken) { @Override public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> typeToken) {
return typeToken.getRawType() == type ? (TypeAdapter<T>) typeAdapter : null; return typeToken.getRawType() == type ? (TypeAdapter<T>) typeAdapter : null;
} }
@Override public String toString() { @Override public String toString() {
@ -787,7 +842,7 @@ public final class TypeAdapters {
final Class<TT> unboxed, final Class<TT> boxed, final TypeAdapter<? super TT> typeAdapter) { final Class<TT> unboxed, final Class<TT> boxed, final TypeAdapter<? super TT> typeAdapter) {
return new TypeAdapterFactory() { return new TypeAdapterFactory() {
@SuppressWarnings("unchecked") // we use a runtime check to make sure the 'T's equal @SuppressWarnings("unchecked") // we use a runtime check to make sure the 'T's equal
public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> typeToken) { @Override public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> typeToken) {
Class<? super T> rawType = typeToken.getRawType(); Class<? super T> rawType = typeToken.getRawType();
return (rawType == unboxed || rawType == boxed) ? (TypeAdapter<T>) typeAdapter : null; return (rawType == unboxed || rawType == boxed) ? (TypeAdapter<T>) typeAdapter : null;
} }
@ -802,7 +857,7 @@ public final class TypeAdapters {
final Class<? extends TT> sub, final TypeAdapter<? super TT> typeAdapter) { final Class<? extends TT> sub, final TypeAdapter<? super TT> typeAdapter) {
return new TypeAdapterFactory() { return new TypeAdapterFactory() {
@SuppressWarnings("unchecked") // we use a runtime check to make sure the 'T's equal @SuppressWarnings("unchecked") // we use a runtime check to make sure the 'T's equal
public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> typeToken) { @Override public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> typeToken) {
Class<? super T> rawType = typeToken.getRawType(); Class<? super T> rawType = typeToken.getRawType();
return (rawType == base || rawType == sub) ? (TypeAdapter<T>) typeAdapter : null; return (rawType == base || rawType == sub) ? (TypeAdapter<T>) typeAdapter : null;
} }
@ -813,12 +868,33 @@ public final class TypeAdapters {
}; };
} }
public static <TT> TypeAdapterFactory newTypeHierarchyFactory( /**
final Class<TT> clazz, final TypeAdapter<TT> typeAdapter) { * Returns a factory for all subtypes of {@code typeAdapter}. We do a runtime check to confirm
* that the deserialized type matches the type requested.
*/
public static <T1> TypeAdapterFactory newTypeHierarchyFactory(
final Class<T1> clazz, final TypeAdapter<T1> typeAdapter) {
return new TypeAdapterFactory() { return new TypeAdapterFactory() {
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> typeToken) { @Override public <T2> TypeAdapter<T2> create(Gson gson, TypeToken<T2> typeToken) {
return clazz.isAssignableFrom(typeToken.getRawType()) ? (TypeAdapter<T>) typeAdapter : null; final Class<? super T2> requestedType = typeToken.getRawType();
if (!clazz.isAssignableFrom(requestedType)) {
return null;
}
return (TypeAdapter<T2>) new TypeAdapter<T1>() {
@Override public void write(JsonWriter out, T1 value) throws IOException {
typeAdapter.write(out, value);
}
@Override public T1 read(JsonReader in) throws IOException {
T1 result = typeAdapter.read(in);
if (result != null && !requestedType.isInstance(result)) {
throw new JsonSyntaxException("Expected a " + requestedType.getName()
+ " but was " + result.getClass().getName());
}
return result;
}
};
} }
@Override public String toString() { @Override public String toString() {
return "Factory[typeHierarchy=" + clazz.getName() + ",adapter=" + typeAdapter + "]"; return "Factory[typeHierarchy=" + clazz.getName() + ",adapter=" + typeAdapter + "]";

View File

@ -0,0 +1,352 @@
package com.massivecraft.massivecore.xlib.gson.internal.bind.util;
import java.text.ParseException;
import java.text.ParsePosition;
import java.util.*;
/**
* Utilities methods for manipulating dates in iso8601 format. This is much much faster and GC friendly than using SimpleDateFormat so
* highly suitable if you (un)serialize lots of date objects.
*
* Supported parse format: [yyyy-MM-dd|yyyyMMdd][T(hh:mm[:ss[.sss]]|hhmm[ss[.sss]])]?[Z|[+-]hh[:]mm]]
*
* @see <a href="http://www.w3.org/TR/NOTE-datetime">this specification</a>
*/
//Date parsing code from Jackson databind ISO8601Utils.java
// https://github.com/FasterXML/jackson-databind/blob/master/src/main/java/com/fasterxml/jackson/databind/util/ISO8601Utils.java
public class ISO8601Utils
{
/**
* ID to represent the 'UTC' string, default timezone since Jackson 2.7
*
* @since 2.7
*/
private static final String UTC_ID = "UTC";
/**
* The UTC timezone, prefetched to avoid more lookups.
*
* @since 2.7
*/
private static final TimeZone TIMEZONE_UTC = TimeZone.getTimeZone(UTC_ID);
/*
/**********************************************************
/* Formatting
/**********************************************************
*/
/**
* Format a date into 'yyyy-MM-ddThh:mm:ssZ' (default timezone, no milliseconds precision)
*
* @param date the date to format
* @return the date formatted as 'yyyy-MM-ddThh:mm:ssZ'
*/
public static String format(Date date) {
return format(date, false, TIMEZONE_UTC);
}
/**
* Format a date into 'yyyy-MM-ddThh:mm:ss[.sss]Z' (GMT timezone)
*
* @param date the date to format
* @param millis true to include millis precision otherwise false
* @return the date formatted as 'yyyy-MM-ddThh:mm:ss[.sss]Z'
*/
public static String format(Date date, boolean millis) {
return format(date, millis, TIMEZONE_UTC);
}
/**
* Format date into yyyy-MM-ddThh:mm:ss[.sss][Z|[+-]hh:mm]
*
* @param date the date to format
* @param millis true to include millis precision otherwise false
* @param tz timezone to use for the formatting (UTC will produce 'Z')
* @return the date formatted as yyyy-MM-ddThh:mm:ss[.sss][Z|[+-]hh:mm]
*/
public static String format(Date date, boolean millis, TimeZone tz) {
Calendar calendar = new GregorianCalendar(tz, Locale.US);
calendar.setTime(date);
// estimate capacity of buffer as close as we can (yeah, that's pedantic ;)
int capacity = "yyyy-MM-ddThh:mm:ss".length();
capacity += millis ? ".sss".length() : 0;
capacity += tz.getRawOffset() == 0 ? "Z".length() : "+hh:mm".length();
StringBuilder formatted = new StringBuilder(capacity);
padInt(formatted, calendar.get(Calendar.YEAR), "yyyy".length());
formatted.append('-');
padInt(formatted, calendar.get(Calendar.MONTH) + 1, "MM".length());
formatted.append('-');
padInt(formatted, calendar.get(Calendar.DAY_OF_MONTH), "dd".length());
formatted.append('T');
padInt(formatted, calendar.get(Calendar.HOUR_OF_DAY), "hh".length());
formatted.append(':');
padInt(formatted, calendar.get(Calendar.MINUTE), "mm".length());
formatted.append(':');
padInt(formatted, calendar.get(Calendar.SECOND), "ss".length());
if (millis) {
formatted.append('.');
padInt(formatted, calendar.get(Calendar.MILLISECOND), "sss".length());
}
int offset = tz.getOffset(calendar.getTimeInMillis());
if (offset != 0) {
int hours = Math.abs((offset / (60 * 1000)) / 60);
int minutes = Math.abs((offset / (60 * 1000)) % 60);
formatted.append(offset < 0 ? '-' : '+');
padInt(formatted, hours, "hh".length());
formatted.append(':');
padInt(formatted, minutes, "mm".length());
} else {
formatted.append('Z');
}
return formatted.toString();
}
/*
/**********************************************************
/* Parsing
/**********************************************************
*/
/**
* Parse a date from ISO-8601 formatted string. It expects a format
* [yyyy-MM-dd|yyyyMMdd][T(hh:mm[:ss[.sss]]|hhmm[ss[.sss]])]?[Z|[+-]hh[:mm]]]
*
* @param date ISO string to parse in the appropriate format.
* @param pos The position to start parsing from, updated to where parsing stopped.
* @return the parsed date
* @throws ParseException if the date is not in the appropriate format
*/
public static Date parse(String date, ParsePosition pos) throws ParseException {
Exception fail = null;
try {
int offset = pos.getIndex();
// extract year
int year = parseInt(date, offset, offset += 4);
if (checkOffset(date, offset, '-')) {
offset += 1;
}
// extract month
int month = parseInt(date, offset, offset += 2);
if (checkOffset(date, offset, '-')) {
offset += 1;
}
// extract day
int day = parseInt(date, offset, offset += 2);
// default time value
int hour = 0;
int minutes = 0;
int seconds = 0;
int milliseconds = 0; // always use 0 otherwise returned date will include millis of current time
// if the value has no time component (and no time zone), we are done
boolean hasT = checkOffset(date, offset, 'T');
if (!hasT && (date.length() <= offset)) {
Calendar calendar = new GregorianCalendar(year, month - 1, day);
pos.setIndex(offset);
return calendar.getTime();
}
if (hasT) {
// extract hours, minutes, seconds and milliseconds
hour = parseInt(date, offset += 1, offset += 2);
if (checkOffset(date, offset, ':')) {
offset += 1;
}
minutes = parseInt(date, offset, offset += 2);
if (checkOffset(date, offset, ':')) {
offset += 1;
}
// second and milliseconds can be optional
if (date.length() > offset) {
char c = date.charAt(offset);
if (c != 'Z' && c != '+' && c != '-') {
seconds = parseInt(date, offset, offset += 2);
if (seconds > 59 && seconds < 63) seconds = 59; // truncate up to 3 leap seconds
// milliseconds can be optional in the format
if (checkOffset(date, offset, '.')) {
offset += 1;
int endOffset = indexOfNonDigit(date, offset + 1); // assume at least one digit
int parseEndOffset = Math.min(endOffset, offset + 3); // parse up to 3 digits
int fraction = parseInt(date, offset, parseEndOffset);
// compensate for "missing" digits
switch (parseEndOffset - offset) { // number of digits parsed
case 2:
milliseconds = fraction * 10;
break;
case 1:
milliseconds = fraction * 100;
break;
default:
milliseconds = fraction;
}
offset = endOffset;
}
}
}
}
// extract timezone
if (date.length() <= offset) {
throw new IllegalArgumentException("No time zone indicator");
}
TimeZone timezone = null;
char timezoneIndicator = date.charAt(offset);
if (timezoneIndicator == 'Z') {
timezone = TIMEZONE_UTC;
offset += 1;
} else if (timezoneIndicator == '+' || timezoneIndicator == '-') {
String timezoneOffset = date.substring(offset);
// When timezone has no minutes, we should append it, valid timezones are, for example: +00:00, +0000 and +00
timezoneOffset = timezoneOffset.length() >= 5 ? timezoneOffset : timezoneOffset + "00";
offset += timezoneOffset.length();
// 18-Jun-2015, tatu: Minor simplification, skip offset of "+0000"/"+00:00"
if ("+0000".equals(timezoneOffset) || "+00:00".equals(timezoneOffset)) {
timezone = TIMEZONE_UTC;
} else {
// 18-Jun-2015, tatu: Looks like offsets only work from GMT, not UTC...
// not sure why, but that's the way it looks. Further, Javadocs for
// `java.util.TimeZone` specifically instruct use of GMT as base for
// custom timezones... odd.
String timezoneId = "GMT" + timezoneOffset;
// String timezoneId = "UTC" + timezoneOffset;
timezone = TimeZone.getTimeZone(timezoneId);
String act = timezone.getID();
if (!act.equals(timezoneId)) {
/* 22-Jan-2015, tatu: Looks like canonical version has colons, but we may be given
* one without. If so, don't sweat.
* Yes, very inefficient. Hopefully not hit often.
* If it becomes a perf problem, add 'loose' comparison instead.
*/
String cleaned = act.replace(":", "");
if (!cleaned.equals(timezoneId)) {
throw new IndexOutOfBoundsException("Mismatching time zone indicator: "+timezoneId+" given, resolves to "
+timezone.getID());
}
}
}
} else {
throw new IndexOutOfBoundsException("Invalid time zone indicator '" + timezoneIndicator+"'");
}
Calendar calendar = new GregorianCalendar(timezone);
calendar.setLenient(false);
calendar.set(Calendar.YEAR, year);
calendar.set(Calendar.MONTH, month - 1);
calendar.set(Calendar.DAY_OF_MONTH, day);
calendar.set(Calendar.HOUR_OF_DAY, hour);
calendar.set(Calendar.MINUTE, minutes);
calendar.set(Calendar.SECOND, seconds);
calendar.set(Calendar.MILLISECOND, milliseconds);
pos.setIndex(offset);
return calendar.getTime();
// If we get a ParseException it'll already have the right message/offset.
// Other exception types can convert here.
} catch (IndexOutOfBoundsException e) {
fail = e;
} catch (NumberFormatException e) {
fail = e;
} catch (IllegalArgumentException e) {
fail = e;
}
String input = (date == null) ? null : ('"' + date + "'");
String msg = fail.getMessage();
if (msg == null || msg.isEmpty()) {
msg = "("+fail.getClass().getName()+")";
}
ParseException ex = new ParseException("Failed to parse date [" + input + "]: " + msg, pos.getIndex());
ex.initCause(fail);
throw ex;
}
/**
* Check if the expected character exist at the given offset in the value.
*
* @param value the string to check at the specified offset
* @param offset the offset to look for the expected character
* @param expected the expected character
* @return true if the expected character exist at the given offset
*/
private static boolean checkOffset(String value, int offset, char expected) {
return (offset < value.length()) && (value.charAt(offset) == expected);
}
/**
* Parse an integer located between 2 given offsets in a string
*
* @param value the string to parse
* @param beginIndex the start index for the integer in the string
* @param endIndex the end index for the integer in the string
* @return the int
* @throws NumberFormatException if the value is not a number
*/
private static int parseInt(String value, int beginIndex, int endIndex) throws NumberFormatException {
if (beginIndex < 0 || endIndex > value.length() || beginIndex > endIndex) {
throw new NumberFormatException(value);
}
// use same logic as in Integer.parseInt() but less generic we're not supporting negative values
int i = beginIndex;
int result = 0;
int digit;
if (i < endIndex) {
digit = Character.digit(value.charAt(i++), 10);
if (digit < 0) {
throw new NumberFormatException("Invalid number: " + value.substring(beginIndex, endIndex));
}
result = -digit;
}
while (i < endIndex) {
digit = Character.digit(value.charAt(i++), 10);
if (digit < 0) {
throw new NumberFormatException("Invalid number: " + value.substring(beginIndex, endIndex));
}
result *= 10;
result -= digit;
}
return -result;
}
/**
* Zero pad a number to a specified length
*
* @param buffer buffer to use for padding
* @param value the integer value to pad if necessary.
* @param length the length of the string we should zero pad
*/
private static void padInt(StringBuilder buffer, int value, int length) {
String strValue = Integer.toString(value);
for (int i = length - strValue.length(); i > 0; i--) {
buffer.append('0');
}
buffer.append(strValue);
}
/**
* Returns the index of the first character in the string that is not a digit, starting at offset.
*/
private static int indexOfNonDigit(String string, int offset) {
for (int i = offset; i < string.length(); i++) {
char c = string.charAt(i);
if (c < '0' || c > '9') return i;
}
return string.length();
}
}

View File

@ -303,4 +303,19 @@ public class TypeToken<T> {
public static <T> TypeToken<T> get(Class<T> type) { public static <T> TypeToken<T> get(Class<T> type) {
return new TypeToken<>(type); return new TypeToken<>(type);
} }
/**
* Gets type literal for the parameterized type represented by applying {@code typeArguments} to
* {@code rawType}.
*/
public static TypeToken<?> getParameterized(Type rawType, Type... typeArguments) {
return new TypeToken<Object>($Gson$Types.newParameterizedTypeWithOwner(null, rawType, typeArguments));
}
/**
* Gets type literal for the array type whose elements are all instances of {@code componentType}.
*/
public static TypeToken<?> getArray(Type componentType) {
return new TypeToken<Object>($Gson$Types.arrayOf(componentType));
}
} }

View File

@ -25,7 +25,7 @@ import java.io.IOException;
import java.io.Reader; import java.io.Reader;
/** /**
* Reads a JSON (<a href="http://www.ietf.org/rfc/rfc4627.txt">RFC 4627</a>) * Reads a JSON (<a href="http://www.ietf.org/rfc/rfc7159.txt">RFC 7159</a>)
* encoded value as a stream of tokens. This stream includes both literal * encoded value as a stream of tokens. This stream includes both literal
* values (strings, numbers, booleans, and nulls) as well as the begin and * values (strings, numbers, booleans, and nulls) as well as the begin and
* end delimiters of objects and arrays. The tokens are traversed in * end delimiters of objects and arrays. The tokens are traversed in
@ -243,7 +243,7 @@ public class JsonReader implements Closeable {
private int lineNumber = 0; private int lineNumber = 0;
private int lineStart = 0; private int lineStart = 0;
private int peeked = PEEKED_NONE; int peeked = PEEKED_NONE;
/** /**
* A peeked value that was composed entirely of digits with an optional * A peeked value that was composed entirely of digits with an optional
@ -295,7 +295,7 @@ public class JsonReader implements Closeable {
} }
/** /**
* Configure this parser to be be liberal in what it accepts. By default, * Configure this parser to be liberal in what it accepts. By default,
* this parser is strict and only accepts JSON as specified by <a * this parser is strict and only accepts JSON as specified by <a
* href="http://www.ietf.org/rfc/rfc4627.txt">RFC 4627</a>. Setting the * href="http://www.ietf.org/rfc/rfc4627.txt">RFC 4627</a>. Setting the
* parser to lenient causes it to ignore the following syntax errors: * parser to lenient causes it to ignore the following syntax errors:
@ -348,8 +348,7 @@ public class JsonReader implements Closeable {
pathIndices[stackSize - 1] = 0; pathIndices[stackSize - 1] = 0;
peeked = PEEKED_NONE; peeked = PEEKED_NONE;
} else { } else {
throw new IllegalStateException("Expected BEGIN_ARRAY but was " + peek() throw new IllegalStateException("Expected BEGIN_ARRAY but was " + peek() + locationString());
+ " at line " + getLineNumber() + " column " + getColumnNumber() + " path " + getPath());
} }
} }
@ -367,8 +366,7 @@ public class JsonReader implements Closeable {
pathIndices[stackSize - 1]++; pathIndices[stackSize - 1]++;
peeked = PEEKED_NONE; peeked = PEEKED_NONE;
} else { } else {
throw new IllegalStateException("Expected END_ARRAY but was " + peek() throw new IllegalStateException("Expected END_ARRAY but was " + peek() + locationString());
+ " at line " + getLineNumber() + " column " + getColumnNumber() + " path " + getPath());
} }
} }
@ -385,8 +383,7 @@ public class JsonReader implements Closeable {
push(JsonScope.EMPTY_OBJECT); push(JsonScope.EMPTY_OBJECT);
peeked = PEEKED_NONE; peeked = PEEKED_NONE;
} else { } else {
throw new IllegalStateException("Expected BEGIN_OBJECT but was " + peek() throw new IllegalStateException("Expected BEGIN_OBJECT but was " + peek() + locationString());
+ " at line " + getLineNumber() + " column " + getColumnNumber() + " path " + getPath());
} }
} }
@ -405,8 +402,7 @@ public class JsonReader implements Closeable {
pathIndices[stackSize - 1]++; pathIndices[stackSize - 1]++;
peeked = PEEKED_NONE; peeked = PEEKED_NONE;
} else { } else {
throw new IllegalStateException("Expected END_OBJECT but was " + peek() throw new IllegalStateException("Expected END_OBJECT but was " + peek() + locationString());
+ " at line " + getLineNumber() + " column " + getColumnNumber() + " path " + getPath());
} }
} }
@ -463,7 +459,7 @@ public class JsonReader implements Closeable {
} }
} }
private int doPeek() throws IOException { int doPeek() throws IOException {
int peekStack = stack[stackSize - 1]; int peekStack = stack[stackSize - 1];
if (peekStack == JsonScope.EMPTY_ARRAY) { if (peekStack == JsonScope.EMPTY_ARRAY) {
stack[stackSize - 1] = JsonScope.NONEMPTY_ARRAY; stack[stackSize - 1] = JsonScope.NONEMPTY_ARRAY;
@ -572,9 +568,6 @@ public class JsonReader implements Closeable {
checkLenient(); checkLenient();
return peeked = PEEKED_SINGLE_QUOTED; return peeked = PEEKED_SINGLE_QUOTED;
case '"': case '"':
if (stackSize == 1) {
checkLenient();
}
return peeked = PEEKED_DOUBLE_QUOTED; return peeked = PEEKED_DOUBLE_QUOTED;
case '[': case '[':
return peeked = PEEKED_BEGIN_ARRAY; return peeked = PEEKED_BEGIN_ARRAY;
@ -584,10 +577,6 @@ public class JsonReader implements Closeable {
pos--; // Don't consume the first character in a literal value. pos--; // Don't consume the first character in a literal value.
} }
if (stackSize == 1) {
checkLenient(); // Top-level value isn't an array or an object.
}
int result = peekKeyword(); int result = peekKeyword();
if (result != PEEKED_NONE) { if (result != PEEKED_NONE) {
return result; return result;
@ -779,7 +768,7 @@ public class JsonReader implements Closeable {
} }
/** /**
* Returns the next token, a {@link com.massivecraft.massivecore.xlib.gson.stream.JsonToken#NAME property name}, and * Returns the next token, a {@link JsonToken#NAME property name}, and
* consumes it. * consumes it.
* *
* @throws java.io.IOException if the next token in the stream is not a property * @throws java.io.IOException if the next token in the stream is not a property
@ -798,8 +787,7 @@ public class JsonReader implements Closeable {
} else if (p == PEEKED_DOUBLE_QUOTED_NAME) { } else if (p == PEEKED_DOUBLE_QUOTED_NAME) {
result = nextQuotedValue('"'); result = nextQuotedValue('"');
} else { } else {
throw new IllegalStateException("Expected a name but was " + peek() throw new IllegalStateException("Expected a name but was " + peek() + locationString());
+ " at line " + getLineNumber() + " column " + getColumnNumber() + " path " + getPath());
} }
peeked = PEEKED_NONE; peeked = PEEKED_NONE;
pathNames[stackSize - 1] = result; pathNames[stackSize - 1] = result;
@ -807,7 +795,7 @@ public class JsonReader implements Closeable {
} }
/** /**
* Returns the {@link com.massivecraft.massivecore.xlib.gson.stream.JsonToken#STRING string} value of the next token, * Returns the {@link JsonToken#STRING string} value of the next token,
* consuming it. If the next token is a number, this method will return its * consuming it. If the next token is a number, this method will return its
* string form. * string form.
* *
@ -835,8 +823,7 @@ public class JsonReader implements Closeable {
result = new String(buffer, pos, peekedNumberLength); result = new String(buffer, pos, peekedNumberLength);
pos += peekedNumberLength; pos += peekedNumberLength;
} else { } else {
throw new IllegalStateException("Expected a string but was " + peek() throw new IllegalStateException("Expected a string but was " + peek() + locationString());
+ " at line " + getLineNumber() + " column " + getColumnNumber() + " path " + getPath());
} }
peeked = PEEKED_NONE; peeked = PEEKED_NONE;
pathIndices[stackSize - 1]++; pathIndices[stackSize - 1]++;
@ -844,7 +831,7 @@ public class JsonReader implements Closeable {
} }
/** /**
* Returns the {@link com.massivecraft.massivecore.xlib.gson.stream.JsonToken#BOOLEAN boolean} value of the next token, * Returns the {@link JsonToken#BOOLEAN boolean} value of the next token,
* consuming it. * consuming it.
* *
* @throws IllegalStateException if the next token is not a boolean or if * @throws IllegalStateException if the next token is not a boolean or if
@ -864,8 +851,7 @@ public class JsonReader implements Closeable {
pathIndices[stackSize - 1]++; pathIndices[stackSize - 1]++;
return false; return false;
} }
throw new IllegalStateException("Expected a boolean but was " + peek() throw new IllegalStateException("Expected a boolean but was " + peek() + locationString());
+ " at line " + getLineNumber() + " column " + getColumnNumber() + " path " + getPath());
} }
/** /**
@ -884,13 +870,12 @@ public class JsonReader implements Closeable {
peeked = PEEKED_NONE; peeked = PEEKED_NONE;
pathIndices[stackSize - 1]++; pathIndices[stackSize - 1]++;
} else { } else {
throw new IllegalStateException("Expected null but was " + peek() throw new IllegalStateException("Expected null but was " + peek() + locationString());
+ " at line " + getLineNumber() + " column " + getColumnNumber() + " path " + getPath());
} }
} }
/** /**
* Returns the {@link com.massivecraft.massivecore.xlib.gson.stream.JsonToken#NUMBER double} value of the next token, * Returns the {@link JsonToken#NUMBER double} value of the next token,
* consuming it. If the next token is a string, this method will attempt to * consuming it. If the next token is a string, this method will attempt to
* parse it as a double using {@link Double#parseDouble(String)}. * parse it as a double using {@link Double#parseDouble(String)}.
* *
@ -918,15 +903,14 @@ public class JsonReader implements Closeable {
} else if (p == PEEKED_UNQUOTED) { } else if (p == PEEKED_UNQUOTED) {
peekedString = nextUnquotedValue(); peekedString = nextUnquotedValue();
} else if (p != PEEKED_BUFFERED) { } else if (p != PEEKED_BUFFERED) {
throw new IllegalStateException("Expected a double but was " + peek() throw new IllegalStateException("Expected a double but was " + peek() + locationString());
+ " at line " + getLineNumber() + " column " + getColumnNumber() + " path " + getPath());
} }
peeked = PEEKED_BUFFERED; peeked = PEEKED_BUFFERED;
double result = Double.parseDouble(peekedString); // don't catch this NumberFormatException. double result = Double.parseDouble(peekedString); // don't catch this NumberFormatException.
if (!lenient && (Double.isNaN(result) || Double.isInfinite(result))) { if (!lenient && (Double.isNaN(result) || Double.isInfinite(result))) {
throw new MalformedJsonException("JSON forbids NaN and infinities: " + result throw new MalformedJsonException(
+ " at line " + getLineNumber() + " column " + getColumnNumber() + " path " + getPath()); "JSON forbids NaN and infinities: " + result + locationString());
} }
peekedString = null; peekedString = null;
peeked = PEEKED_NONE; peeked = PEEKED_NONE;
@ -935,7 +919,7 @@ public class JsonReader implements Closeable {
} }
/** /**
* Returns the {@link com.massivecraft.massivecore.xlib.gson.stream.JsonToken#NUMBER long} value of the next token, * Returns the {@link JsonToken#NUMBER long} value of the next token,
* consuming it. If the next token is a string, this method will attempt to * consuming it. If the next token is a string, this method will attempt to
* parse it as a long. If the next token's numeric value cannot be exactly * parse it as a long. If the next token's numeric value cannot be exactly
* represented by a Java {@code long}, this method throws. * represented by a Java {@code long}, this method throws.
@ -959,8 +943,12 @@ public class JsonReader implements Closeable {
if (p == PEEKED_NUMBER) { if (p == PEEKED_NUMBER) {
peekedString = new String(buffer, pos, peekedNumberLength); peekedString = new String(buffer, pos, peekedNumberLength);
pos += peekedNumberLength; pos += peekedNumberLength;
} else if (p == PEEKED_SINGLE_QUOTED || p == PEEKED_DOUBLE_QUOTED) { } else if (p == PEEKED_SINGLE_QUOTED || p == PEEKED_DOUBLE_QUOTED || p == PEEKED_UNQUOTED) {
if (p == PEEKED_UNQUOTED) {
peekedString = nextUnquotedValue();
} else {
peekedString = nextQuotedValue(p == PEEKED_SINGLE_QUOTED ? '\'' : '"'); peekedString = nextQuotedValue(p == PEEKED_SINGLE_QUOTED ? '\'' : '"');
}
try { try {
long result = Long.parseLong(peekedString); long result = Long.parseLong(peekedString);
peeked = PEEKED_NONE; peeked = PEEKED_NONE;
@ -970,16 +958,14 @@ public class JsonReader implements Closeable {
// Fall back to parse as a double below. // Fall back to parse as a double below.
} }
} else { } else {
throw new IllegalStateException("Expected a long but was " + peek() throw new IllegalStateException("Expected a long but was " + peek() + locationString());
+ " at line " + getLineNumber() + " column " + getColumnNumber() + " path " + getPath());
} }
peeked = PEEKED_BUFFERED; peeked = PEEKED_BUFFERED;
double asDouble = Double.parseDouble(peekedString); // don't catch this NumberFormatException. double asDouble = Double.parseDouble(peekedString); // don't catch this NumberFormatException.
long result = (long) asDouble; long result = (long) asDouble;
if (result != asDouble) { // Make sure no precision was lost casting to 'long'. if (result != asDouble) { // Make sure no precision was lost casting to 'long'.
throw new NumberFormatException("Expected a long but was " + peekedString throw new NumberFormatException("Expected a long but was " + peekedString + locationString());
+ " at line " + getLineNumber() + " column " + getColumnNumber() + " path " + getPath());
} }
peekedString = null; peekedString = null;
peeked = PEEKED_NONE; peeked = PEEKED_NONE;
@ -1157,7 +1143,7 @@ public class JsonReader implements Closeable {
} }
/** /**
* Returns the {@link com.massivecraft.massivecore.xlib.gson.stream.JsonToken#NUMBER int} value of the next token, * Returns the {@link JsonToken#NUMBER int} value of the next token,
* consuming it. If the next token is a string, this method will attempt to * consuming it. If the next token is a string, this method will attempt to
* parse it as an int. If the next token's numeric value cannot be exactly * parse it as an int. If the next token's numeric value cannot be exactly
* represented by a Java {@code int}, this method throws. * represented by a Java {@code int}, this method throws.
@ -1176,8 +1162,7 @@ public class JsonReader implements Closeable {
if (p == PEEKED_LONG) { if (p == PEEKED_LONG) {
result = (int) peekedLong; result = (int) peekedLong;
if (peekedLong != result) { // Make sure no precision was lost casting to 'int'. if (peekedLong != result) { // Make sure no precision was lost casting to 'int'.
throw new NumberFormatException("Expected an int but was " + peekedLong throw new NumberFormatException("Expected an int but was " + peekedLong + locationString());
+ " at line " + getLineNumber() + " column " + getColumnNumber() + " path " + getPath());
} }
peeked = PEEKED_NONE; peeked = PEEKED_NONE;
pathIndices[stackSize - 1]++; pathIndices[stackSize - 1]++;
@ -1187,8 +1172,12 @@ public class JsonReader implements Closeable {
if (p == PEEKED_NUMBER) { if (p == PEEKED_NUMBER) {
peekedString = new String(buffer, pos, peekedNumberLength); peekedString = new String(buffer, pos, peekedNumberLength);
pos += peekedNumberLength; pos += peekedNumberLength;
} else if (p == PEEKED_SINGLE_QUOTED || p == PEEKED_DOUBLE_QUOTED) { } else if (p == PEEKED_SINGLE_QUOTED || p == PEEKED_DOUBLE_QUOTED || p == PEEKED_UNQUOTED) {
if (p == PEEKED_UNQUOTED) {
peekedString = nextUnquotedValue();
} else {
peekedString = nextQuotedValue(p == PEEKED_SINGLE_QUOTED ? '\'' : '"'); peekedString = nextQuotedValue(p == PEEKED_SINGLE_QUOTED ? '\'' : '"');
}
try { try {
result = Integer.parseInt(peekedString); result = Integer.parseInt(peekedString);
peeked = PEEKED_NONE; peeked = PEEKED_NONE;
@ -1198,16 +1187,14 @@ public class JsonReader implements Closeable {
// Fall back to parse as a double below. // Fall back to parse as a double below.
} }
} else { } else {
throw new IllegalStateException("Expected an int but was " + peek() throw new IllegalStateException("Expected an int but was " + peek() + locationString());
+ " at line " + getLineNumber() + " column " + getColumnNumber() + " path " + getPath());
} }
peeked = PEEKED_BUFFERED; peeked = PEEKED_BUFFERED;
double asDouble = Double.parseDouble(peekedString); // don't catch this NumberFormatException. double asDouble = Double.parseDouble(peekedString); // don't catch this NumberFormatException.
result = (int) asDouble; result = (int) asDouble;
if (result != asDouble) { // Make sure no precision was lost casting to 'int'. if (result != asDouble) { // Make sure no precision was lost casting to 'int'.
throw new NumberFormatException("Expected an int but was " + peekedString throw new NumberFormatException("Expected an int but was " + peekedString + locationString());
+ " at line " + getLineNumber() + " column " + getColumnNumber() + " path " + getPath());
} }
peekedString = null; peekedString = null;
peeked = PEEKED_NONE; peeked = PEEKED_NONE;
@ -1315,14 +1302,6 @@ public class JsonReader implements Closeable {
return false; return false;
} }
private int getLineNumber() {
return lineNumber + 1;
}
private int getColumnNumber() {
return pos - lineStart + 1;
}
/** /**
* Returns the next character in the stream that is neither whitespace nor a * Returns the next character in the stream that is neither whitespace nor a
* part of a comment. When this returns, the returned character is always at * part of a comment. When this returns, the returned character is always at
@ -1412,8 +1391,7 @@ public class JsonReader implements Closeable {
} }
} }
if (throwOnEof) { if (throwOnEof) {
throw new EOFException("End of input" throw new EOFException("End of input" + locationString());
+ " at line " + getLineNumber() + " column " + getColumnNumber());
} else { } else {
return -1; return -1;
} }
@ -1465,8 +1443,13 @@ public class JsonReader implements Closeable {
} }
@Override public String toString() { @Override public String toString() {
return getClass().getSimpleName() return getClass().getSimpleName() + locationString();
+ " at line " + getLineNumber() + " column " + getColumnNumber(); }
private String locationString() {
int line = lineNumber + 1;
int column = pos - lineStart + 1;
return " at line " + line + " column " + column + " path " + getPath();
} }
/** /**
@ -1561,8 +1544,11 @@ public class JsonReader implements Closeable {
case '\'': case '\'':
case '"': case '"':
case '\\': case '\\':
default: case '/':
return escaped; return escaped;
default:
// throw error when none of the above cases are matched
throw syntaxError("Invalid escape sequence");
} }
} }
@ -1571,8 +1557,7 @@ public class JsonReader implements Closeable {
* with this reader's content. * with this reader's content.
*/ */
private IOException syntaxError(String message) throws IOException { private IOException syntaxError(String message) throws IOException {
throw new MalformedJsonException(message throw new MalformedJsonException(message + locationString());
+ " at line " + getLineNumber() + " column " + getColumnNumber() + " path " + getPath());
} }
/** /**
@ -1615,9 +1600,8 @@ public class JsonReader implements Closeable {
} else if (p == PEEKED_UNQUOTED_NAME) { } else if (p == PEEKED_UNQUOTED_NAME) {
reader.peeked = PEEKED_UNQUOTED; reader.peeked = PEEKED_UNQUOTED;
} else { } else {
throw new IllegalStateException("Expected a name but was " + reader.peek() + " " throw new IllegalStateException(
+ " at line " + reader.getLineNumber() + " column " + reader.getColumnNumber() "Expected a name but was " + reader.peek() + reader.locationString());
+ " path " + reader.getPath());
} }
} }
}; };

View File

@ -21,16 +21,10 @@ import java.io.Flushable;
import java.io.IOException; import java.io.IOException;
import java.io.Writer; import java.io.Writer;
import static com.massivecraft.massivecore.xlib.gson.stream.JsonScope.DANGLING_NAME; import static com.massivecraft.massivecore.xlib.gson.stream.JsonScope.*;
import static com.massivecraft.massivecore.xlib.gson.stream.JsonScope.EMPTY_ARRAY;
import static com.massivecraft.massivecore.xlib.gson.stream.JsonScope.EMPTY_DOCUMENT;
import static com.massivecraft.massivecore.xlib.gson.stream.JsonScope.EMPTY_OBJECT;
import static com.massivecraft.massivecore.xlib.gson.stream.JsonScope.NONEMPTY_ARRAY;
import static com.massivecraft.massivecore.xlib.gson.stream.JsonScope.NONEMPTY_DOCUMENT;
import static com.massivecraft.massivecore.xlib.gson.stream.JsonScope.NONEMPTY_OBJECT;
/** /**
* Writes a JSON (<a href="http://www.ietf.org/rfc/rfc4627.txt">RFC 4627</a>) * Writes a JSON (<a href="http://www.ietf.org/rfc/rfc7159.txt">RFC 7159</a>)
* encoded value to a stream, one token at a time. The stream includes both * encoded value to a stream, one token at a time. The stream includes both
* literal values (strings, numbers, booleans and nulls) as well as the begin * literal values (strings, numbers, booleans and nulls) as well as the begin
* and end delimiters of objects and arrays. * and end delimiters of objects and arrays.
@ -77,7 +71,7 @@ import static com.massivecraft.massivecore.xlib.gson.stream.JsonScope.NONEMPTY_O
* This code encodes the above structure: <pre> {@code * This code encodes the above structure: <pre> {@code
* public void writeJsonStream(OutputStream out, List<Message> messages) throws IOException { * public void writeJsonStream(OutputStream out, List<Message> messages) throws IOException {
* JsonWriter writer = new JsonWriter(new OutputStreamWriter(out, "UTF-8")); * JsonWriter writer = new JsonWriter(new OutputStreamWriter(out, "UTF-8"));
* writer.setIndentSpaces(4); * writer.setIndent(" ");
* writeMessagesArray(writer, messages); * writeMessagesArray(writer, messages);
* writer.close(); * writer.close();
* } * }
@ -130,7 +124,7 @@ import static com.massivecraft.massivecore.xlib.gson.stream.JsonScope.NONEMPTY_O
public class JsonWriter implements Closeable, Flushable { public class JsonWriter implements Closeable, Flushable {
/* /*
* From RFC 4627, "All Unicode characters may be placed within the * From RFC 7159, "All Unicode characters may be placed within the
* quotation marks except for the characters that must be escaped: * quotation marks except for the characters that must be escaped:
* quotation mark, reverse solidus, and the control characters * quotation mark, reverse solidus, and the control characters
* (U+0000 through U+001F)." * (U+0000 through U+001F)."
@ -222,7 +216,7 @@ public class JsonWriter implements Closeable, Flushable {
/** /**
* Configure this writer to relax its syntax rules. By default, this writer * Configure this writer to relax its syntax rules. By default, this writer
* only emits well-formed JSON as specified by <a * only emits well-formed JSON as specified by <a
* href="http://www.ietf.org/rfc/rfc4627.txt">RFC 4627</a>. Setting the writer * href="http://www.ietf.org/rfc/rfc7159.txt">RFC 7159</a>. Setting the writer
* to lenient permits the following: * to lenient permits the following:
* <ul> * <ul>
* <li>Top-level values of any type. With strict writing, the top-level * <li>Top-level values of any type. With strict writing, the top-level
@ -322,7 +316,7 @@ public class JsonWriter implements Closeable, Flushable {
* bracket. * bracket.
*/ */
private JsonWriter open(int empty, String openBracket) throws IOException { private JsonWriter open(int empty, String openBracket) throws IOException {
beforeValue(true); beforeValue();
push(empty); push(empty);
out.write(openBracket); out.write(openBracket);
return this; return this;
@ -415,11 +409,28 @@ public class JsonWriter implements Closeable, Flushable {
return nullValue(); return nullValue();
} }
writeDeferredName(); writeDeferredName();
beforeValue(false); beforeValue();
string(value); string(value);
return this; return this;
} }
/**
* Writes {@code value} directly to the writer without quoting or
* escaping.
*
* @param value the literal string value, or null to encode a null literal.
* @return this writer.
*/
public JsonWriter jsonValue(String value) throws IOException {
if (value == null) {
return nullValue();
}
writeDeferredName();
beforeValue();
out.append(value);
return this;
}
/** /**
* Encodes {@code null}. * Encodes {@code null}.
* *
@ -434,7 +445,7 @@ public class JsonWriter implements Closeable, Flushable {
return this; // skip the name and the value return this; // skip the name and the value
} }
} }
beforeValue(false); beforeValue();
out.write("null"); out.write("null");
return this; return this;
} }
@ -446,7 +457,22 @@ public class JsonWriter implements Closeable, Flushable {
*/ */
public JsonWriter value(boolean value) throws IOException { public JsonWriter value(boolean value) throws IOException {
writeDeferredName(); writeDeferredName();
beforeValue(false); beforeValue();
out.write(value ? "true" : "false");
return this;
}
/**
* Encodes {@code value}.
*
* @return this writer.
*/
public JsonWriter value(Boolean value) throws IOException {
if (value == null) {
return nullValue();
}
writeDeferredName();
beforeValue();
out.write(value ? "true" : "false"); out.write(value ? "true" : "false");
return this; return this;
} }
@ -463,7 +489,7 @@ public class JsonWriter implements Closeable, Flushable {
throw new IllegalArgumentException("Numeric values must be finite, but was " + value); throw new IllegalArgumentException("Numeric values must be finite, but was " + value);
} }
writeDeferredName(); writeDeferredName();
beforeValue(false); beforeValue();
out.append(Double.toString(value)); out.append(Double.toString(value));
return this; return this;
} }
@ -475,7 +501,7 @@ public class JsonWriter implements Closeable, Flushable {
*/ */
public JsonWriter value(long value) throws IOException { public JsonWriter value(long value) throws IOException {
writeDeferredName(); writeDeferredName();
beforeValue(false); beforeValue();
out.write(Long.toString(value)); out.write(Long.toString(value));
return this; return this;
} }
@ -498,7 +524,7 @@ public class JsonWriter implements Closeable, Flushable {
&& (string.equals("-Infinity") || string.equals("Infinity") || string.equals("NaN"))) { && (string.equals("-Infinity") || string.equals("Infinity") || string.equals("NaN"))) {
throw new IllegalArgumentException("Numeric values must be finite, but was " + value); throw new IllegalArgumentException("Numeric values must be finite, but was " + value);
} }
beforeValue(false); beforeValue();
out.append(string); out.append(string);
return this; return this;
} }
@ -591,12 +617,9 @@ public class JsonWriter implements Closeable, Flushable {
* Inserts any necessary separators and whitespace before a literal value, * Inserts any necessary separators and whitespace before a literal value,
* inline array, or inline object. Also adjusts the stack to expect either a * inline array, or inline object. Also adjusts the stack to expect either a
* closing bracket or another element. * closing bracket or another element.
*
* @param root true if the value is a new array or object, the two values
* permitted as top-level elements.
*/ */
@SuppressWarnings("fallthrough") @SuppressWarnings("fallthrough")
private void beforeValue(boolean root) throws IOException { private void beforeValue() throws IOException {
switch (peek()) { switch (peek()) {
case NONEMPTY_DOCUMENT: case NONEMPTY_DOCUMENT:
if (!lenient) { if (!lenient) {
@ -605,10 +628,6 @@ public class JsonWriter implements Closeable, Flushable {
} }
// fall-through // fall-through
case EMPTY_DOCUMENT: // first in document case EMPTY_DOCUMENT: // first in document
if (!lenient && !root) {
throw new IllegalStateException(
"JSON must start with an array or an object.");
}
replaceTop(NONEMPTY_DOCUMENT); replaceTop(NONEMPTY_DOCUMENT);
break; break;