added gson2 and persis v2beta

This commit is contained in:
Olof Larsson 2011-12-07 18:31:56 +01:00
parent 0f85595703
commit ecaa3759dc
102 changed files with 13485 additions and 0 deletions

View File

@ -0,0 +1,38 @@
/*
* Copyright (C) 2008 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.massivecraft.core.lib.gson2;
/**
* Strategy for excluding anonymous and local classes.
*
* @author Joel Leitch
*/
final class AnonymousAndLocalClassExclusionStrategy implements ExclusionStrategy {
public boolean shouldSkipField(FieldAttributes f) {
return isAnonymousOrLocal(f.getDeclaredClass());
}
public boolean shouldSkipClass(Class<?> clazz) {
return isAnonymousOrLocal(clazz);
}
private boolean isAnonymousOrLocal(Class<?> clazz) {
return !Enum.class.isAssignableFrom(clazz)
&& (clazz.isAnonymousClass() || clazz.isLocalClass());
}
}

View File

@ -0,0 +1,43 @@
/*
* Copyright (C) 2010 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.massivecraft.core.lib.gson2;
/**
* Defines generic cache interface.
*
* @author Inderjeet Singh
* @author Joel Leitch
*/
interface Cache<K, V> {
/**
* Adds the new value object into the cache for the given key. If the key already
* exists, then this method will override the value for the key.
*
* @param key the key identifier for the {@code value} object
* @param value the value object to store in the cache
*/
void addElement(K key, V value);
/**
* Retrieve the cached value for the given {@code key}.
*
* @param key the key identifying the value
* @return the cached value for the given {@code key}
*/
V getElement(K key);
}

View File

@ -0,0 +1,75 @@
/*
* Copyright (C) 2008 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.massivecraft.core.lib.gson2;
import com.massivecraft.core.lib.gson2.internal.$Gson$Preconditions;
import java.lang.annotation.Annotation;
import java.lang.reflect.Type;
import java.util.Collection;
/**
* Converts the field name that uses camel-case define word separation into separate words that
* are separated by the provided {@code separatorString}.
*
* <p>The following is an example:</p>
* <pre>
* class IntWrapper {
* public int integerField = 0;
* }
*
* CamelCaseSeparatorNamingPolicy policy = new CamelCaseSeparatorNamingPolicy("_");
* String translatedFieldName =
* policy.translateName(IntWrapper.class.getField("integerField"));
*
* assert("integer_Field".equals(translatedFieldName));
* </pre>
*
* @author Joel Leitch
*/
final class CamelCaseSeparatorNamingPolicy extends RecursiveFieldNamingPolicy {
private final String separatorString;
/**
* Constructs a new CamelCaseSeparatorNamingPolicy object that will add the
* {@code separatorString} between each of the words separated by camel case.
*
* @param separatorString the string value to place between words
* @throws IllegalArgumentException thrown if the {@code separatorString} parameter
* is null or empty.
*/
public CamelCaseSeparatorNamingPolicy(String separatorString) {
$Gson$Preconditions.checkNotNull(separatorString);
$Gson$Preconditions.checkArgument(!"".equals(separatorString));
this.separatorString = separatorString;
}
@Override
protected String translateName(String target, Type fieldType,
Collection<Annotation> annnotations) {
StringBuilder translation = new StringBuilder();
for (int i = 0; i < target.length(); i++) {
char character = target.charAt(i);
if (Character.isUpperCase(character) && translation.length() != 0) {
translation.append(separatorString);
}
translation.append(character);
}
return translation.toString();
}
}

View File

@ -0,0 +1,46 @@
/*
* Copyright (C) 2008 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.massivecraft.core.lib.gson2;
import java.lang.annotation.Annotation;
import java.lang.reflect.Type;
import java.util.Collection;
/**
* Performs numerous field naming translations wrapped up as one object.
*
* @author Joel Leitch
*/
abstract class CompositionFieldNamingPolicy extends RecursiveFieldNamingPolicy {
private final RecursiveFieldNamingPolicy[] fieldPolicies;
public CompositionFieldNamingPolicy(RecursiveFieldNamingPolicy... fieldNamingPolicies) {
if (fieldNamingPolicies == null) {
throw new NullPointerException("naming policies can not be null.");
}
this.fieldPolicies = fieldNamingPolicies;
}
@Override
protected String translateName(String target, Type fieldType, Collection<Annotation> annotations) {
for (RecursiveFieldNamingPolicy policy : fieldPolicies) {
target = policy.translateName(target, fieldType, annotations);
}
return target;
}
}

View File

@ -0,0 +1,122 @@
/*
* Copyright (C) 2008 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.massivecraft.core.lib.gson2;
import java.lang.reflect.Type;
import java.sql.Timestamp;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
import java.util.TimeZone;
/**
* List of all the default type adapters ({@link JsonSerializer}s, {@link JsonDeserializer}s,
* and {@link InstanceCreator}s.
*
* @author Inderjeet Singh
* @author Joel Leitch
*/
final class DefaultTypeAdapters {
/**
* This type adapter supports three subclasses of date: Date, Timestamp, and
* java.sql.Date.
*/
static final class DefaultDateTypeAdapter implements JsonSerializer<Date>, JsonDeserializer<Date> {
private final DateFormat enUsFormat;
private final DateFormat localFormat;
private final DateFormat iso8601Format;
DefaultDateTypeAdapter() {
this(DateFormat.getDateTimeInstance(DateFormat.DEFAULT, DateFormat.DEFAULT, Locale.US),
DateFormat.getDateTimeInstance(DateFormat.DEFAULT, DateFormat.DEFAULT));
}
DefaultDateTypeAdapter(String datePattern) {
this(new SimpleDateFormat(datePattern, Locale.US), new SimpleDateFormat(datePattern));
}
DefaultDateTypeAdapter(int style) {
this(DateFormat.getDateInstance(style, Locale.US), DateFormat.getDateInstance(style));
}
public DefaultDateTypeAdapter(int dateStyle, int timeStyle) {
this(DateFormat.getDateTimeInstance(dateStyle, timeStyle, Locale.US),
DateFormat.getDateTimeInstance(dateStyle, timeStyle));
}
DefaultDateTypeAdapter(DateFormat enUsFormat, DateFormat localFormat) {
this.enUsFormat = enUsFormat;
this.localFormat = localFormat;
this.iso8601Format = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'", Locale.US);
this.iso8601Format.setTimeZone(TimeZone.getTimeZone("UTC"));
}
// These methods need to be synchronized since JDK DateFormat classes are not thread-safe
// See issue 162
public JsonElement serialize(Date src, Type typeOfSrc, JsonSerializationContext context) {
synchronized (localFormat) {
String dateFormatAsString = enUsFormat.format(src);
return new JsonPrimitive(dateFormatAsString);
}
}
public Date deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
throws JsonParseException {
if (!(json instanceof JsonPrimitive)) {
throw new JsonParseException("The date should be a string value");
}
Date date = deserializeToDate(json);
if (typeOfT == Date.class) {
return date;
} else if (typeOfT == Timestamp.class) {
return new Timestamp(date.getTime());
} else if (typeOfT == java.sql.Date.class) {
return new java.sql.Date(date.getTime());
} else {
throw new IllegalArgumentException(getClass() + " cannot deserialize to " + typeOfT);
}
}
private Date deserializeToDate(JsonElement json) {
synchronized (localFormat) {
try {
return localFormat.parse(json.getAsString());
} catch (ParseException ignored) {
}
try {
return enUsFormat.parse(json.getAsString());
} catch (ParseException ignored) {
}
try {
return iso8601Format.parse(json.getAsString());
} catch (ParseException e) {
throw new JsonSyntaxException(json.getAsString(), e);
}
}
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append(DefaultDateTypeAdapter.class.getSimpleName());
sb.append('(').append(localFormat.getClass().getSimpleName()).append(')');
return sb.toString();
}
}
}

View File

@ -0,0 +1,53 @@
/*
* Copyright (C) 2008 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.massivecraft.core.lib.gson2;
import com.massivecraft.core.lib.gson2.internal.$Gson$Preconditions;
import java.util.Collection;
/**
* A wrapper class used to collect numerous {@link ExclusionStrategy} objects
* and perform a short-circuited OR operation.
*
* @author Joel Leitch
*/
final class DisjunctionExclusionStrategy implements ExclusionStrategy {
private final Collection<ExclusionStrategy> strategies;
DisjunctionExclusionStrategy(Collection<ExclusionStrategy> strategies) {
this.strategies = $Gson$Preconditions.checkNotNull(strategies);
}
public boolean shouldSkipField(FieldAttributes f) {
for (ExclusionStrategy strategy : strategies) {
if (strategy.shouldSkipField(f)) {
return true;
}
}
return false;
}
public boolean shouldSkipClass(Class<?> clazz) {
for (ExclusionStrategy strategy : strategies) {
if (strategy.shouldSkipClass(clazz)) {
return true;
}
}
return false;
}
}

View File

@ -0,0 +1,109 @@
/*
* Copyright (C) 2008 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.massivecraft.core.lib.gson2;
/**
* A strategy (or policy) definition that is used to decide whether or not a field or top-level
* class should be serialized or deserialized as part of the JSON output/input. For serialization,
* if the {@link #shouldSkipClass(Class)} method returns false then that class or field type
* will not be part of the JSON output. For deserialization, if {@link #shouldSkipClass(Class)}
* returns false, then it will not be set as part of the Java object structure.
*
* <p>The following are a few examples that shows how you can use this exclusion mechanism.
*
* <p><strong>Exclude fields and objects based on a particular class type:</strong>
* <pre class="code">
* private static class SpecificClassExclusionStrategy implements ExclusionStrategy {
* private final Class&lt;?&gt; excludedThisClass;
*
* public SpecificClassExclusionStrategy(Class&lt;?&gt; excludedThisClass) {
* this.excludedThisClass = excludedThisClass;
* }
*
* public boolean shouldSkipClass(Class&lt;?&gt; clazz) {
* return excludedThisClass.equals(clazz);
* }
*
* public boolean shouldSkipField(FieldAttributes f) {
* return excludedThisClass.equals(f.getDeclaredClass());
* }
* }
* </pre>
*
* <p><strong>Excludes fields and objects based on a particular annotation:</strong>
* <pre class="code">
* public &#64interface FooAnnotation {
* // some implementation here
* }
*
* // Excludes any field (or class) that is tagged with an "&#64FooAnnotation"
* private static class FooAnnotationExclusionStrategy implements ExclusionStrategy {
* public boolean shouldSkipClass(Class&lt;?&gt; clazz) {
* return clazz.getAnnotation(FooAnnotation.class) != null;
* }
*
* public boolean shouldSkipField(FieldAttributes f) {
* return f.getAnnotation(FooAnnotation.class) != null;
* }
* }
* </pre>
*
* <p>Now if you want to configure {@code Gson} to use a user defined exclusion strategy, then
* the {@code GsonBuilder} is required. The following is an example of how you can use the
* {@code GsonBuilder} to configure Gson to use one of the above sample:
* <pre class="code">
* ExclusionStrategy excludeStrings = new UserDefinedExclusionStrategy(String.class);
* Gson gson = new GsonBuilder()
* .setExclusionStrategies(excludeStrings)
* .create();
* </pre>
*
* <p>For certain model classes, you may only want to serialize a field, but exclude it for
* deserialization. To do that, you can write an {@code ExclusionStrategy} as per normal;
* however, you would register it with the
* {@link GsonBuilder#addDeserializationExclusionStrategy(ExclusionStrategy)} method.
* For example:
* <pre class="code">
* ExclusionStrategy excludeStrings = new UserDefinedExclusionStrategy(String.class);
* Gson gson = new GsonBuilder()
* .addDeserializationExclusionStrategy(excludeStrings)
* .create();
* </pre>
*
* @author Inderjeet Singh
* @author Joel Leitch
*
* @see GsonBuilder#setExclusionStrategies(ExclusionStrategy...)
* @see GsonBuilder#addDeserializationExclusionStrategy(ExclusionStrategy)
* @see GsonBuilder#addSerializationExclusionStrategy(ExclusionStrategy)
*
* @since 1.4
*/
public interface ExclusionStrategy {
/**
* @param f the field object that is under test
* @return true if the field should be ignored; otherwise false
*/
public boolean shouldSkipField(FieldAttributes f);
/**
* @param clazz the class object that is under test
* @return true if the class should be ignored; otherwise false
*/
public boolean shouldSkipClass(Class<?> clazz);
}

View File

@ -0,0 +1,38 @@
/*
* Copyright (C) 2011 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.massivecraft.core.lib.gson2;
import com.massivecraft.core.lib.gson2.annotations.Expose;
/**
* Excludes fields that do not have the {@link Expose} annotation
*
* @author Joel Leitch
*/
final class ExposeAnnotationDeserializationExclusionStrategy implements ExclusionStrategy {
public boolean shouldSkipClass(Class<?> clazz) {
return false;
}
public boolean shouldSkipField(FieldAttributes f) {
Expose annotation = f.getAnnotation(Expose.class);
if (annotation == null) {
return true;
}
return !annotation.deserialize();
}
}

View File

@ -0,0 +1,38 @@
/*
* Copyright (C) 2011 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.massivecraft.core.lib.gson2;
import com.massivecraft.core.lib.gson2.annotations.Expose;
/**
* Excludes fields that do not have the {@link Expose} annotation
*
* @author Joel Leitch
*/
final class ExposeAnnotationSerializationExclusionStrategy implements ExclusionStrategy {
public boolean shouldSkipClass(Class<?> clazz) {
return false;
}
public boolean shouldSkipField(FieldAttributes f) {
Expose annotation = f.getAnnotation(Expose.class);
if (annotation == null) {
return true;
}
return !annotation.serialize();
}
}

View File

@ -0,0 +1,222 @@
/*
* Copyright (C) 2009 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.massivecraft.core.lib.gson2;
import com.massivecraft.core.lib.gson2.internal.$Gson$Preconditions;
import com.massivecraft.core.lib.gson2.internal.Pair;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Type;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
/**
* A data object that stores attributes of a field.
*
* <p>This class is immutable; therefore, it can be safely shared across threads.
*
* @author Inderjeet Singh
* @author Joel Leitch
*
* @since 1.4
*/
public final class FieldAttributes {
private static final String MAX_CACHE_PROPERTY_NAME =
"com.google.gson.annotation_cache_size_hint";
private static final Cache<Pair<Class<?>, String>, Collection<Annotation>> ANNOTATION_CACHE =
new LruCache<Pair<Class<?>,String>, Collection<Annotation>>(getMaxCacheSize());
private final Class<?> declaringClazz;
private final Field field;
private final Class<?> declaredType;
private final boolean isSynthetic;
private final int modifiers;
private final String name;
// Fields used for lazy initialization
private Type genericType;
private Collection<Annotation> annotations;
/**
* Constructs a Field Attributes object from the {@code f}.
*
* @param f the field to pull attributes from
*/
FieldAttributes(Class<?> declaringClazz, Field f) {
this.declaringClazz = $Gson$Preconditions.checkNotNull(declaringClazz);
this.name = f.getName();
this.declaredType = f.getType();
this.isSynthetic = f.isSynthetic();
this.modifiers = f.getModifiers();
this.field = f;
}
private static int getMaxCacheSize() {
final int defaultMaxCacheSize = 2000;
try {
String propertyValue = System.getProperty(
MAX_CACHE_PROPERTY_NAME, String.valueOf(defaultMaxCacheSize));
return Integer.parseInt(propertyValue);
} catch (NumberFormatException e) {
return defaultMaxCacheSize;
}
}
/**
* @return the declaring class that contains this field
*/
public Class<?> getDeclaringClass() {
return declaringClazz;
}
/**
* @return the name of the field
*/
public String getName() {
return name;
}
/**
* <p>For example, assume the following class definition:
* <pre class="code">
* public class Foo {
* private String bar;
* private List&lt;String&gt; red;
* }
*
* Type listParmeterizedType = new TypeToken&lt;List&lt;String&gt;&gt;() {}.getType();
* </pre>
*
* <p>This method would return {@code String.class} for the {@code bar} field and
* {@code listParameterizedType} for the {@code red} field.
*
* @return the specific type declared for this field
*/
public Type getDeclaredType() {
if (genericType == null) {
genericType = field.getGenericType();
}
return genericType;
}
/**
* Returns the {@code Class} object that was declared for this field.
*
* <p>For example, assume the following class definition:
* <pre class="code">
* public class Foo {
* private String bar;
* private List&lt;String&gt; red;
* }
* </pre>
*
* <p>This method would return {@code String.class} for the {@code bar} field and
* {@code List.class} for the {@code red} field.
*
* @return the specific class object that was declared for the field
*/
public Class<?> getDeclaredClass() {
return declaredType;
}
/**
* Return the {@code T} annotation object from this field if it exist; otherwise returns
* {@code null}.
*
* @param annotation the class of the annotation that will be retrieved
* @return the annotation instance if it is bound to the field; otherwise {@code null}
*/
public <T extends Annotation> T getAnnotation(Class<T> annotation) {
return getAnnotationFromArray(getAnnotations(), annotation);
}
/**
* Return the annotations that are present on this field.
*
* @return an array of all the annotations set on the field
* @since 1.4
*/
public Collection<Annotation> getAnnotations() {
if (annotations == null) {
Pair<Class<?>, String> key = new Pair<Class<?>, String>(declaringClazz, name);
Collection<Annotation> cachedValue = ANNOTATION_CACHE.getElement(key);
if (cachedValue == null) {
cachedValue = Collections.unmodifiableCollection(
Arrays.asList(field.getAnnotations()));
ANNOTATION_CACHE.addElement(key, cachedValue);
}
annotations = cachedValue;
}
return annotations;
}
/**
* Returns {@code true} if the field is defined with the {@code modifier}.
*
* <p>This method is meant to be called as:
* <pre class="code">
* boolean hasPublicModifier = fieldAttribute.hasModifier(java.lang.reflect.Modifier.PUBLIC);
* </pre>
*
* @see java.lang.reflect.Modifier
*/
public boolean hasModifier(int modifier) {
return (modifiers & modifier) != 0;
}
/**
* This is exposed internally only for the removing synthetic fields from the JSON output.
*
* @return true if the field is synthetic; otherwise false
* @throws IllegalAccessException
* @throws IllegalArgumentException
*/
Object get(Object instance) throws IllegalAccessException {
return field.get(instance);
}
/**
* This is exposed internally only for the removing synthetic fields from the JSON output.
*
* @return true if the field is synthetic; otherwise false
*/
boolean isSynthetic() {
return isSynthetic;
}
/**
* @deprecated remove this when {@link FieldNamingStrategy} is deleted.
*/
@Deprecated
Field getFieldObject() {
return field;
}
@SuppressWarnings("unchecked")
private static <T extends Annotation> T getAnnotationFromArray(
Collection<Annotation> annotations, Class<T> annotation) {
for (Annotation a : annotations) {
if (a.annotationType() == annotation) {
return (T) a;
}
}
return null;
}
}

View File

@ -0,0 +1,99 @@
/*
* Copyright (C) 2008 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.massivecraft.core.lib.gson2;
/**
* An enumeration that defines a few standard naming conventions for JSON field names.
* This enumeration should be used in conjunction with {@link com.massivecraft.core.lib.gson2.GsonBuilder}
* to configure a {@link com.massivecraft.core.lib.gson2.Gson} instance to properly translate Java field
* names into the desired JSON field names.
*
* @author Inderjeet Singh
* @author Joel Leitch
*/
public enum FieldNamingPolicy {
/**
* Using this naming policy with Gson will ensure that the first "letter" of the Java
* field name is capitalized when serialized to its JSON form.
*
* <p>Here's a few examples of the form "Java Field Name" ---> "JSON Field Name":</p>
* <ul>
* <li>someFieldName ---> SomeFieldName</li>
* <li>_someFieldName ---> _SomeFieldName</li>
* </ul>
*/
UPPER_CAMEL_CASE(new ModifyFirstLetterNamingPolicy(
ModifyFirstLetterNamingPolicy.LetterModifier.UPPER)),
/**
* Using this naming policy with Gson will ensure that the first "letter" of the Java
* field name is capitalized when serialized to its JSON form and the words will be
* separated by a space.
*
* <p>Here's a few examples of the form "Java Field Name" ---> "JSON Field Name":</p>
* <ul>
* <li>someFieldName ---> Some Field Name</li>
* <li>_someFieldName ---> _Some Field Name</li>
* </ul>
*
* @since 1.4
*/
UPPER_CAMEL_CASE_WITH_SPACES(new UpperCamelCaseSeparatorNamingPolicy(" ")),
/**
* Using this naming policy with Gson will modify the Java Field name from its camel cased
* form to a lower case field name where each word is separated by an underscore (_).
*
* <p>Here's a few examples of the form "Java Field Name" ---> "JSON Field Name":</p>
* <ul>
* <li>someFieldName ---> some_field_name</li>
* <li>_someFieldName ---> _some_field_name</li>
* <li>aStringField ---> a_string_field</li>
* <li>aURL ---> a_u_r_l</li>
* </ul>
*/
LOWER_CASE_WITH_UNDERSCORES(new LowerCamelCaseSeparatorNamingPolicy("_")),
/**
* Using this naming policy with Gson will modify the Java Field name from its camel cased
* form to a lower case field name where each word is separated by a dash (-).
*
* <p>Here's a few examples of the form "Java Field Name" ---> "JSON Field Name":</p>
* <ul>
* <li>someFieldName ---> some-field-name</li>
* <li>_someFieldName ---> _some-field-name</li>
* <li>aStringField ---> a-string-field</li>
* <li>aURL ---> a-u-r-l</li>
* </ul>
* Using dashes in JavaScript is not recommended since dash is also used for a minus sign in
* expressions. This requires that a field named with dashes is always accessed as a quoted
* property like {@code myobject['my-field']}. Accessing it as an object field
* {@code myobject.my-field} will result in an unintended javascript expression.
* @since 1.4
*/
LOWER_CASE_WITH_DASHES(new LowerCamelCaseSeparatorNamingPolicy("-"));
private final FieldNamingStrategy2 namingPolicy;
private FieldNamingPolicy(FieldNamingStrategy2 namingPolicy) {
this.namingPolicy = namingPolicy;
}
FieldNamingStrategy2 getFieldNamingPolicy() {
return namingPolicy;
}
}

View File

@ -0,0 +1,40 @@
/*
* Copyright (C) 2008 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.massivecraft.core.lib.gson2;
import java.lang.reflect.Field;
/**
* A mechanism for providing custom field naming in Gson. This allows the client code to translate
* field names into a particular convention that is not supported as a normal Java field
* declaration rules. For example, Java does not support "-" characters in a field name.
*
* @author Inderjeet Singh
* @author Joel Leitch
* @since 1.3
*/
public interface FieldNamingStrategy {
/**
* Translates the field name into its JSON field name representation.
*
* @param f the field object that we are translating
* @return the translated field name.
* @since 1.3
*/
public String translateName(Field f);
}

View File

@ -0,0 +1,37 @@
/*
* Copyright (C) 2010 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.massivecraft.core.lib.gson2;
/**
* The new mechanism for providing custom field naming in Gson. This allows the client code
* to translate field names into a particular convention that is not supported as a normal
* Java field declaration rules. For example, Java does not support "-" characters in a
* field name.
*
* @author Inderjeet Singh
* @author Joel Leitch
*/
interface FieldNamingStrategy2 {
/**
* Translates the field name into its JSON field name representation.
*
* @param f the field that is being translated
* @return the translated field name.
*/
public String translateName(FieldAttributes f);
}

View File

@ -0,0 +1,39 @@
/*
* Copyright (C) 2010 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.massivecraft.core.lib.gson2;
import com.massivecraft.core.lib.gson2.internal.$Gson$Preconditions;
/**
* Adapts the old FieldNamingStrategy to the new {@link FieldNamingStrategy2}
* type.
*
* @author Inderjeet Singh
* @author Joel Leitch
*/
final class FieldNamingStrategy2Adapter implements FieldNamingStrategy2 {
private final FieldNamingStrategy adaptee;
FieldNamingStrategy2Adapter(FieldNamingStrategy adaptee) {
this.adaptee = $Gson$Preconditions.checkNotNull(adaptee);
}
@SuppressWarnings("deprecation")
public String translateName(FieldAttributes f) {
return adaptee.translateName(f.getFieldObject());
}
}

View File

@ -0,0 +1,802 @@
/*
* Copyright (C) 2008 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.massivecraft.core.lib.gson2;
import com.massivecraft.core.lib.gson2.internal.ConstructorConstructor;
import com.massivecraft.core.lib.gson2.internal.ParameterizedTypeHandlerMap;
import com.massivecraft.core.lib.gson2.internal.Primitives;
import com.massivecraft.core.lib.gson2.internal.Streams;
import com.massivecraft.core.lib.gson2.internal.bind.ArrayTypeAdapter;
import com.massivecraft.core.lib.gson2.internal.bind.BigDecimalTypeAdapter;
import com.massivecraft.core.lib.gson2.internal.bind.BigIntegerTypeAdapter;
import com.massivecraft.core.lib.gson2.internal.bind.CollectionTypeAdapterFactory;
import com.massivecraft.core.lib.gson2.internal.bind.DateTypeAdapter;
import com.massivecraft.core.lib.gson2.internal.bind.ExcludedTypeAdapterFactory;
import com.massivecraft.core.lib.gson2.internal.bind.JsonElementReader;
import com.massivecraft.core.lib.gson2.internal.bind.JsonElementWriter;
import com.massivecraft.core.lib.gson2.internal.bind.MapTypeAdapterFactory;
import com.massivecraft.core.lib.gson2.internal.bind.MiniGson;
import com.massivecraft.core.lib.gson2.internal.bind.ObjectTypeAdapter;
import com.massivecraft.core.lib.gson2.internal.bind.ReflectiveTypeAdapterFactory;
import com.massivecraft.core.lib.gson2.internal.bind.SqlDateTypeAdapter;
import com.massivecraft.core.lib.gson2.internal.bind.TimeTypeAdapter;
import com.massivecraft.core.lib.gson2.internal.bind.TypeAdapter;
import com.massivecraft.core.lib.gson2.internal.bind.TypeAdapters;
import com.massivecraft.core.lib.gson2.reflect.TypeToken;
import com.massivecraft.core.lib.gson2.stream.JsonReader;
import com.massivecraft.core.lib.gson2.stream.JsonToken;
import com.massivecraft.core.lib.gson2.stream.JsonWriter;
import com.massivecraft.core.lib.gson2.stream.MalformedJsonException;
import java.io.EOFException;
import java.io.IOException;
import java.io.Reader;
import java.io.StringReader;
import java.io.StringWriter;
import java.io.Writer;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.lang.reflect.Type;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
/**
* This is the main class for using Gson. Gson is typically used by first constructing a
* Gson instance and then invoking {@link #toJson(Object)} or {@link #fromJson(String, Class)}
* methods on it.
*
* <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
* configuration options such as versioning support, pretty printing, custom
* {@link JsonSerializer}s, {@link JsonDeserializer}s, and {@link InstanceCreator}s.</p>
*
* <p>Here is an example of how Gson is used for a simple Class:
*
* <pre>
* Gson gson = new Gson(); // Or use new GsonBuilder().create();
* MyType target = new MyType();
* String json = gson.toJson(target); // serializes target to Json
* MyType target2 = gson.fromJson(json, MyType.class); // deserializes json into target2
* </pre></p>
*
* <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
* {@link #toJson(Object, Type)} or {@link #fromJson(String, Type)} method. Here is an
* example for serializing and deserialing a {@code ParameterizedType}:
*
* <pre>
* Type listType = new TypeToken&lt;List&lt;String&gt;&gt;() {}.getType();
* List&lt;String&gt; target = new LinkedList&lt;String&gt;();
* target.add("blah");
*
* Gson gson = new Gson();
* String json = gson.toJson(target, listType);
* List&lt;String&gt; target2 = gson.fromJson(json, listType);
* </pre></p>
*
* <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>
*
* @see com.massivecraft.core.lib.gson2.reflect.TypeToken
*
* @author Inderjeet Singh
* @author Joel Leitch
*/
public final class Gson {
@SuppressWarnings("rawtypes")
static final ParameterizedTypeHandlerMap EMPTY_MAP =
new ParameterizedTypeHandlerMap().makeUnmodifiable();
static final boolean DEFAULT_JSON_NON_EXECUTABLE = false;
// Default instances of plug-ins
static final AnonymousAndLocalClassExclusionStrategy DEFAULT_ANON_LOCAL_CLASS_EXCLUSION_STRATEGY =
new AnonymousAndLocalClassExclusionStrategy();
static final SyntheticFieldExclusionStrategy DEFAULT_SYNTHETIC_FIELD_EXCLUSION_STRATEGY =
new SyntheticFieldExclusionStrategy(true);
static final ModifierBasedExclusionStrategy DEFAULT_MODIFIER_BASED_EXCLUSION_STRATEGY =
new ModifierBasedExclusionStrategy(Modifier.TRANSIENT, Modifier.STATIC);
static final FieldNamingStrategy2 DEFAULT_NAMING_POLICY =
new SerializedNameAnnotationInterceptingNamingPolicy(new JavaFieldNamingPolicy());
private static final ExclusionStrategy DEFAULT_EXCLUSION_STRATEGY = createExclusionStrategy();
private static final String JSON_NON_EXECUTABLE_PREFIX = ")]}'\n";
private final ExclusionStrategy deserializationExclusionStrategy;
private final ExclusionStrategy serializationExclusionStrategy;
private final ConstructorConstructor constructorConstructor;
/** Map containing Type or Class objects as keys */
private final ParameterizedTypeHandlerMap<JsonSerializer<?>> serializers;
/** Map containing Type or Class objects as keys */
private final ParameterizedTypeHandlerMap<JsonDeserializer<?>> deserializers;
private final boolean serializeNulls;
private final boolean htmlSafe;
private final boolean generateNonExecutableJson;
private final boolean prettyPrinting;
private final MiniGson miniGson;
/**
* Constructs a Gson object with default configuration. The default configuration has the
* following settings:
* <ul>
* <li>The JSON generated by <code>toJson</code> methods is in compact representation. This
* means that all the unneeded white-space is removed. You can change this behavior with
* {@link GsonBuilder#setPrettyPrinting()}. </li>
* <li>The generated JSON omits all the fields that are null. Note that nulls in arrays are
* kept as is since an array is an ordered list. Moreover, if a field is not null, but its
* generated JSON is empty, the field is kept. You can configure Gson to serialize null values
* by setting {@link GsonBuilder#serializeNulls()}.</li>
* <li>Gson provides default serialization and deserialization for Enums, {@link Map},
* {@link java.net.URL}, {@link java.net.URI}, {@link java.util.Locale}, {@link java.util.Date},
* {@link java.math.BigDecimal}, and {@link java.math.BigInteger} classes. If you would prefer
* to change the default representation, you can do so by registering a type adapter through
* {@link GsonBuilder#registerTypeAdapter(Type, Object)}. </li>
* <li>The default Date format is same as {@link java.text.DateFormat#DEFAULT}. This format
* ignores the millisecond portion of the date during serialization. You can change
* this by invoking {@link GsonBuilder#setDateFormat(int)} or
* {@link GsonBuilder#setDateFormat(String)}. </li>
* <li>By default, Gson ignores the {@link com.massivecraft.core.lib.gson2.annotations.Expose} annotation.
* You can enable Gson to serialize/deserialize only those fields marked with this annotation
* through {@link GsonBuilder#excludeFieldsWithoutExposeAnnotation()}. </li>
* <li>By default, Gson ignores the {@link com.massivecraft.core.lib.gson2.annotations.Since} annotation. You
* 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
* field <code>versionNumber</code> will be output as <code>&quot;versionNumber@quot;</code> in
* Json. The same rules are applied for mapping incoming Json to the Java classes. You can
* change this policy through {@link GsonBuilder#setFieldNamingPolicy(FieldNamingPolicy)}.</li>
* <li>By default, Gson excludes <code>transient</code> or <code>static</code> fields from
* consideration for serialization and deserialization. You can change this behavior through
* {@link GsonBuilder#excludeFieldsWithModifiers(int...)}.</li>
* </ul>
*/
@SuppressWarnings("unchecked")
public Gson() {
this(DEFAULT_EXCLUSION_STRATEGY, DEFAULT_EXCLUSION_STRATEGY, DEFAULT_NAMING_POLICY,
EMPTY_MAP, false, EMPTY_MAP, EMPTY_MAP, false, DEFAULT_JSON_NON_EXECUTABLE, true,
false, false, LongSerializationPolicy.DEFAULT,
Collections.<TypeAdapter.Factory>emptyList());
}
Gson(final ExclusionStrategy deserializationExclusionStrategy,
final ExclusionStrategy serializationExclusionStrategy,
final FieldNamingStrategy2 fieldNamingPolicy,
final ParameterizedTypeHandlerMap<InstanceCreator<?>> instanceCreators, boolean serializeNulls,
final ParameterizedTypeHandlerMap<JsonSerializer<?>> serializers,
final ParameterizedTypeHandlerMap<JsonDeserializer<?>> deserializers,
boolean complexMapKeySerialization, boolean generateNonExecutableGson, boolean htmlSafe,
boolean prettyPrinting, boolean serializeSpecialFloatingPointValues,
LongSerializationPolicy longSerializationPolicy,
List<TypeAdapter.Factory> typeAdapterFactories) {
this.deserializationExclusionStrategy = deserializationExclusionStrategy;
this.serializationExclusionStrategy = serializationExclusionStrategy;
this.constructorConstructor = new ConstructorConstructor(instanceCreators);
this.serializeNulls = serializeNulls;
this.serializers = serializers;
this.deserializers = deserializers;
this.generateNonExecutableJson = generateNonExecutableGson;
this.htmlSafe = htmlSafe;
this.prettyPrinting = prettyPrinting;
/*
TODO: for serialization, honor:
serializationExclusionStrategy
fieldNamingPolicy
serializeNulls
serializers
*/
TypeAdapter.Factory reflectiveTypeAdapterFactory
= new ReflectiveTypeAdapterFactory(constructorConstructor) {
@Override
public String getFieldName(Class<?> declaringClazz, Field f, Type declaredType) {
return fieldNamingPolicy.translateName(new FieldAttributes(declaringClazz, f));
}
@Override
public boolean serializeField(Class<?> declaringClazz, Field f, Type declaredType) {
ExclusionStrategy strategy = Gson.this.serializationExclusionStrategy;
return !strategy.shouldSkipClass(f.getType())
&& !strategy.shouldSkipField(new FieldAttributes(declaringClazz, f));
}
@Override
public boolean deserializeField(Class<?> declaringClazz, Field f, Type declaredType) {
ExclusionStrategy strategy = Gson.this.deserializationExclusionStrategy;
return !strategy.shouldSkipClass(f.getType())
&& !strategy.shouldSkipField(new FieldAttributes(declaringClazz, f));
}
};
MiniGson.Builder builder = new MiniGson.Builder()
.withoutDefaultFactories()
.factory(TypeAdapters.STRING_FACTORY)
.factory(TypeAdapters.INTEGER_FACTORY)
.factory(TypeAdapters.BOOLEAN_FACTORY)
.factory(TypeAdapters.BYTE_FACTORY)
.factory(TypeAdapters.SHORT_FACTORY)
.factory(TypeAdapters.newFactory(long.class, Long.class,
longAdapter(longSerializationPolicy)))
.factory(TypeAdapters.newFactory(double.class, Double.class,
doubleAdapter(serializeSpecialFloatingPointValues)))
.factory(TypeAdapters.newFactory(float.class, Float.class,
floatAdapter(serializeSpecialFloatingPointValues)))
.factory(new ExcludedTypeAdapterFactory(
serializationExclusionStrategy, deserializationExclusionStrategy))
.factory(TypeAdapters.NUMBER_FACTORY)
.factory(TypeAdapters.CHARACTER_FACTORY)
.factory(TypeAdapters.STRING_BUILDER_FACTORY)
.factory(TypeAdapters.STRING_BUFFER_FACTORY)
.typeAdapter(BigDecimal.class, new BigDecimalTypeAdapter())
.typeAdapter(BigInteger.class, new BigIntegerTypeAdapter())
.factory(TypeAdapters.JSON_ELEMENT_FACTORY)
.factory(ObjectTypeAdapter.FACTORY);
for (TypeAdapter.Factory factory : typeAdapterFactories) {
builder.factory(factory);
}
builder
.factory(new GsonToMiniGsonTypeAdapterFactory(this, serializers, deserializers))
.factory(new CollectionTypeAdapterFactory(constructorConstructor))
.factory(TypeAdapters.URL_FACTORY)
.factory(TypeAdapters.URI_FACTORY)
.factory(TypeAdapters.UUID_FACTORY)
.factory(TypeAdapters.LOCALE_FACTORY)
.factory(TypeAdapters.INET_ADDRESS_FACTORY)
.factory(TypeAdapters.BIT_SET_FACTORY)
.factory(DateTypeAdapter.FACTORY)
.factory(TypeAdapters.CALENDAR_FACTORY)
.factory(TimeTypeAdapter.FACTORY)
.factory(SqlDateTypeAdapter.FACTORY)
.factory(TypeAdapters.TIMESTAMP_FACTORY)
.factory(new MapTypeAdapterFactory(constructorConstructor, complexMapKeySerialization))
.factory(ArrayTypeAdapter.FACTORY)
.factory(TypeAdapters.ENUM_FACTORY)
.factory(reflectiveTypeAdapterFactory);
this.miniGson = builder.build();
}
private TypeAdapter<Number> doubleAdapter(boolean serializeSpecialFloatingPointValues) {
if (serializeSpecialFloatingPointValues) {
return TypeAdapters.DOUBLE;
}
return new TypeAdapter<Number>() {
@Override public Double read(JsonReader reader) throws IOException {
if (reader.peek() == JsonToken.NULL) {
reader.nextNull();
return null;
}
return reader.nextDouble();
}
@Override public void write(JsonWriter writer, Number value) throws IOException {
if (value == null) {
writer.nullValue(); // TODO: better policy here?
return;
}
double doubleValue = value.doubleValue();
checkValidFloatingPoint(doubleValue);
writer.value(value);
}
};
}
private TypeAdapter<Number> floatAdapter(boolean serializeSpecialFloatingPointValues) {
if (serializeSpecialFloatingPointValues) {
return TypeAdapters.FLOAT;
}
return new TypeAdapter<Number>() {
@Override public Float read(JsonReader reader) throws IOException {
if (reader.peek() == JsonToken.NULL) {
reader.nextNull();
return null;
}
return (float) reader.nextDouble();
}
@Override public void write(JsonWriter writer, Number value) throws IOException {
if (value == null) {
writer.nullValue(); // TODO: better policy here?
return;
}
float floatValue = value.floatValue();
checkValidFloatingPoint(floatValue);
writer.value(value);
}
};
}
private void checkValidFloatingPoint(double value) {
if (Double.isNaN(value) || Double.isInfinite(value)) {
throw new IllegalArgumentException(value
+ " is not a valid double value as per JSON specification. To override this"
+ " behavior, use GsonBuilder.serializeSpecialDoubleValues() method.");
}
}
private TypeAdapter<Number> longAdapter(LongSerializationPolicy longSerializationPolicy) {
if (longSerializationPolicy == LongSerializationPolicy.DEFAULT) {
return TypeAdapters.LONG;
}
return new TypeAdapter<Number>() {
@Override public Number read(JsonReader reader) throws IOException {
if (reader.peek() == JsonToken.NULL) {
reader.nextNull();
return null;
}
return reader.nextLong();
}
@Override public void write(JsonWriter writer, Number value) throws IOException {
if (value == null) {
writer.nullValue(); // TODO: better policy here?
return;
}
writer.value(value.toString());
}
};
}
private static ExclusionStrategy createExclusionStrategy() {
List<ExclusionStrategy> strategies = new LinkedList<ExclusionStrategy>();
strategies.add(DEFAULT_ANON_LOCAL_CLASS_EXCLUSION_STRATEGY);
strategies.add(DEFAULT_SYNTHETIC_FIELD_EXCLUSION_STRATEGY);
strategies.add(DEFAULT_MODIFIER_BASED_EXCLUSION_STRATEGY);
return new DisjunctionExclusionStrategy(strategies);
}
/**
* This method serializes the specified object into its equivalent representation as a tree of
* {@link JsonElement}s. This method should be used when the specified object is not a generic
* type. This method uses {@link Class#getClass()} to get the type for the specified object, but
* the {@code getClass()} loses the generic type information because of the Type Erasure feature
* of Java. Note that this method works fine if the any of the object fields are of generic type,
* just the object itself should not be of a generic type. If the object is of generic type, use
* {@link #toJsonTree(Object, Type)} instead.
*
* @param src the object for which Json representation is to be created setting for Gson
* @return Json representation of {@code src}.
* @since 1.4
*/
public JsonElement toJsonTree(Object src) {
if (src == null) {
return JsonNull.INSTANCE;
}
return toJsonTree(src, src.getClass());
}
/**
* This method serializes the specified object, including those of generic types, into its
* equivalent representation as a tree of {@link JsonElement}s. This method must be used if the
* specified object is a generic type. For non-generic objects, use {@link #toJsonTree(Object)}
* instead.
*
* @param src the object for which JSON representation is to be created
* @param typeOfSrc The specific genericized type of src. You can obtain
* this type by using the {@link com.massivecraft.core.lib.gson2.reflect.TypeToken} class. For example,
* to get the type for {@code Collection<Foo>}, you should use:
* <pre>
* Type typeOfSrc = new TypeToken&lt;Collection&lt;Foo&gt;&gt;(){}.getType();
* </pre>
* @return Json representation of {@code src}
* @since 1.4
*/
@SuppressWarnings({"unchecked", "rawtypes"}) // the caller is required to make src and typeOfSrc consistent
public JsonElement toJsonTree(Object src, Type typeOfSrc) {
JsonElementWriter writer = new JsonElementWriter();
toJson(src, typeOfSrc, writer);
return writer.get();
}
/**
* This method serializes the specified object into its equivalent Json representation.
* This method should be used when the specified object is not a generic type. This method uses
* {@link Class#getClass()} to get the type for the specified object, but the
* {@code getClass()} loses the generic type information because of the Type Erasure feature
* of Java. Note that this method works fine if the any of the object fields are of generic type,
* just the object itself should not be of a generic type. If the object is of generic type, use
* {@link #toJson(Object, Type)} instead. If you want to write out the object to a
* {@link Writer}, use {@link #toJson(Object, Appendable)} instead.
*
* @param src the object for which Json representation is to be created setting for Gson
* @return Json representation of {@code src}.
*/
public String toJson(Object src) {
if (src == null) {
return toJson(JsonNull.INSTANCE);
}
return toJson(src, src.getClass());
}
/**
* This method serializes the specified object, including those of generic types, into its
* equivalent Json representation. This method must be used if the specified object is a generic
* type. For non-generic objects, use {@link #toJson(Object)} instead. If you want to write out
* the object to a {@link Appendable}, use {@link #toJson(Object, Type, Appendable)} instead.
*
* @param src the object for which JSON representation is to be created
* @param typeOfSrc The specific genericized type of src. You can obtain
* this type by using the {@link com.massivecraft.core.lib.gson2.reflect.TypeToken} class. For example,
* to get the type for {@code Collection<Foo>}, you should use:
* <pre>
* Type typeOfSrc = new TypeToken&lt;Collection&lt;Foo&gt;&gt;(){}.getType();
* </pre>
* @return Json representation of {@code src}
*/
public String toJson(Object src, Type typeOfSrc) {
StringWriter writer = new StringWriter();
toJson(src, typeOfSrc, writer);
return writer.toString();
}
/**
* This method serializes the specified object into its equivalent Json representation.
* This method should be used when the specified object is not a generic type. This method uses
* {@link Class#getClass()} to get the type for the specified object, but the
* {@code getClass()} loses the generic type information because of the Type Erasure feature
* of Java. Note that this method works fine if the any of the object fields are of generic type,
* just the object itself should not be of a generic type. If the object is of generic type, use
* {@link #toJson(Object, Type, Appendable)} instead.
*
* @param src the object for which Json representation is to be created setting for Gson
* @param writer Writer to which the Json representation needs to be written
* @throws JsonIOException if there was a problem writing to the writer
* @since 1.2
*/
public void toJson(Object src, Appendable writer) throws JsonIOException {
if (src != null) {
toJson(src, src.getClass(), writer);
} else {
toJson(JsonNull.INSTANCE, writer);
}
}
/**
* This method serializes the specified object, including those of generic types, into its
* equivalent Json representation. This method must be used if the specified object is a generic
* type. For non-generic objects, use {@link #toJson(Object, Appendable)} instead.
*
* @param src the object for which JSON representation is to be created
* @param typeOfSrc The specific genericized type of src. You can obtain
* this type by using the {@link com.massivecraft.core.lib.gson2.reflect.TypeToken} class. For example,
* to get the type for {@code Collection<Foo>}, you should use:
* <pre>
* Type typeOfSrc = new TypeToken&lt;Collection&lt;Foo&gt;&gt;(){}.getType();
* </pre>
* @param writer Writer to which the Json representation of src needs to be written.
* @throws JsonIOException if there was a problem writing to the writer
* @since 1.2
*/
public void toJson(Object src, Type typeOfSrc, Appendable writer) throws JsonIOException {
try {
JsonWriter jsonWriter = newJsonWriter(Streams.writerForAppendable(writer));
toJson(src, typeOfSrc, jsonWriter);
} catch (IOException e) {
throw new JsonIOException(e);
}
}
/**
* Writes the JSON representation of {@code src} of type {@code typeOfSrc} to
* {@code writer}.
* @throws JsonIOException if there was a problem writing to the writer
*/
@SuppressWarnings("unchecked")
public void toJson(Object src, Type typeOfSrc, JsonWriter writer) throws JsonIOException {
TypeAdapter<?> adapter = miniGson.getAdapter(TypeToken.get(typeOfSrc));
boolean oldLenient = writer.isLenient();
writer.setLenient(true);
boolean oldHtmlSafe = writer.isHtmlSafe();
writer.setHtmlSafe(htmlSafe);
boolean oldSerializeNulls = writer.getSerializeNulls();
writer.setSerializeNulls(serializeNulls);
try {
((TypeAdapter<Object>) adapter).write(writer, src);
} catch (IOException e) {
throw new JsonIOException(e);
} finally {
writer.setLenient(oldLenient);
writer.setHtmlSafe(oldHtmlSafe);
writer.setSerializeNulls(oldSerializeNulls);
}
}
/**
* Converts a tree of {@link JsonElement}s into its equivalent JSON representation.
*
* @param jsonElement root of a tree of {@link JsonElement}s
* @return JSON String representation of the tree
* @since 1.4
*/
public String toJson(JsonElement jsonElement) {
StringWriter writer = new StringWriter();
toJson(jsonElement, writer);
return writer.toString();
}
/**
* Writes out the equivalent JSON for a tree of {@link JsonElement}s.
*
* @param jsonElement root of a tree of {@link JsonElement}s
* @param writer Writer to which the Json representation needs to be written
* @throws JsonIOException if there was a problem writing to the writer
* @since 1.4
*/
public void toJson(JsonElement jsonElement, Appendable writer) throws JsonIOException {
try {
JsonWriter jsonWriter = newJsonWriter(Streams.writerForAppendable(writer));
toJson(jsonElement, jsonWriter);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
/**
* Returns a new JSON writer configured for this GSON and with the non-execute
* prefix if that is configured.
*/
private JsonWriter newJsonWriter(Writer writer) throws IOException {
if (generateNonExecutableJson) {
writer.write(JSON_NON_EXECUTABLE_PREFIX);
}
JsonWriter jsonWriter = new JsonWriter(writer);
if (prettyPrinting) {
jsonWriter.setIndent(" ");
}
jsonWriter.setSerializeNulls(serializeNulls);
return jsonWriter;
}
/**
* Writes the JSON for {@code jsonElement} to {@code writer}.
* @throws JsonIOException if there was a problem writing to the writer
*/
public void toJson(JsonElement jsonElement, JsonWriter writer) throws JsonIOException {
boolean oldLenient = writer.isLenient();
writer.setLenient(true);
boolean oldHtmlSafe = writer.isHtmlSafe();
writer.setHtmlSafe(htmlSafe);
boolean oldSerializeNulls = writer.getSerializeNulls();
writer.setSerializeNulls(serializeNulls);
try {
Streams.write(jsonElement, writer);
} catch (IOException e) {
throw new JsonIOException(e);
} finally {
writer.setLenient(oldLenient);
writer.setHtmlSafe(oldHtmlSafe);
writer.setSerializeNulls(oldSerializeNulls);
}
}
/**
* This method deserializes the specified Json into an object of the specified class. It is not
* suitable to use if the specified class is a generic type since it will not have the generic
* type information because of the Type Erasure feature of Java. Therefore, this method should not
* be used if the desired type is a generic type. Note that this method works fine if the any of
* the fields of the specified object are generics, just the object itself should not be a
* generic type. For the cases when the object is of generic type, invoke
* {@link #fromJson(String, Type)}. If you have the Json in a {@link Reader} instead of
* a String, use {@link #fromJson(Reader, Class)} instead.
*
* @param <T> the type of the desired object
* @param json the string from which the object is to be deserialized
* @param classOfT the class of T
* @return an object of type T from the string
* @throws JsonSyntaxException if json is not a valid representation for an object of type
* classOfT
*/
public <T> T fromJson(String json, Class<T> classOfT) throws JsonSyntaxException {
Object object = fromJson(json, (Type) classOfT);
return Primitives.wrap(classOfT).cast(object);
}
/**
* This method deserializes the specified Json into an object of the specified type. This method
* is useful if the specified object is a generic type. For non-generic objects, use
* {@link #fromJson(String, Class)} instead. If you have the Json in a {@link Reader} instead of
* a String, use {@link #fromJson(Reader, Type)} instead.
*
* @param <T> the type of the desired object
* @param json the string from which the object is to be deserialized
* @param typeOfT The specific genericized type of src. You can obtain this type by using the
* {@link com.massivecraft.core.lib.gson2.reflect.TypeToken} class. For example, to get the type for
* {@code Collection<Foo>}, you should use:
* <pre>
* Type typeOfT = new TypeToken&lt;Collection&lt;Foo&gt;&gt;(){}.getType();
* </pre>
* @return an object of type T from the string
* @throws JsonParseException if json is not a valid representation for an object of type typeOfT
* @throws JsonSyntaxException if json is not a valid representation for an object of type
*/
@SuppressWarnings("unchecked")
public <T> T fromJson(String json, Type typeOfT) throws JsonSyntaxException {
if (json == null) {
return null;
}
StringReader reader = new StringReader(json);
T target = (T) fromJson(reader, typeOfT);
return target;
}
/**
* This method deserializes the Json read from the specified reader into an object of the
* specified class. It is not suitable to use if the specified class is a generic type since it
* will not have the generic type information because of the Type Erasure feature of Java.
* Therefore, this method should not be used if the desired type is a generic type. Note that
* this method works fine if the any of the fields of the specified object are generics, just the
* object itself should not be a generic type. For the cases when the object is of generic type,
* invoke {@link #fromJson(Reader, Type)}. If you have the Json in a String form instead of a
* {@link Reader}, use {@link #fromJson(String, Class)} instead.
*
* @param <T> the type of the desired object
* @param json the reader producing the Json from which the object is to be deserialized.
* @param classOfT the class of T
* @return an object of type T from the string
* @throws JsonIOException if there was a problem reading from the Reader
* @throws JsonSyntaxException if json is not a valid representation for an object of type
* @since 1.2
*/
public <T> T fromJson(Reader json, Class<T> classOfT) throws JsonSyntaxException, JsonIOException {
JsonReader jsonReader = new JsonReader(json);
Object object = fromJson(jsonReader, classOfT);
assertFullConsumption(object, jsonReader);
return Primitives.wrap(classOfT).cast(object);
}
/**
* This method deserializes the Json read from the specified reader into an object of the
* specified type. This method is useful if the specified object is a generic type. For
* non-generic objects, use {@link #fromJson(Reader, Class)} instead. If you have the Json in a
* String form instead of a {@link Reader}, use {@link #fromJson(String, Type)} instead.
*
* @param <T> the type of the desired object
* @param json the reader producing Json from which the object is to be deserialized
* @param typeOfT The specific genericized type of src. You can obtain this type by using the
* {@link com.massivecraft.core.lib.gson2.reflect.TypeToken} class. For example, to get the type for
* {@code Collection<Foo>}, you should use:
* <pre>
* Type typeOfT = new TypeToken&lt;Collection&lt;Foo&gt;&gt;(){}.getType();
* </pre>
* @return an object of type T from the json
* @throws JsonIOException if there was a problem reading from the Reader
* @throws JsonSyntaxException if json is not a valid representation for an object of type
* @since 1.2
*/
public <T> T fromJson(Reader json, Type typeOfT) throws JsonIOException, JsonSyntaxException {
JsonReader jsonReader = new JsonReader(json);
T object = (T) fromJson(jsonReader, typeOfT);
assertFullConsumption(object, jsonReader);
return object;
}
private static void assertFullConsumption(Object obj, JsonReader reader) {
try {
if (obj != null && reader.peek() != JsonToken.END_DOCUMENT) {
throw new JsonIOException("JSON document was not fully consumed.");
}
} catch (MalformedJsonException e) {
throw new JsonSyntaxException(e);
} catch (IOException e) {
throw new JsonIOException(e);
}
}
/**
* Reads the next JSON value from {@code reader} and convert it to an object
* of type {@code typeOfT}.
* Since Type is not parameterized by T, this method is type unsafe and should be used carefully
*
* @throws JsonIOException if there was a problem writing to the Reader
* @throws JsonSyntaxException if json is not a valid representation for an object of type
*/
@SuppressWarnings("unchecked")
public <T> T fromJson(JsonReader reader, Type typeOfT) throws JsonIOException, JsonSyntaxException {
boolean isEmpty = true;
boolean oldLenient = reader.isLenient();
reader.setLenient(true);
try {
reader.peek();
isEmpty = false;
TypeAdapter<T> typeAdapter = (TypeAdapter<T>) miniGson.getAdapter(TypeToken.get(typeOfT));
return typeAdapter.read(reader);
} catch (EOFException e) {
/*
* For compatibility with JSON 1.5 and earlier, we return null for empty
* documents instead of throwing.
*/
if (isEmpty) {
return null;
}
throw new JsonSyntaxException(e);
} catch (IllegalStateException e) {
throw new JsonSyntaxException(e);
} catch (IOException e) {
// TODO(inder): Figure out whether it is indeed right to rethrow this as JsonSyntaxException
throw new JsonSyntaxException(e);
} finally {
reader.setLenient(oldLenient);
}
}
/**
* This method deserializes the Json read from the specified parse tree into an object of the
* specified type. It is not suitable to use if the specified class is a generic type since it
* will not have the generic type information because of the Type Erasure feature of Java.
* Therefore, this method should not be used if the desired type is a generic type. Note that
* this method works fine if the any of the fields of the specified object are generics, just the
* object itself should not be a generic type. For the cases when the object is of generic type,
* invoke {@link #fromJson(JsonElement, Type)}.
* @param <T> the type of the desired object
* @param json the root of the parse tree of {@link JsonElement}s from which the object is to
* be deserialized
* @param classOfT The class of T
* @return an object of type T from the json
* @throws JsonSyntaxException if json is not a valid representation for an object of type typeOfT
* @since 1.3
*/
public <T> T fromJson(JsonElement json, Class<T> classOfT) throws JsonSyntaxException {
Object object = fromJson(json, (Type) classOfT);
return Primitives.wrap(classOfT).cast(object);
}
/**
* This method deserializes the Json read from the specified parse tree into an object of the
* specified type. This method is useful if the specified object is a generic type. For
* non-generic objects, use {@link #fromJson(JsonElement, Class)} instead.
*
* @param <T> the type of the desired object
* @param json the root of the parse tree of {@link JsonElement}s from which the object is to
* be deserialized
* @param typeOfT The specific genericized type of src. You can obtain this type by using the
* {@link com.massivecraft.core.lib.gson2.reflect.TypeToken} class. For example, to get the type for
* {@code Collection<Foo>}, you should use:
* <pre>
* Type typeOfT = new TypeToken&lt;Collection&lt;Foo&gt;&gt;(){}.getType();
* </pre>
* @return an object of type T from the json
* @throws JsonSyntaxException if json is not a valid representation for an object of type typeOfT
* @since 1.3
*/
public <T> T fromJson(JsonElement json, Type typeOfT) throws JsonSyntaxException {
if (json == null) {
return null;
}
return (T) fromJson(new JsonElementReader(json), typeOfT);
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder("{")
.append("serializeNulls:").append(serializeNulls)
.append(",serializers:").append(serializers)
.append(",deserializers:").append(deserializers)
// using the name instanceCreator instead of ObjectConstructor since the users of Gson are
// more familiar with the concept of Instance Creators. Moreover, the objectConstructor is
// just a utility class around instance creators, and its toString() only displays them.
.append(",instanceCreators:").append(constructorConstructor)
.append("}");
return sb.toString();
}
}

View File

@ -0,0 +1,717 @@
/*
* Copyright (C) 2008 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.massivecraft.core.lib.gson2;
import com.massivecraft.core.lib.gson2.DefaultTypeAdapters.DefaultDateTypeAdapter;
import com.massivecraft.core.lib.gson2.internal.$Gson$Preconditions;
import com.massivecraft.core.lib.gson2.internal.ParameterizedTypeHandlerMap;
import com.massivecraft.core.lib.gson2.internal.Primitives;
import com.massivecraft.core.lib.gson2.internal.bind.TypeAdapter;
import java.lang.reflect.Type;
import java.sql.Timestamp;
import java.text.DateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
/**
* <p>Use this builder to construct a {@link Gson} instance when you need to set configuration
* options other than the default. For {@link Gson} with default configuration, it is simpler to
* use {@code new Gson()}. {@code GsonBuilder} is best used by creating it, and then invoking its
* various configuration methods, and finally calling create.</p>
*
* <p>The following is an example shows how to use the {@code GsonBuilder} to construct a Gson
* instance:
*
* <pre>
* Gson gson = new GsonBuilder()
* .registerTypeAdapter(Id.class, new IdTypeAdapter())
* .enableComplexMapKeySerialization()
* .serializeNulls()
* .setDateFormat(DateFormat.LONG)
* .setFieldNamingPolicy(FieldNamingPolicy.UPPER_CAMEL_CASE)
* .setPrettyPrinting()
* .setVersion(1.0)
* .create();
* </pre></p>
*
* <p>NOTES:
* <ul>
* <li> the order of invocation of configuration methods does not matter.</li>
* <li> The default serialization of {@link Date} and its subclasses in Gson does
* not contain time-zone information. So, if you are using date/time instances,
* use {@code GsonBuilder} and its {@code setDateFormat} methods.</li>
* </ul>
* </p>
*
* @author Inderjeet Singh
* @author Joel Leitch
*/
public final class GsonBuilder {
private static final InnerClassExclusionStrategy innerClassExclusionStrategy =
new InnerClassExclusionStrategy();
private static final ExposeAnnotationDeserializationExclusionStrategy
exposeAnnotationDeserializationExclusionStrategy =
new ExposeAnnotationDeserializationExclusionStrategy();
private static final ExposeAnnotationSerializationExclusionStrategy
exposeAnnotationSerializationExclusionStrategy =
new ExposeAnnotationSerializationExclusionStrategy();
private final Set<ExclusionStrategy> serializeExclusionStrategies =
new HashSet<ExclusionStrategy>();
private final Set<ExclusionStrategy> deserializeExclusionStrategies =
new HashSet<ExclusionStrategy>();
private double ignoreVersionsAfter;
private ModifierBasedExclusionStrategy modifierBasedExclusionStrategy;
private boolean serializeInnerClasses;
private boolean excludeFieldsWithoutExposeAnnotation;
private LongSerializationPolicy longSerializationPolicy;
private FieldNamingStrategy2 fieldNamingPolicy;
private final ParameterizedTypeHandlerMap<InstanceCreator<?>> instanceCreators;
private final ParameterizedTypeHandlerMap<JsonSerializer<?>> serializers;
private final ParameterizedTypeHandlerMap<JsonDeserializer<?>> deserializers;
private final List<TypeAdapter.Factory> typeAdapterFactories
= new ArrayList<TypeAdapter.Factory>();
private boolean serializeNulls;
private String datePattern;
private int dateStyle;
private int timeStyle;
private boolean complexMapKeySerialization = false;
private boolean serializeSpecialFloatingPointValues;
private boolean escapeHtmlChars;
private boolean prettyPrinting;
private boolean generateNonExecutableJson;
/**
* Creates a GsonBuilder instance that can be used to build Gson with various configuration
* settings. GsonBuilder follows the builder pattern, and it is typically used by first
* invoking various configuration methods to set desired options, and finally calling
* {@link #create()}.
*/
public GsonBuilder() {
// add default exclusion strategies
deserializeExclusionStrategies.add(Gson.DEFAULT_ANON_LOCAL_CLASS_EXCLUSION_STRATEGY);
deserializeExclusionStrategies.add(Gson.DEFAULT_SYNTHETIC_FIELD_EXCLUSION_STRATEGY);
serializeExclusionStrategies.add(Gson.DEFAULT_ANON_LOCAL_CLASS_EXCLUSION_STRATEGY);
serializeExclusionStrategies.add(Gson.DEFAULT_SYNTHETIC_FIELD_EXCLUSION_STRATEGY);
// setup default values
ignoreVersionsAfter = VersionConstants.IGNORE_VERSIONS;
serializeInnerClasses = true;
prettyPrinting = false;
escapeHtmlChars = true;
modifierBasedExclusionStrategy = Gson.DEFAULT_MODIFIER_BASED_EXCLUSION_STRATEGY;
excludeFieldsWithoutExposeAnnotation = false;
longSerializationPolicy = LongSerializationPolicy.DEFAULT;
fieldNamingPolicy = Gson.DEFAULT_NAMING_POLICY;
instanceCreators = new ParameterizedTypeHandlerMap<InstanceCreator<?>>();
serializers = new ParameterizedTypeHandlerMap<JsonSerializer<?>>();
deserializers = new ParameterizedTypeHandlerMap<JsonDeserializer<?>>();
serializeNulls = false;
dateStyle = DateFormat.DEFAULT;
timeStyle = DateFormat.DEFAULT;
serializeSpecialFloatingPointValues = false;
generateNonExecutableJson = false;
}
/**
* Configures Gson to enable versioning support.
*
* @param ignoreVersionsAfter any field or type marked with a version higher than this value
* are ignored during serialization or deserialization.
* @return a reference to this {@code GsonBuilder} object to fulfill the "Builder" pattern
*/
public GsonBuilder setVersion(double ignoreVersionsAfter) {
this.ignoreVersionsAfter = ignoreVersionsAfter;
return this;
}
/**
* Configures Gson to excludes all class fields that have the specified modifiers. By default,
* Gson will exclude all fields marked transient or static. This method will override that
* behavior.
*
* @param modifiers the field modifiers. You must use the modifiers specified in the
* {@link java.lang.reflect.Modifier} class. For example,
* {@link java.lang.reflect.Modifier#TRANSIENT},
* {@link java.lang.reflect.Modifier#STATIC}.
* @return a reference to this {@code GsonBuilder} object to fulfill the "Builder" pattern
*/
public GsonBuilder excludeFieldsWithModifiers(int... modifiers) {
modifierBasedExclusionStrategy = new ModifierBasedExclusionStrategy(modifiers);
return this;
}
/**
* Makes the output JSON non-executable in Javascript by prefixing the generated JSON with some
* special text. This prevents attacks from third-party sites through script sourcing. See
* <a href="http://code.google.com/p/google-gson/issues/detail?id=42">Gson Issue 42</a>
* for details.
*
* @return a reference to this {@code GsonBuilder} object to fulfill the "Builder" pattern
* @since 1.3
*/
public GsonBuilder generateNonExecutableJson() {
this.generateNonExecutableJson = true;
return this;
}
/**
* Configures Gson to exclude all fields from consideration for serialization or deserialization
* that do not have the {@link com.massivecraft.core.lib.gson2.annotations.Expose} annotation.
*
* @return a reference to this {@code GsonBuilder} object to fulfill the "Builder" pattern
*/
public GsonBuilder excludeFieldsWithoutExposeAnnotation() {
excludeFieldsWithoutExposeAnnotation = true;
return this;
}
/**
* Configure Gson to serialize null fields. By default, Gson omits all fields that are null
* during serialization.
*
* @return a reference to this {@code GsonBuilder} object to fulfill the "Builder" pattern
* @since 1.2
*/
public GsonBuilder serializeNulls() {
this.serializeNulls = true;
return this;
}
/**
* Enabling this feature will only change the serialized form if the map key is
* a complex type (i.e. non-primitive) in its <strong>serialized</strong> JSON
* form. The default implementation of map serialization uses {@code toString()}
* on the key; however, when this is called then one of the following cases
* apply:
*
* <h3>Maps as JSON objects</h3>
* For this case, assume that a type adapter is registered to serialize and
* deserialize some {@code Point} class, which contains an x and y coordinate,
* to/from the JSON Primitive string value {@code "(x,y)"}. The Java map would
* then be serialized as a {@link JsonObject}.
*
* <p>Below is an example:
* <pre> {@code
* Gson gson = new GsonBuilder()
* .register(Point.class, new MyPointTypeAdapter())
* .enableComplexMapKeySerialization()
* .create();
*
* Map<Point, String> original = new LinkedHashMap<Point, String>();
* original.put(new Point(5, 6), "a");
* original.put(new Point(8, 8), "b");
* System.out.println(gson.toJson(original, type));
* }</pre>
* The above code prints this JSON object:<pre> {@code
* {
* "(5,6)": "a",
* "(8,8)": "b"
* }
* }</pre>
*
* <h3>Maps as JSON arrays</h3>
* For this case, assume that a type adapter was NOT registered for some
* {@code Point} class, but rather the default Gson serialization is applied.
* In this case, some {@code new Point(2,3)} would serialize as {@code
* {"x":2,"y":5}}.
*
* <p>Given the assumption above, a {@code Map<Point, String>} will be
* serialize as an array of arrays (can be viewed as an entry set of pairs).
*
* <p>Below is an example of serializing complex types as JSON arrays:
* <pre> {@code
* Gson gson = new GsonBuilder()
* .enableComplexMapKeySerialization()
* .create();
*
* Map<Point, String> original = new LinkedHashMap<Point, String>();
* original.put(new Point(5, 6), "a");
* original.put(new Point(8, 8), "b");
* System.out.println(gson.toJson(original, type));
* }
*
* The JSON output would look as follows:
* <pre> {@code
* [
* [
* {
* "x": 5,
* "y": 6
* },
* "a"
* ],
* [
* {
* "x": 8,
* "y": 8
* },
* "b"
* ]
* ]
* }</pre>
*
* @return a reference to this {@code GsonBuilder} object to fulfill the "Builder" pattern
* @since 1.7
*/
public GsonBuilder enableComplexMapKeySerialization() {
complexMapKeySerialization = true;
return this;
}
/**
* Configures Gson to exclude inner classes during serialization.
*
* @return a reference to this {@code GsonBuilder} object to fulfill the "Builder" pattern
* @since 1.3
*/
public GsonBuilder disableInnerClassSerialization() {
serializeInnerClasses = false;
return this;
}
/**
* Configures Gson to apply a specific serialization policy for {@code Long} and {@code long}
* objects.
*
* @param serializationPolicy the particular policy to use for serializing longs.
* @return a reference to this {@code GsonBuilder} object to fulfill the "Builder" pattern
* @since 1.3
*/
public GsonBuilder setLongSerializationPolicy(LongSerializationPolicy serializationPolicy) {
this.longSerializationPolicy = serializationPolicy;
return this;
}
/**
* Configures Gson to apply a specific naming policy to an object's field during serialization
* and deserialization.
*
* @param namingConvention the JSON field naming convention to use for serialization and
* deserialization.
* @return a reference to this {@code GsonBuilder} object to fulfill the "Builder" pattern
*/
public GsonBuilder setFieldNamingPolicy(FieldNamingPolicy namingConvention) {
return setFieldNamingStrategy(namingConvention.getFieldNamingPolicy());
}
/**
* Configures Gson to apply a specific naming policy strategy to an object's field during
* serialization and deserialization.
*
* @param fieldNamingStrategy the actual naming strategy to apply to the fields
* @return a reference to this {@code GsonBuilder} object to fulfill the "Builder" pattern
* @since 1.3
*/
public GsonBuilder setFieldNamingStrategy(FieldNamingStrategy fieldNamingStrategy) {
return setFieldNamingStrategy(new FieldNamingStrategy2Adapter(fieldNamingStrategy));
}
/**
* Configures Gson to apply a specific naming policy strategy to an object's field during
* serialization and deserialization.
*
* @param fieldNamingStrategy the actual naming strategy to apply to the fields
* @return a reference to this {@code GsonBuilder} object to fulfill the "Builder" pattern
*/
GsonBuilder setFieldNamingStrategy(FieldNamingStrategy2 fieldNamingStrategy) {
this.fieldNamingPolicy =
new SerializedNameAnnotationInterceptingNamingPolicy(fieldNamingStrategy);
return this;
}
/**
* Configures Gson to apply a set of exclusion strategies during both serialization and
* deserialization. Each of the {@code strategies} will be applied as a disjunction rule.
* This means that if one of the {@code strategies} suggests that a field (or class) should be
* skipped then that field (or object) is skipped during serializaiton/deserialization.
*
* @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
* @since 1.4
*/
public GsonBuilder setExclusionStrategies(ExclusionStrategy... strategies) {
List<ExclusionStrategy> strategyList = Arrays.asList(strategies);
serializeExclusionStrategies.addAll(strategyList);
deserializeExclusionStrategies.addAll(strategyList);
return this;
}
/**
* Configures Gson to apply the passed in exclusion strategy during serialization.
* If this method is invoked numerous times with different exclusion strategy objects
* then the exclusion strategies that were added will be applied as a disjunction rule.
* This means that if one of the added exclusion strategies suggests that a field (or
* class) should be skipped then that field (or object) is skipped during its
* serialization.
*
* @param strategy an exclusion strategy to apply during serialization.
* @return a reference to this {@code GsonBuilder} object to fulfill the "Builder" pattern
* @since 1.7
*/
public GsonBuilder addSerializationExclusionStrategy(ExclusionStrategy strategy) {
serializeExclusionStrategies.add(strategy);
return this;
}
/**
* Configures Gson to apply the passed in exclusion strategy during deserialization.
* If this method is invoked numerous times with different exclusion strategy objects
* then the exclusion strategies that were added will be applied as a disjunction rule.
* This means that if one of the added exclusion strategies suggests that a field (or
* class) should be skipped then that field (or object) is skipped during its
* deserialization.
*
* @param strategy an exclusion strategy to apply during deserialization.
* @return a reference to this {@code GsonBuilder} object to fulfill the "Builder" pattern
* @since 1.7
*/
public GsonBuilder addDeserializationExclusionStrategy(ExclusionStrategy strategy) {
deserializeExclusionStrategies.add(strategy);
return this;
}
/**
* Configures Gson to output Json that fits in a page for pretty printing. This option only
* affects Json serialization.
*
* @return a reference to this {@code GsonBuilder} object to fulfill the "Builder" pattern
*/
public GsonBuilder setPrettyPrinting() {
prettyPrinting = true;
return this;
}
/**
* By default, Gson escapes HTML characters such as &lt; &gt; etc. Use this option to configure
* Gson to pass-through HTML characters as is.
*
* @return a reference to this {@code GsonBuilder} object to fulfill the "Builder" pattern
* @since 1.3
*/
public GsonBuilder disableHtmlEscaping() {
this.escapeHtmlChars = false;
return this;
}
/**
* Configures Gson to serialize {@code Date} objects according to the pattern provided. You can
* call this method or {@link #setDateFormat(int)} multiple times, but only the last invocation
* will be used to decide the serialization format.
*
* <p>The date format will be used to serialize and deserialize {@link java.util.Date}, {@link
* java.sql.Timestamp} and {@link java.sql.Date}.
*
* <p>Note that this pattern must abide by the convention provided by {@code SimpleDateFormat}
* class. See the documentation in {@link java.text.SimpleDateFormat} for more information on
* valid date and time patterns.</p>
*
* @param pattern the pattern that dates will be serialized/deserialized to/from
* @return a reference to this {@code GsonBuilder} object to fulfill the "Builder" pattern
* @since 1.2
*/
public GsonBuilder setDateFormat(String pattern) {
// TODO(Joel): Make this fail fast if it is an invalid date format
this.datePattern = pattern;
return this;
}
/**
* Configures Gson to to serialize {@code Date} objects according to the style value provided.
* You can call this method or {@link #setDateFormat(String)} multiple times, but only the last
* invocation will be used to decide the serialization format.
*
* <p>Note that this style value should be one of the predefined constants in the
* {@code DateFormat} class. See the documentation in {@link java.text.DateFormat} for more
* information on the valid style constants.</p>
*
* @param style the predefined date style that date objects will be serialized/deserialized
* to/from
* @return a reference to this {@code GsonBuilder} object to fulfill the "Builder" pattern
* @since 1.2
*/
public GsonBuilder setDateFormat(int style) {
this.dateStyle = style;
this.datePattern = null;
return this;
}
/**
* Configures Gson to to serialize {@code Date} objects according to the style value provided.
* You can call this method or {@link #setDateFormat(String)} multiple times, but only the last
* invocation will be used to decide the serialization format.
*
* <p>Note that this style value should be one of the predefined constants in the
* {@code DateFormat} class. See the documentation in {@link java.text.DateFormat} for more
* information on the valid style constants.</p>
*
* @param dateStyle the predefined date style that date objects will be serialized/deserialized
* to/from
* @param timeStyle the predefined style for the time portion of the date objects
* @return a reference to this {@code GsonBuilder} object to fulfill the "Builder" pattern
* @since 1.2
*/
public GsonBuilder setDateFormat(int dateStyle, int timeStyle) {
this.dateStyle = dateStyle;
this.timeStyle = timeStyle;
this.datePattern = null;
return this;
}
/**
* Configures Gson for custom serialization or deserialization. This method combines the
* registration of an {@link InstanceCreator}, {@link JsonSerializer}, and a
* {@link JsonDeserializer}. It is best used when a single object {@code typeAdapter} implements
* all the required interfaces for custom serialization with Gson. If an instance creator,
* serializer or deserializer was previously registered for the specified {@code type}, it is
* overwritten.
*
* @param type the type definition for the type adapter being registered
* @param typeAdapter This object must implement at least one of the {@link InstanceCreator},
* {@link JsonSerializer}, and a {@link JsonDeserializer} interfaces.
* @return a reference to this {@code GsonBuilder} object to fulfill the "Builder" pattern
*/
public GsonBuilder registerTypeAdapter(Type type, Object typeAdapter) {
return registerTypeAdapter(type, typeAdapter, false);
}
private GsonBuilder registerTypeAdapter(Type type, Object typeAdapter, boolean isSystem) {
$Gson$Preconditions.checkArgument(typeAdapter instanceof JsonSerializer<?>
|| typeAdapter instanceof JsonDeserializer<?>
|| typeAdapter instanceof InstanceCreator<?>
|| typeAdapter instanceof TypeAdapter.Factory);
if (Primitives.isPrimitive(type) || Primitives.isWrapperType(type)) {
throw new IllegalArgumentException(
"Cannot register type adapters for " + type);
}
if (typeAdapter instanceof InstanceCreator<?>) {
registerInstanceCreator(type, (InstanceCreator<?>) typeAdapter, isSystem);
}
if (typeAdapter instanceof JsonSerializer<?>) {
registerSerializer(type, (JsonSerializer<?>) typeAdapter, isSystem);
}
if (typeAdapter instanceof JsonDeserializer<?>) {
registerDeserializer(type, (JsonDeserializer<?>) typeAdapter, isSystem);
}
if (typeAdapter instanceof TypeAdapter.Factory) {
typeAdapterFactories.add((TypeAdapter.Factory) typeAdapter);
}
return this;
}
/**
* Configures Gson to use a custom {@link InstanceCreator} for the specified type. If an instance
* creator was previously registered for the specified class, it is overwritten. Since this method
* takes a type instead of a Class object, it can be used to register a specific handler for a
* generic type corresponding to a raw type.
*
* @param <T> the type for which instance creator is being registered
* @param typeOfT The Type definition for T
* @param instanceCreator the instance creator for T
* @return a reference to this {@code GsonBuilder} object to fulfill the "Builder" pattern
*/
private <T> GsonBuilder registerInstanceCreator(Type typeOfT,
InstanceCreator<? extends T> instanceCreator, boolean isSystem) {
instanceCreators.register(typeOfT, instanceCreator, isSystem);
return this;
}
/**
* Configures Gson to use a custom JSON serializer for the specified type. You should use this
* method if you want to register different serializers for different generic types corresponding
* to a raw type.
*
* @param <T> the type for which the serializer is being registered
* @param typeOfT The type definition for T
* @param serializer the custom serializer
* @return a reference to this {@code GsonBuilder} object to fulfill the "Builder" pattern
*/
private <T> GsonBuilder registerSerializer(Type typeOfT, JsonSerializer<T> serializer,
boolean isSystem) {
serializers.register(typeOfT, serializer, isSystem);
return this;
}
/**
* Configures Gson to use a custom JSON deserializer for the specified type. You should use this
* method if you want to register different deserializers for different generic types
* corresponding to a raw type.
*
* @param <T> the type for which the deserializer is being registered
* @param typeOfT The type definition for T
* @param deserializer the custom deserializer
* @return a reference to this {@code GsonBuilder} object to fulfill the "Builder" pattern
*/
private <T> GsonBuilder registerDeserializer(Type typeOfT, JsonDeserializer<T> deserializer,
boolean isSystem) {
deserializers.register(typeOfT, new JsonDeserializerExceptionWrapper<T>(deserializer), isSystem);
return this;
}
/**
* Configures Gson for custom serialization or deserialization for an inheritance type hierarchy.
* This method combines the registration of an {@link InstanceCreator}, {@link JsonSerializer},
* and a {@link JsonDeserializer}. It is best used when a single object {@code typeAdapter}
* implements all the required interfaces for custom serialization with Gson.
* If an instance creator, serializer or deserializer was previously registered for the specified
* type hierarchy, it is overwritten. If an instance creator, serializer or deserializer is
* registered for a specific type in the type hierarchy, it will be invoked instead of the one
* registered for the type hierarchy.
*
* @param baseType the class definition for the type adapter being registered for the base class
* or interface
* @param typeAdapter This object must implement at least one of the {@link InstanceCreator},
* {@link JsonSerializer}, and a {@link JsonDeserializer} interfaces.
* @return a reference to this {@code GsonBuilder} object to fulfill the "Builder" pattern
* @since 1.7
*/
public GsonBuilder registerTypeHierarchyAdapter(Class<?> baseType, Object typeAdapter) {
return registerTypeHierarchyAdapter(baseType, typeAdapter, false);
}
private GsonBuilder registerTypeHierarchyAdapter(Class<?> baseType, Object typeAdapter,
boolean isSystem) {
$Gson$Preconditions.checkArgument(typeAdapter instanceof JsonSerializer<?>
|| typeAdapter instanceof JsonDeserializer<?> || typeAdapter instanceof InstanceCreator<?>);
if (typeAdapter instanceof InstanceCreator<?>) {
registerInstanceCreatorForTypeHierarchy(baseType, (InstanceCreator<?>) typeAdapter, isSystem);
}
if (typeAdapter instanceof JsonSerializer<?>) {
registerSerializerForTypeHierarchy(baseType, (JsonSerializer<?>) typeAdapter, isSystem);
}
if (typeAdapter instanceof JsonDeserializer<?>) {
registerDeserializerForTypeHierarchy(baseType, (JsonDeserializer<?>) typeAdapter, isSystem);
}
return this;
}
private <T> GsonBuilder registerInstanceCreatorForTypeHierarchy(Class<?> classOfT,
InstanceCreator<? extends T> instanceCreator, boolean isSystem) {
instanceCreators.registerForTypeHierarchy(classOfT, instanceCreator, isSystem);
return this;
}
private <T> GsonBuilder registerSerializerForTypeHierarchy(Class<?> classOfT,
JsonSerializer<T> serializer, boolean isSystem) {
serializers.registerForTypeHierarchy(classOfT, serializer, isSystem);
return this;
}
private <T> GsonBuilder registerDeserializerForTypeHierarchy(Class<?> classOfT,
JsonDeserializer<T> deserializer, boolean isSystem) {
deserializers.registerForTypeHierarchy(classOfT,
new JsonDeserializerExceptionWrapper<T>(deserializer), isSystem);
return this;
}
/**
* Section 2.4 of <a href="http://www.ietf.org/rfc/rfc4627.txt">JSON specification</a> disallows
* special double values (NaN, Infinity, -Infinity). However,
* <a href="http://www.ecma-international.org/publications/files/ECMA-ST/Ecma-262.pdf">Javascript
* specification</a> (see section 4.3.20, 4.3.22, 4.3.23) allows these values as valid Javascript
* values. Moreover, most JavaScript engines will accept these special values in JSON without
* problem. So, at a practical level, it makes sense to accept these values as valid JSON even
* though JSON specification disallows them.
*
* <p>Gson always accepts these special values during deserialization. However, it outputs
* strictly compliant JSON. Hence, if it encounters a float value {@link Float#NaN},
* {@link Float#POSITIVE_INFINITY}, {@link Float#NEGATIVE_INFINITY}, or a double value
* {@link Double#NaN}, {@link Double#POSITIVE_INFINITY}, {@link Double#NEGATIVE_INFINITY}, it
* will throw an {@link IllegalArgumentException}. This method provides a way to override the
* default behavior when you know that the JSON receiver will be able to handle these special
* values.
*
* @return a reference to this {@code GsonBuilder} object to fulfill the "Builder" pattern
* @since 1.3
*/
public GsonBuilder serializeSpecialFloatingPointValues() {
this.serializeSpecialFloatingPointValues = true;
return this;
}
/**
* Creates a {@link Gson} instance based on the current configuration. This method is free of
* side-effects to this {@code GsonBuilder} instance and hence can be called multiple times.
*
* @return an instance of Gson configured with the options currently set in this builder
*/
public Gson create() {
List<ExclusionStrategy> deserializationStrategies =
new LinkedList<ExclusionStrategy>(deserializeExclusionStrategies);
List<ExclusionStrategy> serializationStrategies =
new LinkedList<ExclusionStrategy>(serializeExclusionStrategies);
deserializationStrategies.add(modifierBasedExclusionStrategy);
serializationStrategies.add(modifierBasedExclusionStrategy);
if (!serializeInnerClasses) {
deserializationStrategies.add(innerClassExclusionStrategy);
serializationStrategies.add(innerClassExclusionStrategy);
}
if (ignoreVersionsAfter != VersionConstants.IGNORE_VERSIONS) {
VersionExclusionStrategy versionExclusionStrategy =
new VersionExclusionStrategy(ignoreVersionsAfter);
deserializationStrategies.add(versionExclusionStrategy);
serializationStrategies.add(versionExclusionStrategy);
}
if (excludeFieldsWithoutExposeAnnotation) {
deserializationStrategies.add(exposeAnnotationDeserializationExclusionStrategy);
serializationStrategies.add(exposeAnnotationSerializationExclusionStrategy);
}
addTypeAdaptersForDate(datePattern, dateStyle, timeStyle, serializers, deserializers);
return new Gson(new DisjunctionExclusionStrategy(deserializationStrategies),
new DisjunctionExclusionStrategy(serializationStrategies),
fieldNamingPolicy, instanceCreators.copyOf().makeUnmodifiable(), serializeNulls,
serializers.copyOf().makeUnmodifiable(), deserializers.copyOf().makeUnmodifiable(),
complexMapKeySerialization, generateNonExecutableJson, escapeHtmlChars, prettyPrinting,
serializeSpecialFloatingPointValues, longSerializationPolicy, typeAdapterFactories);
}
private static void addTypeAdaptersForDate(String datePattern, int dateStyle, int timeStyle,
ParameterizedTypeHandlerMap<JsonSerializer<?>> serializers,
ParameterizedTypeHandlerMap<JsonDeserializer<?>> deserializers) {
DefaultDateTypeAdapter dateTypeAdapter = null;
if (datePattern != null && !"".equals(datePattern.trim())) {
dateTypeAdapter = new DefaultDateTypeAdapter(datePattern);
} else if (dateStyle != DateFormat.DEFAULT && timeStyle != DateFormat.DEFAULT) {
dateTypeAdapter = new DefaultDateTypeAdapter(dateStyle, timeStyle);
}
if (dateTypeAdapter != null) {
registerIfAbsent(Date.class, serializers, dateTypeAdapter);
registerIfAbsent(Date.class, deserializers, dateTypeAdapter);
registerIfAbsent(Timestamp.class, serializers, dateTypeAdapter);
registerIfAbsent(Timestamp.class, deserializers, dateTypeAdapter);
registerIfAbsent(java.sql.Date.class, serializers, dateTypeAdapter);
registerIfAbsent(java.sql.Date.class, deserializers, dateTypeAdapter);
}
}
private static <T> void registerIfAbsent(Class<?> type,
ParameterizedTypeHandlerMap<T> adapters, T adapter) {
if (!adapters.hasSpecificHandlerFor(type)) {
adapters.register(type, adapter, false);
}
}
}

View File

@ -0,0 +1,110 @@
/*
* Copyright (C) 2011 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.massivecraft.core.lib.gson2;
import com.massivecraft.core.lib.gson2.internal.ParameterizedTypeHandlerMap;
import com.massivecraft.core.lib.gson2.internal.Streams;
import com.massivecraft.core.lib.gson2.internal.bind.MiniGson;
import com.massivecraft.core.lib.gson2.internal.bind.TypeAdapter;
import com.massivecraft.core.lib.gson2.reflect.TypeToken;
import com.massivecraft.core.lib.gson2.stream.JsonReader;
import com.massivecraft.core.lib.gson2.stream.JsonWriter;
import java.io.IOException;
import java.lang.reflect.Type;
final class GsonToMiniGsonTypeAdapterFactory implements TypeAdapter.Factory {
private final ParameterizedTypeHandlerMap<JsonSerializer<?>> serializers;
private final ParameterizedTypeHandlerMap<JsonDeserializer<?>> deserializers;
private final JsonDeserializationContext deserializationContext;
private final JsonSerializationContext serializationContext;
public GsonToMiniGsonTypeAdapterFactory(final Gson gson,
ParameterizedTypeHandlerMap<JsonSerializer<?>> serializers,
ParameterizedTypeHandlerMap<JsonDeserializer<?>> deserializers) {
this.serializers = serializers;
this.deserializers = deserializers;
this.deserializationContext = new JsonDeserializationContext() {
public <T> T deserialize(JsonElement json, Type typeOfT) throws JsonParseException {
return (T) gson.fromJson(json, typeOfT);
}
};
this.serializationContext = new JsonSerializationContext() {
public JsonElement serialize(Object src) {
return gson.toJsonTree(src);
}
public JsonElement serialize(Object src, Type typeOfSrc) {
return gson.toJsonTree(src, typeOfSrc);
}
};
}
public <T> TypeAdapter<T> create(final MiniGson context, final TypeToken<T> typeToken) {
final Type type = typeToken.getType();
@SuppressWarnings("unchecked") // guaranteed to match typeOfT
final JsonSerializer<T> serializer
= (JsonSerializer<T>) serializers.getHandlerFor(type, false);
@SuppressWarnings("unchecked") // guaranteed to match typeOfT
final JsonDeserializer<T> deserializer
= (JsonDeserializer<T>) deserializers.getHandlerFor(type, false);
if (serializer == null && deserializer == null) {
return null;
}
return new TypeAdapter<T>() {
/**
* The delegate is lazily created because it may not be needed, and
* creating it may fail.
*/
private TypeAdapter<T> delegate;
@Override public T read(JsonReader reader) throws IOException {
if (deserializer == null) {
return delegate().read(reader);
}
JsonElement value = Streams.parse(reader);
if (value.isJsonNull()) {
return null;
}
return deserializer.deserialize(value, type, deserializationContext);
}
@Override public void write(JsonWriter writer, T value) throws IOException {
if (serializer == null) {
delegate().write(writer, value);
return;
}
if (value == null) {
writer.nullValue();
return;
}
JsonElement element = serializer.serialize(value, type, serializationContext);
Streams.write(element, writer);
}
private TypeAdapter<T> delegate() {
TypeAdapter<T> d = delegate;
return d != null
? d
: (delegate = context.getNextAdapter(GsonToMiniGsonTypeAdapterFactory.this, typeToken));
}
};
}
}

View File

@ -0,0 +1,43 @@
/*
* Copyright (C) 2008 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.massivecraft.core.lib.gson2;
import java.lang.reflect.Modifier;
/**
* Strategy for excluding inner classes.
*
* @author Joel Leitch
*/
final class InnerClassExclusionStrategy implements ExclusionStrategy {
public boolean shouldSkipField(FieldAttributes f) {
return isInnerClass(f.getDeclaredClass());
}
public boolean shouldSkipClass(Class<?> clazz) {
return isInnerClass(clazz);
}
private boolean isInnerClass(Class<?> clazz) {
return clazz.isMemberClass() && !isStatic(clazz);
}
private boolean isStatic(Class<?> clazz) {
return (clazz.getModifiers() & Modifier.STATIC) != 0;
}
}

View File

@ -0,0 +1,92 @@
/*
* Copyright (C) 2008 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.massivecraft.core.lib.gson2;
import java.lang.reflect.Type;
/**
* This interface is implemented to create instances of a class that does not define a no-args
* constructor. If you can modify the class, you should instead add a private, or public
* no-args constructor. However, that is not possible for library classes, such as JDK classes, or
* a third-party library that you do not have source-code of. In such cases, you should define an
* instance creator for the class. Implementations of this interface should be registered with
* {@link GsonBuilder#registerTypeAdapter(Type, Object)} method before Gson will be able to use
* them.
* <p>Let us look at an example where defining an InstanceCreator might be useful. The
* {@code Id} class defined below does not have a default no-args constructor.</p>
*
* <pre>
* public class Id&lt;T&gt; {
* private final Class&lt;T&gt; clazz;
* private final long value;
* public Id(Class&lt;T&gt; clazz, long value) {
* this.clazz = clazz;
* this.value = value;
* }
* }
* </pre>
*
* <p>If Gson encounters an object of type {@code Id} during deserialization, it will throw an
* exception. The easiest way to solve this problem will be to add a (public or private) no-args
* constructor as follows:</p>
*
* <pre>
* private Id() {
* this(Object.class, 0L);
* }
* </pre>
*
* <p>However, let us assume that the developer does not have access to the source-code of the
* {@code Id} class, or does not want to define a no-args constructor for it. The developer
* can solve this problem by defining an {@code InstanceCreator} for {@code Id}:</p>
*
* <pre>
* class IdInstanceCreator implements InstanceCreator&lt;Id&gt; {
* public Id createInstance(Type type) {
* return new Id(Object.class, 0L);
* }
* }
* </pre>
*
* <p>Note that it does not matter what the fields of the created instance contain since Gson will
* overwrite them with the deserialized values specified in Json. You should also ensure that a
* <i>new</i> object is returned, not a common object since its fields will be overwritten.
* The developer will need to register {@code IdInstanceCreator} with Gson as follows:</p>
*
* <pre>
* Gson gson = new GsonBuilder().registerTypeAdapter(Id.class, new IdInstanceCreator()).create();
* </pre>
*
* @param <T> the type of object that will be created by this implementation.
*
* @author Inderjeet Singh
* @author Joel Leitch
*/
public interface InstanceCreator<T> {
/**
* Gson invokes this call-back method during deserialization to create an instance of the
* specified type. The fields of the returned instance are overwritten with the data present
* in the Json. Since the prior contents of the object are destroyed and overwritten, do not
* return an instance that is useful elsewhere. In particular, do not return a common instance,
* always use {@code new} to create a new instance.
*
* @param type the parameterized T represented as a {@link Type}.
* @return a default object instance of type T.
*/
public T createInstance(Type type);
}

View File

@ -0,0 +1,51 @@
/*
* Copyright (C) 2008 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.massivecraft.core.lib.gson2;
import java.lang.annotation.Annotation;
import java.lang.reflect.Type;
import java.util.Collection;
/**
* A simple implementation of the {@link FieldNamingStrategy2} interface such that it does not
* perform any string translation of the incoming field name.
*
* <p>The following is an example:</p>
*
* <pre>
* class IntWrapper {
* public int integerField = 0;
* }
*
* JavaFieldNamingPolicy policy = new JavaFieldNamingPolicy();
* String translatedFieldName =
* policy.translateName(IntWrapper.class.getField("integerField"));
*
* assert("integerField".equals(translatedFieldName));
* </pre>
*
* <p>This is the default {@link FieldNamingStrategy2} used by Gson.</p>
*
* @author Joel Leitch
*/
final class JavaFieldNamingPolicy extends RecursiveFieldNamingPolicy {
@Override
protected String translateName(String target, Type fieldType, Collection<Annotation> annotations) {
return target;
}
}

View File

@ -0,0 +1,282 @@
/*
* Copyright (C) 2008 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.massivecraft.core.lib.gson2;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
/**
* A class representing an array type in Json. An array is a list of {@link JsonElement}s each of
* which can be of a different type. This is an ordered list, meaning that the order in which
* elements are added is preserved.
*
* @author Inderjeet Singh
* @author Joel Leitch
*/
public final class JsonArray extends JsonElement implements Iterable<JsonElement> {
private final List<JsonElement> elements;
/**
* Creates an empty JsonArray.
*/
public JsonArray() {
elements = new ArrayList<JsonElement>();
}
/**
* Adds the specified element to self.
*
* @param element the element that needs to be added to the array.
*/
public void add(JsonElement element) {
if (element == null) {
element = JsonNull.INSTANCE;
}
elements.add(element);
}
/**
* Adds all the elements of the specified array to self.
*
* @param array the array whose elements need to be added to the array.
*/
public void addAll(JsonArray array) {
elements.addAll(array.elements);
}
/**
* Returns the number of elements in the array.
*
* @return the number of elements in the array.
*/
public int size() {
return elements.size();
}
/**
* Returns an iterator to navigate the elemetns of the array. Since the array is an ordered list,
* the iterator navigates the elements in the order they were inserted.
*
* @return an iterator to navigate the elements of the array.
*/
public Iterator<JsonElement> iterator() {
return elements.iterator();
}
/**
* Returns the ith element of the array.
*
* @param i the index of the element that is being sought.
* @return the element present at the ith index.
* @throws IndexOutOfBoundsException if i is negative or greater than or equal to the
* {@link #size()} of the array.
*/
public JsonElement get(int i) {
return elements.get(i);
}
/**
* convenience method to get this array as a {@link Number} if it contains a single element.
*
* @return get this element as a number if it is single element array.
* @throws ClassCastException if the element in the array is of not a {@link JsonPrimitive} and
* is not a valid Number.
* @throws IllegalStateException if the array has more than one element.
*/
@Override
public Number getAsNumber() {
if (elements.size() == 1) {
return elements.get(0).getAsNumber();
}
throw new IllegalStateException();
}
/**
* convenience method to get this array as a {@link String} if it contains a single element.
*
* @return get this element as a String if it is single element array.
* @throws ClassCastException if the element in the array is of not a {@link JsonPrimitive} and
* is not a valid String.
* @throws IllegalStateException if the array has more than one element.
*/
@Override
public String getAsString() {
if (elements.size() == 1) {
return elements.get(0).getAsString();
}
throw new IllegalStateException();
}
/**
* convenience method to get this array as a double if it contains a single element.
*
* @return get this element as a double if it is single element array.
* @throws ClassCastException if the element in the array is of not a {@link JsonPrimitive} and
* is not a valid double.
* @throws IllegalStateException if the array has more than one element.
*/
@Override
public double getAsDouble() {
if (elements.size() == 1) {
return elements.get(0).getAsDouble();
}
throw new IllegalStateException();
}
/**
* convenience method to get this array as a {@link BigDecimal} if it contains a single element.
*
* @return get this element as a {@link BigDecimal} if it is single element array.
* @throws ClassCastException if the element in the array is of not a {@link JsonPrimitive}.
* @throws NumberFormatException if the element at index 0 is not a valid {@link BigDecimal}.
* @throws IllegalStateException if the array has more than one element.
* @since 1.2
*/
@Override
public BigDecimal getAsBigDecimal() {
if (elements.size() == 1) {
return elements.get(0).getAsBigDecimal();
}
throw new IllegalStateException();
}
/**
* convenience method to get this array as a {@link BigInteger} if it contains a single element.
*
* @return get this element as a {@link BigInteger} if it is single element array.
* @throws ClassCastException if the element in the array is of not a {@link JsonPrimitive}.
* @throws NumberFormatException if the element at index 0 is not a valid {@link BigInteger}.
* @throws IllegalStateException if the array has more than one element.
* @since 1.2
*/
@Override
public BigInteger getAsBigInteger() {
if (elements.size() == 1) {
return elements.get(0).getAsBigInteger();
}
throw new IllegalStateException();
}
/**
* convenience method to get this array as a float if it contains a single element.
*
* @return get this element as a float if it is single element array.
* @throws ClassCastException if the element in the array is of not a {@link JsonPrimitive} and
* is not a valid float.
* @throws IllegalStateException if the array has more than one element.
*/
@Override
public float getAsFloat() {
if (elements.size() == 1) {
return elements.get(0).getAsFloat();
}
throw new IllegalStateException();
}
/**
* convenience method to get this array as a long if it contains a single element.
*
* @return get this element as a long if it is single element array.
* @throws ClassCastException if the element in the array is of not a {@link JsonPrimitive} and
* is not a valid long.
* @throws IllegalStateException if the array has more than one element.
*/
@Override
public long getAsLong() {
if (elements.size() == 1) {
return elements.get(0).getAsLong();
}
throw new IllegalStateException();
}
/**
* convenience method to get this array as an integer if it contains a single element.
*
* @return get this element as an integer if it is single element array.
* @throws ClassCastException if the element in the array is of not a {@link JsonPrimitive} and
* is not a valid integer.
* @throws IllegalStateException if the array has more than one element.
*/
@Override
public int getAsInt() {
if (elements.size() == 1) {
return elements.get(0).getAsInt();
}
throw new IllegalStateException();
}
@Override
public byte getAsByte() {
if (elements.size() == 1) {
return elements.get(0).getAsByte();
}
throw new IllegalStateException();
}
@Override
public char getAsCharacter() {
if (elements.size() == 1) {
return elements.get(0).getAsCharacter();
}
throw new IllegalStateException();
}
/**
* convenience method to get this array as a primitive short if it contains a single element.
*
* @return get this element as a primitive short if it is single element array.
* @throws ClassCastException if the element in the array is of not a {@link JsonPrimitive} and
* is not a valid short.
* @throws IllegalStateException if the array has more than one element.
*/
@Override
public short getAsShort() {
if (elements.size() == 1) {
return elements.get(0).getAsShort();
}
throw new IllegalStateException();
}
/**
* convenience method to get this array as a boolean if it contains a single element.
*
* @return get this element as a boolean if it is single element array.
* @throws ClassCastException if the element in the array is of not a {@link JsonPrimitive} and
* is not a valid boolean.
* @throws IllegalStateException if the array has more than one element.
*/
@Override
public boolean getAsBoolean() {
if (elements.size() == 1) {
return elements.get(0).getAsBoolean();
}
throw new IllegalStateException();
}
@Override
public boolean equals(Object o) {
return (o == this) || (o instanceof JsonArray && ((JsonArray) o).elements.equals(elements));
}
@Override
public int hashCode() {
return elements.hashCode();
}
}

View File

@ -0,0 +1,44 @@
/*
* Copyright (C) 2008 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.massivecraft.core.lib.gson2;
import java.lang.reflect.Type;
/**
* Context for deserialization that is passed to a custom deserializer during invocation of its
* {@link JsonDeserializer#deserialize(JsonElement, Type, JsonDeserializationContext)}
* method.
*
* @author Inderjeet Singh
* @author Joel Leitch
*/
public interface JsonDeserializationContext {
/**
* Invokes default deserialization on the specified object. It should never be invoked on
* the element received as a parameter of the
* {@link JsonDeserializer#deserialize(JsonElement, Type, JsonDeserializationContext)} method. Doing
* so will result in an infinite loop since Gson will in-turn call the custom deserializer again.
*
* @param json the parse tree.
* @param typeOfT type of the expected return value.
* @param <T> The type of the deserialized object.
* @return An object of type typeOfT.
* @throws JsonParseException if the parse tree does not contain expected data.
*/
public <T> T deserialize(JsonElement json, Type typeOfT) throws JsonParseException;
}

View File

@ -0,0 +1,88 @@
/*
* Copyright (C) 2008 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.massivecraft.core.lib.gson2;
import java.lang.reflect.Type;
/**
* <p>Interface representing a custom deserializer for Json. You should write a custom
* deserializer, if you are not happy with the default deserialization done by Gson. You will
* also need to register this deserializer through
* {@link GsonBuilder#registerTypeAdapter(Type, Object)}.</p>
*
* <p>Let us look at example where defining a deserializer will be useful. The {@code Id} class
* defined below has two fields: {@code clazz} and {@code value}.</p>
*
* <pre>
* public class Id&lt;T&gt; {
* private final Class&lt;T&gt; clazz;
* private final long value;
* public Id(Class&lt;T&gt; clazz, long value) {
* this.clazz = clazz;
* this.value = value;
* }
* public long getValue() {
* return value;
* }
* }
* </pre>
*
* <p>The default deserialization of {@code Id(com.foo.MyObject.class, 20L)} will require the
* Json string to be <code>{"clazz":com.foo.MyObject,"value":20}</code>. Suppose, you already know
* the type of the field that the {@code Id} will be deserialized into, and hence just want to
* deserialize it from a Json string {@code 20}. You can achieve that by writing a custom
* deserializer:</p>
*
* <pre>
* class IdDeserializer implements JsonDeserializer&lt;Id&gt;() {
* public Id deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
* throws JsonParseException {
* return new Id((Class)typeOfT, id.getValue());
* }
* </pre>
*
* <p>You will also need to register {@code IdDeserializer} with Gson as follows:</p>
*
* <pre>
* Gson gson = new GsonBuilder().registerTypeAdapter(Id.class, new IdDeserializer()).create();
* </pre>
*
* @author Inderjeet Singh
* @author Joel Leitch
*
* @param <T> type for which the deserializer is being registered. It is possible that a
* deserializer may be asked to deserialize a specific generic type of the T.
*/
public interface JsonDeserializer<T> {
/**
* Gson invokes this call-back method during deserialization when it encounters a field of the
* specified type.
* <p>In the implementation of this call-back method, you should consider invoking
* {@link JsonDeserializationContext#deserialize(JsonElement, Type)} method to create objects
* for any non-trivial field of the returned object. However, you should never invoke it on the
* the same type passing {@code json} since that will cause an infinite loop (Gson will call your
* call-back method again).
*
* @param json The Json data being deserialized
* @param typeOfT The type of the Object to deserialize to
* @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}
*/
public T deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
throws JsonParseException;
}

View File

@ -0,0 +1,72 @@
/*
* Copyright (C) 2008 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.massivecraft.core.lib.gson2;
import com.massivecraft.core.lib.gson2.internal.$Gson$Preconditions;
import java.lang.reflect.Type;
/**
* Decorators a {@code JsonDeserializer} instance with exception handling. This wrapper class
* ensures that a {@code JsonDeserializer} will not propagate any exception other than a
* {@link JsonParseException}.
*
* @param <T> type of the deserializer being wrapped.
*
* @author Inderjeet Singh
* @author Joel Leitch
*/
final class JsonDeserializerExceptionWrapper<T> implements JsonDeserializer<T> {
private final JsonDeserializer<T> delegate;
/**
* Returns a wrapped {@link JsonDeserializer} object that has been decorated with
* {@link JsonParseException} handling.
*
* @param delegate the {@code JsonDeserializer} instance to be wrapped.
* @throws IllegalArgumentException if {@code delegate} is {@code null}.
*/
JsonDeserializerExceptionWrapper(JsonDeserializer<T> delegate) {
this.delegate = $Gson$Preconditions.checkNotNull(delegate);
}
public T deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
throws JsonParseException {
try {
return delegate.deserialize(json, typeOfT, context);
} catch (JsonParseException e) {
// just rethrow the exception
throw e;
} catch (Exception e) {
// rethrow as a JsonParseException
StringBuilder errorMsg = new StringBuilder()
.append("The JsonDeserializer ")
.append(delegate)
.append(" failed to deserialize json object ")
.append(json)
.append(" given the type ")
.append(typeOfT);
throw new JsonParseException(errorMsg.toString(), e);
}
}
@Override
public String toString() {
return delegate.toString();
}
}

View File

@ -0,0 +1,325 @@
/*
* Copyright (C) 2008 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.massivecraft.core.lib.gson2;
import com.massivecraft.core.lib.gson2.internal.Streams;
import com.massivecraft.core.lib.gson2.stream.JsonWriter;
import java.io.IOException;
import java.io.StringWriter;
import java.math.BigDecimal;
import java.math.BigInteger;
/**
* A class representing an element of Json. It could either be a {@link JsonObject}, a
* {@link JsonArray}, a {@link JsonPrimitive} or a {@link JsonNull}.
*
* @author Inderjeet Singh
* @author Joel Leitch
*/
public abstract class JsonElement {
/**
* provides check for verifying if this element is an array or not.
*
* @return true if this element is of type {@link JsonArray}, false otherwise.
*/
public boolean isJsonArray() {
return this instanceof JsonArray;
}
/**
* provides check for verifying if this element is a Json object or not.
*
* @return true if this element is of type {@link JsonObject}, false otherwise.
*/
public boolean isJsonObject() {
return this instanceof JsonObject;
}
/**
* provides check for verifying if this element is a primitive or not.
*
* @return true if this element is of type {@link JsonPrimitive}, false otherwise.
*/
public boolean isJsonPrimitive() {
return this instanceof JsonPrimitive;
}
/**
* provides check for verifying if this element represents a null value or not.
*
* @return true if this element is of type {@link JsonNull}, false otherwise.
* @since 1.2
*/
public boolean isJsonNull() {
return this instanceof JsonNull;
}
/**
* convenience method to get this element as a {@link JsonObject}. If the element is of some
* other type, a {@link ClassCastException} will result. Hence it is best to use this method
* after ensuring that this element is of the desired type by calling {@link #isJsonObject()}
* first.
*
* @return get this element as a {@link JsonObject}.
* @throws IllegalStateException if the element is of another type.
*/
public JsonObject getAsJsonObject() {
if (isJsonObject()) {
return (JsonObject) this;
}
throw new IllegalStateException("Not a JSON Object: " + this);
}
/**
* convenience method to get this element as a {@link JsonArray}. If the element is of some
* other type, a {@link ClassCastException} will result. Hence it is best to use this method
* after ensuring that this element is of the desired type by calling {@link #isJsonArray()}
* first.
*
* @return get this element as a {@link JsonArray}.
* @throws IllegalStateException if the element is of another type.
*/
public JsonArray getAsJsonArray() {
if (isJsonArray()) {
return (JsonArray) this;
}
throw new IllegalStateException("This is not a JSON Array.");
}
/**
* convenience method to get this element as a {@link JsonPrimitive}. If the element is of some
* other type, a {@link ClassCastException} will result. Hence it is best to use this method
* after ensuring that this element is of the desired type by calling {@link #isJsonPrimitive()}
* first.
*
* @return get this element as a {@link JsonPrimitive}.
* @throws IllegalStateException if the element is of another type.
*/
public JsonPrimitive getAsJsonPrimitive() {
if (isJsonPrimitive()) {
return (JsonPrimitive) this;
}
throw new IllegalStateException("This is not a JSON Primitive.");
}
/**
* convenience method to get this element as a {@link JsonNull}. If the element is of some
* other type, a {@link ClassCastException} will result. Hence it is best to use this method
* after ensuring that this element is of the desired type by calling {@link #isJsonNull()}
* first.
*
* @return get this element as a {@link JsonNull}.
* @throws IllegalStateException if the element is of another type.
* @since 1.2
*/
public JsonNull getAsJsonNull() {
if (isJsonNull()) {
return (JsonNull) this;
}
throw new IllegalStateException("This is not a JSON Null.");
}
/**
* convenience method to get this element as a boolean value.
*
* @return get this element as a primitive boolean value.
* @throws ClassCastException if the element is of not a {@link JsonPrimitive} and is not a valid
* boolean value.
* @throws IllegalStateException if the element is of the type {@link JsonArray} but contains
* more than a single element.
*/
public boolean getAsBoolean() {
throw new UnsupportedOperationException(getClass().getSimpleName());
}
/**
* convenience method to get this element as a {@link Boolean} value.
*
* @return get this element as a {@link Boolean} value.
* @throws ClassCastException if the element is of not a {@link JsonPrimitive} and is not a valid
* boolean value.
* @throws IllegalStateException if the element is of the type {@link JsonArray} but contains
* more than a single element.
*/
Boolean getAsBooleanWrapper() {
throw new UnsupportedOperationException(getClass().getSimpleName());
}
/**
* convenience method to get this element as a {@link Number}.
*
* @return get this element as a {@link Number}.
* @throws ClassCastException if the element is of not a {@link JsonPrimitive} and is not a valid
* number.
* @throws IllegalStateException if the element is of the type {@link JsonArray} but contains
* more than a single element.
*/
public Number getAsNumber() {
throw new UnsupportedOperationException(getClass().getSimpleName());
}
/**
* convenience method to get this element as a string value.
*
* @return get this element as a string value.
* @throws ClassCastException if the element is of not a {@link JsonPrimitive} and is not a valid
* string value.
* @throws IllegalStateException if the element is of the type {@link JsonArray} but contains
* more than a single element.
*/
public String getAsString() {
throw new UnsupportedOperationException(getClass().getSimpleName());
}
/**
* convenience method to get this element as a primitive double value.
*
* @return get this element as a primitive double value.
* @throws ClassCastException if the element is of not a {@link JsonPrimitive} and is not a valid
* double value.
* @throws IllegalStateException if the element is of the type {@link JsonArray} but contains
* more than a single element.
*/
public double getAsDouble() {
throw new UnsupportedOperationException(getClass().getSimpleName());
}
/**
* convenience method to get this element as a primitive float value.
*
* @return get this element as a primitive float value.
* @throws ClassCastException if the element is of not a {@link JsonPrimitive} and is not a valid
* float value.
* @throws IllegalStateException if the element is of the type {@link JsonArray} but contains
* more than a single element.
*/
public float getAsFloat() {
throw new UnsupportedOperationException(getClass().getSimpleName());
}
/**
* convenience method to get this element as a primitive long value.
*
* @return get this element as a primitive long value.
* @throws ClassCastException if the element is of not a {@link JsonPrimitive} and is not a valid
* long value.
* @throws IllegalStateException if the element is of the type {@link JsonArray} but contains
* more than a single element.
*/
public long getAsLong() {
throw new UnsupportedOperationException(getClass().getSimpleName());
}
/**
* convenience method to get this element as a primitive integer value.
*
* @return get this element as a primitive integer value.
* @throws ClassCastException if the element is of not a {@link JsonPrimitive} and is not a valid
* integer value.
* @throws IllegalStateException if the element is of the type {@link JsonArray} but contains
* more than a single element.
*/
public int getAsInt() {
throw new UnsupportedOperationException(getClass().getSimpleName());
}
/**
* convenience method to get this element as a primitive byte value.
*
* @return get this element as a primitive byte value.
* @throws ClassCastException if the element is of not a {@link JsonPrimitive} and is not a valid
* byte value.
* @throws IllegalStateException if the element is of the type {@link JsonArray} but contains
* more than a single element.
* @since 1.3
*/
public byte getAsByte() {
throw new UnsupportedOperationException(getClass().getSimpleName());
}
/**
* convenience method to get this element as a primitive character value.
*
* @return get this element as a primitive char value.
* @throws ClassCastException if the element is of not a {@link JsonPrimitive} and is not a valid
* char value.
* @throws IllegalStateException if the element is of the type {@link JsonArray} but contains
* more than a single element.
* @since 1.3
*/
public char getAsCharacter() {
throw new UnsupportedOperationException(getClass().getSimpleName());
}
/**
* convenience method to get this element as a {@link BigDecimal}.
*
* @return get this element as a {@link BigDecimal}.
* @throws ClassCastException if the element is of not a {@link JsonPrimitive}.
* * @throws NumberFormatException if the element is not a valid {@link BigDecimal}.
* @throws IllegalStateException if the element is of the type {@link JsonArray} but contains
* more than a single element.
* @since 1.2
*/
public BigDecimal getAsBigDecimal() {
throw new UnsupportedOperationException(getClass().getSimpleName());
}
/**
* convenience method to get this element as a {@link BigInteger}.
*
* @return get this element as a {@link BigInteger}.
* @throws ClassCastException if the element is of not a {@link JsonPrimitive}.
* @throws NumberFormatException if the element is not a valid {@link BigInteger}.
* @throws IllegalStateException if the element is of the type {@link JsonArray} but contains
* more than a single element.
* @since 1.2
*/
public BigInteger getAsBigInteger() {
throw new UnsupportedOperationException(getClass().getSimpleName());
}
/**
* convenience method to get this element as a primitive short value.
*
* @return get this element as a primitive short value.
* @throws ClassCastException if the element is of not a {@link JsonPrimitive} and is not a valid
* short value.
* @throws IllegalStateException if the element is of the type {@link JsonArray} but contains
* more than a single element.
*/
public short getAsShort() {
throw new UnsupportedOperationException(getClass().getSimpleName());
}
/**
* Returns a String representation of this element.
*/
@Override
public String toString() {
try {
StringWriter stringWriter = new StringWriter();
JsonWriter jsonWriter = new JsonWriter(stringWriter);
jsonWriter.setLenient(true);
Streams.write(this, jsonWriter);
return stringWriter.toString();
} catch (IOException e) {
throw new AssertionError(e);
}
}
}

View File

@ -0,0 +1,47 @@
/*
* Copyright (C) 2008 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.massivecraft.core.lib.gson2;
import java.io.IOException;
/**
* Definition of a visitor for a JsonElement tree.
*
* @author Inderjeet Singh
*/
interface JsonElementVisitor {
void visitPrimitive(JsonPrimitive primitive) throws IOException;
void visitNull() throws IOException;
void startArray(JsonArray array) throws IOException;
void visitArrayMember(JsonArray parent, JsonPrimitive member, boolean isFirst) throws IOException;
void visitArrayMember(JsonArray parent, JsonArray member, boolean isFirst) throws IOException;
void visitArrayMember(JsonArray parent, JsonObject member, boolean isFirst) throws IOException;
void visitNullArrayMember(JsonArray parent, boolean isFirst) throws IOException;
void endArray(JsonArray array) throws IOException;
void startObject(JsonObject object) throws IOException;
void visitObjectMember(JsonObject parent, String memberName, JsonPrimitive member,
boolean isFirst) throws IOException;
void visitObjectMember(JsonObject parent, String memberName, JsonArray member,
boolean isFirst) throws IOException;
void visitObjectMember(JsonObject parent, String memberName, JsonObject member,
boolean isFirst) throws IOException;
void visitNullObjectMember(JsonObject parent, String memberName,
boolean isFirst) throws IOException;
void endObject(JsonObject object) throws IOException;
}

View File

@ -0,0 +1,46 @@
/*
* Copyright (C) 2008 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.massivecraft.core.lib.gson2;
/**
* This exception is raised when Gson was unable to read an input stream
* or write to one.
*
* @author Inderjeet Singh
* @author Joel Leitch
*/
public final class JsonIOException extends JsonParseException {
private static final long serialVersionUID = 1L;
public JsonIOException(String msg) {
super(msg);
}
public JsonIOException(String msg, Throwable cause) {
super(msg, cause);
}
/**
* Creates exception with the specified cause. Consider using
* {@link #JsonIOException(String, Throwable)} instead if you can describe what happened.
*
* @param cause root exception that caused this exception to be thrown.
*/
public JsonIOException(Throwable cause) {
super(cause);
}
}

View File

@ -0,0 +1,58 @@
/*
* Copyright (C) 2008 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.massivecraft.core.lib.gson2;
/**
* A class representing a Json {@code null} value.
*
* @author Inderjeet Singh
* @author Joel Leitch
* @since 1.2
*/
public final class JsonNull extends JsonElement {
/**
* singleton for JsonNull
*
* @since 1.8
*/
public static final JsonNull INSTANCE = new JsonNull();
/**
* Creates a new JsonNull object.
* Deprecated since Gson version 1.8. Use {@link #INSTANCE} instead
*/
@Deprecated
public JsonNull() {
// Do nothing
}
/**
* All instances of JsonNull have the same hash code since they are indistinguishable
*/
@Override
public int hashCode() {
return JsonNull.class.hashCode();
}
/**
* All instances of JsonNull are the same
*/
@Override
public boolean equals(Object other) {
return this == other || other instanceof JsonNull;
}
}

View File

@ -0,0 +1,200 @@
/*
* Copyright (C) 2008 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.massivecraft.core.lib.gson2;
import com.massivecraft.core.lib.gson2.internal.$Gson$Preconditions;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;
/**
* A class representing an object type in Json. An object consists of name-value pairs where names
* are strings, and values are any other type of {@link JsonElement}. This allows for a creating a
* tree of JsonElements. The member elements of this object are maintained in order they were added.
*
* @author Inderjeet Singh
* @author Joel Leitch
*/
public final class JsonObject extends JsonElement {
// We are using a linked hash map because it is important to preserve
// the order in which elements are inserted. This is needed to ensure
// that the fields of an object are inserted in the order they were
// defined in the class.
private final Map<String, JsonElement> members = new LinkedHashMap<String, JsonElement>();
/**
* Creates an empty JsonObject.
*/
public JsonObject() {
}
/**
* Adds a member, which is a name-value pair, to self. The name must be a String, but the value
* can be an arbitrary JsonElement, thereby allowing you to build a full tree of JsonElements
* rooted at this node.
*
* @param property name of the member.
* @param value the member object.
*/
public void add(String property, JsonElement value) {
if (value == null) {
value = JsonNull.INSTANCE;
}
members.put($Gson$Preconditions.checkNotNull(property), value);
}
/**
* Removes the {@code property} from this {@link JsonObject}.
*
* @param property name of the member that should be removed.
* @return the {@link JsonElement} object that is being removed.
* @since 1.3
*/
public JsonElement remove(String property) {
return members.remove(property);
}
/**
* Convenience method to add a primitive member. The specified value is converted to a
* JsonPrimitive of String.
*
* @param property name of the member.
* @param value the string value associated with the member.
*/
public void addProperty(String property, String value) {
add(property, createJsonElement(value));
}
/**
* Convenience method to add a primitive member. The specified value is converted to a
* JsonPrimitive of Number.
*
* @param property name of the member.
* @param value the number value associated with the member.
*/
public void addProperty(String property, Number value) {
add(property, createJsonElement(value));
}
/**
* Convenience method to add a boolean member. The specified value is converted to a
* JsonPrimitive of Boolean.
*
* @param property name of the member.
* @param value the number value associated with the member.
*/
public void addProperty(String property, Boolean value) {
add(property, createJsonElement(value));
}
/**
* Convenience method to add a char member. The specified value is converted to a
* JsonPrimitive of Character.
*
* @param property name of the member.
* @param value the number value associated with the member.
*/
public void addProperty(String property, Character value) {
add(property, createJsonElement(value));
}
/**
* Creates the proper {@link JsonElement} object from the given {@code value} object.
*
* @param value the object to generate the {@link JsonElement} for
* @return a {@link JsonPrimitive} if the {@code value} is not null, otherwise a {@link JsonNull}
*/
private JsonElement createJsonElement(Object value) {
return value == null ? JsonNull.INSTANCE : new JsonPrimitive(value);
}
/**
* Returns a set of members of this object. The set is ordered, and the order is in which the
* elements were added.
*
* @return a set of members of this object.
*/
public Set<Map.Entry<String, JsonElement>> entrySet() {
return members.entrySet();
}
/**
* Convenience method to check if a member with the specified name is present in this object.
*
* @param memberName name of the member that is being checked for presence.
* @return true if there is a member with the specified name, false otherwise.
*/
public boolean has(String memberName) {
return members.containsKey(memberName);
}
/**
* Returns the member with the specified name.
*
* @param memberName name of the member that is being requested.
* @return the member matching the name. Null if no such member exists.
*/
public JsonElement get(String memberName) {
if (members.containsKey(memberName)) {
JsonElement member = members.get(memberName);
return member == null ? JsonNull.INSTANCE : member;
}
return null;
}
/**
* Convenience method to get the specified member as a JsonPrimitive element.
*
* @param memberName name of the member being requested.
* @return the JsonPrimitive corresponding to the specified member.
*/
public JsonPrimitive getAsJsonPrimitive(String memberName) {
return (JsonPrimitive) members.get(memberName);
}
/**
* Convenience method to get the specified member as a JsonArray.
*
* @param memberName name of the member being requested.
* @return the JsonArray corresponding to the specified member.
*/
public JsonArray getAsJsonArray(String memberName) {
return (JsonArray) members.get(memberName);
}
/**
* Convenience method to get the specified member as a JsonObject.
*
* @param memberName name of the member being requested.
* @return the JsonObject corresponding to the specified member.
*/
public JsonObject getAsJsonObject(String memberName) {
return (JsonObject) members.get(memberName);
}
@Override
public boolean equals(Object o) {
return (o == this) || (o instanceof JsonObject
&& ((JsonObject) o).members.equals(members));
}
@Override
public int hashCode() {
return members.hashCode();
}
}

View File

@ -0,0 +1,64 @@
/*
* Copyright (C) 2008 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.massivecraft.core.lib.gson2;
/**
* This exception is raised if there is a serious issue that occurs during parsing of a Json
* string. One of the main usages for this class is for the Gson infrastructure. If the incoming
* Json is bad/malicious, an instance of this exception is raised.
*
* <p>This exception is a {@link RuntimeException} because it is exposed to the client. Using a
* {@link RuntimeException} avoids bad coding practices on the client side where they catch the
* exception and do nothing. It is often the case that you want to blow up if there is a parsing
* error (i.e. often clients do not know how to recover from a {@link JsonParseException}.</p>
*
* @author Inderjeet Singh
* @author Joel Leitch
*/
public class JsonParseException extends RuntimeException {
static final long serialVersionUID = -4086729973971783390L;
/**
* Creates exception with the specified message. If you are wrapping another exception, consider
* using {@link #JsonParseException(String, Throwable)} instead.
*
* @param msg error message describing a possible cause of this exception.
*/
public JsonParseException(String msg) {
super(msg);
}
/**
* Creates exception with the specified message and cause.
*
* @param msg error message describing what happened.
* @param cause root exception that caused this exception to be thrown.
*/
public JsonParseException(String msg, Throwable cause) {
super(msg, cause);
}
/**
* Creates exception with the specified cause. Consider using
* {@link #JsonParseException(String, Throwable)} instead if you can describe what happened.
*
* @param cause root exception that caused this exception to be thrown.
*/
public JsonParseException(Throwable cause) {
super(cause);
}
}

View File

@ -0,0 +1,99 @@
/*
* Copyright (C) 2009 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.massivecraft.core.lib.gson2;
import com.massivecraft.core.lib.gson2.internal.Streams;
import com.massivecraft.core.lib.gson2.stream.JsonReader;
import com.massivecraft.core.lib.gson2.stream.JsonToken;
import com.massivecraft.core.lib.gson2.stream.MalformedJsonException;
import java.io.EOFException;
import java.io.IOException;
import java.io.Reader;
import java.io.StringReader;
/**
* A parser to parse Json into a parse tree of {@link JsonElement}s
*
* @author Inderjeet Singh
* @author Joel Leitch
* @since 1.3
*/
public final class JsonParser {
/**
* Parses the specified JSON string into a parse tree
*
* @param json JSON text
* @return a parse tree of {@link JsonElement}s corresponding to the specified JSON
* @throws JsonParseException if the specified text is not valid JSON
* @since 1.3
*/
public JsonElement parse(String json) throws JsonSyntaxException {
return parse(new StringReader(json));
}
/**
* Parses the specified JSON string into a parse tree
*
* @param json JSON text
* @return a parse tree of {@link JsonElement}s corresponding to the specified JSON
* @throws JsonParseException if the specified text is not valid JSON
* @since 1.3
*/
public JsonElement parse(Reader json) throws JsonIOException, JsonSyntaxException {
try {
JsonReader jsonReader = new JsonReader(json);
JsonElement element = parse(jsonReader);
if (!element.isJsonNull() && jsonReader.peek() != JsonToken.END_DOCUMENT) {
throw new JsonSyntaxException("Did not consume the entire document.");
}
return element;
} catch (MalformedJsonException e) {
throw new JsonSyntaxException(e);
} catch (IOException e) {
throw new JsonIOException(e);
} catch (NumberFormatException e) {
throw new JsonSyntaxException(e);
}
}
/**
* Returns the next value from the JSON stream as a parse tree.
*
* @throws JsonParseException if there is an IOException or if the specified
* text is not valid JSON
* @since 1.6
*/
public JsonElement parse(JsonReader json) throws JsonIOException, JsonSyntaxException {
boolean lenient = json.isLenient();
json.setLenient(true);
try {
return Streams.parse(json);
} catch (StackOverflowError e) {
throw new JsonParseException("Failed parsing JSON source: " + json + " to Json", e);
} catch (OutOfMemoryError e) {
throw new JsonParseException("Failed parsing JSON source: " + json + " to Json", e);
} catch (JsonParseException e) {
if (e.getCause() instanceof EOFException) {
return JsonNull.INSTANCE;
}
throw e;
} finally {
json.setLenient(lenient);
}
}
}

View File

@ -0,0 +1,336 @@
/*
* Copyright (C) 2008 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.massivecraft.core.lib.gson2;
import com.massivecraft.core.lib.gson2.internal.$Gson$Preconditions;
import com.massivecraft.core.lib.gson2.internal.LazilyParsedNumber;
import java.math.BigDecimal;
import java.math.BigInteger;
/**
* A class representing a Json primitive value. A primitive value
* is either a String, a Java primitive, or a Java primitive
* wrapper type.
*
* @author Inderjeet Singh
* @author Joel Leitch
*/
public final class JsonPrimitive extends JsonElement {
private static final Class<?>[] PRIMITIVE_TYPES = { int.class, long.class, short.class,
float.class, double.class, byte.class, boolean.class, char.class, Integer.class, Long.class,
Short.class, Float.class, Double.class, Byte.class, Boolean.class, Character.class };
private Object value;
/**
* Create a primitive containing a boolean value.
*
* @param bool the value to create the primitive with.
*/
public JsonPrimitive(Boolean bool) {
setValue(bool);
}
/**
* Create a primitive containing a {@link Number}.
*
* @param number the value to create the primitive with.
*/
public JsonPrimitive(Number number) {
setValue(number);
}
/**
* Create a primitive containing a String value.
*
* @param string the value to create the primitive with.
*/
public JsonPrimitive(String string) {
setValue(string);
}
/**
* Create a primitive containing a character. The character is turned into a one character String
* since Json only supports String.
*
* @param c the value to create the primitive with.
*/
public JsonPrimitive(Character c) {
setValue(c);
}
/**
* Create a primitive using the specified Object. It must be an instance of {@link Number}, a
* Java primitive type, or a String.
*
* @param primitive the value to create the primitive with.
*/
JsonPrimitive(Object primitive) {
setValue(primitive);
}
void setValue(Object primitive) {
if (primitive instanceof Character) {
// convert characters to strings since in JSON, characters are represented as a single
// character string
char c = ((Character) primitive).charValue();
this.value = String.valueOf(c);
} else {
$Gson$Preconditions.checkArgument(primitive instanceof Number
|| isPrimitiveOrString(primitive));
this.value = primitive;
}
}
/**
* Check whether this primitive contains a boolean value.
*
* @return true if this primitive contains a boolean value, false otherwise.
*/
public boolean isBoolean() {
return value instanceof Boolean;
}
/**
* convenience method to get this element as a {@link Boolean}.
*
* @return get this element as a {@link Boolean}.
*/
@Override
Boolean getAsBooleanWrapper() {
return (Boolean) value;
}
/**
* convenience method to get this element as a boolean value.
*
* @return get this element as a primitive boolean value.
*/
@Override
public boolean getAsBoolean() {
if (isBoolean()) {
return getAsBooleanWrapper().booleanValue();
} else {
// Check to see if the value as a String is "true" in any case.
return Boolean.parseBoolean(getAsString());
}
}
/**
* Check whether this primitive contains a Number.
*
* @return true if this primitive contains a Number, false otherwise.
*/
public boolean isNumber() {
return value instanceof Number;
}
/**
* convenience method to get this element as a Number.
*
* @return get this element as a Number.
* @throws NumberFormatException if the value contained is not a valid Number.
*/
@Override
public Number getAsNumber() {
return value instanceof String ? new LazilyParsedNumber((String) value) : (Number) value;
}
/**
* Check whether this primitive contains a String value.
*
* @return true if this primitive contains a String value, false otherwise.
*/
public boolean isString() {
return value instanceof String;
}
/**
* convenience method to get this element as a String.
*
* @return get this element as a String.
*/
@Override
public String getAsString() {
if (isNumber()) {
return getAsNumber().toString();
} else if (isBoolean()) {
return getAsBooleanWrapper().toString();
} else {
return (String) value;
}
}
/**
* convenience method to get this element as a primitive double.
*
* @return get this element as a primitive double.
* @throws NumberFormatException if the value contained is not a valid double.
*/
@Override
public double getAsDouble() {
return isNumber() ? getAsNumber().doubleValue() : Double.parseDouble(getAsString());
}
/**
* convenience method to get this element as a {@link BigDecimal}.
*
* @return get this element as a {@link BigDecimal}.
* @throws NumberFormatException if the value contained is not a valid {@link BigDecimal}.
*/
@Override
public BigDecimal getAsBigDecimal() {
return value instanceof BigDecimal ? (BigDecimal) value : new BigDecimal(value.toString());
}
/**
* convenience method to get this element as a {@link BigInteger}.
*
* @return get this element as a {@link BigInteger}.
* @throws NumberFormatException if the value contained is not a valid {@link BigInteger}.
*/
@Override
public BigInteger getAsBigInteger() {
return value instanceof BigInteger ?
(BigInteger) value : new BigInteger(value.toString());
}
/**
* convenience method to get this element as a float.
*
* @return get this element as a float.
* @throws NumberFormatException if the value contained is not a valid float.
*/
@Override
public float getAsFloat() {
return isNumber() ? getAsNumber().floatValue() : Float.parseFloat(getAsString());
}
/**
* convenience method to get this element as a primitive long.
*
* @return get this element as a primitive long.
* @throws NumberFormatException if the value contained is not a valid long.
*/
@Override
public long getAsLong() {
return isNumber() ? getAsNumber().longValue() : Long.parseLong(getAsString());
}
/**
* convenience method to get this element as a primitive short.
*
* @return get this element as a primitive short.
* @throws NumberFormatException if the value contained is not a valid short value.
*/
@Override
public short getAsShort() {
return isNumber() ? getAsNumber().shortValue() : Short.parseShort(getAsString());
}
/**
* convenience method to get this element as a primitive integer.
*
* @return get this element as a primitive integer.
* @throws NumberFormatException if the value contained is not a valid integer.
*/
@Override
public int getAsInt() {
return isNumber() ? getAsNumber().intValue() : Integer.parseInt(getAsString());
}
@Override
public byte getAsByte() {
return isNumber() ? getAsNumber().byteValue() : Byte.parseByte(getAsString());
}
@Override
public char getAsCharacter() {
return getAsString().charAt(0);
}
private static boolean isPrimitiveOrString(Object target) {
if (target instanceof String) {
return true;
}
Class<?> classOfPrimitive = target.getClass();
for (Class<?> standardPrimitive : PRIMITIVE_TYPES) {
if (standardPrimitive.isAssignableFrom(classOfPrimitive)) {
return true;
}
}
return false;
}
@Override
public int hashCode() {
if (value == null) {
return 31;
}
// Using recommended hashing algorithm from Effective Java for longs and doubles
if (isIntegral(this)) {
long value = getAsNumber().longValue();
return (int) (value ^ (value >>> 32));
}
if (value instanceof Number) {
long value = Double.doubleToLongBits(getAsNumber().doubleValue());
return (int) (value ^ (value >>> 32));
}
return value.hashCode();
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null || getClass() != obj.getClass()) {
return false;
}
JsonPrimitive other = (JsonPrimitive)obj;
if (value == null) {
return other.value == null;
}
if (isIntegral(this) && isIntegral(other)) {
return getAsNumber().longValue() == other.getAsNumber().longValue();
}
if (value instanceof Number && other.value instanceof Number) {
double a = getAsNumber().doubleValue();
// Java standard types other than double return true for two NaN. So, need
// special handling for double.
double b = other.getAsNumber().doubleValue();
return a == b || (Double.isNaN(a) && Double.isNaN(b));
}
return value.equals(other.value);
}
/**
* Returns true if the specified number is an integral type
* (Long, Integer, Short, Byte, BigInteger)
*/
private static boolean isIntegral(JsonPrimitive primitive) {
if (primitive.value instanceof Number) {
Number number = (Number) primitive.value;
return number instanceof BigInteger || number instanceof Long || number instanceof Integer
|| number instanceof Short || number instanceof Byte;
}
return false;
}
}

View File

@ -0,0 +1,49 @@
/*
* Copyright (C) 2008 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.massivecraft.core.lib.gson2;
import java.lang.reflect.Type;
/**
* Context for serialization that is passed to a custom serializer during invocation of its
* {@link JsonSerializer#serialize(Object, Type, JsonSerializationContext)} method.
*
* @author Inderjeet Singh
* @author Joel Leitch
*/
public interface JsonSerializationContext {
/**
* Invokes default serialization on the specified object.
*
* @param src the object that needs to be serialized.
* @return a tree of {@link JsonElement}s corresponding to the serialized form of {@code src}.
*/
public JsonElement serialize(Object src);
/**
* Invokes default serialization on the specified object passing the specific type information.
* It should never be invoked on the element received as a parameter of the
* {@link JsonSerializer#serialize(Object, Type, JsonSerializationContext)} method. Doing
* so will result in an infinite loop since Gson will in-turn call the custom serializer again.
*
* @param src the object that needs to be serialized.
* @param typeOfSrc the actual genericized type of src object.
* @return a tree of {@link JsonElement}s corresponding to the serialized form of {@code src}.
*/
public JsonElement serialize(Object src, Type typeOfSrc);
}

View File

@ -0,0 +1,86 @@
/*
* Copyright (C) 2008 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.massivecraft.core.lib.gson2;
import java.lang.reflect.Type;
/**
* Interface representing a custom serializer for Json. You should write a custom serializer, if
* you are not happy with the default serialization done by Gson. You will also need to register
* this serializer through {@link com.massivecraft.core.lib.gson2.GsonBuilder#registerTypeAdapter(Type, Object)}.
*
* <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>
*
* <p><pre>
* public class Id&lt;T&gt; {
* private final Class&lt;T&gt; clazz;
* private final long value;
*
* public Id(Class&lt;T&gt; clazz, long value) {
* this.clazz = clazz;
* this.value = value;
* }
*
* public long getValue() {
* return value;
* }
* }
* </pre></p>
*
* <p>The default serialization of {@code Id(com.foo.MyObject.class, 20L)} will be
* <code>{"clazz":com.foo.MyObject,"value":20}</code>. Suppose, you just want the output to be
* the value instead, which is {@code 20} in this case. You can achieve that by writing a custom
* serializer:</p>
*
* <p><pre>
* class IdSerializer implements JsonSerializer&lt;Id&gt;() {
* public JsonElement serialize(Id id, Type typeOfId, JsonSerializationContext context) {
* return new JsonPrimitive(id.getValue());
* }
* }
* </pre></p>
*
* <p>You will also need to register {@code IdSerializer} with Gson as follows:</p>
* <pre>
* Gson gson = new GsonBuilder().registerTypeAdapter(Id.class, new IdSerializer()).create();
* </pre>
*
* @author Inderjeet Singh
* @author Joel Leitch
*
* @param <T> type for which the serializer is being registered. It is possible that a serializer
* may be asked to serialize a specific generic type of the T.
*/
public interface JsonSerializer<T> {
/**
* Gson invokes this call-back method during serialization when it encounters a field of the
* specified type.
*
* <p>In the implementation of this call-back method, you should consider invoking
* {@link JsonSerializationContext#serialize(Object, Type)} method to create JsonElements for any
* non-trivial field of the {@code src} object. However, you should never invoke it on the
* {@code src} object itself since that will cause an infinite loop (Gson will call your
* call-back method again).</p>
*
* @param src the object that needs to be converted to Json.
* @param typeOfSrc the actual type (fully genericized version) of the source object.
* @return a JsonElement corresponding to the specified object.
*/
public JsonElement serialize(T src, Type typeOfSrc, JsonSerializationContext context);
}

View File

@ -0,0 +1,123 @@
/*
* Copyright (C) 2009 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.massivecraft.core.lib.gson2;
import java.io.EOFException;
import java.io.IOException;
import java.io.Reader;
import java.io.StringReader;
import java.util.Iterator;
import java.util.NoSuchElementException;
import com.massivecraft.core.lib.gson2.internal.Streams;
import com.massivecraft.core.lib.gson2.stream.JsonReader;
import com.massivecraft.core.lib.gson2.stream.JsonToken;
import com.massivecraft.core.lib.gson2.stream.MalformedJsonException;
/**
* A streaming parser that allows reading of multiple {@link JsonElement}s from the specified reader
* asynchronously.
*
* <p>This class is conditionally thread-safe (see Item 70, Effective Java second edition). To
* properly use this class across multiple threads, you will need to add some external
* synchronization. For example:
*
* <pre>
* JsonStreamParser parser = new JsonStreamParser("['first'] {'second':10} 'third'");
* JsonElement element;
* synchronized (parser) { // synchronize on an object shared by threads
* if (parser.hasNext()) {
* element = parser.next();
* }
* }
* </pre>
*
* @author Inderjeet Singh
* @author Joel Leitch
* @since 1.4
*/
public final class JsonStreamParser implements Iterator<JsonElement> {
private final JsonReader parser;
private final Object lock;
/**
* @param json The string containing JSON elements concatenated to each other.
* @since 1.4
*/
public JsonStreamParser(String json) {
this(new StringReader(json));
}
/**
* @param reader The data stream containing JSON elements concatenated to each other.
* @since 1.4
*/
public JsonStreamParser(Reader reader) {
parser = new JsonReader(reader);
parser.setLenient(true);
lock = new Object();
}
/**
* Returns the next available {@link JsonElement} on the reader. Null if none available.
*
* @return the next available {@link JsonElement} on the reader. Null if none available.
* @throws JsonParseException if the incoming stream is malformed JSON.
* @since 1.4
*/
public JsonElement next() throws JsonParseException {
if (!hasNext()) {
throw new NoSuchElementException();
}
try {
return Streams.parse(parser);
} catch (StackOverflowError e) {
throw new JsonParseException("Failed parsing JSON source to Json", e);
} catch (OutOfMemoryError e) {
throw new JsonParseException("Failed parsing JSON source to Json", e);
} catch (JsonParseException e) {
throw e.getCause() instanceof EOFException ? new NoSuchElementException() : e;
}
}
/**
* Returns true if a {@link JsonElement} is available on the input for consumption
* @return true if a {@link JsonElement} is available on the input, false otherwise
* @since 1.4
*/
public boolean hasNext() {
synchronized (lock) {
try {
return parser.peek() != JsonToken.END_DOCUMENT;
} catch (MalformedJsonException e) {
throw new JsonSyntaxException(e);
} catch (IOException e) {
throw new JsonIOException(e);
}
}
}
/**
* This optional {@link Iterator} method is not relevant for stream parsing and hence is not
* implemented.
* @since 1.4
*/
public void remove() {
throw new UnsupportedOperationException();
}
}

View File

@ -0,0 +1,47 @@
/*
* Copyright (C) 2010 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.massivecraft.core.lib.gson2;
/**
* This exception is raised when Gson attempts to read (or write) a malformed
* JSON element.
*
* @author Inderjeet Singh
* @author Joel Leitch
*/
public final class JsonSyntaxException extends JsonParseException {
private static final long serialVersionUID = 1L;
public JsonSyntaxException(String msg) {
super(msg);
}
public JsonSyntaxException(String msg, Throwable cause) {
super(msg, cause);
}
/**
* Creates exception with the specified cause. Consider using
* {@link #JsonSyntaxException(String, Throwable)} instead if you can
* describe what actually happened.
*
* @param cause root exception that caused this exception to be thrown.
*/
public JsonSyntaxException(Throwable cause) {
super(cause);
}
}

View File

@ -0,0 +1,74 @@
/*
* Copyright (C) 2009 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.massivecraft.core.lib.gson2;
/**
* Defines the expected format for a {@code long} or {@code Long} type when its serialized.
*
* @since 1.3
*
* @author Inderjeet Singh
* @author Joel Leitch
*/
public enum LongSerializationPolicy {
/**
* This is the "default" serialization policy that will output a {@code long} object as a JSON
* number. For example, assume an object has a long field named "f" then the serialized output
* would be:
* {@code {"f":123}}.
*/
DEFAULT(new DefaultStrategy()),
/**
* Serializes a long value as a quoted string. For example, assume an object has a long field
* named "f" then the serialized output would be:
* {@code {"f":"123"}}.
*/
STRING(new StringStrategy());
private final Strategy strategy;
private LongSerializationPolicy(Strategy strategy) {
this.strategy = strategy;
}
/**
* Serialize this {@code value} using this serialization policy.
*
* @param value the long value to be serialized into a {@link JsonElement}
* @return the serialized version of {@code value}
*/
public JsonElement serialize(Long value) {
return strategy.serialize(value);
}
private interface Strategy {
JsonElement serialize(Long value);
}
private static class DefaultStrategy implements Strategy {
public JsonElement serialize(Long value) {
return new JsonPrimitive(value);
}
}
private static class StringStrategy implements Strategy {
public JsonElement serialize(Long value) {
return new JsonPrimitive(String.valueOf(value));
}
}
}

View File

@ -0,0 +1,43 @@
/*
* Copyright (C) 2008 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.massivecraft.core.lib.gson2;
/**
* A {@link FieldNamingStrategy2} that ensures the JSON field names consist of only
* lower case letters and are separated by a particular {@code separatorString}.
*
*<p>The following is an example:</p>
* <pre>
* class StringWrapper {
* public String AStringField = "abcd";
* }
*
* LowerCamelCaseSeparatorNamingPolicy policy = new LowerCamelCaseSeparatorNamingPolicy("_");
* String translatedFieldName =
* policy.translateName(StringWrapper.class.getField("AStringField"));
*
* assert("a_string_field".equals(translatedFieldName));
* </pre>
*
* @author Joel Leitch
*/
final class LowerCamelCaseSeparatorNamingPolicy extends CompositionFieldNamingPolicy {
public LowerCamelCaseSeparatorNamingPolicy(String separatorString) {
super(new CamelCaseSeparatorNamingPolicy(separatorString), new LowerCaseNamingPolicy());
}
}

View File

@ -0,0 +1,50 @@
/*
* Copyright (C) 2008 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.massivecraft.core.lib.gson2;
import java.lang.annotation.Annotation;
import java.lang.reflect.Type;
import java.util.Collection;
/**
* A {@link FieldNamingStrategy2} that ensures the JSON field names consist of only
* lower case letters.
*
* <p>The following is an example:</p>
* <pre>
* class IntWrapper {
* public int integerField = 0;
* }
*
* LowerCaseNamingPolicy policy = new LowerCaseNamingPolicy();
* String translatedFieldName =
* policy.translateName(IntWrapper.class.getField("integerField"));
*
* assert("integerfield".equals(translatedFieldName));
* </pre>
*
* @author Inderjeet Singh
* @author Joel Leitch
*/
final class LowerCaseNamingPolicy extends RecursiveFieldNamingPolicy {
@Override
protected String translateName(String target, Type fieldType,
Collection<Annotation> annotations) {
return target.toLowerCase();
}
}

View File

@ -0,0 +1,53 @@
/*
* Copyright (C) 2010 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.massivecraft.core.lib.gson2;
import java.util.LinkedHashMap;
import java.util.Map;
/**
* An implementation of the {@link Cache} interface that evict objects from the cache using an
* LRU (least recently used) algorithm. Object start getting evicted from the cache once the
* {@code maxCapacity} is reached.
*
* @author Inderjeet Singh
* @author Joel Leitch
*/
final class LruCache<K, V> extends LinkedHashMap<K, V> implements Cache<K, V> {
private static final long serialVersionUID = 1L;
private final int maxCapacity;
public LruCache(int maxCapacity) {
super(maxCapacity, 0.7F, true);
this.maxCapacity = maxCapacity;
}
public synchronized void addElement(K key, V value) {
put(key, value);
}
public synchronized V getElement(K key) {
return get(key);
}
@Override
protected boolean removeEldestEntry(Map.Entry<K, V> entry) {
return size() > maxCapacity;
}
}

View File

@ -0,0 +1,53 @@
/*
* Copyright (C) 2008 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.massivecraft.core.lib.gson2;
import java.util.Collection;
import java.util.HashSet;
/**
* Exclude fields based on particular field modifiers. For a list of possible
* modifiers, see {@link java.lang.reflect.Modifier}.
*
* @author Inderjeet Singh
* @author Joel Leitch
*/
final class ModifierBasedExclusionStrategy implements ExclusionStrategy {
private final Collection<Integer> modifiers;
public ModifierBasedExclusionStrategy(int... modifiers) {
this.modifiers = new HashSet<Integer>();
if (modifiers != null) {
for (int modifier : modifiers) {
this.modifiers.add(modifier);
}
}
}
public boolean shouldSkipField(FieldAttributes f) {
for (int modifier : modifiers) {
if (f.hasModifier(modifier)) {
return true;
}
}
return false;
}
public boolean shouldSkipClass(Class<?> clazz) {
return false;
}
}

View File

@ -0,0 +1,108 @@
/*
* Copyright (C) 2008 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.massivecraft.core.lib.gson2;
import com.massivecraft.core.lib.gson2.internal.$Gson$Preconditions;
import java.lang.annotation.Annotation;
import java.lang.reflect.Type;
import java.util.Collection;
/**
* A {@link FieldNamingStrategy2} that ensures the JSON field names begins with
* an upper case letter.
*
*<p>The following is an example:</p>
* <pre>
* class StringWrapper {
* public String stringField = "abcd";
* public String _stringField = "efg";
* }
*
* ModifyFirstLetterNamingPolicy policy =
* new ModifyFirstLetterNamingPolicy(LetterModifier.UPPER);
* String translatedFieldName =
* policy.translateName(StringWrapper.class.getField("stringField"));
*
* assert("StringField".equals(translatedFieldName));
*
* String translatedFieldName =
* policy.translateName(StringWrapper.class.getField("_stringField"));
*
* assert("_StringField".equals(translatedFieldName));
* </pre>
*
* @author Joel Leitch
*/
final class ModifyFirstLetterNamingPolicy extends RecursiveFieldNamingPolicy {
public enum LetterModifier {
UPPER,
LOWER;
}
private final LetterModifier letterModifier;
/**
* Creates a new ModifyFirstLetterNamingPolicy that will either modify the first letter of the
* target name to either UPPER case or LOWER case depending on the {@code modifier} parameter.
*
* @param modifier the type of modification that should be performed
* @throws IllegalArgumentException if {@code modifier} is null
*/
ModifyFirstLetterNamingPolicy(LetterModifier modifier) {
this.letterModifier = $Gson$Preconditions.checkNotNull(modifier);
}
@Override
protected String translateName(String target, Type fieldType,
Collection<Annotation> annotations) {
StringBuilder fieldNameBuilder = new StringBuilder();
int index = 0;
char firstCharacter = target.charAt(index);
while (index < target.length() - 1) {
if (Character.isLetter(firstCharacter)) {
break;
}
fieldNameBuilder.append(firstCharacter);
firstCharacter = target.charAt(++index);
}
if (index == target.length()) {
return fieldNameBuilder.toString();
}
boolean capitalizeFirstLetter = (letterModifier == LetterModifier.UPPER);
if (capitalizeFirstLetter && !Character.isUpperCase(firstCharacter)) {
String modifiedTarget = modifyString(Character.toUpperCase(firstCharacter), target, ++index);
return fieldNameBuilder.append(modifiedTarget).toString();
} else if (!capitalizeFirstLetter && Character.isUpperCase(firstCharacter)) {
String modifiedTarget = modifyString(Character.toLowerCase(firstCharacter), target, ++index);
return fieldNameBuilder.append(modifiedTarget).toString();
} else {
return target;
}
}
private String modifyString(char firstCharacter, String srcString, int indexOfSubstring) {
return (indexOfSubstring < srcString.length())
? firstCharacter + srcString.substring(indexOfSubstring)
: String.valueOf(firstCharacter);
}
}

View File

@ -0,0 +1,45 @@
/*
* Copyright (C) 2008 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.massivecraft.core.lib.gson2;
import java.lang.annotation.Annotation;
import java.lang.reflect.Type;
import java.util.Collection;
/**
* A mechanism for providing custom field naming in Gson. This allows the client code to translate
* field names into a particular convention that is not supported as a normal Java field
* declaration rules. For example, Java does not support "-" characters in a field name.
*
* @author Joel Leitch
*/
abstract class RecursiveFieldNamingPolicy implements FieldNamingStrategy2 {
public final String translateName(FieldAttributes f) {
return translateName(f.getName(), f.getDeclaredType(), f.getAnnotations());
}
/**
* Performs the specific string translation.
*
* @param target the string object that will be manipulation/translated
* @param fieldType the actual type value of the field
* @param annotations the annotations set on the field
* @return the translated field name
*/
protected abstract String translateName(String target, Type fieldType, Collection<Annotation> annotations);
}

View File

@ -0,0 +1,48 @@
/*
* Copyright (C) 2008 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.massivecraft.core.lib.gson2;
import com.massivecraft.core.lib.gson2.annotations.SerializedName;
/**
* A {@link FieldNamingStrategy2} that acts as a chain of responsibility. If the
* {@link com.massivecraft.core.lib.gson2.annotations.SerializedName} annotation is applied to a
* field then this strategy will translate the name to the {@code
* serializedName.value()}; otherwise it delegates to the wrapped
* {@link FieldNamingStrategy2}.
*
* <p>
* NOTE: this class performs JSON field name validation for any of the fields
* marked with an {@code @SerializedName} annotation.
* </p>
*
* @see SerializedName
*
* @author Joel Leitch
*/
final class SerializedNameAnnotationInterceptingNamingPolicy implements FieldNamingStrategy2 {
private final FieldNamingStrategy2 delegate;
SerializedNameAnnotationInterceptingNamingPolicy(FieldNamingStrategy2 delegate) {
this.delegate = delegate;
}
public String translateName(FieldAttributes f) {
SerializedName serializedName = f.getAnnotation(SerializedName.class);
return serializedName == null ? delegate.translateName(f) : serializedName.value();
}
}

View File

@ -0,0 +1,44 @@
/*
* Copyright (C) 2009 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.massivecraft.core.lib.gson2;
/**
* A data object that stores attributes of a field.
*
* <p>This class is immutable; therefore, it can be safely shared across threads.
*
* @author Inderjeet Singh
* @author Joel Leitch
*
* @since 1.4
*/
final class SyntheticFieldExclusionStrategy implements ExclusionStrategy {
private final boolean skipSyntheticFields;
SyntheticFieldExclusionStrategy(boolean skipSyntheticFields) {
this.skipSyntheticFields = skipSyntheticFields;
}
public boolean shouldSkipClass(Class<?> clazz) {
return false;
}
public boolean shouldSkipField(FieldAttributes f) {
return skipSyntheticFields && f.isSynthetic();
}
}

View File

@ -0,0 +1,45 @@
/*
* Copyright (C) 2008 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.massivecraft.core.lib.gson2;
/**
* A {@link FieldNamingStrategy2} that ensures the JSON field names consist of mixed
* case letters starting with a capital and are separated by a particular
* {@code separatorString}.
*
*<p>The following is an example:</p>
* <pre>
* class StringWrapper {
* public String AStringField = "abcd";
* }
*
* UpperCamelCaseSeparatorNamingPolicy policy = new UpperCamelCaseSeparatorNamingPolicy("_");
* String translatedFieldName =
* policy.translateName(StringWrapper.class.getField("AStringField"));
*
* assert("A_String_Field".equals(translatedFieldName));
* </pre>
*
* @author Joel Leitch
*/
final class UpperCamelCaseSeparatorNamingPolicy extends CompositionFieldNamingPolicy {
public UpperCamelCaseSeparatorNamingPolicy(String separatorString) {
super(new CamelCaseSeparatorNamingPolicy(separatorString),
new ModifyFirstLetterNamingPolicy(ModifyFirstLetterNamingPolicy.LetterModifier.UPPER));
}
}

View File

@ -0,0 +1,48 @@
/*
* Copyright (C) 2008 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.massivecraft.core.lib.gson2;
import java.lang.annotation.Annotation;
import java.lang.reflect.Type;
import java.util.Collection;
/**
* A {@link FieldNamingStrategy2} that ensures the JSON field names consist of only
* upper case letters.
*
* <p>The following is an example:</p>
* <pre>
* class IntWrapper {
* public int integerField = 0;
* }
*
* UpperCaseNamingPolicy policy = new UpperCaseNamingPolicy();
* String translatedFieldName =
* policy.translateName(IntWrapper.class.getField("integerField"));
*
* assert("INTEGERFIELD".equals(translatedFieldName));
* </pre>
*
* @author Joel Leitch
*/
final class UpperCaseNamingPolicy extends RecursiveFieldNamingPolicy {
@Override
protected String translateName(String target, Type fieldType, Collection<Annotation> annotations) {
return target.toUpperCase();
}
}

View File

@ -0,0 +1,29 @@
/*
* Copyright (C) 2008 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.massivecraft.core.lib.gson2;
/**
* Class contain all constants for versioning support.
*
* @author Joel Leitch
*/
final class VersionConstants {
// Prevent instantiation
private VersionConstants() { }
static final double IGNORE_VERSIONS = -1D;
}

View File

@ -0,0 +1,68 @@
/*
* Copyright (C) 2008 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.massivecraft.core.lib.gson2;
import com.massivecraft.core.lib.gson2.annotations.Since;
import com.massivecraft.core.lib.gson2.annotations.Until;
import com.massivecraft.core.lib.gson2.internal.$Gson$Preconditions;
/**
* This strategy will exclude any files and/or class that are passed the
* {@link #version} value.
*
* @author Joel Leitch
*/
final class VersionExclusionStrategy implements ExclusionStrategy {
private final double version;
VersionExclusionStrategy(double version) {
$Gson$Preconditions.checkArgument(version >= 0.0D);
this.version = version;
}
public boolean shouldSkipField(FieldAttributes f) {
return !isValidVersion(f.getAnnotation(Since.class), f.getAnnotation(Until.class));
}
public boolean shouldSkipClass(Class<?> clazz) {
return !isValidVersion(clazz.getAnnotation(Since.class), clazz.getAnnotation(Until.class));
}
private boolean isValidVersion(Since since, Until until) {
return (isValidSince(since) && isValidUntil(until));
}
private boolean isValidSince(Since annotation) {
if (annotation != null) {
double annotationVersion = annotation.value();
if (annotationVersion > version) {
return false;
}
}
return true;
}
private boolean isValidUntil(Until annotation) {
if (annotation != null) {
double annotationVersion = annotation.value();
if (annotationVersion <= version) {
return false;
}
}
return true;
}
}

View File

@ -0,0 +1,79 @@
/*
* Copyright (C) 2008 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.massivecraft.core.lib.gson2.annotations;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* An annotation that indicates this member should be exposed for JSON
* serialization or deserialization.
*
* <p>This annotation has no effect unless you build {@link com.massivecraft.core.lib.gson2.Gson}
* with a {@link com.massivecraft.core.lib.gson2.GsonBuilder} and invoke
* {@link com.massivecraft.core.lib.gson2.GsonBuilder#excludeFieldsWithoutExposeAnnotation()}
* method.</p>
*
* <p>Here is an example of how this annotation is meant to be used:
* <p><pre>
* public class User {
* &#64Expose private String firstName;
* &#64Expose(serialize = false) private String lastName;
* &#64Expose (serialize = false, deserialize = false) private String emailAddress;
* private String password;
* }
* </pre></p>
* If you created Gson with {@code new Gson()}, the {@code toJson()} and {@code fromJson()}
* methods will use the {@code password} field along-with {@code firstName}, {@code lastName},
* and {@code emailAddress} for serialization and deserialization. However, if you created Gson
* with {@code Gson gson = new GsonBuilder().excludeFieldsWithoutExposeAnnotation().create()}
* then the {@code toJson()} and {@code fromJson()} methods of Gson will exclude the
* {@code password} field. This is because the {@code password} field is not marked with the
* {@code @Expose} annotation. Gson will also exclude {@code lastName} and {@code emailAddress}
* from serialization since {@code serialize} is set to {@code false}. Similarly, Gson will
* exclude {@code emailAddress} from deserialization since {@code deserialize} is set to false.
*
* <p>Note that another way to achieve the same effect would have been to just mark the
* {@code password} field as {@code transient}, and Gson would have excluded it even with default
* settings. The {@code @Expose} annotation is useful in a style of programming where you want to
* explicitly specify all fields that should get considered for serialization or deserialization.
*
* @author Inderjeet Singh
* @author Joel Leitch
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface Expose {
/**
* If {@code true}, the field marked with this annotation is written out in the JSON while
* serializing. If {@code false}, the field marked with this annotation is skipped from the
* serialized output. Defaults to {@code true}.
* @since 1.4
*/
public boolean serialize() default true;
/**
* If {@code true}, the field marked with this annotation is deserialized from the JSON.
* If {@code false}, the field marked with this annotation is skipped during deserialization.
* Defaults to {@code true}.
* @since 1.4
*/
public boolean deserialize() default true;
}

View File

@ -0,0 +1,74 @@
/*
* Copyright (C) 2008 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.massivecraft.core.lib.gson2.annotations;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* An annotation that indicates this member should be serialized to JSON with
* the provided name value as its field name.
*
* <p>This annotation will override any {@link com.massivecraft.core.lib.gson2.FieldNamingPolicy}, including
* the default field naming policy, that may have been set on the {@link com.massivecraft.core.lib.gson2.Gson}
* instance. A different naming policy can set using the {@code GsonBuilder} class. See
* {@link com.massivecraft.core.lib.gson2.GsonBuilder#setFieldNamingPolicy(com.massivecraft.core.lib.gson2.FieldNamingPolicy)}
* for more information.</p>
*
* <p>Here is an example of how this annotation is meant to be used:</p>
* <pre>
* public class SomeClassWithFields {
* &#64SerializedName("name") private final String someField;
* private final String someOtherField;
*
* public SomeClassWithFields(String a, String b) {
* this.someField = a;
* this.someOtherField = b;
* }
* }
* </pre>
*
* <p>The following shows the output that is generated when serializing an instance of the
* above example class:</p>
* <pre>
* SomeClassWithFields objectToSerialize = new SomeClassWithFields("a", "b");
* Gson gson = new Gson();
* String jsonRepresentation = gson.toJson(objectToSerialize);
* System.out.println(jsonRepresentation);
*
* ===== OUTPUT =====
* {"name":"a","someOtherField":"b"}
* </pre>
*
* <p>NOTE: The value you specify in this annotation must be a valid JSON field name.</p>
*
* @see com.massivecraft.core.lib.gson2.FieldNamingPolicy
*
* @author Inderjeet Singh
* @author Joel Leitch
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface SerializedName {
/**
* @return the desired name of the field when it is serialized
*/
String value();
}

View File

@ -0,0 +1,61 @@
/*
* Copyright (C) 2008 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.massivecraft.core.lib.gson2.annotations;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 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.
*
* <p>
* This annotation has no effect unless you build {@link com.massivecraft.core.lib.gson2.Gson} with a
* {@link com.massivecraft.core.lib.gson2.GsonBuilder} and invoke
* {@link com.massivecraft.core.lib.gson2.GsonBuilder#setVersion(double)} method.
*
* <p>Here is an example of how this annotation is meant to be used:</p>
* <pre>
* public class User {
* private String firstName;
* private String lastName;
* &#64Since(1.0) private String emailAddress;
* &#64Since(1.0) private String password;
* &#64Since(1.1) private Address address;
* }
* </pre>
*
* <p>If you created Gson with {@code new Gson()}, the {@code toJson()} and {@code fromJson()}
* methods will use all the fields for serialization and deserialization. However, if you created
* Gson with {@code Gson gson = new GsonBuilder().setVersion(1.0).create()} then the
* {@code toJson()} and {@code fromJson()} methods of Gson will exclude the {@code address} field
* since it's version number is set to {@code 1.1}.</p>
*
* @author Inderjeet Singh
* @author Joel Leitch
*/
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD, ElementType.TYPE})
public @interface Since {
/**
* the value indicating a version number since this member
* or type has been present.
*/
double value();
}

View File

@ -0,0 +1,66 @@
/*
* Copyright (C) 2008 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.massivecraft.core.lib.gson2.annotations;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* An annotation that indicates the version number until a member or a type should be present.
* Basically, if Gson is created with a version number that exceeds the value stored in the
* {@code Until} annotation then the field will be ignored from the JSON output. This annotation
* is useful to manage versioning of your JSON classes for a web-service.
*
* <p>
* This annotation has no effect unless you build {@link com.massivecraft.core.lib.gson2.Gson} with a
* {@link com.massivecraft.core.lib.gson2.GsonBuilder} and invoke
* {@link com.massivecraft.core.lib.gson2.GsonBuilder#setVersion(double)} method.
*
* <p>Here is an example of how this annotation is meant to be used:</p>
* <pre>
* public class User {
* private String firstName;
* private String lastName;
* &#64Until(1.1) private String emailAddress;
* &#64Until(1.1) private String password;
* }
* </pre>
*
* <p>If you created Gson with {@code new Gson()}, the {@code toJson()} and {@code fromJson()}
* methods will use all the fields for serialization and deserialization. However, if you created
* Gson with {@code Gson gson = new GsonBuilder().setVersion(1.2).create()} then the
* {@code toJson()} and {@code fromJson()} methods of Gson will exclude the {@code emailAddress}
* and {@code password} fields from the example above, because the version number passed to the
* GsonBuilder, {@code 1.2}, exceeds the version number set on the {@code Until} annotation,
* {@code 1.1}, for those fields.
*
* @author Inderjeet Singh
* @author Joel Leitch
* @since 1.3
*/
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD, ElementType.TYPE})
public @interface Until {
/**
* the value indicating a version number until this member
* or type should be ignored.
*/
double value();
}

View File

@ -0,0 +1,6 @@
/**
* This package provides annotations that can be used with {@link com.massivecraft.core.lib.gson2.Gson}.
*
* @author Inderjeet Singh, Joel Leitch
*/
package com.massivecraft.core.lib.gson2.annotations;

View File

@ -0,0 +1,51 @@
/*
* Copyright (C) 2008 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.massivecraft.core.lib.gson2.internal;
/**
* A simple utility class used to check method Preconditions.
*
* <pre>
* public long divideBy(long value) {
* Preconditions.checkArgument(value != 0);
* return this.value / value;
* }
* </pre>
*
* @author Inderjeet Singh
* @author Joel Leitch
*/
public final class $Gson$Preconditions {
public static <T> T checkNotNull(T obj) {
if (obj == null) {
throw new NullPointerException();
}
return obj;
}
public static void checkArgument(boolean condition) {
if (!condition) {
throw new IllegalArgumentException();
}
}
public static void checkState(boolean condition) {
if (!condition) {
throw new IllegalStateException();
}
}
}

View File

@ -0,0 +1,592 @@
/**
* Copyright (C) 2008 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.massivecraft.core.lib.gson2.internal;
import static com.massivecraft.core.lib.gson2.internal.$Gson$Preconditions.checkArgument;
import static com.massivecraft.core.lib.gson2.internal.$Gson$Preconditions.checkNotNull;
import java.io.Serializable;
import java.lang.reflect.Array;
import java.lang.reflect.GenericArrayType;
import java.lang.reflect.GenericDeclaration;
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;
/**
* Static methods for working with types.
*
* @author Bob Lee
* @author Jesse Wilson
*/
public final class $Gson$Types {
static final Type[] EMPTY_TYPE_ARRAY = new Type[] {};
private $Gson$Types() {}
/**
* Returns a new parameterized type, applying {@code typeArguments} to
* {@code rawType} and enclosed by {@code ownerType}.
*
* @return a {@link java.io.Serializable serializable} parameterized type.
*/
public static ParameterizedType newParameterizedTypeWithOwner(
Type ownerType, Type rawType, Type... typeArguments) {
return new ParameterizedTypeImpl(ownerType, rawType, typeArguments);
}
/**
* Returns an array type whose elements are all instances of
* {@code componentType}.
*
* @return a {@link java.io.Serializable serializable} generic array type.
*/
public static GenericArrayType arrayOf(Type componentType) {
return new GenericArrayTypeImpl(componentType);
}
/**
* Returns a type that represents an unknown type that extends {@code bound}.
* For example, if {@code bound} is {@code CharSequence.class}, this returns
* {@code ? extends CharSequence}. If {@code bound} is {@code Object.class},
* this returns {@code ?}, which is shorthand for {@code ? extends Object}.
*/
public static WildcardType subtypeOf(Type bound) {
return new WildcardTypeImpl(new Type[] { bound }, EMPTY_TYPE_ARRAY);
}
/**
* Returns a type that represents an unknown supertype of {@code bound}. For
* example, if {@code bound} is {@code String.class}, this returns {@code ?
* super String}.
*/
public static WildcardType supertypeOf(Type bound) {
return new WildcardTypeImpl(new Type[] { Object.class }, new Type[] { bound });
}
/**
* Returns a type that is functionally equal but not necessarily equal
* according to {@link Object#equals(Object) Object.equals()}. The returned
* type is {@link java.io.Serializable}.
*/
public static Type canonicalize(Type type) {
if (type instanceof Class) {
Class<?> c = (Class<?>) type;
return c.isArray() ? new GenericArrayTypeImpl(canonicalize(c.getComponentType())) : c;
} else if (type instanceof ParameterizedType) {
ParameterizedType p = (ParameterizedType) type;
return new ParameterizedTypeImpl(p.getOwnerType(),
p.getRawType(), p.getActualTypeArguments());
} else if (type instanceof GenericArrayType) {
GenericArrayType g = (GenericArrayType) type;
return new GenericArrayTypeImpl(g.getGenericComponentType());
} else if (type instanceof WildcardType) {
WildcardType w = (WildcardType) type;
return new WildcardTypeImpl(w.getUpperBounds(), w.getLowerBounds());
} else {
// type is either serializable as-is or unsupported
return type;
}
}
public static Class<?> getRawType(Type type) {
if (type instanceof Class<?>) {
// type is a normal class.
return (Class<?>) type;
} else if (type instanceof ParameterizedType) {
ParameterizedType parameterizedType = (ParameterizedType) type;
// I'm not exactly sure why getRawType() returns Type instead of Class.
// Neal isn't either but suspects some pathological case related
// to nested classes exists.
Type rawType = parameterizedType.getRawType();
checkArgument(rawType instanceof Class);
return (Class<?>) rawType;
} else if (type instanceof GenericArrayType) {
Type componentType = ((GenericArrayType)type).getGenericComponentType();
return Array.newInstance(getRawType(componentType), 0).getClass();
} else if (type instanceof TypeVariable) {
// we could use the variable's bounds, but that won't work if there are multiple.
// having a raw type that's more general than necessary is okay
return Object.class;
} else if (type instanceof WildcardType) {
return getRawType(((WildcardType) type).getUpperBounds()[0]);
} else {
String className = type == null ? "null" : type.getClass().getName();
throw new IllegalArgumentException("Expected a Class, ParameterizedType, or "
+ "GenericArrayType, but <" + type + "> is of type " + className);
}
}
static boolean equal(Object a, Object b) {
return a == b || (a != null && a.equals(b));
}
/**
* Returns true if {@code a} and {@code b} are equal.
*/
public static boolean equals(Type a, Type b) {
if (a == b) {
// also handles (a == null && b == null)
return true;
} else if (a instanceof Class) {
// Class already specifies equals().
return a.equals(b);
} else if (a instanceof ParameterizedType) {
if (!(b instanceof ParameterizedType)) {
return false;
}
// TODO: save a .clone() call
ParameterizedType pa = (ParameterizedType) a;
ParameterizedType pb = (ParameterizedType) b;
return equal(pa.getOwnerType(), pb.getOwnerType())
&& pa.getRawType().equals(pb.getRawType())
&& Arrays.equals(pa.getActualTypeArguments(), pb.getActualTypeArguments());
} else if (a instanceof GenericArrayType) {
if (!(b instanceof GenericArrayType)) {
return false;
}
GenericArrayType ga = (GenericArrayType) a;
GenericArrayType gb = (GenericArrayType) b;
return equals(ga.getGenericComponentType(), gb.getGenericComponentType());
} else if (a instanceof WildcardType) {
if (!(b instanceof WildcardType)) {
return false;
}
WildcardType wa = (WildcardType) a;
WildcardType wb = (WildcardType) b;
return Arrays.equals(wa.getUpperBounds(), wb.getUpperBounds())
&& Arrays.equals(wa.getLowerBounds(), wb.getLowerBounds());
} else if (a instanceof TypeVariable) {
if (!(b instanceof TypeVariable)) {
return false;
}
TypeVariable<?> va = (TypeVariable<?>) a;
TypeVariable<?> vb = (TypeVariable<?>) b;
return va.getGenericDeclaration() == vb.getGenericDeclaration()
&& va.getName().equals(vb.getName());
} else {
// This isn't a type we support. Could be a generic array type, wildcard type, etc.
return false;
}
}
private static int hashCodeOrZero(Object o) {
return o != null ? o.hashCode() : 0;
}
public static String typeToString(Type type) {
return type instanceof Class ? ((Class<?>) type).getName() : type.toString();
}
/**
* Returns the generic supertype for {@code supertype}. For example, given a class {@code
* IntegerSet}, the result for when supertype is {@code Set.class} is {@code Set<Integer>} and the
* result when the supertype is {@code Collection.class} is {@code Collection<Integer>}.
*/
static Type getGenericSupertype(Type context, Class<?> rawType, Class<?> toResolve) {
if (toResolve == rawType) {
return context;
}
// we skip searching through interfaces if unknown is an interface
if (toResolve.isInterface()) {
Class<?>[] interfaces = rawType.getInterfaces();
for (int i = 0, length = interfaces.length; i < length; i++) {
if (interfaces[i] == toResolve) {
return rawType.getGenericInterfaces()[i];
} else if (toResolve.isAssignableFrom(interfaces[i])) {
return getGenericSupertype(rawType.getGenericInterfaces()[i], interfaces[i], toResolve);
}
}
}
// check our supertypes
if (!rawType.isInterface()) {
while (rawType != Object.class) {
Class<?> rawSupertype = rawType.getSuperclass();
if (rawSupertype == toResolve) {
return rawType.getGenericSuperclass();
} else if (toResolve.isAssignableFrom(rawSupertype)) {
return getGenericSupertype(rawType.getGenericSuperclass(), rawSupertype, toResolve);
}
rawType = rawSupertype;
}
}
// we can't resolve this further
return toResolve;
}
/**
* Returns the generic form of {@code supertype}. For example, if this is {@code
* ArrayList<String>}, this returns {@code Iterable<String>} given the input {@code
* Iterable.class}.
*
* @param supertype a superclass of, or interface implemented by, this.
*/
static Type getSupertype(Type context, Class<?> contextRawType, Class<?> supertype) {
checkArgument(supertype.isAssignableFrom(contextRawType));
return resolve(context, contextRawType,
$Gson$Types.getGenericSupertype(context, contextRawType, supertype));
}
/**
* Returns true if this type is an array.
*/
public static boolean isArray(Type type) {
return type instanceof GenericArrayType
|| (type instanceof Class && ((Class<?>) type).isArray());
}
/**
* Returns the component type of this array type.
* @throws ClassCastException if this type is not an array.
*/
public static Type getArrayComponentType(Type array) {
return array instanceof GenericArrayType
? ((GenericArrayType) array).getGenericComponentType()
: ((Class<?>) array).getComponentType();
}
/**
* Returns the element type of this collection type.
* @throws IllegalArgumentException if this type is not a collection.
*/
public static Type getCollectionElementType(Type context, Class<?> contextRawType) {
Type collectionType = getSupertype(context, contextRawType, Collection.class);
if (collectionType instanceof WildcardType) {
collectionType = ((WildcardType)collectionType).getUpperBounds()[0];
}
if (collectionType instanceof ParameterizedType) {
return ((ParameterizedType) collectionType).getActualTypeArguments()[0];
}
return Object.class;
}
/**
* Returns a two element array containing this map's key and value types in
* positions 0 and 1 respectively.
*/
public static Type[] getMapKeyAndValueTypes(Type context, Class<?> contextRawType) {
/*
* Work around a problem with the declaration of java.util.Properties. That
* class should extend Hashtable<String, String>, but it's declared to
* extend Hashtable<Object, Object>.
*/
if (context == Properties.class) {
return new Type[] { String.class, String.class }; // TODO: test subclasses of Properties!
}
Type mapType = getSupertype(context, contextRawType, Map.class);
// TODO: strip wildcards?
if (mapType instanceof ParameterizedType) {
ParameterizedType mapParameterizedType = (ParameterizedType) mapType;
return mapParameterizedType.getActualTypeArguments();
}
return new Type[] { Object.class, Object.class };
}
public static Type resolve(Type context, Class<?> contextRawType, Type toResolve) {
// this implementation is made a little more complicated in an attempt to avoid object-creation
while (true) {
if (toResolve instanceof TypeVariable) {
TypeVariable<?> typeVariable = (TypeVariable<?>) toResolve;
toResolve = resolveTypeVariable(context, contextRawType, typeVariable);
if (toResolve == typeVariable) {
return toResolve;
}
} else if (toResolve instanceof Class && ((Class<?>) toResolve).isArray()) {
Class<?> original = (Class<?>) toResolve;
Type componentType = original.getComponentType();
Type newComponentType = resolve(context, contextRawType, componentType);
return componentType == newComponentType
? original
: arrayOf(newComponentType);
} else if (toResolve instanceof GenericArrayType) {
GenericArrayType original = (GenericArrayType) toResolve;
Type componentType = original.getGenericComponentType();
Type newComponentType = resolve(context, contextRawType, componentType);
return componentType == newComponentType
? original
: arrayOf(newComponentType);
} else if (toResolve instanceof ParameterizedType) {
ParameterizedType original = (ParameterizedType) toResolve;
Type ownerType = original.getOwnerType();
Type newOwnerType = resolve(context, contextRawType, ownerType);
boolean changed = newOwnerType != ownerType;
Type[] args = original.getActualTypeArguments();
for (int t = 0, length = args.length; t < length; t++) {
Type resolvedTypeArgument = resolve(context, contextRawType, args[t]);
if (resolvedTypeArgument != args[t]) {
if (!changed) {
args = args.clone();
changed = true;
}
args[t] = resolvedTypeArgument;
}
}
return changed
? newParameterizedTypeWithOwner(newOwnerType, original.getRawType(), args)
: original;
} else if (toResolve instanceof WildcardType) {
WildcardType original = (WildcardType) toResolve;
Type[] originalLowerBound = original.getLowerBounds();
Type[] originalUpperBound = original.getUpperBounds();
if (originalLowerBound.length == 1) {
Type lowerBound = resolve(context, contextRawType, originalLowerBound[0]);
if (lowerBound != originalLowerBound[0]) {
return supertypeOf(lowerBound);
}
} else if (originalUpperBound.length == 1) {
Type upperBound = resolve(context, contextRawType, originalUpperBound[0]);
if (upperBound != originalUpperBound[0]) {
return subtypeOf(upperBound);
}
}
return original;
} else {
return toResolve;
}
}
}
static Type resolveTypeVariable(Type context, Class<?> contextRawType, TypeVariable<?> unknown) {
Class<?> declaredByRaw = declaringClassOf(unknown);
// we can't reduce this further
if (declaredByRaw == null) {
return unknown;
}
Type declaredBy = getGenericSupertype(context, contextRawType, declaredByRaw);
if (declaredBy instanceof ParameterizedType) {
int index = indexOf(declaredByRaw.getTypeParameters(), unknown);
return ((ParameterizedType) declaredBy).getActualTypeArguments()[index];
}
return unknown;
}
private static int indexOf(Object[] array, Object toFind) {
for (int i = 0; i < array.length; i++) {
if (toFind.equals(array[i])) {
return i;
}
}
throw new NoSuchElementException();
}
/**
* Returns the declaring class of {@code typeVariable}, or {@code null} if it was not declared by
* a class.
*/
private static Class<?> declaringClassOf(TypeVariable<?> typeVariable) {
GenericDeclaration genericDeclaration = typeVariable.getGenericDeclaration();
return genericDeclaration instanceof Class
? (Class<?>) genericDeclaration
: null;
}
private static void checkNotPrimitive(Type type) {
checkArgument(!(type instanceof Class<?>) || !((Class<?>) type).isPrimitive());
}
private static final class ParameterizedTypeImpl implements ParameterizedType, Serializable {
private final Type ownerType;
private final Type rawType;
private final Type[] typeArguments;
public ParameterizedTypeImpl(Type ownerType, Type rawType, Type... typeArguments) {
// require an owner type if the raw type needs it
if (rawType instanceof Class<?>) {
Class<?> rawTypeAsClass = (Class<?>) rawType;
checkArgument(ownerType != null || rawTypeAsClass.getEnclosingClass() == null);
checkArgument(ownerType == null || rawTypeAsClass.getEnclosingClass() != null);
}
this.ownerType = ownerType == null ? null : canonicalize(ownerType);
this.rawType = canonicalize(rawType);
this.typeArguments = typeArguments.clone();
for (int t = 0; t < this.typeArguments.length; t++) {
checkNotNull(this.typeArguments[t]);
checkNotPrimitive(this.typeArguments[t]);
this.typeArguments[t] = canonicalize(this.typeArguments[t]);
}
}
public Type[] getActualTypeArguments() {
return typeArguments.clone();
}
public Type getRawType() {
return rawType;
}
public Type getOwnerType() {
return ownerType;
}
@Override public boolean equals(Object other) {
return other instanceof ParameterizedType
&& $Gson$Types.equals(this, (ParameterizedType) other);
}
@Override public int hashCode() {
return Arrays.hashCode(typeArguments)
^ rawType.hashCode()
^ hashCodeOrZero(ownerType);
}
@Override public String toString() {
StringBuilder stringBuilder = new StringBuilder(30 * (typeArguments.length + 1));
stringBuilder.append(typeToString(rawType));
if (typeArguments.length == 0) {
return stringBuilder.toString();
}
stringBuilder.append("<").append(typeToString(typeArguments[0]));
for (int i = 1; i < typeArguments.length; i++) {
stringBuilder.append(", ").append(typeToString(typeArguments[i]));
}
return stringBuilder.append(">").toString();
}
private static final long serialVersionUID = 0;
}
private static final class GenericArrayTypeImpl implements GenericArrayType, Serializable {
private final Type componentType;
public GenericArrayTypeImpl(Type componentType) {
this.componentType = canonicalize(componentType);
}
public Type getGenericComponentType() {
return componentType;
}
@Override public boolean equals(Object o) {
return o instanceof GenericArrayType
&& $Gson$Types.equals(this, (GenericArrayType) o);
}
@Override public int hashCode() {
return componentType.hashCode();
}
@Override public String toString() {
return typeToString(componentType) + "[]";
}
private static final long serialVersionUID = 0;
}
/**
* The WildcardType interface supports multiple upper bounds and multiple
* lower bounds. We only support what the Java 6 language needs - at most one
* bound. If a lower bound is set, the upper bound must be Object.class.
*/
private static final class WildcardTypeImpl implements WildcardType, Serializable {
private final Type upperBound;
private final Type lowerBound;
public WildcardTypeImpl(Type[] upperBounds, Type[] lowerBounds) {
checkArgument(lowerBounds.length <= 1);
checkArgument(upperBounds.length == 1);
if (lowerBounds.length == 1) {
checkNotNull(lowerBounds[0]);
checkNotPrimitive(lowerBounds[0]);
checkArgument(upperBounds[0] == Object.class);
this.lowerBound = canonicalize(lowerBounds[0]);
this.upperBound = Object.class;
} else {
checkNotNull(upperBounds[0]);
checkNotPrimitive(upperBounds[0]);
this.lowerBound = null;
this.upperBound = canonicalize(upperBounds[0]);
}
}
public Type[] getUpperBounds() {
return new Type[] { upperBound };
}
public Type[] getLowerBounds() {
return lowerBound != null ? new Type[] { lowerBound } : EMPTY_TYPE_ARRAY;
}
@Override public boolean equals(Object other) {
return other instanceof WildcardType
&& $Gson$Types.equals(this, (WildcardType) other);
}
@Override public int hashCode() {
// this equals Arrays.hashCode(getLowerBounds()) ^ Arrays.hashCode(getUpperBounds());
return (lowerBound != null ? 31 + lowerBound.hashCode() : 1)
^ (31 + upperBound.hashCode());
}
@Override public String toString() {
if (lowerBound != null) {
return "? super " + typeToString(lowerBound);
} else if (upperBound == Object.class) {
return "?";
} else {
return "? extends " + typeToString(upperBound);
}
}
private static final long serialVersionUID = 0;
}
}

View File

@ -0,0 +1,177 @@
/*
* Copyright (C) 2011 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.massivecraft.core.lib.gson2.internal;
import com.massivecraft.core.lib.gson2.InstanceCreator;
import com.massivecraft.core.lib.gson2.reflect.TypeToken;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.Map;
import java.util.Queue;
import java.util.Set;
import java.util.SortedSet;
import java.util.TreeSet;
/**
* Returns a function that can construct an instance of a requested type.
*/
public final class ConstructorConstructor {
private final ParameterizedTypeHandlerMap<InstanceCreator<?>> instanceCreators;
public ConstructorConstructor(ParameterizedTypeHandlerMap<InstanceCreator<?>> instanceCreators) {
this.instanceCreators = instanceCreators;
}
public ConstructorConstructor() {
this(new ParameterizedTypeHandlerMap<InstanceCreator<?>>());
}
public <T> ObjectConstructor<T> getConstructor(TypeToken<T> typeToken) {
final Type type = typeToken.getType();
final Class<? super T> rawType = typeToken.getRawType();
// first try an instance creator
@SuppressWarnings("unchecked") // types must agree
final InstanceCreator<T> creator
= (InstanceCreator<T>) instanceCreators.getHandlerFor(type, false);
if (creator != null) {
return new ObjectConstructor<T>() {
public T construct() {
return creator.createInstance(type);
}
};
}
ObjectConstructor<T> defaultConstructor = newDefaultConstructor(rawType);
if (defaultConstructor != null) {
return defaultConstructor;
}
ObjectConstructor<T> defaultImplementation = newDefaultImplementationConstructor(rawType);
if (defaultImplementation != null) {
return defaultImplementation;
}
// finally try unsafe
return newUnsafeAllocator(type, rawType);
}
private <T> ObjectConstructor<T> newDefaultConstructor(Class<? super T> rawType) {
try {
final Constructor<? super T> constructor = rawType.getDeclaredConstructor();
if (!constructor.isAccessible()) {
constructor.setAccessible(true);
}
return new ObjectConstructor<T>() {
@SuppressWarnings("unchecked") // T is the same raw type as is requested
public T construct() {
try {
Object[] args = null;
return (T) constructor.newInstance(args);
} catch (InstantiationException e) {
// TODO: JsonParseException ?
throw new RuntimeException("Failed to invoke " + constructor + " with no args", e);
} catch (InvocationTargetException e) {
// TODO: don't wrap if cause is unchecked!
// TODO: JsonParseException ?
throw new RuntimeException("Failed to invoke " + constructor + " with no args",
e.getTargetException());
} catch (IllegalAccessException e) {
throw new AssertionError(e);
}
}
};
} catch (NoSuchMethodException e) {
return null;
}
}
/**
* Constructors for common interface types like Map and List and their
* subytpes.
*/
@SuppressWarnings("unchecked") // use runtime checks to guarantee that 'T' is what it is
private <T> ObjectConstructor<T> newDefaultImplementationConstructor(Class<? super T> rawType) {
if (Collection.class.isAssignableFrom(rawType)) {
if (SortedSet.class.isAssignableFrom(rawType)) {
return new ObjectConstructor<T>() {
public T construct() {
return (T) new TreeSet<Object>();
}
};
} else if (Set.class.isAssignableFrom(rawType)) {
return new ObjectConstructor<T>() {
public T construct() {
return (T) new LinkedHashSet<Object>();
}
};
} else if (Queue.class.isAssignableFrom(rawType)) {
return new ObjectConstructor<T>() {
public T construct() {
return (T) new LinkedList<Object>();
}
};
} else {
return new ObjectConstructor<T>() {
public T construct() {
return (T) new ArrayList<Object>();
}
};
}
}
if (Map.class.isAssignableFrom(rawType)) {
return new ObjectConstructor<T>() {
public T construct() {
return (T) new LinkedHashMap<Object, Object>();
}
};
// TODO: SortedMap ?
}
return null;
}
private <T> ObjectConstructor<T> newUnsafeAllocator(
final Type type, final Class<? super T> rawType) {
return new ObjectConstructor<T>() {
private final UnsafeAllocator unsafeAllocator = UnsafeAllocator.create();
@SuppressWarnings("unchecked")
public T construct() {
try {
Object newInstance = unsafeAllocator.newInstance(rawType);
return (T) newInstance;
} catch (Exception e) {
throw new RuntimeException(("Unable to invoke no-args constructor for " + type + ". "
+ "Register an InstanceCreator with Gson for this type may fix this problem."), e);
}
}
};
}
@Override public String toString() {
return instanceCreators.toString();
}
}

View File

@ -0,0 +1,69 @@
/*
* Copyright (C) 2011 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.massivecraft.core.lib.gson2.internal;
import java.math.BigInteger;
/**
* This class holds a number value that is lazily converted to a specific number type
*
* @author Inderjeet Singh
*/
@SuppressWarnings("serial")
public final class LazilyParsedNumber extends Number {
private final String value;
public LazilyParsedNumber(String value) {
this.value = value;
}
@Override
public int intValue() {
try {
return Integer.parseInt(value);
} catch (NumberFormatException e) {
try {
return (int) Long.parseLong(value);
} catch (NumberFormatException nfe) {
return new BigInteger(value).intValue();
}
}
}
@Override
public long longValue() {
try {
return Long.parseLong(value);
} catch (NumberFormatException e) {
return new BigInteger(value).longValue();
}
}
@Override
public float floatValue() {
return Float.parseFloat(value);
}
@Override
public double doubleValue() {
return Double.parseDouble(value);
}
@Override
public String toString() {
return value;
}
}

View File

@ -0,0 +1,33 @@
/*
* Copyright (C) 2008 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.massivecraft.core.lib.gson2.internal;
/**
* Defines a generic object construction factory. The purpose of this class
* is to construct a default instance of a class that can be used for object
* navigation while deserialization from its JSON representation.
*
* @author Inderjeet Singh
* @author Joel Leitch
*/
public interface ObjectConstructor<T> {
/**
* Returns a new instance.
*/
public T construct();
}

View File

@ -0,0 +1,61 @@
/*
* Copyright (C) 2009 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.massivecraft.core.lib.gson2.internal;
/**
* A simple object that holds onto a pair of object references, first and second.
*
* @author Inderjeet Singh
* @author Joel Leitch
*
* @param <FIRST>
* @param <SECOND>
*/
public final class Pair<FIRST, SECOND> {
public final FIRST first;
public final SECOND second;
public Pair(FIRST first, SECOND second) {
this.first = first;
this.second = second;
}
@Override
public int hashCode() {
return 17 * ((first != null) ? first.hashCode() : 0)
+ 17 * ((second != null) ? second.hashCode() : 0);
}
@Override
public boolean equals(Object o) {
if (!(o instanceof Pair<?, ?>)) {
return false;
}
Pair<?, ?> that = (Pair<?, ?>) o;
return equal(this.first, that.first) && equal(this.second, that.second);
}
private static boolean equal(Object a, Object b) {
return a == b || (a != null && a.equals(b));
}
@Override
public String toString() {
return String.format("{%s,%s}", first, second);
}
}

View File

@ -0,0 +1,247 @@
/*
* Copyright (C) 2008 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.massivecraft.core.lib.gson2.internal;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* A map that provides ability to associate handlers for a specific type or all
* of its sub-types
*
* @author Inderjeet Singh
* @author Joel Leitch
*
* @param <T> The handler that will be looked up by type
*/
public final class ParameterizedTypeHandlerMap<T> {
private static final Logger logger =
Logger.getLogger(ParameterizedTypeHandlerMap.class.getName());
/**
* Map that is meant for storing default type adapters
*/
private final Map<Type, T> systemMap = new HashMap<Type, T>();
private final Map<Type, T> userMap = new HashMap<Type, T>();
/**
* List of default type hierarchy adapters
*/
private final List<Pair<Class<?>, T>> systemTypeHierarchyList = new ArrayList<Pair<Class<?>, T>>();
private final List<Pair<Class<?>, T>> userTypeHierarchyList = new ArrayList<Pair<Class<?>, T>>();
private boolean modifiable = true;
public synchronized void registerForTypeHierarchy(Class<?> typeOfT, T value, boolean isSystem) {
Pair<Class<?>, T> pair = new Pair<Class<?>, T>(typeOfT, value);
registerForTypeHierarchy(pair, isSystem);
}
public synchronized void registerForTypeHierarchy(Pair<Class<?>, T> pair, boolean isSystem) {
if (!modifiable) {
throw new IllegalStateException("Attempted to modify an unmodifiable map.");
}
List<Pair<Class<?>, T>> typeHierarchyList = isSystem ? systemTypeHierarchyList : userTypeHierarchyList;
int index = getIndexOfSpecificHandlerForTypeHierarchy(pair.first, typeHierarchyList);
if (index >= 0) {
logger.log(Level.WARNING, "Overriding the existing type handler for {0}", pair.first);
typeHierarchyList.remove(index);
}
index = getIndexOfAnOverriddenHandler(pair.first, typeHierarchyList);
if (index >= 0) {
throw new IllegalArgumentException("The specified type handler for type " + pair.first
+ " hides the previously registered type hierarchy handler for "
+ typeHierarchyList.get(index).first + ". Gson does not allow this.");
}
// We want stack behavior for adding to this list. A type adapter added subsequently should
// override a previously registered one.
typeHierarchyList.add(0, pair);
}
private static <T> int getIndexOfAnOverriddenHandler(Class<?> type, List<Pair<Class<?>, T>> typeHierarchyList) {
for (int i = typeHierarchyList.size()-1; i >= 0; --i) {
Pair<Class<?>, T> entry = typeHierarchyList.get(i);
if (type.isAssignableFrom(entry.first)) {
return i;
}
}
return -1;
}
public synchronized void register(Type typeOfT, T value, boolean isSystem) {
if (!modifiable) {
throw new IllegalStateException("Attempted to modify an unmodifiable map.");
}
if (hasSpecificHandlerFor(typeOfT)) {
logger.log(Level.WARNING, "Overriding the existing type handler for {0}", typeOfT);
}
Map<Type, T> map = isSystem ? systemMap : userMap;
map.put(typeOfT, value);
}
public synchronized void registerIfAbsent(ParameterizedTypeHandlerMap<T> other) {
if (!modifiable) {
throw new IllegalStateException("Attempted to modify an unmodifiable map.");
}
for (Map.Entry<Type, T> entry : other.userMap.entrySet()) {
if (!userMap.containsKey(entry.getKey())) {
register(entry.getKey(), entry.getValue(), false);
}
}
for (Map.Entry<Type, T> entry : other.systemMap.entrySet()) {
if (!systemMap.containsKey(entry.getKey())) {
register(entry.getKey(), entry.getValue(), true);
}
}
// Quite important to traverse the typeHierarchyList from stack bottom first since
// we want to register the handlers in the same order to preserve priority order
for (int i = other.userTypeHierarchyList.size()-1; i >= 0; --i) {
Pair<Class<?>, T> entry = other.userTypeHierarchyList.get(i);
int index = getIndexOfSpecificHandlerForTypeHierarchy(entry.first, userTypeHierarchyList);
if (index < 0) {
registerForTypeHierarchy(entry, false);
}
}
for (int i = other.systemTypeHierarchyList.size()-1; i >= 0; --i) {
Pair<Class<?>, T> entry = other.systemTypeHierarchyList.get(i);
int index = getIndexOfSpecificHandlerForTypeHierarchy(entry.first, systemTypeHierarchyList);
if (index < 0) {
registerForTypeHierarchy(entry, true);
}
}
}
public synchronized ParameterizedTypeHandlerMap<T> makeUnmodifiable() {
modifiable = false;
return this;
}
public synchronized T getHandlerFor(Type type, boolean systemOnly) {
T handler;
if (!systemOnly) {
handler = userMap.get(type);
if (handler != null) {
return handler;
}
}
handler = systemMap.get(type);
if (handler != null) {
return handler;
}
Class<?> rawClass = $Gson$Types.getRawType(type);
if (rawClass != type) {
handler = getHandlerFor(rawClass, systemOnly);
if (handler != null) {
return handler;
}
}
// check if something registered for type hierarchy
handler = getHandlerForTypeHierarchy(rawClass, systemOnly);
return handler;
}
private T getHandlerForTypeHierarchy(Class<?> type, boolean systemOnly) {
if (!systemOnly) {
for (Pair<Class<?>, T> entry : userTypeHierarchyList) {
if (entry.first.isAssignableFrom(type)) {
return entry.second;
}
}
}
for (Pair<Class<?>, T> entry : systemTypeHierarchyList) {
if (entry.first.isAssignableFrom(type)) {
return entry.second;
}
}
return null;
}
public synchronized boolean hasSpecificHandlerFor(Type type) {
return userMap.containsKey(type) || systemMap.containsKey(type);
}
private static <T> int getIndexOfSpecificHandlerForTypeHierarchy(
Class<?> type, List<Pair<Class<?>, T>> typeHierarchyList) {
for (int i = typeHierarchyList.size()-1; i >= 0; --i) {
if (type.equals(typeHierarchyList.get(i).first)) {
return i;
}
}
return -1;
}
public synchronized ParameterizedTypeHandlerMap<T> copyOf() {
ParameterizedTypeHandlerMap<T> copy = new ParameterizedTypeHandlerMap<T>();
// Instead of individually registering entries in the map, make an efficient copy
// of the list and map
// TODO (inder): Performance optimization. We can probably just share the
// systemMap and systemTypeHierarchyList instead of making copies
copy.systemMap.putAll(systemMap);
copy.userMap.putAll(userMap);
copy.systemTypeHierarchyList.addAll(systemTypeHierarchyList);
copy.userTypeHierarchyList.addAll(userTypeHierarchyList);
return copy;
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder("{userTypeHierarchyList:{");
appendList(sb, userTypeHierarchyList);
sb.append("},systemTypeHierarchyList:{");
appendList(sb, systemTypeHierarchyList);
sb.append("},userMap:{");
appendMap(sb, userMap);
sb.append("},systemMap:{");
appendMap(sb, systemMap);
sb.append("}");
return sb.toString();
}
private void appendList(StringBuilder sb, List<Pair<Class<?>,T>> list) {
boolean first = true;
for (Pair<Class<?>, T> entry : list) {
if (first) {
first = false;
} else {
sb.append(',');
}
sb.append(typeToString(entry.first)).append(':');
sb.append(entry.second);
}
}
private void appendMap(StringBuilder sb, Map<Type, T> map) {
boolean first = true;
for (Map.Entry<Type, T> entry : map.entrySet()) {
if (first) {
first = false;
} else {
sb.append(',');
}
sb.append(typeToString(entry.getKey())).append(':');
sb.append(entry.getValue());
}
}
private String typeToString(Type type) {
return $Gson$Types.getRawType(type).getSimpleName();
}
}

View File

@ -0,0 +1,119 @@
/*
* Copyright (C) 2008 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.massivecraft.core.lib.gson2.internal;
import com.massivecraft.core.lib.gson2.internal.$Gson$Preconditions;
import java.lang.reflect.Type;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
/**
* Contains static utility methods pertaining to primitive types and their
* corresponding wrapper types.
*
* @author Kevin Bourrillion
*/
public final class Primitives {
private Primitives() {}
/** A map from primitive types to their corresponding wrapper types. */
private static final Map<Class<?>, Class<?>> PRIMITIVE_TO_WRAPPER_TYPE;
/** A map from wrapper types to their corresponding primitive types. */
private static final Map<Class<?>, Class<?>> WRAPPER_TO_PRIMITIVE_TYPE;
// Sad that we can't use a BiMap. :(
static {
Map<Class<?>, Class<?>> primToWrap = new HashMap<Class<?>, Class<?>>(16);
Map<Class<?>, Class<?>> wrapToPrim = new HashMap<Class<?>, Class<?>>(16);
add(primToWrap, wrapToPrim, boolean.class, Boolean.class);
add(primToWrap, wrapToPrim, byte.class, Byte.class);
add(primToWrap, wrapToPrim, char.class, Character.class);
add(primToWrap, wrapToPrim, double.class, Double.class);
add(primToWrap, wrapToPrim, float.class, Float.class);
add(primToWrap, wrapToPrim, int.class, Integer.class);
add(primToWrap, wrapToPrim, long.class, Long.class);
add(primToWrap, wrapToPrim, short.class, Short.class);
add(primToWrap, wrapToPrim, void.class, Void.class);
PRIMITIVE_TO_WRAPPER_TYPE = Collections.unmodifiableMap(primToWrap);
WRAPPER_TO_PRIMITIVE_TYPE = Collections.unmodifiableMap(wrapToPrim);
}
private static void add(Map<Class<?>, Class<?>> forward,
Map<Class<?>, Class<?>> backward, Class<?> key, Class<?> value) {
forward.put(key, value);
backward.put(value, key);
}
/**
* Returns true if this type is a primitive.
*/
public static boolean isPrimitive(Type type) {
return PRIMITIVE_TO_WRAPPER_TYPE.containsKey(type);
}
/**
* Returns {@code true} if {@code type} is one of the nine
* primitive-wrapper types, such as {@link Integer}.
*
* @see Class#isPrimitive
*/
public static boolean isWrapperType(Type type) {
return WRAPPER_TO_PRIMITIVE_TYPE.containsKey(
$Gson$Preconditions.checkNotNull(type));
}
/**
* Returns the corresponding wrapper type of {@code type} if it is a primitive
* type; otherwise returns {@code type} itself. Idempotent.
* <pre>
* wrap(int.class) == Integer.class
* wrap(Integer.class) == Integer.class
* wrap(String.class) == String.class
* </pre>
*/
public static <T> Class<T> wrap(Class<T> type) {
// cast is safe: long.class and Long.class are both of type Class<Long>
@SuppressWarnings("unchecked")
Class<T> wrapped = (Class<T>) PRIMITIVE_TO_WRAPPER_TYPE.get(
$Gson$Preconditions.checkNotNull(type));
return (wrapped == null) ? type : wrapped;
}
/**
* Returns the corresponding primitive type of {@code type} if it is a
* wrapper type; otherwise returns {@code type} itself. Idempotent.
* <pre>
* unwrap(Integer.class) == int.class
* unwrap(int.class) == int.class
* unwrap(String.class) == String.class
* </pre>
*/
public static <T> Class<T> unwrap(Class<T> type) {
// cast is safe: long.class and Long.class are both of type Class<Long>
@SuppressWarnings("unchecked")
Class<T> unwrapped = (Class<T>) WRAPPER_TO_PRIMITIVE_TYPE.get(
$Gson$Preconditions.checkNotNull(type));
return (unwrapped == null) ? type : unwrapped;
}
}

View File

@ -0,0 +1,116 @@
/*
* Copyright (C) 2010 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.massivecraft.core.lib.gson2.internal;
import com.massivecraft.core.lib.gson2.JsonElement;
import com.massivecraft.core.lib.gson2.JsonIOException;
import com.massivecraft.core.lib.gson2.JsonNull;
import com.massivecraft.core.lib.gson2.JsonParseException;
import com.massivecraft.core.lib.gson2.JsonSyntaxException;
import com.massivecraft.core.lib.gson2.internal.bind.TypeAdapters;
import com.massivecraft.core.lib.gson2.stream.JsonReader;
import com.massivecraft.core.lib.gson2.stream.JsonWriter;
import com.massivecraft.core.lib.gson2.stream.MalformedJsonException;
import java.io.EOFException;
import java.io.IOException;
import java.io.Writer;
/**
* Reads and writes GSON parse trees over streams.
*/
public final class Streams {
/**
* Takes a reader in any state and returns the next value as a JsonElement.
*/
public static JsonElement parse(JsonReader reader) throws JsonParseException {
boolean isEmpty = true;
try {
reader.peek();
isEmpty = false;
return TypeAdapters.JSON_ELEMENT.read(reader);
} catch (EOFException e) {
/*
* For compatibility with JSON 1.5 and earlier, we return a JsonNull for
* empty documents instead of throwing.
*/
if (isEmpty) {
return JsonNull.INSTANCE;
}
throw new JsonIOException(e);
} catch (MalformedJsonException e) {
throw new JsonSyntaxException(e);
} catch (IOException e) {
throw new JsonIOException(e);
} catch (NumberFormatException e) {
throw new JsonSyntaxException(e);
}
}
/**
* Writes the JSON element to the writer, recursively.
*/
public static void write(JsonElement element, JsonWriter writer) throws IOException {
TypeAdapters.JSON_ELEMENT.write(writer, element);
}
public static Writer writerForAppendable(Appendable appendable) {
return appendable instanceof Writer ? (Writer) appendable : new AppendableWriter(appendable);
}
/**
* Adapts an {@link Appendable} so it can be passed anywhere a {@link Writer}
* is used.
*/
private static class AppendableWriter extends Writer {
private final Appendable appendable;
private final CurrentWrite currentWrite = new CurrentWrite();
private AppendableWriter(Appendable appendable) {
this.appendable = appendable;
}
@Override public void write(char[] chars, int offset, int length) throws IOException {
currentWrite.chars = chars;
appendable.append(currentWrite, offset, offset + length);
}
@Override public void write(int i) throws IOException {
appendable.append((char) i);
}
@Override public void flush() {}
@Override public void close() {}
/**
* A mutable char sequence pointing at a single char[].
*/
static class CurrentWrite implements CharSequence {
char[] chars;
public int length() {
return chars.length;
}
public char charAt(int i) {
return chars[i];
}
public CharSequence subSequence(int start, int end) {
return new String(chars, start, end - start);
}
}
}
}

View File

@ -0,0 +1,104 @@
/*
* Copyright (C) 2011 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.massivecraft.core.lib.gson2.internal;
import java.io.ObjectInputStream;
import java.io.ObjectStreamClass;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
/**
* Do sneaky things to allocate objects without invoking their constructors.
*
* @author Joel Leitch
* @author Jesse Wilson
*/
public abstract class UnsafeAllocator {
public abstract <T> T newInstance(Class<T> c) throws Exception;
public static UnsafeAllocator create() {
// try JVM
// public class Unsafe {
// public Object allocateInstance(Class<?> type);
// }
try {
Class<?> unsafeClass = Class.forName("sun.misc.Unsafe");
Field f = unsafeClass.getDeclaredField("theUnsafe");
f.setAccessible(true);
final Object unsafe = f.get(null);
final Method allocateInstance = unsafeClass.getMethod("allocateInstance", Class.class);
return new UnsafeAllocator() {
@Override
@SuppressWarnings("unchecked")
public <T> T newInstance(Class<T> c) throws Exception {
return (T) allocateInstance.invoke(unsafe, c);
}
};
} catch (Exception ignored) {
}
// try dalvikvm, pre-gingerbread
// public class ObjectInputStream {
// private static native Object newInstance(
// Class<?> instantiationClass, Class<?> constructorClass);
// }
try {
final Method newInstance = ObjectInputStream.class
.getDeclaredMethod("newInstance", Class.class, Class.class);
newInstance.setAccessible(true);
return new UnsafeAllocator() {
@Override
@SuppressWarnings("unchecked")
public <T> T newInstance(Class<T> c) throws Exception {
return (T) newInstance.invoke(null, c, Object.class);
}
};
} catch (Exception ignored) {
}
// try dalvikvm, post-gingerbread
// public class ObjectStreamClass {
// private static native int getConstructorId(Class<?> c);
// private static native Object newInstance(Class<?> instantiationClass, int methodId);
// }
try {
Method getConstructorId = ObjectStreamClass.class
.getDeclaredMethod("getConstructorId", Class.class);
getConstructorId.setAccessible(true);
final int constructorId = (Integer) getConstructorId.invoke(null, Object.class);
final Method newInstance = ObjectStreamClass.class
.getDeclaredMethod("newInstance", Class.class, int.class);
newInstance.setAccessible(true);
return new UnsafeAllocator() {
@Override
@SuppressWarnings("unchecked")
public <T> T newInstance(Class<T> c) throws Exception {
return (T) newInstance.invoke(null, c, constructorId);
}
};
} catch (Exception ignored) {
}
// give up
return new UnsafeAllocator() {
@Override
public <T> T newInstance(Class<T> c) {
throw new UnsupportedOperationException("Cannot allocate " + c);
}
};
}
}

View File

@ -0,0 +1,96 @@
/*
* Copyright (C) 2011 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.massivecraft.core.lib.gson2.internal.bind;
import java.io.IOException;
import java.lang.reflect.Array;
import java.lang.reflect.GenericArrayType;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.List;
import com.massivecraft.core.lib.gson2.internal.$Gson$Types;
import com.massivecraft.core.lib.gson2.reflect.TypeToken;
import com.massivecraft.core.lib.gson2.stream.JsonReader;
import com.massivecraft.core.lib.gson2.stream.JsonToken;
import com.massivecraft.core.lib.gson2.stream.JsonWriter;
/**
* Adapt an array of objects.
*/
public final class ArrayTypeAdapter<E> extends TypeAdapter<Object> {
public static final Factory FACTORY = new Factory() {
@SuppressWarnings({"unchecked", "rawtypes"})
public <T> TypeAdapter<T> create(MiniGson context, TypeToken<T> typeToken) {
Type type = typeToken.getType();
if (!(type instanceof GenericArrayType || type instanceof Class && ((Class<?>) type).isArray())) {
return null;
}
Type componentType = $Gson$Types.getArrayComponentType(type);
TypeAdapter<?> componentTypeAdapter = context.getAdapter(TypeToken.get(componentType));
// create() doesn't define a type parameter
TypeAdapter<T> result = new ArrayTypeAdapter(
context, componentTypeAdapter, $Gson$Types.getRawType(componentType));
return result;
}
};
private final Class<E> componentType;
private final TypeAdapter<E> componentTypeAdapter;
public ArrayTypeAdapter(MiniGson context, TypeAdapter<E> componentTypeAdapter, Class<E> componentType) {
this.componentTypeAdapter =
new TypeAdapterRuntimeTypeWrapper<E>(context, componentTypeAdapter, componentType);
this.componentType = componentType;
}
public Object read(JsonReader reader) throws IOException {
if (reader.peek() == JsonToken.NULL) {
reader.nextNull();
return null;
}
List<E> list = new ArrayList<E>();
reader.beginArray();
while (reader.hasNext()) {
E instance = componentTypeAdapter.read(reader);
list.add(instance);
}
reader.endArray();
Object array = Array.newInstance(componentType, list.size());
for (int i = 0; i < list.size(); i++) {
Array.set(array, i, list.get(i));
}
return array;
}
@SuppressWarnings("unchecked")
@Override public void write(JsonWriter writer, Object array) throws IOException {
if (array == null) {
writer.nullValue(); // TODO: better policy here?
return;
}
writer.beginArray();
for (int i = 0, length = Array.getLength(array); i < length; i++) {
final E value = (E) Array.get(array, i);
componentTypeAdapter.write(writer, value);
}
writer.endArray();
}
}

View File

@ -0,0 +1,51 @@
/*
* Copyright (C) 2011 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.massivecraft.core.lib.gson2.internal.bind;
import com.massivecraft.core.lib.gson2.JsonSyntaxException;
import com.massivecraft.core.lib.gson2.stream.JsonReader;
import com.massivecraft.core.lib.gson2.stream.JsonToken;
import com.massivecraft.core.lib.gson2.stream.JsonWriter;
import java.io.IOException;
import java.math.BigDecimal;
/**
* Adapts a BigDecimal type to and from its JSON representation.
*
* @author Joel Leitch
*/
public final class BigDecimalTypeAdapter extends TypeAdapter<BigDecimal> {
@Override
public BigDecimal read(JsonReader reader) throws IOException {
if (reader.peek() == JsonToken.NULL) {
reader.nextNull();
return null;
}
try {
return new BigDecimal(reader.nextString());
} catch (NumberFormatException e) {
throw new JsonSyntaxException(e);
}
}
@Override
public void write(JsonWriter writer, BigDecimal value) throws IOException {
writer.value(value);
}
}

View File

@ -0,0 +1,51 @@
/*
* Copyright (C) 2011 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.massivecraft.core.lib.gson2.internal.bind;
import com.massivecraft.core.lib.gson2.JsonSyntaxException;
import com.massivecraft.core.lib.gson2.stream.JsonReader;
import com.massivecraft.core.lib.gson2.stream.JsonToken;
import com.massivecraft.core.lib.gson2.stream.JsonWriter;
import java.io.IOException;
import java.math.BigInteger;
/**
* Adapts a BigInteger type to and from its JSON representation.
*
* @author Joel Leitch
*/
public final class BigIntegerTypeAdapter extends TypeAdapter<BigInteger> {
@Override
public BigInteger read(JsonReader reader) throws IOException {
if (reader.peek() == JsonToken.NULL) {
reader.nextNull();
return null;
}
try {
return new BigInteger(reader.nextString());
} catch (NumberFormatException e) {
throw new JsonSyntaxException(e);
}
}
@Override
public void write(JsonWriter writer, BigInteger value) throws IOException {
writer.value(value);
}
}

View File

@ -0,0 +1,99 @@
/*
* Copyright (C) 2011 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.massivecraft.core.lib.gson2.internal.bind;
import com.massivecraft.core.lib.gson2.internal.$Gson$Types;
import com.massivecraft.core.lib.gson2.internal.ConstructorConstructor;
import com.massivecraft.core.lib.gson2.internal.ObjectConstructor;
import com.massivecraft.core.lib.gson2.reflect.TypeToken;
import com.massivecraft.core.lib.gson2.stream.JsonReader;
import com.massivecraft.core.lib.gson2.stream.JsonToken;
import com.massivecraft.core.lib.gson2.stream.JsonWriter;
import java.io.IOException;
import java.lang.reflect.Type;
import java.util.Collection;
/**
* Adapt a homogeneous collection of objects.
*/
public final class CollectionTypeAdapterFactory implements TypeAdapter.Factory {
private final ConstructorConstructor constructorConstructor;
public CollectionTypeAdapterFactory(ConstructorConstructor constructorConstructor) {
this.constructorConstructor = constructorConstructor;
}
public <T> TypeAdapter<T> create(MiniGson context, TypeToken<T> typeToken) {
Type type = typeToken.getType();
Class<? super T> rawType = typeToken.getRawType();
if (!Collection.class.isAssignableFrom(rawType)) {
return null;
}
Type elementType = $Gson$Types.getCollectionElementType(type, rawType);
TypeAdapter<?> elementTypeAdapter = context.getAdapter(TypeToken.get(elementType));
ObjectConstructor<T> constructor = constructorConstructor.getConstructor(typeToken);
@SuppressWarnings({"unchecked", "rawtypes"}) // create() doesn't define a type parameter
TypeAdapter<T> result = new Adapter(context, elementType, elementTypeAdapter, constructor);
return result;
}
private final class Adapter<E> extends TypeAdapter<Collection<E>> {
private final TypeAdapter<E> elementTypeAdapter;
private final ObjectConstructor<? extends Collection<E>> constructor;
public Adapter(MiniGson context, Type elementType,
TypeAdapter<E> elementTypeAdapter,
ObjectConstructor<? extends Collection<E>> constructor) {
this.elementTypeAdapter =
new TypeAdapterRuntimeTypeWrapper<E>(context, elementTypeAdapter, elementType);
this.constructor = constructor;
}
public Collection<E> read(JsonReader reader) throws IOException {
if (reader.peek() == JsonToken.NULL) {
reader.nextNull();
return null;
}
Collection<E> collection = constructor.construct();
reader.beginArray();
while (reader.hasNext()) {
E instance = elementTypeAdapter.read(reader);
collection.add(instance);
}
reader.endArray();
return collection;
}
public void write(JsonWriter writer, Collection<E> collection) throws IOException {
if (collection == null) {
writer.nullValue(); // TODO: better policy here?
return;
}
writer.beginArray();
for (E element : collection) {
elementTypeAdapter.write(writer, element);
}
writer.endArray();
}
}
}

View File

@ -0,0 +1,91 @@
/*
* Copyright (C) 2011 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.massivecraft.core.lib.gson2.internal.bind;
import com.massivecraft.core.lib.gson2.JsonSyntaxException;
import com.massivecraft.core.lib.gson2.reflect.TypeToken;
import com.massivecraft.core.lib.gson2.stream.JsonReader;
import com.massivecraft.core.lib.gson2.stream.JsonToken;
import com.massivecraft.core.lib.gson2.stream.JsonWriter;
import java.io.IOException;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
import java.util.TimeZone;
/**
* Adapter for Date. Although this class appears stateless, it is not.
* DateFormat captures its time zone and locale when it is created, which gives
* this class state. DateFormat isn't thread safe either, so this class has
* to synchronize its read and write methods.
*/
public final class DateTypeAdapter extends TypeAdapter<Date> {
public static final Factory FACTORY = new Factory() {
@SuppressWarnings("unchecked") // we use a runtime check to make sure the 'T's equal
public <T> TypeAdapter<T> create(MiniGson context, TypeToken<T> typeToken) {
return typeToken.getRawType() == Date.class ? (TypeAdapter<T>) new DateTypeAdapter() : null;
}
};
private final DateFormat enUsFormat
= DateFormat.getDateTimeInstance(DateFormat.DEFAULT, DateFormat.DEFAULT, Locale.US);
private final DateFormat localFormat
= DateFormat.getDateTimeInstance(DateFormat.DEFAULT, DateFormat.DEFAULT);
private final DateFormat iso8601Format = buildIso8601Format();
private static DateFormat buildIso8601Format() {
DateFormat iso8601Format = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'", Locale.US);
iso8601Format.setTimeZone(TimeZone.getTimeZone("UTC"));
return iso8601Format;
}
@Override public Date read(JsonReader reader) throws IOException {
if (reader.peek() == JsonToken.NULL) {
reader.nextNull();
return null;
}
return deserializeToDate(reader.nextString());
}
private synchronized Date deserializeToDate(String json) {
try {
return localFormat.parse(json);
} catch (ParseException ignored) {
}
try {
return enUsFormat.parse(json);
} catch (ParseException ignored) {
}
try {
return iso8601Format.parse(json);
} catch (ParseException e) {
throw new JsonSyntaxException(json, e);
}
}
@Override public synchronized void write(JsonWriter writer, Date value) throws IOException {
if (value == null) {
writer.nullValue();
return;
}
String dateFormatAsString = enUsFormat.format(value);
writer.value(dateFormatAsString);
}
}

View File

@ -0,0 +1,80 @@
/*
* Copyright (C) 2011 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.massivecraft.core.lib.gson2.internal.bind;
import com.massivecraft.core.lib.gson2.ExclusionStrategy;
import com.massivecraft.core.lib.gson2.reflect.TypeToken;
import com.massivecraft.core.lib.gson2.stream.JsonReader;
import com.massivecraft.core.lib.gson2.stream.JsonWriter;
import java.io.IOException;
/**
* This type adapter skips values using an exclusion strategy. It may delegate
* to another type adapter if only one direction is excluded.
*/
public final class ExcludedTypeAdapterFactory implements TypeAdapter.Factory {
private final ExclusionStrategy serializationExclusionStrategy;
private final ExclusionStrategy deserializationExclusionStrategy;
public ExcludedTypeAdapterFactory(ExclusionStrategy serializationExclusionStrategy,
ExclusionStrategy deserializationExclusionStrategy) {
this.serializationExclusionStrategy = serializationExclusionStrategy;
this.deserializationExclusionStrategy = deserializationExclusionStrategy;
}
public <T> TypeAdapter<T> create(final MiniGson context, final TypeToken<T> type) {
Class<?> rawType = type.getRawType();
final boolean skipSerialize = serializationExclusionStrategy.shouldSkipClass(rawType);
final boolean skipDeserialize = deserializationExclusionStrategy.shouldSkipClass(rawType);
if (!skipSerialize && !skipDeserialize) {
return null;
}
return new TypeAdapter<T>() {
/**
* The delegate is lazily created because it may not be needed, and
* creating it may fail.
*/
private TypeAdapter<T> delegate;
@Override public T read(JsonReader reader) throws IOException {
if (skipDeserialize) {
reader.skipValue();
return null;
}
return delegate().read(reader);
}
@Override public void write(JsonWriter writer, T value) throws IOException {
if (skipSerialize) {
writer.nullValue();
return;
}
delegate().write(writer, value);
}
private TypeAdapter<T> delegate() {
TypeAdapter<T> d = delegate;
return d != null
? d
: (delegate = context.getNextAdapter(ExcludedTypeAdapterFactory.this, type));
}
};
}
}

View File

@ -0,0 +1,219 @@
/*
* Copyright (C) 2011 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.massivecraft.core.lib.gson2.internal.bind;
import com.massivecraft.core.lib.gson2.JsonArray;
import com.massivecraft.core.lib.gson2.JsonElement;
import com.massivecraft.core.lib.gson2.JsonNull;
import com.massivecraft.core.lib.gson2.JsonObject;
import com.massivecraft.core.lib.gson2.JsonPrimitive;
import com.massivecraft.core.lib.gson2.stream.JsonReader;
import com.massivecraft.core.lib.gson2.stream.JsonToken;
import java.io.IOException;
import java.io.Reader;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
/**
* This reader walks the elements of a JsonElement as if it was coming from a
* character stream.
*
* @author Jesse Wilson
*/
public final class JsonElementReader extends JsonReader {
private static final Reader UNREADABLE_READER = new Reader() {
@Override public int read(char[] buffer, int offset, int count) throws IOException {
throw new AssertionError();
}
@Override public void close() throws IOException {
throw new AssertionError();
}
};
private static final Object SENTINEL_CLOSED = new Object();
private final List<Object> stack = new ArrayList<Object>();
public JsonElementReader(JsonElement element) {
super(UNREADABLE_READER);
stack.add(element);
}
@Override public void beginArray() throws IOException {
expect(JsonToken.BEGIN_ARRAY);
JsonArray array = (JsonArray) peekStack();
stack.add(array.iterator());
}
@Override public void endArray() throws IOException {
expect(JsonToken.END_ARRAY);
popStack(); // empty iterator
popStack(); // array
}
@Override public void beginObject() throws IOException {
expect(JsonToken.BEGIN_OBJECT);
JsonObject object = (JsonObject) peekStack();
stack.add(object.entrySet().iterator());
}
@Override public void endObject() throws IOException {
expect(JsonToken.END_OBJECT);
popStack(); // empty iterator
popStack(); // object
}
@Override public boolean hasNext() throws IOException {
JsonToken token = peek();
return token != JsonToken.END_OBJECT && token != JsonToken.END_ARRAY;
}
@Override public JsonToken peek() throws IOException {
if (stack.isEmpty()) {
return JsonToken.END_DOCUMENT;
}
Object o = peekStack();
if (o instanceof Iterator) {
boolean isObject = stack.get(stack.size() - 2) instanceof JsonObject;
Iterator<?> iterator = (Iterator<?>) o;
if (iterator.hasNext()) {
if (isObject) {
return JsonToken.NAME;
} else {
stack.add(iterator.next());
return peek();
}
} else {
return isObject ? JsonToken.END_OBJECT : JsonToken.END_ARRAY;
}
} else if (o instanceof JsonObject) {
return JsonToken.BEGIN_OBJECT;
} else if (o instanceof JsonArray) {
return JsonToken.BEGIN_ARRAY;
} else if (o instanceof JsonPrimitive) {
JsonPrimitive primitive = (JsonPrimitive) o;
if (primitive.isString()) {
return JsonToken.STRING;
} else if (primitive.isBoolean()) {
return JsonToken.BOOLEAN;
} else if (primitive.isNumber()) {
return JsonToken.NUMBER;
} else {
throw new AssertionError();
}
} else if (o instanceof JsonNull) {
return JsonToken.NULL;
} else if (o == SENTINEL_CLOSED) {
throw new IllegalStateException("JsonReader is closed");
} else {
throw new AssertionError();
}
}
private Object peekStack() {
return stack.get(stack.size() - 1);
}
private Object popStack() {
return stack.remove(stack.size() - 1);
}
private void expect(JsonToken expected) throws IOException {
if (peek() != expected) {
throw new IllegalStateException("Expected " + expected + " but was " + peek());
}
}
@Override public String nextName() throws IOException {
expect(JsonToken.NAME);
Iterator<?> i = (Iterator<?>) peekStack();
Map.Entry<?, ?> entry = (Map.Entry<?, ?>) i.next();
stack.add(entry.getValue());
return (String) entry.getKey();
}
@Override public String nextString() throws IOException {
JsonToken token = peek();
if (token != JsonToken.STRING && token != JsonToken.NUMBER) {
throw new IllegalStateException("Expected " + JsonToken.STRING + " but was " + token);
}
return ((JsonPrimitive) popStack()).getAsString();
}
@Override public boolean nextBoolean() throws IOException {
expect(JsonToken.BOOLEAN);
return ((JsonPrimitive) popStack()).getAsBoolean();
}
@Override public void nextNull() throws IOException {
expect(JsonToken.NULL);
popStack();
}
@Override public double nextDouble() throws IOException {
JsonToken token = peek();
if (token != JsonToken.NUMBER && token != JsonToken.STRING) {
throw new IllegalStateException("Expected " + JsonToken.NUMBER + " but was " + token);
}
double result = ((JsonPrimitive) peekStack()).getAsDouble();
if (!isLenient() && (Double.isNaN(result) || Double.isInfinite(result))) {
throw new NumberFormatException("JSON forbids NaN and infinities: " + result);
}
popStack();
return result;
}
@Override public long nextLong() throws IOException {
JsonToken token = peek();
if (token != JsonToken.NUMBER && token != JsonToken.STRING) {
throw new IllegalStateException("Expected " + JsonToken.NUMBER + " but was " + token);
}
long result = ((JsonPrimitive) peekStack()).getAsLong();
popStack();
return result;
}
@Override public int nextInt() throws IOException {
JsonToken token = peek();
if (token != JsonToken.NUMBER && token != JsonToken.STRING) {
throw new IllegalStateException("Expected " + JsonToken.NUMBER + " but was " + token);
}
int result = ((JsonPrimitive) peekStack()).getAsInt();
popStack();
return result;
}
@Override public void close() throws IOException {
stack.clear();
stack.add(SENTINEL_CLOSED);
}
@Override public void skipValue() throws IOException {
if (peek() == JsonToken.NAME) {
nextName();
} else {
popStack();
}
}
@Override public String toString() {
return getClass().getSimpleName();
}
}

View File

@ -0,0 +1,201 @@
/*
* Copyright (C) 2011 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.massivecraft.core.lib.gson2.internal.bind;
import com.massivecraft.core.lib.gson2.JsonArray;
import com.massivecraft.core.lib.gson2.JsonElement;
import com.massivecraft.core.lib.gson2.JsonNull;
import com.massivecraft.core.lib.gson2.JsonObject;
import com.massivecraft.core.lib.gson2.JsonPrimitive;
import com.massivecraft.core.lib.gson2.stream.JsonWriter;
import java.io.IOException;
import java.io.Writer;
import java.util.ArrayList;
import java.util.List;
/**
* This writer creates a JsonElement.
*/
public final class JsonElementWriter extends JsonWriter {
private static final Writer UNWRITABLE_WRITER = new Writer() {
@Override public void write(char[] buffer, int offset, int counter) {
throw new AssertionError();
}
@Override public void flush() throws IOException {
throw new AssertionError();
}
@Override public void close() throws IOException {
throw new AssertionError();
}
};
/** Added to the top of the stack when this writer is closed to cause following ops to fail. */
private static final JsonPrimitive SENTINEL_CLOSED = new JsonPrimitive("closed");
/** The JsonElements and JsonArrays under modification, outermost to innermost. */
private final List<JsonElement> stack = new ArrayList<JsonElement>();
/** The name for the next JSON object value. If non-null, the top of the stack is a JsonObject. */
private String pendingName;
/** the JSON element constructed by this writer. */
private JsonElement product = JsonNull.INSTANCE; // TODO: is this really what we want?;
public JsonElementWriter() {
super(UNWRITABLE_WRITER);
}
/**
* Returns the top level object produced by this writer.
*/
public JsonElement get() {
if (!stack.isEmpty()) {
throw new IllegalStateException("Expected one JSON element but was " + stack);
}
return product;
}
private JsonElement peek() {
return stack.get(stack.size() - 1);
}
private void put(JsonElement value) {
if (pendingName != null) {
if (!value.isJsonNull() || getSerializeNulls()) {
JsonObject object = (JsonObject) peek();
object.add(pendingName, value);
}
pendingName = null;
} else if (stack.isEmpty()) {
product = value;
} else {
JsonElement element = peek();
if (element instanceof JsonArray) {
((JsonArray) element).add(value);
} else {
throw new IllegalStateException();
}
}
}
@Override public JsonWriter beginArray() throws IOException {
JsonArray array = new JsonArray();
put(array);
stack.add(array);
return this;
}
@Override public JsonWriter endArray() throws IOException {
if (stack.isEmpty() || pendingName != null) {
throw new IllegalStateException();
}
JsonElement element = peek();
if (element instanceof JsonArray) {
stack.remove(stack.size() - 1);
return this;
}
throw new IllegalStateException();
}
@Override public JsonWriter beginObject() throws IOException {
JsonObject object = new JsonObject();
put(object);
stack.add(object);
return this;
}
@Override public JsonWriter endObject() throws IOException {
if (stack.isEmpty() || pendingName != null) {
throw new IllegalStateException();
}
JsonElement element = peek();
if (element instanceof JsonObject) {
stack.remove(stack.size() - 1);
return this;
}
throw new IllegalStateException();
}
@Override public JsonWriter name(String name) throws IOException {
if (stack.isEmpty() || pendingName != null) {
throw new IllegalStateException();
}
JsonElement element = peek();
if (element instanceof JsonObject) {
pendingName = name;
return this;
}
throw new IllegalStateException();
}
@Override public JsonWriter value(String value) throws IOException {
if (value == null) {
return nullValue();
}
put(new JsonPrimitive(value));
return this;
}
@Override public JsonWriter nullValue() throws IOException {
put(JsonNull.INSTANCE);
return this;
}
@Override public JsonWriter value(boolean value) throws IOException {
put(new JsonPrimitive(value));
return this;
}
@Override public JsonWriter value(double value) throws IOException {
if (!isLenient() && (Double.isNaN(value) || Double.isInfinite(value))) {
throw new IllegalArgumentException("JSON forbids NaN and infinities: " + value);
}
put(new JsonPrimitive(value));
return this;
}
@Override public JsonWriter value(long value) throws IOException {
put(new JsonPrimitive(value));
return this;
}
@Override public JsonWriter value(Number value) throws IOException {
if (value == null) {
return nullValue();
}
if (!isLenient()) {
double d = value.doubleValue();
if (Double.isNaN(d) || Double.isInfinite(d)) {
throw new IllegalArgumentException("JSON forbids NaN and infinities: " + value);
}
}
put(new JsonPrimitive(value));
return this;
}
@Override public void flush() throws IOException {
}
@Override public void close() throws IOException {
if (!stack.isEmpty()) {
throw new IOException("Incomplete document");
}
stack.add(SENTINEL_CLOSED);
}
}

View File

@ -0,0 +1,261 @@
/*
* Copyright (C) 2011 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.massivecraft.core.lib.gson2.internal.bind;
import com.massivecraft.core.lib.gson2.JsonElement;
import com.massivecraft.core.lib.gson2.JsonPrimitive;
import com.massivecraft.core.lib.gson2.JsonSyntaxException;
import com.massivecraft.core.lib.gson2.internal.$Gson$Types;
import com.massivecraft.core.lib.gson2.internal.ConstructorConstructor;
import com.massivecraft.core.lib.gson2.internal.ObjectConstructor;
import com.massivecraft.core.lib.gson2.internal.Streams;
import com.massivecraft.core.lib.gson2.reflect.TypeToken;
import com.massivecraft.core.lib.gson2.stream.JsonReader;
import com.massivecraft.core.lib.gson2.stream.JsonToken;
import com.massivecraft.core.lib.gson2.stream.JsonWriter;
import java.io.IOException;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
/**
* Adapts maps to either JSON objects or JSON arrays.
*
* <h3>Maps as JSON objects</h3>
* For primitive keys or when complex map key serialization is not enabled, this
* converts Java {@link Map Maps} to JSON Objects. This requires that map keys
* can be serialized as strings; this is insufficient for some key types. For
* example, consider a map whose keys are points on a grid. The default JSON
* form encodes reasonably: <pre> {@code
* Map<Point, String> original = new LinkedHashMap<Point, String>();
* original.put(new Point(5, 6), "a");
* original.put(new Point(8, 8), "b");
* System.out.println(gson.toJson(original, type));
* }</pre>
* The above code prints this JSON object:<pre> {@code
* {
* "(5,6)": "a",
* "(8,8)": "b"
* }
* }</pre>
* But GSON is unable to deserialize this value because the JSON string name is
* just the {@link Object#toString() toString()} of the map key. Attempting to
* convert the above JSON to an object fails with a parse exception:
* <pre>com.google.gson.JsonParseException: Expecting object found: "(5,6)"
* at com.google.gson.JsonObjectDeserializationVisitor.visitFieldUsingCustomHandler
* at com.google.gson.ObjectNavigator.navigateClassFields
* ...</pre>
*
* <h3>Maps as JSON arrays</h3>
* An alternative approach taken by this type adapter when it is required and
* complex map key serialization is enabled is to encode maps as arrays of map
* entries. Each map entry is a two element array containing a key and a value.
* This approach is more flexible because any type can be used as the map's key;
* not just strings. But it's also less portable because the receiver of such
* JSON must be aware of the map entry convention.
*
* <p>Register this adapter when you are creating your GSON instance.
* <pre> {@code
* Gson gson = new GsonBuilder()
* .registerTypeAdapter(Map.class, new MapAsArrayTypeAdapter())
* .create();
* }</pre>
* This will change the structure of the JSON emitted by the code above. Now we
* get an array. In this case the arrays elements are map entries:
* <pre> {@code
* [
* [
* {
* "x": 5,
* "y": 6
* },
* "a",
* ],
* [
* {
* "x": 8,
* "y": 8
* },
* "b"
* ]
* ]
* }</pre>
* This format will serialize and deserialize just fine as long as this adapter
* is registered.
*/
public final class MapTypeAdapterFactory implements TypeAdapter.Factory {
private final ConstructorConstructor constructorConstructor;
private final boolean complexMapKeySerialization;
public MapTypeAdapterFactory(ConstructorConstructor constructorConstructor,
boolean complexMapKeySerialization) {
this.constructorConstructor = constructorConstructor;
this.complexMapKeySerialization = complexMapKeySerialization;
}
public <T> TypeAdapter<T> create(MiniGson context, TypeToken<T> typeToken) {
Type type = typeToken.getType();
Class<? super T> rawType = typeToken.getRawType();
if (!Map.class.isAssignableFrom(rawType)) {
return null;
}
Class<?> rawTypeOfSrc = $Gson$Types.getRawType(type);
Type[] keyAndValueTypes = $Gson$Types.getMapKeyAndValueTypes(type, rawTypeOfSrc);
TypeAdapter<?> keyAdapter = getKeyAdapter(context, keyAndValueTypes[0]);
TypeAdapter<?> valueAdapter = context.getAdapter(TypeToken.get(keyAndValueTypes[1]));
ObjectConstructor<T> constructor = constructorConstructor.getConstructor(typeToken);
@SuppressWarnings({"unchecked", "rawtypes"})
// we don't define a type parameter for the key or value types
TypeAdapter<T> result = new Adapter(context, keyAndValueTypes[0], keyAdapter,
keyAndValueTypes[1], valueAdapter, constructor);
return result;
}
/**
* Returns a type adapter that writes the value as a string.
*/
private TypeAdapter<?> getKeyAdapter(MiniGson context, Type keyType) {
return (keyType == boolean.class || keyType == Boolean.class)
? TypeAdapters.BOOLEAN_AS_STRING
: context.getAdapter(TypeToken.get(keyType));
}
private final class Adapter<K, V> extends TypeAdapter<Map<K, V>> {
private final TypeAdapter<K> keyTypeAdapter;
private final TypeAdapter<V> valueTypeAdapter;
private final ObjectConstructor<? extends Map<K, V>> constructor;
public Adapter(MiniGson context, Type keyType, TypeAdapter<K> keyTypeAdapter,
Type valueType, TypeAdapter<V> valueTypeAdapter,
ObjectConstructor<? extends Map<K, V>> constructor) {
this.keyTypeAdapter =
new TypeAdapterRuntimeTypeWrapper<K>(context, keyTypeAdapter, keyType);
this.valueTypeAdapter =
new TypeAdapterRuntimeTypeWrapper<V>(context, valueTypeAdapter, valueType);
this.constructor = constructor;
}
public Map<K, V> read(JsonReader reader) throws IOException {
JsonToken peek = reader.peek();
if (peek == JsonToken.NULL) {
reader.nextNull();
return null;
}
Map<K, V> map = constructor.construct();
if (peek == JsonToken.BEGIN_ARRAY) {
reader.beginArray();
while (reader.hasNext()) {
reader.beginArray(); // entry array
K key = keyTypeAdapter.read(reader);
V value = valueTypeAdapter.read(reader);
V replaced = map.put(key, value);
if (replaced != null) {
throw new JsonSyntaxException("duplicate key: " + key);
}
reader.endArray();
}
reader.endArray();
} else {
reader.beginObject();
while (reader.hasNext()) {
String keyString = reader.nextName();
K key = keyTypeAdapter.fromJsonElement(new JsonPrimitive(keyString));
V value = valueTypeAdapter.read(reader);
V replaced = map.put(key, value);
if (replaced != null) {
throw new JsonSyntaxException("duplicate key: " + key);
}
}
reader.endObject();
}
return map;
}
public void write(JsonWriter writer, Map<K, V> map) throws IOException {
if (map == null) {
writer.nullValue(); // TODO: better policy here?
return;
}
if (!complexMapKeySerialization) {
writer.beginObject();
for (Map.Entry<K, V> entry : map.entrySet()) {
writer.name(String.valueOf(entry.getKey()));
valueTypeAdapter.write(writer, entry.getValue());
}
writer.endObject();
return;
}
boolean hasComplexKeys = false;
List<JsonElement> keys = new ArrayList<JsonElement>(map.size());
List<V> values = new ArrayList<V>(map.size());
for (Map.Entry<K, V> entry : map.entrySet()) {
JsonElement keyElement = keyTypeAdapter.toJsonElement(entry.getKey());
keys.add(keyElement);
values.add(entry.getValue());
hasComplexKeys |= keyElement.isJsonArray() || keyElement.isJsonObject();
}
if (hasComplexKeys) {
writer.beginArray();
for (int i = 0; i < keys.size(); i++) {
writer.beginArray(); // entry array
Streams.write(keys.get(i), writer);
valueTypeAdapter.write(writer, values.get(i));
writer.endArray();
}
writer.endArray();
} else {
writer.beginObject();
for (int i = 0; i < keys.size(); i++) {
JsonElement keyElement = keys.get(i);
writer.name(keyToString(keyElement));
valueTypeAdapter.write(writer, values.get(i));
}
writer.endObject();
}
}
private String keyToString(JsonElement keyElement) {
if (keyElement.isJsonPrimitive()) {
JsonPrimitive primitive = keyElement.getAsJsonPrimitive();
if (primitive.isNumber()) {
return String.valueOf(primitive.getAsNumber());
} else if (primitive.isBoolean()) {
return Boolean.toString(primitive.getAsBoolean());
} else if (primitive.isString()) {
return primitive.getAsString();
} else {
throw new AssertionError();
}
} else if (keyElement.isJsonNull()) {
return "null";
} else {
throw new AssertionError();
}
}
}
}

View File

@ -0,0 +1,207 @@
/*
* Copyright (C) 2011 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.massivecraft.core.lib.gson2.internal.bind;
import com.massivecraft.core.lib.gson2.internal.ConstructorConstructor;
import com.massivecraft.core.lib.gson2.reflect.TypeToken;
import com.massivecraft.core.lib.gson2.stream.JsonReader;
import com.massivecraft.core.lib.gson2.stream.JsonWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* A basic binding between JSON and Java objects.
*/
public final class MiniGson {
/**
* This thread local guards against reentrant calls to getAdapter(). In
* certain object graphs, creating an adapter for a type may recursively
* require an adapter for the same type! Without intervention, the recursive
* lookup would stack overflow. We cheat by returning a proxy type adapter.
* The proxy is wired up once the initial adapter has been created.
*/
private final ThreadLocal<Map<TypeToken<?>, FutureTypeAdapter<?>>> calls
= new ThreadLocal<Map<TypeToken<?>, FutureTypeAdapter<?>>>() {
@Override protected Map<TypeToken<?>, FutureTypeAdapter<?>> initialValue() {
return new HashMap<TypeToken<?>, FutureTypeAdapter<?>>();
}
};
private final List<TypeAdapter.Factory> factories;
private MiniGson(Builder builder) {
ConstructorConstructor constructorConstructor = new ConstructorConstructor();
List<TypeAdapter.Factory> factories = new ArrayList<TypeAdapter.Factory>();
if (builder.addDefaultFactories) {
factories.add(TypeAdapters.BOOLEAN_FACTORY);
factories.add(TypeAdapters.INTEGER_FACTORY);
factories.add(TypeAdapters.DOUBLE_FACTORY);
factories.add(TypeAdapters.FLOAT_FACTORY);
factories.add(TypeAdapters.LONG_FACTORY);
factories.add(TypeAdapters.STRING_FACTORY);
}
factories.addAll(builder.factories);
if (builder.addDefaultFactories) {
factories.add(new CollectionTypeAdapterFactory(constructorConstructor));
factories.add(new StringToValueMapTypeAdapterFactory(constructorConstructor));
factories.add(ArrayTypeAdapter.FACTORY);
factories.add(ObjectTypeAdapter.FACTORY);
factories.add(new ReflectiveTypeAdapterFactory(constructorConstructor));
}
this.factories = Collections.unmodifiableList(factories);
}
/**
* Returns the type adapter for {@code} type.
*
* @throws IllegalArgumentException if this GSON cannot serialize and
* deserialize {@code type}.
*/
public <T> TypeAdapter<T> getAdapter(TypeToken<T> type) {
// TODO: create a cache!
Map<TypeToken<?>, FutureTypeAdapter<?>> threadCalls = calls.get();
@SuppressWarnings("unchecked") // the key and value type parameters always agree
FutureTypeAdapter<T> ongoingCall = (FutureTypeAdapter<T>) threadCalls.get(type);
if (ongoingCall != null) {
return ongoingCall;
}
FutureTypeAdapter<T> call = new FutureTypeAdapter<T>();
threadCalls.put(type, call);
try {
for (TypeAdapter.Factory factory : factories) {
TypeAdapter<T> candidate = factory.create(this, type);
if (candidate != null) {
call.setDelegate(candidate);
return candidate;
}
}
throw new IllegalArgumentException("This MiniGSON cannot handle " + type);
} finally {
threadCalls.remove(type);
}
}
/**
* Returns a type adapter for {@code} type that isn't {@code skipPast}. This
* can be used for type adapters to compose other, simpler type adapters.
*
* @throws IllegalArgumentException if this GSON cannot serialize and
* deserialize {@code type}.
*/
public <T> TypeAdapter<T> getNextAdapter(TypeAdapter.Factory skipPast, TypeToken<T> type) {
boolean skipPastFound = false;
for (TypeAdapter.Factory factory : factories) {
if (!skipPastFound) {
if (factory == skipPast) {
skipPastFound = true;
}
continue;
}
TypeAdapter<T> candidate = factory.create(this, type);
if (candidate != null) {
return candidate;
}
}
throw new IllegalArgumentException("This MiniGSON cannot serialize " + type);
}
static class FutureTypeAdapter<T> extends TypeAdapter<T> {
private TypeAdapter<T> delegate;
public void setDelegate(TypeAdapter<T> typeAdapter) {
if (delegate != null) {
throw new AssertionError();
}
delegate = typeAdapter;
}
@Override public T read(JsonReader reader) throws IOException {
if (delegate == null) {
throw new IllegalStateException();
}
return delegate.read(reader);
}
@Override public void write(JsonWriter writer, T value) throws IOException {
if (delegate == null) {
throw new IllegalStateException();
}
delegate.write(writer, value);
}
}
/**
* Returns the type adapter for {@code} type.
*
* @throws IllegalArgumentException if this GSON cannot serialize and
* deserialize {@code type}.
*/
public <T> TypeAdapter<T> getAdapter(Class<T> type) {
return getAdapter(TypeToken.get(type));
}
/**
* Returns the type adapters of this context in order of precedence.
*/
public List<TypeAdapter.Factory> getFactories() {
return factories;
}
public static final class Builder {
private final List<TypeAdapter.Factory> factories = new ArrayList<TypeAdapter.Factory>();
boolean addDefaultFactories = true;
public Builder factory(TypeAdapter.Factory factory) {
factories.add(factory);
return this;
}
public Builder withoutDefaultFactories() {
this.addDefaultFactories = false;
return this;
}
public <T> Builder typeAdapter(final Class<T> type, final TypeAdapter<T> typeAdapter) {
factories.add(TypeAdapters.newFactory(type, typeAdapter));
return this;
}
public <T> Builder typeAdapter(TypeToken<T> type, TypeAdapter<T> typeAdapter) {
factories.add(TypeAdapters.newFactory(type, typeAdapter));
return this;
}
public <T> Builder typeHierarchyAdapter(Class<T> type, TypeAdapter<T> typeAdapter) {
factories.add(TypeAdapters.newTypeHierarchyFactory(type, typeAdapter));
return this;
}
public MiniGson build() {
return new MiniGson(this);
}
}
}

View File

@ -0,0 +1,105 @@
/*
* Copyright (C) 2011 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.massivecraft.core.lib.gson2.internal.bind;
import com.massivecraft.core.lib.gson2.reflect.TypeToken;
import com.massivecraft.core.lib.gson2.stream.JsonReader;
import com.massivecraft.core.lib.gson2.stream.JsonToken;
import com.massivecraft.core.lib.gson2.stream.JsonWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
/**
* Adapts types whose static type is only 'Object'. Uses getClass() on
* serialization and a primitive/Map/List on deserialization.
*/
public final class ObjectTypeAdapter extends TypeAdapter<Object> {
public static final Factory FACTORY = new Factory() {
@SuppressWarnings("unchecked")
public <T> TypeAdapter<T> create(MiniGson context, TypeToken<T> type) {
if (type.getRawType() == Object.class) {
return (TypeAdapter<T>) new ObjectTypeAdapter(context);
}
return null;
}
};
private final MiniGson miniGson;
private ObjectTypeAdapter(MiniGson miniGson) {
this.miniGson = miniGson;
}
@Override public Object read(JsonReader reader) throws IOException {
JsonToken token = reader.peek();
switch (token) {
case BEGIN_ARRAY:
List<Object> list = new ArrayList<Object>();
reader.beginArray();
while (reader.hasNext()) {
list.add(read(reader));
}
reader.endArray();
return list;
case BEGIN_OBJECT:
Map<String, Object> map = new LinkedHashMap<String, Object>();
reader.beginObject();
while (reader.hasNext()) {
map.put(reader.nextName(), read(reader));
}
reader.endObject();
return map;
case STRING:
return reader.nextString();
case NUMBER:
return reader.nextDouble();
case BOOLEAN:
return reader.nextBoolean();
case NULL:
reader.nextNull();
return null;
}
throw new IllegalStateException();
}
@SuppressWarnings("unchecked")
@Override public void write(JsonWriter writer, Object value) throws IOException {
if (value == null) {
writer.nullValue(); // TODO: better policy here?
return;
}
TypeAdapter<Object> typeAdapter = (TypeAdapter<Object>) miniGson.getAdapter(value.getClass());
if (typeAdapter instanceof ObjectTypeAdapter) {
writer.beginObject();
writer.endObject();
return;
}
typeAdapter.write(writer, value);
}
}

View File

@ -0,0 +1,32 @@
/*
* Copyright (C) 2011 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.massivecraft.core.lib.gson2.internal.bind;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
final class Reflection {
/**
* Finds a compatible runtime type if it is more specific
*/
public static Type getRuntimeTypeIfMoreSpecific(Type type, Object value) {
if (value != null
&& (type == Object.class || type instanceof TypeVariable<?> || type instanceof Class<?>)) {
type = value.getClass();
}
return type;
}
}

View File

@ -0,0 +1,204 @@
/*
* Copyright (C) 2011 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.massivecraft.core.lib.gson2.internal.bind;
import com.massivecraft.core.lib.gson2.JsonSyntaxException;
import com.massivecraft.core.lib.gson2.internal.$Gson$Types;
import com.massivecraft.core.lib.gson2.internal.ConstructorConstructor;
import com.massivecraft.core.lib.gson2.internal.ObjectConstructor;
import com.massivecraft.core.lib.gson2.internal.Primitives;
import com.massivecraft.core.lib.gson2.reflect.TypeToken;
import com.massivecraft.core.lib.gson2.stream.JsonReader;
import com.massivecraft.core.lib.gson2.stream.JsonToken;
import com.massivecraft.core.lib.gson2.stream.JsonWriter;
import java.io.IOException;
import java.lang.reflect.AccessibleObject;
import java.lang.reflect.Field;
import java.lang.reflect.Type;
import java.util.LinkedHashMap;
import java.util.Map;
/**
* Type adapter that reflects over the fields and methods of a class.
*/
public class ReflectiveTypeAdapterFactory implements TypeAdapter.Factory {
private final ConstructorConstructor constructorConstructor;
public ReflectiveTypeAdapterFactory(ConstructorConstructor constructorConstructor) {
this.constructorConstructor = constructorConstructor;
}
protected boolean serializeField(Class<?> declaringClazz, Field f, Type declaredType) {
return !f.isSynthetic();
}
protected boolean deserializeField(Class<?> declaringClazz, Field f, Type declaredType) {
return !f.isSynthetic();
}
protected String getFieldName(Class<?> declaringClazz, Field f, Type declaredType) {
return f.getName();
}
public <T> TypeAdapter<T> create(MiniGson context, final TypeToken<T> type) {
Class<? super T> raw = type.getRawType();
if (!Object.class.isAssignableFrom(raw)) {
return null; // it's a primitive!
}
ObjectConstructor<T> constructor = constructorConstructor.getConstructor(type);
return new Adapter<T>(constructor, getBoundFields(context, type, raw));
}
private ReflectiveTypeAdapterFactory.BoundField createBoundField(
final MiniGson context, final Field field, final String name,
final TypeToken<?> fieldType, boolean serialize, boolean deserialize) {
final boolean isPrimitive = Primitives.isPrimitive(fieldType.getRawType());
// special casing primitives here saves ~5% on Android...
return new ReflectiveTypeAdapterFactory.BoundField(name, serialize, deserialize) {
final TypeAdapter<?> typeAdapter = context.getAdapter(fieldType);
@SuppressWarnings({"unchecked", "rawtypes"}) // the type adapter and field type always agree
@Override void write(JsonWriter writer, Object value)
throws IOException, IllegalAccessException {
Object fieldValue = field.get(value);
TypeAdapter t =
new TypeAdapterRuntimeTypeWrapper(context, this.typeAdapter, fieldType.getType());
t.write(writer, fieldValue);
}
@Override void read(JsonReader reader, Object value)
throws IOException, IllegalAccessException {
Object fieldValue = typeAdapter.read(reader);
if (fieldValue != null || !isPrimitive) {
field.set(value, fieldValue);
}
}
};
}
private Map<String, BoundField> getBoundFields(
MiniGson context, TypeToken<?> type, Class<?> raw) {
Map<String, BoundField> result = new LinkedHashMap<String, BoundField>();
if (raw.isInterface()) {
return result;
}
Type declaredType = type.getType();
while (raw != Object.class) {
Field[] fields = raw.getDeclaredFields();
AccessibleObject.setAccessible(fields, true);
for (Field field : fields) {
boolean serialize = serializeField(raw, field, declaredType);
boolean deserialize = deserializeField(raw, field, declaredType);
if (!serialize && !deserialize) {
continue;
}
Type fieldType = $Gson$Types.resolve(type.getType(), raw, field.getGenericType());
BoundField boundField = createBoundField(context, field, getFieldName(raw, field, declaredType),
TypeToken.get(fieldType), serialize, deserialize);
BoundField previous = result.put(boundField.name, boundField);
if (previous != null) {
throw new IllegalArgumentException(declaredType
+ " declares multiple JSON fields named " + previous.name);
}
}
type = TypeToken.get($Gson$Types.resolve(type.getType(), raw, raw.getGenericSuperclass()));
raw = type.getRawType();
}
return result;
}
static abstract class BoundField {
final String name;
final boolean serialized;
final boolean deserialized;
protected BoundField(String name, boolean serialized, boolean deserialized) {
this.name = name;
this.serialized = serialized;
this.deserialized = deserialized;
}
abstract void write(JsonWriter writer, Object value) throws IOException, IllegalAccessException;
abstract void read(JsonReader reader, Object value) throws IOException, IllegalAccessException;
}
public final class Adapter<T> extends TypeAdapter<T> {
private final ObjectConstructor<T> constructor;
private final Map<String, BoundField> boundFields;
private Adapter(ObjectConstructor<T> constructor, Map<String, BoundField> boundFields) {
this.constructor = constructor;
this.boundFields = boundFields;
}
@Override
public T read(JsonReader reader) throws IOException {
if (reader.peek() == JsonToken.NULL) {
reader.nextNull();
return null;
}
T instance = constructor.construct();
// TODO: null out the other fields?
try {
reader.beginObject();
while (reader.hasNext()) {
String name = reader.nextName();
BoundField field = boundFields.get(name);
if (field == null || !field.deserialized) {
// TODO: define a better policy
reader.skipValue();
} else {
field.read(reader, instance);
}
}
} catch (IllegalStateException e) {
throw new JsonSyntaxException(e);
} catch (IllegalAccessException e) {
throw new AssertionError(e);
}
reader.endObject();
return instance;
}
@Override
public void write(JsonWriter writer, T value) throws IOException {
if (value == null) {
writer.nullValue(); // TODO: better policy here?
return;
}
writer.beginObject();
try {
for (BoundField boundField : boundFields.values()) {
if (boundField.serialized) {
writer.name(boundField.name);
boundField.write(writer, value);
}
}
} catch (IllegalAccessException e) {
throw new AssertionError();
}
writer.endObject();
}
}
}

View File

@ -0,0 +1,65 @@
/*
* Copyright (C) 2011 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.massivecraft.core.lib.gson2.internal.bind;
import com.massivecraft.core.lib.gson2.JsonSyntaxException;
import com.massivecraft.core.lib.gson2.reflect.TypeToken;
import com.massivecraft.core.lib.gson2.stream.JsonReader;
import com.massivecraft.core.lib.gson2.stream.JsonToken;
import com.massivecraft.core.lib.gson2.stream.JsonWriter;
import java.io.IOException;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
/**
* Adapter for java.sql.Date. Although this class appears stateless, it is not.
* DateFormat captures its time zone and locale when it is created, which gives
* this class state. DateFormat isn't thread safe either, so this class has
* to synchronize its read and write methods.
*/
public final class SqlDateTypeAdapter extends TypeAdapter<java.sql.Date> {
public static final Factory FACTORY = new Factory() {
@SuppressWarnings("unchecked") // we use a runtime check to make sure the 'T's equal
public <T> TypeAdapter<T> create(MiniGson context, TypeToken<T> typeToken) {
return typeToken.getRawType() == java.sql.Date.class
? (TypeAdapter<T>) new SqlDateTypeAdapter() : null;
}
};
private final DateFormat format = new SimpleDateFormat("MMM d, yyyy");
@Override
public synchronized java.sql.Date read(JsonReader reader) throws IOException {
if (reader.peek() == JsonToken.NULL) {
reader.nextNull();
return null;
}
try {
final long utilDate = format.parse(reader.nextString()).getTime();
return new java.sql.Date(utilDate);
} catch (ParseException e) {
throw new JsonSyntaxException(e);
}
}
@Override
public synchronized void write(JsonWriter writer, java.sql.Date value) throws IOException {
writer.value(value == null ? null : format.format(value));
}
}

View File

@ -0,0 +1,108 @@
/*
* Copyright (C) 2011 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.massivecraft.core.lib.gson2.internal.bind;
import com.massivecraft.core.lib.gson2.internal.$Gson$Types;
import com.massivecraft.core.lib.gson2.internal.ConstructorConstructor;
import com.massivecraft.core.lib.gson2.internal.ObjectConstructor;
import com.massivecraft.core.lib.gson2.reflect.TypeToken;
import com.massivecraft.core.lib.gson2.stream.JsonReader;
import com.massivecraft.core.lib.gson2.stream.JsonToken;
import com.massivecraft.core.lib.gson2.stream.JsonWriter;
import java.io.IOException;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.Map;
/**
* Adapt a map whose keys are strings.
*/
public final class StringToValueMapTypeAdapterFactory implements TypeAdapter.Factory {
private final ConstructorConstructor constructorConstructor;
public StringToValueMapTypeAdapterFactory(ConstructorConstructor constructorConstructor) {
this.constructorConstructor = constructorConstructor;
}
public <T> TypeAdapter<T> create(MiniGson context, TypeToken<T> typeToken) {
Type type = typeToken.getType();
if (!(type instanceof ParameterizedType)) {
return null;
}
Class<? super T> rawType = typeToken.getRawType();
if (!Map.class.isAssignableFrom(rawType)) {
return null;
}
Type[] keyAndValueTypes = $Gson$Types.getMapKeyAndValueTypes(type, rawType);
if (keyAndValueTypes[0] != String.class) {
return null;
}
TypeAdapter<?> valueAdapter = context.getAdapter(TypeToken.get(keyAndValueTypes[1]));
ObjectConstructor<?> constructor = constructorConstructor.getConstructor(typeToken);
@SuppressWarnings({"unchecked", "rawtypes"})
// we don't define a type parameter for the key or value types
TypeAdapter<T> result = new Adapter(valueAdapter, constructor);
return result;
}
private final class Adapter<V> extends TypeAdapter<Map<String, V>> {
private final TypeAdapter<V> valueTypeAdapter;
private final ObjectConstructor<? extends Map<String, V>> constructor;
public Adapter(TypeAdapter<V> valueTypeAdapter,
ObjectConstructor<? extends Map<String, V>> constructor) {
this.valueTypeAdapter = valueTypeAdapter;
this.constructor = constructor;
}
public Map<String, V> read(JsonReader reader) throws IOException {
if (reader.peek() == JsonToken.NULL) {
reader.nextNull();
return null;
}
Map<String, V> map = constructor.construct();
reader.beginObject();
while (reader.hasNext()) {
String key = reader.nextName();
V value = valueTypeAdapter.read(reader);
map.put(key, value);
}
reader.endObject();
return map;
}
public void write(JsonWriter writer, Map<String, V> map) throws IOException {
if (map == null) {
writer.nullValue(); // TODO: better policy here?
return;
}
writer.beginObject();
for (Map.Entry<String, V> entry : map.entrySet()) {
writer.name(entry.getKey());
valueTypeAdapter.write(writer, entry.getValue());
}
writer.endObject();
}
}
}

View File

@ -0,0 +1,64 @@
/*
* Copyright (C) 2011 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.massivecraft.core.lib.gson2.internal.bind;
import com.massivecraft.core.lib.gson2.JsonSyntaxException;
import com.massivecraft.core.lib.gson2.reflect.TypeToken;
import com.massivecraft.core.lib.gson2.stream.JsonReader;
import com.massivecraft.core.lib.gson2.stream.JsonToken;
import com.massivecraft.core.lib.gson2.stream.JsonWriter;
import java.io.IOException;
import java.sql.Time;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
/**
* Adapter for Time. Although this class appears stateless, it is not.
* DateFormat captures its time zone and locale when it is created, which gives
* this class state. DateFormat isn't thread safe either, so this class has
* to synchronize its read and write methods.
*/
public final class TimeTypeAdapter extends TypeAdapter<Time> {
public static final Factory FACTORY = new Factory() {
@SuppressWarnings("unchecked") // we use a runtime check to make sure the 'T's equal
public <T> TypeAdapter<T> create(MiniGson context, TypeToken<T> typeToken) {
return typeToken.getRawType() == Time.class ? (TypeAdapter<T>) new TimeTypeAdapter() : null;
}
};
private final DateFormat format = new SimpleDateFormat("hh:mm:ss a");
@Override public synchronized Time read(JsonReader reader) throws IOException {
if (reader.peek() == JsonToken.NULL) {
reader.nextNull();
return null;
}
try {
Date date = format.parse(reader.nextString());
return new Time(date.getTime());
} catch (ParseException e) {
throw new JsonSyntaxException(e);
}
}
@Override public synchronized void write(JsonWriter writer, Time value) throws IOException {
writer.value(value == null ? null : format.format(value));
}
}

View File

@ -0,0 +1,80 @@
/*
* Copyright (C) 2011 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.massivecraft.core.lib.gson2.internal.bind;
import com.massivecraft.core.lib.gson2.JsonElement;
import com.massivecraft.core.lib.gson2.JsonIOException;
import com.massivecraft.core.lib.gson2.reflect.TypeToken;
import com.massivecraft.core.lib.gson2.stream.JsonReader;
import com.massivecraft.core.lib.gson2.stream.JsonWriter;
import java.io.IOException;
import java.io.Reader;
import java.io.StringReader;
import java.io.StringWriter;
import java.io.Writer;
public abstract class TypeAdapter<T> {
public abstract T read(JsonReader reader) throws IOException;
public abstract void write(JsonWriter writer, T value) throws IOException;
public final String toJson(T value) throws IOException {
StringWriter stringWriter = new StringWriter();
write(stringWriter, value);
return stringWriter.toString();
}
public final void write(Writer out, T value) throws IOException {
JsonWriter writer = new JsonWriter(out);
write(writer, value);
}
public final T fromJson(String json) throws IOException {
return read(new StringReader(json));
}
public final T read(Reader in) throws IOException {
JsonReader reader = new JsonReader(in);
reader.setLenient(true);
return read(reader);
}
public JsonElement toJsonElement(T src) {
try {
JsonElementWriter jsonWriter = new JsonElementWriter();
jsonWriter.setLenient(true);
write(jsonWriter, src);
return jsonWriter.get();
} catch (IOException e) {
throw new JsonIOException(e);
}
}
public T fromJsonElement(JsonElement json) {
try {
JsonReader jsonReader = new JsonElementReader(json);
jsonReader.setLenient(true);
return read(jsonReader);
} catch (IOException e) {
throw new JsonIOException(e);
}
}
public interface Factory {
<T> TypeAdapter<T> create(MiniGson context, TypeToken<T> type);
}
}

View File

@ -0,0 +1,69 @@
/*
* Copyright (C) 2011 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.massivecraft.core.lib.gson2.internal.bind;
import com.massivecraft.core.lib.gson2.reflect.TypeToken;
import com.massivecraft.core.lib.gson2.stream.JsonReader;
import com.massivecraft.core.lib.gson2.stream.JsonWriter;
import java.io.IOException;
import java.lang.reflect.Type;
final class TypeAdapterRuntimeTypeWrapper<T> extends TypeAdapter<T> {
private final MiniGson context;
private final TypeAdapter<T> delegate;
private final Type type;
TypeAdapterRuntimeTypeWrapper(MiniGson context, TypeAdapter<T> delegate, Type type) {
this.context = context;
this.delegate = delegate;
this.type = type;
}
@Override
public T read(JsonReader reader) throws IOException {
return delegate.read(reader);
}
@SuppressWarnings({"rawtypes", "unchecked"})
@Override
public void write(JsonWriter writer, T value) throws IOException {
// Order of preference for choosing type adapters
// First preference: a type adapter registered for the runtime type
// Second preference: a type adapter registered for the declared type
// Third preference: reflective type adapter for the runtime type (if it is a sub class of the declared type)
// Fourth preference: reflective type adapter for the declared type
TypeAdapter chosen = delegate;
Type runtimeType = Reflection.getRuntimeTypeIfMoreSpecific(type, value);
if (runtimeType != type) {
TypeAdapter runtimeTypeAdapter = context.getAdapter(TypeToken.get(runtimeType));
if (!(runtimeTypeAdapter instanceof ReflectiveTypeAdapterFactory.Adapter)) {
// The user registered a type adapter for the runtime type, so we will use that
chosen = runtimeTypeAdapter;
} else if (!(delegate instanceof ReflectiveTypeAdapterFactory.Adapter)) {
// The user registered a type adapter for Base class, so we prefer it over the
// reflective type adapter for the runtime type
chosen = delegate;
} else {
// Use the type adapter for runtime type
chosen = runtimeTypeAdapter;
}
}
chosen.write(writer, value);
}
}

View File

@ -0,0 +1,752 @@
/*
* Copyright (C) 2011 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.massivecraft.core.lib.gson2.internal.bind;
import com.massivecraft.core.lib.gson2.JsonArray;
import com.massivecraft.core.lib.gson2.JsonElement;
import com.massivecraft.core.lib.gson2.JsonIOException;
import com.massivecraft.core.lib.gson2.JsonNull;
import com.massivecraft.core.lib.gson2.JsonObject;
import com.massivecraft.core.lib.gson2.JsonPrimitive;
import com.massivecraft.core.lib.gson2.JsonSyntaxException;
import com.massivecraft.core.lib.gson2.internal.LazilyParsedNumber;
import com.massivecraft.core.lib.gson2.reflect.TypeToken;
import com.massivecraft.core.lib.gson2.stream.JsonReader;
import com.massivecraft.core.lib.gson2.stream.JsonToken;
import com.massivecraft.core.lib.gson2.stream.JsonWriter;
import java.io.IOException;
import java.net.InetAddress;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.sql.Timestamp;
import java.util.BitSet;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.Locale;
import java.util.Map;
import java.util.StringTokenizer;
import java.util.UUID;
/**
* Type adapters for basic types.
*/
public final class TypeAdapters {
private TypeAdapters() {}
public static final TypeAdapter<BitSet> BIT_SET = new TypeAdapter<BitSet>() {
public BitSet read(JsonReader reader) throws IOException {
if (reader.peek() == JsonToken.NULL) {
reader.nextNull();
return null;
}
BitSet bitset = new BitSet();
reader.beginArray();
int i = 0;
JsonToken tokenType = reader.peek();
while (tokenType != JsonToken.END_ARRAY) {
boolean set;
switch (tokenType) {
case NUMBER:
set = reader.nextInt() != 0;
break;
case BOOLEAN:
set = reader.nextBoolean();
break;
case STRING:
String stringValue = reader.nextString();
try {
set = Integer.parseInt(stringValue) != 0;
} catch (NumberFormatException e) {
throw new JsonSyntaxException(
"Error: Expecting: bitset number value (1, 0), Found: " + stringValue);
}
break;
default:
throw new JsonSyntaxException("Invalid bitset value type: " + tokenType);
}
if (set) {
bitset.set(i);
}
++i;
tokenType = reader.peek();
}
reader.endArray();
return bitset;
}
public void write(JsonWriter writer, BitSet src) throws IOException {
if (src == null) {
writer.nullValue();
return;
}
writer.beginArray();
for (int i = 0; i < src.length(); i++) {
int value = (src.get(i)) ? 1 : 0;
writer.value(value);
}
writer.endArray();
}
};
public static final TypeAdapter.Factory BIT_SET_FACTORY = newFactory(BitSet.class, BIT_SET);
public static final TypeAdapter<Boolean> BOOLEAN = new TypeAdapter<Boolean>() {
@Override
public Boolean read(JsonReader reader) throws IOException {
if (reader.peek() == JsonToken.NULL) {
reader.nextNull();
return null;
} else if (reader.peek() == JsonToken.STRING) {
// support strings for compatibility with GSON 1.7
return Boolean.parseBoolean(reader.nextString());
}
return reader.nextBoolean();
}
@Override
public void write(JsonWriter writer, Boolean value) throws IOException {
if (value == null) {
writer.nullValue();
return;
}
writer.value(value);
}
};
/**
* Writes a boolean as a string. Useful for map keys, where booleans aren't
* otherwise permitted.
*/
public static final TypeAdapter<Boolean> BOOLEAN_AS_STRING = new TypeAdapter<Boolean>() {
@Override public Boolean read(JsonReader reader) throws IOException {
if (reader.peek() == JsonToken.NULL) {
reader.nextNull();
return null;
}
return Boolean.valueOf(reader.nextString());
}
@Override public void write(JsonWriter writer, Boolean value) throws IOException {
writer.value(value == null ? "null" : value.toString());
}
};
public static final TypeAdapter.Factory BOOLEAN_FACTORY
= newFactory(boolean.class, Boolean.class, BOOLEAN);
public static final TypeAdapter<Number> BYTE = new TypeAdapter<Number>() {
@Override
public Number read(JsonReader reader) throws IOException {
if (reader.peek() == JsonToken.NULL) {
reader.nextNull();
return null;
}
try {
int intValue = reader.nextInt();
return (byte) intValue;
} catch (NumberFormatException e) {
throw new JsonSyntaxException(e);
}
}
@Override
public void write(JsonWriter writer, Number value) throws IOException {
writer.value(value);
}
};
public static final TypeAdapter.Factory BYTE_FACTORY
= newFactory(byte.class, Byte.class, BYTE);
public static final TypeAdapter<Number> SHORT = new TypeAdapter<Number>() {
@Override
public Number read(JsonReader reader) throws IOException {
if (reader.peek() == JsonToken.NULL) {
reader.nextNull();
return null;
}
try {
return (short) reader.nextInt();
} catch (NumberFormatException e) {
throw new JsonSyntaxException(e);
}
}
@Override
public void write(JsonWriter writer, Number value) throws IOException {
writer.value(value);
}
};
public static final TypeAdapter.Factory SHORT_FACTORY
= newFactory(short.class, Short.class, SHORT);
public static final TypeAdapter<Number> INTEGER = new TypeAdapter<Number>() {
@Override
public Number read(JsonReader reader) throws IOException {
if (reader.peek() == JsonToken.NULL) {
reader.nextNull();
return null;
}
try {
return reader.nextInt();
} catch (NumberFormatException e) {
throw new JsonSyntaxException(e);
}
}
@Override
public void write(JsonWriter writer, Number value) throws IOException {
writer.value(value);
}
};
public static final TypeAdapter.Factory INTEGER_FACTORY
= newFactory(int.class, Integer.class, INTEGER);
public static final TypeAdapter<Number> LONG = new TypeAdapter<Number>() {
@Override
public Number read(JsonReader reader) throws IOException {
if (reader.peek() == JsonToken.NULL) {
reader.nextNull();
return null;
}
try {
return reader.nextLong();
} catch (NumberFormatException e) {
throw new JsonSyntaxException(e);
}
}
@Override
public void write(JsonWriter writer, Number value) throws IOException {
writer.value(value);
}
};
public static final TypeAdapter.Factory LONG_FACTORY
= newFactory(long.class, Long.class, LONG);
public static final TypeAdapter<Number> FLOAT = new TypeAdapter<Number>() {
@Override
public Number read(JsonReader reader) throws IOException {
if (reader.peek() == JsonToken.NULL) {
reader.nextNull();
return null;
}
return (float) reader.nextDouble();
}
@Override
public void write(JsonWriter writer, Number value) throws IOException {
writer.value(value);
}
};
public static final TypeAdapter.Factory FLOAT_FACTORY
= newFactory(float.class, Float.class, FLOAT);
public static final TypeAdapter<Number> DOUBLE = new TypeAdapter<Number>() {
@Override
public Number read(JsonReader reader) throws IOException {
if (reader.peek() == JsonToken.NULL) {
reader.nextNull();
return null;
}
return reader.nextDouble();
}
@Override
public void write(JsonWriter writer, Number value) throws IOException {
writer.value(value);
}
};
public static final TypeAdapter.Factory DOUBLE_FACTORY
= newFactory(double.class, Double.class, DOUBLE);
public static final TypeAdapter<Number> NUMBER = new TypeAdapter<Number>() {
@Override
public Number read(JsonReader reader) throws IOException {
JsonToken jsonToken = reader.peek();
switch (jsonToken) {
case NULL:
reader.nextNull();
return null;
case NUMBER:
return new LazilyParsedNumber(reader.nextString());
default:
throw new JsonSyntaxException("Expecting number, got: " + jsonToken);
}
}
@Override
public void write(JsonWriter writer, Number value) throws IOException {
writer.value(value);
}
};
public static final TypeAdapter.Factory NUMBER_FACTORY = newFactory(Number.class, NUMBER);
public static final TypeAdapter<Character> CHARACTER = new TypeAdapter<Character>() {
@Override
public Character read(JsonReader reader) throws IOException {
if (reader.peek() == JsonToken.NULL) {
reader.nextNull();
return null;
}
return reader.nextString().charAt(0);
}
@Override
public void write(JsonWriter writer, Character value) throws IOException {
writer.value(value == null ? null : String.valueOf(value));
}
};
public static final TypeAdapter.Factory CHARACTER_FACTORY
= newFactory(char.class, Character.class, CHARACTER);
public static final TypeAdapter<String> STRING = new TypeAdapter<String>() {
@Override
public String read(JsonReader reader) throws IOException {
JsonToken peek = reader.peek();
if (peek == JsonToken.NULL) {
reader.nextNull();
return null;
}
/* coerce booleans to strings for backwards compatibility */
if (peek == JsonToken.BOOLEAN) {
return Boolean.toString(reader.nextBoolean());
}
return reader.nextString();
}
@Override
public void write(JsonWriter writer, String value) throws IOException {
writer.value(value);
}
};
public static final TypeAdapter.Factory STRING_FACTORY = newFactory(String.class, STRING);
public static final TypeAdapter<StringBuilder> STRING_BUILDER = new TypeAdapter<StringBuilder>() {
@Override
public StringBuilder read(JsonReader reader) throws IOException {
if (reader.peek() == JsonToken.NULL) {
reader.nextNull();
return null;
}
return new StringBuilder(reader.nextString());
}
@Override
public void write(JsonWriter writer, StringBuilder value) throws IOException {
writer.value(value == null ? null : value.toString());
}
};
public static final TypeAdapter.Factory STRING_BUILDER_FACTORY =
newFactory(StringBuilder.class, STRING_BUILDER);
public static final TypeAdapter<StringBuffer> STRING_BUFFER = new TypeAdapter<StringBuffer>() {
@Override
public StringBuffer read(JsonReader reader) throws IOException {
if (reader.peek() == JsonToken.NULL) {
reader.nextNull();
return null;
}
return new StringBuffer(reader.nextString());
}
@Override
public void write(JsonWriter writer, StringBuffer value) throws IOException {
writer.value(value == null ? null : value.toString());
}
};
public static final TypeAdapter.Factory STRING_BUFFER_FACTORY =
newFactory(StringBuffer.class, STRING_BUFFER);
public static final TypeAdapter<URL> URL = new TypeAdapter<URL>() {
@Override
public URL read(JsonReader reader) throws IOException {
if (reader.peek() == JsonToken.NULL) {
reader.nextNull();
return null;
}
String nextString = reader.nextString();
return "null".equals(nextString) ? null : new URL(nextString);
}
@Override
public void write(JsonWriter writer, URL value) throws IOException {
writer.value(value == null ? null : value.toExternalForm());
}
};
public static final TypeAdapter.Factory URL_FACTORY = newFactory(URL.class, URL);
public static final TypeAdapter<URI> URI = new TypeAdapter<URI>() {
@Override
public URI read(JsonReader reader) throws IOException {
if (reader.peek() == JsonToken.NULL) {
reader.nextNull();
return null;
}
try {
String nextString = reader.nextString();
return "null".equals(nextString) ? null : new URI(nextString);
} catch (URISyntaxException e) {
throw new JsonIOException(e);
}
}
@Override
public void write(JsonWriter writer, URI value) throws IOException {
writer.value(value == null ? null : value.toASCIIString());
}
};
public static final TypeAdapter.Factory URI_FACTORY = newFactory(URI.class, URI);
public static final TypeAdapter<InetAddress> INET_ADDRESS = new TypeAdapter<InetAddress>() {
@Override
public InetAddress read(JsonReader reader) throws IOException {
if (reader.peek() == JsonToken.NULL) {
reader.nextNull();
return null;
}
return InetAddress.getByName(reader.nextString());
}
@Override
public void write(JsonWriter writer, InetAddress value) throws IOException {
writer.value(value == null ? null : value.getHostAddress());
}
};
public static final TypeAdapter.Factory INET_ADDRESS_FACTORY =
newTypeHierarchyFactory(InetAddress.class, INET_ADDRESS);
public static final TypeAdapter<UUID> UUID = new TypeAdapter<UUID>() {
@Override
public UUID read(JsonReader reader) throws IOException {
if (reader.peek() == JsonToken.NULL) {
reader.nextNull();
return null;
}
return java.util.UUID.fromString(reader.nextString());
}
@Override
public void write(JsonWriter writer, UUID value) throws IOException {
writer.value(value == null ? null : value.toString());
}
};
public static final TypeAdapter.Factory UUID_FACTORY = newFactory(UUID.class, UUID);
public static final TypeAdapter.Factory TIMESTAMP_FACTORY = new TypeAdapter.Factory() {
@SuppressWarnings("unchecked") // we use a runtime check to make sure the 'T's equal
public <T> TypeAdapter<T> create(MiniGson context, TypeToken<T> typeToken) {
if (typeToken.getRawType() != Timestamp.class) {
return null;
}
final TypeAdapter<Date> dateTypeAdapter = context.getAdapter(Date.class);
return (TypeAdapter<T>) new TypeAdapter<Timestamp>() {
@Override public Timestamp read(JsonReader reader) throws IOException {
Date date = dateTypeAdapter.read(reader);
return date != null ? new Timestamp(date.getTime()) : null;
}
@Override public void write(JsonWriter writer, Timestamp value) throws IOException {
dateTypeAdapter.write(writer, value);
}
};
}
};
public static final TypeAdapter<Calendar> CALENDAR = new TypeAdapter<Calendar>() {
private static final String YEAR = "year";
private static final String MONTH = "month";
private static final String DAY_OF_MONTH = "dayOfMonth";
private static final String HOUR_OF_DAY = "hourOfDay";
private static final String MINUTE = "minute";
private static final String SECOND = "second";
@Override
public Calendar read(JsonReader reader) throws IOException {
if (reader.peek() == JsonToken.NULL) {
reader.nextNull();
return null;
}
reader.beginObject();
int year = 0;
int month = 0;
int dayOfMonth = 0;
int hourOfDay = 0;
int minute = 0;
int second = 0;
while (reader.peek() != JsonToken.END_OBJECT) {
String name = reader.nextName();
int value = reader.nextInt();
if (YEAR.equals(name)) {
year = value;
} else if (MONTH.equals(name)) {
month = value;
} else if (DAY_OF_MONTH.equals(name)) {
dayOfMonth = value;
} else if (HOUR_OF_DAY.equals(name)) {
hourOfDay = value;
} else if (MINUTE.equals(name)) {
minute = value;
} else if (SECOND.equals(name)) {
second = value;
}
}
reader.endObject();
return new GregorianCalendar(year, month, dayOfMonth, hourOfDay, minute, second);
}
@Override
public void write(JsonWriter writer, Calendar value) throws IOException {
if (value == null) {
writer.nullValue();
return;
}
writer.beginObject();
writer.name(YEAR);
writer.value(value.get(Calendar.YEAR));
writer.name(MONTH);
writer.value(value.get(Calendar.MONTH));
writer.name(DAY_OF_MONTH);
writer.value(value.get(Calendar.DAY_OF_MONTH));
writer.name(HOUR_OF_DAY);
writer.value(value.get(Calendar.HOUR_OF_DAY));
writer.name(MINUTE);
writer.value(value.get(Calendar.MINUTE));
writer.name(SECOND);
writer.value(value.get(Calendar.SECOND));
writer.endObject();
}
};
public static final TypeAdapter.Factory CALENDAR_FACTORY =
newFactoryForMultipleTypes(Calendar.class, GregorianCalendar.class, CALENDAR);
public static final TypeAdapter<Locale> LOCALE = new TypeAdapter<Locale>() {
@Override
public Locale read(JsonReader reader) throws IOException {
if (reader.peek() == JsonToken.NULL) {
reader.nextNull();
return null;
}
String locale = reader.nextString();
StringTokenizer tokenizer = new StringTokenizer(locale, "_");
String language = null;
String country = null;
String variant = null;
if (tokenizer.hasMoreElements()) {
language = tokenizer.nextToken();
}
if (tokenizer.hasMoreElements()) {
country = tokenizer.nextToken();
}
if (tokenizer.hasMoreElements()) {
variant = tokenizer.nextToken();
}
if (country == null && variant == null) {
return new Locale(language);
} else if (variant == null) {
return new Locale(language, country);
} else {
return new Locale(language, country, variant);
}
}
@Override
public void write(JsonWriter writer, Locale value) throws IOException {
writer.value(value == null ? null : value.toString());
}
};
public static final TypeAdapter.Factory LOCALE_FACTORY = newFactory(Locale.class, LOCALE);
public static final TypeAdapter<JsonElement> JSON_ELEMENT = new TypeAdapter<JsonElement>() {
@Override public JsonElement read(JsonReader reader) throws IOException {
switch (reader.peek()) {
case STRING:
return new JsonPrimitive(reader.nextString());
case NUMBER:
String number = reader.nextString();
return new JsonPrimitive(new LazilyParsedNumber(number));
case BOOLEAN:
return new JsonPrimitive(reader.nextBoolean());
case NULL:
reader.nextNull();
return JsonNull.INSTANCE;
case BEGIN_ARRAY:
JsonArray array = new JsonArray();
reader.beginArray();
while (reader.hasNext()) {
array.add(read(reader));
}
reader.endArray();
return array;
case BEGIN_OBJECT:
JsonObject object = new JsonObject();
reader.beginObject();
while (reader.hasNext()) {
object.add(reader.nextName(), read(reader));
}
reader.endObject();
return object;
case END_DOCUMENT:
case NAME:
case END_OBJECT:
case END_ARRAY:
default:
throw new IllegalArgumentException();
}
}
@Override public void write(JsonWriter writer, JsonElement value) throws IOException {
if (value == null || value.isJsonNull()) {
writer.nullValue();
} else if (value.isJsonPrimitive()) {
JsonPrimitive primitive = value.getAsJsonPrimitive();
if (primitive.isNumber()) {
writer.value(primitive.getAsNumber());
} else if (primitive.isBoolean()) {
writer.value(primitive.getAsBoolean());
} else {
writer.value(primitive.getAsString());
}
} else if (value.isJsonArray()) {
writer.beginArray();
for (JsonElement e : value.getAsJsonArray()) {
write(writer, e);
}
writer.endArray();
} else if (value.isJsonObject()) {
writer.beginObject();
for (Map.Entry<String, JsonElement> e : value.getAsJsonObject().entrySet()) {
writer.name(e.getKey());
write(writer, e.getValue());
}
writer.endObject();
} else {
throw new IllegalArgumentException("Couldn't write " + value.getClass());
}
}
};
public static final TypeAdapter.Factory JSON_ELEMENT_FACTORY
= newFactory(JsonElement.class, JSON_ELEMENT);
private static final class EnumTypeAdapter<T extends Enum<T>> extends TypeAdapter<T> {
private final Class<T> classOfT;
public EnumTypeAdapter(Class<T> classOfT) {
this.classOfT = classOfT;
}
public T read(JsonReader reader) throws IOException {
if (reader.peek() == JsonToken.NULL) {
reader.nextNull();
return null;
}
return (T) Enum.valueOf((Class<T>) classOfT, reader.nextString());
}
public void write(JsonWriter writer, T value) throws IOException {
writer.value(value == null ? null : value.name());
}
};
public static final TypeAdapter.Factory ENUM_FACTORY = newEnumTypeHierarchyFactory(Enum.class);
public static <TT> TypeAdapter.Factory newEnumTypeHierarchyFactory(final Class<TT> clazz) {
return new TypeAdapter.Factory() {
@SuppressWarnings({"rawtypes", "unchecked"})
public <T> TypeAdapter<T> create(MiniGson context, TypeToken<T> typeToken) {
Class<? super T> rawType = typeToken.getRawType();
return clazz.isAssignableFrom(rawType)
? (TypeAdapter<T>) new EnumTypeAdapter(rawType) : null;
}
};
}
public static <TT> TypeAdapter.Factory newFactory(
final TypeToken<TT> type, final TypeAdapter<TT> typeAdapter) {
return new TypeAdapter.Factory() {
@SuppressWarnings("unchecked") // we use a runtime check to make sure the 'T's equal
public <T> TypeAdapter<T> create(MiniGson context, TypeToken<T> typeToken) {
return typeToken.equals(type) ? (TypeAdapter<T>) typeAdapter : null;
}
};
}
public static <TT> TypeAdapter.Factory newFactory(
final Class<TT> type, final TypeAdapter<TT> typeAdapter) {
return new TypeAdapter.Factory() {
@SuppressWarnings("unchecked") // we use a runtime check to make sure the 'T's equal
public <T> TypeAdapter<T> create(MiniGson context, TypeToken<T> typeToken) {
return typeToken.getRawType() == type ? (TypeAdapter<T>) typeAdapter : null;
}
@Override public String toString() {
return "Factory[type=" + type.getName() + ",adapter=" + typeAdapter + "]";
}
};
}
public static <TT> TypeAdapter.Factory newFactory(
final Class<TT> unboxed, final Class<TT> boxed, final TypeAdapter<? super TT> typeAdapter) {
return new TypeAdapter.Factory() {
@SuppressWarnings("unchecked") // we use a runtime check to make sure the 'T's equal
public <T> TypeAdapter<T> create(MiniGson context, TypeToken<T> typeToken) {
Class<? super T> rawType = typeToken.getRawType();
return (rawType == unboxed || rawType == boxed) ? (TypeAdapter<T>) typeAdapter : null;
}
@Override public String toString() {
return "Factory[type=" + boxed.getName()
+ "+" + unboxed.getName() + ",adapter=" + typeAdapter + "]";
}
};
}
public static <TT> TypeAdapter.Factory newFactoryForMultipleTypes(
final Class<TT> base, final Class<? extends TT> sub, final TypeAdapter<? super TT> typeAdapter) {
return new TypeAdapter.Factory() {
@SuppressWarnings("unchecked") // we use a runtime check to make sure the 'T's equal
public <T> TypeAdapter<T> create(MiniGson context, TypeToken<T> typeToken) {
Class<? super T> rawType = typeToken.getRawType();
return (rawType == base || rawType == sub) ? (TypeAdapter<T>) typeAdapter : null;
}
@Override public String toString() {
return "Factory[type=" + base.getName()
+ "+" + sub.getName() + ",adapter=" + typeAdapter + "]";
}
};
}
public static <TT> TypeAdapter.Factory newTypeHierarchyFactory(
final Class<TT> clazz, final TypeAdapter<TT> typeAdapter) {
return new TypeAdapter.Factory() {
@SuppressWarnings("unchecked")
public <T> TypeAdapter<T> create(MiniGson context, TypeToken<T> typeToken) {
return clazz.isAssignableFrom(typeToken.getRawType()) ? (TypeAdapter<T>) typeAdapter : null;
}
@Override public String toString() {
return "Factory[typeHierarchy=" + clazz.getName() + ",adapter=" + typeAdapter + "]";
}
};
}
}

View File

@ -0,0 +1,7 @@
/**
* Do NOT use any class in this package as they are meant for internal use in Gson.
* These classes will very likely change incompatibly in future versions. You have been warned.
*
* @author Inderjeet Singh, Joel Leitch, Jesse Wilson
*/
package com.massivecraft.core.lib.gson2.internal;

View File

@ -0,0 +1,11 @@
/**
* This package provides the {@link com.massivecraft.core.lib.gson2.Gson} class to convert Json to Java and
* vice-versa.
*
* <p>The primary class to use is {@link com.massivecraft.core.lib.gson2.Gson} which can be constructed with
* {@code new Gson()} (using default settings) or by using {@link com.massivecraft.core.lib.gson2.GsonBuilder}
* (to configure various options such as using versioning and so on).</p>
*
* @author Inderjeet Singh, Joel Leitch
*/
package com.massivecraft.core.lib.gson2;

View File

@ -0,0 +1,307 @@
/*
* Copyright (C) 2008 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.massivecraft.core.lib.gson2.reflect;
import com.massivecraft.core.lib.gson2.internal.$Gson$Preconditions;
import com.massivecraft.core.lib.gson2.internal.$Gson$Types;
import java.lang.reflect.GenericArrayType;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import java.util.HashMap;
import java.util.Map;
/**
* Represents a generic type {@code T}. Java doesn't yet provide a way to
* represent generic types, so this class does. Forces clients to create a
* subclass of this class which enables retrieval the type information even at
* runtime.
*
* <p>For example, to create a type literal for {@code List<String>}, you can
* create an empty anonymous inner class:
*
* <p>
* {@code TypeToken<List<String>> list = new TypeToken<List<String>>() {};}
*
* <p>This syntax cannot be used to create type literals that have wildcard
* parameters, such as {@code Class<?>} or {@code List<? extends CharSequence>}.
*
* @author Bob Lee
* @author Sven Mawson
* @author Jesse Wilson
*/
public class TypeToken<T> {
final Class<? super T> rawType;
final Type type;
final int hashCode;
/**
* Constructs a new type literal. Derives represented class from type
* parameter.
*
* <p>Clients create an empty anonymous subclass. Doing so embeds the type
* parameter in the anonymous class's type hierarchy so we can reconstitute it
* at runtime despite erasure.
*/
@SuppressWarnings("unchecked")
protected TypeToken() {
this.type = getSuperclassTypeParameter(getClass());
this.rawType = (Class<? super T>) $Gson$Types.getRawType(type);
this.hashCode = type.hashCode();
}
/**
* Unsafe. Constructs a type literal manually.
*/
@SuppressWarnings("unchecked")
TypeToken(Type type) {
this.type = $Gson$Types.canonicalize($Gson$Preconditions.checkNotNull(type));
this.rawType = (Class<? super T>) $Gson$Types.getRawType(this.type);
this.hashCode = this.type.hashCode();
}
/**
* Returns the type from super class's type parameter in {@link $Gson$Types#canonicalize
* canonical form}.
*/
static Type getSuperclassTypeParameter(Class<?> subclass) {
Type superclass = subclass.getGenericSuperclass();
if (superclass instanceof Class) {
throw new RuntimeException("Missing type parameter.");
}
ParameterizedType parameterized = (ParameterizedType) superclass;
return $Gson$Types.canonicalize(parameterized.getActualTypeArguments()[0]);
}
/**
* Returns the raw (non-generic) type for this type.
*/
public final Class<? super T> getRawType() {
return rawType;
}
/**
* Gets underlying {@code Type} instance.
*/
public final Type getType() {
return type;
}
/**
* Check if this type is assignable from the given class object.
*
* @deprecated this implementation may be inconsistent with javac for types
* with wildcards.
*/
@Deprecated
public boolean isAssignableFrom(Class<?> cls) {
return isAssignableFrom((Type) cls);
}
/**
* Check if this type is assignable from the given Type.
*
* @deprecated this implementation may be inconsistent with javac for types
* with wildcards.
*/
@Deprecated
public boolean isAssignableFrom(Type from) {
if (from == null) {
return false;
}
if (type.equals(from)) {
return true;
}
if (type instanceof Class<?>) {
return rawType.isAssignableFrom($Gson$Types.getRawType(from));
} else if (type instanceof ParameterizedType) {
return isAssignableFrom(from, (ParameterizedType) type,
new HashMap<String, Type>());
} else if (type instanceof GenericArrayType) {
return rawType.isAssignableFrom($Gson$Types.getRawType(from))
&& isAssignableFrom(from, (GenericArrayType) type);
} else {
throw buildUnexpectedTypeError(
type, Class.class, ParameterizedType.class, GenericArrayType.class);
}
}
/**
* Check if this type is assignable from the given type token.
*
* @deprecated this implementation may be inconsistent with javac for types
* with wildcards.
*/
@Deprecated
public boolean isAssignableFrom(TypeToken<?> token) {
return isAssignableFrom(token.getType());
}
/**
* Private helper function that performs some assignability checks for
* the provided GenericArrayType.
*/
private static boolean isAssignableFrom(Type from, GenericArrayType to) {
Type toGenericComponentType = to.getGenericComponentType();
if (toGenericComponentType instanceof ParameterizedType) {
Type t = from;
if (from instanceof GenericArrayType) {
t = ((GenericArrayType) from).getGenericComponentType();
} else if (from instanceof Class<?>) {
Class<?> classType = (Class<?>) from;
while (classType.isArray()) {
classType = classType.getComponentType();
}
t = classType;
}
return isAssignableFrom(t, (ParameterizedType) toGenericComponentType,
new HashMap<String, Type>());
}
// No generic defined on "to"; therefore, return true and let other
// checks determine assignability
return true;
}
/**
* Private recursive helper function to actually do the type-safe checking
* of assignability.
*/
private static boolean isAssignableFrom(Type from, ParameterizedType to,
Map<String, Type> typeVarMap) {
if (from == null) {
return false;
}
if (to.equals(from)) {
return true;
}
// First figure out the class and any type information.
Class<?> clazz = $Gson$Types.getRawType(from);
ParameterizedType ptype = null;
if (from instanceof ParameterizedType) {
ptype = (ParameterizedType) from;
}
// Load up parameterized variable info if it was parameterized.
if (ptype != null) {
Type[] tArgs = ptype.getActualTypeArguments();
TypeVariable<?>[] tParams = clazz.getTypeParameters();
for (int i = 0; i < tArgs.length; i++) {
Type arg = tArgs[i];
TypeVariable<?> var = tParams[i];
while (arg instanceof TypeVariable<?>) {
TypeVariable<?> v = (TypeVariable<?>) arg;
arg = typeVarMap.get(v.getName());
}
typeVarMap.put(var.getName(), arg);
}
// check if they are equivalent under our current mapping.
if (typeEquals(ptype, to, typeVarMap)) {
return true;
}
}
for (Type itype : clazz.getGenericInterfaces()) {
if (isAssignableFrom(itype, to, new HashMap<String, Type>(typeVarMap))) {
return true;
}
}
// Interfaces didn't work, try the superclass.
Type sType = clazz.getGenericSuperclass();
return isAssignableFrom(sType, to, new HashMap<String, Type>(typeVarMap));
}
/**
* Checks if two parameterized types are exactly equal, under the variable
* replacement described in the typeVarMap.
*/
private static boolean typeEquals(ParameterizedType from,
ParameterizedType to, Map<String, Type> typeVarMap) {
if (from.getRawType().equals(to.getRawType())) {
Type[] fromArgs = from.getActualTypeArguments();
Type[] toArgs = to.getActualTypeArguments();
for (int i = 0; i < fromArgs.length; i++) {
if (!matches(fromArgs[i], toArgs[i], typeVarMap)) {
return false;
}
}
return true;
}
return false;
}
private static AssertionError buildUnexpectedTypeError(
Type token, Class<?>... expected) {
// Build exception message
StringBuilder exceptionMessage =
new StringBuilder("Unexpected type. Expected one of: ");
for (Class<?> clazz : expected) {
exceptionMessage.append(clazz.getName()).append(", ");
}
exceptionMessage.append("but got: ").append(token.getClass().getName())
.append(", for type token: ").append(token.toString()).append('.');
return new AssertionError(exceptionMessage.toString());
}
/**
* Checks if two types are the same or are equivalent under a variable mapping
* given in the type map that was provided.
*/
private static boolean matches(Type from, Type to, Map<String, Type> typeMap) {
return to.equals(from)
|| (from instanceof TypeVariable
&& to.equals(typeMap.get(((TypeVariable<?>) from).getName())));
}
@Override public final int hashCode() {
return this.hashCode;
}
@Override public final boolean equals(Object o) {
return o instanceof TypeToken<?>
&& $Gson$Types.equals(type, ((TypeToken<?>) o).type);
}
@Override public final String toString() {
return $Gson$Types.typeToString(type);
}
/**
* Gets type literal for the given {@code Type} instance.
*/
public static TypeToken<?> get(Type type) {
return new TypeToken<Object>(type);
}
/**
* Gets type literal for the given {@code Class} instance.
*/
public static <T> TypeToken<T> get(Class<T> type) {
return new TypeToken<T>(type);
}
}

View File

@ -0,0 +1,6 @@
/**
* This package provides utility classes for finding type information for generic types.
*
* @author Inderjeet Singh, Joel Leitch
*/
package com.massivecraft.core.lib.gson2.reflect;

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,71 @@
/*
* Copyright (C) 2010 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.massivecraft.core.lib.gson2.stream;
/**
* Lexical scoping elements within a JSON reader or writer.
*
* @author Jesse Wilson
* @since 1.6
*/
enum JsonScope {
/**
* An array with no elements requires no separators or newlines before
* it is closed.
*/
EMPTY_ARRAY,
/**
* A array with at least one value requires a comma and newline before
* the next element.
*/
NONEMPTY_ARRAY,
/**
* An object with no name/value pairs requires no separators or newlines
* before it is closed.
*/
EMPTY_OBJECT,
/**
* An object whose most recent element is a key. The next element must
* be a value.
*/
DANGLING_NAME,
/**
* An object with at least one name/value pair requires a comma and
* newline before the next element.
*/
NONEMPTY_OBJECT,
/**
* No object or array has been started.
*/
EMPTY_DOCUMENT,
/**
* A document with at an array or object.
*/
NONEMPTY_DOCUMENT,
/**
* A document that's been closed and cannot be accessed.
*/
CLOSED,
}

View File

@ -0,0 +1,85 @@
/*
* Copyright (C) 2010 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.massivecraft.core.lib.gson2.stream;
/**
* A structure, name or value type in a JSON-encoded string.
*
* @author Jesse Wilson
* @since 1.6
*/
public enum JsonToken {
/**
* The opening of a JSON array. Written using {@link JsonWriter#beginObject}
* and read using {@link JsonReader#beginObject}.
*/
BEGIN_ARRAY,
/**
* The closing of a JSON array. Written using {@link JsonWriter#endArray}
* and read using {@link JsonReader#endArray}.
*/
END_ARRAY,
/**
* The opening of a JSON object. Written using {@link JsonWriter#beginObject}
* and read using {@link JsonReader#beginObject}.
*/
BEGIN_OBJECT,
/**
* The closing of a JSON object. Written using {@link JsonWriter#endObject}
* and read using {@link JsonReader#endObject}.
*/
END_OBJECT,
/**
* A JSON property name. Within objects, tokens alternate between names and
* their values. Written using {@link JsonWriter#name} and read using {@link
* JsonReader#nextName}
*/
NAME,
/**
* A JSON string.
*/
STRING,
/**
* A JSON number represented in this API by a Java {@code double}, {@code
* long}, or {@code int}.
*/
NUMBER,
/**
* A JSON {@code true} or {@code false}.
*/
BOOLEAN,
/**
* A JSON {@code null}.
*/
NULL,
/**
* The end of the JSON stream. This sentinel value is returned by {@link
* JsonReader#peek()} to signal that the JSON-encoded value has no more
* tokens.
*/
END_DOCUMENT
}

View File

@ -0,0 +1,610 @@
/*
* Copyright (C) 2010 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.massivecraft.core.lib.gson2.stream;
import java.io.Closeable;
import java.io.IOException;
import java.io.Writer;
import java.util.ArrayList;
import java.util.List;
/**
* Writes a JSON (<a href="http://www.ietf.org/rfc/rfc4627.txt">RFC 4627</a>)
* 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
* and end delimiters of objects and arrays.
*
* <h3>Encoding JSON</h3>
* To encode your data as JSON, create a new {@code JsonWriter}. Each JSON
* document must contain one top-level array or object. Call methods on the
* writer as you walk the structure's contents, nesting arrays and objects as
* necessary:
* <ul>
* <li>To write <strong>arrays</strong>, first call {@link #beginArray()}.
* Write each of the array's elements with the appropriate {@link #value}
* methods or by nesting other arrays and objects. Finally close the array
* using {@link #endArray()}.
* <li>To write <strong>objects</strong>, first call {@link #beginObject()}.
* Write each of the object's properties by alternating calls to
* {@link #name} with the property's value. Write property values with the
* appropriate {@link #value} method or by nesting other objects or arrays.
* Finally close the object using {@link #endObject()}.
* </ul>
*
* <h3>Example</h3>
* Suppose we'd like to encode a stream of messages such as the following: <pre> {@code
* [
* {
* "id": 912345678901,
* "text": "How do I stream JSON in Java?",
* "geo": null,
* "user": {
* "name": "json_newb",
* "followers_count": 41
* }
* },
* {
* "id": 912345678902,
* "text": "@json_newb just use JsonWriter!",
* "geo": [50.454722, -104.606667],
* "user": {
* "name": "jesse",
* "followers_count": 2
* }
* }
* ]}</pre>
* This code encodes the above structure: <pre> {@code
* public void writeJsonStream(OutputStream out, List<Message> messages) throws IOException {
* JsonWriter writer = new JsonWriter(new OutputStreamWriter(out, "UTF-8"));
* writer.setIndentSpaces(4);
* writeMessagesArray(writer, messages);
* writer.close();
* }
*
* public void writeMessagesArray(JsonWriter writer, List<Message> messages) throws IOException {
* writer.beginArray();
* for (Message message : messages) {
* writeMessage(writer, message);
* }
* writer.endArray();
* }
*
* public void writeMessage(JsonWriter writer, Message message) throws IOException {
* writer.beginObject();
* writer.name("id").value(message.getId());
* writer.name("text").value(message.getText());
* if (message.getGeo() != null) {
* writer.name("geo");
* writeDoublesArray(writer, message.getGeo());
* } else {
* writer.name("geo").nullValue();
* }
* writer.name("user");
* writeUser(writer, message.getUser());
* writer.endObject();
* }
*
* public void writeUser(JsonWriter writer, User user) throws IOException {
* writer.beginObject();
* writer.name("name").value(user.getName());
* writer.name("followers_count").value(user.getFollowersCount());
* writer.endObject();
* }
*
* public void writeDoublesArray(JsonWriter writer, List<Double> doubles) throws IOException {
* writer.beginArray();
* for (Double value : doubles) {
* writer.value(value);
* }
* writer.endArray();
* }}</pre>
*
* <p>Each {@code JsonWriter} may be used to write a single JSON stream.
* Instances of this class are not thread safe. Calls that would result in a
* malformed JSON string will fail with an {@link IllegalStateException}.
*
* @author Jesse Wilson
* @since 1.6
*/
public class JsonWriter implements Closeable {
/** The output data, containing at most one top-level array or object. */
private final Writer out;
private final List<JsonScope> stack = new ArrayList<JsonScope>();
{
stack.add(JsonScope.EMPTY_DOCUMENT);
}
/**
* A string containing a full set of spaces for a single level of
* indentation, or null for no pretty printing.
*/
private String indent;
/**
* The name/value separator; either ":" or ": ".
*/
private String separator = ":";
private boolean lenient;
private boolean htmlSafe;
private String deferredName;
private boolean serializeNulls = true;
/**
* Creates a new instance that writes a JSON-encoded stream to {@code out}.
* For best performance, ensure {@link Writer} is buffered; wrapping in
* {@link java.io.BufferedWriter BufferedWriter} if necessary.
*/
public JsonWriter(Writer out) {
if (out == null) {
throw new NullPointerException("out == null");
}
this.out = out;
}
/**
* Sets the indentation string to be repeated for each level of indentation
* in the encoded document. If {@code indent.isEmpty()} the encoded document
* will be compact. Otherwise the encoded document will be more
* human-readable.
*
* @param indent a string containing only whitespace.
*/
public final void setIndent(String indent) {
if (indent.length() == 0) {
this.indent = null;
this.separator = ":";
} else {
this.indent = indent;
this.separator = ": ";
}
}
/**
* Configure this writer to relax its syntax rules. By default, this writer
* only emits well-formed JSON as specified by <a
* href="http://www.ietf.org/rfc/rfc4627.txt">RFC 4627</a>. Setting the writer
* to lenient permits the following:
* <ul>
* <li>Top-level values of any type. With strict writing, the top-level
* value must be an object or an array.
* <li>Numbers may be {@link Double#isNaN() NaNs} or {@link
* Double#isInfinite() infinities}.
* </ul>
*/
public final void setLenient(boolean lenient) {
this.lenient = lenient;
}
/**
* Returns true if this writer has relaxed syntax rules.
*/
public boolean isLenient() {
return lenient;
}
/**
* Configure this writer to emit JSON that's safe for direct inclusion in HTML
* and XML documents. This escapes the HTML characters {@code <}, {@code >},
* {@code &} and {@code =} before writing them to the stream. Without this
* setting, your XML/HTML encoder should replace these characters with the
* corresponding escape sequences.
*/
public final void setHtmlSafe(boolean htmlSafe) {
this.htmlSafe = htmlSafe;
}
/**
* Returns true if this writer writes JSON that's safe for inclusion in HTML
* and XML documents.
*/
public final boolean isHtmlSafe() {
return htmlSafe;
}
/**
* Sets whether object members are serialized when their value is null.
* This has no impact on array elements. The default is true.
*/
public final void setSerializeNulls(boolean serializeNulls) {
this.serializeNulls = serializeNulls;
}
/**
* Returns true if object members are serialized when their value is null.
* This has no impact on array elements. The default is true.
*/
public final boolean getSerializeNulls() {
return serializeNulls;
}
/**
* Begins encoding a new array. Each call to this method must be paired with
* a call to {@link #endArray}.
*
* @return this writer.
*/
public JsonWriter beginArray() throws IOException {
writeDeferredName();
return open(JsonScope.EMPTY_ARRAY, "[");
}
/**
* Ends encoding the current array.
*
* @return this writer.
*/
public JsonWriter endArray() throws IOException {
return close(JsonScope.EMPTY_ARRAY, JsonScope.NONEMPTY_ARRAY, "]");
}
/**
* Begins encoding a new object. Each call to this method must be paired
* with a call to {@link #endObject}.
*
* @return this writer.
*/
public JsonWriter beginObject() throws IOException {
writeDeferredName();
return open(JsonScope.EMPTY_OBJECT, "{");
}
/**
* Ends encoding the current object.
*
* @return this writer.
*/
public JsonWriter endObject() throws IOException {
return close(JsonScope.EMPTY_OBJECT, JsonScope.NONEMPTY_OBJECT, "}");
}
/**
* Enters a new scope by appending any necessary whitespace and the given
* bracket.
*/
private JsonWriter open(JsonScope empty, String openBracket) throws IOException {
beforeValue(true);
stack.add(empty);
out.write(openBracket);
return this;
}
/**
* Closes the current scope by appending any necessary whitespace and the
* given bracket.
*/
private JsonWriter close(JsonScope empty, JsonScope nonempty, String closeBracket)
throws IOException {
JsonScope context = peek();
if (context != nonempty && context != empty) {
throw new IllegalStateException("Nesting problem: " + stack);
}
if (deferredName != null) {
throw new IllegalStateException("Dangling name: " + deferredName);
}
stack.remove(stack.size() - 1);
if (context == nonempty) {
newline();
}
out.write(closeBracket);
return this;
}
/**
* Returns the value on the top of the stack.
*/
private JsonScope peek() {
return stack.get(stack.size() - 1);
}
/**
* Replace the value on the top of the stack with the given value.
*/
private void replaceTop(JsonScope topOfStack) {
stack.set(stack.size() - 1, topOfStack);
}
/**
* Encodes the property name.
*
* @param name the name of the forthcoming value. May not be null.
* @return this writer.
*/
public JsonWriter name(String name) throws IOException {
if (name == null) {
throw new NullPointerException("name == null");
}
if (deferredName != null) {
throw new IllegalStateException();
}
deferredName = name;
return this;
}
private void writeDeferredName() throws IOException {
if (deferredName != null) {
beforeName();
string(deferredName);
deferredName = null;
}
}
/**
* Encodes {@code value}.
*
* @param value the literal string value, or null to encode a null literal.
* @return this writer.
*/
public JsonWriter value(String value) throws IOException {
if (value == null) {
return nullValue();
}
writeDeferredName();
beforeValue(false);
string(value);
return this;
}
/**
* Encodes {@code null}.
*
* @return this writer.
*/
public JsonWriter nullValue() throws IOException {
if (deferredName != null) {
if (serializeNulls) {
writeDeferredName();
} else {
deferredName = null;
return this; // skip the name and the value
}
}
beforeValue(false);
out.write("null");
return this;
}
/**
* Encodes {@code value}.
*
* @return this writer.
*/
public JsonWriter value(boolean value) throws IOException {
writeDeferredName();
beforeValue(false);
out.write(value ? "true" : "false");
return this;
}
/**
* Encodes {@code value}.
*
* @param value a finite value. May not be {@link Double#isNaN() NaNs} or
* {@link Double#isInfinite() infinities}.
* @return this writer.
*/
public JsonWriter value(double value) throws IOException {
if (Double.isNaN(value) || Double.isInfinite(value)) {
throw new IllegalArgumentException("Numeric values must be finite, but was " + value);
}
writeDeferredName();
beforeValue(false);
out.append(Double.toString(value));
return this;
}
/**
* Encodes {@code value}.
*
* @return this writer.
*/
public JsonWriter value(long value) throws IOException {
writeDeferredName();
beforeValue(false);
out.write(Long.toString(value));
return this;
}
/**
* Encodes {@code value}.
*
* @param value a finite value. May not be {@link Double#isNaN() NaNs} or
* {@link Double#isInfinite() infinities}.
* @return this writer.
*/
public JsonWriter value(Number value) throws IOException {
if (value == null) {
return nullValue();
}
writeDeferredName();
String string = value.toString();
if (!lenient
&& (string.equals("-Infinity") || string.equals("Infinity") || string.equals("NaN"))) {
throw new IllegalArgumentException("Numeric values must be finite, but was " + value);
}
beforeValue(false);
out.append(string);
return this;
}
/**
* Ensures all buffered data is written to the underlying {@link Writer}
* and flushes that writer.
*/
public void flush() throws IOException {
out.flush();
}
/**
* Flushes and closes this writer and the underlying {@link Writer}.
*
* @throws IOException if the JSON document is incomplete.
*/
public void close() throws IOException {
out.close();
if (peek() != JsonScope.NONEMPTY_DOCUMENT) {
throw new IOException("Incomplete document");
}
}
private void string(String value) throws IOException {
out.write("\"");
for (int i = 0, length = value.length(); i < length; i++) {
char c = value.charAt(i);
/*
* From RFC 4627, "All Unicode characters may be placed within the
* quotation marks except for the characters that must be escaped:
* quotation mark, reverse solidus, and the control characters
* (U+0000 through U+001F)."
*
* We also escape '\u2028' and '\u2029', which JavaScript interprets as
* newline characters. This prevents eval() from failing with a syntax
* error. http://code.google.com/p/google-gson/issues/detail?id=341
*/
switch (c) {
case '"':
case '\\':
out.write('\\');
out.write(c);
break;
case '\t':
out.write("\\t");
break;
case '\b':
out.write("\\b");
break;
case '\n':
out.write("\\n");
break;
case '\r':
out.write("\\r");
break;
case '\f':
out.write("\\f");
break;
case '<':
case '>':
case '&':
case '=':
case '\'':
if (htmlSafe) {
out.write(String.format("\\u%04x", (int) c));
} else {
out.write(c);
}
break;
case '\u2028':
case '\u2029':
out.write(String.format("\\u%04x", (int) c));
break;
default:
if (c <= 0x1F) {
out.write(String.format("\\u%04x", (int) c));
} else {
out.write(c);
}
break;
}
}
out.write("\"");
}
private void newline() throws IOException {
if (indent == null) {
return;
}
out.write("\n");
for (int i = 1; i < stack.size(); i++) {
out.write(indent);
}
}
/**
* Inserts any necessary separators and whitespace before a name. Also
* adjusts the stack to expect the name's value.
*/
private void beforeName() throws IOException {
JsonScope context = peek();
if (context == JsonScope.NONEMPTY_OBJECT) { // first in object
out.write(',');
} else if (context != JsonScope.EMPTY_OBJECT) { // not in an object!
throw new IllegalStateException("Nesting problem: " + stack);
}
newline();
replaceTop(JsonScope.DANGLING_NAME);
}
/**
* Inserts any necessary separators and whitespace before a literal value,
* inline array, or inline object. Also adjusts the stack to expect either a
* 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.
*/
private void beforeValue(boolean root) throws IOException {
switch (peek()) {
case EMPTY_DOCUMENT: // first in document
if (!lenient && !root) {
throw new IllegalStateException(
"JSON must start with an array or an object.");
}
replaceTop(JsonScope.NONEMPTY_DOCUMENT);
break;
case EMPTY_ARRAY: // first in array
replaceTop(JsonScope.NONEMPTY_ARRAY);
newline();
break;
case NONEMPTY_ARRAY: // another in array
out.append(',');
newline();
break;
case DANGLING_NAME: // value for name
out.append(separator);
replaceTop(JsonScope.NONEMPTY_OBJECT);
break;
case NONEMPTY_DOCUMENT:
throw new IllegalStateException(
"JSON must have only one top-level value.");
default:
throw new IllegalStateException("Nesting problem: " + stack);
}
}
}

View File

@ -0,0 +1,44 @@
/*
* Copyright (C) 2010 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.massivecraft.core.lib.gson2.stream;
import java.io.IOException;
/**
* Thrown when a reader encounters malformed JSON. Some syntax errors can be
* ignored by calling {@link JsonReader#setLenient(boolean)}.
*/
public final class MalformedJsonException extends IOException {
private static final long serialVersionUID = 1L;
public MalformedJsonException(String msg) {
super(msg);
}
public MalformedJsonException(String msg, Throwable throwable) {
super(msg);
// Using initCause() instead of calling super() because Java 1.5 didn't retrofit IOException
// with a constructor with Throwable. This was done in Java 1.6
initCause(throwable);
}
public MalformedJsonException(Throwable throwable) {
// Using initCause() instead of calling super() because Java 1.5 didn't retrofit IOException
// with a constructor with Throwable. This was done in Java 1.6
initCause(throwable);
}
}

View File

@ -0,0 +1,60 @@
/*
* Copyright (C) 2011 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.massivecraft.core.lib.gson2.stream;
/**
* A pool of string instances. Unlike the {@link String#intern() VM's
* interned strings}, this pool provides no guarantee of reference equality.
* It is intended only to save allocations. This class is not thread safe.
*/
final class StringPool {
private final String[] pool = new String[512];
/**
* Returns a string equal to {@code new String(array, start, length)}.
*/
public String get(char[] array, int start, int length) {
// Compute an arbitrary hash of the content
int hashCode = 0;
for (int i = start; i < start + length; i++) {
hashCode = (hashCode * 31) + array[i];
}
// Pick a bucket using Doug Lea's supplemental secondaryHash function (from HashMap)
hashCode ^= (hashCode >>> 20) ^ (hashCode >>> 12);
hashCode ^= (hashCode >>> 7) ^ (hashCode >>> 4);
int index = hashCode & (pool.length - 1);
String pooled = pool[index];
if (pooled == null || pooled.length() != length) {
String result = new String(array, start, length);
pool[index] = result;
return result;
}
for (int i = 0; i < length; i++) {
if (pooled.charAt(i) != array[start + i]) {
String result = new String(array, start, length);
pool[index] = result;
return result;
}
}
return pooled;
}
}

View File

@ -0,0 +1,45 @@
package com.massivecraft.core.persist;
/**
* Usage of this class is highly optional. You may persist anything. If you are
* creating the class to be persisted yourself, it might be handy to extend this
* Entity class. It just contains a set of shortcut methods.
*/
// Self referencing generics using the "getThis trick".
// http://www.angelikalanger.com/GenericsFAQ/FAQSections/ProgrammingIdioms.html#FAQ206
public abstract class Entity<T extends Entity<T>>
{
public abstract IClassManager<T> getManager();
protected abstract T getThis();
public String attach()
{
return this.getManager().attach(getThis());
}
public void detach()
{
this.getManager().detachEntity(getThis());
}
public boolean attached()
{
return this.getManager().containsEntity(getThis());
}
public boolean detached()
{
return ! this.attached();
}
public boolean save()
{
return this.getManager().saveEntity(getThis());
}
public String getId()
{
return this.getManager().id(getThis());
}
}

View File

@ -0,0 +1,74 @@
package com.massivecraft.core.persist;
import java.util.Collection;
import java.util.Comparator;
import java.util.Map;
public interface IClassManager<T>
{
// What do we handle?
public Class<T> getManagedClass();
// This simply creates and returns a new instance
// It does not detach/attach or anything. Just creates a new instance.
public T createNewInstance();
// Creativeness
public boolean getIsCreative();
public void setIsCreative(boolean val);
// Create new instance with auto increment id
public T create();
// Create new instance with the requested id
public T create(Object oid);
// Add & Remove
public String attach(T entity);
public String attach(T entity, Object oid);
public void detachEntity(T entity);
public void detachId(Object oid);
public boolean containsEntity(T entity);
public boolean containsId(Object oid);
// Disc io triggers
public boolean saveEntity(T entity);
public boolean saveId(Object oid);
public boolean saveAll();
public boolean loaded(Object oid);
public T load(Object oid);
public boolean loadAll();
// Should that instance be saved or not?
// If it is default it should not be saved.
public boolean shouldBeSaved(T entity);
// Id handling
// Get the id for this entity. Return null if it does not have an ID
public String id(T entity);
// In some cases certain non string objects can represent a String id.
// This method is used as a wrapper for that.
// TODO: Javadoc: Should start with a null check as well as a String check.
public String idFix(Object oid);
// autoIncrement ids
public String idCurrent();
public String idNext(boolean advance);
public boolean idUpdateCurrentFor(Object oid);
// Get the entity for the id. If creative it is created if it does not exist else null.
public T get(Object oid, boolean creative);
public T get(Object oid);
// Get the entity that best matches the id.
public T getBestMatch(Object oid);
// Get all
public Collection<T> getAll();
public Collection<T> getAll(Predictate<T> where);
public Collection<T> getAll(Predictate<T> where, Comparator<T> orderby);
public Collection<T> getAll(Predictate<T> where, Comparator<T> orderby, Integer limit);
public Collection<T> getAll(Predictate<T> where, Comparator<T> orderby, Integer limit, Integer offset);
public Collection<String> getIds();
public Map<String, T> getMap();
}

View File

@ -0,0 +1,209 @@
package com.massivecraft.core.persist;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Map;
import java.util.Timer;
public class Persist
{
private Map<Class<?>, IClassManager<?>> classManagers = new HashMap<Class<?>, IClassManager<?>>();
public <T> void setManager(Class<T> clazz, IClassManager<T> manager)
{
this.classManagers.put(clazz, manager);
};
public Map<Class<?>, IClassManager<?>> getClassManagers()
{
return this.classManagers;
}
protected Timer timer = new Timer();
private Map<Class<?>, SaveTask<?>> classSaveTasks = new HashMap<Class<?>, SaveTask<?>>();
@SuppressWarnings("unchecked")
public <T> void setSaveInterval(Class<T> clazz, long interval)
{
// Fetch the task or create a new one.
SaveTask<T> task = (SaveTask<T>) this.classSaveTasks.get(clazz);
if (task == null)
{
task = new SaveTask<T>(this, clazz);
this.classSaveTasks.put(clazz, task);
}
else
{
task.cancel();
}
// Schedule the task
timer.scheduleAtFixedRate(task, interval, interval);
};
@SuppressWarnings("unchecked")
public <T> IClassManager<T> getManager(Class<T> clazz)
{
return (IClassManager<T>) this.classManagers.get(clazz);
}
@SuppressWarnings("unchecked")
public <T extends Object> IClassManager<T> getManager(T entity)
{
return (IClassManager<T>) this.getManager(entity.getClass());
}
// -------------------------------------------- //
// SAVE ALL
// -------------------------------------------- //
public void saveAll()
{
for (IClassManager<?> m : this.classManagers.values())
{
m.saveAll();
}
}
// -------------------------------------------- //
// UTILS
// -------------------------------------------- //
public static void write(File file, String content) throws IOException
{
BufferedWriter out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file, false), "UTF8"));
out.write(content);
out.close();
}
public static String read(File file) throws IOException
{
BufferedReader in = new BufferedReader(new InputStreamReader(new FileInputStream(file), "UTF-8"));
String ret = new String(new byte[0], "UTF-8");
String line;
while ((line = in.readLine()) != null)
{
ret += line;
}
in.close();
return ret;
}
public static boolean writeCatch(File file, String content)
{
try
{
write(file, content);
return true;
}
catch (Exception e)
{
return false;
}
}
public static String readCatch(File file)
{
try
{
return read(file);
}
catch (IOException e)
{
return null;
}
}
public static <T> ArrayList<T> uglySQL(Collection<T> items, Predictate<T> where, Comparator<T> orderby, Integer limit, Integer offset)
{
ArrayList<T> ret = new ArrayList<T>(items.size());
// WHERE
for (T item : items)
{
if (where.apply(item))
{
ret.add(item);
}
}
// ORDERBY
Collections.sort(ret, orderby);
// LIMIT AND OFFSET
// Parse args
int fromIndex = 0;
if (offset != null)
{
fromIndex = offset;
}
int toIndex = ret.size()-1;
if (limit != null)
{
toIndex = offset+limit;
}
// Clean args
if (fromIndex < 0)
{
fromIndex = 0;
}
else if (fromIndex > ret.size()-1)
{
fromIndex = ret.size()-1;
}
if (toIndex < fromIndex)
{
toIndex = fromIndex;
}
else if (toIndex > ret.size()-1)
{
toIndex = ret.size()-1;
}
// No limit?
if (fromIndex == 0 && toIndex == ret.size()-1) return ret;
return new ArrayList<T>(ret.subList(fromIndex, toIndex));
}
public static String getBestCIStart(Collection<String> candidates, String start)
{
String ret = null;
int best = 0;
start = start.toLowerCase();
int minlength = start.length();
for (String candidate : candidates)
{
if (candidate.length() < minlength) continue;
if ( ! candidate.toLowerCase().startsWith(start)) continue;
// The closer to zero the better
int lendiff = candidate.length() - minlength;
if (lendiff == 0)
{
return candidate;
}
if (lendiff < best || best == 0)
{
best = lendiff;
ret = candidate;
}
}
return ret;
}
}

View File

@ -0,0 +1,30 @@
package com.massivecraft.core.persist;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
public abstract class PlayerEntity extends Entity<PlayerEntity>
{
@Override
protected PlayerEntity getThis()
{
return this;
}
public Player getPlayer()
{
return Bukkit.getPlayer(this.getId());
}
public boolean isOnline()
{
return this.getPlayer() != null;
}
public boolean isOffline()
{
return ! isOnline();
}
}

View File

@ -0,0 +1,6 @@
package com.massivecraft.core.persist;
public interface Predictate<T>
{
public boolean apply(T type);
}

View File

@ -0,0 +1,35 @@
package com.massivecraft.core.persist;
import java.util.TimerTask;
public class SaveTask<T> extends TimerTask
{
private Persist persist;
private Class<T> clazz;
public Class<T> getToBeSavedClass() { return clazz; }
public SaveTask(Persist persist, Class<T> clazz)
{
this.persist = persist;
this.clazz = clazz;
}
public SaveTask(Persist persist)
{
this(persist, null);
}
@Override
public void run()
{
if (this.clazz == null)
{
this.persist.saveAll();
}
else
{
this.persist.getManager(this.clazz).saveAll();
}
}
}

View File

@ -0,0 +1,458 @@
package com.massivecraft.core.persist.gson;
import java.io.File;
import java.util.Collection;
import java.util.Comparator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentSkipListMap;
import java.util.concurrent.ConcurrentSkipListSet;
import java.util.concurrent.CopyOnWriteArraySet;
import com.massivecraft.core.lib.gson2.Gson;
import com.massivecraft.core.persist.IClassManager;
import com.massivecraft.core.persist.Persist;
import com.massivecraft.core.persist.Predictate;
public abstract class GsonClassManager<T> implements IClassManager<T>
{
// -------------------------------------------- //
// NON INTERFACE
// -------------------------------------------- //
private final static String DOTJSON = ".json";
private Set<String> ids;
private Set<T> entities;
private Map<String, T> id2entity;
private Map<T, String> entity2id;
private long idCurrent = 1;
private Gson gson;
public Gson getGson() { return gson; }
public void setGson(Gson gson) { this.gson = gson; }
private File folder;
public File getFolder() { return folder; }
public void setFolder(File val) { this.folder = val; this.folder.mkdirs(); }
private boolean creative;
@Override
public boolean getIsCreative() { return this.creative; }
@Override
public void setIsCreative(boolean val) { this.creative = val; }
private boolean didLoadAll = false;
private void loadIds()
{
for(File file : this.getFolder().listFiles(JsonFileFilter.getInstance()))
{
this.ids.add(this.idFromFile(file));
}
}
private String idFromFile(File file)
{
if (file == null) return null;
String name = file.getName();
return name.substring(0, name.length()-5);
}
private File fileFromId(Object oid)
{
String id = this.idFix(oid);
if (id == null) return null;
return new File(this.getFolder(), id+DOTJSON);
}
// -------------------------------------------- //
// CONSTRUCTORS
// -------------------------------------------- //
public GsonClassManager(Gson gson, File folder, boolean creative, boolean lazy, Set<String> ids, Set<T> entities, Map<String, T> id2entity, Map<T, String> entity2id)
{
this.gson = gson;
this.folder = folder;
this.creative = creative;
this.ids = ids;
this.entities = entities;
this.id2entity = id2entity;
this.entity2id = entity2id;
this.getFolder().mkdirs();
this.loadIds();
if ( ! lazy)
{
this.loadAll();
}
}
public GsonClassManager(Gson gson, File folder, boolean creative, boolean lazy)
{
this(
gson,
folder,
creative,
lazy,
new ConcurrentSkipListSet<String>(String.CASE_INSENSITIVE_ORDER),
new CopyOnWriteArraySet<T>(),
new ConcurrentSkipListMap<String, T>(String.CASE_INSENSITIVE_ORDER),
new ConcurrentHashMap<T, String>()
);
}
// -------------------------------------------- //
// INTERFACE IMPLEMENTATION
// -------------------------------------------- //
@Override
public abstract Class<T> getManagedClass();
@Override
public T createNewInstance()
{
try
{
return this.getManagedClass().newInstance();
}
catch (Exception e)
{
e.printStackTrace();
}
return null;
}
@Override
public synchronized T create()
{
return this.create(null);
}
@Override
public synchronized T create(Object oid)
{
T entity = this.createNewInstance();
if (this.attach(entity, oid) == null) return null;
return entity;
}
@Override
public synchronized String attach(T entity)
{
return this.attach(entity, null);
}
@Override
public synchronized String attach(T entity, Object oid)
{
return this.attach(entity, oid, false);
}
private synchronized String attach(T entity, Object oid, boolean allowExistingIdUsage)
{
// Check entity
if (entity == null) return null;
String id = this.id(entity);
if (id != null) return id;
// Check/Fix id
if (oid == null)
{
id = this.idNext(true);
}
else
{
id = this.idFix(oid);
if (id == null) return null;
if (this.containsId(id) && ! allowExistingIdUsage) return null;
}
// Attach
this.ids.add(id);
this.entities.add(entity);
this.id2entity.put(id, entity);
this.entity2id.put(entity, id);
// Update Auto Increment
this.idUpdateCurrentFor(id);
return id;
}
@Override
public synchronized void detachEntity(T entity)
{
if (entity == null) return;
String id = this.entity2id.get(entity);
if (id == null) return;
this.detach(entity, id);
}
@Override
public void detachId(Object oid)
{
String id = this.idFix(oid);
if (id == null) return;
T entity = this.id2entity.get(id);
this.detach(entity, id);
}
// Assumes the id is correct! For internal use only!
private void detach(T entity, String id)
{
if (id != null)
{
this.ids.remove(id);
this.removeFile(id);
}
if (entity != null)
{
this.entities.remove(entity);
this.entity2id.remove(entity);
}
}
private void removeFile(String id)
{
File file = this.fileFromId(id);
if (file.exists())
{
file.delete();
}
this.id2entity.remove(id);
}
@Override
public boolean containsEntity(T entity)
{
return this.entity2id.containsKey(entity);
}
@Override
public boolean containsId(Object oid)
{
String id = this.idFix(oid);
if (id == null) return false;
return ids.contains(id);
}
@Override
public boolean saveEntity(T entity)
{
String id = this.id(entity);
return this.save(id, entity);
}
@Override
public boolean saveId(Object oid)
{
String id = this.idFix(oid);
T entity = this.get(id);
return this.save(id, entity);
}
private boolean save(String id, T entity)
{
if (id == null) return false;
if (entity == null) return false;
if (this.shouldBeSaved(entity))
{
String json = this.getGson().toJson(entity);
File file = this.fileFromId(id);
return Persist.writeCatch(file, json);
}
this.removeFile(id);
return true;
// TODO: Perhaps implement a logger in the interface?
}
@Override
public boolean saveAll()
{
// Delete all files we do not care about.
for (File file : this.getFolder().listFiles(JsonFileFilter.getInstance()))
{
String id = this.idFromFile(file);
if ( ! this.containsId(id))
{
file.delete();
}
}
// Save all loaded entites.
for (Entry<String, T> entry: this.id2entity.entrySet())
{
this.save(entry.getKey(), entry.getValue());
}
// TODO: Bogus! This should return if a single error was encountered!
return true;
}
@Override
public boolean loaded(Object oid)
{
String id = this.idFix(oid);
if (id == null) return false;
return this.id2entity.containsKey(id);
}
@Override
public T load(Object oid)
{
String id = this.idFix(oid);
if (id == null) return null;
T entity = this.id2entity.get(id);
if (entity != null) return entity;
if ( ! this.containsId(id)) return null;
File file = this.fileFromId(id);
String json = Persist.readCatch(file);
if (json == null) return null;
entity = this.getGson().fromJson(json, this.getManagedClass());
this.attach(entity, id, true);
return entity;
}
@Override
public boolean loadAll()
{
if (this.didLoadAll) return false;
for (String id : this.ids)
{
this.load(id);
}
this.didLoadAll = true;
return true;
}
@Override
public boolean shouldBeSaved(T entity)
{
return true;
}
@Override
public String id(T entity)
{
return this.entity2id.get(entity);
}
@Override
public abstract String idFix(Object oid);
@Override
public String idCurrent()
{
return Long.toString(this.idCurrent);
}
@Override
public synchronized String idNext(boolean advance)
{
long next = this.idCurrent;
String nextString = String.valueOf(next);
while (this.containsId(nextString))
{
next += 1;
nextString = String.valueOf(next);
}
if (advance)
{
this.idCurrent = next;
}
return nextString;
}
@Override
public synchronized boolean idUpdateCurrentFor(Object oid)
{
String id = this.idFix(oid);
if (id == null) return false;
long primid;
try
{
primid = Long.parseLong(id);
}
catch (Exception e)
{
// The id was not a number. No need to care about it.
return false;
}
if (this.idCurrent < primid)
{
this.idCurrent = primid;
return true;
}
return false;
}
@Override
public synchronized T get(Object oid, boolean creative)
{
String id = this.idFix(oid);
if (id == null) return null;
T ret = this.load(id);
if (ret != null)
{
return ret;
}
if ( ! creative) return null;
return this.create(id);
}
@Override
public T get(Object oid)
{
return this.get(oid, this.getIsCreative());
}
@Override
public Collection<T> getAll()
{
this.loadAll();
return entities;
}
@Override
public Collection<T> getAll(Predictate<T> where)
{
return Persist.uglySQL(this.entities, where, null, null, null);
}
@Override
public Collection<T> getAll(Predictate<T> where, Comparator<T> orderby)
{
return Persist.uglySQL(this.entities, where, orderby, null, null);
}
@Override
public Collection<T> getAll(Predictate<T> where, Comparator<T> orderby, Integer limit)
{
return Persist.uglySQL(this.entities, where, orderby, limit, null);
}
@Override
public Collection<T> getAll(Predictate<T> where, Comparator<T> orderby, Integer limit, Integer offset)
{
return Persist.uglySQL(this.entities, where, orderby, limit, offset);
}
@Override
public Collection<String> getIds()
{
return this.ids;
}
@Override
public Map<String, T> getMap()
{
return this.id2entity;
}
@Override
public T getBestMatch(Object oid)
{
String start = this.idFix(oid);
String id = Persist.getBestCIStart(this.ids, start);
return this.get(id);
}
}

Some files were not shown because too many files have changed in this diff Show More