创建工程
This commit is contained in:
@@ -0,0 +1,121 @@
|
||||
/*
|
||||
* 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.google.gson;
|
||||
|
||||
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;
|
||||
|
||||
/**
|
||||
* This type adapter supports three subclasses of date: Date, Timestamp, and
|
||||
* java.sql.Date.
|
||||
*
|
||||
* @author Inderjeet Singh
|
||||
* @author Joel Leitch
|
||||
*/
|
||||
final class DefaultDateTypeAdapter implements JsonSerializer<Date>, JsonDeserializer<Date> {
|
||||
|
||||
// TODO: migrate to streaming adapter
|
||||
|
||||
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
|
||||
@Override
|
||||
public JsonElement serialize(Date src, Type typeOfSrc, JsonSerializationContext context) {
|
||||
synchronized (localFormat) {
|
||||
String dateFormatAsString = enUsFormat.format(src);
|
||||
return new JsonPrimitive(dateFormatAsString);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
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();
|
||||
}
|
||||
}
|
||||
@@ -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.google.gson;
|
||||
|
||||
/**
|
||||
* 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<?> excludedThisClass;
|
||||
*
|
||||
* public SpecificClassExclusionStrategy(Class<?> excludedThisClass) {
|
||||
* this.excludedThisClass = excludedThisClass;
|
||||
* }
|
||||
*
|
||||
* public boolean shouldSkipClass(Class<?> 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 @interface FooAnnotation {
|
||||
* // some implementation here
|
||||
* }
|
||||
*
|
||||
* // Excludes any field (or class) that is tagged with an "@FooAnnotation"
|
||||
* private static class FooAnnotationExclusionStrategy implements ExclusionStrategy {
|
||||
* public boolean shouldSkipClass(Class<?> 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);
|
||||
}
|
||||
@@ -0,0 +1,157 @@
|
||||
/*
|
||||
* 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.google.gson;
|
||||
|
||||
import com.google.gson.internal.$Gson$Preconditions;
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
|
||||
/**
|
||||
* 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 final Field field;
|
||||
|
||||
/**
|
||||
* Constructs a Field Attributes object from the {@code f}.
|
||||
*
|
||||
* @param f the field to pull attributes from
|
||||
*/
|
||||
public FieldAttributes(Field f) {
|
||||
$Gson$Preconditions.checkNotNull(f);
|
||||
this.field = f;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the declaring class that contains this field
|
||||
*/
|
||||
public Class<?> getDeclaringClass() {
|
||||
return field.getDeclaringClass();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the name of the field
|
||||
*/
|
||||
public String getName() {
|
||||
return field.getName();
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>For example, assume the following class definition:
|
||||
* <pre class="code">
|
||||
* public class Foo {
|
||||
* private String bar;
|
||||
* private List<String> red;
|
||||
* }
|
||||
*
|
||||
* Type listParmeterizedType = new TypeToken<List<String>>() {}.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() {
|
||||
return field.getGenericType();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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<String> 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 field.getType();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 field.getAnnotation(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() {
|
||||
return Arrays.asList(field.getAnnotations());
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 (field.getModifiers() & 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 field.isSynthetic();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,173 @@
|
||||
/*
|
||||
* 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.google.gson;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
|
||||
/**
|
||||
* An enumeration that defines a few standard naming conventions for JSON field names.
|
||||
* This enumeration should be used in conjunction with {@link com.google.gson.GsonBuilder}
|
||||
* to configure a {@link com.google.gson.Gson} instance to properly translate Java field
|
||||
* names into the desired JSON field names.
|
||||
*
|
||||
* @author Inderjeet Singh
|
||||
* @author Joel Leitch
|
||||
*/
|
||||
public enum FieldNamingPolicy implements FieldNamingStrategy {
|
||||
|
||||
/**
|
||||
* Using this naming policy with Gson will ensure that the field name is
|
||||
* unchanged.
|
||||
*/
|
||||
IDENTITY() {
|
||||
@Override
|
||||
public String translateName(Field f) {
|
||||
return f.getName();
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 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() {
|
||||
@Override
|
||||
public String translateName(Field f) {
|
||||
return upperCaseFirstLetter(f.getName());
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 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() {
|
||||
@Override
|
||||
public String translateName(Field f) {
|
||||
return upperCaseFirstLetter(separateCamelCase(f.getName(), " "));
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 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() {
|
||||
@Override
|
||||
public String translateName(Field f) {
|
||||
return separateCamelCase(f.getName(), "_").toLowerCase();
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 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() {
|
||||
@Override
|
||||
public String translateName(Field f) {
|
||||
return separateCamelCase(f.getName(), "-").toLowerCase();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Converts the field name that uses camel-case define word separation into
|
||||
* separate words that are separated by the provided {@code separatorString}.
|
||||
*/
|
||||
private static String separateCamelCase(String name, String separator) {
|
||||
StringBuilder translation = new StringBuilder();
|
||||
for (int i = 0; i < name.length(); i++) {
|
||||
char character = name.charAt(i);
|
||||
if (Character.isUpperCase(character) && translation.length() != 0) {
|
||||
translation.append(separator);
|
||||
}
|
||||
translation.append(character);
|
||||
}
|
||||
return translation.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensures the JSON field names begins with an upper case letter.
|
||||
*/
|
||||
private static String upperCaseFirstLetter(String name) {
|
||||
StringBuilder fieldNameBuilder = new StringBuilder();
|
||||
int index = 0;
|
||||
char firstCharacter = name.charAt(index);
|
||||
|
||||
while (index < name.length() - 1) {
|
||||
if (Character.isLetter(firstCharacter)) {
|
||||
break;
|
||||
}
|
||||
|
||||
fieldNameBuilder.append(firstCharacter);
|
||||
firstCharacter = name.charAt(++index);
|
||||
}
|
||||
|
||||
if (index == name.length()) {
|
||||
return fieldNameBuilder.toString();
|
||||
}
|
||||
|
||||
if (!Character.isUpperCase(firstCharacter)) {
|
||||
String modifiedTarget = modifyString(Character.toUpperCase(firstCharacter), name, ++index);
|
||||
return fieldNameBuilder.append(modifiedTarget).toString();
|
||||
} else {
|
||||
return name;
|
||||
}
|
||||
}
|
||||
|
||||
private static String modifyString(char firstCharacter, String srcString, int indexOfSubstring) {
|
||||
return (indexOfSubstring < srcString.length())
|
||||
? firstCharacter + srcString.substring(indexOfSubstring)
|
||||
: String.valueOf(firstCharacter);
|
||||
}
|
||||
}
|
||||
@@ -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.google.gson;
|
||||
|
||||
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);
|
||||
}
|
||||
947
collect-library/src/main/java/com/google/gson/Gson.java
Normal file
947
collect-library/src/main/java/com/google/gson/Gson.java
Normal file
@@ -0,0 +1,947 @@
|
||||
/*
|
||||
* 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.google.gson;
|
||||
|
||||
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.Type;
|
||||
import java.math.BigDecimal;
|
||||
import java.math.BigInteger;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import com.google.gson.internal.ConstructorConstructor;
|
||||
import com.google.gson.internal.Excluder;
|
||||
import com.google.gson.internal.Primitives;
|
||||
import com.google.gson.internal.Streams;
|
||||
import com.google.gson.internal.bind.ArrayTypeAdapter;
|
||||
import com.google.gson.internal.bind.CollectionTypeAdapterFactory;
|
||||
import com.google.gson.internal.bind.DateTypeAdapter;
|
||||
import com.google.gson.internal.bind.JsonAdapterAnnotationTypeAdapterFactory;
|
||||
import com.google.gson.internal.bind.JsonTreeReader;
|
||||
import com.google.gson.internal.bind.JsonTreeWriter;
|
||||
import com.google.gson.internal.bind.MapTypeAdapterFactory;
|
||||
import com.google.gson.internal.bind.ObjectTypeAdapter;
|
||||
import com.google.gson.internal.bind.ReflectiveTypeAdapterFactory;
|
||||
import com.google.gson.internal.bind.SqlDateTypeAdapter;
|
||||
import com.google.gson.internal.bind.TimeTypeAdapter;
|
||||
import com.google.gson.internal.bind.TypeAdapters;
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
import com.google.gson.stream.JsonReader;
|
||||
import com.google.gson.stream.JsonToken;
|
||||
import com.google.gson.stream.JsonWriter;
|
||||
import com.google.gson.stream.MalformedJsonException;
|
||||
|
||||
/**
|
||||
* 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<List<String>>() {}.getType();
|
||||
* List<String> target = new LinkedList<String>();
|
||||
* target.add("blah");
|
||||
*
|
||||
* Gson gson = new Gson();
|
||||
* String json = gson.toJson(target, listType);
|
||||
* List<String> 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.google.gson.reflect.TypeToken
|
||||
*
|
||||
* @author Inderjeet Singh
|
||||
* @author Joel Leitch
|
||||
* @author Jesse Wilson
|
||||
*/
|
||||
public final class Gson {
|
||||
static final boolean DEFAULT_JSON_NON_EXECUTABLE = false;
|
||||
|
||||
private static final String JSON_NON_EXECUTABLE_PREFIX = ")]}'\n";
|
||||
|
||||
/**
|
||||
* 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<?>>>();
|
||||
|
||||
private final Map<TypeToken<?>, TypeAdapter<?>> typeTokenCache
|
||||
= Collections.synchronizedMap(new HashMap<TypeToken<?>, TypeAdapter<?>>());
|
||||
|
||||
/** Indicates whether Gson is in the phase of constructor invocation. It is used to determine
|
||||
* whether to add a constructor in preconfiguredGeneratedTypeAdapter set or not. */
|
||||
private boolean inConstructorPhase = true;
|
||||
/** List of type adapters that are generated by Gson during its constructor */
|
||||
private Set<TypeAdapter<?>> preconfiguredGeneratedTypeAdapters = new HashSet<TypeAdapter<?>>();
|
||||
/** List of type adapters that are generated by Gson during toJson/fromJson. */
|
||||
private final ThreadLocal<Set<TypeAdapter<?>>> runtimeGeneratedTypeAdapters =
|
||||
new ThreadLocal<Set<TypeAdapter<?>>>();
|
||||
|
||||
private final List<TypeAdapterFactory> factories;
|
||||
private final ConstructorConstructor constructorConstructor;
|
||||
|
||||
private final boolean serializeNulls;
|
||||
private final boolean htmlSafe;
|
||||
private final boolean generateNonExecutableJson;
|
||||
private final boolean prettyPrinting;
|
||||
|
||||
final JsonDeserializationContext deserializationContext = new JsonDeserializationContext() {
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T> T deserialize(JsonElement json, Type typeOfT) throws JsonParseException {
|
||||
return (T) fromJson(json, typeOfT);
|
||||
}
|
||||
};
|
||||
|
||||
final JsonSerializationContext serializationContext = new JsonSerializationContext() {
|
||||
@Override
|
||||
public JsonElement serialize(Object src) {
|
||||
return toJsonTree(src);
|
||||
}
|
||||
@Override
|
||||
public JsonElement serialize(Object src, Type typeOfSrc) {
|
||||
return toJsonTree(src, typeOfSrc);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 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.google.gson.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.google.gson.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>"versionNumber"</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>
|
||||
*/
|
||||
public Gson() {
|
||||
this(Excluder.DEFAULT, FieldNamingPolicy.IDENTITY,
|
||||
Collections.<Type, InstanceCreator<?>>emptyMap(), false, false, DEFAULT_JSON_NON_EXECUTABLE,
|
||||
true, false, false, LongSerializationPolicy.DEFAULT,
|
||||
Collections.<TypeAdapterFactory>emptyList());
|
||||
}
|
||||
|
||||
Gson(final Excluder excluder, final FieldNamingStrategy fieldNamingPolicy,
|
||||
final Map<Type, InstanceCreator<?>> instanceCreators, boolean serializeNulls,
|
||||
boolean complexMapKeySerialization, boolean generateNonExecutableGson, boolean htmlSafe,
|
||||
boolean prettyPrinting, boolean serializeSpecialFloatingPointValues,
|
||||
LongSerializationPolicy longSerializationPolicy,
|
||||
List<TypeAdapterFactory> typeAdapterFactories) {
|
||||
this.constructorConstructor = new ConstructorConstructor(instanceCreators);
|
||||
this.serializeNulls = serializeNulls;
|
||||
this.generateNonExecutableJson = generateNonExecutableGson;
|
||||
this.htmlSafe = htmlSafe;
|
||||
this.prettyPrinting = prettyPrinting;
|
||||
|
||||
List<TypeAdapterFactory> factories = new ArrayList<TypeAdapterFactory>();
|
||||
|
||||
// built-in type adapters that cannot be overridden
|
||||
factories.add(TypeAdapters.JSON_ELEMENT_FACTORY);
|
||||
factories.add(ObjectTypeAdapter.FACTORY);
|
||||
|
||||
// the excluder must precede all adapters that handle user-defined types
|
||||
factories.add(excluder);
|
||||
|
||||
// user's type adapters
|
||||
factories.addAll(typeAdapterFactories);
|
||||
|
||||
// type adapters for basic platform types
|
||||
factories.add(TypeAdapters.STRING_FACTORY);
|
||||
factories.add(TypeAdapters.INTEGER_FACTORY);
|
||||
factories.add(TypeAdapters.BOOLEAN_FACTORY);
|
||||
factories.add(TypeAdapters.BYTE_FACTORY);
|
||||
factories.add(TypeAdapters.SHORT_FACTORY);
|
||||
factories.add(TypeAdapters.newFactory(long.class, Long.class,
|
||||
longAdapter(longSerializationPolicy)));
|
||||
factories.add(TypeAdapters.newFactory(double.class, Double.class,
|
||||
doubleAdapter(serializeSpecialFloatingPointValues)));
|
||||
factories.add(TypeAdapters.newFactory(float.class, Float.class,
|
||||
floatAdapter(serializeSpecialFloatingPointValues)));
|
||||
factories.add(TypeAdapters.NUMBER_FACTORY);
|
||||
factories.add(TypeAdapters.CHARACTER_FACTORY);
|
||||
factories.add(TypeAdapters.STRING_BUILDER_FACTORY);
|
||||
factories.add(TypeAdapters.STRING_BUFFER_FACTORY);
|
||||
factories.add(TypeAdapters.newFactory(BigDecimal.class, TypeAdapters.BIG_DECIMAL));
|
||||
factories.add(TypeAdapters.newFactory(BigInteger.class, TypeAdapters.BIG_INTEGER));
|
||||
factories.add(TypeAdapters.URL_FACTORY);
|
||||
factories.add(TypeAdapters.URI_FACTORY);
|
||||
factories.add(TypeAdapters.UUID_FACTORY);
|
||||
factories.add(TypeAdapters.LOCALE_FACTORY);
|
||||
factories.add(TypeAdapters.INET_ADDRESS_FACTORY);
|
||||
factories.add(TypeAdapters.BIT_SET_FACTORY);
|
||||
factories.add(DateTypeAdapter.FACTORY);
|
||||
factories.add(TypeAdapters.CALENDAR_FACTORY);
|
||||
factories.add(TimeTypeAdapter.FACTORY);
|
||||
factories.add(SqlDateTypeAdapter.FACTORY);
|
||||
factories.add(TypeAdapters.TIMESTAMP_FACTORY);
|
||||
factories.add(ArrayTypeAdapter.FACTORY);
|
||||
factories.add(TypeAdapters.ENUM_FACTORY);
|
||||
factories.add(TypeAdapters.CLASS_FACTORY);
|
||||
|
||||
// type adapters for composite and user-defined types
|
||||
factories.add(new CollectionTypeAdapterFactory(constructorConstructor));
|
||||
factories.add(new MapTypeAdapterFactory(constructorConstructor, complexMapKeySerialization));
|
||||
factories.add(new JsonAdapterAnnotationTypeAdapterFactory(constructorConstructor));
|
||||
factories.add(new ReflectiveTypeAdapterFactory(
|
||||
constructorConstructor, fieldNamingPolicy, excluder));
|
||||
|
||||
this.factories = Collections.unmodifiableList(factories);
|
||||
this.preconfiguredGeneratedTypeAdapters = Collections.unmodifiableSet(preconfiguredGeneratedTypeAdapters);
|
||||
inConstructorPhase = false;
|
||||
}
|
||||
|
||||
private TypeAdapter<Number> doubleAdapter(boolean serializeSpecialFloatingPointValues) {
|
||||
if (serializeSpecialFloatingPointValues) {
|
||||
return TypeAdapters.DOUBLE;
|
||||
}
|
||||
return new TypeAdapter<Number>() {
|
||||
@Override public Double read(JsonReader in) throws IOException {
|
||||
if (in.peek() == JsonToken.NULL) {
|
||||
in.nextNull();
|
||||
return null;
|
||||
}
|
||||
return in.nextDouble();
|
||||
}
|
||||
@Override public void write(JsonWriter out, Number value) throws IOException {
|
||||
if (value == null) {
|
||||
out.nullValue();
|
||||
return;
|
||||
}
|
||||
double doubleValue = value.doubleValue();
|
||||
checkValidFloatingPoint(doubleValue);
|
||||
out.value(value);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private TypeAdapter<Number> floatAdapter(boolean serializeSpecialFloatingPointValues) {
|
||||
if (serializeSpecialFloatingPointValues) {
|
||||
return TypeAdapters.FLOAT;
|
||||
}
|
||||
return new TypeAdapter<Number>() {
|
||||
@Override public Float read(JsonReader in) throws IOException {
|
||||
if (in.peek() == JsonToken.NULL) {
|
||||
in.nextNull();
|
||||
return null;
|
||||
}
|
||||
return (float) in.nextDouble();
|
||||
}
|
||||
@Override public void write(JsonWriter out, Number value) throws IOException {
|
||||
if (value == null) {
|
||||
out.nullValue();
|
||||
return;
|
||||
}
|
||||
float floatValue = value.floatValue();
|
||||
checkValidFloatingPoint(floatValue);
|
||||
out.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.serializeSpecialFloatingPointValues() method.");
|
||||
}
|
||||
}
|
||||
|
||||
private TypeAdapter<Number> longAdapter(LongSerializationPolicy longSerializationPolicy) {
|
||||
if (longSerializationPolicy == LongSerializationPolicy.DEFAULT) {
|
||||
return TypeAdapters.LONG;
|
||||
}
|
||||
return new TypeAdapter<Number>() {
|
||||
@Override public Number read(JsonReader in) throws IOException {
|
||||
if (in.peek() == JsonToken.NULL) {
|
||||
in.nextNull();
|
||||
return null;
|
||||
}
|
||||
return in.nextLong();
|
||||
}
|
||||
@Override public void write(JsonWriter out, Number value) throws IOException {
|
||||
if (value == null) {
|
||||
out.nullValue();
|
||||
return;
|
||||
}
|
||||
out.value(value.toString());
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the type adapter for {@code} type.
|
||||
*
|
||||
* @throws IllegalArgumentException if this GSON cannot serialize and
|
||||
* deserialize {@code type}.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T> TypeAdapter<T> getAdapter(TypeToken<T> type) {
|
||||
TypeAdapter<?> cached = typeTokenCache.get(type);
|
||||
if (cached != null) {
|
||||
return (TypeAdapter<T>) cached;
|
||||
}
|
||||
|
||||
Map<TypeToken<?>, FutureTypeAdapter<?>> threadCalls = calls.get();
|
||||
boolean requiresThreadLocalCleanup = false;
|
||||
if (threadCalls == null) {
|
||||
threadCalls = new HashMap<TypeToken<?>, FutureTypeAdapter<?>>();
|
||||
calls.set(threadCalls);
|
||||
requiresThreadLocalCleanup = true;
|
||||
}
|
||||
|
||||
// the key and value type parameters always agree
|
||||
FutureTypeAdapter<T> ongoingCall = (FutureTypeAdapter<T>) threadCalls.get(type);
|
||||
if (ongoingCall != null) {
|
||||
return ongoingCall;
|
||||
}
|
||||
|
||||
try {
|
||||
FutureTypeAdapter<T> call = new FutureTypeAdapter<T>();
|
||||
threadCalls.put(type, call);
|
||||
|
||||
for (TypeAdapterFactory factory : factories) {
|
||||
TypeAdapter<T> candidate = factory.create(this, type);
|
||||
if (candidate != null) {
|
||||
call.setDelegate(candidate);
|
||||
typeTokenCache.put(type, candidate);
|
||||
return candidate;
|
||||
}
|
||||
}
|
||||
throw new IllegalArgumentException("GSON cannot handle " + type);
|
||||
} finally {
|
||||
threadCalls.remove(type);
|
||||
|
||||
if (requiresThreadLocalCleanup) {
|
||||
calls.remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is used to get an alternate type adapter for the specified type. This is used
|
||||
* to access a type adapter that is overridden by a {@link TypeAdapterFactory} that you
|
||||
* may have registered. This features is typically used when you want to register a type
|
||||
* adapter that does a little bit of work but then delegates further processing to the Gson
|
||||
* default type adapter. Here is an example:
|
||||
* <p>Let's say we want to write a type adapter that counts the number of objects being read
|
||||
* from or written to JSON. We can achieve this by writing a type adapter factory that uses
|
||||
* the <code>getDelegateAdapter</code> method:
|
||||
* <pre> {@code
|
||||
* class StatsTypeAdapterFactory implements TypeAdapterFactory {
|
||||
* public int numReads = 0;
|
||||
* public int numWrites = 0;
|
||||
* public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type) {
|
||||
* final TypeAdapter<T> delegate = gson.getDelegateAdapter(this, type);
|
||||
* return new TypeAdapter<T>() {
|
||||
* public void write(JsonWriter out, T value) throws IOException {
|
||||
* ++numWrites;
|
||||
* delegate.write(out, value);
|
||||
* }
|
||||
* public T read(JsonReader in) throws IOException {
|
||||
* ++numReads;
|
||||
* return delegate.read(in);
|
||||
* }
|
||||
* };
|
||||
* }
|
||||
* }
|
||||
* } </pre>
|
||||
* This factory can now be used like this:
|
||||
* <pre> {@code
|
||||
* StatsTypeAdapterFactory stats = new StatsTypeAdapterFactory();
|
||||
* Gson gson = new GsonBuilder().registerTypeAdapterFactory(stats).create();
|
||||
* // Call gson.toJson() and fromJson methods on objects
|
||||
* System.out.println("Num JSON reads" + stats.numReads);
|
||||
* System.out.println("Num JSON writes" + stats.numWrites);
|
||||
* }</pre>
|
||||
* Note that since you can not override type adapter factories for String and Java primitive
|
||||
* types, our stats factory will not count the number of String or primitives that will be
|
||||
* read or written.
|
||||
* @param skipPast The type adapter factory that needs to be skipped while searching for
|
||||
* a matching type adapter. In most cases, you should just pass <i>this</i> (the type adapter
|
||||
* factory from where {@link #getDelegateAdapter} method is being invoked).
|
||||
* @param type Type for which the delegate adapter is being searched for.
|
||||
*
|
||||
* @since 2.2
|
||||
*/
|
||||
public <T> TypeAdapter<T> getDelegateAdapter(TypeAdapterFactory skipPast, TypeToken<T> type) {
|
||||
boolean skipPastFound = false;
|
||||
|
||||
for (TypeAdapterFactory 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("GSON cannot serialize " + type);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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));
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.google.gson.reflect.TypeToken} class. For example,
|
||||
* to get the type for {@code Collection<Foo>}, you should use:
|
||||
* <pre>
|
||||
* Type typeOfSrc = new TypeToken<Collection<Foo>>(){}.getType();
|
||||
* </pre>
|
||||
* @return Json representation of {@code src}
|
||||
* @since 1.4
|
||||
*/
|
||||
public JsonElement toJsonTree(Object src, Type typeOfSrc) {
|
||||
JsonTreeWriter writer = new JsonTreeWriter();
|
||||
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.google.gson.reflect.TypeToken} class. For example,
|
||||
* to get the type for {@code Collection<Foo>}, you should use:
|
||||
* <pre>
|
||||
* Type typeOfSrc = new TypeToken<Collection<Foo>>(){}.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.google.gson.reflect.TypeToken} class. For example,
|
||||
* to get the type for {@code Collection<Foo>}, you should use:
|
||||
* <pre>
|
||||
* Type typeOfSrc = new TypeToken<Collection<Foo>>(){}.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 = getAdapter(TypeToken.get(typeOfSrc));
|
||||
boolean oldLenient = writer.isLenient();
|
||||
writer.setLenient(true);
|
||||
boolean oldHtmlSafe = writer.isHtmlSafe();
|
||||
writer.setHtmlSafe(htmlSafe);
|
||||
boolean oldSerializeNulls = writer.getSerializeNulls();
|
||||
// Log.i("json", oldSerializeNulls+"==============");
|
||||
writer.setSerializeNulls(true);//齐济修改,生成key值,value为空也生成
|
||||
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. Returns {@code null} if {@code json} is {@code null}.
|
||||
* @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.google.gson.reflect.TypeToken} class. For example, to get the type for
|
||||
* {@code Collection<Foo>}, you should use:
|
||||
* <pre>
|
||||
* Type typeOfT = new TypeToken<Collection<Foo>>(){}.getType();
|
||||
* </pre>
|
||||
* @return an object of type T from the string. Returns {@code null} if {@code json} is {@code null}.
|
||||
* @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. Returns {@code null} if {@code json} is at EOF.
|
||||
* @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.google.gson.reflect.TypeToken} class. For example, to get the type for
|
||||
* {@code Collection<Foo>}, you should use:
|
||||
* <pre>
|
||||
* Type typeOfT = new TypeToken<Collection<Foo>>(){}.getType();
|
||||
* </pre>
|
||||
* @return an object of type T from the json. Returns {@code null} if {@code json} is at EOF.
|
||||
* @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
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
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}. Returns {@code null}, if the {@code reader} is at EOF.
|
||||
* 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;
|
||||
TypeToken<T> typeToken = (TypeToken<T>) TypeToken.get(typeOfT);
|
||||
TypeAdapter<T> typeAdapter = getAdapter(typeToken);
|
||||
T object = typeAdapter.read(reader);
|
||||
return object;
|
||||
} 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. Returns {@code null} if {@code json} is {@code null}.
|
||||
* @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.google.gson.reflect.TypeToken} class. For example, to get the type for
|
||||
* {@code Collection<Foo>}, you should use:
|
||||
* <pre>
|
||||
* Type typeOfT = new TypeToken<Collection<Foo>>(){}.getType();
|
||||
* </pre>
|
||||
* @return an object of type T from the json. Returns {@code null} if {@code json} is {@code null}.
|
||||
* @throws JsonSyntaxException if json is not a valid representation for an object of type typeOfT
|
||||
* @since 1.3
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T> T fromJson(JsonElement json, Type typeOfT) throws JsonSyntaxException {
|
||||
if (json == null) {
|
||||
return null;
|
||||
}
|
||||
return (T) fromJson(new JsonTreeReader(json), typeOfT);
|
||||
}
|
||||
|
||||
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 in) throws IOException {
|
||||
if (delegate == null) {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
return delegate.read(in);
|
||||
}
|
||||
|
||||
@Override public void write(JsonWriter out, T value) throws IOException {
|
||||
if (delegate == null) {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
delegate.write(out, value);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return new StringBuilder("{serializeNulls:")
|
||||
.append(serializeNulls)
|
||||
.append("factories:").append(factories)
|
||||
.append(",instanceCreators:").append(constructorConstructor)
|
||||
.append("}")
|
||||
.toString();
|
||||
}
|
||||
|
||||
public static final class $$Internal {
|
||||
public static void addGeneratedTypeAdapter(Gson gson, TypeAdapter<?> typeAdapter) {
|
||||
if (gson.inConstructorPhase) {
|
||||
gson.preconfiguredGeneratedTypeAdapters.add(typeAdapter);
|
||||
} else {
|
||||
Set<TypeAdapter<?>> adapters = getRuntimeGeneratedTypeAdapters(gson);
|
||||
adapters.add(typeAdapter);
|
||||
}
|
||||
}
|
||||
public static boolean isGeneratedTypeAdapter(Gson gson, TypeAdapter<?> typeAdapter) {
|
||||
boolean generated = gson.preconfiguredGeneratedTypeAdapters.contains(typeAdapter);
|
||||
if (!generated) generated = getRuntimeGeneratedTypeAdapters(gson).contains(typeAdapter);
|
||||
return generated;
|
||||
}
|
||||
private static Set<TypeAdapter<?>> getRuntimeGeneratedTypeAdapters(Gson gson) {
|
||||
Set<TypeAdapter<?>> adapters = gson.runtimeGeneratedTypeAdapters.get();
|
||||
if (adapters == null) adapters = new HashSet<TypeAdapter<?>>();
|
||||
gson.runtimeGeneratedTypeAdapters.set(adapters);
|
||||
return adapters;
|
||||
}
|
||||
}
|
||||
}
|
||||
566
collect-library/src/main/java/com/google/gson/GsonBuilder.java
Normal file
566
collect-library/src/main/java/com/google/gson/GsonBuilder.java
Normal file
@@ -0,0 +1,566 @@
|
||||
/*
|
||||
* 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.google.gson;
|
||||
|
||||
import java.lang.reflect.Type;
|
||||
import java.sql.Timestamp;
|
||||
import java.text.DateFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import com.google.gson.internal.$Gson$Preconditions;
|
||||
import com.google.gson.internal.Excluder;
|
||||
import com.google.gson.internal.bind.TypeAdapters;
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
|
||||
/**
|
||||
* <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
|
||||
* @author Jesse Wilson
|
||||
*/
|
||||
public final class GsonBuilder {
|
||||
private Excluder excluder = Excluder.DEFAULT;
|
||||
private LongSerializationPolicy longSerializationPolicy = LongSerializationPolicy.DEFAULT;
|
||||
private FieldNamingStrategy fieldNamingPolicy = FieldNamingPolicy.IDENTITY;
|
||||
private final Map<Type, InstanceCreator<?>> instanceCreators
|
||||
= new HashMap<Type, InstanceCreator<?>>();
|
||||
private final List<TypeAdapterFactory> factories = new ArrayList<TypeAdapterFactory>();
|
||||
/** tree-style hierarchy factories. These come after factories for backwards compatibility. */
|
||||
private final List<TypeAdapterFactory> hierarchyFactories = new ArrayList<TypeAdapterFactory>();
|
||||
private boolean serializeNulls;
|
||||
private String datePattern;
|
||||
private int dateStyle = DateFormat.DEFAULT;
|
||||
private int timeStyle = DateFormat.DEFAULT;
|
||||
private boolean complexMapKeySerialization;
|
||||
private boolean serializeSpecialFloatingPointValues;
|
||||
private boolean escapeHtmlChars = true;
|
||||
private boolean prettyPrinting;
|
||||
private boolean generateNonExecutableJson;
|
||||
|
||||
/**
|
||||
* 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() {
|
||||
}
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
excluder = excluder.withVersion(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) {
|
||||
excluder = excluder.withModifiers(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.google.gson.annotations.Expose} annotation.
|
||||
*
|
||||
* @return a reference to this {@code GsonBuilder} object to fulfill the "Builder" pattern
|
||||
*/
|
||||
public GsonBuilder excludeFieldsWithoutExposeAnnotation() {
|
||||
excluder = excluder.excludeFieldsWithoutExposeAnnotation();
|
||||
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() {
|
||||
excluder = excluder.disableInnerClassSerialization();
|
||||
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) {
|
||||
this.fieldNamingPolicy = namingConvention;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
this.fieldNamingPolicy = 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) {
|
||||
for (ExclusionStrategy strategy : strategies) {
|
||||
excluder = excluder.withExclusionStrategy(strategy, true, true);
|
||||
}
|
||||
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) {
|
||||
excluder = excluder.withExclusionStrategy(strategy, true, false);
|
||||
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) {
|
||||
excluder = excluder.withExclusionStrategy(strategy, false, true);
|
||||
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 < > 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 TypeAdapter}, {@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 a type adapter was
|
||||
* previously registered for the specified {@code type}, it is overwritten.
|
||||
*
|
||||
* <p>This registers the type specified and no other types: you must manually register related
|
||||
* types! For example, applications registering {@code boolean.class} should also register {@code
|
||||
* Boolean.class}.
|
||||
*
|
||||
* @param type the type definition for the type adapter being registered
|
||||
* @param typeAdapter This object must implement at least one of the {@link TypeAdapter},
|
||||
* {@link InstanceCreator}, {@link JsonSerializer}, and a {@link JsonDeserializer} interfaces.
|
||||
* @return a reference to this {@code GsonBuilder} object to fulfill the "Builder" pattern
|
||||
*/
|
||||
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||
public GsonBuilder registerTypeAdapter(Type type, Object typeAdapter) {
|
||||
$Gson$Preconditions.checkArgument(typeAdapter instanceof JsonSerializer<?>
|
||||
|| typeAdapter instanceof JsonDeserializer<?>
|
||||
|| typeAdapter instanceof InstanceCreator<?>
|
||||
|| typeAdapter instanceof TypeAdapter<?>);
|
||||
if (typeAdapter instanceof InstanceCreator<?>) {
|
||||
instanceCreators.put(type, (InstanceCreator) typeAdapter);
|
||||
}
|
||||
if (typeAdapter instanceof JsonSerializer<?> || typeAdapter instanceof JsonDeserializer<?>) {
|
||||
TypeToken<?> typeToken = TypeToken.get(type);
|
||||
factories.add(TreeTypeAdapter.newFactoryWithMatchRawType(typeToken, typeAdapter));
|
||||
}
|
||||
if (typeAdapter instanceof TypeAdapter<?>) {
|
||||
factories.add(TypeAdapters.newFactory(TypeToken.get(type), (TypeAdapter)typeAdapter));
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a factory for type adapters. Registering a factory is useful when the type
|
||||
* adapter needs to be configured based on the type of the field being processed. Gson
|
||||
* is designed to handle a large number of factories, so you should consider registering
|
||||
* them to be at par with registering an individual type adapter.
|
||||
*
|
||||
* @since 2.1
|
||||
*/
|
||||
public GsonBuilder registerTypeAdapterFactory(TypeAdapterFactory factory) {
|
||||
factories.add(factory);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures Gson for custom serialization or deserialization for an inheritance type hierarchy.
|
||||
* This method combines the registration of a {@link TypeAdapter}, {@link JsonSerializer} and
|
||||
* a {@link JsonDeserializer}. If a type adapter was previously registered for the specified
|
||||
* type hierarchy, it is overridden. If a type adapter 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 {@link TypeAdapter},
|
||||
* {@link JsonSerializer} or {@link JsonDeserializer} interfaces.
|
||||
* @return a reference to this {@code GsonBuilder} object to fulfill the "Builder" pattern
|
||||
* @since 1.7
|
||||
*/
|
||||
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||
public GsonBuilder registerTypeHierarchyAdapter(Class<?> baseType, Object typeAdapter) {
|
||||
$Gson$Preconditions.checkArgument(typeAdapter instanceof JsonSerializer<?>
|
||||
|| typeAdapter instanceof JsonDeserializer<?>
|
||||
|| typeAdapter instanceof TypeAdapter<?>);
|
||||
if (typeAdapter instanceof JsonDeserializer || typeAdapter instanceof JsonSerializer) {
|
||||
hierarchyFactories.add(0,
|
||||
TreeTypeAdapter.newTypeHierarchyFactory(baseType, typeAdapter));
|
||||
}
|
||||
if (typeAdapter instanceof TypeAdapter<?>) {
|
||||
factories.add(TypeAdapters.newTypeHierarchyFactory(baseType, (TypeAdapter)typeAdapter));
|
||||
}
|
||||
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<TypeAdapterFactory> factories = new ArrayList<TypeAdapterFactory>();
|
||||
factories.addAll(this.factories);
|
||||
Collections.reverse(factories);
|
||||
factories.addAll(this.hierarchyFactories);
|
||||
addTypeAdaptersForDate(datePattern, dateStyle, timeStyle, factories);
|
||||
|
||||
return new Gson(excluder, fieldNamingPolicy, instanceCreators,
|
||||
serializeNulls, complexMapKeySerialization,
|
||||
generateNonExecutableJson, escapeHtmlChars, prettyPrinting,
|
||||
serializeSpecialFloatingPointValues, longSerializationPolicy, factories);
|
||||
}
|
||||
|
||||
private void addTypeAdaptersForDate(String datePattern, int dateStyle, int timeStyle,
|
||||
List<TypeAdapterFactory> factories) {
|
||||
DefaultDateTypeAdapter dateTypeAdapter;
|
||||
if (datePattern != null && !"".equals(datePattern.trim())) {
|
||||
dateTypeAdapter = new DefaultDateTypeAdapter(datePattern);
|
||||
} else if (dateStyle != DateFormat.DEFAULT && timeStyle != DateFormat.DEFAULT) {
|
||||
dateTypeAdapter = new DefaultDateTypeAdapter(dateStyle, timeStyle);
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
||||
factories.add(TreeTypeAdapter.newFactory(TypeToken.get(Date.class), dateTypeAdapter));
|
||||
factories.add(TreeTypeAdapter.newFactory(TypeToken.get(Timestamp.class), dateTypeAdapter));
|
||||
factories.add(TreeTypeAdapter.newFactory(TypeToken.get(java.sql.Date.class), dateTypeAdapter));
|
||||
}
|
||||
}
|
||||
@@ -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.google.gson;
|
||||
|
||||
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<T> {
|
||||
* private final Class<T> clazz;
|
||||
* private final long value;
|
||||
* public Id(Class<T> 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<Id> {
|
||||
* 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);
|
||||
}
|
||||
292
collect-library/src/main/java/com/google/gson/JsonArray.java
Normal file
292
collect-library/src/main/java/com/google/gson/JsonArray.java
Normal file
@@ -0,0 +1,292 @@
|
||||
/*
|
||||
* 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.google.gson;
|
||||
|
||||
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>();
|
||||
}
|
||||
|
||||
@Override
|
||||
JsonArray deepCopy() {
|
||||
JsonArray result = new JsonArray();
|
||||
for (JsonElement element : elements) {
|
||||
result.add(element.deepCopy());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
@Override
|
||||
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();
|
||||
}
|
||||
}
|
||||
@@ -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.google.gson;
|
||||
|
||||
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;
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
/*
|
||||
* 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.google.gson;
|
||||
|
||||
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<T> {
|
||||
* private final Class<T> clazz;
|
||||
* private final long value;
|
||||
* public Id(Class<T> 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<Id>() {
|
||||
* 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>
|
||||
*
|
||||
* <p>New applications should prefer {@link TypeAdapter}, whose streaming API
|
||||
* is more efficient than this interface's tree API.
|
||||
*
|
||||
* @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;
|
||||
}
|
||||
330
collect-library/src/main/java/com/google/gson/JsonElement.java
Normal file
330
collect-library/src/main/java/com/google/gson/JsonElement.java
Normal file
@@ -0,0 +1,330 @@
|
||||
/*
|
||||
* 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.google.gson;
|
||||
|
||||
import com.google.gson.internal.Streams;
|
||||
import com.google.gson.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 {
|
||||
/**
|
||||
* Returns a deep copy of this element. Immutable elements like primitives
|
||||
* and nulls are not copied.
|
||||
*/
|
||||
abstract JsonElement deepCopy();
|
||||
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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.google.gson;
|
||||
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
}
|
||||
63
collect-library/src/main/java/com/google/gson/JsonNull.java
Normal file
63
collect-library/src/main/java/com/google/gson/JsonNull.java
Normal file
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
* 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.google.gson;
|
||||
|
||||
/**
|
||||
* 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
|
||||
}
|
||||
|
||||
@Override
|
||||
JsonNull deepCopy() {
|
||||
return INSTANCE;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
}
|
||||
197
collect-library/src/main/java/com/google/gson/JsonObject.java
Normal file
197
collect-library/src/main/java/com/google/gson/JsonObject.java
Normal file
@@ -0,0 +1,197 @@
|
||||
/*
|
||||
* 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.google.gson;
|
||||
|
||||
import com.google.gson.internal.LinkedTreeMap;
|
||||
|
||||
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 {
|
||||
private final LinkedTreeMap<String, JsonElement> members =
|
||||
new LinkedTreeMap<String, JsonElement>();
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
JsonObject deepCopy() {
|
||||
JsonObject result = new JsonObject();
|
||||
for (Map.Entry<String, JsonElement> entry : members.entrySet()) {
|
||||
result.add(entry.getKey(), entry.getValue().deepCopy());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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(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) {
|
||||
return members.get(memberName);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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();
|
||||
}
|
||||
}
|
||||
@@ -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.google.gson;
|
||||
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
/*
|
||||
* 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.google.gson;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.Reader;
|
||||
import java.io.StringReader;
|
||||
|
||||
import com.google.gson.internal.Streams;
|
||||
import com.google.gson.stream.JsonReader;
|
||||
import com.google.gson.stream.JsonToken;
|
||||
import com.google.gson.stream.MalformedJsonException;
|
||||
|
||||
/**
|
||||
* 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);
|
||||
} finally {
|
||||
json.setLenient(lenient);
|
||||
}
|
||||
}
|
||||
}
|
||||
341
collect-library/src/main/java/com/google/gson/JsonPrimitive.java
Normal file
341
collect-library/src/main/java/com/google/gson/JsonPrimitive.java
Normal file
@@ -0,0 +1,341 @@
|
||||
/*
|
||||
* 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.google.gson;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.math.BigInteger;
|
||||
|
||||
import com.google.gson.internal.$Gson$Preconditions;
|
||||
import com.google.gson.internal.LazilyParsedNumber;
|
||||
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
|
||||
@Override
|
||||
JsonPrimitive deepCopy() {
|
||||
return this;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -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.google.gson;
|
||||
|
||||
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);
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
/*
|
||||
* 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.google.gson;
|
||||
|
||||
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.google.gson.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<T> {
|
||||
* private final Class<T> clazz;
|
||||
* private final long value;
|
||||
*
|
||||
* public Id(Class<T> 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<Id>() {
|
||||
* 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>
|
||||
*
|
||||
* <p>New applications should prefer {@link TypeAdapter}, whose streaming API
|
||||
* is more efficient than this interface's tree API.
|
||||
*
|
||||
* @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);
|
||||
}
|
||||
@@ -0,0 +1,125 @@
|
||||
/*
|
||||
* 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.google.gson;
|
||||
|
||||
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.google.gson.internal.Streams;
|
||||
import com.google.gson.stream.JsonReader;
|
||||
import com.google.gson.stream.JsonToken;
|
||||
import com.google.gson.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
|
||||
*/
|
||||
@Override
|
||||
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
|
||||
*/
|
||||
@Override
|
||||
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
|
||||
*/
|
||||
@Override
|
||||
public void remove() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
}
|
||||
@@ -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.google.gson;
|
||||
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
* 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.google.gson;
|
||||
|
||||
/**
|
||||
* 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() {
|
||||
@Override
|
||||
public JsonElement serialize(Long value) {
|
||||
return new JsonPrimitive(value);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 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() {
|
||||
@Override
|
||||
public JsonElement serialize(Long value) {
|
||||
return new JsonPrimitive(String.valueOf(value));
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 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 abstract JsonElement serialize(Long value);
|
||||
}
|
||||
@@ -0,0 +1,141 @@
|
||||
/*
|
||||
* 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.google.gson;
|
||||
|
||||
import com.google.gson.internal.$Gson$Preconditions;
|
||||
import com.google.gson.internal.Streams;
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
import com.google.gson.stream.JsonReader;
|
||||
import com.google.gson.stream.JsonWriter;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* Adapts a Gson 1.x tree-style adapter as a streaming TypeAdapter. Since the
|
||||
* tree adapter may be serialization-only or deserialization-only, this class
|
||||
* has a facility to lookup a delegate type adapter on demand.
|
||||
*/
|
||||
final class TreeTypeAdapter<T> extends TypeAdapter<T> {
|
||||
private final JsonSerializer<T> serializer;
|
||||
private final JsonDeserializer<T> deserializer;
|
||||
private final Gson gson;
|
||||
private final TypeToken<T> typeToken;
|
||||
private final TypeAdapterFactory skipPast;
|
||||
|
||||
/** The delegate is lazily created because it may not be needed, and creating it may fail. */
|
||||
private TypeAdapter<T> delegate;
|
||||
|
||||
private TreeTypeAdapter(JsonSerializer<T> serializer, JsonDeserializer<T> deserializer,
|
||||
Gson gson, TypeToken<T> typeToken, TypeAdapterFactory skipPast) {
|
||||
this.serializer = serializer;
|
||||
this.deserializer = deserializer;
|
||||
this.gson = gson;
|
||||
this.typeToken = typeToken;
|
||||
this.skipPast = skipPast;
|
||||
}
|
||||
|
||||
@Override public T read(JsonReader in) throws IOException {
|
||||
if (deserializer == null) {
|
||||
return delegate().read(in);
|
||||
}
|
||||
JsonElement value = Streams.parse(in);
|
||||
if (value.isJsonNull()) {
|
||||
return null;
|
||||
}
|
||||
return deserializer.deserialize(value, typeToken.getType(), gson.deserializationContext);
|
||||
}
|
||||
|
||||
@Override public void write(JsonWriter out, T value) throws IOException {
|
||||
if (serializer == null) {
|
||||
delegate().write(out, value);
|
||||
return;
|
||||
}
|
||||
if (value == null) {
|
||||
out.nullValue();
|
||||
return;
|
||||
}
|
||||
JsonElement tree = serializer.serialize(value, typeToken.getType(), gson.serializationContext);
|
||||
Streams.write(tree, out);
|
||||
}
|
||||
|
||||
private TypeAdapter<T> delegate() {
|
||||
TypeAdapter<T> d = delegate;
|
||||
return d != null
|
||||
? d
|
||||
: (delegate = gson.getDelegateAdapter(skipPast, typeToken));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new factory that will match each type against {@code exactType}.
|
||||
*/
|
||||
public static TypeAdapterFactory newFactory(TypeToken<?> exactType, Object typeAdapter) {
|
||||
return new SingleTypeFactory(typeAdapter, exactType, false, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new factory that will match each type and its raw type against
|
||||
* {@code exactType}.
|
||||
*/
|
||||
public static TypeAdapterFactory newFactoryWithMatchRawType(
|
||||
TypeToken<?> exactType, Object typeAdapter) {
|
||||
// only bother matching raw types if exact type is a raw type
|
||||
boolean matchRawType = exactType.getType() == exactType.getRawType();
|
||||
return new SingleTypeFactory(typeAdapter, exactType, matchRawType, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new factory that will match each type's raw type for assignability
|
||||
* to {@code hierarchyType}.
|
||||
*/
|
||||
public static TypeAdapterFactory newTypeHierarchyFactory(
|
||||
Class<?> hierarchyType, Object typeAdapter) {
|
||||
return new SingleTypeFactory(typeAdapter, null, false, hierarchyType);
|
||||
}
|
||||
|
||||
private static class SingleTypeFactory implements TypeAdapterFactory {
|
||||
private final TypeToken<?> exactType;
|
||||
private final boolean matchRawType;
|
||||
private final Class<?> hierarchyType;
|
||||
private final JsonSerializer<?> serializer;
|
||||
private final JsonDeserializer<?> deserializer;
|
||||
|
||||
private SingleTypeFactory(Object typeAdapter, TypeToken<?> exactType, boolean matchRawType,
|
||||
Class<?> hierarchyType) {
|
||||
serializer = typeAdapter instanceof JsonSerializer
|
||||
? (JsonSerializer<?>) typeAdapter
|
||||
: null;
|
||||
deserializer = typeAdapter instanceof JsonDeserializer
|
||||
? (JsonDeserializer<?>) typeAdapter
|
||||
: null;
|
||||
$Gson$Preconditions.checkArgument(serializer != null || deserializer != null);
|
||||
this.exactType = exactType;
|
||||
this.matchRawType = matchRawType;
|
||||
this.hierarchyType = hierarchyType;
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked") // guarded by typeToken.equals() call
|
||||
public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type) {
|
||||
boolean matches = exactType != null
|
||||
? exactType.equals(type) || matchRawType && exactType.getType() == type.getRawType()
|
||||
: hierarchyType.isAssignableFrom(type.getRawType());
|
||||
return matches
|
||||
? new TreeTypeAdapter<T>((JsonSerializer<T>) serializer,
|
||||
(JsonDeserializer<T>) deserializer, gson, type, this)
|
||||
: null;
|
||||
}
|
||||
}
|
||||
}
|
||||
286
collect-library/src/main/java/com/google/gson/TypeAdapter.java
Normal file
286
collect-library/src/main/java/com/google/gson/TypeAdapter.java
Normal file
@@ -0,0 +1,286 @@
|
||||
/*
|
||||
* 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.google.gson;
|
||||
|
||||
import com.google.gson.internal.bind.JsonTreeWriter;
|
||||
import com.google.gson.internal.bind.JsonTreeReader;
|
||||
import com.google.gson.stream.JsonReader;
|
||||
import com.google.gson.stream.JsonToken;
|
||||
import com.google.gson.stream.JsonWriter;
|
||||
import java.io.IOException;
|
||||
import java.io.Reader;
|
||||
import java.io.StringReader;
|
||||
import java.io.StringWriter;
|
||||
import java.io.Writer;
|
||||
|
||||
/**
|
||||
* Converts Java objects to and from JSON.
|
||||
*
|
||||
* <h3>Defining a type's JSON form</h3>
|
||||
* By default Gson converts application classes to JSON using its built-in type
|
||||
* adapters. If Gson's default JSON conversion isn't appropriate for a type,
|
||||
* extend this class to customize the conversion. Here's an example of a type
|
||||
* adapter for an (X,Y) coordinate point: <pre> {@code
|
||||
*
|
||||
* public class PointAdapter extends TypeAdapter<Point> {
|
||||
* public Point read(JsonReader reader) throws IOException {
|
||||
* if (reader.peek() == JsonToken.NULL) {
|
||||
* reader.nextNull();
|
||||
* return null;
|
||||
* }
|
||||
* String xy = reader.nextString();
|
||||
* String[] parts = xy.split(",");
|
||||
* int x = Integer.parseInt(parts[0]);
|
||||
* int y = Integer.parseInt(parts[1]);
|
||||
* return new Point(x, y);
|
||||
* }
|
||||
* public void write(JsonWriter writer, Point value) throws IOException {
|
||||
* if (value == null) {
|
||||
* writer.nullValue();
|
||||
* return;
|
||||
* }
|
||||
* String xy = value.getX() + "," + value.getY();
|
||||
* writer.value(xy);
|
||||
* }
|
||||
* }}</pre>
|
||||
* With this type adapter installed, Gson will convert {@code Points} to JSON as
|
||||
* strings like {@code "5,8"} rather than objects like {@code {"x":5,"y":8}}. In
|
||||
* this case the type adapter binds a rich Java class to a compact JSON value.
|
||||
*
|
||||
* <p>The {@link #read(JsonReader) read()} method must read exactly one value
|
||||
* and {@link #write(JsonWriter,Object) write()} must write exactly one value.
|
||||
* For primitive types this is means readers should make exactly one call to
|
||||
* {@code nextBoolean()}, {@code nextDouble()}, {@code nextInt()}, {@code
|
||||
* nextLong()}, {@code nextString()} or {@code nextNull()}. Writers should make
|
||||
* exactly one call to one of <code>value()</code> or <code>nullValue()</code>.
|
||||
* For arrays, type adapters should start with a call to {@code beginArray()},
|
||||
* convert all elements, and finish with a call to {@code endArray()}. For
|
||||
* objects, they should start with {@code beginObject()}, convert the object,
|
||||
* and finish with {@code endObject()}. Failing to convert a value or converting
|
||||
* too many values may cause the application to crash.
|
||||
*
|
||||
* <p>Type adapters should be prepared to read null from the stream and write it
|
||||
* to the stream. Alternatively, they should use {@link #nullSafe()} method while
|
||||
* registering the type adapter with Gson. If your {@code Gson} instance
|
||||
* has been configured to {@link GsonBuilder#serializeNulls()}, these nulls will be
|
||||
* written to the final document. Otherwise the value (and the corresponding name
|
||||
* when writing to a JSON object) will be omitted automatically. In either case
|
||||
* your type adapter must handle null.
|
||||
*
|
||||
* <p>To use a custom type adapter with Gson, you must <i>register</i> it with a
|
||||
* {@link GsonBuilder}: <pre> {@code
|
||||
*
|
||||
* GsonBuilder builder = new GsonBuilder();
|
||||
* builder.registerTypeAdapter(Point.class, new PointAdapter());
|
||||
* // if PointAdapter didn't check for nulls in its read/write methods, you should instead use
|
||||
* // builder.registerTypeAdapter(Point.class, new PointAdapter().nullSafe());
|
||||
* ...
|
||||
* Gson gson = builder.create();
|
||||
* }</pre>
|
||||
*
|
||||
* @since 2.1
|
||||
*/
|
||||
// non-Javadoc:
|
||||
//
|
||||
// <h3>JSON Conversion</h3>
|
||||
// <p>A type adapter registered with Gson is automatically invoked while serializing
|
||||
// or deserializing JSON. However, you can also use type adapters directly to serialize
|
||||
// and deserialize JSON. Here is an example for deserialization: <pre> {@code
|
||||
//
|
||||
// String json = "{'origin':'0,0','points':['1,2','3,4']}";
|
||||
// TypeAdapter<Graph> graphAdapter = gson.getAdapter(Graph.class);
|
||||
// Graph graph = graphAdapter.fromJson(json);
|
||||
// }</pre>
|
||||
// And an example for serialization: <pre> {@code
|
||||
//
|
||||
// Graph graph = new Graph(...);
|
||||
// TypeAdapter<Graph> graphAdapter = gson.getAdapter(Graph.class);
|
||||
// String json = graphAdapter.toJson(graph);
|
||||
// }</pre>
|
||||
//
|
||||
// <p>Type adapters are <strong>type-specific</strong>. For example, a {@code
|
||||
// TypeAdapter<Date>} can convert {@code Date} instances to JSON and JSON to
|
||||
// instances of {@code Date}, but cannot convert any other types.
|
||||
//
|
||||
public abstract class TypeAdapter<T> {
|
||||
|
||||
/**
|
||||
* Writes one JSON value (an array, object, string, number, boolean or null)
|
||||
* for {@code value}.
|
||||
*
|
||||
* @param value the Java object to write. May be null.
|
||||
*/
|
||||
public abstract void write(JsonWriter out, T value) throws IOException;
|
||||
|
||||
/**
|
||||
* Converts {@code value} to a JSON document and writes it to {@code out}.
|
||||
* Unlike Gson's similar {@link Gson#toJson(JsonElement, Appendable) toJson}
|
||||
* method, this write is strict. Create a {@link
|
||||
* JsonWriter#setLenient(boolean) lenient} {@code JsonWriter} and call
|
||||
* {@link #write(com.google.gson.stream.JsonWriter, Object)} for lenient
|
||||
* writing.
|
||||
*
|
||||
* @param value the Java object to convert. May be null.
|
||||
* @since 2.2
|
||||
*/
|
||||
public final void toJson(Writer out, T value) throws IOException {
|
||||
JsonWriter writer = new JsonWriter(out);
|
||||
write(writer, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* This wrapper method is used to make a type adapter null tolerant. In general, a
|
||||
* type adapter is required to handle nulls in write and read methods. Here is how this
|
||||
* is typically done:<br>
|
||||
* <pre> {@code
|
||||
*
|
||||
* Gson gson = new GsonBuilder().registerTypeAdapter(Foo.class,
|
||||
* new TypeAdapter<Foo>() {
|
||||
* public Foo read(JsonReader in) throws IOException {
|
||||
* if (in.peek() == JsonToken.NULL) {
|
||||
* in.nextNull();
|
||||
* return null;
|
||||
* }
|
||||
* // read a Foo from in and return it
|
||||
* }
|
||||
* public void write(JsonWriter out, Foo src) throws IOException {
|
||||
* if (src == null) {
|
||||
* out.nullValue();
|
||||
* return;
|
||||
* }
|
||||
* // write src as JSON to out
|
||||
* }
|
||||
* }).create();
|
||||
* }</pre>
|
||||
* You can avoid this boilerplate handling of nulls by wrapping your type adapter with
|
||||
* this method. Here is how we will rewrite the above example:
|
||||
* <pre> {@code
|
||||
*
|
||||
* Gson gson = new GsonBuilder().registerTypeAdapter(Foo.class,
|
||||
* new TypeAdapter<Foo>() {
|
||||
* public Foo read(JsonReader in) throws IOException {
|
||||
* // read a Foo from in and return it
|
||||
* }
|
||||
* public void write(JsonWriter out, Foo src) throws IOException {
|
||||
* // write src as JSON to out
|
||||
* }
|
||||
* }.nullSafe()).create();
|
||||
* }</pre>
|
||||
* Note that we didn't need to check for nulls in our type adapter after we used nullSafe.
|
||||
*/
|
||||
public final TypeAdapter<T> nullSafe() {
|
||||
return new TypeAdapter<T>() {
|
||||
@Override public void write(JsonWriter out, T value) throws IOException {
|
||||
if (value == null) {
|
||||
out.nullValue();
|
||||
} else {
|
||||
TypeAdapter.this.write(out, value);
|
||||
}
|
||||
}
|
||||
@Override public T read(JsonReader reader) throws IOException {
|
||||
if (reader.peek() == JsonToken.NULL) {
|
||||
reader.nextNull();
|
||||
return null;
|
||||
}
|
||||
return TypeAdapter.this.read(reader);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts {@code value} to a JSON document. Unlike Gson's similar {@link
|
||||
* Gson#toJson(Object) toJson} method, this write is strict. Create a {@link
|
||||
* JsonWriter#setLenient(boolean) lenient} {@code JsonWriter} and call
|
||||
* {@link #write(com.google.gson.stream.JsonWriter, Object)} for lenient
|
||||
* writing.
|
||||
*
|
||||
* @param value the Java object to convert. May be null.
|
||||
* @since 2.2
|
||||
*/
|
||||
public final String toJson(T value) throws IOException {
|
||||
StringWriter stringWriter = new StringWriter();
|
||||
toJson(stringWriter, value);
|
||||
return stringWriter.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts {@code value} to a JSON tree.
|
||||
*
|
||||
* @param value the Java object to convert. May be null.
|
||||
* @return the converted JSON tree. May be {@link JsonNull}.
|
||||
* @since 2.2
|
||||
*/
|
||||
public final JsonElement toJsonTree(T value) {
|
||||
try {
|
||||
JsonTreeWriter jsonWriter = new JsonTreeWriter();
|
||||
write(jsonWriter, value);
|
||||
return jsonWriter.get();
|
||||
} catch (IOException e) {
|
||||
throw new JsonIOException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads one JSON value (an array, object, string, number, boolean or null)
|
||||
* and converts it to a Java object. Returns the converted object.
|
||||
*
|
||||
* @return the converted Java object. May be null.
|
||||
*/
|
||||
public abstract T read(JsonReader in) throws IOException;
|
||||
|
||||
/**
|
||||
* Converts the JSON document in {@code in} to a Java object. Unlike Gson's
|
||||
* similar {@link Gson#fromJson(java.io.Reader, Class) fromJson} method, this
|
||||
* read is strict. Create a {@link JsonReader#setLenient(boolean) lenient}
|
||||
* {@code JsonReader} and call {@link #read(JsonReader)} for lenient reading.
|
||||
*
|
||||
* @return the converted Java object. May be null.
|
||||
* @since 2.2
|
||||
*/
|
||||
public final T fromJson(Reader in) throws IOException {
|
||||
JsonReader reader = new JsonReader(in);
|
||||
return read(reader);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the JSON document in {@code json} to a Java object. Unlike Gson's
|
||||
* similar {@link Gson#fromJson(String, Class) fromJson} method, this read is
|
||||
* strict. Create a {@link JsonReader#setLenient(boolean) lenient} {@code
|
||||
* JsonReader} and call {@link #read(JsonReader)} for lenient reading.
|
||||
*
|
||||
* @return the converted Java object. May be null.
|
||||
* @since 2.2
|
||||
*/
|
||||
public final T fromJson(String json) throws IOException {
|
||||
return fromJson(new StringReader(json));
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts {@code jsonTree} to a Java object.
|
||||
*
|
||||
* @param jsonTree the Java object to convert. May be {@link JsonNull}.
|
||||
* @since 2.2
|
||||
*/
|
||||
public final T fromJsonTree(JsonElement jsonTree) {
|
||||
try {
|
||||
JsonReader jsonReader = new JsonTreeReader(jsonTree);
|
||||
return read(jsonReader);
|
||||
} catch (IOException e) {
|
||||
throw new JsonIOException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,170 @@
|
||||
/*
|
||||
* 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.google.gson;
|
||||
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
|
||||
/**
|
||||
* Creates type adapters for set of related types. Type adapter factories are
|
||||
* most useful when several types share similar structure in their JSON form.
|
||||
*
|
||||
* <h3>Example: Converting enums to lowercase</h3>
|
||||
* In this example, we implement a factory that creates type adapters for all
|
||||
* enums. The type adapters will write enums in lowercase, despite the fact
|
||||
* that they're defined in {@code CONSTANT_CASE} in the corresponding Java
|
||||
* model: <pre> {@code
|
||||
*
|
||||
* public class LowercaseEnumTypeAdapterFactory implements TypeAdapter.Factory {
|
||||
* public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type) {
|
||||
* Class<T> rawType = (Class<T>) type.getRawType();
|
||||
* if (!rawType.isEnum()) {
|
||||
* return null;
|
||||
* }
|
||||
*
|
||||
* final Map<String, T> lowercaseToConstant = new HashMap<String, T>();
|
||||
* for (T constant : rawType.getEnumConstants()) {
|
||||
* lowercaseToConstant.put(toLowercase(constant), constant);
|
||||
* }
|
||||
*
|
||||
* return new TypeAdapter<T>() {
|
||||
* public void write(JsonWriter out, T value) throws IOException {
|
||||
* if (value == null) {
|
||||
* out.nullValue();
|
||||
* } else {
|
||||
* out.value(toLowercase(value));
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* public T read(JsonReader reader) throws IOException {
|
||||
* if (reader.peek() == JsonToken.NULL) {
|
||||
* reader.nextNull();
|
||||
* return null;
|
||||
* } else {
|
||||
* return lowercaseToConstant.get(reader.nextString());
|
||||
* }
|
||||
* }
|
||||
* };
|
||||
* }
|
||||
*
|
||||
* private String toLowercase(Object o) {
|
||||
* return o.toString().toLowerCase(Locale.US);
|
||||
* }
|
||||
* }
|
||||
* }</pre>
|
||||
*
|
||||
* <p>Type adapter factories select which types they provide type adapters
|
||||
* for. If a factory cannot support a given type, it must return null when
|
||||
* that type is passed to {@link #create}. Factories should expect {@code
|
||||
* create()} to be called on them for many types and should return null for
|
||||
* most of those types. In the above example the factory returns null for
|
||||
* calls to {@code create()} where {@code type} is not an enum.
|
||||
*
|
||||
* <p>A factory is typically called once per type, but the returned type
|
||||
* adapter may be used many times. It is most efficient to do expensive work
|
||||
* like reflection in {@code create()} so that the type adapter's {@code
|
||||
* read()} and {@code write()} methods can be very fast. In this example the
|
||||
* mapping from lowercase name to enum value is computed eagerly.
|
||||
*
|
||||
* <p>As with type adapters, factories must be <i>registered</i> with a {@link
|
||||
* com.google.gson.GsonBuilder} for them to take effect: <pre> {@code
|
||||
*
|
||||
* GsonBuilder builder = new GsonBuilder();
|
||||
* builder.registerTypeAdapterFactory(new LowercaseEnumTypeAdapterFactory());
|
||||
* ...
|
||||
* Gson gson = builder.create();
|
||||
* }</pre>
|
||||
* If multiple factories support the same type, the factory registered earlier
|
||||
* takes precedence.
|
||||
*
|
||||
* <h3>Example: composing other type adapters</h3>
|
||||
* In this example we implement a factory for Guava's {@code Multiset}
|
||||
* collection type. The factory can be used to create type adapters for
|
||||
* multisets of any element type: the type adapter for {@code
|
||||
* Multiset<String>} is different from the type adapter for {@code
|
||||
* Multiset<URL>}.
|
||||
*
|
||||
* <p>The type adapter <i>delegates</i> to another type adapter for the
|
||||
* multiset elements. It figures out the element type by reflecting on the
|
||||
* multiset's type token. A {@code Gson} is passed in to {@code create} for
|
||||
* just this purpose: <pre> {@code
|
||||
*
|
||||
* public class MultisetTypeAdapterFactory implements TypeAdapter.Factory {
|
||||
* public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> typeToken) {
|
||||
* Type type = typeToken.getType();
|
||||
* if (typeToken.getRawType() != Multiset.class
|
||||
* || !(type instanceof ParameterizedType)) {
|
||||
* return null;
|
||||
* }
|
||||
*
|
||||
* Type elementType = ((ParameterizedType) type).getActualTypeArguments()[0];
|
||||
* TypeAdapter<?> elementAdapter = gson.getAdapter(TypeToken.get(elementType));
|
||||
* return (TypeAdapter<T>) newMultisetAdapter(elementAdapter);
|
||||
* }
|
||||
*
|
||||
* private <E> TypeAdapter<Multiset<E>> newMultisetAdapter(
|
||||
* final TypeAdapter<E> elementAdapter) {
|
||||
* return new TypeAdapter<Multiset<E>>() {
|
||||
* public void write(JsonWriter out, Multiset<E> value) throws IOException {
|
||||
* if (value == null) {
|
||||
* out.nullValue();
|
||||
* return;
|
||||
* }
|
||||
*
|
||||
* out.beginArray();
|
||||
* for (Multiset.Entry<E> entry : value.entrySet()) {
|
||||
* out.value(entry.getCount());
|
||||
* elementAdapter.write(out, entry.getElement());
|
||||
* }
|
||||
* out.endArray();
|
||||
* }
|
||||
*
|
||||
* public Multiset<E> read(JsonReader in) throws IOException {
|
||||
* if (in.peek() == JsonToken.NULL) {
|
||||
* in.nextNull();
|
||||
* return null;
|
||||
* }
|
||||
*
|
||||
* Multiset<E> result = LinkedHashMultiset.create();
|
||||
* in.beginArray();
|
||||
* while (in.hasNext()) {
|
||||
* int count = in.nextInt();
|
||||
* E element = elementAdapter.read(in);
|
||||
* result.add(element, count);
|
||||
* }
|
||||
* in.endArray();
|
||||
* return result;
|
||||
* }
|
||||
* };
|
||||
* }
|
||||
* }
|
||||
* }</pre>
|
||||
* Delegating from one type adapter to another is extremely powerful; it's
|
||||
* the foundation of how Gson converts Java objects and collections. Whenever
|
||||
* possible your factory should retrieve its delegate type adapter in the
|
||||
* {@code create()} method; this ensures potentially-expensive type adapter
|
||||
* creation happens only once.
|
||||
*
|
||||
* @since 2.1
|
||||
*/
|
||||
public interface TypeAdapterFactory {
|
||||
|
||||
/**
|
||||
* Returns a type adapter for {@code type}, or null if this factory doesn't
|
||||
* support {@code type}.
|
||||
*/
|
||||
<T> TypeAdapter<T> create(Gson gson, TypeToken<T> type);
|
||||
}
|
||||
@@ -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.google.gson.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.google.gson.Gson}
|
||||
* with a {@link com.google.gson.GsonBuilder} and invoke
|
||||
* {@link com.google.gson.GsonBuilder#excludeFieldsWithoutExposeAnnotation()}
|
||||
* method.</p>
|
||||
*
|
||||
* <p>Here is an example of how this annotation is meant to be used:
|
||||
* <p><pre>
|
||||
* public class User {
|
||||
* @Expose private String firstName;
|
||||
* @Expose(serialize = false) private String lastName;
|
||||
* @Expose (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;
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
/*
|
||||
* Copyright (C) 2014 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.google.gson.annotations;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
import com.google.gson.TypeAdapter;
|
||||
|
||||
/**
|
||||
* An annotation that indicates the Gson {@link TypeAdapter} to use with a class or a field.
|
||||
* Any type adapters registered in {@link com.google.gson.GsonBuilder} supersede the adapter
|
||||
* specified in this annotation.
|
||||
*
|
||||
* <p>Here is an example of how this annotation is used:</p>
|
||||
* <pre>
|
||||
* @JsonAdapter(UserJsonAdapter.class)
|
||||
* public class User {
|
||||
* public final String firstName, lastName;
|
||||
* private User(String firstName, String lastName) {
|
||||
* this.firstName = firstName;
|
||||
* this.lastName = lastName;
|
||||
* }
|
||||
* }
|
||||
* public class UserJsonAdapter extends TypeAdapter<User> {
|
||||
* @Override public void write(JsonWriter out, User user) throws IOException {
|
||||
* // implement write: combine firstName and lastName into name
|
||||
* out.beginObject();
|
||||
* out.name("name");
|
||||
* out.value(user.firstName + " " + user.lastName);
|
||||
* out.endObject();
|
||||
* // implement the write method
|
||||
* }
|
||||
* @Override public User read(JsonReader in) throws IOException {
|
||||
* // implement read: split name into firstName and lastName
|
||||
* in.beginObject();
|
||||
* in.nextName();
|
||||
* String[] nameParts = in.nextString().split(" ");
|
||||
* in.endObject();
|
||||
* return new User(nameParts[0], nameParts[1]);
|
||||
* }
|
||||
* }
|
||||
* </pre>
|
||||
*
|
||||
* Since User class specified UserJsonAdapter.class in @JsonAdapter annotation, it
|
||||
* will automatically be invoked to serialize/deserialize User instances. <br>
|
||||
*
|
||||
* If the UserJsonAdapter needs a constructor other than a no-args constructor, you must register
|
||||
* an {@link com.google.gson.InstanceCreator} for it.
|
||||
*
|
||||
* <p> Here is an example of how to apply this annotation to a field.
|
||||
* <pre>
|
||||
* private static final class Gadget {
|
||||
* @JsonAdapter(UserJsonAdapter2.class)
|
||||
* final User user;
|
||||
* Gadget(User user) {
|
||||
* this.user = user;
|
||||
* }
|
||||
* }
|
||||
* </pre>
|
||||
* The above annotation will ensure UserJsonAdapter2 supersedes UserJsonAdapter for the user
|
||||
* field of the Gadget class.
|
||||
*
|
||||
* @since 2.3
|
||||
*
|
||||
* @author Inderjeet Singh
|
||||
* @author Joel Leitch
|
||||
* @author Jesse Leitch
|
||||
*/
|
||||
// Note that the above example is taken from AdaptAnnotationTest.
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target({ElementType.TYPE, ElementType.FIELD})
|
||||
public @interface JsonAdapter {
|
||||
|
||||
Class<? extends TypeAdapter<?>> value();
|
||||
|
||||
}
|
||||
@@ -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.google.gson.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.google.gson.FieldNamingPolicy}, including
|
||||
* the default field naming policy, that may have been set on the {@link com.google.gson.Gson}
|
||||
* instance. A different naming policy can set using the {@code GsonBuilder} class. See
|
||||
* {@link com.google.gson.GsonBuilder#setFieldNamingPolicy(com.google.gson.FieldNamingPolicy)}
|
||||
* for more information.</p>
|
||||
*
|
||||
* <p>Here is an example of how this annotation is meant to be used:</p>
|
||||
* <pre>
|
||||
* public class SomeClassWithFields {
|
||||
* @SerializedName("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.google.gson.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();
|
||||
}
|
||||
@@ -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.google.gson.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.google.gson.Gson} with a
|
||||
* {@link com.google.gson.GsonBuilder} and invoke
|
||||
* {@link com.google.gson.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;
|
||||
* @Since(1.0) private String emailAddress;
|
||||
* @Since(1.0) private String password;
|
||||
* @Since(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();
|
||||
}
|
||||
@@ -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.google.gson.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.google.gson.Gson} with a
|
||||
* {@link com.google.gson.GsonBuilder} and invoke
|
||||
* {@link com.google.gson.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;
|
||||
* @Until(1.1) private String emailAddress;
|
||||
* @Until(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();
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
/**
|
||||
* This package provides annotations that can be used with {@link com.google.gson.Gson}.
|
||||
*
|
||||
* @author Inderjeet Singh, Joel Leitch
|
||||
*/
|
||||
package com.google.gson.annotations;
|
||||
@@ -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.google.gson.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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,608 @@
|
||||
/**
|
||||
* 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.google.gson.internal;
|
||||
|
||||
import static com.google.gson.internal.$Gson$Preconditions.checkArgument;
|
||||
import static com.google.gson.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.Modifier;
|
||||
import java.lang.reflect.ParameterizedType;
|
||||
import java.lang.reflect.Type;
|
||||
import java.lang.reflect.TypeVariable;
|
||||
import java.lang.reflect.WildcardType;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Map;
|
||||
import java.util.NoSuchElementException;
|
||||
import java.util.Properties;
|
||||
|
||||
/**
|
||||
* 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 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a parameterized type A<B,C>, returns B. If the specified type is not
|
||||
* a generic type, returns null.
|
||||
*/
|
||||
public static Type getFirstTypeArgument(Type type) {
|
||||
try {
|
||||
if (!(type instanceof ParameterizedType)) return null;
|
||||
ParameterizedType ptype = (ParameterizedType) type;
|
||||
Type[] actualTypeArguments = ptype.getActualTypeArguments();
|
||||
if (actualTypeArguments.length == 0) return null;
|
||||
return canonicalize(actualTypeArguments[0]);
|
||||
} catch (Exception e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
boolean isStaticOrTopLevelClass = Modifier.isStatic(rawTypeAsClass.getModifiers())
|
||||
|| rawTypeAsClass.getEnclosingClass() == null;
|
||||
checkArgument(ownerType != null || isStaticOrTopLevelClass);
|
||||
}
|
||||
|
||||
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]);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Type[] getActualTypeArguments() {
|
||||
return typeArguments.clone();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Type getRawType() {
|
||||
return rawType;
|
||||
}
|
||||
|
||||
@Override
|
||||
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);
|
||||
}
|
||||
|
||||
@Override
|
||||
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]);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Type[] getUpperBounds() {
|
||||
return new Type[] { upperBound };
|
||||
}
|
||||
|
||||
@Override
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,232 @@
|
||||
/*
|
||||
* 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.google.gson.internal;
|
||||
|
||||
import com.google.gson.InstanceCreator;
|
||||
import com.google.gson.JsonIOException;
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.ParameterizedType;
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.EnumSet;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.LinkedList;
|
||||
import java.util.Map;
|
||||
import java.util.Queue;
|
||||
import java.util.Set;
|
||||
import java.util.SortedMap;
|
||||
import java.util.SortedSet;
|
||||
import java.util.TreeMap;
|
||||
import java.util.TreeSet;
|
||||
|
||||
/**
|
||||
* Returns a function that can construct an instance of a requested type.
|
||||
*/
|
||||
public final class ConstructorConstructor {
|
||||
private final Map<Type, InstanceCreator<?>> instanceCreators;
|
||||
|
||||
public ConstructorConstructor(Map<Type, InstanceCreator<?>> instanceCreators) {
|
||||
this.instanceCreators = instanceCreators;
|
||||
}
|
||||
|
||||
public <T> ObjectConstructor<T> get(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> typeCreator = (InstanceCreator<T>) instanceCreators.get(type);
|
||||
if (typeCreator != null) {
|
||||
return new ObjectConstructor<T>() {
|
||||
@Override
|
||||
public T construct() {
|
||||
return typeCreator.createInstance(type);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// Next try raw type match for instance creators
|
||||
@SuppressWarnings("unchecked") // types must agree
|
||||
final InstanceCreator<T> rawTypeCreator =
|
||||
(InstanceCreator<T>) instanceCreators.get(rawType);
|
||||
if (rawTypeCreator != null) {
|
||||
return new ObjectConstructor<T>() {
|
||||
@Override
|
||||
public T construct() {
|
||||
return rawTypeCreator.createInstance(type);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
ObjectConstructor<T> defaultConstructor = newDefaultConstructor(rawType);
|
||||
if (defaultConstructor != null) {
|
||||
return defaultConstructor;
|
||||
}
|
||||
|
||||
ObjectConstructor<T> defaultImplementation = newDefaultImplementationConstructor(type, 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>() {
|
||||
@Override
|
||||
@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(
|
||||
final Type type, Class<? super T> rawType) {
|
||||
if (Collection.class.isAssignableFrom(rawType)) {
|
||||
if (SortedSet.class.isAssignableFrom(rawType)) {
|
||||
return new ObjectConstructor<T>() {
|
||||
@Override
|
||||
public T construct() {
|
||||
return (T) new TreeSet<Object>();
|
||||
}
|
||||
};
|
||||
} else if (EnumSet.class.isAssignableFrom(rawType)) {
|
||||
return new ObjectConstructor<T>() {
|
||||
@Override
|
||||
@SuppressWarnings("rawtypes")
|
||||
public T construct() {
|
||||
if (type instanceof ParameterizedType) {
|
||||
Type elementType = ((ParameterizedType) type).getActualTypeArguments()[0];
|
||||
if (elementType instanceof Class) {
|
||||
return (T) EnumSet.noneOf((Class)elementType);
|
||||
} else {
|
||||
throw new JsonIOException("Invalid EnumSet type: " + type.toString());
|
||||
}
|
||||
} else {
|
||||
throw new JsonIOException("Invalid EnumSet type: " + type.toString());
|
||||
}
|
||||
}
|
||||
};
|
||||
} else if (Set.class.isAssignableFrom(rawType)) {
|
||||
return new ObjectConstructor<T>() {
|
||||
@Override
|
||||
public T construct() {
|
||||
return (T) new LinkedHashSet<Object>();
|
||||
}
|
||||
};
|
||||
} else if (Queue.class.isAssignableFrom(rawType)) {
|
||||
return new ObjectConstructor<T>() {
|
||||
@Override
|
||||
public T construct() {
|
||||
return (T) new LinkedList<Object>();
|
||||
}
|
||||
};
|
||||
} else {
|
||||
return new ObjectConstructor<T>() {
|
||||
@Override
|
||||
public T construct() {
|
||||
return (T) new ArrayList<Object>();
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
if (Map.class.isAssignableFrom(rawType)) {
|
||||
if (SortedMap.class.isAssignableFrom(rawType)) {
|
||||
return new ObjectConstructor<T>() {
|
||||
@Override
|
||||
public T construct() {
|
||||
return (T) new TreeMap<Object, Object>();
|
||||
}
|
||||
};
|
||||
} else if (type instanceof ParameterizedType && !(String.class.isAssignableFrom(
|
||||
TypeToken.get(((ParameterizedType) type).getActualTypeArguments()[0]).getRawType()))) {
|
||||
return new ObjectConstructor<T>() {
|
||||
@Override
|
||||
public T construct() {
|
||||
return (T) new LinkedHashMap<Object, Object>();
|
||||
}
|
||||
};
|
||||
} else {
|
||||
return new ObjectConstructor<T>() {
|
||||
@Override
|
||||
public T construct() {
|
||||
return (T) new LinkedTreeMap<String, Object>();
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
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();
|
||||
@Override
|
||||
@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();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,252 @@
|
||||
/*
|
||||
* 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.google.gson.internal;
|
||||
|
||||
import com.google.gson.ExclusionStrategy;
|
||||
import com.google.gson.FieldAttributes;
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.TypeAdapter;
|
||||
import com.google.gson.TypeAdapterFactory;
|
||||
import com.google.gson.annotations.Expose;
|
||||
import com.google.gson.annotations.Since;
|
||||
import com.google.gson.annotations.Until;
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
import com.google.gson.stream.JsonReader;
|
||||
import com.google.gson.stream.JsonWriter;
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* This class selects which fields and types to omit. It is configurable,
|
||||
* supporting version attributes {@link Since} and {@link Until}, modifiers,
|
||||
* synthetic fields, anonymous and local classes, inner classes, and fields with
|
||||
* the {@link Expose} annotation.
|
||||
*
|
||||
* <p>This class is a type adapter factory; types that are excluded will be
|
||||
* adapted to null. It may delegate to another type adapter if only one
|
||||
* direction is excluded.
|
||||
*
|
||||
* @author Joel Leitch
|
||||
* @author Jesse Wilson
|
||||
*/
|
||||
public final class Excluder implements TypeAdapterFactory, Cloneable {
|
||||
private static final double IGNORE_VERSIONS = -1.0d;
|
||||
public static final Excluder DEFAULT = new Excluder();
|
||||
|
||||
private double version = IGNORE_VERSIONS;
|
||||
private int modifiers = Modifier.TRANSIENT | Modifier.STATIC;
|
||||
private boolean serializeInnerClasses = true;
|
||||
private boolean requireExpose;
|
||||
private List<ExclusionStrategy> serializationStrategies = Collections.emptyList();
|
||||
private List<ExclusionStrategy> deserializationStrategies = Collections.emptyList();
|
||||
|
||||
@Override protected Excluder clone() {
|
||||
try {
|
||||
return (Excluder) super.clone();
|
||||
} catch (CloneNotSupportedException e) {
|
||||
throw new AssertionError();
|
||||
}
|
||||
}
|
||||
|
||||
public Excluder withVersion(double ignoreVersionsAfter) {
|
||||
Excluder result = clone();
|
||||
result.version = ignoreVersionsAfter;
|
||||
return result;
|
||||
}
|
||||
|
||||
public Excluder withModifiers(int... modifiers) {
|
||||
Excluder result = clone();
|
||||
result.modifiers = 0;
|
||||
for (int modifier : modifiers) {
|
||||
result.modifiers |= modifier;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public Excluder disableInnerClassSerialization() {
|
||||
Excluder result = clone();
|
||||
result.serializeInnerClasses = false;
|
||||
return result;
|
||||
}
|
||||
|
||||
public Excluder excludeFieldsWithoutExposeAnnotation() {
|
||||
Excluder result = clone();
|
||||
result.requireExpose = true;
|
||||
return result;
|
||||
}
|
||||
|
||||
public Excluder withExclusionStrategy(ExclusionStrategy exclusionStrategy,
|
||||
boolean serialization, boolean deserialization) {
|
||||
Excluder result = clone();
|
||||
if (serialization) {
|
||||
result.serializationStrategies = new ArrayList<ExclusionStrategy>(serializationStrategies);
|
||||
result.serializationStrategies.add(exclusionStrategy);
|
||||
}
|
||||
if (deserialization) {
|
||||
result.deserializationStrategies
|
||||
= new ArrayList<ExclusionStrategy>(deserializationStrategies);
|
||||
result.deserializationStrategies.add(exclusionStrategy);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> TypeAdapter<T> create(final Gson gson, final TypeToken<T> type) {
|
||||
Class<?> rawType = type.getRawType();
|
||||
final boolean skipSerialize = excludeClass(rawType, true);
|
||||
final boolean skipDeserialize = excludeClass(rawType, false);
|
||||
|
||||
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 in) throws IOException {
|
||||
if (skipDeserialize) {
|
||||
in.skipValue();
|
||||
return null;
|
||||
}
|
||||
return delegate().read(in);
|
||||
}
|
||||
|
||||
@Override public void write(JsonWriter out, T value) throws IOException {
|
||||
if (skipSerialize) {
|
||||
out.nullValue();
|
||||
return;
|
||||
}
|
||||
delegate().write(out, value);
|
||||
}
|
||||
|
||||
private TypeAdapter<T> delegate() {
|
||||
TypeAdapter<T> d = delegate;
|
||||
return d != null
|
||||
? d
|
||||
: (delegate = gson.getDelegateAdapter(Excluder.this, type));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public boolean excludeField(Field field, boolean serialize) {
|
||||
if ((modifiers & field.getModifiers()) != 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (version != Excluder.IGNORE_VERSIONS
|
||||
&& !isValidVersion(field.getAnnotation(Since.class), field.getAnnotation(Until.class))) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (field.isSynthetic()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (requireExpose) {
|
||||
Expose annotation = field.getAnnotation(Expose.class);
|
||||
if (annotation == null || (serialize ? !annotation.serialize() : !annotation.deserialize())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!serializeInnerClasses && isInnerClass(field.getType())) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (isAnonymousOrLocal(field.getType())) {
|
||||
return true;
|
||||
}
|
||||
|
||||
List<ExclusionStrategy> list = serialize ? serializationStrategies : deserializationStrategies;
|
||||
if (!list.isEmpty()) {
|
||||
FieldAttributes fieldAttributes = new FieldAttributes(field);
|
||||
for (ExclusionStrategy exclusionStrategy : list) {
|
||||
if (exclusionStrategy.shouldSkipField(fieldAttributes)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean excludeClass(Class<?> clazz, boolean serialize) {
|
||||
if (version != Excluder.IGNORE_VERSIONS
|
||||
&& !isValidVersion(clazz.getAnnotation(Since.class), clazz.getAnnotation(Until.class))) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!serializeInnerClasses && isInnerClass(clazz)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (isAnonymousOrLocal(clazz)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
List<ExclusionStrategy> list = serialize ? serializationStrategies : deserializationStrategies;
|
||||
for (ExclusionStrategy exclusionStrategy : list) {
|
||||
if (exclusionStrategy.shouldSkipClass(clazz)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean isAnonymousOrLocal(Class<?> clazz) {
|
||||
return !Enum.class.isAssignableFrom(clazz)
|
||||
&& (clazz.isAnonymousClass() || clazz.isLocalClass());
|
||||
}
|
||||
|
||||
private boolean isInnerClass(Class<?> clazz) {
|
||||
return clazz.isMemberClass() && !isStatic(clazz);
|
||||
}
|
||||
|
||||
private boolean isStatic(Class<?> clazz) {
|
||||
return (clazz.getModifiers() & Modifier.STATIC) != 0;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -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.google.gson.internal;
|
||||
|
||||
import com.google.gson.stream.JsonReader;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* Internal-only APIs of JsonReader available only to other classes in Gson.
|
||||
*/
|
||||
public abstract class JsonReaderInternalAccess {
|
||||
public static JsonReaderInternalAccess INSTANCE;
|
||||
|
||||
/**
|
||||
* Changes the type of the current property name token to a string value.
|
||||
*/
|
||||
public abstract void promoteNameToValue(JsonReader reader) throws IOException;
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
/*
|
||||
* 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.google.gson.internal;
|
||||
|
||||
import java.io.ObjectStreamException;
|
||||
import java.math.BigDecimal;
|
||||
|
||||
/**
|
||||
* This class holds a number value that is lazily converted to a specific number type
|
||||
*
|
||||
* @author Inderjeet Singh
|
||||
*/
|
||||
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 BigDecimal(value).intValue();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public long longValue() {
|
||||
try {
|
||||
return Long.parseLong(value);
|
||||
} catch (NumberFormatException e) {
|
||||
return new BigDecimal(value).longValue();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public float floatValue() {
|
||||
return Float.parseFloat(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public double doubleValue() {
|
||||
return Double.parseDouble(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* If somebody is unlucky enough to have to serialize one of these, serialize
|
||||
* it as a BigDecimal so that they won't need Gson on the other side to
|
||||
* deserialize it.
|
||||
*/
|
||||
private Object writeReplace() throws ObjectStreamException {
|
||||
return new BigDecimal(value);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,869 @@
|
||||
/*
|
||||
* Copyright (C) 2010 The Android Open Source Project
|
||||
* Copyright (C) 2012 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.google.gson.internal;
|
||||
|
||||
import java.io.ObjectStreamException;
|
||||
import java.io.Serializable;
|
||||
import java.util.AbstractMap;
|
||||
import java.util.AbstractSet;
|
||||
import java.util.Arrays;
|
||||
import java.util.Comparator;
|
||||
import java.util.ConcurrentModificationException;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.NoSuchElementException;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* A map of comparable keys to values. Unlike {@code TreeMap}, this class uses
|
||||
* insertion order for iteration order. Comparison order is only used as an
|
||||
* optimization for efficient insertion and removal.
|
||||
*
|
||||
* <p>This implementation was derived from Android 4.1's TreeMap and
|
||||
* LinkedHashMap classes.
|
||||
*/
|
||||
public final class LinkedHashTreeMap<K, V> extends AbstractMap<K, V> implements Serializable {
|
||||
@SuppressWarnings({ "unchecked", "rawtypes" }) // to avoid Comparable<Comparable<Comparable<...>>>
|
||||
private static final Comparator<Comparable> NATURAL_ORDER = new Comparator<Comparable>() {
|
||||
@Override
|
||||
public int compare(Comparable a, Comparable b) {
|
||||
return a.compareTo(b);
|
||||
}
|
||||
};
|
||||
|
||||
Comparator<? super K> comparator;
|
||||
Node<K, V>[] table;
|
||||
final Node<K, V> header;
|
||||
int size = 0;
|
||||
int modCount = 0;
|
||||
int threshold;
|
||||
|
||||
/**
|
||||
* Create a natural order, empty tree map whose keys must be mutually
|
||||
* comparable and non-null.
|
||||
*/
|
||||
@SuppressWarnings("unchecked") // unsafe! this assumes K is comparable
|
||||
public LinkedHashTreeMap() {
|
||||
this((Comparator<? super K>) NATURAL_ORDER);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a tree map ordered by {@code comparator}. This map's keys may only
|
||||
* be null if {@code comparator} permits.
|
||||
*
|
||||
* @param comparator the comparator to order elements with, or {@code null} to
|
||||
* use the natural ordering.
|
||||
*/
|
||||
@SuppressWarnings({ "unchecked", "rawtypes" }) // unsafe! if comparator is null, this assumes K is comparable
|
||||
public LinkedHashTreeMap(Comparator<? super K> comparator) {
|
||||
this.comparator = comparator != null
|
||||
? comparator
|
||||
: (Comparator) NATURAL_ORDER;
|
||||
this.header = new Node<K, V>();
|
||||
this.table = new Node[16]; // TODO: sizing/resizing policies
|
||||
this.threshold = (table.length / 2) + (table.length / 4); // 3/4 capacity
|
||||
}
|
||||
|
||||
@Override public int size() {
|
||||
return size;
|
||||
}
|
||||
|
||||
@Override public V get(Object key) {
|
||||
Node<K, V> node = findByObject(key);
|
||||
return node != null ? node.value : null;
|
||||
}
|
||||
|
||||
@Override public boolean containsKey(Object key) {
|
||||
return findByObject(key) != null;
|
||||
}
|
||||
|
||||
@Override public V put(K key, V value) {
|
||||
if (key == null) {
|
||||
throw new NullPointerException("key == null");
|
||||
}
|
||||
Node<K, V> created = find(key, true);
|
||||
V result = created.value;
|
||||
created.value = value;
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override public void clear() {
|
||||
Arrays.fill(table, null);
|
||||
size = 0;
|
||||
modCount++;
|
||||
|
||||
// Clear all links to help GC
|
||||
Node<K, V> header = this.header;
|
||||
for (Node<K, V> e = header.next; e != header; ) {
|
||||
Node<K, V> next = e.next;
|
||||
e.next = e.prev = null;
|
||||
e = next;
|
||||
}
|
||||
|
||||
header.next = header.prev = header;
|
||||
}
|
||||
|
||||
@Override public V remove(Object key) {
|
||||
Node<K, V> node = removeInternalByKey(key);
|
||||
return node != null ? node.value : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the node at or adjacent to the given key, creating it if requested.
|
||||
*
|
||||
* @throws ClassCastException if {@code key} and the tree's keys aren't
|
||||
* mutually comparable.
|
||||
*/
|
||||
Node<K, V> find(K key, boolean create) {
|
||||
Comparator<? super K> comparator = this.comparator;
|
||||
Node<K, V>[] table = this.table;
|
||||
int hash = secondaryHash(key.hashCode());
|
||||
int index = hash & (table.length - 1);
|
||||
Node<K, V> nearest = table[index];
|
||||
int comparison = 0;
|
||||
|
||||
if (nearest != null) {
|
||||
// Micro-optimization: avoid polymorphic calls to Comparator.compare().
|
||||
@SuppressWarnings("unchecked") // Throws a ClassCastException below if there's trouble.
|
||||
Comparable<Object> comparableKey = (comparator == NATURAL_ORDER)
|
||||
? (Comparable<Object>) key
|
||||
: null;
|
||||
|
||||
while (true) {
|
||||
comparison = (comparableKey != null)
|
||||
? comparableKey.compareTo(nearest.key)
|
||||
: comparator.compare(key, nearest.key);
|
||||
|
||||
// We found the requested key.
|
||||
if (comparison == 0) {
|
||||
return nearest;
|
||||
}
|
||||
|
||||
// If it exists, the key is in a subtree. Go deeper.
|
||||
Node<K, V> child = (comparison < 0) ? nearest.left : nearest.right;
|
||||
if (child == null) {
|
||||
break;
|
||||
}
|
||||
|
||||
nearest = child;
|
||||
}
|
||||
}
|
||||
|
||||
// The key doesn't exist in this tree.
|
||||
if (!create) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Create the node and add it to the tree or the table.
|
||||
Node<K, V> header = this.header;
|
||||
Node<K, V> created;
|
||||
if (nearest == null) {
|
||||
// Check that the value is comparable if we didn't do any comparisons.
|
||||
if (comparator == NATURAL_ORDER && !(key instanceof Comparable)) {
|
||||
throw new ClassCastException(key.getClass().getName() + " is not Comparable");
|
||||
}
|
||||
created = new Node<K, V>(nearest, key, hash, header, header.prev);
|
||||
table[index] = created;
|
||||
} else {
|
||||
created = new Node<K, V>(nearest, key, hash, header, header.prev);
|
||||
if (comparison < 0) { // nearest.key is higher
|
||||
nearest.left = created;
|
||||
} else { // comparison > 0, nearest.key is lower
|
||||
nearest.right = created;
|
||||
}
|
||||
rebalance(nearest, true);
|
||||
}
|
||||
|
||||
if (size++ > threshold) {
|
||||
doubleCapacity();
|
||||
}
|
||||
modCount++;
|
||||
|
||||
return created;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
Node<K, V> findByObject(Object key) {
|
||||
try {
|
||||
return key != null ? find((K) key, false) : null;
|
||||
} catch (ClassCastException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns this map's entry that has the same key and value as {@code
|
||||
* entry}, or null if this map has no such entry.
|
||||
*
|
||||
* <p>This method uses the comparator for key equality rather than {@code
|
||||
* equals}. If this map's comparator isn't consistent with equals (such as
|
||||
* {@code String.CASE_INSENSITIVE_ORDER}), then {@code remove()} and {@code
|
||||
* contains()} will violate the collections API.
|
||||
*/
|
||||
Node<K, V> findByEntry(Entry<?, ?> entry) {
|
||||
Node<K, V> mine = findByObject(entry.getKey());
|
||||
boolean valuesEqual = mine != null && equal(mine.value, entry.getValue());
|
||||
return valuesEqual ? mine : null;
|
||||
}
|
||||
|
||||
private boolean equal(Object a, Object b) {
|
||||
return a == b || (a != null && a.equals(b));
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies a supplemental hash function to a given hashCode, which defends
|
||||
* against poor quality hash functions. This is critical because HashMap
|
||||
* uses power-of-two length hash tables, that otherwise encounter collisions
|
||||
* for hashCodes that do not differ in lower or upper bits.
|
||||
*/
|
||||
private static int secondaryHash(int h) {
|
||||
// Doug Lea's supplemental hash function
|
||||
h ^= (h >>> 20) ^ (h >>> 12);
|
||||
return h ^ (h >>> 7) ^ (h >>> 4);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes {@code node} from this tree, rearranging the tree's structure as
|
||||
* necessary.
|
||||
*
|
||||
* @param unlink true to also unlink this node from the iteration linked list.
|
||||
*/
|
||||
void removeInternal(Node<K, V> node, boolean unlink) {
|
||||
if (unlink) {
|
||||
node.prev.next = node.next;
|
||||
node.next.prev = node.prev;
|
||||
node.next = node.prev = null; // Help the GC (for performance)
|
||||
}
|
||||
|
||||
Node<K, V> left = node.left;
|
||||
Node<K, V> right = node.right;
|
||||
Node<K, V> originalParent = node.parent;
|
||||
if (left != null && right != null) {
|
||||
|
||||
/*
|
||||
* To remove a node with both left and right subtrees, move an
|
||||
* adjacent node from one of those subtrees into this node's place.
|
||||
*
|
||||
* Removing the adjacent node may change this node's subtrees. This
|
||||
* node may no longer have two subtrees once the adjacent node is
|
||||
* gone!
|
||||
*/
|
||||
|
||||
Node<K, V> adjacent = (left.height > right.height) ? left.last() : right.first();
|
||||
removeInternal(adjacent, false); // takes care of rebalance and size--
|
||||
|
||||
int leftHeight = 0;
|
||||
left = node.left;
|
||||
if (left != null) {
|
||||
leftHeight = left.height;
|
||||
adjacent.left = left;
|
||||
left.parent = adjacent;
|
||||
node.left = null;
|
||||
}
|
||||
int rightHeight = 0;
|
||||
right = node.right;
|
||||
if (right != null) {
|
||||
rightHeight = right.height;
|
||||
adjacent.right = right;
|
||||
right.parent = adjacent;
|
||||
node.right = null;
|
||||
}
|
||||
adjacent.height = Math.max(leftHeight, rightHeight) + 1;
|
||||
replaceInParent(node, adjacent);
|
||||
return;
|
||||
} else if (left != null) {
|
||||
replaceInParent(node, left);
|
||||
node.left = null;
|
||||
} else if (right != null) {
|
||||
replaceInParent(node, right);
|
||||
node.right = null;
|
||||
} else {
|
||||
replaceInParent(node, null);
|
||||
}
|
||||
|
||||
rebalance(originalParent, false);
|
||||
size--;
|
||||
modCount++;
|
||||
}
|
||||
|
||||
Node<K, V> removeInternalByKey(Object key) {
|
||||
Node<K, V> node = findByObject(key);
|
||||
if (node != null) {
|
||||
removeInternal(node, true);
|
||||
}
|
||||
return node;
|
||||
}
|
||||
|
||||
private void replaceInParent(Node<K, V> node, Node<K, V> replacement) {
|
||||
Node<K, V> parent = node.parent;
|
||||
node.parent = null;
|
||||
if (replacement != null) {
|
||||
replacement.parent = parent;
|
||||
}
|
||||
|
||||
if (parent != null) {
|
||||
if (parent.left == node) {
|
||||
parent.left = replacement;
|
||||
} else {
|
||||
assert (parent.right == node);
|
||||
parent.right = replacement;
|
||||
}
|
||||
} else {
|
||||
int index = node.hash & (table.length - 1);
|
||||
table[index] = replacement;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Rebalances the tree by making any AVL rotations necessary between the
|
||||
* newly-unbalanced node and the tree's root.
|
||||
*
|
||||
* @param insert true if the node was unbalanced by an insert; false if it
|
||||
* was by a removal.
|
||||
*/
|
||||
private void rebalance(Node<K, V> unbalanced, boolean insert) {
|
||||
for (Node<K, V> node = unbalanced; node != null; node = node.parent) {
|
||||
Node<K, V> left = node.left;
|
||||
Node<K, V> right = node.right;
|
||||
int leftHeight = left != null ? left.height : 0;
|
||||
int rightHeight = right != null ? right.height : 0;
|
||||
|
||||
int delta = leftHeight - rightHeight;
|
||||
if (delta == -2) {
|
||||
Node<K, V> rightLeft = right.left;
|
||||
Node<K, V> rightRight = right.right;
|
||||
int rightRightHeight = rightRight != null ? rightRight.height : 0;
|
||||
int rightLeftHeight = rightLeft != null ? rightLeft.height : 0;
|
||||
|
||||
int rightDelta = rightLeftHeight - rightRightHeight;
|
||||
if (rightDelta == -1 || (rightDelta == 0 && !insert)) {
|
||||
rotateLeft(node); // AVL right right
|
||||
} else {
|
||||
assert (rightDelta == 1);
|
||||
rotateRight(right); // AVL right left
|
||||
rotateLeft(node);
|
||||
}
|
||||
if (insert) {
|
||||
break; // no further rotations will be necessary
|
||||
}
|
||||
|
||||
} else if (delta == 2) {
|
||||
Node<K, V> leftLeft = left.left;
|
||||
Node<K, V> leftRight = left.right;
|
||||
int leftRightHeight = leftRight != null ? leftRight.height : 0;
|
||||
int leftLeftHeight = leftLeft != null ? leftLeft.height : 0;
|
||||
|
||||
int leftDelta = leftLeftHeight - leftRightHeight;
|
||||
if (leftDelta == 1 || (leftDelta == 0 && !insert)) {
|
||||
rotateRight(node); // AVL left left
|
||||
} else {
|
||||
assert (leftDelta == -1);
|
||||
rotateLeft(left); // AVL left right
|
||||
rotateRight(node);
|
||||
}
|
||||
if (insert) {
|
||||
break; // no further rotations will be necessary
|
||||
}
|
||||
|
||||
} else if (delta == 0) {
|
||||
node.height = leftHeight + 1; // leftHeight == rightHeight
|
||||
if (insert) {
|
||||
break; // the insert caused balance, so rebalancing is done!
|
||||
}
|
||||
|
||||
} else {
|
||||
assert (delta == -1 || delta == 1);
|
||||
node.height = Math.max(leftHeight, rightHeight) + 1;
|
||||
if (!insert) {
|
||||
break; // the height hasn't changed, so rebalancing is done!
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Rotates the subtree so that its root's right child is the new root.
|
||||
*/
|
||||
private void rotateLeft(Node<K, V> root) {
|
||||
Node<K, V> left = root.left;
|
||||
Node<K, V> pivot = root.right;
|
||||
Node<K, V> pivotLeft = pivot.left;
|
||||
Node<K, V> pivotRight = pivot.right;
|
||||
|
||||
// move the pivot's left child to the root's right
|
||||
root.right = pivotLeft;
|
||||
if (pivotLeft != null) {
|
||||
pivotLeft.parent = root;
|
||||
}
|
||||
|
||||
replaceInParent(root, pivot);
|
||||
|
||||
// move the root to the pivot's left
|
||||
pivot.left = root;
|
||||
root.parent = pivot;
|
||||
|
||||
// fix heights
|
||||
root.height = Math.max(left != null ? left.height : 0,
|
||||
pivotLeft != null ? pivotLeft.height : 0) + 1;
|
||||
pivot.height = Math.max(root.height,
|
||||
pivotRight != null ? pivotRight.height : 0) + 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Rotates the subtree so that its root's left child is the new root.
|
||||
*/
|
||||
private void rotateRight(Node<K, V> root) {
|
||||
Node<K, V> pivot = root.left;
|
||||
Node<K, V> right = root.right;
|
||||
Node<K, V> pivotLeft = pivot.left;
|
||||
Node<K, V> pivotRight = pivot.right;
|
||||
|
||||
// move the pivot's right child to the root's left
|
||||
root.left = pivotRight;
|
||||
if (pivotRight != null) {
|
||||
pivotRight.parent = root;
|
||||
}
|
||||
|
||||
replaceInParent(root, pivot);
|
||||
|
||||
// move the root to the pivot's right
|
||||
pivot.right = root;
|
||||
root.parent = pivot;
|
||||
|
||||
// fixup heights
|
||||
root.height = Math.max(right != null ? right.height : 0,
|
||||
pivotRight != null ? pivotRight.height : 0) + 1;
|
||||
pivot.height = Math.max(root.height,
|
||||
pivotLeft != null ? pivotLeft.height : 0) + 1;
|
||||
}
|
||||
|
||||
private EntrySet entrySet;
|
||||
private KeySet keySet;
|
||||
|
||||
@Override public Set<Entry<K, V>> entrySet() {
|
||||
EntrySet result = entrySet;
|
||||
return result != null ? result : (entrySet = new EntrySet());
|
||||
}
|
||||
|
||||
@Override public Set<K> keySet() {
|
||||
KeySet result = keySet;
|
||||
return result != null ? result : (keySet = new KeySet());
|
||||
}
|
||||
|
||||
static final class Node<K, V> implements Entry<K, V> {
|
||||
Node<K, V> parent;
|
||||
Node<K, V> left;
|
||||
Node<K, V> right;
|
||||
Node<K, V> next;
|
||||
Node<K, V> prev;
|
||||
final K key;
|
||||
final int hash;
|
||||
V value;
|
||||
int height;
|
||||
|
||||
/** Create the header entry */
|
||||
Node() {
|
||||
key = null;
|
||||
hash = -1;
|
||||
next = prev = this;
|
||||
}
|
||||
|
||||
/** Create a regular entry */
|
||||
Node(Node<K, V> parent, K key, int hash, Node<K, V> next, Node<K, V> prev) {
|
||||
this.parent = parent;
|
||||
this.key = key;
|
||||
this.hash = hash;
|
||||
this.height = 1;
|
||||
this.next = next;
|
||||
this.prev = prev;
|
||||
prev.next = this;
|
||||
next.prev = this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public K getKey() {
|
||||
return key;
|
||||
}
|
||||
|
||||
@Override
|
||||
public V getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public V setValue(V value) {
|
||||
V oldValue = this.value;
|
||||
this.value = value;
|
||||
return oldValue;
|
||||
}
|
||||
|
||||
@SuppressWarnings("rawtypes")
|
||||
@Override public boolean equals(Object o) {
|
||||
if (o instanceof Entry) {
|
||||
Entry other = (Entry) o;
|
||||
return (key == null ? other.getKey() == null : key.equals(other.getKey()))
|
||||
&& (value == null ? other.getValue() == null : value.equals(other.getValue()));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override public int hashCode() {
|
||||
return (key == null ? 0 : key.hashCode())
|
||||
^ (value == null ? 0 : value.hashCode());
|
||||
}
|
||||
|
||||
@Override public String toString() {
|
||||
return key + "=" + value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the first node in this subtree.
|
||||
*/
|
||||
public Node<K, V> first() {
|
||||
Node<K, V> node = this;
|
||||
Node<K, V> child = node.left;
|
||||
while (child != null) {
|
||||
node = child;
|
||||
child = node.left;
|
||||
}
|
||||
return node;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the last node in this subtree.
|
||||
*/
|
||||
public Node<K, V> last() {
|
||||
Node<K, V> node = this;
|
||||
Node<K, V> child = node.right;
|
||||
while (child != null) {
|
||||
node = child;
|
||||
child = node.right;
|
||||
}
|
||||
return node;
|
||||
}
|
||||
}
|
||||
|
||||
private void doubleCapacity() {
|
||||
table = doubleCapacity(table);
|
||||
threshold = (table.length / 2) + (table.length / 4); // 3/4 capacity
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new array containing the same nodes as {@code oldTable}, but with
|
||||
* twice as many trees, each of (approximately) half the previous size.
|
||||
*/
|
||||
static <K, V> Node<K, V>[] doubleCapacity(Node<K, V>[] oldTable) {
|
||||
// TODO: don't do anything if we're already at MAX_CAPACITY
|
||||
int oldCapacity = oldTable.length;
|
||||
@SuppressWarnings("unchecked") // Arrays and generics don't get along.
|
||||
Node<K, V>[] newTable = new Node[oldCapacity * 2];
|
||||
AvlIterator<K, V> iterator = new AvlIterator<K, V>();
|
||||
AvlBuilder<K, V> leftBuilder = new AvlBuilder<K, V>();
|
||||
AvlBuilder<K, V> rightBuilder = new AvlBuilder<K, V>();
|
||||
|
||||
// Split each tree into two trees.
|
||||
for (int i = 0; i < oldCapacity; i++) {
|
||||
Node<K, V> root = oldTable[i];
|
||||
if (root == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Compute the sizes of the left and right trees.
|
||||
iterator.reset(root);
|
||||
int leftSize = 0;
|
||||
int rightSize = 0;
|
||||
for (Node<K, V> node; (node = iterator.next()) != null; ) {
|
||||
if ((node.hash & oldCapacity) == 0) {
|
||||
leftSize++;
|
||||
} else {
|
||||
rightSize++;
|
||||
}
|
||||
}
|
||||
|
||||
// Split the tree into two.
|
||||
leftBuilder.reset(leftSize);
|
||||
rightBuilder.reset(rightSize);
|
||||
iterator.reset(root);
|
||||
for (Node<K, V> node; (node = iterator.next()) != null; ) {
|
||||
if ((node.hash & oldCapacity) == 0) {
|
||||
leftBuilder.add(node);
|
||||
} else {
|
||||
rightBuilder.add(node);
|
||||
}
|
||||
}
|
||||
|
||||
// Populate the enlarged array with these new roots.
|
||||
newTable[i] = leftSize > 0 ? leftBuilder.root() : null;
|
||||
newTable[i + oldCapacity] = rightSize > 0 ? rightBuilder.root() : null;
|
||||
}
|
||||
return newTable;
|
||||
}
|
||||
|
||||
/**
|
||||
* Walks an AVL tree in iteration order. Once a node has been returned, its
|
||||
* left, right and parent links are <strong>no longer used</strong>. For this
|
||||
* reason it is safe to transform these links as you walk a tree.
|
||||
*
|
||||
* <p><strong>Warning:</strong> this iterator is destructive. It clears the
|
||||
* parent node of all nodes in the tree. It is an error to make a partial
|
||||
* iteration of a tree.
|
||||
*/
|
||||
static class AvlIterator<K, V> {
|
||||
/** This stack is a singly linked list, linked by the 'parent' field. */
|
||||
private Node<K, V> stackTop;
|
||||
|
||||
void reset(Node<K, V> root) {
|
||||
Node<K, V> stackTop = null;
|
||||
for (Node<K, V> n = root; n != null; n = n.left) {
|
||||
n.parent = stackTop;
|
||||
stackTop = n; // Stack push.
|
||||
}
|
||||
this.stackTop = stackTop;
|
||||
}
|
||||
|
||||
public Node<K, V> next() {
|
||||
Node<K, V> stackTop = this.stackTop;
|
||||
if (stackTop == null) {
|
||||
return null;
|
||||
}
|
||||
Node<K, V> result = stackTop;
|
||||
stackTop = result.parent;
|
||||
result.parent = null;
|
||||
for (Node<K, V> n = result.right; n != null; n = n.left) {
|
||||
n.parent = stackTop;
|
||||
stackTop = n; // Stack push.
|
||||
}
|
||||
this.stackTop = stackTop;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds AVL trees of a predetermined size by accepting nodes of increasing
|
||||
* value. To use:
|
||||
* <ol>
|
||||
* <li>Call {@link #reset} to initialize the target size <i>size</i>.
|
||||
* <li>Call {@link #add} <i>size</i> times with increasing values.
|
||||
* <li>Call {@link #root} to get the root of the balanced tree.
|
||||
* </ol>
|
||||
*
|
||||
* <p>The returned tree will satisfy the AVL constraint: for every node
|
||||
* <i>N</i>, the height of <i>N.left</i> and <i>N.right</i> is different by at
|
||||
* most 1. It accomplishes this by omitting deepest-level leaf nodes when
|
||||
* building trees whose size isn't a power of 2 minus 1.
|
||||
*
|
||||
* <p>Unlike rebuilding a tree from scratch, this approach requires no value
|
||||
* comparisons. Using this class to create a tree of size <i>S</i> is
|
||||
* {@code O(S)}.
|
||||
*/
|
||||
final static class AvlBuilder<K, V> {
|
||||
/** This stack is a singly linked list, linked by the 'parent' field. */
|
||||
private Node<K, V> stack;
|
||||
private int leavesToSkip;
|
||||
private int leavesSkipped;
|
||||
private int size;
|
||||
|
||||
void reset(int targetSize) {
|
||||
// compute the target tree size. This is a power of 2 minus one, like 15 or 31.
|
||||
int treeCapacity = Integer.highestOneBit(targetSize) * 2 - 1;
|
||||
leavesToSkip = treeCapacity - targetSize;
|
||||
size = 0;
|
||||
leavesSkipped = 0;
|
||||
stack = null;
|
||||
}
|
||||
|
||||
void add(Node<K, V> node) {
|
||||
node.left = node.parent = node.right = null;
|
||||
node.height = 1;
|
||||
|
||||
// Skip a leaf if necessary.
|
||||
if (leavesToSkip > 0 && (size & 1) == 0) {
|
||||
size++;
|
||||
leavesToSkip--;
|
||||
leavesSkipped++;
|
||||
}
|
||||
|
||||
node.parent = stack;
|
||||
stack = node; // Stack push.
|
||||
size++;
|
||||
|
||||
// Skip a leaf if necessary.
|
||||
if (leavesToSkip > 0 && (size & 1) == 0) {
|
||||
size++;
|
||||
leavesToSkip--;
|
||||
leavesSkipped++;
|
||||
}
|
||||
|
||||
/*
|
||||
* Combine 3 nodes into subtrees whenever the size is one less than a
|
||||
* multiple of 4. For example we combine the nodes A, B, C into a
|
||||
* 3-element tree with B as the root.
|
||||
*
|
||||
* Combine two subtrees and a spare single value whenever the size is one
|
||||
* less than a multiple of 8. For example at 8 we may combine subtrees
|
||||
* (A B C) and (E F G) with D as the root to form ((A B C) D (E F G)).
|
||||
*
|
||||
* Just as we combine single nodes when size nears a multiple of 4, and
|
||||
* 3-element trees when size nears a multiple of 8, we combine subtrees of
|
||||
* size (N-1) whenever the total size is 2N-1 whenever N is a power of 2.
|
||||
*/
|
||||
for (int scale = 4; (size & scale - 1) == scale - 1; scale *= 2) {
|
||||
if (leavesSkipped == 0) {
|
||||
// Pop right, center and left, then make center the top of the stack.
|
||||
Node<K, V> right = stack;
|
||||
Node<K, V> center = right.parent;
|
||||
Node<K, V> left = center.parent;
|
||||
center.parent = left.parent;
|
||||
stack = center;
|
||||
// Construct a tree.
|
||||
center.left = left;
|
||||
center.right = right;
|
||||
center.height = right.height + 1;
|
||||
left.parent = center;
|
||||
right.parent = center;
|
||||
} else if (leavesSkipped == 1) {
|
||||
// Pop right and center, then make center the top of the stack.
|
||||
Node<K, V> right = stack;
|
||||
Node<K, V> center = right.parent;
|
||||
stack = center;
|
||||
// Construct a tree with no left child.
|
||||
center.right = right;
|
||||
center.height = right.height + 1;
|
||||
right.parent = center;
|
||||
leavesSkipped = 0;
|
||||
} else if (leavesSkipped == 2) {
|
||||
leavesSkipped = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Node<K, V> root() {
|
||||
Node<K, V> stackTop = this.stack;
|
||||
if (stackTop.parent != null) {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
return stackTop;
|
||||
}
|
||||
}
|
||||
|
||||
private abstract class LinkedTreeMapIterator<T> implements Iterator<T> {
|
||||
Node<K, V> next = header.next;
|
||||
Node<K, V> lastReturned = null;
|
||||
int expectedModCount = modCount;
|
||||
|
||||
@Override
|
||||
public final boolean hasNext() {
|
||||
return next != header;
|
||||
}
|
||||
|
||||
final Node<K, V> nextNode() {
|
||||
Node<K, V> e = next;
|
||||
if (e == header) {
|
||||
throw new NoSuchElementException();
|
||||
}
|
||||
if (modCount != expectedModCount) {
|
||||
throw new ConcurrentModificationException();
|
||||
}
|
||||
next = e.next;
|
||||
return lastReturned = e;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void remove() {
|
||||
if (lastReturned == null) {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
removeInternal(lastReturned, true);
|
||||
lastReturned = null;
|
||||
expectedModCount = modCount;
|
||||
}
|
||||
}
|
||||
|
||||
class EntrySet extends AbstractSet<Entry<K, V>> {
|
||||
@Override public int size() {
|
||||
return size;
|
||||
}
|
||||
|
||||
@Override public Iterator<Entry<K, V>> iterator() {
|
||||
return new LinkedTreeMapIterator<Entry<K, V>>() {
|
||||
@Override
|
||||
public Entry<K, V> next() {
|
||||
return nextNode();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override public boolean contains(Object o) {
|
||||
return o instanceof Entry && findByEntry((Entry<?, ?>) o) != null;
|
||||
}
|
||||
|
||||
@Override public boolean remove(Object o) {
|
||||
if (!(o instanceof Entry)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Node<K, V> node = findByEntry((Entry<?, ?>) o);
|
||||
if (node == null) {
|
||||
return false;
|
||||
}
|
||||
removeInternal(node, true);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override public void clear() {
|
||||
LinkedHashTreeMap.this.clear();
|
||||
}
|
||||
}
|
||||
|
||||
class KeySet extends AbstractSet<K> {
|
||||
@Override public int size() {
|
||||
return size;
|
||||
}
|
||||
|
||||
@Override public Iterator<K> iterator() {
|
||||
return new LinkedTreeMapIterator<K>() {
|
||||
@Override
|
||||
public K next() {
|
||||
return nextNode().key;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override public boolean contains(Object o) {
|
||||
return containsKey(o);
|
||||
}
|
||||
|
||||
@Override public boolean remove(Object key) {
|
||||
return removeInternalByKey(key) != null;
|
||||
}
|
||||
|
||||
@Override public void clear() {
|
||||
LinkedHashTreeMap.this.clear();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* If somebody is unlucky enough to have to serialize one of these, serialize
|
||||
* it as a LinkedHashMap so that they won't need Gson on the other side to
|
||||
* deserialize it. Using serialization defeats our DoS defence, so most apps
|
||||
* shouldn't use it.
|
||||
*/
|
||||
private Object writeReplace() throws ObjectStreamException {
|
||||
return new LinkedHashMap<K, V>(this);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,635 @@
|
||||
/*
|
||||
* Copyright (C) 2010 The Android Open Source Project
|
||||
* Copyright (C) 2012 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.google.gson.internal;
|
||||
|
||||
import java.io.ObjectStreamException;
|
||||
import java.io.Serializable;
|
||||
import java.util.AbstractMap;
|
||||
import java.util.AbstractSet;
|
||||
import java.util.Comparator;
|
||||
import java.util.ConcurrentModificationException;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.NoSuchElementException;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* A map of comparable keys to values. Unlike {@code TreeMap}, this class uses
|
||||
* insertion order for iteration order. Comparison order is only used as an
|
||||
* optimization for efficient insertion and removal.
|
||||
*
|
||||
* <p>This implementation was derived from Android 4.1's TreeMap class.
|
||||
*/
|
||||
public final class LinkedTreeMap<K, V> extends AbstractMap<K, V> implements Serializable {
|
||||
@SuppressWarnings({ "unchecked", "rawtypes" }) // to avoid Comparable<Comparable<Comparable<...>>>
|
||||
private static final Comparator<Comparable> NATURAL_ORDER = new Comparator<Comparable>() {
|
||||
@Override
|
||||
public int compare(Comparable a, Comparable b) {
|
||||
return a.compareTo(b);
|
||||
}
|
||||
};
|
||||
|
||||
Comparator<? super K> comparator;
|
||||
Node<K, V> root;
|
||||
int size = 0;
|
||||
int modCount = 0;
|
||||
|
||||
// Used to preserve iteration order
|
||||
final Node<K, V> header = new Node<K, V>();
|
||||
|
||||
/**
|
||||
* Create a natural order, empty tree map whose keys must be mutually
|
||||
* comparable and non-null.
|
||||
*/
|
||||
@SuppressWarnings("unchecked") // unsafe! this assumes K is comparable
|
||||
public LinkedTreeMap() {
|
||||
this((Comparator<? super K>) NATURAL_ORDER);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a tree map ordered by {@code comparator}. This map's keys may only
|
||||
* be null if {@code comparator} permits.
|
||||
*
|
||||
* @param comparator the comparator to order elements with, or {@code null} to
|
||||
* use the natural ordering.
|
||||
*/
|
||||
@SuppressWarnings({ "unchecked", "rawtypes" }) // unsafe! if comparator is null, this assumes K is comparable
|
||||
public LinkedTreeMap(Comparator<? super K> comparator) {
|
||||
this.comparator = comparator != null
|
||||
? comparator
|
||||
: (Comparator) NATURAL_ORDER;
|
||||
}
|
||||
|
||||
@Override public int size() {
|
||||
return size;
|
||||
}
|
||||
|
||||
@Override public V get(Object key) {
|
||||
Node<K, V> node = findByObject(key);
|
||||
return node != null ? node.value : null;
|
||||
}
|
||||
|
||||
@Override public boolean containsKey(Object key) {
|
||||
return findByObject(key) != null;
|
||||
}
|
||||
|
||||
@Override public V put(K key, V value) {
|
||||
if (key == null) {
|
||||
throw new NullPointerException("key == null");
|
||||
}
|
||||
Node<K, V> created = find(key, true);
|
||||
V result = created.value;
|
||||
created.value = value;
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override public void clear() {
|
||||
root = null;
|
||||
size = 0;
|
||||
modCount++;
|
||||
|
||||
// Clear iteration order
|
||||
Node<K, V> header = this.header;
|
||||
header.next = header.prev = header;
|
||||
}
|
||||
|
||||
@Override public V remove(Object key) {
|
||||
Node<K, V> node = removeInternalByKey(key);
|
||||
return node != null ? node.value : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the node at or adjacent to the given key, creating it if requested.
|
||||
*
|
||||
* @throws ClassCastException if {@code key} and the tree's keys aren't
|
||||
* mutually comparable.
|
||||
*/
|
||||
Node<K, V> find(K key, boolean create) {
|
||||
Comparator<? super K> comparator = this.comparator;
|
||||
Node<K, V> nearest = root;
|
||||
int comparison = 0;
|
||||
|
||||
if (nearest != null) {
|
||||
// Micro-optimization: avoid polymorphic calls to Comparator.compare().
|
||||
@SuppressWarnings("unchecked") // Throws a ClassCastException below if there's trouble.
|
||||
Comparable<Object> comparableKey = (comparator == NATURAL_ORDER)
|
||||
? (Comparable<Object>) key
|
||||
: null;
|
||||
|
||||
while (true) {
|
||||
comparison = (comparableKey != null)
|
||||
? comparableKey.compareTo(nearest.key)
|
||||
: comparator.compare(key, nearest.key);
|
||||
|
||||
// We found the requested key.
|
||||
if (comparison == 0) {
|
||||
return nearest;
|
||||
}
|
||||
|
||||
// If it exists, the key is in a subtree. Go deeper.
|
||||
Node<K, V> child = (comparison < 0) ? nearest.left : nearest.right;
|
||||
if (child == null) {
|
||||
break;
|
||||
}
|
||||
|
||||
nearest = child;
|
||||
}
|
||||
}
|
||||
|
||||
// The key doesn't exist in this tree.
|
||||
if (!create) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Create the node and add it to the tree or the table.
|
||||
Node<K, V> header = this.header;
|
||||
Node<K, V> created;
|
||||
if (nearest == null) {
|
||||
// Check that the value is comparable if we didn't do any comparisons.
|
||||
if (comparator == NATURAL_ORDER && !(key instanceof Comparable)) {
|
||||
throw new ClassCastException(key.getClass().getName() + " is not Comparable");
|
||||
}
|
||||
created = new Node<K, V>(nearest, key, header, header.prev);
|
||||
root = created;
|
||||
} else {
|
||||
created = new Node<K, V>(nearest, key, header, header.prev);
|
||||
if (comparison < 0) { // nearest.key is higher
|
||||
nearest.left = created;
|
||||
} else { // comparison > 0, nearest.key is lower
|
||||
nearest.right = created;
|
||||
}
|
||||
rebalance(nearest, true);
|
||||
}
|
||||
size++;
|
||||
modCount++;
|
||||
|
||||
return created;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
Node<K, V> findByObject(Object key) {
|
||||
try {
|
||||
return key != null ? find((K) key, false) : null;
|
||||
} catch (ClassCastException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns this map's entry that has the same key and value as {@code
|
||||
* entry}, or null if this map has no such entry.
|
||||
*
|
||||
* <p>This method uses the comparator for key equality rather than {@code
|
||||
* equals}. If this map's comparator isn't consistent with equals (such as
|
||||
* {@code String.CASE_INSENSITIVE_ORDER}), then {@code remove()} and {@code
|
||||
* contains()} will violate the collections API.
|
||||
*/
|
||||
Node<K, V> findByEntry(Entry<?, ?> entry) {
|
||||
Node<K, V> mine = findByObject(entry.getKey());
|
||||
boolean valuesEqual = mine != null && equal(mine.value, entry.getValue());
|
||||
return valuesEqual ? mine : null;
|
||||
}
|
||||
|
||||
private boolean equal(Object a, Object b) {
|
||||
return a == b || (a != null && a.equals(b));
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes {@code node} from this tree, rearranging the tree's structure as
|
||||
* necessary.
|
||||
*
|
||||
* @param unlink true to also unlink this node from the iteration linked list.
|
||||
*/
|
||||
void removeInternal(Node<K, V> node, boolean unlink) {
|
||||
if (unlink) {
|
||||
node.prev.next = node.next;
|
||||
node.next.prev = node.prev;
|
||||
}
|
||||
|
||||
Node<K, V> left = node.left;
|
||||
Node<K, V> right = node.right;
|
||||
Node<K, V> originalParent = node.parent;
|
||||
if (left != null && right != null) {
|
||||
|
||||
/*
|
||||
* To remove a node with both left and right subtrees, move an
|
||||
* adjacent node from one of those subtrees into this node's place.
|
||||
*
|
||||
* Removing the adjacent node may change this node's subtrees. This
|
||||
* node may no longer have two subtrees once the adjacent node is
|
||||
* gone!
|
||||
*/
|
||||
|
||||
Node<K, V> adjacent = (left.height > right.height) ? left.last() : right.first();
|
||||
removeInternal(adjacent, false); // takes care of rebalance and size--
|
||||
|
||||
int leftHeight = 0;
|
||||
left = node.left;
|
||||
if (left != null) {
|
||||
leftHeight = left.height;
|
||||
adjacent.left = left;
|
||||
left.parent = adjacent;
|
||||
node.left = null;
|
||||
}
|
||||
|
||||
int rightHeight = 0;
|
||||
right = node.right;
|
||||
if (right != null) {
|
||||
rightHeight = right.height;
|
||||
adjacent.right = right;
|
||||
right.parent = adjacent;
|
||||
node.right = null;
|
||||
}
|
||||
|
||||
adjacent.height = Math.max(leftHeight, rightHeight) + 1;
|
||||
replaceInParent(node, adjacent);
|
||||
return;
|
||||
} else if (left != null) {
|
||||
replaceInParent(node, left);
|
||||
node.left = null;
|
||||
} else if (right != null) {
|
||||
replaceInParent(node, right);
|
||||
node.right = null;
|
||||
} else {
|
||||
replaceInParent(node, null);
|
||||
}
|
||||
|
||||
rebalance(originalParent, false);
|
||||
size--;
|
||||
modCount++;
|
||||
}
|
||||
|
||||
Node<K, V> removeInternalByKey(Object key) {
|
||||
Node<K, V> node = findByObject(key);
|
||||
if (node != null) {
|
||||
removeInternal(node, true);
|
||||
}
|
||||
return node;
|
||||
}
|
||||
|
||||
private void replaceInParent(Node<K, V> node, Node<K, V> replacement) {
|
||||
Node<K, V> parent = node.parent;
|
||||
node.parent = null;
|
||||
if (replacement != null) {
|
||||
replacement.parent = parent;
|
||||
}
|
||||
|
||||
if (parent != null) {
|
||||
if (parent.left == node) {
|
||||
parent.left = replacement;
|
||||
} else {
|
||||
assert (parent.right == node);
|
||||
parent.right = replacement;
|
||||
}
|
||||
} else {
|
||||
root = replacement;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Rebalances the tree by making any AVL rotations necessary between the
|
||||
* newly-unbalanced node and the tree's root.
|
||||
*
|
||||
* @param insert true if the node was unbalanced by an insert; false if it
|
||||
* was by a removal.
|
||||
*/
|
||||
private void rebalance(Node<K, V> unbalanced, boolean insert) {
|
||||
for (Node<K, V> node = unbalanced; node != null; node = node.parent) {
|
||||
Node<K, V> left = node.left;
|
||||
Node<K, V> right = node.right;
|
||||
int leftHeight = left != null ? left.height : 0;
|
||||
int rightHeight = right != null ? right.height : 0;
|
||||
|
||||
int delta = leftHeight - rightHeight;
|
||||
if (delta == -2) {
|
||||
Node<K, V> rightLeft = right.left;
|
||||
Node<K, V> rightRight = right.right;
|
||||
int rightRightHeight = rightRight != null ? rightRight.height : 0;
|
||||
int rightLeftHeight = rightLeft != null ? rightLeft.height : 0;
|
||||
|
||||
int rightDelta = rightLeftHeight - rightRightHeight;
|
||||
if (rightDelta == -1 || (rightDelta == 0 && !insert)) {
|
||||
rotateLeft(node); // AVL right right
|
||||
} else {
|
||||
assert (rightDelta == 1);
|
||||
rotateRight(right); // AVL right left
|
||||
rotateLeft(node);
|
||||
}
|
||||
if (insert) {
|
||||
break; // no further rotations will be necessary
|
||||
}
|
||||
|
||||
} else if (delta == 2) {
|
||||
Node<K, V> leftLeft = left.left;
|
||||
Node<K, V> leftRight = left.right;
|
||||
int leftRightHeight = leftRight != null ? leftRight.height : 0;
|
||||
int leftLeftHeight = leftLeft != null ? leftLeft.height : 0;
|
||||
|
||||
int leftDelta = leftLeftHeight - leftRightHeight;
|
||||
if (leftDelta == 1 || (leftDelta == 0 && !insert)) {
|
||||
rotateRight(node); // AVL left left
|
||||
} else {
|
||||
assert (leftDelta == -1);
|
||||
rotateLeft(left); // AVL left right
|
||||
rotateRight(node);
|
||||
}
|
||||
if (insert) {
|
||||
break; // no further rotations will be necessary
|
||||
}
|
||||
|
||||
} else if (delta == 0) {
|
||||
node.height = leftHeight + 1; // leftHeight == rightHeight
|
||||
if (insert) {
|
||||
break; // the insert caused balance, so rebalancing is done!
|
||||
}
|
||||
|
||||
} else {
|
||||
assert (delta == -1 || delta == 1);
|
||||
node.height = Math.max(leftHeight, rightHeight) + 1;
|
||||
if (!insert) {
|
||||
break; // the height hasn't changed, so rebalancing is done!
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Rotates the subtree so that its root's right child is the new root.
|
||||
*/
|
||||
private void rotateLeft(Node<K, V> root) {
|
||||
Node<K, V> left = root.left;
|
||||
Node<K, V> pivot = root.right;
|
||||
Node<K, V> pivotLeft = pivot.left;
|
||||
Node<K, V> pivotRight = pivot.right;
|
||||
|
||||
// move the pivot's left child to the root's right
|
||||
root.right = pivotLeft;
|
||||
if (pivotLeft != null) {
|
||||
pivotLeft.parent = root;
|
||||
}
|
||||
|
||||
replaceInParent(root, pivot);
|
||||
|
||||
// move the root to the pivot's left
|
||||
pivot.left = root;
|
||||
root.parent = pivot;
|
||||
|
||||
// fix heights
|
||||
root.height = Math.max(left != null ? left.height : 0,
|
||||
pivotLeft != null ? pivotLeft.height : 0) + 1;
|
||||
pivot.height = Math.max(root.height,
|
||||
pivotRight != null ? pivotRight.height : 0) + 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Rotates the subtree so that its root's left child is the new root.
|
||||
*/
|
||||
private void rotateRight(Node<K, V> root) {
|
||||
Node<K, V> pivot = root.left;
|
||||
Node<K, V> right = root.right;
|
||||
Node<K, V> pivotLeft = pivot.left;
|
||||
Node<K, V> pivotRight = pivot.right;
|
||||
|
||||
// move the pivot's right child to the root's left
|
||||
root.left = pivotRight;
|
||||
if (pivotRight != null) {
|
||||
pivotRight.parent = root;
|
||||
}
|
||||
|
||||
replaceInParent(root, pivot);
|
||||
|
||||
// move the root to the pivot's right
|
||||
pivot.right = root;
|
||||
root.parent = pivot;
|
||||
|
||||
// fixup heights
|
||||
root.height = Math.max(right != null ? right.height : 0,
|
||||
pivotRight != null ? pivotRight.height : 0) + 1;
|
||||
pivot.height = Math.max(root.height,
|
||||
pivotLeft != null ? pivotLeft.height : 0) + 1;
|
||||
}
|
||||
|
||||
private EntrySet entrySet;
|
||||
private KeySet keySet;
|
||||
|
||||
@Override public Set<Entry<K, V>> entrySet() {
|
||||
EntrySet result = entrySet;
|
||||
return result != null ? result : (entrySet = new EntrySet());
|
||||
}
|
||||
|
||||
@Override public Set<K> keySet() {
|
||||
KeySet result = keySet;
|
||||
return result != null ? result : (keySet = new KeySet());
|
||||
}
|
||||
|
||||
static final class Node<K, V> implements Entry<K, V> {
|
||||
Node<K, V> parent;
|
||||
Node<K, V> left;
|
||||
Node<K, V> right;
|
||||
Node<K, V> next;
|
||||
Node<K, V> prev;
|
||||
final K key;
|
||||
V value;
|
||||
int height;
|
||||
|
||||
/** Create the header entry */
|
||||
Node() {
|
||||
key = null;
|
||||
next = prev = this;
|
||||
}
|
||||
|
||||
/** Create a regular entry */
|
||||
Node(Node<K, V> parent, K key, Node<K, V> next, Node<K, V> prev) {
|
||||
this.parent = parent;
|
||||
this.key = key;
|
||||
this.height = 1;
|
||||
this.next = next;
|
||||
this.prev = prev;
|
||||
prev.next = this;
|
||||
next.prev = this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public K getKey() {
|
||||
return key;
|
||||
}
|
||||
|
||||
@Override
|
||||
public V getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public V setValue(V value) {
|
||||
V oldValue = this.value;
|
||||
this.value = value;
|
||||
return oldValue;
|
||||
}
|
||||
|
||||
@SuppressWarnings("rawtypes")
|
||||
@Override public boolean equals(Object o) {
|
||||
if (o instanceof Entry) {
|
||||
Entry other = (Entry) o;
|
||||
return (key == null ? other.getKey() == null : key.equals(other.getKey()))
|
||||
&& (value == null ? other.getValue() == null : value.equals(other.getValue()));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override public int hashCode() {
|
||||
return (key == null ? 0 : key.hashCode())
|
||||
^ (value == null ? 0 : value.hashCode());
|
||||
}
|
||||
|
||||
@Override public String toString() {
|
||||
return key + "=" + value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the first node in this subtree.
|
||||
*/
|
||||
public Node<K, V> first() {
|
||||
Node<K, V> node = this;
|
||||
Node<K, V> child = node.left;
|
||||
while (child != null) {
|
||||
node = child;
|
||||
child = node.left;
|
||||
}
|
||||
return node;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the last node in this subtree.
|
||||
*/
|
||||
public Node<K, V> last() {
|
||||
Node<K, V> node = this;
|
||||
Node<K, V> child = node.right;
|
||||
while (child != null) {
|
||||
node = child;
|
||||
child = node.right;
|
||||
}
|
||||
return node;
|
||||
}
|
||||
}
|
||||
|
||||
private abstract class LinkedTreeMapIterator<T> implements Iterator<T> {
|
||||
Node<K, V> next = header.next;
|
||||
Node<K, V> lastReturned = null;
|
||||
int expectedModCount = modCount;
|
||||
|
||||
@Override
|
||||
public final boolean hasNext() {
|
||||
return next != header;
|
||||
}
|
||||
|
||||
final Node<K, V> nextNode() {
|
||||
Node<K, V> e = next;
|
||||
if (e == header) {
|
||||
throw new NoSuchElementException();
|
||||
}
|
||||
if (modCount != expectedModCount) {
|
||||
throw new ConcurrentModificationException();
|
||||
}
|
||||
next = e.next;
|
||||
return lastReturned = e;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void remove() {
|
||||
if (lastReturned == null) {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
removeInternal(lastReturned, true);
|
||||
lastReturned = null;
|
||||
expectedModCount = modCount;
|
||||
}
|
||||
}
|
||||
|
||||
class EntrySet extends AbstractSet<Entry<K, V>> {
|
||||
@Override public int size() {
|
||||
return size;
|
||||
}
|
||||
|
||||
@Override public Iterator<Entry<K, V>> iterator() {
|
||||
return new LinkedTreeMapIterator<Entry<K, V>>() {
|
||||
@Override
|
||||
public Entry<K, V> next() {
|
||||
return nextNode();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override public boolean contains(Object o) {
|
||||
return o instanceof Entry && findByEntry((Entry<?, ?>) o) != null;
|
||||
}
|
||||
|
||||
@Override public boolean remove(Object o) {
|
||||
if (!(o instanceof Entry)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Node<K, V> node = findByEntry((Entry<?, ?>) o);
|
||||
if (node == null) {
|
||||
return false;
|
||||
}
|
||||
removeInternal(node, true);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override public void clear() {
|
||||
LinkedTreeMap.this.clear();
|
||||
}
|
||||
}
|
||||
|
||||
class KeySet extends AbstractSet<K> {
|
||||
@Override public int size() {
|
||||
return size;
|
||||
}
|
||||
|
||||
@Override public Iterator<K> iterator() {
|
||||
return new LinkedTreeMapIterator<K>() {
|
||||
@Override
|
||||
public K next() {
|
||||
return nextNode().key;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override public boolean contains(Object o) {
|
||||
return containsKey(o);
|
||||
}
|
||||
|
||||
@Override public boolean remove(Object key) {
|
||||
return removeInternalByKey(key) != null;
|
||||
}
|
||||
|
||||
@Override public void clear() {
|
||||
LinkedTreeMap.this.clear();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* If somebody is unlucky enough to have to serialize one of these, serialize
|
||||
* it as a LinkedHashMap so that they won't need Gson on the other side to
|
||||
* deserialize it. Using serialization defeats our DoS defence, so most apps
|
||||
* shouldn't use it.
|
||||
*/
|
||||
private Object writeReplace() throws ObjectStreamException {
|
||||
return new LinkedHashMap<K, V>(this);
|
||||
}
|
||||
}
|
||||
@@ -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.google.gson.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();
|
||||
}
|
||||
@@ -0,0 +1,117 @@
|
||||
/*
|
||||
* 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.google.gson.internal;
|
||||
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,120 @@
|
||||
/*
|
||||
* 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.google.gson.internal;
|
||||
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonIOException;
|
||||
import com.google.gson.JsonNull;
|
||||
import com.google.gson.JsonParseException;
|
||||
import com.google.gson.JsonSyntaxException;
|
||||
import com.google.gson.internal.bind.TypeAdapters;
|
||||
import com.google.gson.stream.JsonReader;
|
||||
import com.google.gson.stream.JsonWriter;
|
||||
import com.google.gson.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;
|
||||
}
|
||||
// The stream ended prematurely so it is likely a syntax error.
|
||||
throw new JsonSyntaxException(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);
|
||||
}
|
||||
|
||||
@SuppressWarnings("resource")
|
||||
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 final 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;
|
||||
@Override
|
||||
public int length() {
|
||||
return chars.length;
|
||||
}
|
||||
@Override
|
||||
public char charAt(int i) {
|
||||
return chars[i];
|
||||
}
|
||||
@Override
|
||||
public CharSequence subSequence(int start, int end) {
|
||||
return new String(chars, start, end - start);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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.google.gson.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);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -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.google.gson.internal.bind;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.TypeAdapter;
|
||||
import com.google.gson.TypeAdapterFactory;
|
||||
import com.google.gson.internal.$Gson$Types;
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
import com.google.gson.stream.JsonReader;
|
||||
import com.google.gson.stream.JsonToken;
|
||||
import com.google.gson.stream.JsonWriter;
|
||||
|
||||
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;
|
||||
|
||||
/**
|
||||
* Adapt an array of objects.
|
||||
*/
|
||||
public final class ArrayTypeAdapter<E> extends TypeAdapter<Object> {
|
||||
public static final TypeAdapterFactory FACTORY = new TypeAdapterFactory() {
|
||||
@Override
|
||||
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||
public <T> TypeAdapter<T> create(Gson gson, 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 = gson.getAdapter(TypeToken.get(componentType));
|
||||
return new ArrayTypeAdapter(
|
||||
gson, componentTypeAdapter, $Gson$Types.getRawType(componentType));
|
||||
}
|
||||
};
|
||||
|
||||
private final Class<E> componentType;
|
||||
private final TypeAdapter<E> componentTypeAdapter;
|
||||
|
||||
public ArrayTypeAdapter(Gson context, TypeAdapter<E> componentTypeAdapter, Class<E> componentType) {
|
||||
this.componentTypeAdapter =
|
||||
new TypeAdapterRuntimeTypeWrapper<E>(context, componentTypeAdapter, componentType);
|
||||
this.componentType = componentType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object read(JsonReader in) throws IOException {
|
||||
if (in.peek() == JsonToken.NULL) {
|
||||
in.nextNull();
|
||||
return null;
|
||||
}
|
||||
|
||||
List<E> list = new ArrayList<E>();
|
||||
in.beginArray();
|
||||
while (in.hasNext()) {
|
||||
E instance = componentTypeAdapter.read(in);
|
||||
list.add(instance);
|
||||
}
|
||||
in.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 out, Object array) throws IOException {
|
||||
if (array == null) {
|
||||
out.nullValue();
|
||||
return;
|
||||
}
|
||||
|
||||
out.beginArray();
|
||||
for (int i = 0, length = Array.getLength(array); i < length; i++) {
|
||||
E value = (E) Array.get(array, i);
|
||||
componentTypeAdapter.write(out, value);
|
||||
}
|
||||
out.endArray();
|
||||
}
|
||||
}
|
||||
@@ -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.google.gson.internal.bind;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.TypeAdapter;
|
||||
import com.google.gson.TypeAdapterFactory;
|
||||
import com.google.gson.internal.$Gson$Types;
|
||||
import com.google.gson.internal.ConstructorConstructor;
|
||||
import com.google.gson.internal.ObjectConstructor;
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
import com.google.gson.stream.JsonReader;
|
||||
import com.google.gson.stream.JsonToken;
|
||||
import com.google.gson.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 TypeAdapterFactory {
|
||||
private final ConstructorConstructor constructorConstructor;
|
||||
|
||||
public CollectionTypeAdapterFactory(ConstructorConstructor constructorConstructor) {
|
||||
this.constructorConstructor = constructorConstructor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> TypeAdapter<T> create(Gson gson, 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 = gson.getAdapter(TypeToken.get(elementType));
|
||||
ObjectConstructor<T> constructor = constructorConstructor.get(typeToken);
|
||||
|
||||
@SuppressWarnings({"unchecked", "rawtypes"}) // create() doesn't define a type parameter
|
||||
TypeAdapter<T> result = new Adapter(gson, elementType, elementTypeAdapter, constructor);
|
||||
return result;
|
||||
}
|
||||
|
||||
private static final class Adapter<E> extends TypeAdapter<Collection<E>> {
|
||||
private final TypeAdapter<E> elementTypeAdapter;
|
||||
private final ObjectConstructor<? extends Collection<E>> constructor;
|
||||
|
||||
public Adapter(Gson context, Type elementType,
|
||||
TypeAdapter<E> elementTypeAdapter,
|
||||
ObjectConstructor<? extends Collection<E>> constructor) {
|
||||
this.elementTypeAdapter =
|
||||
new TypeAdapterRuntimeTypeWrapper<E>(context, elementTypeAdapter, elementType);
|
||||
this.constructor = constructor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<E> read(JsonReader in) throws IOException {
|
||||
if (in.peek() == JsonToken.NULL) {
|
||||
in.nextNull();
|
||||
return null;
|
||||
}
|
||||
|
||||
Collection<E> collection = constructor.construct();
|
||||
in.beginArray();
|
||||
while (in.hasNext()) {
|
||||
E instance = elementTypeAdapter.read(in);
|
||||
collection.add(instance);
|
||||
}
|
||||
in.endArray();
|
||||
return collection;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(JsonWriter out, Collection<E> collection) throws IOException {
|
||||
if (collection == null) {
|
||||
out.nullValue();
|
||||
return;
|
||||
}
|
||||
|
||||
out.beginArray();
|
||||
for (E element : collection) {
|
||||
elementTypeAdapter.write(out, element);
|
||||
}
|
||||
out.endArray();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
/*
|
||||
* 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.google.gson.internal.bind;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.JsonSyntaxException;
|
||||
import com.google.gson.TypeAdapter;
|
||||
import com.google.gson.TypeAdapterFactory;
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
import com.google.gson.stream.JsonReader;
|
||||
import com.google.gson.stream.JsonToken;
|
||||
import com.google.gson.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 TypeAdapterFactory FACTORY = new TypeAdapterFactory() {
|
||||
@Override
|
||||
@SuppressWarnings("unchecked") // we use a runtime check to make sure the 'T's equal
|
||||
public <T> TypeAdapter<T> create(Gson gson, 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 in) throws IOException {
|
||||
if (in.peek() == JsonToken.NULL) {
|
||||
in.nextNull();
|
||||
return null;
|
||||
}
|
||||
return deserializeToDate(in.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 out, Date value) throws IOException {
|
||||
if (value == null) {
|
||||
out.nullValue();
|
||||
return;
|
||||
}
|
||||
String dateFormatAsString = enUsFormat.format(value);
|
||||
out.value(dateFormatAsString);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
* Copyright (C) 2014 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.google.gson.internal.bind;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.TypeAdapter;
|
||||
import com.google.gson.TypeAdapterFactory;
|
||||
import com.google.gson.annotations.JsonAdapter;
|
||||
import com.google.gson.internal.ConstructorConstructor;
|
||||
import com.google.gson.internal.ObjectConstructor;
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
|
||||
/**
|
||||
* Given a type T, looks for the annotation {@link JsonAdapter} and uses an instance of the
|
||||
* specified class as the default type adapter.
|
||||
*
|
||||
* @since 2.3
|
||||
*/
|
||||
public final class JsonAdapterAnnotationTypeAdapterFactory implements TypeAdapterFactory {
|
||||
|
||||
private final ConstructorConstructor constructorConstructor;
|
||||
|
||||
public JsonAdapterAnnotationTypeAdapterFactory(ConstructorConstructor constructorConstructor) {
|
||||
this.constructorConstructor = constructorConstructor;
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings({ "rawtypes", "unchecked" })
|
||||
public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> targetType) {
|
||||
Class<? super T> clazz = targetType.getRawType();
|
||||
JsonAdapter annotation = clazz.getAnnotation(JsonAdapter.class);
|
||||
if (annotation == null) return null;
|
||||
TypeAdapter adapter = getAnnotationTypeAdapter(gson, constructorConstructor, annotation);
|
||||
return adapter;
|
||||
}
|
||||
|
||||
static TypeAdapter<?> getAnnotationTypeAdapter(Gson gson,
|
||||
ConstructorConstructor constructorConstructor, JsonAdapter annotation) {
|
||||
Class<? extends TypeAdapter<?>> adapterClass = annotation.value();
|
||||
ObjectConstructor<? extends TypeAdapter<?>> constructor =
|
||||
constructorConstructor.get(TypeToken.get(adapterClass));
|
||||
TypeAdapter<?> adapter = constructor.construct();
|
||||
Gson.$$Internal.addGeneratedTypeAdapter(gson, adapter);
|
||||
return adapter;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,226 @@
|
||||
/*
|
||||
* 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.google.gson.internal.bind;
|
||||
|
||||
import com.google.gson.JsonArray;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonNull;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonPrimitive;
|
||||
import com.google.gson.stream.JsonReader;
|
||||
import com.google.gson.stream.JsonToken;
|
||||
import java.io.IOException;
|
||||
import java.io.Reader;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* This reader walks the elements of a JsonElement as if it was coming from a
|
||||
* character stream.
|
||||
*
|
||||
* @author Jesse Wilson
|
||||
*/
|
||||
public final class JsonTreeReader 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 JsonTreeReader(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();
|
||||
}
|
||||
|
||||
public void promoteNameToValue() throws IOException {
|
||||
expect(JsonToken.NAME);
|
||||
Iterator<?> i = (Iterator<?>) peekStack();
|
||||
Map.Entry<?, ?> entry = (Map.Entry<?, ?>) i.next();
|
||||
stack.add(entry.getValue());
|
||||
stack.add(new JsonPrimitive((String)entry.getKey()));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,200 @@
|
||||
/*
|
||||
* 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.google.gson.internal.bind;
|
||||
|
||||
import com.google.gson.JsonArray;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonNull;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonPrimitive;
|
||||
import com.google.gson.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 JsonTreeWriter 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 JsonTreeWriter() {
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,267 @@
|
||||
/*
|
||||
* 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.google.gson.internal.bind;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonPrimitive;
|
||||
import com.google.gson.JsonSyntaxException;
|
||||
import com.google.gson.TypeAdapter;
|
||||
import com.google.gson.TypeAdapterFactory;
|
||||
import com.google.gson.internal.$Gson$Types;
|
||||
import com.google.gson.internal.ConstructorConstructor;
|
||||
import com.google.gson.internal.JsonReaderInternalAccess;
|
||||
import com.google.gson.internal.ObjectConstructor;
|
||||
import com.google.gson.internal.Streams;
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
import com.google.gson.stream.JsonReader;
|
||||
import com.google.gson.stream.JsonToken;
|
||||
import com.google.gson.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 TypeAdapterFactory {
|
||||
private final ConstructorConstructor constructorConstructor;
|
||||
private final boolean complexMapKeySerialization;
|
||||
|
||||
public MapTypeAdapterFactory(ConstructorConstructor constructorConstructor,
|
||||
boolean complexMapKeySerialization) {
|
||||
this.constructorConstructor = constructorConstructor;
|
||||
this.complexMapKeySerialization = complexMapKeySerialization;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> TypeAdapter<T> create(Gson gson, 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(gson, keyAndValueTypes[0]);
|
||||
TypeAdapter<?> valueAdapter = gson.getAdapter(TypeToken.get(keyAndValueTypes[1]));
|
||||
ObjectConstructor<T> constructor = constructorConstructor.get(typeToken);
|
||||
|
||||
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||
// we don't define a type parameter for the key or value types
|
||||
TypeAdapter<T> result = new Adapter(gson, keyAndValueTypes[0], keyAdapter,
|
||||
keyAndValueTypes[1], valueAdapter, constructor);
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a type adapter that writes the value as a string.
|
||||
*/
|
||||
private TypeAdapter<?> getKeyAdapter(Gson 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(Gson 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;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<K, V> read(JsonReader in) throws IOException {
|
||||
JsonToken peek = in.peek();
|
||||
if (peek == JsonToken.NULL) {
|
||||
in.nextNull();
|
||||
return null;
|
||||
}
|
||||
|
||||
Map<K, V> map = constructor.construct();
|
||||
|
||||
if (peek == JsonToken.BEGIN_ARRAY) {
|
||||
in.beginArray();
|
||||
while (in.hasNext()) {
|
||||
in.beginArray(); // entry array
|
||||
K key = keyTypeAdapter.read(in);
|
||||
V value = valueTypeAdapter.read(in);
|
||||
V replaced = map.put(key, value);
|
||||
if (replaced != null) {
|
||||
throw new JsonSyntaxException("duplicate key: " + key);
|
||||
}
|
||||
in.endArray();
|
||||
}
|
||||
in.endArray();
|
||||
} else {
|
||||
in.beginObject();
|
||||
while (in.hasNext()) {
|
||||
JsonReaderInternalAccess.INSTANCE.promoteNameToValue(in);
|
||||
K key = keyTypeAdapter.read(in);
|
||||
V value = valueTypeAdapter.read(in);
|
||||
V replaced = map.put(key, value);
|
||||
if (replaced != null) {
|
||||
throw new JsonSyntaxException("duplicate key: " + key);
|
||||
}
|
||||
}
|
||||
in.endObject();
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(JsonWriter out, Map<K, V> map) throws IOException {
|
||||
if (map == null) {
|
||||
out.nullValue();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!complexMapKeySerialization) {
|
||||
out.beginObject();
|
||||
for (Map.Entry<K, V> entry : map.entrySet()) {
|
||||
out.name(String.valueOf(entry.getKey()));
|
||||
valueTypeAdapter.write(out, entry.getValue());
|
||||
}
|
||||
out.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.toJsonTree(entry.getKey());
|
||||
keys.add(keyElement);
|
||||
values.add(entry.getValue());
|
||||
hasComplexKeys |= keyElement.isJsonArray() || keyElement.isJsonObject();
|
||||
}
|
||||
|
||||
if (hasComplexKeys) {
|
||||
out.beginArray();
|
||||
for (int i = 0; i < keys.size(); i++) {
|
||||
out.beginArray(); // entry array
|
||||
Streams.write(keys.get(i), out);
|
||||
valueTypeAdapter.write(out, values.get(i));
|
||||
out.endArray();
|
||||
}
|
||||
out.endArray();
|
||||
} else {
|
||||
out.beginObject();
|
||||
for (int i = 0; i < keys.size(); i++) {
|
||||
JsonElement keyElement = keys.get(i);
|
||||
out.name(keyToString(keyElement));
|
||||
valueTypeAdapter.write(out, values.get(i));
|
||||
}
|
||||
out.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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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.google.gson.internal.bind;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.TypeAdapter;
|
||||
import com.google.gson.TypeAdapterFactory;
|
||||
import com.google.gson.internal.LinkedTreeMap;
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
import com.google.gson.stream.JsonReader;
|
||||
import com.google.gson.stream.JsonToken;
|
||||
import com.google.gson.stream.JsonWriter;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
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 TypeAdapterFactory FACTORY = new TypeAdapterFactory() {
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type) {
|
||||
if (type.getRawType() == Object.class) {
|
||||
return (TypeAdapter<T>) new ObjectTypeAdapter(gson);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
private final Gson gson;
|
||||
|
||||
private ObjectTypeAdapter(Gson gson) {
|
||||
this.gson = gson;
|
||||
}
|
||||
|
||||
@Override public Object read(JsonReader in) throws IOException {
|
||||
JsonToken token = in.peek();
|
||||
switch (token) {
|
||||
case BEGIN_ARRAY:
|
||||
List<Object> list = new ArrayList<Object>();
|
||||
in.beginArray();
|
||||
while (in.hasNext()) {
|
||||
list.add(read(in));
|
||||
}
|
||||
in.endArray();
|
||||
return list;
|
||||
|
||||
case BEGIN_OBJECT:
|
||||
Map<String, Object> map = new LinkedTreeMap<String, Object>();
|
||||
in.beginObject();
|
||||
while (in.hasNext()) {
|
||||
map.put(in.nextName(), read(in));
|
||||
}
|
||||
in.endObject();
|
||||
return map;
|
||||
|
||||
case STRING:
|
||||
return in.nextString();
|
||||
|
||||
case NUMBER:
|
||||
return in.nextDouble();
|
||||
|
||||
case BOOLEAN:
|
||||
return in.nextBoolean();
|
||||
|
||||
case NULL:
|
||||
in.nextNull();
|
||||
return null;
|
||||
|
||||
default:
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override public void write(JsonWriter out, Object value) throws IOException {
|
||||
if (value == null) {
|
||||
out.nullValue();
|
||||
return;
|
||||
}
|
||||
|
||||
TypeAdapter<Object> typeAdapter = (TypeAdapter<Object>) gson.getAdapter(value.getClass());
|
||||
if (typeAdapter instanceof ObjectTypeAdapter) {
|
||||
out.beginObject();
|
||||
out.endObject();
|
||||
return;
|
||||
}
|
||||
|
||||
typeAdapter.write(out, value);
|
||||
}
|
||||
}
|
||||
@@ -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.google.gson.internal.bind;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import com.google.gson.FieldNamingStrategy;
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.JsonSyntaxException;
|
||||
import com.google.gson.TypeAdapter;
|
||||
import com.google.gson.TypeAdapterFactory;
|
||||
import com.google.gson.annotations.JsonAdapter;
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
import com.google.gson.internal.$Gson$Types;
|
||||
import com.google.gson.internal.ConstructorConstructor;
|
||||
import com.google.gson.internal.Excluder;
|
||||
import com.google.gson.internal.ObjectConstructor;
|
||||
import com.google.gson.internal.Primitives;
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
import com.google.gson.stream.JsonReader;
|
||||
import com.google.gson.stream.JsonToken;
|
||||
import com.google.gson.stream.JsonWriter;
|
||||
|
||||
/**
|
||||
* Type adapter that reflects over the fields and methods of a class.
|
||||
*/
|
||||
public final class ReflectiveTypeAdapterFactory implements TypeAdapterFactory {
|
||||
private final ConstructorConstructor constructorConstructor;
|
||||
private final FieldNamingStrategy fieldNamingPolicy;
|
||||
private final Excluder excluder;
|
||||
|
||||
public ReflectiveTypeAdapterFactory(ConstructorConstructor constructorConstructor,
|
||||
FieldNamingStrategy fieldNamingPolicy, Excluder excluder) {
|
||||
this.constructorConstructor = constructorConstructor;
|
||||
this.fieldNamingPolicy = fieldNamingPolicy;
|
||||
this.excluder = excluder;
|
||||
}
|
||||
|
||||
public boolean excludeField(Field f, boolean serialize) {
|
||||
return !excluder.excludeClass(f.getType(), serialize) && !excluder.excludeField(f, serialize);
|
||||
}
|
||||
|
||||
private String getFieldName(Field f) {
|
||||
SerializedName serializedName = f.getAnnotation(SerializedName.class);
|
||||
return serializedName == null ? fieldNamingPolicy.translateName(f) : serializedName.value();
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> TypeAdapter<T> create(Gson gson, 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.get(type);
|
||||
Adapter<T> adapter = new Adapter<T>(constructor, getBoundFields(gson, type, raw));
|
||||
Gson.$$Internal.addGeneratedTypeAdapter(gson, adapter);
|
||||
return adapter;
|
||||
}
|
||||
|
||||
private ReflectiveTypeAdapterFactory.BoundField createBoundField(
|
||||
final Gson context, final Field field, final String name,
|
||||
final TypeToken<?> fieldType, boolean serialize, boolean deserialize) {
|
||||
final boolean isPrimitive = Primitives.isPrimitive(fieldType.getRawType());
|
||||
// special casing primitives here saves ~5% on Android...
|
||||
return new ReflectiveTypeAdapterFactory.BoundField(name, serialize, deserialize) {
|
||||
final TypeAdapter<?> typeAdapter = getFieldAdapter(context, field, fieldType);
|
||||
@SuppressWarnings({"unchecked", "rawtypes"}) // the type adapter and field type always agree
|
||||
@Override void write(JsonWriter writer, Object value)
|
||||
throws IOException, IllegalAccessException {
|
||||
Object fieldValue = field.get(value);
|
||||
TypeAdapter t =
|
||||
new TypeAdapterRuntimeTypeWrapper(context, this.typeAdapter, fieldType.getType());
|
||||
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 TypeAdapter<?> getFieldAdapter(Gson gson, Field field, TypeToken<?> fieldType) {
|
||||
TypeAdapter<?> adapter = gson.getAdapter(fieldType);
|
||||
boolean generatedAdapter = Gson.$$Internal.isGeneratedTypeAdapter(gson, adapter);
|
||||
if (generatedAdapter && field.isAnnotationPresent(JsonAdapter.class)) {
|
||||
JsonAdapter annotation = field.getAnnotation(JsonAdapter.class);
|
||||
return JsonAdapterAnnotationTypeAdapterFactory.getAnnotationTypeAdapter(
|
||||
gson, constructorConstructor, annotation);
|
||||
}
|
||||
return adapter;
|
||||
}
|
||||
|
||||
private Map<String, BoundField> getBoundFields(Gson 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();
|
||||
for (Field field : fields) {
|
||||
boolean serialize = excludeField(field, true);
|
||||
boolean deserialize = excludeField(field, false);
|
||||
if (!serialize && !deserialize) {
|
||||
continue;
|
||||
}
|
||||
field.setAccessible(true);
|
||||
Type fieldType = $Gson$Types.resolve(type.getType(), raw, field.getGenericType());
|
||||
BoundField boundField = createBoundField(context, field, getFieldName(field),
|
||||
TypeToken.get(fieldType), serialize, deserialize);
|
||||
BoundField previous = result.put(boundField.name, boundField);
|
||||
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 static 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 in) throws IOException {
|
||||
if (in.peek() == JsonToken.NULL) {
|
||||
in.nextNull();
|
||||
return null;
|
||||
}
|
||||
|
||||
T instance = constructor.construct();
|
||||
|
||||
try {
|
||||
in.beginObject();
|
||||
while (in.hasNext()) {
|
||||
String name = in.nextName();
|
||||
BoundField field = boundFields.get(name);
|
||||
if (field == null || !field.deserialized) {
|
||||
in.skipValue();
|
||||
} else {
|
||||
field.read(in, instance);
|
||||
}
|
||||
}
|
||||
} catch (IllegalStateException e) {
|
||||
throw new JsonSyntaxException(e);
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new AssertionError(e);
|
||||
}
|
||||
in.endObject();
|
||||
return instance;
|
||||
}
|
||||
|
||||
@Override public void write(JsonWriter out, T value) throws IOException {
|
||||
if (value == null) {
|
||||
out.nullValue();
|
||||
return;
|
||||
}
|
||||
|
||||
out.beginObject();
|
||||
try {
|
||||
for (BoundField boundField : boundFields.values()) {
|
||||
if (boundField.serialized) {
|
||||
out.name(boundField.name);
|
||||
boundField.write(out, value);
|
||||
}
|
||||
}
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new AssertionError();
|
||||
}
|
||||
out.endObject();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
* 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.google.gson.internal.bind;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.JsonSyntaxException;
|
||||
import com.google.gson.TypeAdapter;
|
||||
import com.google.gson.TypeAdapterFactory;
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
import com.google.gson.stream.JsonReader;
|
||||
import com.google.gson.stream.JsonToken;
|
||||
import com.google.gson.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 TypeAdapterFactory FACTORY = new TypeAdapterFactory() {
|
||||
@Override
|
||||
@SuppressWarnings("unchecked") // we use a runtime check to make sure the 'T's equal
|
||||
public <T> TypeAdapter<T> create(Gson gson, 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 in) throws IOException {
|
||||
if (in.peek() == JsonToken.NULL) {
|
||||
in.nextNull();
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
final long utilDate = format.parse(in.nextString()).getTime();
|
||||
return new java.sql.Date(utilDate);
|
||||
} catch (ParseException e) {
|
||||
throw new JsonSyntaxException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void write(JsonWriter out, java.sql.Date value) throws IOException {
|
||||
out.value(value == null ? null : format.format(value));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
/*
|
||||
* 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.google.gson.internal.bind;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.JsonSyntaxException;
|
||||
import com.google.gson.TypeAdapter;
|
||||
import com.google.gson.TypeAdapterFactory;
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
import com.google.gson.stream.JsonReader;
|
||||
import com.google.gson.stream.JsonToken;
|
||||
import com.google.gson.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 TypeAdapterFactory FACTORY = new TypeAdapterFactory() {
|
||||
@Override
|
||||
@SuppressWarnings("unchecked") // we use a runtime check to make sure the 'T's equal
|
||||
public <T> TypeAdapter<T> create(Gson gson, 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 in) throws IOException {
|
||||
if (in.peek() == JsonToken.NULL) {
|
||||
in.nextNull();
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
Date date = format.parse(in.nextString());
|
||||
return new Time(date.getTime());
|
||||
} catch (ParseException e) {
|
||||
throw new JsonSyntaxException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override public synchronized void write(JsonWriter out, Time value) throws IOException {
|
||||
out.value(value == null ? null : format.format(value));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
/*
|
||||
* 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.google.gson.internal.bind;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.TypeAdapter;
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
import com.google.gson.stream.JsonReader;
|
||||
import com.google.gson.stream.JsonWriter;
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Type;
|
||||
import java.lang.reflect.TypeVariable;
|
||||
|
||||
final class TypeAdapterRuntimeTypeWrapper<T> extends TypeAdapter<T> {
|
||||
private final Gson context;
|
||||
private final TypeAdapter<T> delegate;
|
||||
private final Type type;
|
||||
|
||||
TypeAdapterRuntimeTypeWrapper(Gson context, TypeAdapter<T> delegate, Type type) {
|
||||
this.context = context;
|
||||
this.delegate = delegate;
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
@Override
|
||||
public T read(JsonReader in) throws IOException {
|
||||
return delegate.read(in);
|
||||
}
|
||||
|
||||
@SuppressWarnings({"rawtypes", "unchecked"})
|
||||
@Override
|
||||
public void write(JsonWriter out, 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 = 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(out, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds a compatible runtime type if it is more specific
|
||||
*/
|
||||
private Type getRuntimeTypeIfMoreSpecific(Type type, Object value) {
|
||||
if (value != null
|
||||
&& (type == Object.class || type instanceof TypeVariable<?> || type instanceof Class<?>)) {
|
||||
type = value.getClass();
|
||||
}
|
||||
return type;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,843 @@
|
||||
/*
|
||||
* 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.google.gson.internal.bind;
|
||||
|
||||
import com.google.gson.TypeAdapterFactory;
|
||||
import java.io.IOException;
|
||||
import java.math.BigDecimal;
|
||||
import java.math.BigInteger;
|
||||
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.HashMap;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.StringTokenizer;
|
||||
import java.util.UUID;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.JsonArray;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonIOException;
|
||||
import com.google.gson.JsonNull;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonPrimitive;
|
||||
import com.google.gson.JsonSyntaxException;
|
||||
import com.google.gson.TypeAdapter;
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
import com.google.gson.internal.LazilyParsedNumber;
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
import com.google.gson.stream.JsonReader;
|
||||
import com.google.gson.stream.JsonToken;
|
||||
import com.google.gson.stream.JsonWriter;
|
||||
|
||||
/**
|
||||
* Type adapters for basic types.
|
||||
*/
|
||||
public final class TypeAdapters {
|
||||
private TypeAdapters() {}
|
||||
|
||||
@SuppressWarnings("rawtypes")
|
||||
public static final TypeAdapter<Class> CLASS = new TypeAdapter<Class>() {
|
||||
@Override
|
||||
public void write(JsonWriter out, Class value) throws IOException {
|
||||
if (value == null) {
|
||||
out.nullValue();
|
||||
} else {
|
||||
throw new UnsupportedOperationException("Attempted to serialize java.lang.Class: "
|
||||
+ value.getName() + ". Forgot to register a type adapter?");
|
||||
}
|
||||
}
|
||||
@Override
|
||||
public Class read(JsonReader in) throws IOException {
|
||||
if (in.peek() == JsonToken.NULL) {
|
||||
in.nextNull();
|
||||
return null;
|
||||
} else {
|
||||
throw new UnsupportedOperationException(
|
||||
"Attempted to deserialize a java.lang.Class. Forgot to register a type adapter?");
|
||||
}
|
||||
}
|
||||
};
|
||||
public static final TypeAdapterFactory CLASS_FACTORY = newFactory(Class.class, CLASS);
|
||||
|
||||
public static final TypeAdapter<BitSet> BIT_SET = new TypeAdapter<BitSet>() {
|
||||
@Override
|
||||
public BitSet read(JsonReader in) throws IOException {
|
||||
if (in.peek() == JsonToken.NULL) {
|
||||
in.nextNull();
|
||||
return null;
|
||||
}
|
||||
|
||||
BitSet bitset = new BitSet();
|
||||
in.beginArray();
|
||||
int i = 0;
|
||||
JsonToken tokenType = in.peek();
|
||||
while (tokenType != JsonToken.END_ARRAY) {
|
||||
boolean set;
|
||||
switch (tokenType) {
|
||||
case NUMBER:
|
||||
set = in.nextInt() != 0;
|
||||
break;
|
||||
case BOOLEAN:
|
||||
set = in.nextBoolean();
|
||||
break;
|
||||
case STRING:
|
||||
String stringValue = in.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 = in.peek();
|
||||
}
|
||||
in.endArray();
|
||||
return bitset;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(JsonWriter out, BitSet src) throws IOException {
|
||||
if (src == null) {
|
||||
out.nullValue();
|
||||
return;
|
||||
}
|
||||
|
||||
out.beginArray();
|
||||
for (int i = 0; i < src.length(); i++) {
|
||||
int value = (src.get(i)) ? 1 : 0;
|
||||
out.value(value);
|
||||
}
|
||||
out.endArray();
|
||||
}
|
||||
};
|
||||
|
||||
public static final TypeAdapterFactory BIT_SET_FACTORY = newFactory(BitSet.class, BIT_SET);
|
||||
|
||||
public static final TypeAdapter<Boolean> BOOLEAN = new TypeAdapter<Boolean>() {
|
||||
@Override
|
||||
public Boolean read(JsonReader in) throws IOException {
|
||||
if (in.peek() == JsonToken.NULL) {
|
||||
in.nextNull();
|
||||
return null;
|
||||
} else if (in.peek() == JsonToken.STRING) {
|
||||
// support strings for compatibility with GSON 1.7
|
||||
return Boolean.parseBoolean(in.nextString());
|
||||
}
|
||||
return in.nextBoolean();
|
||||
}
|
||||
@Override
|
||||
public void write(JsonWriter out, Boolean value) throws IOException {
|
||||
if (value == null) {
|
||||
out.nullValue();
|
||||
return;
|
||||
}
|
||||
out.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 in) throws IOException {
|
||||
if (in.peek() == JsonToken.NULL) {
|
||||
in.nextNull();
|
||||
return null;
|
||||
}
|
||||
return Boolean.valueOf(in.nextString());
|
||||
}
|
||||
|
||||
@Override public void write(JsonWriter out, Boolean value) throws IOException {
|
||||
out.value(value == null ? "null" : value.toString());
|
||||
}
|
||||
};
|
||||
|
||||
public static final TypeAdapterFactory BOOLEAN_FACTORY
|
||||
= newFactory(boolean.class, Boolean.class, BOOLEAN);
|
||||
|
||||
public static final TypeAdapter<Number> BYTE = new TypeAdapter<Number>() {
|
||||
@Override
|
||||
public Number read(JsonReader in) throws IOException {
|
||||
if (in.peek() == JsonToken.NULL) {
|
||||
in.nextNull();
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
int intValue = in.nextInt();
|
||||
return (byte) intValue;
|
||||
} catch (NumberFormatException e) {
|
||||
throw new JsonSyntaxException(e);
|
||||
}
|
||||
}
|
||||
@Override
|
||||
public void write(JsonWriter out, Number value) throws IOException {
|
||||
out.value(value);
|
||||
}
|
||||
};
|
||||
|
||||
public static final TypeAdapterFactory BYTE_FACTORY
|
||||
= newFactory(byte.class, Byte.class, BYTE);
|
||||
|
||||
public static final TypeAdapter<Number> SHORT = new TypeAdapter<Number>() {
|
||||
@Override
|
||||
public Number read(JsonReader in) throws IOException {
|
||||
if (in.peek() == JsonToken.NULL) {
|
||||
in.nextNull();
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
return (short) in.nextInt();
|
||||
} catch (NumberFormatException e) {
|
||||
throw new JsonSyntaxException(e);
|
||||
}
|
||||
}
|
||||
@Override
|
||||
public void write(JsonWriter out, Number value) throws IOException {
|
||||
out.value(value);
|
||||
}
|
||||
};
|
||||
|
||||
public static final TypeAdapterFactory SHORT_FACTORY
|
||||
= newFactory(short.class, Short.class, SHORT);
|
||||
|
||||
public static final TypeAdapter<Number> INTEGER = new TypeAdapter<Number>() {
|
||||
@Override
|
||||
public Number read(JsonReader in) throws IOException {
|
||||
if (in.peek() == JsonToken.NULL) {
|
||||
in.nextNull();
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
return in.nextInt();
|
||||
} catch (NumberFormatException e) {
|
||||
throw new JsonSyntaxException(e);
|
||||
}
|
||||
}
|
||||
@Override
|
||||
public void write(JsonWriter out, Number value) throws IOException {
|
||||
out.value(value);
|
||||
}
|
||||
};
|
||||
|
||||
public static final TypeAdapterFactory INTEGER_FACTORY
|
||||
= newFactory(int.class, Integer.class, INTEGER);
|
||||
|
||||
public static final TypeAdapter<Number> LONG = new TypeAdapter<Number>() {
|
||||
@Override
|
||||
public Number read(JsonReader in) throws IOException {
|
||||
if (in.peek() == JsonToken.NULL) {
|
||||
in.nextNull();
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
return in.nextLong();
|
||||
} catch (NumberFormatException e) {
|
||||
throw new JsonSyntaxException(e);
|
||||
}
|
||||
}
|
||||
@Override
|
||||
public void write(JsonWriter out, Number value) throws IOException {
|
||||
out.value(value);
|
||||
}
|
||||
};
|
||||
|
||||
public static final TypeAdapter<Number> FLOAT = new TypeAdapter<Number>() {
|
||||
@Override
|
||||
public Number read(JsonReader in) throws IOException {
|
||||
if (in.peek() == JsonToken.NULL) {
|
||||
in.nextNull();
|
||||
return null;
|
||||
}
|
||||
return (float) in.nextDouble();
|
||||
}
|
||||
@Override
|
||||
public void write(JsonWriter out, Number value) throws IOException {
|
||||
out.value(value);
|
||||
}
|
||||
};
|
||||
|
||||
public static final TypeAdapter<Number> DOUBLE = new TypeAdapter<Number>() {
|
||||
@Override
|
||||
public Number read(JsonReader in) throws IOException {
|
||||
if (in.peek() == JsonToken.NULL) {
|
||||
in.nextNull();
|
||||
return null;
|
||||
}
|
||||
return in.nextDouble();
|
||||
}
|
||||
@Override
|
||||
public void write(JsonWriter out, Number value) throws IOException {
|
||||
out.value(value);
|
||||
}
|
||||
};
|
||||
|
||||
public static final TypeAdapter<Number> NUMBER = new TypeAdapter<Number>() {
|
||||
@Override
|
||||
public Number read(JsonReader in) throws IOException {
|
||||
JsonToken jsonToken = in.peek();
|
||||
switch (jsonToken) {
|
||||
case NULL:
|
||||
in.nextNull();
|
||||
return null;
|
||||
case NUMBER:
|
||||
return new LazilyParsedNumber(in.nextString());
|
||||
default:
|
||||
throw new JsonSyntaxException("Expecting number, got: " + jsonToken);
|
||||
}
|
||||
}
|
||||
@Override
|
||||
public void write(JsonWriter out, Number value) throws IOException {
|
||||
out.value(value);
|
||||
}
|
||||
};
|
||||
|
||||
public static final TypeAdapterFactory NUMBER_FACTORY = newFactory(Number.class, NUMBER);
|
||||
|
||||
public static final TypeAdapter<Character> CHARACTER = new TypeAdapter<Character>() {
|
||||
@Override
|
||||
public Character read(JsonReader in) throws IOException {
|
||||
if (in.peek() == JsonToken.NULL) {
|
||||
in.nextNull();
|
||||
return null;
|
||||
}
|
||||
String str = in.nextString();
|
||||
if (str.length() != 1) {
|
||||
throw new JsonSyntaxException("Expecting character, got: " + str);
|
||||
}
|
||||
return str.charAt(0);
|
||||
}
|
||||
@Override
|
||||
public void write(JsonWriter out, Character value) throws IOException {
|
||||
out.value(value == null ? null : String.valueOf(value));
|
||||
}
|
||||
};
|
||||
|
||||
public static final TypeAdapterFactory CHARACTER_FACTORY
|
||||
= newFactory(char.class, Character.class, CHARACTER);
|
||||
|
||||
public static final TypeAdapter<String> STRING = new TypeAdapter<String>() {
|
||||
@Override
|
||||
public String read(JsonReader in) throws IOException {
|
||||
JsonToken peek = in.peek();
|
||||
if (peek == JsonToken.NULL) {
|
||||
in.nextNull();
|
||||
return null;
|
||||
}
|
||||
/* coerce booleans to strings for backwards compatibility */
|
||||
if (peek == JsonToken.BOOLEAN) {
|
||||
return Boolean.toString(in.nextBoolean());
|
||||
}
|
||||
return in.nextString();
|
||||
}
|
||||
@Override
|
||||
public void write(JsonWriter out, String value) throws IOException {
|
||||
out.value(value);
|
||||
}
|
||||
};
|
||||
|
||||
public static final TypeAdapter<BigDecimal> BIG_DECIMAL = new TypeAdapter<BigDecimal>() {
|
||||
@Override public BigDecimal read(JsonReader in) throws IOException {
|
||||
if (in.peek() == JsonToken.NULL) {
|
||||
in.nextNull();
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
return new BigDecimal(in.nextString());
|
||||
} catch (NumberFormatException e) {
|
||||
throw new JsonSyntaxException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override public void write(JsonWriter out, BigDecimal value) throws IOException {
|
||||
out.value(value);
|
||||
}
|
||||
};
|
||||
|
||||
public static final TypeAdapter<BigInteger> BIG_INTEGER = new TypeAdapter<BigInteger>() {
|
||||
@Override public BigInteger read(JsonReader in) throws IOException {
|
||||
if (in.peek() == JsonToken.NULL) {
|
||||
in.nextNull();
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
return new BigInteger(in.nextString());
|
||||
} catch (NumberFormatException e) {
|
||||
throw new JsonSyntaxException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override public void write(JsonWriter out, BigInteger value) throws IOException {
|
||||
out.value(value);
|
||||
}
|
||||
};
|
||||
|
||||
public static final TypeAdapterFactory STRING_FACTORY = newFactory(String.class, STRING);
|
||||
|
||||
public static final TypeAdapter<StringBuilder> STRING_BUILDER = new TypeAdapter<StringBuilder>() {
|
||||
@Override
|
||||
public StringBuilder read(JsonReader in) throws IOException {
|
||||
if (in.peek() == JsonToken.NULL) {
|
||||
in.nextNull();
|
||||
return null;
|
||||
}
|
||||
return new StringBuilder(in.nextString());
|
||||
}
|
||||
@Override
|
||||
public void write(JsonWriter out, StringBuilder value) throws IOException {
|
||||
out.value(value == null ? null : value.toString());
|
||||
}
|
||||
};
|
||||
|
||||
public static final TypeAdapterFactory STRING_BUILDER_FACTORY =
|
||||
newFactory(StringBuilder.class, STRING_BUILDER);
|
||||
|
||||
public static final TypeAdapter<StringBuffer> STRING_BUFFER = new TypeAdapter<StringBuffer>() {
|
||||
@Override
|
||||
public StringBuffer read(JsonReader in) throws IOException {
|
||||
if (in.peek() == JsonToken.NULL) {
|
||||
in.nextNull();
|
||||
return null;
|
||||
}
|
||||
return new StringBuffer(in.nextString());
|
||||
}
|
||||
@Override
|
||||
public void write(JsonWriter out, StringBuffer value) throws IOException {
|
||||
out.value(value == null ? null : value.toString());
|
||||
}
|
||||
};
|
||||
|
||||
public static final TypeAdapterFactory STRING_BUFFER_FACTORY =
|
||||
newFactory(StringBuffer.class, STRING_BUFFER);
|
||||
|
||||
public static final TypeAdapter<URL> URL = new TypeAdapter<URL>() {
|
||||
@Override
|
||||
public URL read(JsonReader in) throws IOException {
|
||||
if (in.peek() == JsonToken.NULL) {
|
||||
in.nextNull();
|
||||
return null;
|
||||
}
|
||||
String nextString = in.nextString();
|
||||
return "null".equals(nextString) ? null : new URL(nextString);
|
||||
}
|
||||
@Override
|
||||
public void write(JsonWriter out, URL value) throws IOException {
|
||||
out.value(value == null ? null : value.toExternalForm());
|
||||
}
|
||||
};
|
||||
|
||||
public static final TypeAdapterFactory URL_FACTORY = newFactory(URL.class, URL);
|
||||
|
||||
public static final TypeAdapter<URI> URI = new TypeAdapter<URI>() {
|
||||
@Override
|
||||
public URI read(JsonReader in) throws IOException {
|
||||
if (in.peek() == JsonToken.NULL) {
|
||||
in.nextNull();
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
String nextString = in.nextString();
|
||||
return "null".equals(nextString) ? null : new URI(nextString);
|
||||
} catch (URISyntaxException e) {
|
||||
throw new JsonIOException(e);
|
||||
}
|
||||
}
|
||||
@Override
|
||||
public void write(JsonWriter out, URI value) throws IOException {
|
||||
out.value(value == null ? null : value.toASCIIString());
|
||||
}
|
||||
};
|
||||
|
||||
public static final TypeAdapterFactory URI_FACTORY = newFactory(URI.class, URI);
|
||||
|
||||
public static final TypeAdapter<InetAddress> INET_ADDRESS = new TypeAdapter<InetAddress>() {
|
||||
@Override
|
||||
public InetAddress read(JsonReader in) throws IOException {
|
||||
if (in.peek() == JsonToken.NULL) {
|
||||
in.nextNull();
|
||||
return null;
|
||||
}
|
||||
// regrettably, this should have included both the host name and the host address
|
||||
return InetAddress.getByName(in.nextString());
|
||||
}
|
||||
@Override
|
||||
public void write(JsonWriter out, InetAddress value) throws IOException {
|
||||
out.value(value == null ? null : value.getHostAddress());
|
||||
}
|
||||
};
|
||||
|
||||
public static final TypeAdapterFactory INET_ADDRESS_FACTORY =
|
||||
newTypeHierarchyFactory(InetAddress.class, INET_ADDRESS);
|
||||
|
||||
public static final TypeAdapter<UUID> UUID = new TypeAdapter<UUID>() {
|
||||
@Override
|
||||
public UUID read(JsonReader in) throws IOException {
|
||||
if (in.peek() == JsonToken.NULL) {
|
||||
in.nextNull();
|
||||
return null;
|
||||
}
|
||||
return java.util.UUID.fromString(in.nextString());
|
||||
}
|
||||
@Override
|
||||
public void write(JsonWriter out, UUID value) throws IOException {
|
||||
out.value(value == null ? null : value.toString());
|
||||
}
|
||||
};
|
||||
|
||||
public static final TypeAdapterFactory UUID_FACTORY = newFactory(UUID.class, UUID);
|
||||
|
||||
public static final TypeAdapterFactory TIMESTAMP_FACTORY = new TypeAdapterFactory() {
|
||||
@Override
|
||||
@SuppressWarnings("unchecked") // we use a runtime check to make sure the 'T's equal
|
||||
public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> typeToken) {
|
||||
if (typeToken.getRawType() != Timestamp.class) {
|
||||
return null;
|
||||
}
|
||||
|
||||
final TypeAdapter<Date> dateTypeAdapter = gson.getAdapter(Date.class);
|
||||
return (TypeAdapter<T>) new TypeAdapter<Timestamp>() {
|
||||
@Override public Timestamp read(JsonReader in) throws IOException {
|
||||
Date date = dateTypeAdapter.read(in);
|
||||
return date != null ? new Timestamp(date.getTime()) : null;
|
||||
}
|
||||
|
||||
@Override public void write(JsonWriter out, Timestamp value) throws IOException {
|
||||
dateTypeAdapter.write(out, 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 in) throws IOException {
|
||||
if (in.peek() == JsonToken.NULL) {
|
||||
in.nextNull();
|
||||
return null;
|
||||
}
|
||||
in.beginObject();
|
||||
int year = 0;
|
||||
int month = 0;
|
||||
int dayOfMonth = 0;
|
||||
int hourOfDay = 0;
|
||||
int minute = 0;
|
||||
int second = 0;
|
||||
while (in.peek() != JsonToken.END_OBJECT) {
|
||||
String name = in.nextName();
|
||||
int value = in.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;
|
||||
}
|
||||
}
|
||||
in.endObject();
|
||||
return new GregorianCalendar(year, month, dayOfMonth, hourOfDay, minute, second);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(JsonWriter out, Calendar value) throws IOException {
|
||||
if (value == null) {
|
||||
out.nullValue();
|
||||
return;
|
||||
}
|
||||
out.beginObject();
|
||||
out.name(YEAR);
|
||||
out.value(value.get(Calendar.YEAR));
|
||||
out.name(MONTH);
|
||||
out.value(value.get(Calendar.MONTH));
|
||||
out.name(DAY_OF_MONTH);
|
||||
out.value(value.get(Calendar.DAY_OF_MONTH));
|
||||
out.name(HOUR_OF_DAY);
|
||||
out.value(value.get(Calendar.HOUR_OF_DAY));
|
||||
out.name(MINUTE);
|
||||
out.value(value.get(Calendar.MINUTE));
|
||||
out.name(SECOND);
|
||||
out.value(value.get(Calendar.SECOND));
|
||||
out.endObject();
|
||||
}
|
||||
};
|
||||
|
||||
public static final TypeAdapterFactory CALENDAR_FACTORY =
|
||||
newFactoryForMultipleTypes(Calendar.class, GregorianCalendar.class, CALENDAR);
|
||||
|
||||
public static final TypeAdapter<Locale> LOCALE = new TypeAdapter<Locale>() {
|
||||
@Override
|
||||
public Locale read(JsonReader in) throws IOException {
|
||||
if (in.peek() == JsonToken.NULL) {
|
||||
in.nextNull();
|
||||
return null;
|
||||
}
|
||||
String locale = in.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 out, Locale value) throws IOException {
|
||||
out.value(value == null ? null : value.toString());
|
||||
}
|
||||
};
|
||||
|
||||
public static final TypeAdapterFactory LOCALE_FACTORY = newFactory(Locale.class, LOCALE);
|
||||
|
||||
public static final TypeAdapter<JsonElement> JSON_ELEMENT = new TypeAdapter<JsonElement>() {
|
||||
@Override public JsonElement read(JsonReader in) throws IOException {
|
||||
switch (in.peek()) {
|
||||
case STRING:
|
||||
return new JsonPrimitive(in.nextString());
|
||||
case NUMBER:
|
||||
String number = in.nextString();
|
||||
return new JsonPrimitive(new LazilyParsedNumber(number));
|
||||
case BOOLEAN:
|
||||
return new JsonPrimitive(in.nextBoolean());
|
||||
case NULL:
|
||||
in.nextNull();
|
||||
return JsonNull.INSTANCE;
|
||||
case BEGIN_ARRAY:
|
||||
JsonArray array = new JsonArray();
|
||||
in.beginArray();
|
||||
while (in.hasNext()) {
|
||||
array.add(read(in));
|
||||
}
|
||||
in.endArray();
|
||||
return array;
|
||||
case BEGIN_OBJECT:
|
||||
JsonObject object = new JsonObject();
|
||||
in.beginObject();
|
||||
while (in.hasNext()) {
|
||||
object.add(in.nextName(), read(in));
|
||||
}
|
||||
in.endObject();
|
||||
return object;
|
||||
case END_DOCUMENT:
|
||||
case NAME:
|
||||
case END_OBJECT:
|
||||
case END_ARRAY:
|
||||
default:
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
}
|
||||
|
||||
@Override public void write(JsonWriter out, JsonElement value) throws IOException {
|
||||
if (value == null || value.isJsonNull()) {
|
||||
out.nullValue();
|
||||
} else if (value.isJsonPrimitive()) {
|
||||
JsonPrimitive primitive = value.getAsJsonPrimitive();
|
||||
if (primitive.isNumber()) {
|
||||
out.value(primitive.getAsNumber());
|
||||
} else if (primitive.isBoolean()) {
|
||||
out.value(primitive.getAsBoolean());
|
||||
} else {
|
||||
out.value(primitive.getAsString());
|
||||
}
|
||||
|
||||
} else if (value.isJsonArray()) {
|
||||
out.beginArray();
|
||||
for (JsonElement e : value.getAsJsonArray()) {
|
||||
write(out, e);
|
||||
}
|
||||
out.endArray();
|
||||
|
||||
} else if (value.isJsonObject()) {
|
||||
out.beginObject();
|
||||
for (Map.Entry<String, JsonElement> e : value.getAsJsonObject().entrySet()) {
|
||||
out.name(e.getKey());
|
||||
write(out, e.getValue());
|
||||
}
|
||||
out.endObject();
|
||||
|
||||
} else {
|
||||
throw new IllegalArgumentException("Couldn't write " + value.getClass());
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
public static final TypeAdapterFactory JSON_ELEMENT_FACTORY
|
||||
= newTypeHierarchyFactory(JsonElement.class, JSON_ELEMENT);
|
||||
|
||||
private static final class EnumTypeAdapter<T extends Enum<T>> extends TypeAdapter<T> {
|
||||
private final Map<String, T> nameToConstant = new HashMap<String, T>();
|
||||
private final Map<T, String> constantToName = new HashMap<T, String>();
|
||||
|
||||
public EnumTypeAdapter(Class<T> classOfT) {
|
||||
try {
|
||||
for (T constant : classOfT.getEnumConstants()) {
|
||||
String name = constant.name();
|
||||
SerializedName annotation = classOfT.getField(name).getAnnotation(SerializedName.class);
|
||||
if (annotation != null) {
|
||||
name = annotation.value();
|
||||
}
|
||||
nameToConstant.put(name, constant);
|
||||
constantToName.put(constant, name);
|
||||
}
|
||||
} catch (NoSuchFieldException e) {
|
||||
throw new AssertionError();
|
||||
}
|
||||
}
|
||||
@Override
|
||||
public T read(JsonReader in) throws IOException {
|
||||
if (in.peek() == JsonToken.NULL) {
|
||||
in.nextNull();
|
||||
return null;
|
||||
}
|
||||
return nameToConstant.get(in.nextString());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(JsonWriter out, T value) throws IOException {
|
||||
out.value(value == null ? null : constantToName.get(value));
|
||||
}
|
||||
}
|
||||
|
||||
public static final TypeAdapterFactory ENUM_FACTORY = newEnumTypeHierarchyFactory();
|
||||
|
||||
public static TypeAdapterFactory newEnumTypeHierarchyFactory() {
|
||||
return new TypeAdapterFactory() {
|
||||
@Override
|
||||
@SuppressWarnings({"rawtypes", "unchecked"})
|
||||
public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> typeToken) {
|
||||
Class<? super T> rawType = typeToken.getRawType();
|
||||
if (!Enum.class.isAssignableFrom(rawType) || rawType == Enum.class) {
|
||||
return null;
|
||||
}
|
||||
if (!rawType.isEnum()) {
|
||||
rawType = rawType.getSuperclass(); // handle anonymous subclasses
|
||||
}
|
||||
return new EnumTypeAdapter(rawType);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public static <TT> TypeAdapterFactory newFactory(
|
||||
final TypeToken<TT> type, final TypeAdapter<TT> typeAdapter) {
|
||||
return new TypeAdapterFactory() {
|
||||
@Override
|
||||
@SuppressWarnings("unchecked") // we use a runtime check to make sure the 'T's equal
|
||||
public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> typeToken) {
|
||||
return typeToken.equals(type) ? (TypeAdapter<T>) typeAdapter : null;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public static <TT> TypeAdapterFactory newFactory(
|
||||
final Class<TT> type, final TypeAdapter<TT> typeAdapter) {
|
||||
return new TypeAdapterFactory() {
|
||||
@Override
|
||||
@SuppressWarnings("unchecked") // we use a runtime check to make sure the 'T's equal
|
||||
public <T> TypeAdapter<T> create(Gson gson, 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> TypeAdapterFactory newFactory(
|
||||
final Class<TT> unboxed, final Class<TT> boxed, final TypeAdapter<? super TT> typeAdapter) {
|
||||
return new TypeAdapterFactory() {
|
||||
@Override
|
||||
@SuppressWarnings("unchecked") // we use a runtime check to make sure the 'T's equal
|
||||
public <T> TypeAdapter<T> create(Gson gson, 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> TypeAdapterFactory newFactoryForMultipleTypes(final Class<TT> base,
|
||||
final Class<? extends TT> sub, final TypeAdapter<? super TT> typeAdapter) {
|
||||
return new TypeAdapterFactory() {
|
||||
@Override
|
||||
@SuppressWarnings("unchecked") // we use a runtime check to make sure the 'T's equal
|
||||
public <T> TypeAdapter<T> create(Gson gson, 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> TypeAdapterFactory newTypeHierarchyFactory(
|
||||
final Class<TT> clazz, final TypeAdapter<TT> typeAdapter) {
|
||||
return new TypeAdapterFactory() {
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> typeToken) {
|
||||
return clazz.isAssignableFrom(typeToken.getRawType()) ? (TypeAdapter<T>) typeAdapter : null;
|
||||
}
|
||||
@Override public String toString() {
|
||||
return "Factory[typeHierarchy=" + clazz.getName() + ",adapter=" + typeAdapter + "]";
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -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.google.gson.internal;
|
||||
@@ -0,0 +1,11 @@
|
||||
/**
|
||||
* This package provides the {@link com.google.gson.Gson} class to convert Json to Java and
|
||||
* vice-versa.
|
||||
*
|
||||
* <p>The primary class to use is {@link com.google.gson.Gson} which can be constructed with
|
||||
* {@code new Gson()} (using default settings) or by using {@link com.google.gson.GsonBuilder}
|
||||
* (to configure various options such as using versioning and so on).</p>
|
||||
*
|
||||
* @author Inderjeet Singh, Joel Leitch
|
||||
*/
|
||||
package com.google.gson;
|
||||
@@ -0,0 +1,305 @@
|
||||
/*
|
||||
* 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.google.gson.reflect;
|
||||
|
||||
import com.google.gson.internal.$Gson$Types;
|
||||
import com.google.gson.internal.$Gson$Preconditions;
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
/**
|
||||
* This package provides utility classes for finding type information for generic types.
|
||||
*
|
||||
* @author Inderjeet Singh, Joel Leitch
|
||||
*/
|
||||
package com.google.gson.reflect;
|
||||
1556
collect-library/src/main/java/com/google/gson/stream/JsonReader.java
Normal file
1556
collect-library/src/main/java/com/google/gson/stream/JsonReader.java
Normal file
File diff suppressed because it is too large
Load Diff
@@ -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.google.gson.stream;
|
||||
|
||||
/**
|
||||
* Lexical scoping elements within a JSON reader or writer.
|
||||
*
|
||||
* @author Jesse Wilson
|
||||
* @since 1.6
|
||||
*/
|
||||
final class JsonScope {
|
||||
|
||||
/**
|
||||
* An array with no elements requires no separators or newlines before
|
||||
* it is closed.
|
||||
*/
|
||||
static final int EMPTY_ARRAY = 1;
|
||||
|
||||
/**
|
||||
* A array with at least one value requires a comma and newline before
|
||||
* the next element.
|
||||
*/
|
||||
static final int NONEMPTY_ARRAY = 2;
|
||||
|
||||
/**
|
||||
* An object with no name/value pairs requires no separators or newlines
|
||||
* before it is closed.
|
||||
*/
|
||||
static final int EMPTY_OBJECT = 3;
|
||||
|
||||
/**
|
||||
* An object whose most recent element is a key. The next element must
|
||||
* be a value.
|
||||
*/
|
||||
static final int DANGLING_NAME = 4;
|
||||
|
||||
/**
|
||||
* An object with at least one name/value pair requires a comma and
|
||||
* newline before the next element.
|
||||
*/
|
||||
static final int NONEMPTY_OBJECT = 5;
|
||||
|
||||
/**
|
||||
* No object or array has been started.
|
||||
*/
|
||||
static final int EMPTY_DOCUMENT = 6;
|
||||
|
||||
/**
|
||||
* A document with at an array or object.
|
||||
*/
|
||||
static final int NONEMPTY_DOCUMENT = 7;
|
||||
|
||||
/**
|
||||
* A document that's been closed and cannot be accessed.
|
||||
*/
|
||||
static final int CLOSED = 8;
|
||||
}
|
||||
@@ -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.google.gson.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#beginArray}
|
||||
* and read using {@link JsonReader#beginArray}.
|
||||
*/
|
||||
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
|
||||
}
|
||||
@@ -0,0 +1,636 @@
|
||||
/*
|
||||
* 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.google.gson.stream;
|
||||
|
||||
import java.io.Closeable;
|
||||
import java.io.Flushable;
|
||||
import java.io.IOException;
|
||||
import java.io.Writer;
|
||||
|
||||
import static com.google.gson.stream.JsonScope.DANGLING_NAME;
|
||||
import static com.google.gson.stream.JsonScope.EMPTY_ARRAY;
|
||||
import static com.google.gson.stream.JsonScope.EMPTY_DOCUMENT;
|
||||
import static com.google.gson.stream.JsonScope.EMPTY_OBJECT;
|
||||
import static com.google.gson.stream.JsonScope.NONEMPTY_ARRAY;
|
||||
import static com.google.gson.stream.JsonScope.NONEMPTY_DOCUMENT;
|
||||
import static com.google.gson.stream.JsonScope.NONEMPTY_OBJECT;
|
||||
|
||||
/**
|
||||
* 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, Flushable {
|
||||
|
||||
/*
|
||||
* 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
|
||||
*/
|
||||
private static final String[] REPLACEMENT_CHARS;
|
||||
private static final String[] HTML_SAFE_REPLACEMENT_CHARS;
|
||||
static {
|
||||
REPLACEMENT_CHARS = new String[128];
|
||||
for (int i = 0; i <= 0x1f; i++) {
|
||||
REPLACEMENT_CHARS[i] = String.format("\\u%04x", i);
|
||||
}
|
||||
REPLACEMENT_CHARS['"'] = "\\\"";
|
||||
REPLACEMENT_CHARS['\\'] = "\\\\";
|
||||
REPLACEMENT_CHARS['\t'] = "\\t";
|
||||
REPLACEMENT_CHARS['\b'] = "\\b";
|
||||
REPLACEMENT_CHARS['\n'] = "\\n";
|
||||
REPLACEMENT_CHARS['\r'] = "\\r";
|
||||
REPLACEMENT_CHARS['\f'] = "\\f";
|
||||
HTML_SAFE_REPLACEMENT_CHARS = REPLACEMENT_CHARS.clone();
|
||||
HTML_SAFE_REPLACEMENT_CHARS['<'] = "\\u003c";
|
||||
HTML_SAFE_REPLACEMENT_CHARS['>'] = "\\u003e";
|
||||
HTML_SAFE_REPLACEMENT_CHARS['&'] = "\\u0026";
|
||||
HTML_SAFE_REPLACEMENT_CHARS['='] = "\\u003d";
|
||||
HTML_SAFE_REPLACEMENT_CHARS['\''] = "\\u0027";
|
||||
}
|
||||
|
||||
/** The output data, containing at most one top-level array or object. */
|
||||
private final Writer out;
|
||||
|
||||
private int[] stack = new int[32];
|
||||
private int stackSize = 0;
|
||||
{
|
||||
push(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(EMPTY_ARRAY, "[");
|
||||
}
|
||||
|
||||
/**
|
||||
* Ends encoding the current array.
|
||||
*
|
||||
* @return this writer.
|
||||
*/
|
||||
public JsonWriter endArray() throws IOException {
|
||||
return close(EMPTY_ARRAY, 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(EMPTY_OBJECT, "{");
|
||||
}
|
||||
|
||||
/**
|
||||
* Ends encoding the current object.
|
||||
*
|
||||
* @return this writer.
|
||||
*/
|
||||
public JsonWriter endObject() throws IOException {
|
||||
return close(EMPTY_OBJECT, NONEMPTY_OBJECT, "}");
|
||||
}
|
||||
|
||||
/**
|
||||
* Enters a new scope by appending any necessary whitespace and the given
|
||||
* bracket.
|
||||
*/
|
||||
private JsonWriter open(int empty, String openBracket) throws IOException {
|
||||
beforeValue(true);
|
||||
push(empty);
|
||||
out.write(openBracket);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes the current scope by appending any necessary whitespace and the
|
||||
* given bracket.
|
||||
*/
|
||||
private JsonWriter close(int empty, int nonempty, String closeBracket)
|
||||
throws IOException {
|
||||
int context = peek();
|
||||
if (context != nonempty && context != empty) {
|
||||
throw new IllegalStateException("Nesting problem.");
|
||||
}
|
||||
if (deferredName != null) {
|
||||
throw new IllegalStateException("Dangling name: " + deferredName);
|
||||
}
|
||||
|
||||
stackSize--;
|
||||
if (context == nonempty) {
|
||||
newline();
|
||||
}
|
||||
out.write(closeBracket);
|
||||
return this;
|
||||
}
|
||||
|
||||
private void push(int newTop) {
|
||||
if (stackSize == stack.length) {
|
||||
int[] newStack = new int[stackSize * 2];
|
||||
System.arraycopy(stack, 0, newStack, 0, stackSize);
|
||||
stack = newStack;
|
||||
}
|
||||
stack[stackSize++] = newTop;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the value on the top of the stack.
|
||||
*/
|
||||
private int peek() {
|
||||
if (stackSize == 0) {
|
||||
throw new IllegalStateException("JsonWriter is closed.");
|
||||
}
|
||||
return stack[stackSize - 1];
|
||||
}
|
||||
|
||||
/**
|
||||
* Replace the value on the top of the stack with the given value.
|
||||
*/
|
||||
private void replaceTop(int topOfStack) {
|
||||
stack[stackSize - 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();
|
||||
}
|
||||
if (stackSize == 0) {
|
||||
throw new IllegalStateException("JsonWriter is closed.");
|
||||
}
|
||||
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.
|
||||
*/
|
||||
@Override
|
||||
public void flush() throws IOException {
|
||||
if (stackSize == 0) {
|
||||
throw new IllegalStateException("JsonWriter is closed.");
|
||||
}
|
||||
out.flush();
|
||||
}
|
||||
|
||||
/**
|
||||
* Flushes and closes this writer and the underlying {@link Writer}.
|
||||
*
|
||||
* @throws IOException if the JSON document is incomplete.
|
||||
*/
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
out.close();
|
||||
|
||||
int size = stackSize;
|
||||
if (size > 1 || size == 1 && stack[size - 1] != NONEMPTY_DOCUMENT) {
|
||||
throw new IOException("Incomplete document");
|
||||
}
|
||||
stackSize = 0;
|
||||
}
|
||||
|
||||
private void string(String value) throws IOException {
|
||||
String[] replacements = htmlSafe ? HTML_SAFE_REPLACEMENT_CHARS : REPLACEMENT_CHARS;
|
||||
out.write("\"");
|
||||
int last = 0;
|
||||
int length = value.length();
|
||||
for (int i = 0; i < length; i++) {
|
||||
char c = value.charAt(i);
|
||||
String replacement;
|
||||
if (c < 128) {
|
||||
replacement = replacements[c];
|
||||
if (replacement == null) {
|
||||
continue;
|
||||
}
|
||||
} else if (c == '\u2028') {
|
||||
replacement = "\\u2028";
|
||||
} else if (c == '\u2029') {
|
||||
replacement = "\\u2029";
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
if (last < i) {
|
||||
out.write(value, last, i - last);
|
||||
}
|
||||
out.write(replacement);
|
||||
last = i + 1;
|
||||
}
|
||||
if (last < length) {
|
||||
out.write(value, last, length - last);
|
||||
}
|
||||
out.write("\"");
|
||||
}
|
||||
|
||||
private void newline() throws IOException {
|
||||
if (indent == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
out.write("\n");
|
||||
for (int i = 1, size = stackSize; i < 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 {
|
||||
int context = peek();
|
||||
if (context == NONEMPTY_OBJECT) { // first in object
|
||||
out.write(',');
|
||||
} else if (context != EMPTY_OBJECT) { // not in an object!
|
||||
throw new IllegalStateException("Nesting problem.");
|
||||
}
|
||||
newline();
|
||||
replaceTop(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.
|
||||
*/
|
||||
@SuppressWarnings("fallthrough")
|
||||
private void beforeValue(boolean root) throws IOException {
|
||||
switch (peek()) {
|
||||
case NONEMPTY_DOCUMENT:
|
||||
if (!lenient) {
|
||||
throw new IllegalStateException(
|
||||
"JSON must have only one top-level value.");
|
||||
}
|
||||
// fall-through
|
||||
case EMPTY_DOCUMENT: // first in document
|
||||
if (!lenient && !root) {
|
||||
throw new IllegalStateException(
|
||||
"JSON must start with an array or an object.");
|
||||
}
|
||||
replaceTop(NONEMPTY_DOCUMENT);
|
||||
break;
|
||||
|
||||
case EMPTY_ARRAY: // first in array
|
||||
replaceTop(NONEMPTY_ARRAY);
|
||||
newline();
|
||||
break;
|
||||
|
||||
case NONEMPTY_ARRAY: // another in array
|
||||
out.append(',');
|
||||
newline();
|
||||
break;
|
||||
|
||||
case DANGLING_NAME: // value for name
|
||||
out.append(separator);
|
||||
replaceTop(NONEMPTY_OBJECT);
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new IllegalStateException("Nesting problem.");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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.google.gson.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);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,117 @@
|
||||
package com.navinfo.collect;
|
||||
|
||||
import android.Manifest;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.os.Environment;
|
||||
import android.provider.Settings;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.core.app.ActivityCompat;
|
||||
import androidx.core.content.PermissionChecker;
|
||||
|
||||
import com.baidu.ai.edge.core.base.Consts;
|
||||
|
||||
|
||||
/**
|
||||
* Created by linyiran on 6/16/22.
|
||||
*/
|
||||
public abstract class BaseActivity extends AppCompatActivity {
|
||||
private static final int REQUEST_PERMISSION = 1;
|
||||
|
||||
protected boolean allPermissionsGranted;
|
||||
|
||||
@Override
|
||||
protected void onCreate(@Nullable Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setLayout();
|
||||
requestPermission();
|
||||
onActivityCreated(savedInstanceState);
|
||||
}
|
||||
|
||||
protected abstract void setLayout();
|
||||
|
||||
protected abstract void onActivityCreated(Bundle savedInstanceState);
|
||||
|
||||
private void requestPermission() {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||
String[] permissions = new String[]{
|
||||
Manifest.permission.WRITE_EXTERNAL_STORAGE,
|
||||
Manifest.permission.INTERNET,
|
||||
Manifest.permission.ACCESS_NETWORK_STATE,
|
||||
Manifest.permission.READ_PHONE_STATE,
|
||||
Manifest.permission.CAMERA
|
||||
};
|
||||
allPermissionsGranted = true;
|
||||
for (String perm : permissions) {
|
||||
if ((PermissionChecker.checkSelfPermission(this, perm) != PermissionChecker.PERMISSION_GRANTED)) {
|
||||
allPermissionsGranted = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!allPermissionsGranted) {
|
||||
ActivityCompat.requestPermissions(BaseActivity.this, permissions, REQUEST_PERMISSION);
|
||||
} else {
|
||||
requestAllFilesAccess();
|
||||
}
|
||||
} else {
|
||||
allPermissionsGranted = true;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions,
|
||||
@NonNull int[] grantResults) {
|
||||
if (requestCode == REQUEST_PERMISSION) {
|
||||
allPermissionsGranted = true;
|
||||
for (int grantRes : grantResults) {
|
||||
if (grantRes != PackageManager.PERMISSION_GRANTED) {
|
||||
allPermissionsGranted = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (allPermissionsGranted) {
|
||||
requestAllFilesAccess();
|
||||
}
|
||||
}
|
||||
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
|
||||
}
|
||||
|
||||
/**
|
||||
* Android 11 跳转到设置获取SD卡根目录写入权限
|
||||
*/
|
||||
private void requestAllFilesAccess() {
|
||||
if (!Consts.AUTH_REQUIRE_SDCARD) {
|
||||
return;
|
||||
}
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R && !Environment.isExternalStorageManager()) {
|
||||
allPermissionsGranted = false;
|
||||
|
||||
AlertDialog.Builder alertBuilder = new AlertDialog.Builder(BaseActivity.this,
|
||||
androidx.appcompat.R.style.Theme_AppCompat_Light_Dialog_Alert);
|
||||
alertBuilder.setMessage("需授权访问SD卡文件");
|
||||
alertBuilder.setCancelable(false);
|
||||
alertBuilder.setPositiveButton("去设置", new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
Intent intent = new Intent(Settings.ACTION_MANAGE_ALL_FILES_ACCESS_PERMISSION);
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
startActivity(intent);
|
||||
}
|
||||
});
|
||||
alertBuilder.setNegativeButton("取消", new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
dialog.dismiss();
|
||||
}
|
||||
});
|
||||
alertBuilder.show();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
package com.navinfo.collect
|
||||
|
||||
import android.content.Intent
|
||||
import android.util.Log
|
||||
import com.baidu.ai.edge.ui.view.model.BasePolygonResultModel
|
||||
import com.navinfo.collect.library.map.flutter.plugin.FlutterMapViewFactory
|
||||
import com.navinfo.collect.library.map.flutter.plugin.FlutterMapViewFlutterPlugin
|
||||
import io.flutter.embedding.android.FlutterActivity
|
||||
import io.flutter.embedding.engine.FlutterEngine
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.MainScope
|
||||
import kotlinx.coroutines.cancel
|
||||
|
||||
abstract class FlutterBaseActivity : FlutterActivity(), CoroutineScope by MainScope() {
|
||||
lateinit var factory: FlutterMapViewFactory
|
||||
override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
|
||||
super.configureFlutterEngine(flutterEngine)
|
||||
factory = FlutterMapViewFlutterPlugin.registerWith(flutterEngine, this);
|
||||
// FlutterNiMapCopyViewFlutterPlugin.registerWith(flutterEngine, this);
|
||||
}
|
||||
|
||||
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
|
||||
super.onActivityResult(requestCode, resultCode, data)
|
||||
if (resultCode == 0x10 && data != null) {
|
||||
val path = data.getStringExtra("photo_path")
|
||||
val list = data.getParcelableArrayListExtra<BasePolygonResultModel>("result_list")
|
||||
Log.e("jingo","OCR java 返回的数据:"+ path + list.toString());
|
||||
if (path != null && list != null) {
|
||||
factory.dataController.flutterDataCameraHandler.sendOcrResults(
|
||||
path,
|
||||
list as List<BasePolygonResultModel>
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
super.onDestroy()
|
||||
//协程销毁
|
||||
cancel()
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,224 @@
|
||||
package com.navinfo.collect.library.data
|
||||
|
||||
import android.graphics.Point
|
||||
import android.graphics.Rect
|
||||
import android.util.Log
|
||||
import android.view.WindowInsetsAnimation.Bounds
|
||||
import com.baidu.ai.edge.ui.view.model.BasePolygonResultModel
|
||||
import com.baidu.ai.edge.ui.view.model.OcrViewResultModel
|
||||
import com.navinfo.collect.library.data.entity.*
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.MainScope
|
||||
import kotlinx.coroutines.async
|
||||
import kotlinx.coroutines.launch
|
||||
import org.json.JSONArray
|
||||
import org.json.JSONObject
|
||||
import java.io.BufferedWriter
|
||||
import java.io.File
|
||||
import java.io.FileOutputStream
|
||||
import java.io.OutputStream
|
||||
import java.io.OutputStreamWriter
|
||||
|
||||
/**
|
||||
* 和flutter层进行数据传递时的数据格式转换
|
||||
*/
|
||||
class DataConversion {
|
||||
companion object {
|
||||
/**
|
||||
* 将数据转成map形式-传给flutter使用
|
||||
*/
|
||||
fun toElementMapList(list: List<Element>): List<Map<*, *>> {
|
||||
val newList = mutableListOf<Map<*, *>>()
|
||||
for (element in list) {
|
||||
newList.add(element.toMap())
|
||||
}
|
||||
return newList
|
||||
}
|
||||
|
||||
/**
|
||||
* json转数据图层自定义子表的所有字段对象列表
|
||||
* (现在用于从数据库中提取数据图层自定义表所有字段信息时,主要用于原生层)
|
||||
*/
|
||||
fun jsonToLayerItemsList(json: String): List<CustomLayerItem> {
|
||||
val list = mutableListOf<CustomLayerItem>()
|
||||
try {
|
||||
val jsonArray = JSONArray(json)
|
||||
for (i in 0 until jsonArray.length()) {
|
||||
val itemObject = jsonArray.getJSONObject(i)
|
||||
val itemMap = jsonToCustomLayerItem(itemObject)
|
||||
list.add(itemMap);
|
||||
}
|
||||
} catch (e: Throwable) {
|
||||
e.message?.let { Log.e("jingo", it) }
|
||||
Log.e("jingo", e.stackTraceToString())
|
||||
}
|
||||
return list
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 数据图层子表字段配置转Map
|
||||
* (主要原生层使用)
|
||||
*/
|
||||
fun customLayerItemsToMapList(list: List<CustomLayerItem>?): List<Map<*, *>> {
|
||||
var newList = mutableListOf<Map<*, *>>()
|
||||
if (list == null) {
|
||||
return newList
|
||||
}
|
||||
|
||||
for (item in list) {
|
||||
newList.add(item.toMap())
|
||||
}
|
||||
return newList
|
||||
}
|
||||
|
||||
/**
|
||||
* 数据图层子表字段配置 json形式的数据 转成对象形式
|
||||
* (原生层主要用json,所以主要原生层使用)
|
||||
*/
|
||||
private fun jsonToCustomLayerItem(itemObject: JSONObject): CustomLayerItem {
|
||||
return CustomLayerItem(
|
||||
key = itemObject.optString("key"),
|
||||
title = itemObject.optString("title"),
|
||||
type = DataLayerItemType.values()[itemObject.optInt("type")],
|
||||
// nullable = itemObject.optBoolean("nullable"),
|
||||
// primaryKey = itemObject.optBoolean("primaryKey"),
|
||||
// value = itemObject.opt("value"),
|
||||
// selectOptions = itemObject.optString("selectOptions"),
|
||||
// isMainName = itemObject.optBoolean("isMainName"),
|
||||
describe = itemObject.optString("describe"),
|
||||
// checkManagerList = mutableListOf()
|
||||
itemBean = ""
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* 数据图层子表字段配置转Map
|
||||
* (flutter层喜欢用map,所以主要用于flutter和原生交互使用)
|
||||
*/
|
||||
private fun jsonToCustomLayerItem(itemMap: Map<*, *>): CustomLayerItem {
|
||||
val checkMapList = itemMap["checkMangerList"] as List<Map<*, *>>
|
||||
val checkManagerList = mutableListOf<CheckManager>()
|
||||
for (c in checkMapList) {
|
||||
checkManagerList.add(
|
||||
CheckManager(
|
||||
id = (c["id"] as Int).toLong(),
|
||||
type = c["type"] as Int,
|
||||
tag = c["tag"] as String,
|
||||
regexStr = c["regexStr"] as String
|
||||
)
|
||||
)
|
||||
}
|
||||
return CustomLayerItem(
|
||||
key = itemMap["key"] as String,
|
||||
title = itemMap["title"] as String,
|
||||
type = DataLayerItemType.values()[itemMap["type"] as Int],
|
||||
// nullable = itemMap["nullable"] as Boolean,
|
||||
// primaryKey = itemMap["primaryKey"] as Boolean,
|
||||
// value = itemMap["value"] as Any,
|
||||
// selectOptions = itemMap["selectOptions"] as String,
|
||||
// isMainName = itemMap["isMainName"] as Boolean,
|
||||
describe = itemMap["describe"] as String,
|
||||
// checkManagerList = checkManagerList
|
||||
itemBean = ""
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* 数据图层 map形式的字段数据集合转成对象集合
|
||||
* (主要用于flutter层与原生层数据交互时)
|
||||
*/
|
||||
fun listMapToCustomLayerItemsList(itemList: List<Map<*, *>>): List<CustomLayerItem> {
|
||||
val list = mutableListOf<CustomLayerItem>()
|
||||
for (itemMap in itemList) {
|
||||
list.add(jsonToCustomLayerItem(itemMap));
|
||||
}
|
||||
return list
|
||||
}
|
||||
|
||||
// /**
|
||||
// * layerManager转化成map
|
||||
// */
|
||||
// fun layerManagerToMap(
|
||||
// layerManager: LayerManager,
|
||||
// itemsMap: List<Map<String, *>>
|
||||
// ): Map<*, *> {
|
||||
// return mapOf("layerManager" to layerManager.toMap(), "itemsList" to itemsMap);
|
||||
// }
|
||||
/**
|
||||
* 检查项转map给flutter
|
||||
*/
|
||||
fun toCheckManagerMapList(list: List<CheckManager>): List<Map<*, *>> {
|
||||
val newList = mutableListOf<Map<*, *>>()
|
||||
for (check in list) {
|
||||
newList.add(
|
||||
check.toMap()
|
||||
)
|
||||
}
|
||||
return newList
|
||||
}
|
||||
|
||||
fun toOcrList(
|
||||
path: String,
|
||||
basePolygonResultModels: List<BasePolygonResultModel>
|
||||
): Map<*, *> {
|
||||
val newList = mutableMapOf<String, Any>()
|
||||
newList["photo_path"] = path
|
||||
val list = mutableListOf<String>();
|
||||
for (model in basePolygonResultModels) {
|
||||
list.add(model.name)
|
||||
}
|
||||
newList["result"] = list
|
||||
return newList
|
||||
}
|
||||
|
||||
/**
|
||||
* 将ocr识别结果返回给flutter
|
||||
*/
|
||||
suspend fun toFlutterOcrList(
|
||||
list: List<Map<String, Any>>,
|
||||
): List<Any> {
|
||||
val listR = mutableListOf<Any>()
|
||||
val job = MainScope().async(Dispatchers.IO) {
|
||||
Log.e("jingo", "OCR图像识别写CSV文件 ${Thread.currentThread().name}")
|
||||
for (item in list) {
|
||||
if (item is Map) {
|
||||
val map1 = mutableMapOf<String, Any>()
|
||||
map1["path"] = item["path"]!!
|
||||
map1["width"] = item["width"]!!
|
||||
map1["height"] = item["height"]!!
|
||||
var data = item["data"]
|
||||
if (data is List<*>) {
|
||||
val dataList = mutableListOf<Any>()
|
||||
for (v in data) {
|
||||
val map = mutableMapOf<String, Any>()
|
||||
map["index"] = (v as OcrViewResultModel).index
|
||||
map["bounds"] = rectToMap(v.bounds)
|
||||
map["text"] = v.name
|
||||
map["confidence"] = v.confidence
|
||||
dataList.add(map)
|
||||
}
|
||||
map1["data"] = dataList
|
||||
}
|
||||
listR.add(map1)
|
||||
}
|
||||
}
|
||||
}
|
||||
job.await()
|
||||
Log.e("jingo", "ORC 识别结果 ${listR.toString()}")
|
||||
return listR
|
||||
}
|
||||
|
||||
private fun rectToMap(list: List<Point>): List<List<Int>> {
|
||||
val pointList = mutableListOf<List<Int>>()
|
||||
for (point in list) {
|
||||
val l = mutableListOf<Int>()
|
||||
l.add(point.x)
|
||||
l.add(point.y)
|
||||
pointList.add(l)
|
||||
}
|
||||
|
||||
return pointList
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
package com.navinfo.collect.library.data
|
||||
|
||||
import com.navinfo.collect.library.data.entity.GeometryFeatureEntity
|
||||
import java.io.File
|
||||
|
||||
interface GisFileUtils {
|
||||
fun parserGisFile(file: File): List<GeometryFeatureEntity>
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package com.navinfo.collect.library.data;
|
||||
|
||||
import org.gdal.gdal.gdal;
|
||||
import org.gdal.ogr.ogr;
|
||||
|
||||
/**
|
||||
* 使用gdal和ogr解析Gis数据的引擎
|
||||
*/
|
||||
public abstract class GisFileParserEngine implements IGisFileParserEngine {
|
||||
public GisFileParserEngine() {
|
||||
initEngine();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initEngine() {
|
||||
ogr.RegisterAll();
|
||||
gdal.AllRegister();
|
||||
gdal.SetConfigOption("GDAL_FILENAME_IS_UTF8", "YES");
|
||||
gdal.SetConfigOption("SHAPE_ENCODING", "CP936");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package com.navinfo.collect.library.data;
|
||||
|
||||
import com.navinfo.collect.library.data.entity.LayerEntity;
|
||||
|
||||
import org.locationtech.jts.geom.Geometry;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 常用Gis文件的解析写入引擎
|
||||
* */
|
||||
public interface IGisFileParserEngine {
|
||||
void initEngine();
|
||||
|
||||
List<LayerEntity> parserGisFile(File gisFile);
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
package com.navinfo.collect.library.data;
|
||||
|
||||
import com.navinfo.collect.library.data.entity.GeometryFeatureEntity;
|
||||
import com.navinfo.collect.library.data.entity.LayerEntity;
|
||||
|
||||
import org.gdal.gdal.gdal;
|
||||
import org.gdal.ogr.DataSource;
|
||||
import org.gdal.ogr.Feature;
|
||||
import org.gdal.ogr.FeatureDefn;
|
||||
import org.gdal.ogr.ogr;
|
||||
import org.locationtech.jts.geom.Geometry;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class MapInfoParserEngine extends GisFileParserEngine {
|
||||
|
||||
/**
|
||||
* 读取MapInfo文件
|
||||
* */
|
||||
@Override
|
||||
public List<LayerEntity> parserGisFile(File mifFile) {
|
||||
DataSource ds = ogr.Open(mifFile.getAbsolutePath(), 0);
|
||||
if (ds == null) {
|
||||
System.out.println("打开文件失败");
|
||||
return null;
|
||||
}
|
||||
System.out.println("打开文件成功");
|
||||
List<LayerEntity> layerEntityList = new ArrayList<>();
|
||||
if (ds.GetLayerCount()>0) {
|
||||
for (int i = 0; i < ds.GetLayerCount(); i++) {
|
||||
org.gdal.ogr.Layer layer = ds.GetLayer(i);
|
||||
if (layer == null) {
|
||||
System.out.println("获取第"+i+"个图层失败");
|
||||
continue;
|
||||
}
|
||||
|
||||
LayerEntity layerEntity = new LayerEntity();
|
||||
layerEntity.setLayerName(layer.GetName());
|
||||
layerEntity.setGeomType(layer.GetGeomType());
|
||||
layerEntity.setFromDataName(mifFile.getName());
|
||||
|
||||
|
||||
System.out.println("读取到Layer" + layer.GetName());
|
||||
layer.ResetReading();
|
||||
for (int j = 0; j < layer.GetFeatureCount(); j++) {
|
||||
GeometryFeatureEntity entity = new GeometryFeatureEntity();
|
||||
Feature feature = layer.GetFeature(j);
|
||||
entity.setGeometry(feature.GetGeometryRef().toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
return layerEntityList;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package com.navinfo.collect.library.data
|
||||
|
||||
import android.content.Context
|
||||
import com.navinfo.collect.FlutterBaseActivity
|
||||
import com.navinfo.collect.library.data.dao.impl.MapLifeDataBase
|
||||
import com.navinfo.collect.library.data.handler.DataLayerHandler
|
||||
import com.navinfo.collect.library.system.Constant
|
||||
|
||||
/**
|
||||
* 地图控制器
|
||||
*/
|
||||
|
||||
open class NIDataController(
|
||||
context: Context,
|
||||
activity: FlutterBaseActivity,
|
||||
) {
|
||||
protected val mContext = context
|
||||
protected val mActivity = activity
|
||||
internal val mDateBase: MapLifeDataBase = MapLifeDataBase.getDatabase(
|
||||
context, "${Constant.ROOT_PATH}/coremap.db"
|
||||
)
|
||||
|
||||
init {
|
||||
RealmUtils.getInstance().init(mContext, Constant.ROOT_PATH, "hd-data")
|
||||
}
|
||||
|
||||
protected val dataHandler: DataLayerHandler = DataLayerHandler(mContext, mDateBase);
|
||||
|
||||
open fun release() {
|
||||
mDateBase.close()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,162 @@
|
||||
package com.navinfo.collect.library.data;
|
||||
|
||||
import com.google.protobuf.Descriptors;
|
||||
import com.google.protobuf.GeneratedMessageV3;
|
||||
import com.navinfo.collect.library.data.entity.GeometryFeatureEntity;
|
||||
import com.navinfo.onemap.det.sdkpbf.proto.crowdsource.Resultdata;
|
||||
|
||||
import org.locationtech.jts.geom.Coordinate;
|
||||
import org.locationtech.jts.geom.CoordinateXYM;
|
||||
import org.locationtech.jts.geom.CoordinateXYZM;
|
||||
import org.locationtech.jts.geom.Geometry;
|
||||
import org.locationtech.jts.geom.GeometryFactory;
|
||||
import org.locationtech.jts.geom.LineString;
|
||||
import org.locationtech.jts.geom.Point;
|
||||
import org.locationtech.jts.geom.Polygon;
|
||||
import org.locationtech.jts.io.WKBWriter;
|
||||
import org.locationtech.spatial4j.context.SpatialContext;
|
||||
import org.locationtech.spatial4j.context.SpatialContextFactory;
|
||||
import org.locationtech.spatial4j.context.jts.JtsSpatialContextFactory;
|
||||
import org.locationtech.spatial4j.io.ShapeIO;
|
||||
import org.locationtech.spatial4j.io.ShapeReader;
|
||||
import org.locationtech.spatial4j.io.ShapeWriter;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
public class NavinfoPbfData {
|
||||
private static GeometryFactory factory;
|
||||
private static SpatialContext spatialContext;
|
||||
private static ShapeReader shapeReader;
|
||||
private static ShapeWriter shapeWriter;
|
||||
private static WKBWriter wkbWriter;
|
||||
private boolean isInit = false;
|
||||
|
||||
public NavinfoPbfData() {
|
||||
if (!isInit) {
|
||||
init();
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean init() {
|
||||
SpatialContextFactory spatialContextFactory = new SpatialContextFactory();
|
||||
wkbWriter= new WKBWriter(3, true);
|
||||
factory = new JtsSpatialContextFactory().getGeometryFactory();
|
||||
|
||||
spatialContext = new SpatialContext(spatialContextFactory);
|
||||
shapeReader = spatialContext.getFormats().getWktReader();
|
||||
shapeWriter = spatialContext.getFormats().getWktWriter();
|
||||
return true;
|
||||
}
|
||||
|
||||
public static Resultdata.ResultData readPbfData(InputStream inputStream) throws IOException {
|
||||
return Resultdata.ResultData.parseFrom(inputStream);
|
||||
}
|
||||
|
||||
public static Geometry createGeometry(GeneratedMessageV3 geometry) {
|
||||
Geometry resultGeometry = null;
|
||||
if (geometry instanceof com.navinfo.onemap.det.sdkpbf.proto.geometry.Geometry.Point2d) {
|
||||
com.navinfo.onemap.det.sdkpbf.proto.geometry.Geometry.Point2d point2d = (com.navinfo.onemap.det.sdkpbf.proto.geometry.Geometry.Point2d) geometry;
|
||||
Coordinate coordinate = new Coordinate(point2d.getLongitudeDegrees(), point2d.getLatitudeDegrees());
|
||||
resultGeometry = factory.createPoint(coordinate);
|
||||
} else if (geometry instanceof com.navinfo.onemap.det.sdkpbf.proto.geometry.Geometry.Point3d) {
|
||||
com.navinfo.onemap.det.sdkpbf.proto.geometry.Geometry.Point3d point3d = (com.navinfo.onemap.det.sdkpbf.proto.geometry.Geometry.Point3d) geometry;
|
||||
CoordinateXYZM coordinate = new CoordinateXYZM(point3d.getLatLon().getLongitudeDegrees(), point3d.getLatLon().getLatitudeDegrees(), point3d.getElevation().getZ(), 7);
|
||||
resultGeometry = factory.createPoint(coordinate);
|
||||
} else if (geometry instanceof com.navinfo.onemap.det.sdkpbf.proto.geometry.Geometry.LineString2d) {
|
||||
com.navinfo.onemap.det.sdkpbf.proto.geometry.Geometry.LineString2d lineString2d = (com.navinfo.onemap.det.sdkpbf.proto.geometry.Geometry.LineString2d) geometry;
|
||||
Coordinate[] coordinates = new Coordinate[lineString2d.getLinestringPointsCount()];
|
||||
for (int i = 0; i < lineString2d.getLinestringPointsCount(); i++) {
|
||||
coordinates[i] = new Coordinate(lineString2d.getLinestringPoints(i).getLongitudeDegrees(), lineString2d.getLinestringPoints(i).getLatitudeDegrees());
|
||||
}
|
||||
resultGeometry = factory.createLineString(coordinates);
|
||||
} else if (geometry instanceof com.navinfo.onemap.det.sdkpbf.proto.geometry.Geometry.LineString3d) {
|
||||
com.navinfo.onemap.det.sdkpbf.proto.geometry.Geometry.LineString3d lineString3d = (com.navinfo.onemap.det.sdkpbf.proto.geometry.Geometry.LineString3d) geometry;
|
||||
CoordinateXYZM[] coordinates = new CoordinateXYZM[lineString3d.getLinestringPointsCount()];
|
||||
for (int i = 0; i < lineString3d.getLinestringPointsCount(); i++) {
|
||||
coordinates[i] = new CoordinateXYZM(lineString3d.getLinestringPoints(i).getLatLon().getLongitudeDegrees(), lineString3d.getLinestringPoints(i).getLatLon().getLatitudeDegrees(), lineString3d.getLinestringPoints(i).getElevation().getZ(), 7);
|
||||
}
|
||||
resultGeometry = factory.createLineString(coordinates);
|
||||
} else if (geometry instanceof com.navinfo.onemap.det.sdkpbf.proto.geometry.Geometry.MultiLineString2d) {
|
||||
com.navinfo.onemap.det.sdkpbf.proto.geometry.Geometry.MultiLineString2d multiLineString2d = (com.navinfo.onemap.det.sdkpbf.proto.geometry.Geometry.MultiLineString2d) geometry;
|
||||
LineString[] lineStrings = new LineString[multiLineString2d.getLineStringsCount()];
|
||||
for (int i = 0; i < multiLineString2d.getLineStringsCount(); i++) {
|
||||
lineStrings[i] = (LineString) createGeometry(multiLineString2d.getLineStrings(i));
|
||||
}
|
||||
resultGeometry = factory.createMultiLineString(lineStrings);
|
||||
} else if (geometry instanceof com.navinfo.onemap.det.sdkpbf.proto.geometry.Geometry.MultiLineString3d) {
|
||||
com.navinfo.onemap.det.sdkpbf.proto.geometry.Geometry.MultiLineString3d multiLineString3d = (com.navinfo.onemap.det.sdkpbf.proto.geometry.Geometry.MultiLineString3d) geometry;
|
||||
LineString[] lineStrings = new LineString[multiLineString3d.getLineStringsCount()];
|
||||
for (int i = 0; i < multiLineString3d.getLineStringsCount(); i++) {
|
||||
lineStrings[i] = (LineString) createGeometry(multiLineString3d.getLineStrings(i));
|
||||
}
|
||||
resultGeometry = factory.createMultiLineString(lineStrings);
|
||||
} else if (geometry instanceof com.navinfo.onemap.det.sdkpbf.proto.geometry.Geometry.Polygon2d) {
|
||||
com.navinfo.onemap.det.sdkpbf.proto.geometry.Geometry.Polygon2d polygon2d = (com.navinfo.onemap.det.sdkpbf.proto.geometry.Geometry.Polygon2d) geometry;
|
||||
Coordinate[] coordinates = new Coordinate[polygon2d.getPolygonPointsCount()];
|
||||
for (int i = 0; i < polygon2d.getPolygonPointsCount(); i++) {
|
||||
coordinates[i] = new Coordinate(polygon2d.getPolygonPoints(i).getLongitudeDegrees(), polygon2d.getPolygonPoints(i).getLatitudeDegrees());
|
||||
}
|
||||
resultGeometry = factory.createPolygon(coordinates);
|
||||
} else if (geometry instanceof com.navinfo.onemap.det.sdkpbf.proto.geometry.Geometry.Polygon3d) {
|
||||
com.navinfo.onemap.det.sdkpbf.proto.geometry.Geometry.Polygon3d polygon3d = (com.navinfo.onemap.det.sdkpbf.proto.geometry.Geometry.Polygon3d) geometry;
|
||||
CoordinateXYZM[] coordinates = new CoordinateXYZM[polygon3d.getPolygonPointsCount()];
|
||||
for (int i = 0; i < polygon3d.getPolygonPointsCount(); i++) {
|
||||
coordinates[i] = new CoordinateXYZM(polygon3d.getPolygonPoints(i).getLatLon().getLongitudeDegrees(), polygon3d.getPolygonPoints(i).getLatLon().getLatitudeDegrees(), polygon3d.getPolygonPoints(i).getElevation().getZ(), 7);
|
||||
}
|
||||
resultGeometry = factory.createPolygon(coordinates);
|
||||
} else if (geometry instanceof com.navinfo.onemap.det.sdkpbf.proto.geometry.Geometry.MultiPolygon2d) {
|
||||
com.navinfo.onemap.det.sdkpbf.proto.geometry.Geometry.MultiPolygon2d multiPolygon2d = (com.navinfo.onemap.det.sdkpbf.proto.geometry.Geometry.MultiPolygon2d) geometry;
|
||||
Polygon[] polygons = new Polygon[multiPolygon2d.getPolygonsCount()];
|
||||
for (int i = 0; i < multiPolygon2d.getPolygonsCount(); i++) {
|
||||
polygons[i] = (Polygon) createGeometry(multiPolygon2d.getPolygons(i));
|
||||
}
|
||||
resultGeometry = factory.createMultiPolygon(polygons);
|
||||
} else if (geometry instanceof com.navinfo.onemap.det.sdkpbf.proto.geometry.Geometry.MultiPolygon3d) {
|
||||
com.navinfo.onemap.det.sdkpbf.proto.geometry.Geometry.MultiPolygon3d multiPolygon3d = (com.navinfo.onemap.det.sdkpbf.proto.geometry.Geometry.MultiPolygon3d) geometry;
|
||||
Polygon[] polygons = new Polygon[multiPolygon3d.getPolygonsCount()];
|
||||
for (int i = 0; i < multiPolygon3d.getPolygonsCount(); i++) {
|
||||
polygons[i] = (Polygon) createGeometry(multiPolygon3d.getPolygons(i));
|
||||
}
|
||||
resultGeometry = factory.createMultiPolygon(polygons);
|
||||
}
|
||||
return resultGeometry;
|
||||
}
|
||||
|
||||
public static GeometryFeatureEntity createGeometryEntity(Geometry geometry, Map<Descriptors.FieldDescriptor, Object> userData, Map<String, String> extendFields) {
|
||||
if (geometry == null) {
|
||||
return null;
|
||||
}
|
||||
StringBuilder prefixStr = new StringBuilder();
|
||||
GeometryFeatureEntity entity = new GeometryFeatureEntity();
|
||||
entity.setId(UUID.randomUUID().toString());
|
||||
entity.setGeometry(geometry);
|
||||
entity.setWkb(wkbWriter.write(geometry));
|
||||
if (userData!=null&&!userData.isEmpty()) {
|
||||
for (Map.Entry<Descriptors.FieldDescriptor, Object> e: userData.entrySet()) {
|
||||
getFieldEntry(e, prefixStr, entity.getProperties());
|
||||
}
|
||||
}
|
||||
if (extendFields!=null&&!extendFields.isEmpty()) {
|
||||
entity.getOtherProperties().putAll(extendFields);
|
||||
}
|
||||
return entity;
|
||||
}
|
||||
|
||||
/**
|
||||
* 递归调用,如果存在子属性,则父子属性以{parent.child}格式存入map
|
||||
* */
|
||||
private static void getFieldEntry(Map.Entry<Descriptors.FieldDescriptor, Object> e, StringBuilder prefix, Map<String, String> properties) {
|
||||
prefix.append(".").append(e.getKey().getName());
|
||||
if (e.getValue() instanceof GeneratedMessageV3) {
|
||||
for (Map.Entry<Descriptors.FieldDescriptor, Object> subE: ((GeneratedMessageV3)e.getValue()).getAllFields().entrySet()) {
|
||||
getFieldEntry(subE, prefix, properties);
|
||||
}
|
||||
} else {
|
||||
properties.put(e.getKey().getName(), e.getValue().toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,384 @@
|
||||
package com.navinfo.collect.library.data
|
||||
|
||||
import android.os.Build
|
||||
import androidx.annotation.RequiresApi
|
||||
import com.google.protobuf.GeneratedMessageV3
|
||||
import com.navinfo.collect.library.data.entity.GeometryFeatureEntity
|
||||
import com.navinfo.collect.library.data.entity.LayerEntity
|
||||
import com.navinfo.collect.library.utils.GeometryTools
|
||||
import com.navinfo.onemap.det.sdkpbf.proto.crowdsource.*
|
||||
import org.locationtech.jts.algorithm.Angle
|
||||
import org.locationtech.jts.geom.Coordinate
|
||||
import org.locationtech.jts.geom.Geometry
|
||||
import org.locationtech.jts.geom.LineString
|
||||
import org.locationtech.jts.geom.util.AffineTransformation
|
||||
import org.locationtech.jts.geom.util.AffineTransformationFactory
|
||||
import java.io.File
|
||||
import java.io.FileInputStream
|
||||
import java.util.*
|
||||
import kotlin.math.cos
|
||||
import kotlin.math.sin
|
||||
import kotlin.streams.toList
|
||||
|
||||
@RequiresApi(Build.VERSION_CODES.N)
|
||||
class NavinfoPbfFileUtils: GisFileUtils {
|
||||
/*
|
||||
* 解析Pbf文件
|
||||
* */
|
||||
override fun parserGisFile(file: File): List<GeometryFeatureEntity> {
|
||||
val geometryEntityList = ArrayList<GeometryFeatureEntity>()
|
||||
if (!file?.isFile || !file.exists()) {
|
||||
return geometryEntityList;
|
||||
}
|
||||
// 解析pbf文件
|
||||
val pbfData = NavinfoPbfData.readPbfData(FileInputStream(file))
|
||||
|
||||
// 解析roadLink道路线
|
||||
geometryEntityList.addAll(parserRoadLink(pbfData.roadlinkList, file, "道路线"))
|
||||
// 解析roadLink上的道路方向
|
||||
geometryEntityList.addAll(parserRoadDirect(pbfData.roadlinkList, file, "道路方向"))
|
||||
// 解析roadLink的桥属性
|
||||
geometryEntityList.addAll(parserRoadBrunnel(pbfData.roadlinkList, file, "桥隧道"))
|
||||
// 解析roadLink的移动式桥属性
|
||||
geometryEntityList.addAll(parserRoadMovBrg(pbfData.roadlinkList, file, "移动式桥"))
|
||||
// 解析hadLaneLink车道中心线
|
||||
geometryEntityList.addAll(parserHadLaneLink(pbfData.hadlanelinkList, file, "车道中心线"))
|
||||
// 解析hadLaneMarkLink车道边线
|
||||
geometryEntityList.addAll(parserHadLaneMarkLink(pbfData.hadlanemarklinkList, file, "车道边线"))
|
||||
// 解析hadLaneMarkLink.traversal车道边线可跨越性
|
||||
geometryEntityList.addAll(parserHadLaneMarkLinkTraversal(pbfData.hadlanemarklinkList, file, "车道边线可跨越性"))
|
||||
// 解析hadLaneMarkLink.marking车道边线划线类型
|
||||
geometryEntityList.addAll(parserHadLaneMarkLinkBoundary(pbfData.hadlanemarklinkList, file, "车道边线非标线类型"))
|
||||
// 解析speedLimitGen固定限速
|
||||
geometryEntityList.addAll(parserSpeedLimitGen(pbfData.rdspeedlimitgenList, file, "固定限速"))
|
||||
// 解析speedLimitDepend条件限速
|
||||
geometryEntityList.addAll(parserSpeedLimitDepend(pbfData.rdspeedlimitdependList, file, "条件限速"))
|
||||
// 解析speedLimitVar可变限速
|
||||
geometryEntityList.addAll(parserSpeedLimitVar(pbfData.rdspeedlimitvarList, file, "可变限速"))
|
||||
return geometryEntityList;
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理道路线RoadLink
|
||||
* */
|
||||
private fun parserRoadLink(roadLinkList: List<Roadlink.RoadLink>, file: File, layerName: String= "道路线", layerTableName: String = "ROAD_LINK"): List<GeometryFeatureEntity> {
|
||||
val featureEntityList = ArrayList<GeometryFeatureEntity>()
|
||||
if (roadLinkList?.isEmpty()) {
|
||||
return featureEntityList
|
||||
}
|
||||
for (roadLink in roadLinkList) {
|
||||
val geometry = NavinfoPbfData.createGeometry(roadLink.geometry)
|
||||
val geometryFeatureEntity =
|
||||
convertDefaultHDEntity(geometry, roadLink, layerName, layerTableName, file)
|
||||
featureEntityList.add(geometryFeatureEntity)
|
||||
}
|
||||
|
||||
return featureEntityList;
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理车道中心线HadLaneLink
|
||||
* */
|
||||
private fun parserHadLaneLink(hadLaneLinkList: List<Hadlanelink.HadLaneLink>, file: File, layerName: String= "车道中心线", layerTableName: String = "HAD_LANE_LINK"): List<GeometryFeatureEntity> {
|
||||
val featureEntityList = ArrayList<GeometryFeatureEntity>()
|
||||
if (hadLaneLinkList?.isEmpty()) {
|
||||
return featureEntityList
|
||||
}
|
||||
for (hadLaneLink in hadLaneLinkList) {
|
||||
val geometry = NavinfoPbfData.createGeometry(hadLaneLink.geometry)
|
||||
val geometryFeatureEntity =
|
||||
convertDefaultHDEntity(geometry, hadLaneLink, layerName, layerTableName, file)
|
||||
featureEntityList.add(geometryFeatureEntity)
|
||||
}
|
||||
return featureEntityList;
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理speedLimitGen固定限速
|
||||
* */
|
||||
private fun parserSpeedLimitGen(speedLimitGenList: List<Rdspeedlimitgen.RdSpeedlimitGen>, file: File, layerName: String= "固定限速", layerTableName: String = "SPEED_LIMIT_GEN"): List<GeometryFeatureEntity> {
|
||||
val featureEntityList = ArrayList<GeometryFeatureEntity>()
|
||||
if (speedLimitGenList?.isEmpty()) {
|
||||
return featureEntityList
|
||||
}
|
||||
for (speedLimitGen in speedLimitGenList) {
|
||||
val geometry = NavinfoPbfData.createGeometry(speedLimitGen.geometry)
|
||||
val geometryFeatureEntity =
|
||||
convertDefaultHDEntity(geometry, speedLimitGen, layerName, layerTableName, file)
|
||||
geometryFeatureEntity.otherProperties["SPEED_FLAG"] = speedLimitGen.speedFlag.toString()
|
||||
featureEntityList.add(geometryFeatureEntity)
|
||||
}
|
||||
return featureEntityList;
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理speedLimitDepend条件限速
|
||||
* */
|
||||
private fun parserSpeedLimitDepend(speedLimitDenpendList: List<Rdspeedlimitdepend.RdSpeedlimitDepend>, file: File, layerName: String= "条件限速", layerTableName: String = "SPEED_LIMIT_DEPEND"): List<GeometryFeatureEntity> {
|
||||
val featureEntityList = ArrayList<GeometryFeatureEntity>()
|
||||
if (speedLimitDenpendList?.isEmpty()) {
|
||||
return featureEntityList
|
||||
}
|
||||
for (speedLimitDepend in speedLimitDenpendList) {
|
||||
val geometry = NavinfoPbfData.createGeometry(speedLimitDepend.geometry)
|
||||
val geometryFeatureEntity =
|
||||
convertDefaultHDEntity(geometry, speedLimitDepend, layerName, layerTableName, file)
|
||||
geometryFeatureEntity.otherProperties["SPEED_FLAG"] = speedLimitDepend.speedFlag.toString()
|
||||
featureEntityList.add(geometryFeatureEntity)
|
||||
}
|
||||
return featureEntityList;
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理speedLimitDepend条件限速
|
||||
* */
|
||||
private fun parserSpeedLimitVar(speedLimitVarList: List<Rdspeedlimitvar.RdSpeedlimitVar>, file: File,
|
||||
layerName: String= "可变限速", layerTableName: String = "SPEED_LIMIT_VAR"): List<GeometryFeatureEntity> {
|
||||
val featureEntityList = ArrayList<GeometryFeatureEntity>()
|
||||
if (speedLimitVarList?.isEmpty()) {
|
||||
return featureEntityList
|
||||
}
|
||||
for (speedLimitVar in speedLimitVarList) {
|
||||
val geometry = NavinfoPbfData.createGeometry(speedLimitVar.geometry)
|
||||
val geometryFeatureEntity =
|
||||
convertDefaultHDEntity(geometry, speedLimitVar, layerName, layerTableName, file)
|
||||
geometryFeatureEntity.otherProperties["SPEED_FLAG"] = speedLimitVar.speedFlag.toString()
|
||||
featureEntityList.add(geometryFeatureEntity)
|
||||
}
|
||||
return featureEntityList;
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理车道边线HadLaneMarkLink
|
||||
* */
|
||||
private fun parserHadLaneMarkLink(hadLaneMarkLinkList: List<Hadlanemarklink.HadLaneMarkLink>, file: File,
|
||||
layerName: String= "车道边线", layerTableName: String = "HAD_LANE_MARK_LINK"): List<GeometryFeatureEntity> {
|
||||
val featureEntityList = ArrayList<GeometryFeatureEntity>()
|
||||
if (hadLaneMarkLinkList?.isEmpty()) {
|
||||
return featureEntityList
|
||||
}
|
||||
for (hadLaneMarkLink in hadLaneMarkLinkList) {
|
||||
val geometry = NavinfoPbfData.createGeometry(hadLaneMarkLink.geometry)
|
||||
val geometryFeatureEntity =
|
||||
convertDefaultHDEntity(geometry, hadLaneMarkLink, layerName, layerTableName, file)
|
||||
featureEntityList.add(geometryFeatureEntity)
|
||||
}
|
||||
|
||||
return featureEntityList;
|
||||
}
|
||||
/**
|
||||
* 处理车道边线HadLaneMarkLinkTraversal
|
||||
* */
|
||||
private fun parserHadLaneMarkLinkTraversal(hadLaneMarkLinkList: List<Hadlanemarklink.HadLaneMarkLink>, file: File,
|
||||
layerName: String= "车道边线可跨越性", layerTableName: String = "HAD_LANE_MARK_LINK_TRAVERSAL"): List<GeometryFeatureEntity> {
|
||||
val featureEntityList = ArrayList<GeometryFeatureEntity>()
|
||||
if (hadLaneMarkLinkList?.isEmpty()) {
|
||||
return featureEntityList
|
||||
}
|
||||
// 解析道路边线可跨越性
|
||||
for (hadLaneMarkLink in hadLaneMarkLinkList) {
|
||||
// for (traversal in hadLaneMarkLink.traversalList) {
|
||||
// // 根据给出的起终点计算切割点在线上的index
|
||||
// val startPoint3d = traversal.paPosition.sIndex
|
||||
// val endPoint3d = traversal.paPosition.eIndex
|
||||
// val subIndex:List<Map<String, Any>> = List()
|
||||
// val sortedIndex =subIndex.sortedBy { item->item["startIndex"].toString().toInt() }
|
||||
// for ((index, map) in sortedIndex.withIndex()) {
|
||||
//
|
||||
// }
|
||||
// }
|
||||
for (traversal in hadLaneMarkLink.traversalList) {
|
||||
val startPoint3d = traversal.paPosition.sIndex
|
||||
val endPoint3d = traversal.paPosition.eIndex
|
||||
val traversalLineString = subLine(hadLaneMarkLink.geometry, startPoint3d, endPoint3d)
|
||||
val geometryFeatureEntity =
|
||||
convertDefaultHDEntity(traversalLineString, traversal, layerName, layerTableName, file)
|
||||
geometryFeatureEntity.otherProperties["TYPE"] = traversal.type.toString()
|
||||
featureEntityList.add(geometryFeatureEntity)
|
||||
}
|
||||
}
|
||||
|
||||
return featureEntityList;
|
||||
}
|
||||
/**
|
||||
* 处理车道边线HadLaneMarkLinkBoundary
|
||||
* */
|
||||
private fun parserHadLaneMarkLinkBoundary(hadLaneMarkLinkList: List<Hadlanemarklink.HadLaneMarkLink>, file: File,
|
||||
layerName: String= "车道边线非标线类型", layerTableName: String = "HAD_LANE_MARK_LINK_BOUNDARY"): List<GeometryFeatureEntity> {
|
||||
val featureEntityList = ArrayList<GeometryFeatureEntity>()
|
||||
if (hadLaneMarkLinkList?.isEmpty()) {
|
||||
return featureEntityList
|
||||
}
|
||||
// 解析道路边线可跨越性
|
||||
for (hadLaneMarkLink in hadLaneMarkLinkList) {
|
||||
for (boundary in hadLaneMarkLink.boundaryList) {
|
||||
val startPoint3d = boundary.paPosition.sIndex
|
||||
val endPoint3d = boundary.paPosition.eIndex
|
||||
val boundaryLineString = subLine(hadLaneMarkLink.geometry, startPoint3d, endPoint3d)
|
||||
val geometryFeatureEntity =
|
||||
convertDefaultHDEntity(boundaryLineString, boundary, layerName, layerTableName, file)
|
||||
if (boundary.boundaryType.type!=2) {
|
||||
geometryFeatureEntity.otherProperties["BOUNDARY_TYPE"] = boundary.boundaryType.type.toString()
|
||||
featureEntityList.add(geometryFeatureEntity)
|
||||
} else {
|
||||
if (boundary.markingList!=null&&boundary.markingList.isNotEmpty()) {
|
||||
for (mark in boundary.markingList) {
|
||||
val geometryFeatureEntity =
|
||||
convertDefaultHDEntity(boundaryLineString, mark, "车道边线标线类型", "HAD_LANE_MARK_LINK_MARKING", file)
|
||||
geometryFeatureEntity.otherProperties["BOUNDARY_TYPE"] = "2"
|
||||
geometryFeatureEntity.otherProperties["MARK_TYPE"] = mark.markType.toString()
|
||||
geometryFeatureEntity.otherProperties["MARK_COLOR"] = mark.markColor.toString()
|
||||
// 根据mark的横向偏移量重新设置geometry值
|
||||
val lineString = boundaryLineString as LineString
|
||||
var degree = Angle.toDegrees(Angle.angle(lineString.startPoint.coordinate, lineString.endPoint.coordinate))
|
||||
degree = degree+90
|
||||
val transformation:AffineTransformation = AffineTransformationFactory.createFromControlVectors(
|
||||
Coordinate(0.toDouble(), 0.toDouble()), Coordinate(sin(degree) *(mark.lateralOffset*7e-9).toDouble(), cos(degree)*mark.lateralOffset*7e-9.toDouble())
|
||||
)
|
||||
val transformGeometry = transformation.transform(boundaryLineString)
|
||||
geometryFeatureEntity.geometry = transformGeometry.toString()
|
||||
featureEntityList.add(geometryFeatureEntity)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return featureEntityList;
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理道路方向
|
||||
* */
|
||||
private fun parserRoadDirect(roadLinkList: List<Roadlink.RoadLink>, file: File,
|
||||
layerName: String= "道路方向", layerTableName: String = "ROAD_LINK_DIRECT"): List<GeometryFeatureEntity> {
|
||||
val featureEntityList = ArrayList<GeometryFeatureEntity>()
|
||||
if (roadLinkList?.isEmpty()) {
|
||||
return featureEntityList
|
||||
}
|
||||
for (roadLink in roadLinkList) {
|
||||
val geometry = NavinfoPbfData.createGeometry(roadLink.geometry)
|
||||
// 解析道路方向
|
||||
for (dirct in roadLink.directList) {
|
||||
val startPoint3d = dirct.paPosition.sIndex
|
||||
val endPoint3d = dirct.paPosition.eIndex
|
||||
val directLineString = subLine(roadLink.geometry, startPoint3d, endPoint3d)
|
||||
val geometryFeatureEntity =
|
||||
convertDefaultHDEntity(directLineString, dirct, layerName, layerTableName, file)
|
||||
geometryFeatureEntity.otherProperties["VALUE"] = dirct.value.toString()
|
||||
featureEntityList.add(geometryFeatureEntity)
|
||||
}
|
||||
}
|
||||
|
||||
return featureEntityList;
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理道路的桥属性
|
||||
* */
|
||||
private fun parserRoadBrunnel(roadLinkList: List<Roadlink.RoadLink>, file: File,
|
||||
layerName: String= "桥隧道", layerTableName: String = "ROAD_LINK_BRUNNEL"): List<GeometryFeatureEntity> {
|
||||
val featureEntityList = ArrayList<GeometryFeatureEntity>()
|
||||
if (roadLinkList?.isEmpty()) {
|
||||
return featureEntityList
|
||||
}
|
||||
for (roadLink in roadLinkList) {
|
||||
// 解析桥属性IS_BRUNNEL
|
||||
for (brunnel in roadLink.isBrunnelList) {
|
||||
val startPoint3d = brunnel.paPosition.sIndex
|
||||
val endPoint3d = brunnel.paPosition.eIndex
|
||||
val brunnelLineString = subLine(roadLink.geometry, startPoint3d, endPoint3d)
|
||||
val geometryFeatureEntity =
|
||||
convertDefaultHDEntity(brunnelLineString, brunnel, layerName, layerTableName, file)
|
||||
geometryFeatureEntity.otherProperties["TYPE"] = brunnel.type.toString()
|
||||
featureEntityList.add(geometryFeatureEntity)
|
||||
}
|
||||
}
|
||||
|
||||
return featureEntityList;
|
||||
}
|
||||
/**
|
||||
* 处理道路的移动式桥属性
|
||||
* */
|
||||
private fun parserRoadMovBrg(roadLinkList: List<Roadlink.RoadLink>, file: File,
|
||||
layerName: String= "移动式桥", layerTableName: String = "ROAD_LINK_MOVBRG"): List<GeometryFeatureEntity> {
|
||||
val featureEntityList = ArrayList<GeometryFeatureEntity>()
|
||||
if (roadLinkList?.isEmpty()) {
|
||||
return featureEntityList
|
||||
}
|
||||
for (roadLink in roadLinkList) {
|
||||
// 解析移动式桥属性MOVBRG
|
||||
for (movbrg in roadLink.movbrgList) {
|
||||
val startPoint3d = movbrg.paPosition.sIndex
|
||||
val endPoint3d = movbrg.paPosition.eIndex
|
||||
val movbrgLineString = subLine(roadLink.geometry, startPoint3d, endPoint3d)
|
||||
val geometryFeatureEntity =
|
||||
convertDefaultHDEntity(movbrgLineString, movbrg, layerName, layerTableName, file)
|
||||
featureEntityList.add(geometryFeatureEntity)
|
||||
}
|
||||
}
|
||||
|
||||
return featureEntityList;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 读取指定的HD元素数据
|
||||
* */
|
||||
private fun convertDefaultHDEntity(geometry: Geometry, generatedMsg: GeneratedMessageV3, layerName: String, layerTableName: String, file: File): GeometryFeatureEntity {
|
||||
// 处理roadLink
|
||||
val layerEntity = LayerEntity()
|
||||
layerEntity.layerName = layerName
|
||||
layerEntity.layerTableName = layerTableName
|
||||
layerEntity.fromDataName = file.name
|
||||
// 转换geometry和属性信息为数据库字段
|
||||
val renderMap = obtainRenderMap(layerTableName)
|
||||
val featureEntity =
|
||||
NavinfoPbfData.createGeometryEntity(geometry, generatedMsg.allFields, renderMap)
|
||||
featureEntity.name = layerName
|
||||
featureEntity.layerEntity = layerEntity
|
||||
return featureEntity
|
||||
}
|
||||
|
||||
private fun obtainRenderMap(kind: String): Map<String, String> {
|
||||
val renderPropMap = HashMap<String, String>()
|
||||
renderPropMap.put("navinfo_hd", kind)
|
||||
return renderPropMap
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据给定的起终点截取LineString3D
|
||||
* */
|
||||
private fun subLine(line: com.navinfo.onemap.det.sdkpbf.proto.geometry.Geometry.LineString3d,
|
||||
sPoint: com.navinfo.onemap.det.sdkpbf.proto.geometry.Geometry.Point3d,
|
||||
ePoint: com.navinfo.onemap.det.sdkpbf.proto.geometry.Geometry.Point3d): Geometry {
|
||||
var sIndex = 0
|
||||
var eIndex = 0
|
||||
for ((index, linePoint) in line.linestringPointsList.withIndex()) {
|
||||
if (sIndex <= eIndex&&linePoint.equals(sPoint)) {
|
||||
sIndex = index
|
||||
} else if (linePoint.equals(ePoint)) {
|
||||
eIndex = index
|
||||
break
|
||||
}
|
||||
}
|
||||
if (eIndex>sIndex&&eIndex<line.linestringPointsList.size) {
|
||||
// 转换为geometry
|
||||
// 注意,因为sublist取的尾数为参数-1,所以eIndex需要加1
|
||||
val coordinateArrays: List<Coordinate> = line.linestringPointsList.subList(sIndex, eIndex+1).stream().map { Coordinate(it.latLon.longitudeDegrees, it.latLon.latitudeDegrees, it.elevation.z) }.toList()
|
||||
val toTypedArray = coordinateArrays.toTypedArray()
|
||||
if (toTypedArray.size == 1) {
|
||||
toTypedArray[1] = toTypedArray[0].copy()
|
||||
}
|
||||
// println("toTypedArray点数:${toTypedArray.size},sIndex:${sIndex},eIndex:${eIndex}")
|
||||
return GeometryTools.getLineStrinGeo(toTypedArray)
|
||||
}
|
||||
// 如果没能截取到子线段,则返回第一个点组成的线
|
||||
return GeometryTools.getLineStrinGeo(
|
||||
arrayOf(
|
||||
Coordinate(line.linestringPointsList.get(0).latLon.longitudeDegrees, line.linestringPointsList.get(0).latLon.latitudeDegrees, line.linestringPointsList.get(0).elevation.z),
|
||||
Coordinate(line.linestringPointsList.get(1).latLon.longitudeDegrees, line.linestringPointsList.get(1).latLon.latitudeDegrees, line.linestringPointsList.get(1).elevation.z))
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,488 @@
|
||||
package com.navinfo.collect.library.data;
|
||||
|
||||
import android.content.Context;
|
||||
import android.util.Log;
|
||||
|
||||
import com.google.protobuf.GeneratedMessageV3;
|
||||
import com.navinfo.collect.library.data.entity.GeometryFeatureEntity;
|
||||
import com.navinfo.collect.library.data.entity.LayerEntity;
|
||||
import com.navinfo.collect.library.map.NILayerManager;
|
||||
import com.navinfo.collect.library.utils.GeometryTools;
|
||||
import com.navinfo.onemap.det.sdkpbf.proto.crowdsource.Hadlanelink;
|
||||
import com.navinfo.onemap.det.sdkpbf.proto.crowdsource.Hadlanemarklink;
|
||||
import com.navinfo.onemap.det.sdkpbf.proto.crowdsource.Objectarrow;
|
||||
import com.navinfo.onemap.det.sdkpbf.proto.crowdsource.Objectcrosswalk;
|
||||
import com.navinfo.onemap.det.sdkpbf.proto.crowdsource.Objectpole;
|
||||
import com.navinfo.onemap.det.sdkpbf.proto.crowdsource.Objectsymbol;
|
||||
import com.navinfo.onemap.det.sdkpbf.proto.crowdsource.Objecttrafficlights;
|
||||
import com.navinfo.onemap.det.sdkpbf.proto.crowdsource.Resultdata;
|
||||
import com.navinfo.onemap.det.sdkpbf.proto.crowdsource.object.BoundaryOuterClass;
|
||||
import com.navinfo.onemap.det.sdkpbf.proto.crowdsource.object.Boundarytype;
|
||||
import com.navinfo.onemap.det.sdkpbf.proto.crowdsource.object.MarkingOuterClass;
|
||||
import com.navinfo.onemap.det.sdkpbf.proto.crowdsource.object.Paposition;
|
||||
|
||||
import org.locationtech.jts.geom.Coordinate;
|
||||
import org.locationtech.jts.geom.Geometry;
|
||||
import org.locationtech.jts.geom.LineString;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import io.realm.Realm;
|
||||
import io.realm.RealmConfiguration;
|
||||
import io.realm.RealmModel;
|
||||
import kotlin.jvm.internal.Intrinsics;
|
||||
|
||||
|
||||
public class RealmUtils {
|
||||
private Context mContext;
|
||||
private static RealmUtils instance;
|
||||
private Realm realm;
|
||||
private RealmConfiguration realmConfiguration;
|
||||
private String defaultDir = NILayerManager.defaultDir;
|
||||
private String realmName;
|
||||
|
||||
private final String NAME = "name";
|
||||
private final String TYPE = "navi_type";
|
||||
|
||||
public void init(Context mContext, String dir, String realmName) {
|
||||
this.mContext = mContext;
|
||||
Realm.init(this.mContext);
|
||||
this.realmName = realmName;
|
||||
realmConfiguration = new RealmConfiguration.Builder()
|
||||
.directory(dir == null?new File(defaultDir): new File(dir))
|
||||
.name(realmName)
|
||||
.allowWritesOnUiThread(true)
|
||||
.allowQueriesOnUiThread(true)
|
||||
.schemaVersion(1)
|
||||
.deleteRealmIfMigrationNeeded()
|
||||
.encryptionKey(Arrays.copyOf(new String("encryp").getBytes(StandardCharsets.UTF_8), 64))
|
||||
.build();
|
||||
System.out.println("encryp:"+toHexString(Arrays.copyOf(new String("encryp").getBytes(StandardCharsets.UTF_8), 64)));
|
||||
Realm.setDefaultConfiguration(realmConfiguration);
|
||||
}
|
||||
|
||||
public static RealmUtils getInstance() {
|
||||
if (instance == null) {
|
||||
instance = new RealmUtils();
|
||||
}
|
||||
return instance;
|
||||
}
|
||||
|
||||
public Realm getRealm() throws Exception {
|
||||
if (realmConfiguration == null) {
|
||||
throw new Exception("请先调用Realm.init方法初始化!");
|
||||
}
|
||||
return Realm.getDefaultInstance();
|
||||
}
|
||||
|
||||
public RealmConfiguration getRealmConfiguration() {
|
||||
return realmConfiguration;
|
||||
}
|
||||
|
||||
|
||||
private static final char[] HEX_CHAR = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
|
||||
/**
|
||||
* 方法一:将byte类型数组转化成16进制字符串
|
||||
* @explain 字符串拼接
|
||||
* @param bytes
|
||||
* @return
|
||||
*/
|
||||
public static String toHexString(byte[] bytes) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
int num;
|
||||
for (byte b : bytes) {
|
||||
num = b < 0 ? 256 + b : b;
|
||||
sb.append(HEX_CHAR[num / 16]).append(HEX_CHAR[num % 16]);
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
public float getFileSize(){
|
||||
|
||||
File file = new File(defaultDir+"/"+realmName);
|
||||
|
||||
if(file.exists()&&file.isFile()){
|
||||
return file.length();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
public Map<String, Object> importPbfData(List<String> files) throws IOException {
|
||||
NavinfoPbfData pbfData = new NavinfoPbfData();
|
||||
long startTime = System.currentTimeMillis();
|
||||
RealmUtils var10000 = RealmUtils.getInstance();
|
||||
Intrinsics.checkNotNullExpressionValue(var10000, "RealmUtils.getInstance()");
|
||||
Realm.getInstance(var10000.getRealmConfiguration()).beginTransaction();
|
||||
Iterator fileIterator = files.iterator();
|
||||
|
||||
while(fileIterator.hasNext()) {
|
||||
String file = (String)fileIterator.next();
|
||||
Log.d("Realm-file:", file);
|
||||
System.out.print("file:" + file);
|
||||
Resultdata.ResultData resultData = pbfData.readPbfData((InputStream)(new FileInputStream(new File(file))));
|
||||
long sqlStartTime = System.currentTimeMillis();
|
||||
Intrinsics.checkNotNullExpressionValue(resultData, "resultData");
|
||||
List var40 = resultData.getHadlanelinkList();
|
||||
Intrinsics.checkNotNullExpressionValue(var40, "resultData.hadlanelinkList");
|
||||
Collection var9 = (Collection)var40;
|
||||
HashMap extentMapx;
|
||||
Iterator var12;
|
||||
Geometry geometry;
|
||||
GeometryFeatureEntity geometryEntityx;
|
||||
LayerEntity layerEntity;
|
||||
if (!var9.isEmpty()) {
|
||||
layerEntity = new LayerEntity();
|
||||
layerEntity.setLayerName("test");
|
||||
extentMapx = new HashMap();
|
||||
((Map)extentMapx).put(NAME, "车道中心线");
|
||||
((Map)extentMapx).put(TYPE, "had_lane_link");
|
||||
var12 = resultData.getHadlanelinkList().iterator();
|
||||
|
||||
while(var12.hasNext()) {
|
||||
Hadlanelink.HadLaneLink hadLaneLink = (Hadlanelink.HadLaneLink)var12.next();
|
||||
Intrinsics.checkNotNullExpressionValue(hadLaneLink, "hadLaneLink");
|
||||
geometry = pbfData.createGeometry((GeneratedMessageV3)hadLaneLink.getGeometry());
|
||||
geometryEntityx = pbfData.createGeometryEntity(geometry, hadLaneLink.getAllFields(), (Map)extentMapx);
|
||||
Intrinsics.checkNotNullExpressionValue(geometryEntityx, "geometryEntity");
|
||||
geometryEntityx.setLayerEntity(layerEntity);
|
||||
geometryEntityx.setName((String)extentMapx.get(NAME));
|
||||
var10000 = RealmUtils.getInstance();
|
||||
Intrinsics.checkNotNullExpressionValue(var10000, "RealmUtils.getInstance()");
|
||||
Realm.getInstance(var10000.getRealmConfiguration()).insertOrUpdate((RealmModel)geometryEntityx);
|
||||
}
|
||||
}
|
||||
|
||||
var40 = resultData.getHadlanemarklinkList();
|
||||
Intrinsics.checkNotNullExpressionValue(var40, "resultData.hadlanemarklinkList");
|
||||
var9 = (Collection)var40;
|
||||
Geometry lanLinkGeometry;
|
||||
Geometry intersection;
|
||||
if (!var9.isEmpty()) {
|
||||
layerEntity = new LayerEntity();
|
||||
layerEntity.setLayerName("test");
|
||||
extentMapx = new HashMap();
|
||||
((Map)extentMapx).put(NAME, "车道边线");
|
||||
((Map)extentMapx).put(TYPE, "had_lane_mark_link");
|
||||
var12 = resultData.getHadlanemarklinkList().iterator();
|
||||
|
||||
while(var12.hasNext()) {
|
||||
Hadlanemarklink.HadLaneMarkLink hadLaneMarkLink = (Hadlanemarklink.HadLaneMarkLink)var12.next();
|
||||
Intrinsics.checkNotNullExpressionValue(hadLaneMarkLink, "hadLaneMarkLink");
|
||||
geometry = pbfData.createGeometry((GeneratedMessageV3)hadLaneMarkLink.getGeometry());
|
||||
Iterator var15 = hadLaneMarkLink.getBoundaryList().iterator();
|
||||
|
||||
while(var15.hasNext()) {
|
||||
BoundaryOuterClass.Boundary boundary = (BoundaryOuterClass.Boundary)var15.next();
|
||||
Intrinsics.checkNotNullExpressionValue(boundary, "boundary");
|
||||
Paposition.PaPosition var43 = boundary.getPaPosition();
|
||||
Intrinsics.checkNotNullExpressionValue(var43, "boundary.paPosition");
|
||||
com.navinfo.onemap.det.sdkpbf.proto.geometry.Geometry.Point3d sPoint = var43.getSIndex();
|
||||
var43 = boundary.getPaPosition();
|
||||
Intrinsics.checkNotNullExpressionValue(var43, "boundary.paPosition");
|
||||
com.navinfo.onemap.det.sdkpbf.proto.geometry.Geometry.Point3d ePoint = var43.getEIndex();
|
||||
Coordinate[] var45 = new Coordinate[2];
|
||||
Intrinsics.checkNotNullExpressionValue(sPoint, "sPoint");
|
||||
com.navinfo.onemap.det.sdkpbf.proto.geometry.Geometry.Point2d var10005 = sPoint.getLatLon();
|
||||
Intrinsics.checkNotNullExpressionValue(var10005, "sPoint.latLon");
|
||||
double fileIterator0 = var10005.getLongitudeDegrees();
|
||||
com.navinfo.onemap.det.sdkpbf.proto.geometry.Geometry.Point2d var10006 = sPoint.getLatLon();
|
||||
Intrinsics.checkNotNullExpressionValue(var10006, "sPoint.latLon");
|
||||
double fileIterator3 = var10006.getLatitudeDegrees();
|
||||
com.navinfo.onemap.det.sdkpbf.proto.geometry.Geometry.ElevationMeasure var10007 = sPoint.getElevation();
|
||||
Intrinsics.checkNotNullExpressionValue(var10007, "sPoint.elevation");
|
||||
var45[0] = new Coordinate(fileIterator0, fileIterator3, var10007.getZ());
|
||||
Intrinsics.checkNotNullExpressionValue(ePoint, "ePoint");
|
||||
var10005 = ePoint.getLatLon();
|
||||
Intrinsics.checkNotNullExpressionValue(var10005, "ePoint.latLon");
|
||||
fileIterator0 = var10005.getLongitudeDegrees();
|
||||
var10006 = ePoint.getLatLon();
|
||||
Intrinsics.checkNotNullExpressionValue(var10006, "ePoint.latLon");
|
||||
fileIterator3 = var10006.getLatitudeDegrees();
|
||||
var10007 = ePoint.getElevation();
|
||||
Intrinsics.checkNotNullExpressionValue(var10007, "ePoint.elevation");
|
||||
var45[1] = new Coordinate(fileIterator0, fileIterator3, var10007.getZ());
|
||||
LineString line = GeometryTools.getLineStrinGeo(var45);
|
||||
lanLinkGeometry = line.buffer(1.0E-7D, 1, 2);
|
||||
intersection = geometry.intersection(lanLinkGeometry);
|
||||
extentMapx.remove("boundary_type");
|
||||
extentMapx.remove("mark_type");
|
||||
extentMapx.remove("mark_color");
|
||||
extentMapx.remove("mark_material");
|
||||
extentMapx.remove("mark_width");
|
||||
if (boundary.getBoundaryType() != null) {
|
||||
Map var46 = (Map)extentMapx;
|
||||
Boundarytype.BoundaryType2 var10002 = boundary.getBoundaryType();
|
||||
Intrinsics.checkNotNullExpressionValue(var10002, "boundary.boundaryType");
|
||||
var46.put("boundary_type", String.valueOf(var10002.getType()));
|
||||
Boundarytype.BoundaryType2 var49 = boundary.getBoundaryType();
|
||||
Intrinsics.checkNotNullExpressionValue(var49, "boundary.boundaryType");
|
||||
if (var49.getType() == 2 && boundary.getMarkingList() != null) {
|
||||
var40 = boundary.getMarkingList();
|
||||
Intrinsics.checkNotNullExpressionValue(var40, "boundary.markingList");
|
||||
Collection var21 = (Collection)var40;
|
||||
if (!var21.isEmpty()) {
|
||||
var46 = (Map)extentMapx;
|
||||
Object var48 = boundary.getMarkingList().get(0);
|
||||
Intrinsics.checkNotNullExpressionValue(var48, "boundary.markingList[0]");
|
||||
var46.put("mark_type", String.valueOf(((MarkingOuterClass.Marking)var48).getMarkType()));
|
||||
var46 = (Map)extentMapx;
|
||||
var48 = boundary.getMarkingList().get(0);
|
||||
Intrinsics.checkNotNullExpressionValue(var48, "boundary.markingList[0]");
|
||||
var46.put("mark_color", String.valueOf(((MarkingOuterClass.Marking)var48).getMarkColor()));
|
||||
var46 = (Map)extentMapx;
|
||||
var48 = boundary.getMarkingList().get(0);
|
||||
Intrinsics.checkNotNullExpressionValue(var48, "boundary.markingList[0]");
|
||||
var46.put("mark_material", String.valueOf(((MarkingOuterClass.Marking)var48).getMarkMaterial()));
|
||||
var46 = (Map)extentMapx;
|
||||
var48 = boundary.getMarkingList().get(0);
|
||||
Intrinsics.checkNotNullExpressionValue(var48, "boundary.markingList[0]");
|
||||
var46.put("mark_width", String.valueOf(((MarkingOuterClass.Marking)var48).getMarkWidth()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
GeometryFeatureEntity geometryEntity = pbfData.createGeometryEntity(intersection, boundary.getAllFields(), (Map)extentMapx);
|
||||
Intrinsics.checkNotNullExpressionValue(geometryEntity, "geometryEntity");
|
||||
geometryEntity.setLayerEntity(layerEntity);
|
||||
geometryEntity.setName((String)extentMapx.get(NAME));
|
||||
var10000 = RealmUtils.getInstance();
|
||||
Intrinsics.checkNotNullExpressionValue(var10000, "RealmUtils.getInstance()");
|
||||
Realm.getInstance(var10000.getRealmConfiguration()).insertOrUpdate((RealmModel)geometryEntity);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var40 = resultData.getObjectarrowList();
|
||||
Intrinsics.checkNotNullExpressionValue(var40, "resultData.objectarrowList");
|
||||
var9 = (Collection)var40;
|
||||
if (!var9.isEmpty()) {
|
||||
layerEntity = new LayerEntity();
|
||||
layerEntity.setLayerName("test");
|
||||
extentMapx = new HashMap();
|
||||
((Map)extentMapx).put(NAME, "道路箭头");
|
||||
((Map)extentMapx).put(TYPE, "object_arrow");
|
||||
var12 = resultData.getObjectarrowList().iterator();
|
||||
|
||||
label143:
|
||||
while(true) {
|
||||
label141:
|
||||
while(true) {
|
||||
if (!var12.hasNext()) {
|
||||
break label143;
|
||||
}
|
||||
|
||||
Objectarrow.ObjectArrow objectArrow = (Objectarrow.ObjectArrow)var12.next();
|
||||
Intrinsics.checkNotNullExpressionValue(objectArrow, "objectArrow");
|
||||
geometry = pbfData.createGeometry((GeneratedMessageV3)objectArrow.getGeometry());
|
||||
geometryEntityx = pbfData.createGeometryEntity(geometry, objectArrow.getAllFields(), (Map)extentMapx);
|
||||
Intrinsics.checkNotNullExpressionValue(geometryEntityx, "geometryEntity");
|
||||
geometryEntityx.setLayerEntity(layerEntity);
|
||||
geometryEntityx.setName((String)extentMapx.get(NAME));
|
||||
var10000 = RealmUtils.getInstance();
|
||||
Intrinsics.checkNotNullExpressionValue(var10000, "RealmUtils.getInstance()");
|
||||
Realm.getInstance(var10000.getRealmConfiguration()).insertOrUpdate((RealmModel)geometryEntityx);
|
||||
Iterator var41 = objectArrow.getLanePidList().iterator();
|
||||
|
||||
while(var41.hasNext()) {
|
||||
String lanLinkPid = (String)var41.next();
|
||||
Iterator var47 = resultData.getHadlanelinkList().iterator();
|
||||
|
||||
while(var47.hasNext()) {
|
||||
Hadlanelink.HadLaneLink lanLink = (Hadlanelink.HadLaneLink)var47.next();
|
||||
Intrinsics.checkNotNullExpressionValue(lanLink, "lanLink");
|
||||
if (lanLinkPid.equals(lanLink.getLaneLinkPid())) {
|
||||
lanLinkGeometry = pbfData.createGeometry((GeneratedMessageV3)lanLink.getGeometry());
|
||||
if (lanLinkGeometry.intersects(geometry)) {
|
||||
intersection = geometry.intersection(lanLinkGeometry);
|
||||
Intrinsics.checkNotNullExpressionValue(intersection, "intersection");
|
||||
if (intersection.isValid() && !intersection.isEmpty() && intersection instanceof LineString) {
|
||||
HashMap extentMap = new HashMap();
|
||||
((Map)extentMap).put(TYPE, "symbol_object_arrow");
|
||||
((Map)extentMap).put(NAME, "symbol_object_arrow");
|
||||
((Map)extentMap).put("arrow_class", String.valueOf(objectArrow.getArrowClass()));
|
||||
GeometryFeatureEntity geometryEntityxx = pbfData.createGeometryEntity(intersection, (Map)null, (Map)extentMap);
|
||||
Intrinsics.checkNotNullExpressionValue(geometryEntityxx, "geometryEntity");
|
||||
geometryEntityxx.setLayerEntity(layerEntity);
|
||||
geometryEntityxx.setName((String)extentMap.get(NAME));
|
||||
var10000 = RealmUtils.getInstance();
|
||||
Intrinsics.checkNotNullExpressionValue(var10000, "RealmUtils.getInstance()");
|
||||
Realm.getInstance(var10000.getRealmConfiguration()).insertOrUpdate((RealmModel)geometryEntityxx);
|
||||
}
|
||||
continue label141;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var40 = resultData.getObjectcrosswalkList();
|
||||
Intrinsics.checkNotNullExpressionValue(var40, "resultData.objectcrosswalkList");
|
||||
var9 = (Collection)var40;
|
||||
if (!var9.isEmpty()) {
|
||||
layerEntity = new LayerEntity();
|
||||
layerEntity.setLayerName("test");
|
||||
extentMapx = new HashMap();
|
||||
((Map)extentMapx).put(NAME, "人行横道");
|
||||
((Map)extentMapx).put(TYPE, "object_crosswalk");
|
||||
var12 = resultData.getObjectcrosswalkList().iterator();
|
||||
|
||||
while(var12.hasNext()) {
|
||||
Objectcrosswalk.ObjectCrossWalk objectCross = (Objectcrosswalk.ObjectCrossWalk)var12.next();
|
||||
Intrinsics.checkNotNullExpressionValue(objectCross, "objectCross");
|
||||
geometry = pbfData.createGeometry((GeneratedMessageV3)objectCross.getGeometry());
|
||||
geometryEntityx = pbfData.createGeometryEntity(geometry, objectCross.getAllFields(), (Map)extentMapx);
|
||||
Intrinsics.checkNotNullExpressionValue(geometryEntityx, "geometryEntity");
|
||||
geometryEntityx.setLayerEntity(layerEntity);
|
||||
geometryEntityx.setName((String)extentMapx.get(NAME));
|
||||
var10000 = RealmUtils.getInstance();
|
||||
Intrinsics.checkNotNullExpressionValue(var10000, "RealmUtils.getInstance()");
|
||||
Realm.getInstance(var10000.getRealmConfiguration()).insertOrUpdate((RealmModel)geometryEntityx);
|
||||
}
|
||||
}
|
||||
|
||||
var40 = resultData.getObjectpoleList();
|
||||
Intrinsics.checkNotNullExpressionValue(var40, "resultData.objectpoleList");
|
||||
var9 = (Collection)var40;
|
||||
HashMap extentMap3D;
|
||||
Iterator var36;
|
||||
Geometry geometryx;
|
||||
GeometryFeatureEntity geometryEntityxxx;
|
||||
GeometryFeatureEntity symbolEntity;
|
||||
if (!var9.isEmpty()) {
|
||||
layerEntity = new LayerEntity();
|
||||
layerEntity.setLayerName("test");
|
||||
extentMapx = new HashMap();
|
||||
((Map)extentMapx).put(NAME, "杆状物");
|
||||
((Map)extentMapx).put(TYPE, "object_pole");
|
||||
extentMap3D = new HashMap();
|
||||
((Map)extentMap3D).put(TYPE, "symbol_object_pole");
|
||||
((Map)extentMap3D).put(NAME, "symbol_object_pole");
|
||||
var36 = resultData.getObjectpoleList().iterator();
|
||||
|
||||
while(var36.hasNext()) {
|
||||
Objectpole.ObjectPole objectPole = (Objectpole.ObjectPole)var36.next();
|
||||
Intrinsics.checkNotNullExpressionValue(objectPole, "objectPole");
|
||||
geometryx = pbfData.createGeometry((GeneratedMessageV3)objectPole.getGeometry());
|
||||
geometryEntityxxx = pbfData.createGeometryEntity(geometryx, objectPole.getAllFields(), (Map)extentMapx);
|
||||
Intrinsics.checkNotNullExpressionValue(geometryEntityxxx, "geometryEntity");
|
||||
geometryEntityxxx.setLayerEntity(layerEntity);
|
||||
geometryEntityxxx.setName((String)extentMapx.get(TYPE));
|
||||
var10000 = RealmUtils.getInstance();
|
||||
Intrinsics.checkNotNullExpressionValue(var10000, "RealmUtils.getInstance()");
|
||||
Realm.getInstance(var10000.getRealmConfiguration()).insertOrUpdate((RealmModel)geometryEntityxxx);
|
||||
Intrinsics.checkNotNullExpressionValue(geometryx, "geometry");
|
||||
symbolEntity = pbfData.createGeometryEntity((Geometry)geometryx.getCentroid(), (Map)null, (Map)extentMap3D);
|
||||
Intrinsics.checkNotNullExpressionValue(symbolEntity, "symbolEntity");
|
||||
symbolEntity.setLayerEntity(layerEntity);
|
||||
symbolEntity.setName((String)extentMap3D.get(NAME));
|
||||
var10000 = RealmUtils.getInstance();
|
||||
Intrinsics.checkNotNullExpressionValue(var10000, "RealmUtils.getInstance()");
|
||||
Realm.getInstance(var10000.getRealmConfiguration()).insertOrUpdate((RealmModel)symbolEntity);
|
||||
}
|
||||
}
|
||||
|
||||
var40 = resultData.getObjectsymbolList();
|
||||
Intrinsics.checkNotNullExpressionValue(var40, "resultData.objectsymbolList");
|
||||
var9 = (Collection)var40;
|
||||
if (!var9.isEmpty()) {
|
||||
layerEntity = new LayerEntity();
|
||||
layerEntity.setLayerName("test");
|
||||
extentMapx = new HashMap();
|
||||
((Map)extentMapx).put(NAME, "对象标志");
|
||||
((Map)extentMapx).put(TYPE, "object_symbol");
|
||||
extentMap3D = new HashMap();
|
||||
((Map)extentMap3D).put(NAME, "symbol_object_symbol");
|
||||
((Map)extentMap3D).put(TYPE, "symbol_object_symbol");
|
||||
var36 = resultData.getObjectsymbolList().iterator();
|
||||
|
||||
while(var36.hasNext()) {
|
||||
Objectsymbol.ObjectSymbol objectSymbol = (Objectsymbol.ObjectSymbol)var36.next();
|
||||
Intrinsics.checkNotNullExpressionValue(objectSymbol, "objectSymbol");
|
||||
geometryx = pbfData.createGeometry((GeneratedMessageV3)objectSymbol.getGeometry());
|
||||
geometryEntityxxx = pbfData.createGeometryEntity(geometryx, objectSymbol.getAllFields(), (Map)extentMapx);
|
||||
Intrinsics.checkNotNullExpressionValue(geometryEntityxxx, "geometryEntity");
|
||||
geometryEntityxxx.setLayerEntity(layerEntity);
|
||||
geometryEntityxxx.setName((String)extentMapx.get(NAME));
|
||||
var10000 = RealmUtils.getInstance();
|
||||
Intrinsics.checkNotNullExpressionValue(var10000, "RealmUtils.getInstance()");
|
||||
Realm.getInstance(var10000.getRealmConfiguration()).insertOrUpdate((RealmModel)geometryEntityxxx);
|
||||
Intrinsics.checkNotNullExpressionValue(geometryx, "geometry");
|
||||
symbolEntity = pbfData.createGeometryEntity((Geometry)geometryx.getCentroid(), (Map)null, (Map)extentMap3D);
|
||||
Intrinsics.checkNotNullExpressionValue(symbolEntity, "symbolEntity");
|
||||
symbolEntity.setLayerEntity(layerEntity);
|
||||
symbolEntity.setName((String)extentMap3D.get(NAME));
|
||||
var10000 = RealmUtils.getInstance();
|
||||
Intrinsics.checkNotNullExpressionValue(var10000, "RealmUtils.getInstance()");
|
||||
Realm.getInstance(var10000.getRealmConfiguration()).insertOrUpdate((RealmModel)symbolEntity);
|
||||
}
|
||||
}
|
||||
|
||||
var40 = resultData.getObjecttrafficlightsList();
|
||||
Intrinsics.checkNotNullExpressionValue(var40, "resultData.objecttrafficlightsList");
|
||||
var9 = (Collection)var40;
|
||||
if (!var9.isEmpty()) {
|
||||
layerEntity = new LayerEntity();
|
||||
layerEntity.setLayerName("test");
|
||||
extentMapx = new HashMap();
|
||||
((Map)extentMapx).put(NAME, "交通灯");
|
||||
((Map)extentMapx).put(TYPE, "object_traffic");
|
||||
extentMap3D = new HashMap();
|
||||
((Map)extentMap3D).put(TYPE, "symbol_object_traffic");
|
||||
((Map)extentMap3D).put(NAME, "symbol_object_traffic");
|
||||
var36 = resultData.getObjecttrafficlightsList().iterator();
|
||||
|
||||
while(var36.hasNext()) {
|
||||
Objecttrafficlights.ObjectTrafficLights objectTrrafic = (Objecttrafficlights.ObjectTrafficLights)var36.next();
|
||||
Intrinsics.checkNotNullExpressionValue(objectTrrafic, "objectTrrafic");
|
||||
geometryx = pbfData.createGeometry((GeneratedMessageV3)objectTrrafic.getGeometry());
|
||||
geometryEntityxxx = pbfData.createGeometryEntity(geometryx, objectTrrafic.getAllFields(), (Map)extentMapx);
|
||||
Intrinsics.checkNotNullExpressionValue(geometryEntityxxx, "geometryEntity");
|
||||
geometryEntityxxx.setLayerEntity(layerEntity);
|
||||
geometryEntityxxx.setName((String)extentMapx.get(NAME));
|
||||
var10000 = RealmUtils.getInstance();
|
||||
Intrinsics.checkNotNullExpressionValue(var10000, "RealmUtils.getInstance()");
|
||||
Realm.getInstance(var10000.getRealmConfiguration()).insertOrUpdate((RealmModel)geometryEntityxxx);
|
||||
Intrinsics.checkNotNullExpressionValue(geometryx, "geometry");
|
||||
symbolEntity = pbfData.createGeometryEntity((Geometry)geometryx.getCentroid(), (Map)null, (Map)extentMap3D);
|
||||
Intrinsics.checkNotNullExpressionValue(symbolEntity, "symbolEntity");
|
||||
symbolEntity.setLayerEntity(layerEntity);
|
||||
symbolEntity.setName((String)extentMap3D.get(NAME));
|
||||
var10000 = RealmUtils.getInstance();
|
||||
Intrinsics.checkNotNullExpressionValue(var10000, "RealmUtils.getInstance()");
|
||||
Realm.getInstance(var10000.getRealmConfiguration()).insertOrUpdate((RealmModel)symbolEntity);
|
||||
}
|
||||
}
|
||||
|
||||
long sqlEndTime = System.currentTimeMillis();
|
||||
System.out.println(file + "All-Time:" + (sqlEndTime - sqlStartTime));
|
||||
}
|
||||
|
||||
var10000 = RealmUtils.getInstance();
|
||||
Intrinsics.checkNotNullExpressionValue(var10000, "RealmUtils.getInstance()");
|
||||
Realm.getInstance(var10000.getRealmConfiguration()).commitTransaction();
|
||||
long endTime = System.currentTimeMillis();
|
||||
System.out.println("All-Time:" + (endTime - startTime));
|
||||
var10000 = RealmUtils.getInstance();
|
||||
Intrinsics.checkNotNullExpressionValue(var10000, "RealmUtils.getInstance()");
|
||||
Realm.compactRealm(var10000.getRealmConfiguration());
|
||||
System.out.println("Arrow-All-Time: 数据处理结束");
|
||||
Map<String, Object> resultMap = new HashMap<>();
|
||||
resultMap.put("success", true);
|
||||
resultMap.put("msg", "导入成功");
|
||||
resultMap.put("data", (endTime - startTime)+"");
|
||||
return resultMap;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package com.navinfo.collect.library.data.dao.impl
|
||||
|
||||
import androidx.room.Dao
|
||||
import androidx.room.Insert
|
||||
import androidx.room.OnConflictStrategy
|
||||
import androidx.room.Query
|
||||
import com.navinfo.collect.library.data.entity.CheckManager
|
||||
|
||||
@Dao
|
||||
interface ICheckManagerDao {
|
||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||
fun insert(vararg check: CheckManager?)
|
||||
|
||||
@Query("SELECT * FROM CheckManager where id =:id")
|
||||
fun findCheckManagerById(id: Long): CheckManager?
|
||||
|
||||
@Query("SELECT * FROM CheckManager")
|
||||
fun findList(): List<CheckManager>
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
package com.navinfo.collect.library.data.dao.impl;
|
||||
|
||||
import androidx.room.Dao;
|
||||
import androidx.room.Delete;
|
||||
import androidx.room.Insert;
|
||||
import androidx.room.OnConflictStrategy;
|
||||
import androidx.room.Query;
|
||||
import androidx.room.Update;
|
||||
|
||||
import com.navinfo.collect.library.data.entity.Element;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
@Dao
|
||||
public interface IElementDao {
|
||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||
void insert(Element... elements);
|
||||
|
||||
@Update
|
||||
int update(Element[] elements);
|
||||
|
||||
@Delete
|
||||
int delete(Element elements);
|
||||
|
||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||
void inserts(Element[] elements);
|
||||
|
||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||
void insertList(List<Element> elementList);
|
||||
|
||||
@Query("DELETE FROM element")
|
||||
void deleteAll();
|
||||
|
||||
@Query("DELETE FROM element where rowId>:start and rowId<:end")
|
||||
void deleteAll(int start, int end);
|
||||
|
||||
@Query("SELECT * FROM element limit :start,:count")
|
||||
List<Element> findList(int start, int count);
|
||||
|
||||
|
||||
@Query("SELECT * FROM element where 1=1 limit :start,:count ")
|
||||
Element[] findAll(int start, int count);
|
||||
|
||||
|
||||
@Query("SELECT geometry FROM element where rowId>:value limit :start,:count")
|
||||
String[] find(int value, int start, int count);
|
||||
|
||||
@Query("SELECT * FROM element where uuid =:uuid")
|
||||
Element findById(String uuid);
|
||||
|
||||
@Query("SELECT * FROM element where uuid in (select element_uuid from tileElement where tilex>=:minx and tilex<=:maxx and tiley>=:miny and tiley <=:maxy)")
|
||||
List<Element> findList(int minx, int maxx, int miny, int maxy);
|
||||
|
||||
@Query("SELECT * FROM element where uuid in (select distinct element_uuid from tileElement where tilex IN (:xList) and tiley IN (:yList))")
|
||||
List<Element> findList(Set<Integer> xList, Set<Integer> yList);
|
||||
|
||||
@Query("SELECT * FROM element where display_text like '%'||:keyword||'%' limit :start,:count")
|
||||
List<Element> findListByKeyword(String keyword, int start, int count);
|
||||
|
||||
|
||||
@Query("SELECT * FROM element where display_text like '%'||:keyword||'%' and layer_id in (:layerIds) limit :start,:count")
|
||||
List<Element> findListByKeywordLimitLayer(String keyword, Set<String> layerIds, int start, int count);
|
||||
|
||||
@Query("SELECT * FROM element where display_text like '%'||:keyword||'%' and layer_id in (Select uuid from layerManager where project_id in (:projectIds)) limit :start,:count")
|
||||
List<Element> findListByKeywordLimitProject(String keyword, Set<String> projectIds, int start, int count);
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
package com.navinfo.collect.library.data.dao.impl;
|
||||
|
||||
import androidx.room.Dao;
|
||||
import androidx.room.Delete;
|
||||
import androidx.room.Insert;
|
||||
import androidx.room.OnConflictStrategy;
|
||||
import androidx.room.Query;
|
||||
import androidx.room.Update;
|
||||
|
||||
import com.navinfo.collect.library.data.entity.Element;
|
||||
import com.navinfo.collect.library.data.entity.LayerManager;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
@Dao
|
||||
public interface ILayerManagerDao {
|
||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||
void insert(LayerManager... layerManagers);
|
||||
|
||||
@Update
|
||||
int updates(LayerManager[] layerManagers);
|
||||
|
||||
@Update
|
||||
int update(LayerManager layerManagers);
|
||||
|
||||
@Delete
|
||||
int delete(LayerManager layerManagers);
|
||||
|
||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||
void inserts(LayerManager[] layerManagers);
|
||||
|
||||
@Query("DELETE FROM layerManager")
|
||||
void deleteAll();
|
||||
|
||||
@Query("DELETE FROM layerManager where rowId>:start and rowId<:end")
|
||||
void deleteAll(int start, int end);
|
||||
|
||||
@Query("SELECT * FROM layerManager where visibility =:visable")
|
||||
List<LayerManager> findList(int visable);
|
||||
|
||||
@Query("SELECT * FROM layerManager")
|
||||
List<LayerManager> findList();
|
||||
|
||||
@Query("SELECT * FROM layerManager where project_id in(:projectIds)")
|
||||
List<LayerManager> findListByProject(Set<String> projectIds);
|
||||
|
||||
@Query("SELECT * FROM layerManager where uuid =:id")
|
||||
LayerManager findLayerManager(String id);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
package com.navinfo.collect.library.data.dao.impl;
|
||||
|
||||
import androidx.room.Dao;
|
||||
import androidx.room.Delete;
|
||||
import androidx.room.Insert;
|
||||
import androidx.room.OnConflictStrategy;
|
||||
import androidx.room.Query;
|
||||
import androidx.room.Update;
|
||||
|
||||
import com.navinfo.collect.library.data.entity.Element;
|
||||
import com.navinfo.collect.library.data.entity.NiLocation;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
@Dao
|
||||
public interface INiLocationDao {
|
||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||
void insert(NiLocation... niLocations);
|
||||
|
||||
@Update
|
||||
int update(NiLocation niLocation);
|
||||
|
||||
@Update
|
||||
int updateList(NiLocation[] niLocations);
|
||||
|
||||
@Delete
|
||||
int delete(NiLocation niLocations);
|
||||
|
||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||
void inserts(NiLocation[] niLocations);
|
||||
|
||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||
void insertList(List<NiLocation> niLocationList);
|
||||
|
||||
@Query("DELETE FROM niLocation")
|
||||
void deleteAll();
|
||||
|
||||
@Query("SELECT * FROM niLocation where uuid=:id ")
|
||||
NiLocation find(String id);
|
||||
|
||||
@Query("SELECT * FROM niLocation where tilex>=:minx and tilex<=:maxx and tiley>=:miny and tiley <=:maxy")
|
||||
List<NiLocation> findList(int minx, int maxx, int miny, int maxy);
|
||||
|
||||
@Query("SELECT * FROM niLocation where tilex>=:minx and tilex<=:maxx and tiley>=:miny and tiley <=:maxy and time>=:startTime and time<=:endTime")
|
||||
List<NiLocation> timeTofindList(int minx, int maxx, int miny, int maxy,long startTime,long endTime);
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
//package com.navinfo.collect.library.data.dao.impl;
|
||||
//
|
||||
//import androidx.room.Dao;
|
||||
//import androidx.room.Delete;
|
||||
//import androidx.room.Insert;
|
||||
//import androidx.room.OnConflictStrategy;
|
||||
//import androidx.room.Query;
|
||||
//import androidx.room.Update;
|
||||
//import com.navinfo.collect.library.data.entity.Project;
|
||||
//import java.util.List;
|
||||
//
|
||||
//@Dao
|
||||
//public interface IProjectDao {
|
||||
// @Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||
// void insert(Project... projects);
|
||||
//
|
||||
// @Update
|
||||
// int update(Project[] projects);
|
||||
//
|
||||
// @Delete
|
||||
// int delete(Project projects);
|
||||
//
|
||||
// @Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||
// void inserts(Project[] projects);
|
||||
//
|
||||
// @Query("DELETE FROM project")
|
||||
// void deleteAll();
|
||||
//
|
||||
// @Query("DELETE FROM project where rowId>:start and rowId<:end")
|
||||
// void deleteAll(int start, int end);
|
||||
//
|
||||
// @Query("SELECT * FROM project")
|
||||
// List<Project> findList();
|
||||
//
|
||||
// @Query("SELECT * FROM project where uuid =:id")
|
||||
// Project findProject(String id);
|
||||
//}
|
||||
@@ -0,0 +1,39 @@
|
||||
package com.navinfo.collect.library.data.dao.impl;
|
||||
|
||||
import androidx.room.Dao;
|
||||
import androidx.room.Delete;
|
||||
import androidx.room.Insert;
|
||||
import androidx.room.OnConflictStrategy;
|
||||
import androidx.room.Query;
|
||||
import androidx.room.Update;
|
||||
|
||||
import com.navinfo.collect.library.data.entity.Project;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Dao
|
||||
public interface IProjectManagerDao {
|
||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||
void insert(Project... projects);
|
||||
|
||||
@Update
|
||||
int update(Project[] projects);
|
||||
|
||||
@Delete
|
||||
int delete(Project project);
|
||||
|
||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||
void inserts(Project[] projects);
|
||||
|
||||
@Query("DELETE FROM project")
|
||||
void deleteAll();
|
||||
|
||||
@Query("SELECT * FROM project where project_visibility =:visable")
|
||||
List<Project> findList(int visable);
|
||||
|
||||
@Query("SELECT * FROM project")
|
||||
List<Project> findList();
|
||||
|
||||
@Query("SELECT * FROM project where uuid =:id")
|
||||
Project findProject(String id);
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
package com.navinfo.collect.library.data.dao.impl;
|
||||
|
||||
import androidx.room.Dao;
|
||||
import androidx.room.Delete;
|
||||
import androidx.room.Insert;
|
||||
import androidx.room.OnConflictStrategy;
|
||||
import androidx.room.Query;
|
||||
import androidx.room.Update;
|
||||
import com.navinfo.collect.library.data.entity.LayerManager;
|
||||
import com.navinfo.collect.library.data.entity.TileElement;
|
||||
import java.util.List;
|
||||
|
||||
@Dao
|
||||
public interface ITileElementDao {
|
||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||
void insert(TileElement... tileElements);
|
||||
|
||||
@Update
|
||||
int update(TileElement[] tileElements);
|
||||
|
||||
@Delete
|
||||
int delete(TileElement tileElements);
|
||||
|
||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||
void inserts(TileElement[] tileElements);
|
||||
|
||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||
void insertList(List<TileElement> tileElementList);
|
||||
|
||||
@Query("DELETE FROM tileElement")
|
||||
void deleteAll();
|
||||
|
||||
@Query("DELETE FROM tileElement where element_uuid =:elementId")
|
||||
void deleteElementId(String elementId);
|
||||
|
||||
@Query("DELETE FROM tileElement where rowId>:start and rowId<:end")
|
||||
void deleteAll(int start,int end);
|
||||
|
||||
@Query("SELECT * FROM tileElement where element_uuid =:elementId")
|
||||
List<TileElement> getElementIdList(String elementId);
|
||||
}
|
||||
@@ -0,0 +1,220 @@
|
||||
package com.navinfo.collect.library.data.dao.impl;
|
||||
|
||||
import android.content.Context;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.room.Database;
|
||||
import androidx.room.Room;
|
||||
import androidx.room.RoomDatabase;
|
||||
import androidx.sqlite.db.SupportSQLiteDatabase;
|
||||
|
||||
import com.navinfo.collect.library.data.entity.CheckManager;
|
||||
import com.navinfo.collect.library.data.entity.Element;
|
||||
import com.navinfo.collect.library.data.entity.LayerManager;
|
||||
import com.navinfo.collect.library.data.entity.NiLocation;
|
||||
import com.navinfo.collect.library.data.entity.Project;
|
||||
import com.navinfo.collect.library.data.entity.TileElement;
|
||||
import com.tencent.wcdb.database.SQLiteCipherSpec;
|
||||
import com.tencent.wcdb.database.SQLiteDatabase;
|
||||
|
||||
import com.tencent.wcdb.room.db.WCDBOpenHelperFactory;
|
||||
import android.os.AsyncTask;
|
||||
import android.util.Log;
|
||||
import com.tencent.wcdb.repair.BackupKit;
|
||||
import com.tencent.wcdb.repair.RecoverKit;
|
||||
import com.tencent.wcdb.room.db.WCDBDatabase;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@Database(entities = {Element.class, TileElement.class, LayerManager.class, Project.class, NiLocation.class, CheckManager.class},version = 1, exportSchema = false)
|
||||
public abstract class MapLifeDataBase extends RoomDatabase {
|
||||
// marking the instance as volatile to ensure atomic access to the variable
|
||||
/**
|
||||
* 数据库单例对象
|
||||
*/
|
||||
private static volatile MapLifeDataBase INSTANCE;
|
||||
|
||||
/**
|
||||
* 要素数据库类
|
||||
*/
|
||||
public abstract IElementDao getElementDao();
|
||||
|
||||
/**
|
||||
* 地图坐标库类
|
||||
*/
|
||||
public abstract INiLocationDao getNiLocationDao();
|
||||
|
||||
/**
|
||||
* 图层要素数据库类
|
||||
*/
|
||||
public abstract ITileElementDao getTileElementDao();
|
||||
|
||||
/**
|
||||
* 图层数据库类
|
||||
*/
|
||||
|
||||
public abstract ILayerManagerDao getLayerManagerDao();
|
||||
|
||||
/**
|
||||
* 项目管理类
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public abstract IProjectManagerDao getProjectManagerDao();
|
||||
|
||||
/**
|
||||
* 字段检查项
|
||||
*/
|
||||
public abstract ICheckManagerDao getCheckManagerDao();
|
||||
|
||||
/**
|
||||
* 数据库秘钥
|
||||
*/
|
||||
private final static String DB_PASSWORD = "123456";
|
||||
|
||||
public static MapLifeDataBase getDatabase(final Context context, final String name) {
|
||||
if (INSTANCE == null) {
|
||||
synchronized (MapLifeDataBase.class) {
|
||||
if (INSTANCE == null) {
|
||||
// [WCDB] To use Room library with WCDB, pass a WCDBOpenHelper factory object
|
||||
// to the database builder with .openHelperFactory(...). In the factory object,
|
||||
// you can specify passphrase and cipher options to open or create encrypted
|
||||
// database, as well as optimization options like asynchronous checkpoint.
|
||||
SQLiteCipherSpec cipherSpec = new SQLiteCipherSpec()
|
||||
.setPageSize(1024)
|
||||
.setSQLCipherVersion(3);
|
||||
WCDBOpenHelperFactory factory = new WCDBOpenHelperFactory()
|
||||
.passphrase(DB_PASSWORD.getBytes()) // passphrase to the database, remove this line for plain-text
|
||||
.cipherSpec(cipherSpec) // cipher to use, remove for default settings
|
||||
.writeAheadLoggingEnabled(true) // enable WAL mode, remove if not needed
|
||||
.asyncCheckpointEnabled(true); // enable asynchronous checkpoint, remove if not needed
|
||||
|
||||
INSTANCE = Room.databaseBuilder(context.getApplicationContext(),
|
||||
MapLifeDataBase.class, name)
|
||||
|
||||
// [WCDB] Specify open helper to use WCDB database implementation instead
|
||||
// of the Android framework.
|
||||
.openHelperFactory(factory)
|
||||
|
||||
// Wipes and rebuilds instead of migrating if no Migration object.
|
||||
// Migration is not part of this codelab.
|
||||
.fallbackToDestructiveMigration()
|
||||
.addCallback(sRoomDatabaseCallback)
|
||||
.build();
|
||||
}
|
||||
}
|
||||
}
|
||||
return INSTANCE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Override the onOpen method to populate the database.
|
||||
* For this sample, we clear the database every time it is created or opened.
|
||||
* <p>
|
||||
* If you want to populate the database only when the database is created for the 1st time,
|
||||
* override RoomDatabase.Callback()#onCreate
|
||||
*/
|
||||
private static RoomDatabase.Callback sRoomDatabaseCallback = new RoomDatabase.Callback() {
|
||||
|
||||
@Override
|
||||
public void onOpen(@NonNull SupportSQLiteDatabase db) {
|
||||
super.onOpen(db);
|
||||
// If you want to keep the data through app restarts,
|
||||
// comment out the following line.
|
||||
new PopulateDbAsync(INSTANCE).execute();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Populate the database in the background.
|
||||
* If you want to start with more words, just add them.
|
||||
*/
|
||||
private static class PopulateDbAsync extends AsyncTask<Void, Void, Void> {
|
||||
|
||||
private final ILayerManagerDao mLayerDao;
|
||||
|
||||
PopulateDbAsync(MapLifeDataBase db) {
|
||||
mLayerDao = db.getLayerManagerDao();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Void doInBackground(final Void... params) {
|
||||
// Start the app with a clean database every time.
|
||||
// Not needed if you only populate on creation.
|
||||
//mDao.deleteAll();
|
||||
Log.e("qj", "doInBackground");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 数据恢复
|
||||
*/
|
||||
protected boolean recoverData(){
|
||||
if(INSTANCE!=null){
|
||||
SQLiteDatabase sqlite = ((WCDBDatabase) INSTANCE.getOpenHelper().getWritableDatabase()).getInnerDatabase();
|
||||
RecoverKit recover = new RecoverKit(
|
||||
sqlite, // 要恢复到的目标 DB
|
||||
sqlite.getPath() + "-backup", // 备份文件
|
||||
DB_PASSWORD.getBytes() // 加密备份文件的密钥,非 DB 密钥
|
||||
);
|
||||
int result = recover.run(false); // fatal 参数传 false 表示遇到错误忽略并继续,
|
||||
// 若传 true 遇到错误则中止并返回 FAILED
|
||||
switch (result) {
|
||||
case RecoverKit.RESULT_OK:
|
||||
/* 成功 */
|
||||
Log.e("qj","sRoomDatabaseCallback==RecoverKit成功");
|
||||
return true;
|
||||
case RecoverKit.RESULT_CANCELED: /* 取消操作 */
|
||||
Log.e("qj","sRoomDatabaseCallback==RecoverKit取消操作");
|
||||
break;
|
||||
case RecoverKit.RESULT_FAILED: /* 失败 */
|
||||
Log.e("qj","sRoomDatabaseCallback==RecoverKit失败");
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
recover.release();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 备份数据
|
||||
*/
|
||||
protected boolean backup() {
|
||||
Log.e("qj", "sRoomDatabaseCallback===backup==start");
|
||||
if (INSTANCE != null) {
|
||||
//备份文件
|
||||
SQLiteDatabase sqlite = ((WCDBDatabase) INSTANCE.getOpenHelper().getWritableDatabase()).getInnerDatabase();
|
||||
BackupKit backup = new BackupKit(
|
||||
sqlite, // 要备份的 DB
|
||||
sqlite.getPath() + "-backup", // 备份文件
|
||||
"123456".getBytes(), // 加密备份文件的密钥,非 DB 密钥
|
||||
0, null);
|
||||
int result = backup.run();
|
||||
switch (result) {
|
||||
case BackupKit.RESULT_OK:
|
||||
/* 成功 */
|
||||
Log.e("qj", "sRoomDatabaseCallback==成功");
|
||||
return true;
|
||||
case BackupKit.RESULT_CANCELED:
|
||||
/* 取消操作 */
|
||||
Log.e("qj", "sRoomDatabaseCallback==取消操作");
|
||||
break;
|
||||
case BackupKit.RESULT_FAILED:
|
||||
/* 失败 */
|
||||
Log.e("qj", "sRoomDatabaseCallback==失败");
|
||||
break;
|
||||
}
|
||||
|
||||
backup.release();
|
||||
}
|
||||
Log.e("qj", "sRoomDatabaseCallback===backup==end");
|
||||
return false;
|
||||
}
|
||||
|
||||
protected void release() {
|
||||
INSTANCE = null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
package com.navinfo.collect.library.data.db;
|
||||
|
||||
import androidx.room.RoomDatabase;
|
||||
|
||||
public abstract class CipherDataBase extends RoomDatabase {
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
package com.navinfo.collect.library.data.db;
|
||||
|
||||
public interface GeometryDao {
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
package com.navinfo.collect.library.data.db;
|
||||
|
||||
import com.navinfo.collect.library.utils.GeometryTools;
|
||||
|
||||
import org.locationtech.jts.geom.Geometry;
|
||||
import java.util.UUID;
|
||||
|
||||
public class GeometryEntity {
|
||||
private String id = UUID.randomUUID().toString();
|
||||
private String name;
|
||||
private String geometry;
|
||||
private String layerName;
|
||||
private String style;
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(String id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getGeometry() {
|
||||
return geometry;
|
||||
}
|
||||
|
||||
public void setGeometry(String wkt) {
|
||||
if (wkt == null||wkt.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
Geometry geometry = GeometryTools.createGeometry(wkt);
|
||||
setGeometry(geometry);
|
||||
}
|
||||
|
||||
public void setGeometry(Geometry geometry) {
|
||||
this.geometry = geometry.toString();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
package com.navinfo.collect.library.data.db;
|
||||
|
||||
public class GeometryProperties {
|
||||
public String key;
|
||||
public String value;
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
package com.navinfo.collect.library.data.db;
|
||||
|
||||
public class GeometryTile {
|
||||
public int tileX;
|
||||
public int tileY;
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package com.navinfo.collect.library.data.entity
|
||||
|
||||
import androidx.room.Entity
|
||||
import androidx.room.PrimaryKey
|
||||
|
||||
@Entity(tableName = "CheckManager")
|
||||
class CheckManager(
|
||||
@PrimaryKey(autoGenerate = true)
|
||||
var id: Long = 0,
|
||||
//检查项类型
|
||||
val type: Int,
|
||||
//检查项名称
|
||||
val tag: String,
|
||||
//检查项正则内容
|
||||
val regexStr: String
|
||||
|
||||
) {
|
||||
fun toJson(): String {
|
||||
return "{\"id\":$id,\"type\":$type,\"tag\":\"$tag\",\"regexStr\":\"$regexStr\"}"
|
||||
}
|
||||
|
||||
fun toMap(): Map<String, *> {
|
||||
return return mapOf("id" to id, "type" to type, "tag" to tag, "regexStr" to regexStr)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
package com.navinfo.collect.library.data.entity
|
||||
|
||||
|
||||
enum class DataLayerItemType {
|
||||
///未知类型
|
||||
DataLayerItemTypeUnKnow,
|
||||
|
||||
///输入框
|
||||
DataLayerItemTypeInput,
|
||||
|
||||
///输入框集合
|
||||
DataLayerItemTypeInputArray,
|
||||
|
||||
///纯展示文本
|
||||
DataLayerItemTypeText,
|
||||
|
||||
///单选框
|
||||
DataLayerItemTypeSingleSelection,
|
||||
|
||||
///多选框
|
||||
DataLayerItemTypeMultipleSelection,
|
||||
|
||||
///照片
|
||||
DataLayerItemTypePhoto,
|
||||
|
||||
///多级菜单
|
||||
DataLayerItemTypeMultiLevelMenu
|
||||
}
|
||||
|
||||
data class CustomLayerItem(
|
||||
///对应的数据库字段名称
|
||||
val key: String,
|
||||
///用来显示控件名称
|
||||
val title: String = key,
|
||||
///控件类型
|
||||
val type: DataLayerItemType,
|
||||
///非必填项
|
||||
// val nullable: Boolean = true,
|
||||
///是不是主键
|
||||
// val primaryKey: Boolean = false,
|
||||
///默认值
|
||||
// var value: Any,
|
||||
///多选或单选的内容,如果 [isSelect] 为 false 可忽略这字段
|
||||
// var selectOptions: String,
|
||||
///是否是主名称属性,设置为true,地图上会已该字段内容渲染
|
||||
// val isMainName: Boolean = false,
|
||||
///简介
|
||||
val describe: String,
|
||||
val itemBean: String,
|
||||
///检查项
|
||||
// val checkManagerList: List<CheckManager>,
|
||||
) {
|
||||
|
||||
fun toMap(): Map<String, *> {
|
||||
// val checkManagerMapList = mutableListOf<Map<String, *>>()
|
||||
// for (check in checkManagerList) {
|
||||
// checkManagerMapList.add(check.toMap())
|
||||
// }
|
||||
return mapOf(
|
||||
"key" to key,
|
||||
"title" to title,
|
||||
"type" to type.ordinal,
|
||||
// "nullable" to nullable,
|
||||
// "primaryKey" to primaryKey,
|
||||
// "value" to value,
|
||||
// "selectOptions" to selectOptions,
|
||||
// "isMainName" to isMainName,
|
||||
"describe" to describe,
|
||||
// "checkManagerList" to checkManagerMapList,
|
||||
)
|
||||
}
|
||||
|
||||
companion object {
|
||||
// fun fromJson(json:JSONObject): CustomLayerItem {
|
||||
// return CustomLayerItem()
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
//class DataLayerItemTypeAdapter {
|
||||
// @ToJson
|
||||
// fun toJson(enum: DataLayerItemType): Int {
|
||||
// return enum.ordinal;
|
||||
// }
|
||||
//
|
||||
// @FromJson
|
||||
// fun fromJson(type: Int): DataLayerItemType {
|
||||
// return DataLayerItemType.values()[type];
|
||||
// }
|
||||
//}
|
||||
@@ -0,0 +1,24 @@
|
||||
package com.navinfo.collect.library.data.entity;
|
||||
|
||||
import androidx.room.ColumnInfo;
|
||||
import androidx.room.Entity;
|
||||
import androidx.room.Index;
|
||||
import androidx.room.PrimaryKey;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
public class DataBase extends Feature {
|
||||
|
||||
@ColumnInfo(name = "geometry")
|
||||
private String geometry;
|
||||
|
||||
|
||||
public String getGeometry() {
|
||||
return geometry;
|
||||
}
|
||||
|
||||
public void setGeometry(String geometry) {
|
||||
this.geometry = geometry;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,219 @@
|
||||
package com.navinfo.collect.library.data.entity;
|
||||
|
||||
import androidx.room.ColumnInfo;
|
||||
import androidx.room.Entity;
|
||||
import androidx.room.Ignore;
|
||||
import androidx.room.Index;
|
||||
|
||||
import com.google.protobuf.Any;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* @author qj
|
||||
* @version V1.0
|
||||
* @ClassName: Element
|
||||
* @Date 2022/4/14
|
||||
* @Description: ${Element}(要素数据)
|
||||
*/
|
||||
//@Entity(tableName = "element",indices = {@Index("visibility"),@Index(value = {"maxx", "minx", "maxy", "miny"})})
|
||||
@Entity(tableName = "element", indices = {@Index("visibility")})
|
||||
public class Element extends DataBase {
|
||||
|
||||
@ColumnInfo(name = "layer_id")
|
||||
private String LayerId;
|
||||
|
||||
@ColumnInfo(name = "display_style")
|
||||
private String DisplayStyle;
|
||||
|
||||
@ColumnInfo(name = "display_text")
|
||||
private String DisplayText;
|
||||
|
||||
@ColumnInfo(name = "start_level")
|
||||
private int StartLevel;
|
||||
|
||||
@ColumnInfo(name = "end_level")
|
||||
private int EndLevel;
|
||||
|
||||
@ColumnInfo(name = "zindex")
|
||||
private int ZIndex;
|
||||
|
||||
@ColumnInfo(name = "visibility")
|
||||
private int Visibility;
|
||||
|
||||
@ColumnInfo(name = "operation_time")
|
||||
private String OperationTime;
|
||||
|
||||
@ColumnInfo(name = "export_time")
|
||||
private String ExportTime;
|
||||
|
||||
@ColumnInfo(name = "t_lifecycle")
|
||||
private int TLifecycle;
|
||||
|
||||
@ColumnInfo(name = "t_status")
|
||||
private int TStatus;
|
||||
|
||||
@Ignore
|
||||
private Map<String, String> values;
|
||||
|
||||
public Map<String, String> getValues() {
|
||||
return values;
|
||||
}
|
||||
|
||||
public void setValues(Map<String, String> values) {
|
||||
this.values = values;
|
||||
}
|
||||
|
||||
public String getLayerId() {
|
||||
return LayerId;
|
||||
}
|
||||
|
||||
public void setLayerId(String layerId) {
|
||||
this.LayerId = layerId;
|
||||
}
|
||||
|
||||
public String getDisplayStyle() {
|
||||
return DisplayStyle;
|
||||
}
|
||||
|
||||
public void setDisplayStyle(String displayStyle) {
|
||||
DisplayStyle = displayStyle;
|
||||
}
|
||||
|
||||
public String getDisplayText() {
|
||||
return DisplayText;
|
||||
}
|
||||
|
||||
public void setDisplayText(String displayText) {
|
||||
DisplayText = displayText;
|
||||
}
|
||||
|
||||
public int getStartLevel() {
|
||||
return StartLevel;
|
||||
}
|
||||
|
||||
public void setStartLevel(int startLevel) {
|
||||
StartLevel = startLevel;
|
||||
}
|
||||
|
||||
public int getEndLevel() {
|
||||
return EndLevel;
|
||||
}
|
||||
|
||||
public void setEndLevel(int endLevel) {
|
||||
EndLevel = endLevel;
|
||||
}
|
||||
|
||||
public int getZIndex() {
|
||||
return ZIndex;
|
||||
}
|
||||
|
||||
public void setZIndex(int ZIndex) {
|
||||
this.ZIndex = ZIndex;
|
||||
}
|
||||
|
||||
public int getVisibility() {
|
||||
return Visibility;
|
||||
}
|
||||
|
||||
public void setVisibility(int visibility) {
|
||||
Visibility = visibility;
|
||||
}
|
||||
|
||||
public String getOperationTime() {
|
||||
return OperationTime;
|
||||
}
|
||||
|
||||
public void setOperationTime(String operationTime) {
|
||||
OperationTime = operationTime;
|
||||
}
|
||||
|
||||
public String getExportTime() {
|
||||
return ExportTime;
|
||||
}
|
||||
|
||||
public void setExportTime(String exportTime) {
|
||||
ExportTime = exportTime;
|
||||
}
|
||||
|
||||
public int getTLifecycle() {
|
||||
return TLifecycle;
|
||||
}
|
||||
|
||||
public void setTLifecycle(int TLifecycle) {
|
||||
this.TLifecycle = TLifecycle;
|
||||
}
|
||||
|
||||
public int getTStatus() {
|
||||
return TStatus;
|
||||
}
|
||||
|
||||
public void setTStatus(int TStatus) {
|
||||
this.TStatus = TStatus;
|
||||
}
|
||||
|
||||
|
||||
@NotNull
|
||||
public Map toMap() {
|
||||
Map map = new HashMap();
|
||||
map.put("layerId", LayerId);
|
||||
map.put("uuid", getId());
|
||||
map.put("geometry", getGeometry());
|
||||
map.put("displayText", DisplayText);
|
||||
map.put("displayStyle", DisplayStyle);
|
||||
map.put("tOperateDate", OperationTime);
|
||||
map.put("tLifecycle", TLifecycle);
|
||||
map.put("tStatus", TStatus);
|
||||
map.put("values", values);
|
||||
return map;
|
||||
}
|
||||
|
||||
public static Element fromMap(Map<String, Object> map) {
|
||||
try {
|
||||
Element element = new Element();
|
||||
element.setLayerId((String) map.get("layerId"));
|
||||
element.setId((String) map.get("uuid"));
|
||||
element.setGeometry((String) map.get("geometry"));
|
||||
element.setDisplayText((String) map.get("displayText"));
|
||||
element.setDisplayStyle((String) map.get("displayStyle"));
|
||||
element.setOperationTime((String) map.get("tOperateDate"));
|
||||
element.setTLifecycle((Integer) map.get("tLifecycle"));
|
||||
element.setTStatus((Integer) map.get("tStatus"));
|
||||
return element;
|
||||
} catch (Exception e) {
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static Element fromJson(String json) {
|
||||
try {
|
||||
JSONObject jsonObject = new JSONObject(json);
|
||||
return fromJson(jsonObject);
|
||||
} catch (Exception e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static Element fromJson(JSONObject jsonObject) {
|
||||
Element element = new Element();
|
||||
element.setLayerId(jsonObject.optString("layerId"));
|
||||
element.setId(jsonObject.optString("uuid"));
|
||||
element.setGeometry(jsonObject.optString("geometry"));
|
||||
element.setDisplayText(jsonObject.optString("displayText"));
|
||||
element.setDisplayStyle(jsonObject.optString("displayStyle"));
|
||||
element.setOperationTime(jsonObject.optString("tOperateDate"));
|
||||
element.setTLifecycle(jsonObject.optInt("tLifecycle"));
|
||||
element.setTStatus(jsonObject.optInt("tStatus"));
|
||||
return element;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
package com.navinfo.collect.library.data.entity;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.room.ColumnInfo;
|
||||
import androidx.room.PrimaryKey;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* @author qj
|
||||
* @version V1.0
|
||||
* @ClassName: Feature
|
||||
* @Date 2022/4/14
|
||||
* @Description: ${TODO}(数据基类)
|
||||
*/
|
||||
public class Feature implements Serializable, Cloneable {
|
||||
// //主键
|
||||
// @PrimaryKey(autoGenerate = true)
|
||||
// public int rowId;
|
||||
|
||||
@PrimaryKey()
|
||||
@ColumnInfo(name = "uuid")
|
||||
@NonNull
|
||||
private String id = UUID.randomUUID().toString();
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(String id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object clone() throws CloneNotSupportedException {
|
||||
return super.clone();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,237 @@
|
||||
package com.navinfo.collect.library.data.entity;
|
||||
|
||||
import com.navinfo.collect.library.system.Constant;
|
||||
import com.navinfo.collect.library.utils.GeometryTools;
|
||||
|
||||
import org.locationtech.jts.geom.Coordinate;
|
||||
import org.locationtech.jts.geom.Envelope;
|
||||
import org.locationtech.jts.geom.Geometry;
|
||||
import org.oscim.core.MercatorProjection;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
|
||||
import io.realm.RealmDictionary;
|
||||
import io.realm.RealmObject;
|
||||
import io.realm.RealmSet;
|
||||
import io.realm.annotations.PrimaryKey;
|
||||
import io.realm.annotations.Required;
|
||||
|
||||
public class GeometryFeatureEntity extends RealmObject implements Cloneable {
|
||||
@PrimaryKey
|
||||
private String id = UUID.randomUUID().toString();
|
||||
private String name;
|
||||
@Required
|
||||
private String geometry;
|
||||
private byte[] wkb;
|
||||
private double xmax;
|
||||
private double xmin;
|
||||
private double ymax;
|
||||
private double ymin;
|
||||
private LayerEntity layerEntity;
|
||||
private RealmSet<Integer> tileX = new RealmSet<>();
|
||||
private RealmSet<Integer> tileY = new RealmSet<>();
|
||||
private RealmDictionary<String> properties = new RealmDictionary<>();
|
||||
private RealmDictionary<String> otherProperties = new RealmDictionary<>();
|
||||
|
||||
public GeometryFeatureEntity() {
|
||||
}
|
||||
|
||||
public GeometryFeatureEntity(String name, String geometry) {
|
||||
this.name = name;
|
||||
this.geometry = geometry;
|
||||
}
|
||||
|
||||
public GeometryFeatureEntity(String name, String geometry, double xmax, double xmin, double ymax, double ymin) {
|
||||
this.name = name;
|
||||
this.geometry = geometry;
|
||||
this.xmax = xmax;
|
||||
this.xmin = xmin;
|
||||
this.ymax = ymax;
|
||||
this.ymin = ymin;
|
||||
}
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(String id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public double getXmax() {
|
||||
return xmax;
|
||||
}
|
||||
|
||||
public void setXmax(double xmax) {
|
||||
this.xmax = xmax;
|
||||
}
|
||||
|
||||
public double getXmin() {
|
||||
return xmin;
|
||||
}
|
||||
|
||||
public void setXmin(double xmin) {
|
||||
this.xmin = xmin;
|
||||
}
|
||||
|
||||
public double getYmax() {
|
||||
return ymax;
|
||||
}
|
||||
|
||||
public void setYmax(double ymax) {
|
||||
this.ymax = ymax;
|
||||
}
|
||||
|
||||
public double getYmin() {
|
||||
return ymin;
|
||||
}
|
||||
|
||||
public void setYmin(double ymin) {
|
||||
this.ymin = ymin;
|
||||
}
|
||||
|
||||
public Map<String, String> getProperties() {
|
||||
if (this.properties == null) {
|
||||
this.properties = new RealmDictionary<>();
|
||||
}
|
||||
return properties;
|
||||
}
|
||||
|
||||
public void addProperties(String k, String v) {
|
||||
this.properties.put(k, v);
|
||||
}
|
||||
|
||||
public RealmDictionary<String> getOtherProperties() {
|
||||
if (this.otherProperties == null) {
|
||||
this.otherProperties = new RealmDictionary<>();
|
||||
}
|
||||
return otherProperties;
|
||||
}
|
||||
|
||||
public String getGeometry() {
|
||||
return geometry;
|
||||
}
|
||||
|
||||
public void setGeometry(String wkt) {
|
||||
if (wkt == null||wkt.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
Geometry geometry = GeometryTools.createGeometry(wkt);
|
||||
setGeometry(geometry);
|
||||
}
|
||||
|
||||
public void setGeometry(Geometry geometry) {
|
||||
this.geometry = geometry.toString();
|
||||
// 每一次对geometry的更新,都会影响到数据的瓦片编号,记录数据的20级瓦片编号
|
||||
getTileXByGeometry(geometry, tileX);
|
||||
getTileYByGeometry(geometry, tileY);
|
||||
|
||||
Envelope envelope = geometry.getBoundary().getEnvelopeInternal();
|
||||
setXmin(envelope.getMinX());
|
||||
setXmax(envelope.getMaxX());
|
||||
setYmin(envelope.getMinY());
|
||||
setYmax(envelope.getMaxY());
|
||||
}
|
||||
|
||||
public LayerEntity getLayerEntity() {
|
||||
return layerEntity;
|
||||
}
|
||||
|
||||
public void setLayerEntity(LayerEntity layerEntity) {
|
||||
this.layerEntity = layerEntity;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据给定的geometry计算其横跨的20级瓦片X值
|
||||
* */
|
||||
private Set<Integer> getTileXByGeometry(Geometry geometry, Set<Integer> tileXSet) {
|
||||
// long startTime = System.currentTimeMillis();
|
||||
if (tileXSet == null) {
|
||||
tileXSet = new RealmSet<>();
|
||||
}
|
||||
if (geometry!=null) {
|
||||
Geometry envelope = geometry.getEnvelope();
|
||||
if (envelope!=null) {
|
||||
Coordinate[] coordinates = envelope.getCoordinates();
|
||||
// 最小最大x轴坐标,索引0位最小x值,索引1位最大x值
|
||||
if (coordinates!=null&&coordinates.length>0) {
|
||||
double[] minMaxX = new double[]{coordinates[0].x, coordinates[0].x};
|
||||
for (Coordinate coordinate: coordinates) {
|
||||
// 获取最大和最小x的值
|
||||
if (coordinate.x<minMaxX[0]) {
|
||||
minMaxX[0] = coordinate.x;
|
||||
}
|
||||
if (coordinate.x>minMaxX[1]) {
|
||||
minMaxX[1] = coordinate.x;
|
||||
}
|
||||
}
|
||||
// 分别计算最大和最小x值对应的tile号
|
||||
int tileX0 = MercatorProjection.longitudeToTileX(minMaxX[0], (byte) Constant.OVER_ZOOM);
|
||||
int tileX1 = MercatorProjection.longitudeToTileX(minMaxX[1], (byte) Constant.OVER_ZOOM);
|
||||
int minTileX = tileX0 <= tileX1? tileX0: tileX1;
|
||||
int maxTileX = tileX0 <= tileX1? tileX1: tileX0;
|
||||
for (int i = minTileX; i <= maxTileX; i++) {
|
||||
tileXSet.add(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// System.out.println("XGeometry-time:"+(System.currentTimeMillis()-startTime));
|
||||
return tileXSet;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据给定的geometry计算其横跨的20级瓦片Y值
|
||||
* */
|
||||
private Set<Integer> getTileYByGeometry(Geometry geometry, Set<Integer> tileYSet) {
|
||||
// long startTime = System.currentTimeMillis();
|
||||
if (tileYSet == null) {
|
||||
tileYSet = new RealmSet<>();
|
||||
}
|
||||
Geometry envelope = geometry.getEnvelope();
|
||||
if (envelope!=null) {
|
||||
Coordinate[] coordinates = envelope.getCoordinates();
|
||||
// 最小最大x轴坐标,索引0位最小x值,索引1位最大y值
|
||||
if (coordinates!=null&&coordinates.length>0) {
|
||||
double[] minMaxY = new double[]{coordinates[0].y, coordinates[0].y};
|
||||
for (Coordinate coordinate: coordinates) {
|
||||
// 获取最大和最小y的值
|
||||
if (coordinate.y<minMaxY[0]) {
|
||||
minMaxY[0] = coordinate.y;
|
||||
}
|
||||
if (coordinate.y>minMaxY[1]) {
|
||||
minMaxY[1] = coordinate.y;
|
||||
}
|
||||
}
|
||||
// 分别计算最大和最小x值对应的tile号
|
||||
int tileY0 = MercatorProjection.latitudeToTileY(minMaxY[0], (byte) Constant.OVER_ZOOM);
|
||||
int tileY1 = MercatorProjection.latitudeToTileY(minMaxY[1], (byte) Constant.OVER_ZOOM);
|
||||
int minTileY = tileY0 <= tileY1? tileY0: tileY1;
|
||||
int maxTileY = tileY0 <= tileY1? tileY1: tileY0;
|
||||
for (int i = minTileY; i <= maxTileY; i++) {
|
||||
tileYSet.add(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
// System.out.println("YGeometry-time:"+(System.currentTimeMillis()-startTime));
|
||||
return tileYSet;
|
||||
}
|
||||
|
||||
public byte[] getWkb() {
|
||||
return wkb;
|
||||
}
|
||||
|
||||
public void setWkb(byte[] wkb) {
|
||||
this.wkb = wkb;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
package com.navinfo.collect.library.data.entity;
|
||||
|
||||
import androidx.room.ColumnInfo;
|
||||
import androidx.room.TypeConverter;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* @author qj
|
||||
* @version V1.0
|
||||
* @ClassName: GeometryEntity
|
||||
* @Date 2022/4/14
|
||||
* @Description: ${TODO}(几何基类)
|
||||
*/
|
||||
public class GeometryIndexEntity implements Serializable,Cloneable{
|
||||
@ColumnInfo(name = "geometry")
|
||||
private String geometry;
|
||||
@ColumnInfo(name = "xmax")
|
||||
private double xMax;
|
||||
@ColumnInfo(name = "xmin")
|
||||
private double xMin;
|
||||
@ColumnInfo(name = "ymax")
|
||||
private double yMax;
|
||||
@ColumnInfo(name = "ymin")
|
||||
private double yMin;
|
||||
|
||||
public String getGeometry() {
|
||||
return geometry;
|
||||
}
|
||||
|
||||
public void setGeometry(String geometry) {
|
||||
this.geometry = geometry;
|
||||
}
|
||||
|
||||
public double getxMax() {
|
||||
return xMax;
|
||||
}
|
||||
|
||||
public void setxMax(double xMax) {
|
||||
this.xMax = xMax;
|
||||
}
|
||||
|
||||
public double getxMin() {
|
||||
return xMin;
|
||||
}
|
||||
|
||||
public void setxMin(double xMin) {
|
||||
this.xMin = xMin;
|
||||
}
|
||||
|
||||
public double getyMax() {
|
||||
return yMax;
|
||||
}
|
||||
|
||||
public void setyMax(double yMax) {
|
||||
this.yMax = yMax;
|
||||
}
|
||||
|
||||
public double getyMin() {
|
||||
return yMin;
|
||||
}
|
||||
|
||||
public void setyMin(double yMin) {
|
||||
this.yMin = yMin;
|
||||
}
|
||||
|
||||
public GeometryIndexEntity() {
|
||||
super();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Object clone() throws CloneNotSupportedException {
|
||||
return super.clone();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void finalize() throws Throwable {
|
||||
super.finalize();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
package com.navinfo.collect.library.data.entity;
|
||||
|
||||
import org.locationtech.jts.geom.Geometry;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
import io.realm.RealmObject;
|
||||
import io.realm.annotations.Ignore;
|
||||
import io.realm.annotations.PrimaryKey;
|
||||
import io.realm.annotations.Required;
|
||||
|
||||
public class LayerEntity extends RealmObject {
|
||||
@Required
|
||||
@PrimaryKey
|
||||
private String layerName;
|
||||
private String fromDataName;
|
||||
private int GeomType;
|
||||
private String layerTableName;
|
||||
@Ignore
|
||||
private List<Geometry> featureList;
|
||||
|
||||
public LayerEntity() {
|
||||
}
|
||||
|
||||
|
||||
public String getLayerName() {
|
||||
return layerName;
|
||||
}
|
||||
|
||||
public void setLayerName(String layerName) {
|
||||
this.layerName = layerName;
|
||||
}
|
||||
|
||||
public String getFromDataName() {
|
||||
return fromDataName;
|
||||
}
|
||||
|
||||
public void setFromDataName(String fromDataName) {
|
||||
this.fromDataName = fromDataName;
|
||||
}
|
||||
|
||||
public int getGeomType() {
|
||||
return GeomType;
|
||||
}
|
||||
|
||||
public void setGeomType(int geomType) {
|
||||
GeomType = geomType;
|
||||
}
|
||||
|
||||
public List<Geometry> getFeatureList() {
|
||||
return featureList;
|
||||
}
|
||||
|
||||
public void setFeatureList(List<Geometry> featureList) {
|
||||
this.featureList = featureList;
|
||||
}
|
||||
|
||||
public String getLayerTableName() {
|
||||
return layerTableName;
|
||||
}
|
||||
|
||||
public void setLayerTableName(String layerTableName) {
|
||||
this.layerTableName = layerTableName;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,199 @@
|
||||
package com.navinfo.collect.library.data.entity;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.room.ColumnInfo;
|
||||
import androidx.room.Entity;
|
||||
import androidx.room.Ignore;
|
||||
import androidx.room.PrimaryKey;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* @author qj
|
||||
* @version V1.0
|
||||
* @ClassName: LayerManager
|
||||
* @Date 2022/4/14
|
||||
* @Description: ${LayerManager}(图层管理)
|
||||
*/
|
||||
@Entity(tableName = "layerManager")
|
||||
public class LayerManager extends Feature {
|
||||
|
||||
///项目Id
|
||||
@ColumnInfo(name = "project_id")
|
||||
private String projectId;
|
||||
///图层名称
|
||||
@ColumnInfo(name = "layer_name")
|
||||
private String layerName;
|
||||
///图层级别
|
||||
@ColumnInfo(name = "zindex")
|
||||
private int ZIndex;
|
||||
///图层是否显示
|
||||
@ColumnInfo(name = "visibility")
|
||||
private boolean visibility;
|
||||
///导出时间
|
||||
@ColumnInfo(name = "export_time")
|
||||
private String exportTime;
|
||||
///导入时间
|
||||
@ColumnInfo(name = "import_time")
|
||||
private String importTime;
|
||||
@ColumnInfo(name = "bundle")
|
||||
private String bundle;
|
||||
///图层表述信息
|
||||
@ColumnInfo(name = "describe")
|
||||
private String describe;
|
||||
|
||||
///点线面类型
|
||||
@ColumnInfo(name = "geometryType")
|
||||
private int geometryType;
|
||||
|
||||
///来源c
|
||||
@ColumnInfo(name = "source")
|
||||
private String source;
|
||||
|
||||
@ColumnInfo(name = "style")
|
||||
private String style;
|
||||
|
||||
///每个字段的样式和控制条件
|
||||
@Ignore
|
||||
private List<CustomLayerItem> itemList;
|
||||
|
||||
public List<CustomLayerItem> getItemList() {
|
||||
return itemList;
|
||||
}
|
||||
|
||||
public void setItemList(List<CustomLayerItem> itemList) {
|
||||
this.itemList = itemList;
|
||||
}
|
||||
|
||||
public String getStyle() {
|
||||
return style;
|
||||
}
|
||||
|
||||
public void setStyle(String style) {
|
||||
this.style = style;
|
||||
}
|
||||
|
||||
public void setDescribe(String describe) {
|
||||
this.describe = describe;
|
||||
}
|
||||
|
||||
public String getDescribe() {
|
||||
return describe;
|
||||
}
|
||||
|
||||
public int getGeometryType() {
|
||||
return geometryType;
|
||||
}
|
||||
|
||||
public void setGeometryType(int geometryType) {
|
||||
this.geometryType = geometryType;
|
||||
}
|
||||
|
||||
|
||||
public String getLayerName() {
|
||||
return layerName;
|
||||
}
|
||||
|
||||
public void setLayerName(String layerName) {
|
||||
this.layerName = layerName;
|
||||
}
|
||||
|
||||
public int getZIndex() {
|
||||
return ZIndex;
|
||||
}
|
||||
|
||||
public void setZIndex(int zIndex) {
|
||||
this.ZIndex = zIndex;
|
||||
}
|
||||
|
||||
public boolean getVisibility() {
|
||||
return visibility;
|
||||
}
|
||||
|
||||
public void setVisibility(boolean visibility) {
|
||||
this.visibility = visibility;
|
||||
}
|
||||
|
||||
public String getExportTime() {
|
||||
return exportTime;
|
||||
}
|
||||
|
||||
public void setExportTime(String exportTime) {
|
||||
this.exportTime = exportTime;
|
||||
}
|
||||
|
||||
public String getImportTime() {
|
||||
return importTime;
|
||||
}
|
||||
|
||||
public void setImportTime(String importTime) {
|
||||
this.importTime = importTime;
|
||||
}
|
||||
|
||||
public String getBundle() {
|
||||
return bundle;
|
||||
}
|
||||
|
||||
public void setBundle(String bundle) {
|
||||
this.bundle = bundle;
|
||||
}
|
||||
|
||||
public String getProjectId() {
|
||||
return projectId;
|
||||
}
|
||||
|
||||
public void setProjectId(String projectId) {
|
||||
this.projectId = projectId;
|
||||
}
|
||||
|
||||
public String getSource() {
|
||||
return source;
|
||||
}
|
||||
|
||||
public void setSource(String source) {
|
||||
this.source = source;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "LayerManager{" +
|
||||
"id='" + getId() + '\'' +
|
||||
",projectId='" + projectId + '\'' +
|
||||
", layerName='" + layerName + '\'' +
|
||||
", ZIndex=" + ZIndex +
|
||||
", visibility=" + visibility +
|
||||
", exportTime='" + exportTime + '\'' +
|
||||
", importTime='" + importTime + '\'' +
|
||||
", bundle='" + bundle + '\'' +
|
||||
", describe='" + describe + '\'' +
|
||||
", geometryType='" + geometryType + '\'' +
|
||||
", source='" + source + '\'' +
|
||||
", style='" + style + '\'' +
|
||||
'}';
|
||||
}
|
||||
|
||||
public Map toMap() {
|
||||
Map map = new HashMap();
|
||||
map.put("uuid", getId());
|
||||
map.put("layerName", getLayerName());
|
||||
map.put("ZIndex", ZIndex);
|
||||
map.put("visibility", getVisibility());
|
||||
map.put("exportTime", exportTime);
|
||||
map.put("import_time", getImportTime());
|
||||
map.put("describe", getDescribe());
|
||||
map.put("geometryType", getGeometryType());
|
||||
map.put("source", source);
|
||||
map.put("style", style);
|
||||
return map;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
package com.navinfo.collect.library.data.entity;
|
||||
|
||||
import io.realm.RealmObject;
|
||||
import io.realm.annotations.PrimaryKey;
|
||||
|
||||
public class LayerProperties {
|
||||
private String name;
|
||||
private int fieldType = FieldType.STRING.getTypeCode(); /*默认是字符串类型*/
|
||||
|
||||
public LayerProperties() {
|
||||
}
|
||||
|
||||
public enum FieldType {
|
||||
STRING(0), NUM(1);
|
||||
int typeCode;
|
||||
|
||||
FieldType(int typeCode) {
|
||||
this.typeCode = typeCode;
|
||||
}
|
||||
|
||||
public int getTypeCode() {
|
||||
return typeCode;
|
||||
}
|
||||
|
||||
public FieldType getFieldType(int typeCode) {
|
||||
for (FieldType fieldType: FieldType.values()) {
|
||||
if (fieldType.getTypeCode() == typeCode) {
|
||||
return fieldType;
|
||||
}
|
||||
}
|
||||
return FieldType.STRING;
|
||||
}
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public FieldType getFieldType() {
|
||||
FieldType fieldType = FieldType.STRING;
|
||||
try {
|
||||
fieldType = fieldType.getFieldType(this.fieldType);
|
||||
} catch (Exception e) {
|
||||
}
|
||||
return fieldType;
|
||||
}
|
||||
|
||||
public void setFieldType(FieldType fieldType) {
|
||||
this.fieldType = fieldType.typeCode;
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user