Gson 2.8.0
This commit is contained in:
parent
b7ddb71f04
commit
3e520e7e9f
@ -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);
|
||||||
}
|
}
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
@ -69,7 +69,7 @@ public final class FieldAttributes {
|
|||||||
* private List<String> red;
|
* private List<String> red;
|
||||||
* }
|
* }
|
||||||
*
|
*
|
||||||
* Type listParmeterizedType = new TypeToken<List<String>>() {}.getType();
|
* Type listParameterizedType = new TypeToken<List<String>>() {}.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
|
||||||
|
@ -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);
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
@ -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<List<String>>() {}.getType();
|
* Type listType = new TypeToken<List<String>>() {}.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>"versionNumber"</code> in
|
* field <code>versionNumber</code> will be output as <code>"versionNumber"</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 <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type) {
|
* public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type) {
|
||||||
* final TypeAdapter<T> delegate = gson.getDelegateAdapter(this, type);
|
* final TypeAdapter<T> delegate = gson.getDelegateAdapter(this, type);
|
||||||
* return new TypeAdapter<T>() {
|
* 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<Collection<Foo>>(){}.getType();
|
* Type typeOfSrc = new TypeToken<Collection<Foo>>(){}.getType();
|
||||||
@ -522,7 +573,7 @@ public final class Gson {
|
|||||||
*
|
*
|
||||||
* @param src the object for which JSON representation is to be created
|
* @param 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<Collection<Foo>>(){}.getType();
|
* Type typeOfSrc = new TypeToken<Collection<Foo>>(){}.getType();
|
||||||
@ -564,7 +615,7 @@ public final class Gson {
|
|||||||
*
|
*
|
||||||
* @param src the object for which JSON representation is to be created
|
* @param 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<Collection<Foo>>(){}.getType();
|
* Type typeOfSrc = new TypeToken<Collection<Foo>>(){}.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<Collection<Foo>>(){}.getType();
|
* Type typeOfT = new TypeToken<Collection<Foo>>(){}.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<Collection<Foo>>(){}.getType();
|
* Type typeOfT = new TypeToken<Collection<Foo>>(){}.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<Collection<Foo>>(){}.getType();
|
* Type typeOfT = new TypeToken<Collection<Foo>>(){}.getType();
|
||||||
|
@ -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 < > etc. Use this option to configure
|
* By default, Gson escapes HTML characters such as < > 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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
@ -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.
|
||||||
|
@ -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;
|
||||||
}
|
}
|
@ -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;
|
||||||
}
|
}
|
||||||
|
@ -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.
|
||||||
*
|
*
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
@ -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));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -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();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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());
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -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 {
|
||||||
* @SerializedName("name") private final String someField;
|
* @SerializedName("name") String a;
|
||||||
* private final String someOtherField;
|
* @SerializedName(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 {};
|
||||||
}
|
}
|
||||||
|
@ -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 {
|
||||||
|
@ -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 {
|
||||||
|
@ -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();
|
||||||
|
@ -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());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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;
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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;
|
||||||
|
}
|
||||||
}
|
}
|
@ -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;
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
@ -29,5 +29,5 @@ public interface ObjectConstructor<T> {
|
|||||||
/**
|
/**
|
||||||
* Returns a new instance.
|
* Returns a new instance.
|
||||||
*/
|
*/
|
||||||
T construct();
|
public T construct();
|
||||||
}
|
}
|
@ -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;
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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());
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
|
@ -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;
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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);
|
||||||
|
@ -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;
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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();
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -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);
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
@ -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 + "]";
|
||||||
|
@ -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();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -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));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -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());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -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;
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user