Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
S
Sobot_module_Dev
Project
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
sobot_android
Sobot_module_Dev
Commits
3b40792f
Commit
3b40792f
authored
May 16, 2022
by
郑娜伟
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
gson 1.1.1
parent
5b523bc3
Show whitespace changes
Inline
Side-by-side
Showing
84 changed files
with
15301 additions
and
31 deletions
+15301
-31
.gitignore
sobot_gson/.gitignore
+0
-0
build.gradle
sobot_gson/build.gradle
+4
-4
consumer-rules.pro
sobot_gson/consumer-rules.pro
+0
-0
proguard-rules.pro
sobot_gson/proguard-rules.pro
+0
-0
sobot-gson-publish-mavencentral.gradle
sobot_gson/sobot-gson-publish-mavencentral.gradle
+3
-3
AndroidManifest.xml
sobot_gson/src/main/AndroidManifest.xml
+0
-0
ExclusionStrategy.java
..._gson/src/main/java/com/sobot/gson/ExclusionStrategy.java
+111
-0
FieldAttributes.java
sobot_gson/src/main/java/com/sobot/gson/FieldAttributes.java
+161
-0
FieldNamingPolicy.java
..._gson/src/main/java/com/sobot/gson/FieldNamingPolicy.java
+202
-0
FieldNamingStrategy.java
...son/src/main/java/com/sobot/gson/FieldNamingStrategy.java
+40
-0
Gson.java
sobot_gson/src/main/java/com/sobot/gson/Gson.java
+1102
-0
GsonBuilder.java
sobot_gson/src/main/java/com/sobot/gson/GsonBuilder.java
+726
-0
InstanceCreator.java
sobot_gson/src/main/java/com/sobot/gson/InstanceCreator.java
+92
-0
JsonArray.java
sobot_gson/src/main/java/com/sobot/gson/JsonArray.java
+400
-0
JsonDeserializationContext.java
.../main/java/com/sobot/gson/JsonDeserializationContext.java
+49
-0
JsonDeserializer.java
...t_gson/src/main/java/com/sobot/gson/JsonDeserializer.java
+95
-0
JsonElement.java
sobot_gson/src/main/java/com/sobot/gson/JsonElement.java
+325
-0
JsonIOException.java
sobot_gson/src/main/java/com/sobot/gson/JsonIOException.java
+47
-0
JsonNull.java
sobot_gson/src/main/java/com/sobot/gson/JsonNull.java
+67
-0
JsonObject.java
sobot_gson/src/main/java/com/sobot/gson/JsonObject.java
+205
-0
JsonParseException.java
...gson/src/main/java/com/sobot/gson/JsonParseException.java
+64
-0
JsonParser.java
sobot_gson/src/main/java/com/sobot/gson/JsonParser.java
+113
-0
JsonPrimitive.java
sobot_gson/src/main/java/com/sobot/gson/JsonPrimitive.java
+295
-0
JsonSerializationContext.java
...rc/main/java/com/sobot/gson/JsonSerializationContext.java
+51
-0
JsonSerializer.java
sobot_gson/src/main/java/com/sobot/gson/JsonSerializer.java
+91
-0
JsonStreamParser.java
...t_gson/src/main/java/com/sobot/gson/JsonStreamParser.java
+130
-0
JsonSyntaxException.java
...son/src/main/java/com/sobot/gson/JsonSyntaxException.java
+47
-0
LongSerializationPolicy.java
...src/main/java/com/sobot/gson/LongSerializationPolicy.java
+68
-0
ReflectionAccessFilter.java
.../src/main/java/com/sobot/gson/ReflectionAccessFilter.java
+195
-0
SobotGsonUtil.java
sobot_gson/src/main/java/com/sobot/gson/SobotGsonUtil.java
+178
-0
ToNumberPolicy.java
sobot_gson/src/main/java/com/sobot/gson/ToNumberPolicy.java
+100
-0
ToNumberStrategy.java
...t_gson/src/main/java/com/sobot/gson/ToNumberStrategy.java
+70
-0
TypeAdapter.java
sobot_gson/src/main/java/com/sobot/gson/TypeAdapter.java
+290
-0
TypeAdapterFactory.java
...gson/src/main/java/com/sobot/gson/TypeAdapterFactory.java
+170
-0
Expose.java
...gson/src/main/java/com/sobot/gson/annotations/Expose.java
+81
-0
JsonAdapter.java
...src/main/java/com/sobot/gson/annotations/JsonAdapter.java
+104
-0
SerializedName.java
.../main/java/com/sobot/gson/annotations/SerializedName.java
+93
-0
Since.java
..._gson/src/main/java/com/sobot/gson/annotations/Since.java
+63
-0
Until.java
..._gson/src/main/java/com/sobot/gson/annotations/Until.java
+68
-0
package-info.java
...rc/main/java/com/sobot/gson/annotations/package-info.java
+7
-0
$Gson$Preconditions.java
...ain/java/com/sobot/gson/internal/$Gson$Preconditions.java
+49
-0
$Gson$Types.java
...on/src/main/java/com/sobot/gson/internal/$Gson$Types.java
+638
-0
ConstructorConstructor.java
.../java/com/sobot/gson/internal/ConstructorConstructor.java
+374
-0
Excluder.java
..._gson/src/main/java/com/sobot/gson/internal/Excluder.java
+259
-0
JavaVersion.java
...on/src/main/java/com/sobot/gson/internal/JavaVersion.java
+92
-0
JsonReaderInternalAccess.java
...ava/com/sobot/gson/internal/JsonReaderInternalAccess.java
+9
-8
LazilyParsedNumber.java
...main/java/com/sobot/gson/internal/LazilyParsedNumber.java
+104
-0
LinkedTreeMap.java
.../src/main/java/com/sobot/gson/internal/LinkedTreeMap.java
+638
-0
ObjectConstructor.java
.../main/java/com/sobot/gson/internal/ObjectConstructor.java
+14
-7
PreJava9DateFormatProvider.java
...a/com/sobot/gson/internal/PreJava9DateFormatProvider.java
+86
-0
Primitives.java
...son/src/main/java/com/sobot/gson/internal/Primitives.java
+100
-0
ReflectionAccessFilterHelper.java
...com/sobot/gson/internal/ReflectionAccessFilterHelper.java
+101
-0
Streams.java
...t_gson/src/main/java/com/sobot/gson/internal/Streams.java
+120
-0
UnsafeAllocator.java
...rc/main/java/com/sobot/gson/internal/UnsafeAllocator.java
+142
-0
ArrayTypeAdapter.java
...n/java/com/sobot/gson/internal/bind/ArrayTypeAdapter.java
+100
-0
CollectionTypeAdapterFactory.java
...obot/gson/internal/bind/CollectionTypeAdapterFactory.java
+103
-0
DateTypeAdapter.java
...in/java/com/sobot/gson/internal/bind/DateTypeAdapter.java
+107
-0
DefaultDateTypeAdapter.java
.../com/sobot/gson/internal/bind/DefaultDateTypeAdapter.java
+178
-0
JsonAdapterAnnotationTypeAdapterFactory.java
...nternal/bind/JsonAdapterAnnotationTypeAdapterFactory.java
+83
-0
JsonTreeReader.java
...ain/java/com/sobot/gson/internal/bind/JsonTreeReader.java
+344
-0
JsonTreeWriter.java
...ain/java/com/sobot/gson/internal/bind/JsonTreeWriter.java
+211
-0
MapTypeAdapterFactory.java
...a/com/sobot/gson/internal/bind/MapTypeAdapterFactory.java
+264
-0
NumberTypeAdapter.java
.../java/com/sobot/gson/internal/bind/NumberTypeAdapter.java
+82
-0
ObjectTypeAdapter.java
.../java/com/sobot/gson/internal/bind/ObjectTypeAdapter.java
+128
-0
ReflectiveTypeAdapterFactory.java
...obot/gson/internal/bind/ReflectiveTypeAdapterFactory.java
+301
-0
TreeTypeAdapter.java
...in/java/com/sobot/gson/internal/bind/TreeTypeAdapter.java
+166
-0
TypeAdapterRuntimeTypeWrapper.java
...bot/gson/internal/bind/TypeAdapterRuntimeTypeWrapper.java
+82
-0
TypeAdapters.java
.../main/java/com/sobot/gson/internal/bind/TypeAdapters.java
+938
-0
ISO8601Utils.java
.../java/com/sobot/gson/internal/bind/util/ISO8601Utils.java
+352
-0
package-info.java
...n/src/main/java/com/sobot/gson/internal/package-info.java
+8
-0
ReflectionHelper.java
...ava/com/sobot/gson/internal/reflect/ReflectionHelper.java
+73
-0
SqlDateTypeAdapter.java
.../java/com/sobot/gson/internal/sql/SqlDateTypeAdapter.java
+83
-0
SqlTimeTypeAdapter.java
.../java/com/sobot/gson/internal/sql/SqlTimeTypeAdapter.java
+80
-0
SqlTimestampTypeAdapter.java
.../com/sobot/gson/internal/sql/SqlTimestampTypeAdapter.java
+43
-0
SqlTypesSupport.java
...ain/java/com/sobot/gson/internal/sql/SqlTypesSupport.java
+71
-0
package-info.java
sobot_gson/src/main/java/com/sobot/gson/package-info.java
+12
-0
TypeToken.java
..._gson/src/main/java/com/sobot/gson/reflect/TypeToken.java
+333
-0
package-info.java
...on/src/main/java/com/sobot/gson/reflect/package-info.java
+7
-0
JsonReader.java
..._gson/src/main/java/com/sobot/gson/stream/JsonReader.java
+1648
-0
JsonScope.java
...t_gson/src/main/java/com/sobot/gson/stream/JsonScope.java
+71
-0
JsonToken.java
...t_gson/src/main/java/com/sobot/gson/stream/JsonToken.java
+88
-0
JsonWriter.java
..._gson/src/main/java/com/sobot/gson/stream/JsonWriter.java
+691
-0
MalformedJsonException.java
...in/java/com/sobot/gson/stream/MalformedJsonException.java
+17
-9
package-info.java
...son/src/main/java/com/sobot/gson/stream/package-info.java
+4
-0
No files found.
sobot_
j
son/.gitignore
→
sobot_
g
son/.gitignore
View file @
3b40792f
File moved
sobot_
j
son/build.gradle
→
sobot_
g
son/build.gradle
View file @
3b40792f
...
@@ -12,7 +12,7 @@ android {
...
@@ -12,7 +12,7 @@ android {
//这里就是打jar包
//这里就是打jar包
task
makeJar
(
type:
Copy
)
{
task
makeJar
(
type:
Copy
)
{
//删除旧的jar包
//删除旧的jar包
delete
'build/libs/sobot_
json_
1.1.jar'
delete
'build/libs/sobot_
gson_1.
1.1.jar'
//原地址
//原地址
from
(
'build/intermediates/packaged-classes/release/'
)
from
(
'build/intermediates/packaged-classes/release/'
)
//导出jar包的地址
//导出jar包的地址
...
@@ -20,7 +20,7 @@ android {
...
@@ -20,7 +20,7 @@ android {
//包含的jar包
//包含的jar包
include
(
'classes.jar'
)
include
(
'classes.jar'
)
//重命名jar包为mysdk
//重命名jar包为mysdk
rename
(
'classes.jar'
,
'sobot_
json_
1.1.jar'
)
rename
(
'classes.jar'
,
'sobot_
gson_1.
1.1.jar'
)
}
}
makeJar
.
dependsOn
(
build
)
makeJar
.
dependsOn
(
build
)
}
}
...
@@ -30,4 +30,4 @@ dependencies {
...
@@ -30,4 +30,4 @@ dependencies {
}
}
//添加发布到mavenCentral脚本
//添加发布到mavenCentral脚本
apply
from:
'sobot-json-publish-mavencentral.gradle'
apply
from:
'sobot-gson-publish-mavencentral.gradle'
\ No newline at end of file
\ No newline at end of file
sobot_
j
son/consumer-rules.pro
→
sobot_
g
son/consumer-rules.pro
View file @
3b40792f
File moved
sobot_
j
son/proguard-rules.pro
→
sobot_
g
son/proguard-rules.pro
View file @
3b40792f
File moved
sobot_
json/sobot-j
son-publish-mavencentral.gradle
→
sobot_
gson/sobot-g
son-publish-mavencentral.gradle
View file @
3b40792f
...
@@ -11,8 +11,8 @@ task androidSourcesJar(type: Jar) {
...
@@ -11,8 +11,8 @@ task androidSourcesJar(type: Jar) {
ext
{
ext
{
PUBLISH_GROUP_ID
=
"com.sobot.library"
//项目包名
PUBLISH_GROUP_ID
=
"com.sobot.library"
//项目包名
PUBLISH_ARTIFACT_ID
=
'
j
son'
//项目名
PUBLISH_ARTIFACT_ID
=
'
g
son'
//项目名
PUBLISH_VERSION
=
'1.1
'
//版本号
PUBLISH_VERSION
=
'1.1
.1'
//版本号 1.2 已经被使用了,需要跳过它
}
}
...
@@ -115,7 +115,7 @@ publishing {
...
@@ -115,7 +115,7 @@ publishing {
// This is an arbitrary name, you may also use "mavencentral" or
// This is an arbitrary name, you may also use "mavencentral" or
// any other name that's descriptive for you
// any other name that's descriptive for you
//项目名称
//项目名称
name
=
"
j
son"
name
=
"
g
son"
def
releasesRepoUrl
=
"https://s01.oss.sonatype.org/service/local/staging/deploy/maven2/"
def
releasesRepoUrl
=
"https://s01.oss.sonatype.org/service/local/staging/deploy/maven2/"
def
snapshotsRepoUrl
=
"https://s01.oss.sonatype.org/content/repositories/snapshots/"
def
snapshotsRepoUrl
=
"https://s01.oss.sonatype.org/content/repositories/snapshots/"
...
...
sobot_
j
son/src/main/AndroidManifest.xml
→
sobot_
g
son/src/main/AndroidManifest.xml
View file @
3b40792f
File moved
sobot_gson/src/main/java/com/sobot/gson/ExclusionStrategy.java
0 → 100644
View file @
3b40792f
/*
* 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
.
sobot
.
gson
;
import
com.sobot.gson.GsonBuilder
;
/**
* 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 true then that class or field type
* will not be part of the JSON output. For deserialization, if {@link #shouldSkipClass(Class)}
* returns true, 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
);
}
sobot_gson/src/main/java/com/sobot/gson/FieldAttributes.java
0 → 100644
View file @
3b40792f
/*
* 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
.
sobot
.
gson
;
import
com.sobot.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 listParameterizedType = 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
;
}
/**
* Returns the value of the field represented by this {@code Field}, on
* the specified object. The value is automatically wrapped in an
* object if it has a primitive type.
*
* @return the value of the represented field in object
* {@code obj}; primitive values are wrapped in an appropriate
* object before being returned
* @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
();
}
}
sobot_gson/src/main/java/com/sobot/gson/FieldNamingPolicy.java
0 → 100644
View file @
3b40792f
/*
* 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
.
sobot
.
gson
;
import
com.sobot.gson.FieldNamingStrategy
;
import
java.lang.reflect.Field
;
import
java.util.Locale
;
/**
* An enumeration that defines a few standard naming conventions for JSON field names.
* This enumeration should be used in conjunction with {@link com.sobot.gson.GsonBuilder}
* to configure a {@link com.sobot.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 are 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 are 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 an upper case field name where each word is separated by an underscore (_).
*
* <p>Here are 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>
*/
UPPER_CASE_WITH_UNDERSCORES
()
{
@Override
public
String
translateName
(
Field
f
)
{
return
separateCamelCase
(
f
.
getName
(),
'_'
).
toUpperCase
(
Locale
.
ENGLISH
);
}
},
/**
* 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 are 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
(
Locale
.
ENGLISH
);
}
},
/**
* 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 are 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
(
Locale
.
ENGLISH
);
}
},
/**
* 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 dot (.).
*
* <p>Here are 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 dots in JavaScript is not recommended since dot is also used for a member sign in
* expressions. This requires that a field named with dots 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 2.8
*/
LOWER_CASE_WITH_DOTS
()
{
@Override
public
String
translateName
(
Field
f
)
{
return
separateCamelCase
(
f
.
getName
(),
'.'
).
toLowerCase
(
Locale
.
ENGLISH
);
}
};
/**
* Converts the field name that uses camel-case define word separation into
* separate words that are separated by the provided {@code separator}.
*/
static
String
separateCamelCase
(
String
name
,
char
separator
)
{
StringBuilder
translation
=
new
StringBuilder
();
for
(
int
i
=
0
,
length
=
name
.
length
();
i
<
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.
*/
static
String
upperCaseFirstLetter
(
String
s
)
{
int
length
=
s
.
length
();
for
(
int
i
=
0
;
i
<
length
;
i
++)
{
char
c
=
s
.
charAt
(
i
);
if
(
Character
.
isLetter
(
c
))
{
if
(
Character
.
isUpperCase
(
c
))
{
return
s
;
}
char
uppercased
=
Character
.
toUpperCase
(
c
);
// For leading letter only need one substring
if
(
i
==
0
)
{
return
uppercased
+
s
.
substring
(
1
);
}
else
{
return
s
.
substring
(
0
,
i
)
+
uppercased
+
s
.
substring
(
i
+
1
);
}
}
}
return
s
;
}
}
sobot_gson/src/main/java/com/sobot/gson/FieldNamingStrategy.java
0 → 100644
View file @
3b40792f
/*
* 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
.
sobot
.
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
);
}
sobot_gson/src/main/java/com/sobot/gson/Gson.java
0 → 100644
View file @
3b40792f
/*
* 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
.
sobot
.
gson
;
import
com.sobot.gson.internal.ConstructorConstructor
;
import
com.sobot.gson.internal.Excluder
;
import
com.sobot.gson.internal.LazilyParsedNumber
;
import
com.sobot.gson.internal.Primitives
;
import
com.sobot.gson.internal.Streams
;
import
com.sobot.gson.internal.bind.ArrayTypeAdapter
;
import
com.sobot.gson.internal.bind.CollectionTypeAdapterFactory
;
import
com.sobot.gson.internal.bind.DateTypeAdapter
;
import
com.sobot.gson.internal.bind.JsonAdapterAnnotationTypeAdapterFactory
;
import
com.sobot.gson.internal.bind.JsonTreeReader
;
import
com.sobot.gson.internal.bind.JsonTreeWriter
;
import
com.sobot.gson.internal.bind.MapTypeAdapterFactory
;
import
com.sobot.gson.internal.bind.NumberTypeAdapter
;
import
com.sobot.gson.internal.bind.ObjectTypeAdapter
;
import
com.sobot.gson.internal.bind.ReflectiveTypeAdapterFactory
;
import
com.sobot.gson.internal.bind.TypeAdapters
;
import
com.sobot.gson.internal.sql.SqlTypesSupport
;
import
com.sobot.gson.reflect.TypeToken
;
import
com.sobot.gson.stream.JsonReader
;
import
com.sobot.gson.stream.JsonToken
;
import
com.sobot.gson.stream.JsonWriter
;
import
com.sobot.gson.stream.MalformedJsonException
;
import
java.io.EOFException
;
import
java.io.IOException
;
import
java.io.Reader
;
import
java.io.StringReader
;
import
java.io.StringWriter
;
import
java.io.Writer
;
import
java.lang.reflect.Type
;
import
java.math.BigDecimal
;
import
java.math.BigInteger
;
import
java.text.DateFormat
;
import
java.util.ArrayList
;
import
java.util.Collections
;
import
java.util.HashMap
;
import
java.util.List
;
import
java.util.Map
;
import
java.util.concurrent.ConcurrentHashMap
;
import
java.util.concurrent.atomic.AtomicLong
;
import
java.util.concurrent.atomic.AtomicLongArray
;
/**
* This is the main class for using Gson. Gson is typically used by first constructing a
* Gson instance and then invoking {@link #toJson(Object)} or {@link #fromJson(String, Class)}
* methods on it. Gson instances are Thread-safe so you can reuse them freely across multiple
* threads.
*
* <p>You can create a Gson instance by invoking {@code new Gson()} if the default configuration
* 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>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 deserializing 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>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 TypeToken
*
* @author Inderjeet Singh
* @author Joel Leitch
* @author Jesse Wilson
*/
public
final
class
Gson
{
static
final
boolean
DEFAULT_JSON_NON_EXECUTABLE
=
false
;
static
final
boolean
DEFAULT_LENIENT
=
false
;
static
final
boolean
DEFAULT_PRETTY_PRINT
=
false
;
static
final
boolean
DEFAULT_ESCAPE_HTML
=
true
;
static
final
boolean
DEFAULT_SERIALIZE_NULLS
=
false
;
static
final
boolean
DEFAULT_COMPLEX_MAP_KEYS
=
false
;
static
final
boolean
DEFAULT_SPECIALIZE_FLOAT_VALUES
=
false
;
static
final
boolean
DEFAULT_USE_JDK_UNSAFE
=
true
;
static
final
String
DEFAULT_DATE_PATTERN
=
null
;
static
final
FieldNamingStrategy
DEFAULT_FIELD_NAMING_STRATEGY
=
FieldNamingPolicy
.
IDENTITY
;
static
final
ToNumberStrategy
DEFAULT_OBJECT_TO_NUMBER_STRATEGY
=
ToNumberPolicy
.
DOUBLE
;
static
final
ToNumberStrategy
DEFAULT_NUMBER_TO_NUMBER_STRATEGY
=
ToNumberPolicy
.
LAZILY_PARSED_NUMBER
;
private
static
final
TypeToken
<?>
NULL_KEY_SURROGATE
=
TypeToken
.
get
(
Object
.
class
);
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
<>();
private
final
Map
<
TypeToken
<?>,
TypeAdapter
<?>>
typeTokenCache
=
new
ConcurrentHashMap
<>();
private
final
ConstructorConstructor
constructorConstructor
;
private
final
JsonAdapterAnnotationTypeAdapterFactory
jsonAdapterFactory
;
final
List
<
TypeAdapterFactory
>
factories
;
final
Excluder
excluder
;
final
FieldNamingStrategy
fieldNamingStrategy
;
final
Map
<
Type
,
InstanceCreator
<?>>
instanceCreators
;
final
boolean
serializeNulls
;
final
boolean
complexMapKeySerialization
;
final
boolean
generateNonExecutableJson
;
final
boolean
htmlSafe
;
final
boolean
prettyPrinting
;
final
boolean
lenient
;
final
boolean
serializeSpecialFloatingPointValues
;
final
boolean
useJdkUnsafe
;
final
String
datePattern
;
final
int
dateStyle
;
final
int
timeStyle
;
final
LongSerializationPolicy
longSerializationPolicy
;
final
List
<
TypeAdapterFactory
>
builderFactories
;
final
List
<
TypeAdapterFactory
>
builderHierarchyFactories
;
final
ToNumberStrategy
objectToNumberStrategy
;
final
ToNumberStrategy
numberToNumberStrategy
;
final
List
<
ReflectionAccessFilter
>
reflectionFilters
;
/**
* 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 BigDecimal}, and {@link 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 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.sobot.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.sobot.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
,
DEFAULT_FIELD_NAMING_STRATEGY
,
Collections
.<
Type
,
InstanceCreator
<?>>
emptyMap
(),
DEFAULT_SERIALIZE_NULLS
,
DEFAULT_COMPLEX_MAP_KEYS
,
DEFAULT_JSON_NON_EXECUTABLE
,
DEFAULT_ESCAPE_HTML
,
DEFAULT_PRETTY_PRINT
,
DEFAULT_LENIENT
,
DEFAULT_SPECIALIZE_FLOAT_VALUES
,
DEFAULT_USE_JDK_UNSAFE
,
LongSerializationPolicy
.
DEFAULT
,
DEFAULT_DATE_PATTERN
,
DateFormat
.
DEFAULT
,
DateFormat
.
DEFAULT
,
Collections
.<
TypeAdapterFactory
>
emptyList
(),
Collections
.<
TypeAdapterFactory
>
emptyList
(),
Collections
.<
TypeAdapterFactory
>
emptyList
(),
DEFAULT_OBJECT_TO_NUMBER_STRATEGY
,
DEFAULT_NUMBER_TO_NUMBER_STRATEGY
,
Collections
.<
ReflectionAccessFilter
>
emptyList
());
}
Gson
(
Excluder
excluder
,
FieldNamingStrategy
fieldNamingStrategy
,
Map
<
Type
,
InstanceCreator
<?>>
instanceCreators
,
boolean
serializeNulls
,
boolean
complexMapKeySerialization
,
boolean
generateNonExecutableGson
,
boolean
htmlSafe
,
boolean
prettyPrinting
,
boolean
lenient
,
boolean
serializeSpecialFloatingPointValues
,
boolean
useJdkUnsafe
,
LongSerializationPolicy
longSerializationPolicy
,
String
datePattern
,
int
dateStyle
,
int
timeStyle
,
List
<
TypeAdapterFactory
>
builderFactories
,
List
<
TypeAdapterFactory
>
builderHierarchyFactories
,
List
<
TypeAdapterFactory
>
factoriesToBeAdded
,
ToNumberStrategy
objectToNumberStrategy
,
ToNumberStrategy
numberToNumberStrategy
,
List
<
ReflectionAccessFilter
>
reflectionFilters
)
{
this
.
excluder
=
excluder
;
this
.
fieldNamingStrategy
=
fieldNamingStrategy
;
this
.
instanceCreators
=
instanceCreators
;
this
.
constructorConstructor
=
new
ConstructorConstructor
(
instanceCreators
,
useJdkUnsafe
,
reflectionFilters
);
this
.
serializeNulls
=
serializeNulls
;
this
.
complexMapKeySerialization
=
complexMapKeySerialization
;
this
.
generateNonExecutableJson
=
generateNonExecutableGson
;
this
.
htmlSafe
=
htmlSafe
;
this
.
prettyPrinting
=
prettyPrinting
;
this
.
lenient
=
lenient
;
this
.
serializeSpecialFloatingPointValues
=
serializeSpecialFloatingPointValues
;
this
.
useJdkUnsafe
=
useJdkUnsafe
;
this
.
longSerializationPolicy
=
longSerializationPolicy
;
this
.
datePattern
=
datePattern
;
this
.
dateStyle
=
dateStyle
;
this
.
timeStyle
=
timeStyle
;
this
.
builderFactories
=
builderFactories
;
this
.
builderHierarchyFactories
=
builderHierarchyFactories
;
this
.
objectToNumberStrategy
=
objectToNumberStrategy
;
this
.
numberToNumberStrategy
=
numberToNumberStrategy
;
this
.
reflectionFilters
=
reflectionFilters
;
List
<
TypeAdapterFactory
>
factories
=
new
ArrayList
<>();
// built-in type adapters that cannot be overridden
factories
.
add
(
TypeAdapters
.
JSON_ELEMENT_FACTORY
);
factories
.
add
(
ObjectTypeAdapter
.
getFactory
(
objectToNumberStrategy
));
// the excluder must precede all adapters that handle user-defined types
factories
.
add
(
excluder
);
// users' type adapters
factories
.
addAll
(
factoriesToBeAdded
);
// 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
);
TypeAdapter
<
Number
>
longAdapter
=
longAdapter
(
longSerializationPolicy
);
factories
.
add
(
TypeAdapters
.
newFactory
(
long
.
class
,
Long
.
class
,
longAdapter
));
factories
.
add
(
TypeAdapters
.
newFactory
(
double
.
class
,
Double
.
class
,
doubleAdapter
(
serializeSpecialFloatingPointValues
)));
factories
.
add
(
TypeAdapters
.
newFactory
(
float
.
class
,
Float
.
class
,
floatAdapter
(
serializeSpecialFloatingPointValues
)));
factories
.
add
(
NumberTypeAdapter
.
getFactory
(
numberToNumberStrategy
));
factories
.
add
(
TypeAdapters
.
ATOMIC_INTEGER_FACTORY
);
factories
.
add
(
TypeAdapters
.
ATOMIC_BOOLEAN_FACTORY
);
factories
.
add
(
TypeAdapters
.
newFactory
(
AtomicLong
.
class
,
atomicLongAdapter
(
longAdapter
)));
factories
.
add
(
TypeAdapters
.
newFactory
(
AtomicLongArray
.
class
,
atomicLongArrayAdapter
(
longAdapter
)));
factories
.
add
(
TypeAdapters
.
ATOMIC_INTEGER_ARRAY_FACTORY
);
factories
.
add
(
TypeAdapters
.
CHARACTER_FACTORY
);
factories
.
add
(
TypeAdapters
.
STRING_BUILDER_FACTORY
);
factories
.
add
(
TypeAdapters
.
STRING_BUFFER_FACTORY
);
factories
.
add
(
TypeAdapters
.
newFactory
(
BigDecimal
.
class
,
TypeAdapters
.
BIG_DECIMAL
));
factories
.
add
(
TypeAdapters
.
newFactory
(
BigInteger
.
class
,
TypeAdapters
.
BIG_INTEGER
));
// Add adapter for LazilyParsedNumber because user can obtain it from Gson and then try to serialize it again
factories
.
add
(
TypeAdapters
.
newFactory
(
LazilyParsedNumber
.
class
,
TypeAdapters
.
LAZILY_PARSED_NUMBER
));
factories
.
add
(
TypeAdapters
.
URL_FACTORY
);
factories
.
add
(
TypeAdapters
.
URI_FACTORY
);
factories
.
add
(
TypeAdapters
.
UUID_FACTORY
);
factories
.
add
(
TypeAdapters
.
CURRENCY_FACTORY
);
factories
.
add
(
TypeAdapters
.
LOCALE_FACTORY
);
factories
.
add
(
TypeAdapters
.
INET_ADDRESS_FACTORY
);
factories
.
add
(
TypeAdapters
.
BIT_SET_FACTORY
);
factories
.
add
(
DateTypeAdapter
.
FACTORY
);
factories
.
add
(
TypeAdapters
.
CALENDAR_FACTORY
);
if
(
SqlTypesSupport
.
SUPPORTS_SQL_TYPES
)
{
factories
.
add
(
SqlTypesSupport
.
TIME_FACTORY
);
factories
.
add
(
SqlTypesSupport
.
DATE_FACTORY
);
factories
.
add
(
SqlTypesSupport
.
TIMESTAMP_FACTORY
);
}
factories
.
add
(
ArrayTypeAdapter
.
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
));
this
.
jsonAdapterFactory
=
new
JsonAdapterAnnotationTypeAdapterFactory
(
constructorConstructor
);
factories
.
add
(
jsonAdapterFactory
);
factories
.
add
(
TypeAdapters
.
ENUM_FACTORY
);
factories
.
add
(
new
ReflectiveTypeAdapterFactory
(
constructorConstructor
,
fieldNamingStrategy
,
excluder
,
jsonAdapterFactory
,
reflectionFilters
));
this
.
factories
=
Collections
.
unmodifiableList
(
factories
);
}
/**
* Returns a new GsonBuilder containing all custom factories and configuration used by the current
* instance.
*
* @return a GsonBuilder instance.
*/
public
GsonBuilder
newBuilder
()
{
return
new
GsonBuilder
(
this
);
}
/**
* @deprecated This method by accident exposes an internal Gson class; it might be removed in a
* future version.
*/
@Deprecated
public
Excluder
excluder
()
{
return
excluder
;
}
/**
* Returns the field naming strategy used by this Gson instance.
*
* @see GsonBuilder#setFieldNamingStrategy(FieldNamingStrategy)
*/
public
FieldNamingStrategy
fieldNamingStrategy
()
{
return
fieldNamingStrategy
;
}
/**
* Returns whether this Gson instance is serializing JSON object properties with
* {@code null} values, or just omits them.
*
* @see GsonBuilder#serializeNulls()
*/
public
boolean
serializeNulls
()
{
return
serializeNulls
;
}
/**
* Returns whether this Gson instance produces JSON output which is
* HTML-safe, that means all HTML characters are escaped.
*
* @see GsonBuilder#disableHtmlEscaping()
*/
public
boolean
htmlSafe
()
{
return
htmlSafe
;
}
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
);
}
};
}
static
void
checkValidFloatingPoint
(
double
value
)
{
if
(
Double
.
isNaN
(
value
)
||
Double
.
isInfinite
(
value
))
{
throw
new
IllegalArgumentException
(
value
+
" is not a valid double value as per JSON specification. To override this"
+
" behavior, use GsonBuilder.serializeSpecialFloatingPointValues() method."
);
}
}
private
static
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
());
}
};
}
private
static
TypeAdapter
<
AtomicLong
>
atomicLongAdapter
(
final
TypeAdapter
<
Number
>
longAdapter
)
{
return
new
TypeAdapter
<
AtomicLong
>()
{
@Override
public
void
write
(
JsonWriter
out
,
AtomicLong
value
)
throws
IOException
{
longAdapter
.
write
(
out
,
value
.
get
());
}
@Override
public
AtomicLong
read
(
JsonReader
in
)
throws
IOException
{
Number
value
=
longAdapter
.
read
(
in
);
return
new
AtomicLong
(
value
.
longValue
());
}
}.
nullSafe
();
}
private
static
TypeAdapter
<
AtomicLongArray
>
atomicLongArrayAdapter
(
final
TypeAdapter
<
Number
>
longAdapter
)
{
return
new
TypeAdapter
<
AtomicLongArray
>()
{
@Override
public
void
write
(
JsonWriter
out
,
AtomicLongArray
value
)
throws
IOException
{
out
.
beginArray
();
for
(
int
i
=
0
,
length
=
value
.
length
();
i
<
length
;
i
++)
{
longAdapter
.
write
(
out
,
value
.
get
(
i
));
}
out
.
endArray
();
}
@Override
public
AtomicLongArray
read
(
JsonReader
in
)
throws
IOException
{
List
<
Long
>
list
=
new
ArrayList
<>();
in
.
beginArray
();
while
(
in
.
hasNext
())
{
long
value
=
longAdapter
.
read
(
in
).
longValue
();
list
.
add
(
value
);
}
in
.
endArray
();
int
length
=
list
.
size
();
AtomicLongArray
array
=
new
AtomicLongArray
(
length
);
for
(
int
i
=
0
;
i
<
length
;
++
i
)
{
array
.
set
(
i
,
list
.
get
(
i
));
}
return
array
;
}
}.
nullSafe
();
}
/**
* Returns the type adapter for {@code} type.
*
* @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
==
null
?
NULL_KEY_SURROGATE
:
type
);
if
(
cached
!=
null
)
{
return
(
TypeAdapter
<
T
>)
cached
;
}
Map
<
TypeToken
<?>,
FutureTypeAdapter
<?>>
threadCalls
=
calls
.
get
();
boolean
requiresThreadLocalCleanup
=
false
;
if
(
threadCalls
==
null
)
{
threadCalls
=
new
HashMap
<>();
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
<>();
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 ("
+
"2.9.0"
+
") 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 this call will skip all factories registered before {@code skipPast}. In case of
* multiple TypeAdapterFactories registered it is up to the caller of this function to insure
* that the order of registration does not prevent this method from reaching a factory they
* would expect to reply from this call.
* Note that since you can not override type adapter factories for String and Java primitive
* types, our stats factory will not count the number of String or primitives that will be
* read or written.
* @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 {@code 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
)
{
// Hack. If the skipPast factory isn't registered, assume the factory is being requested via
// our @JsonAdapter annotation.
if
(!
factories
.
contains
(
skipPast
))
{
skipPast
=
jsonAdapterFactory
;
}
boolean
skipPastFound
=
false
;
for
(
TypeAdapterFactory
factory
:
factories
)
{
if
(!
skipPastFound
)
{
if
(
factory
==
skipPast
)
{
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 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 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 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
();
writer
.
setSerializeNulls
(
serializeNulls
);
try
{
((
TypeAdapter
<
Object
>)
adapter
).
write
(
writer
,
src
);
}
catch
(
IOException
e
)
{
throw
new
JsonIOException
(
e
);
}
catch
(
AssertionError
e
)
{
AssertionError
error
=
new
AssertionError
(
"AssertionError (GSON "
+
"2.9.0"
+
"): "
+
e
.
getMessage
());
error
.
initCause
(
e
);
throw
error
;
}
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
JsonIOException
(
e
);
}
}
/**
* Returns a new JSON writer configured for the settings on this Gson instance.
*
* <p>The following settings are considered:
* <ul>
* <li>{@link GsonBuilder#disableHtmlEscaping()}</li>
* <li>{@link GsonBuilder#generateNonExecutableJson()}</li>
* <li>{@link GsonBuilder#serializeNulls()}</li>
* <li>{@link GsonBuilder#setLenient()}</li>
* <li>{@link GsonBuilder#setPrettyPrinting()}</li>
* </ul>
*/
public
JsonWriter
newJsonWriter
(
Writer
writer
)
throws
IOException
{
if
(
generateNonExecutableJson
)
{
writer
.
write
(
JSON_NON_EXECUTABLE_PREFIX
);
}
JsonWriter
jsonWriter
=
new
JsonWriter
(
writer
);
if
(
prettyPrinting
)
{
jsonWriter
.
setIndent
(
" "
);
}
jsonWriter
.
setHtmlSafe
(
htmlSafe
);
jsonWriter
.
setLenient
(
lenient
);
jsonWriter
.
setSerializeNulls
(
serializeNulls
);
return
jsonWriter
;
}
/**
* Returns a new JSON reader configured for the settings on this Gson instance.
*
* <p>The following settings are considered:
* <ul>
* <li>{@link GsonBuilder#setLenient()}</li>
* </ul>
*/
public
JsonReader
newJsonReader
(
Reader
reader
)
{
JsonReader
jsonReader
=
new
JsonReader
(
reader
);
jsonReader
.
setLenient
(
lenient
);
return
jsonReader
;
}
/**
* Writes the JSON for {@code jsonElement} to {@code writer}.
* @throws JsonIOException if there was a problem writing to the writer
*/
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
);
}
catch
(
AssertionError
e
)
{
AssertionError
error
=
new
AssertionError
(
"AssertionError (GSON "
+
"2.9.0"
+
"): "
+
e
.
getMessage
());
error
.
initCause
(
e
);
throw
error
;
}
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}
* or if {@code json} is empty.
* @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 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}
* or if {@code json} is empty.
* @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
=
newJsonReader
(
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 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
=
newJsonReader
(
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
);
}
catch
(
AssertionError
e
)
{
AssertionError
error
=
new
AssertionError
(
"AssertionError (GSON "
+
"2.9.0"
+
"): "
+
e
.
getMessage
());
error
.
initCause
(
e
);
throw
error
;
}
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}
* or if {@code json} is empty.
* @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 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}
* or if {@code json} is empty.
* @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
();
}
}
sobot_gson/src/main/java/com/sobot/gson/GsonBuilder.java
0 → 100644
View file @
3b40792f
/*
* 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
.
sobot
.
gson
;
import
java.lang.reflect.Type
;
import
java.text.DateFormat
;
import
java.util.ArrayList
;
import
java.util.Collections
;
import
java.util.Date
;
import
java.util.HashMap
;
import
java.util.LinkedList
;
import
java.util.List
;
import
java.util.Map
;
import
com.sobot.gson.InstanceCreator
;
import
com.sobot.gson.JsonDeserializer
;
import
com.sobot.gson.JsonObject
;
import
com.sobot.gson.JsonSerializer
;
import
com.sobot.gson.LongSerializationPolicy
;
import
com.sobot.gson.ReflectionAccessFilter
;
import
com.sobot.gson.ToNumberPolicy
;
import
com.sobot.gson.ToNumberStrategy
;
import
com.sobot.gson.TypeAdapter
;
import
com.sobot.gson.TypeAdapterFactory
;
import
com.sobot.gson.internal.
$Gson$Preconditions
;
import
com.sobot.gson.internal.Excluder
;
import
com.sobot.gson.internal.bind.DefaultDateTypeAdapter
;
import
com.sobot.gson.internal.bind.TreeTypeAdapter
;
import
com.sobot.gson.internal.bind.TypeAdapters
;
import
com.sobot.gson.internal.sql.SqlTypesSupport
;
import
com.sobot.gson.reflect.TypeToken
;
import
com.sobot.gson.stream.JsonReader
;
import
static
com
.
sobot
.
gson
.
Gson
.
DEFAULT_COMPLEX_MAP_KEYS
;
import
static
com
.
sobot
.
gson
.
Gson
.
DEFAULT_DATE_PATTERN
;
import
static
com
.
sobot
.
gson
.
Gson
.
DEFAULT_ESCAPE_HTML
;
import
static
com
.
sobot
.
gson
.
Gson
.
DEFAULT_JSON_NON_EXECUTABLE
;
import
static
com
.
sobot
.
gson
.
Gson
.
DEFAULT_LENIENT
;
import
static
com
.
sobot
.
gson
.
Gson
.
DEFAULT_NUMBER_TO_NUMBER_STRATEGY
;
import
static
com
.
sobot
.
gson
.
Gson
.
DEFAULT_OBJECT_TO_NUMBER_STRATEGY
;
import
static
com
.
sobot
.
gson
.
Gson
.
DEFAULT_PRETTY_PRINT
;
import
static
com
.
sobot
.
gson
.
Gson
.
DEFAULT_SERIALIZE_NULLS
;
import
static
com
.
sobot
.
gson
.
Gson
.
DEFAULT_SPECIALIZE_FLOAT_VALUES
;
import
static
com
.
sobot
.
gson
.
Gson
.
DEFAULT_USE_JDK_UNSAFE
;
/**
* <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>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>
*
* @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
<>();
private
final
List
<
TypeAdapterFactory
>
factories
=
new
ArrayList
<>();
/** tree-style hierarchy factories. These come after factories for backwards compatibility. */
private
final
List
<
TypeAdapterFactory
>
hierarchyFactories
=
new
ArrayList
<>();
private
boolean
serializeNulls
=
DEFAULT_SERIALIZE_NULLS
;
private
String
datePattern
=
DEFAULT_DATE_PATTERN
;
private
int
dateStyle
=
DateFormat
.
DEFAULT
;
private
int
timeStyle
=
DateFormat
.
DEFAULT
;
private
boolean
complexMapKeySerialization
=
DEFAULT_COMPLEX_MAP_KEYS
;
private
boolean
serializeSpecialFloatingPointValues
=
DEFAULT_SPECIALIZE_FLOAT_VALUES
;
private
boolean
escapeHtmlChars
=
DEFAULT_ESCAPE_HTML
;
private
boolean
prettyPrinting
=
DEFAULT_PRETTY_PRINT
;
private
boolean
generateNonExecutableJson
=
DEFAULT_JSON_NON_EXECUTABLE
;
private
boolean
lenient
=
DEFAULT_LENIENT
;
private
boolean
useJdkUnsafe
=
DEFAULT_USE_JDK_UNSAFE
;
private
ToNumberStrategy
objectToNumberStrategy
=
DEFAULT_OBJECT_TO_NUMBER_STRATEGY
;
private
ToNumberStrategy
numberToNumberStrategy
=
DEFAULT_NUMBER_TO_NUMBER_STRATEGY
;
private
final
LinkedList
<
ReflectionAccessFilter
>
reflectionFilters
=
new
LinkedList
<>();
/**
* 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
()
{
}
/**
* Constructs a GsonBuilder instance from a Gson instance. The newly constructed GsonBuilder
* has the same configuration as the previously built Gson instance.
*
* @param gson the gson instance whose configuration should by applied to a new GsonBuilder.
*/
GsonBuilder
(
Gson
gson
)
{
this
.
excluder
=
gson
.
excluder
;
this
.
fieldNamingPolicy
=
gson
.
fieldNamingStrategy
;
this
.
instanceCreators
.
putAll
(
gson
.
instanceCreators
);
this
.
serializeNulls
=
gson
.
serializeNulls
;
this
.
complexMapKeySerialization
=
gson
.
complexMapKeySerialization
;
this
.
generateNonExecutableJson
=
gson
.
generateNonExecutableJson
;
this
.
escapeHtmlChars
=
gson
.
htmlSafe
;
this
.
prettyPrinting
=
gson
.
prettyPrinting
;
this
.
lenient
=
gson
.
lenient
;
this
.
serializeSpecialFloatingPointValues
=
gson
.
serializeSpecialFloatingPointValues
;
this
.
longSerializationPolicy
=
gson
.
longSerializationPolicy
;
this
.
datePattern
=
gson
.
datePattern
;
this
.
dateStyle
=
gson
.
dateStyle
;
this
.
timeStyle
=
gson
.
timeStyle
;
this
.
factories
.
addAll
(
gson
.
builderFactories
);
this
.
hierarchyFactories
.
addAll
(
gson
.
builderHierarchyFactories
);
this
.
useJdkUnsafe
=
gson
.
useJdkUnsafe
;
this
.
objectToNumberStrategy
=
gson
.
objectToNumberStrategy
;
this
.
numberToNumberStrategy
=
gson
.
numberToNumberStrategy
;
this
.
reflectionFilters
.
addAll
(
gson
.
reflectionFilters
);
}
/**
* 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.sobot.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<>();
* 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<>();
* original.put(new Point(5, 6), "a");
* original.put(new Point(8, 8), "b");
* System.out.println(gson.toJson(original, type));
* }
* </pre>
*
* 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 specific number strategy during deserialization of {@link Object}.
*
* @param objectToNumberStrategy the actual object-to-number strategy
* @return a reference to this {@code GsonBuilder} object to fulfill the "Builder" pattern
* @see ToNumberPolicy#DOUBLE The default object-to-number strategy
*/
public
GsonBuilder
setObjectToNumberStrategy
(
ToNumberStrategy
objectToNumberStrategy
)
{
this
.
objectToNumberStrategy
=
objectToNumberStrategy
;
return
this
;
}
/**
* Configures Gson to apply a specific number strategy during deserialization of {@link Number}.
*
* @param numberToNumberStrategy the actual number-to-number strategy
* @return a reference to this {@code GsonBuilder} object to fulfill the "Builder" pattern
* @see ToNumberPolicy#LAZILY_PARSED_NUMBER The default number-to-number strategy
*/
public
GsonBuilder
setNumberToNumberStrategy
(
ToNumberStrategy
numberToNumberStrategy
)
{
this
.
numberToNumberStrategy
=
numberToNumberStrategy
;
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 serialization/deserialization.
*
* @param strategies the set of strategy object to apply during object (de)serialization.
* @return a reference to this {@code GsonBuilder} object to fulfill the "Builder" pattern
* @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 is strict and only accepts JSON as specified by
* <a href="http://www.ietf.org/rfc/rfc4627.txt">RFC 4627</a>. This option makes the parser
* liberal in what it accepts.
*
* @return a reference to this {@code GsonBuilder} object to fulfill the "Builder" pattern
* @see JsonReader#setLenient(boolean)
*/
public
GsonBuilder
setLenient
()
{
lenient
=
true
;
return
this
;
}
/**
* By default, Gson escapes HTML characters such as < > etc. Use this option to configure
* 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 Date} and in case
* the {@code java.sql} module is present, also {@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 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 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
(
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
;
}
/**
* Disables usage of JDK's {@code sun.misc.Unsafe}.
*
* <p>By default Gson uses {@code Unsafe} to create instances of classes which don't have
* a no-args constructor. However, {@code Unsafe} might not be available for all Java
* runtimes. For example Android does not provide {@code Unsafe}, or only with limited
* functionality. Additionally {@code Unsafe} creates instances without executing any
* constructor or initializer block, or performing initialization of field values. This can
* lead to surprising and difficult to debug errors.
* Therefore, to get reliable behavior regardless of which runtime is used, and to detect
* classes which cannot be deserialized in an early stage of development, this method allows
* disabling usage of {@code Unsafe}.
*
* @return a reference to this {@code GsonBuilder} object to fulfill the "Builder" pattern
*/
public
GsonBuilder
disableJdkUnsafe
()
{
this
.
useJdkUnsafe
=
false
;
return
this
;
}
/**
* Adds a reflection access filter. A reflection access filter prevents Gson from using
* reflection for the serialization and deserialization of certain classes. The logic in
* the filter specifies which classes those are.
*
* <p>Filters will be invoked in reverse registration order, that is, the most recently
* added filter will be invoked first.
*
* <p>By default Gson has no filters configured and will try to use reflection for
* all classes for which no {@link TypeAdapter} has been registered, and for which no
* built-in Gson {@code TypeAdapter} exists.
*
* @param filter filter to add
* @return a reference to this {@code GsonBuilder} object to fulfill the "Builder" pattern
*/
public
GsonBuilder
addReflectionAccessFilter
(
ReflectionAccessFilter
filter
)
{
if
(
filter
==
null
)
throw
new
NullPointerException
();
reflectionFilters
.
addFirst
(
filter
);
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
<>(
this
.
factories
.
size
()
+
this
.
hierarchyFactories
.
size
()
+
3
);
factories
.
addAll
(
this
.
factories
);
Collections
.
reverse
(
factories
);
List
<
TypeAdapterFactory
>
hierarchyFactories
=
new
ArrayList
<>(
this
.
hierarchyFactories
);
Collections
.
reverse
(
hierarchyFactories
);
factories
.
addAll
(
hierarchyFactories
);
addTypeAdaptersForDate
(
datePattern
,
dateStyle
,
timeStyle
,
factories
);
return
new
Gson
(
excluder
,
fieldNamingPolicy
,
new
HashMap
<>(
instanceCreators
),
serializeNulls
,
complexMapKeySerialization
,
generateNonExecutableJson
,
escapeHtmlChars
,
prettyPrinting
,
lenient
,
serializeSpecialFloatingPointValues
,
useJdkUnsafe
,
longSerializationPolicy
,
datePattern
,
dateStyle
,
timeStyle
,
new
ArrayList
<>(
this
.
factories
),
new
ArrayList
<>(
this
.
hierarchyFactories
),
factories
,
objectToNumberStrategy
,
numberToNumberStrategy
,
new
ArrayList
<>(
reflectionFilters
));
}
private
void
addTypeAdaptersForDate
(
String
datePattern
,
int
dateStyle
,
int
timeStyle
,
List
<
TypeAdapterFactory
>
factories
)
{
TypeAdapterFactory
dateAdapterFactory
;
boolean
sqlTypesSupported
=
SqlTypesSupport
.
SUPPORTS_SQL_TYPES
;
TypeAdapterFactory
sqlTimestampAdapterFactory
=
null
;
TypeAdapterFactory
sqlDateAdapterFactory
=
null
;
if
(
datePattern
!=
null
&&
!
datePattern
.
trim
().
isEmpty
())
{
dateAdapterFactory
=
DefaultDateTypeAdapter
.
DateType
.
DATE
.
createAdapterFactory
(
datePattern
);
if
(
sqlTypesSupported
)
{
sqlTimestampAdapterFactory
=
SqlTypesSupport
.
TIMESTAMP_DATE_TYPE
.
createAdapterFactory
(
datePattern
);
sqlDateAdapterFactory
=
SqlTypesSupport
.
DATE_DATE_TYPE
.
createAdapterFactory
(
datePattern
);
}
}
else
if
(
dateStyle
!=
DateFormat
.
DEFAULT
&&
timeStyle
!=
DateFormat
.
DEFAULT
)
{
dateAdapterFactory
=
DefaultDateTypeAdapter
.
DateType
.
DATE
.
createAdapterFactory
(
dateStyle
,
timeStyle
);
if
(
sqlTypesSupported
)
{
sqlTimestampAdapterFactory
=
SqlTypesSupport
.
TIMESTAMP_DATE_TYPE
.
createAdapterFactory
(
dateStyle
,
timeStyle
);
sqlDateAdapterFactory
=
SqlTypesSupport
.
DATE_DATE_TYPE
.
createAdapterFactory
(
dateStyle
,
timeStyle
);
}
}
else
{
return
;
}
factories
.
add
(
dateAdapterFactory
);
if
(
sqlTypesSupported
)
{
factories
.
add
(
sqlTimestampAdapterFactory
);
factories
.
add
(
sqlDateAdapterFactory
);
}
}
}
sobot_gson/src/main/java/com/sobot/gson/InstanceCreator.java
0 → 100644
View file @
3b40792f
/*
* 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
.
sobot
.
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
);
}
sobot_gson/src/main/java/com/sobot/gson/JsonArray.java
0 → 100644
View file @
3b40792f
/*
* 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
.
sobot
.
gson
;
import
com.sobot.gson.JsonElement
;
import
com.sobot.gson.JsonNull
;
import
com.sobot.gson.JsonPrimitive
;
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
<>();
}
public
JsonArray
(
int
capacity
)
{
elements
=
new
ArrayList
<>(
capacity
);
}
/**
* Creates a deep copy of this element and all its children
* @since 2.8.2
*/
@Override
public
JsonArray
deepCopy
()
{
if
(!
elements
.
isEmpty
())
{
JsonArray
result
=
new
JsonArray
(
elements
.
size
());
for
(
JsonElement
element
:
elements
)
{
result
.
add
(
element
.
deepCopy
());
}
return
result
;
}
return
new
JsonArray
();
}
/**
* Adds the specified boolean to self.
*
* @param bool the boolean that needs to be added to the array.
*/
public
void
add
(
Boolean
bool
)
{
elements
.
add
(
bool
==
null
?
JsonNull
.
INSTANCE
:
new
JsonPrimitive
(
bool
));
}
/**
* Adds the specified character to self.
*
* @param character the character that needs to be added to the array.
*/
public
void
add
(
Character
character
)
{
elements
.
add
(
character
==
null
?
JsonNull
.
INSTANCE
:
new
JsonPrimitive
(
character
));
}
/**
* Adds the specified number to self.
*
* @param number the number that needs to be added to the array.
*/
public
void
add
(
Number
number
)
{
elements
.
add
(
number
==
null
?
JsonNull
.
INSTANCE
:
new
JsonPrimitive
(
number
));
}
/**
* Adds the specified string to self.
*
* @param string the string that needs to be added to the array.
*/
public
void
add
(
String
string
)
{
elements
.
add
(
string
==
null
?
JsonNull
.
INSTANCE
:
new
JsonPrimitive
(
string
));
}
/**
* Adds the specified element to self.
*
* @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
);
}
/**
* Replaces the element at the specified position in this array with the specified element.
* Element can be null.
* @param index index of the element to replace
* @param element element to be stored at the specified position
* @return the element previously at the specified position
* @throws IndexOutOfBoundsException if the specified index is outside the array bounds
*/
public
JsonElement
set
(
int
index
,
JsonElement
element
)
{
return
elements
.
set
(
index
,
element
);
}
/**
* Removes the first occurrence of the specified element from this array, if it is present.
* If the array does not contain the element, it is unchanged.
* @param element element to be removed from this array, if present
* @return true if this array contained the specified element, false otherwise
* @since 2.3
*/
public
boolean
remove
(
JsonElement
element
)
{
return
elements
.
remove
(
element
);
}
/**
* Removes the element at the specified position in this array. Shifts any subsequent elements
* to the left (subtracts one from their indices). Returns the element that was removed from
* the array.
* @param index index the index of the element to be removed
* @return the element previously at the specified position
* @throws IndexOutOfBoundsException if the specified index is outside the array bounds
* @since 2.3
*/
public
JsonElement
remove
(
int
index
)
{
return
elements
.
remove
(
index
);
}
/**
* Returns true if this array contains the specified element.
* @return true if this array contains the specified element.
* @param element whose presence in this array is to be tested
* @since 2.3
*/
public
boolean
contains
(
JsonElement
element
)
{
return
elements
.
contains
(
element
);
}
/**
* Returns the number of elements in the array.
*
* @return the number of elements in the array.
*/
public
int
size
()
{
return
elements
.
size
();
}
/**
* Returns true if the array is empty
*
* @return true if the array is empty
*/
public
boolean
isEmpty
()
{
return
elements
.
isEmpty
();
}
/**
* Returns an iterator to navigate the elements of the array. Since the array is an ordered list,
* the iterator navigates the elements in the order they were inserted.
*
* @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
();
}
@Deprecated
@Override
public
char
getAsCharacter
()
{
if
(
elements
.
size
()
==
1
)
{
JsonElement
element
=
elements
.
get
(
0
);
return
element
.
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
();
}
}
sobot_gson/src/main/java/com/sobot/gson/JsonDeserializationContext.java
0 → 100644
View file @
3b40792f
/*
* 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
.
sobot
.
gson
;
import
com.sobot.gson.JsonDeserializer
;
import
com.sobot.gson.JsonElement
;
import
com.sobot.gson.JsonParseException
;
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
;
}
\ No newline at end of file
sobot_gson/src/main/java/com/sobot/gson/JsonDeserializer.java
0 → 100644
View file @
3b40792f
/*
* 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
.
sobot
.
gson
;
import
com.sobot.gson.JsonElement
;
import
com.sobot.gson.JsonParseException
;
import
com.sobot.gson.TypeAdapter
;
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
;
}
sobot_gson/src/main/java/com/sobot/gson/JsonElement.java
0 → 100644
View file @
3b40792f
/*
* 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
.
sobot
.
gson
;
import
com.sobot.gson.JsonNull
;
import
com.sobot.gson.JsonObject
;
import
com.sobot.gson.JsonPrimitive
;
import
com.sobot.gson.internal.Streams
;
import
com.sobot.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.
* @since 2.8.2
*/
public
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 IllegalStateException} 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 IllegalStateException} 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
(
"Not a JSON Array: "
+
this
);
}
/**
* convenience method to get this element as a {@link JsonPrimitive}. If the element is of some
* other type, a {@link IllegalStateException} 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
(
"Not a JSON Primitive: "
+
this
);
}
/**
* convenience method to get this element as a {@link JsonNull}. If the element is of some
* other type, a {@link IllegalStateException} 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
(
"Not a JSON Null: "
+
this
);
}
/**
* 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 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 the first character of this element as a string or the first
* character of this array's first element as a string.
*
* @return the first character of the string.
* @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.
* @since 1.3
* @deprecated This method is misleading, as it does not get this element as a char but rather as
* a string's first character.
*/
@Deprecated
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
);
}
}
}
sobot_gson/src/main/java/com/sobot/gson/JsonIOException.java
0 → 100644
View file @
3b40792f
/*
* 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
.
sobot
.
gson
;
import
com.sobot.gson.JsonParseException
;
/**
* 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
);
}
}
sobot_gson/src/main/java/com/sobot/gson/JsonNull.java
0 → 100644
View file @
3b40792f
/*
* 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
.
sobot
.
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
}
/**
* Returns the same instance since it is an immutable value
* @since 2.8.2
*/
@Override
public
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
;
}
}
sobot_gson/src/main/java/com/sobot/gson/JsonObject.java
0 → 100644
View file @
3b40792f
/*
* 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
.
sobot
.
gson
;
import
com.sobot.gson.JsonPrimitive
;
import
com.sobot.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
<>();
/**
* Creates a deep copy of this element and all its children
* @since 2.8.2
*/
@Override
public
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
)
{
members
.
put
(
property
,
value
==
null
?
JsonNull
.
INSTANCE
:
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
,
value
==
null
?
JsonNull
.
INSTANCE
:
new
JsonPrimitive
(
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
,
value
==
null
?
JsonNull
.
INSTANCE
:
new
JsonPrimitive
(
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
,
value
==
null
?
JsonNull
.
INSTANCE
:
new
JsonPrimitive
(
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
,
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
();
}
/**
* Returns a set of members key values.
*
* @return a set of member keys as Strings
* @since 2.8.1
*/
public
Set
<
String
>
keySet
()
{
return
members
.
keySet
();
}
/**
* Returns the number of key/value pairs in the object.
*
* @return the number of key/value pairs in the object.
*/
public
int
size
()
{
return
members
.
size
();
}
/**
* Convenience method to check if a member with the specified name is present in this object.
*
* @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
();
}
}
sobot_gson/src/main/java/com/sobot/gson/JsonParseException.java
0 → 100644
View file @
3b40792f
/*
* 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
.
sobot
.
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
);
}
}
sobot_gson/src/main/java/com/sobot/gson/JsonParser.java
0 → 100644
View file @
3b40792f
/*
* 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
.
sobot
.
gson
;
import
java.io.IOException
;
import
java.io.Reader
;
import
java.io.StringReader
;
import
com.sobot.gson.JsonSyntaxException
;
import
com.sobot.gson.internal.Streams
;
import
com.sobot.gson.stream.JsonReader
;
import
com.sobot.gson.stream.JsonToken
;
import
com.sobot.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
{
/** @deprecated No need to instantiate this class, use the static methods instead. */
@Deprecated
public
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
*/
public
static
JsonElement
parseString
(
String
json
)
throws
JsonSyntaxException
{
return
parseReader
(
new
StringReader
(
json
));
}
/**
* Parses the specified JSON string into a parse tree
*
* @param reader 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
*/
public
static
JsonElement
parseReader
(
Reader
reader
)
throws
JsonIOException
,
JsonSyntaxException
{
try
{
JsonReader
jsonReader
=
new
JsonReader
(
reader
);
JsonElement
element
=
parseReader
(
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
*/
public
static
JsonElement
parseReader
(
JsonReader
reader
)
throws
JsonIOException
,
JsonSyntaxException
{
boolean
lenient
=
reader
.
isLenient
();
reader
.
setLenient
(
true
);
try
{
return
Streams
.
parse
(
reader
);
}
catch
(
StackOverflowError
e
)
{
throw
new
JsonParseException
(
"Failed parsing JSON source: "
+
reader
+
" to Json"
,
e
);
}
catch
(
OutOfMemoryError
e
)
{
throw
new
JsonParseException
(
"Failed parsing JSON source: "
+
reader
+
" to Json"
,
e
);
}
finally
{
reader
.
setLenient
(
lenient
);
}
}
/** @deprecated Use {@link JsonParser#parseString} */
@Deprecated
public
JsonElement
parse
(
String
json
)
throws
JsonSyntaxException
{
return
parseString
(
json
);
}
/** @deprecated Use {@link JsonParser#parseReader(Reader)} */
@Deprecated
public
JsonElement
parse
(
Reader
json
)
throws
JsonIOException
,
JsonSyntaxException
{
return
parseReader
(
json
);
}
/** @deprecated Use {@link JsonParser#parseReader(JsonReader)} */
@Deprecated
public
JsonElement
parse
(
JsonReader
json
)
throws
JsonIOException
,
JsonSyntaxException
{
return
parseReader
(
json
);
}
}
sobot_gson/src/main/java/com/sobot/gson/JsonPrimitive.java
0 → 100644
View file @
3b40792f
/*
* 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
.
sobot
.
gson
;
import
com.sobot.gson.internal.
$Gson$Preconditions
;
import
java.math.BigDecimal
;
import
java.math.BigInteger
;
import
com.sobot.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
final
Object
value
;
/**
* Create a primitive containing a boolean value.
*
* @param bool the value to create the primitive with.
*/
public
JsonPrimitive
(
Boolean
bool
)
{
value
=
$Gson$Preconditions
.
checkNotNull
(
bool
);
}
/**
* Create a primitive containing a {@link Number}.
*
* @param number the value to create the primitive with.
*/
public
JsonPrimitive
(
Number
number
)
{
value
=
$Gson$Preconditions
.
checkNotNull
(
number
);
}
/**
* Create a primitive containing a String value.
*
* @param string the value to create the primitive with.
*/
public
JsonPrimitive
(
String
string
)
{
value
=
$Gson$Preconditions
.
checkNotNull
(
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
)
{
// convert characters to strings since in JSON, characters are represented as a single
// character string
value
=
$Gson$Preconditions
.
checkNotNull
(
c
).
toString
();
}
/**
* Returns the same value as primitives are immutable.
* @since 2.8.2
*/
@Override
public
JsonPrimitive
deepCopy
()
{
return
this
;
}
/**
* 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 boolean value.
*
* @return get this element as a primitive boolean value.
*/
@Override
public
boolean
getAsBoolean
()
{
if
(
isBoolean
())
{
return
((
Boolean
)
value
).
booleanValue
();
}
// 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
((
Boolean
)
value
).
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
);
}
@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
;
}
}
sobot_gson/src/main/java/com/sobot/gson/JsonSerializationContext.java
0 → 100644
View file @
3b40792f
/*
* 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
.
sobot
.
gson
;
import
com.sobot.gson.JsonSerializer
;
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
);
}
sobot_gson/src/main/java/com/sobot/gson/JsonSerializer.java
0 → 100644
View file @
3b40792f
/*
* 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
.
sobot
.
gson
;
import
com.sobot.gson.TypeAdapter
;
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.sobot.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>
*
* <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 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>
*
* <pre>
* class IdSerializer implements JsonSerializer<Id>() {
* public JsonElement serialize(Id id, Type typeOfId, JsonSerializationContext context) {
* return new JsonPrimitive(id.getValue());
* }
* }
* </pre>
*
* <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
);
}
sobot_gson/src/main/java/com/sobot/gson/JsonStreamParser.java
0 → 100644
View file @
3b40792f
/*
* 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
.
sobot
.
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.sobot.gson.JsonSyntaxException
;
import
com.sobot.gson.internal.Streams
;
import
com.sobot.gson.stream.JsonReader
;
import
com.sobot.gson.stream.JsonToken
;
import
com.sobot.gson.stream.MalformedJsonException
;
/**
* A streaming parser that allows reading of multiple {@link JsonElement}s from the specified reader
* asynchronously. The JSON data is parsed in lenient mode, see also
* {@link JsonReader#setLenient(boolean)}.
*
* <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. Throws a
* {@link NoSuchElementException} if no element is available.
*
* @return the next available {@code JsonElement} on the reader.
* @throws JsonSyntaxException if the incoming stream is malformed JSON.
* @throws NoSuchElementException if no {@code JsonElement} is available.
* @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
* @throws JsonSyntaxException if the incoming stream is malformed JSON.
* @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
();
}
}
sobot_gson/src/main/java/com/sobot/gson/JsonSyntaxException.java
0 → 100644
View file @
3b40792f
/*
* 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
.
sobot
.
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
);
}
}
sobot_gson/src/main/java/com/sobot/gson/LongSerializationPolicy.java
0 → 100644
View file @
3b40792f
/*
* 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
.
sobot
.
gson
;
/**
* Defines the expected format for a {@code long} or {@code Long} type when it is 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}}
*
* <p>A {@code null} value is serialized as {@link JsonNull}.
*/
DEFAULT
()
{
@Override
public
JsonElement
serialize
(
Long
value
)
{
if
(
value
==
null
)
{
return
JsonNull
.
INSTANCE
;
}
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"}}
*
* <p>A {@code null} value is serialized as {@link JsonNull}.
*/
STRING
()
{
@Override
public
JsonElement
serialize
(
Long
value
)
{
if
(
value
==
null
)
{
return
JsonNull
.
INSTANCE
;
}
return
new
JsonPrimitive
(
value
.
toString
());
}
};
/**
* 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
);
}
sobot_gson/src/main/java/com/sobot/gson/ReflectionAccessFilter.java
0 → 100644
View file @
3b40792f
package
com
.
sobot
.
gson
;
import
java.lang.reflect.AccessibleObject
;
import
com.sobot.gson.TypeAdapter
;
import
com.sobot.gson.internal.ReflectionAccessFilterHelper
;
/**
* Filter for determining whether reflection based serialization and
* deserialization is allowed for a class.
*
* <p>A filter can be useful in multiple scenarios, for example when
* upgrading to newer Java versions which use the Java Platform Module
* System (JPMS). A filter then allows to {@linkplain FilterResult#BLOCK_INACCESSIBLE
* prevent making inaccessible members accessible}, even if the used
* Java version might still allow illegal access (but logs a warning),
* or if {@code java} command line arguments are used to open the inaccessible
* packages to other parts of the application. This interface defines some
* convenience filters for this task, such as {@link #BLOCK_INACCESSIBLE_JAVA}.
*
* <p>A filter can also be useful to prevent mixing model classes of a
* project with other non-model classes; the filter could
* {@linkplain FilterResult#BLOCK_ALL block all reflective access} to
* non-model classes.
*
* <p>A reflection access filter is similar to an {@link ExclusionStrategy}
* with the major difference that a filter will cause an exception to be
* thrown when access is disallowed while an exclusion strategy just skips
* fields and classes.
*
* @see GsonBuilder#addReflectionAccessFilter(ReflectionAccessFilter)
*/
public
interface
ReflectionAccessFilter
{
/**
* Result of a filter check.
*/
enum
FilterResult
{
/**
* Reflection access for the class is allowed.
*
* <p>Note that this does not affect the Java access checks in any way,
* it only permits Gson to try using reflection for a class. The Java
* runtime might still deny such access.
*/
ALLOW
,
/**
* The filter is indecisive whether reflection access should be allowed.
* The next registered filter will be consulted to get the result. If
* there is no next filter, this result acts like {@link #ALLOW}.
*/
INDECISIVE
,
/**
* Blocks reflection access if a member of the class is not accessible
* by default and would have to be made accessible. This is unaffected
* by any {@code java} command line arguments being used to make packages
* accessible, or by module declaration directives which <i>open</i> the
* complete module or certain packages for reflection and will consider
* such packages inaccessible.
*
* <p>Note that this <b>only works for Java 9 and higher</b>, for older
* Java versions its functionality will be limited and it might behave like
* {@link #ALLOW}. Access checks are only performed as defined by the Java
* Language Specification (<a href="https://docs.oracle.com/javase/specs/jls/se11/html/jls-6.html#jls-6.6">JLS 11 §6.6</a>),
* restrictions imposed by a {@link SecurityManager} are not considered.
*
* <p>This result type is mainly intended to help enforce the access checks of
* the Java Platform Module System. It allows detecting illegal access, even if
* the used Java version would only log a warning, or is configured to open
* packages for reflection using command line arguments.
*
* @see AccessibleObject#canAccess(Object)
*/
BLOCK_INACCESSIBLE
,
/**
* Blocks all reflection access for the class. Other means for serializing
* and deserializing the class, such as a {@link TypeAdapter}, have to
* be used.
*/
BLOCK_ALL
}
/**
* Blocks all reflection access to members of standard Java classes which are
* not accessible by default. However, reflection access is still allowed for
* classes for which all fields are accessible and which have an accessible
* no-args constructor (or for which an {@link InstanceCreator} has been registered).
*
* <p>If this filter encounters a class other than a standard Java class it
* returns {@link FilterResult#INDECISIVE}.
*
* <p>This filter is mainly intended to help enforcing the access checks of
* Java Platform Module System. It allows detecting illegal access, even if
* the used Java version would only log a warning, or is configured to open
* packages for reflection. However, this filter <b>only works for Java 9 and
* higher</b>, when using an older Java version its functionality will be
* limited.
*
* <p>Note that this filter might not cover all standard Java classes. Currently
* only classes in a {@code java.*} or {@code javax.*} package are considered. The
* set of detected classes might be expanded in the future without prior notice.
*
* @see FilterResult#BLOCK_INACCESSIBLE
*/
ReflectionAccessFilter
BLOCK_INACCESSIBLE_JAVA
=
new
ReflectionAccessFilter
()
{
@Override
public
FilterResult
check
(
Class
<?>
rawClass
)
{
return
ReflectionAccessFilterHelper
.
isJavaType
(
rawClass
)
?
FilterResult
.
BLOCK_INACCESSIBLE
:
FilterResult
.
INDECISIVE
;
}
};
/**
* Blocks all reflection access to members of standard Java classes.
*
* <p>If this filter encounters a class other than a standard Java class it
* returns {@link FilterResult#INDECISIVE}.
*
* <p>This filter is mainly intended to prevent depending on implementation
* details of the Java platform and to help applications prepare for upgrading
* to the Java Platform Module System.
*
* <p>Note that this filter might not cover all standard Java classes. Currently
* only classes in a {@code java.*} or {@code javax.*} package are considered. The
* set of detected classes might be expanded in the future without prior notice.
*
* @see #BLOCK_INACCESSIBLE_JAVA
* @see FilterResult#BLOCK_ALL
*/
ReflectionAccessFilter
BLOCK_ALL_JAVA
=
new
ReflectionAccessFilter
()
{
@Override
public
FilterResult
check
(
Class
<?>
rawClass
)
{
return
ReflectionAccessFilterHelper
.
isJavaType
(
rawClass
)
?
FilterResult
.
BLOCK_ALL
:
FilterResult
.
INDECISIVE
;
}
};
/**
* Blocks all reflection access to members of standard Android classes.
*
* <p>If this filter encounters a class other than a standard Android class it
* returns {@link FilterResult#INDECISIVE}.
*
* <p>This filter is mainly intended to prevent depending on implementation
* details of the Android platform.
*
* <p>Note that this filter might not cover all standard Android classes. Currently
* only classes in an {@code android.*} or {@code androidx.*} package, and standard
* Java classes in a {@code java.*} or {@code javax.*} package are considered. The
* set of detected classes might be expanded in the future without prior notice.
*
* @see FilterResult#BLOCK_ALL
*/
ReflectionAccessFilter
BLOCK_ALL_ANDROID
=
new
ReflectionAccessFilter
()
{
@Override
public
FilterResult
check
(
Class
<?>
rawClass
)
{
return
ReflectionAccessFilterHelper
.
isAndroidType
(
rawClass
)
?
FilterResult
.
BLOCK_ALL
:
FilterResult
.
INDECISIVE
;
}
};
/**
* Blocks all reflection access to members of classes belonging to programming
* language platforms, such as Java, Android, Kotlin or Scala.
*
* <p>If this filter encounters a class other than a standard platform class it
* returns {@link FilterResult#INDECISIVE}.
*
* <p>This filter is mainly intended to prevent depending on implementation
* details of the platform classes.
*
* <p>Note that this filter might not cover all platform classes. Currently it
* combines the filters {@link #BLOCK_ALL_JAVA} and {@link #BLOCK_ALL_ANDROID},
* and checks for other language-specific platform classes like {@code kotlin.*}.
* The set of detected classes might be expanded in the future without prior notice.
*
* @see FilterResult#BLOCK_ALL
*/
ReflectionAccessFilter
BLOCK_ALL_PLATFORM
=
new
ReflectionAccessFilter
()
{
@Override
public
FilterResult
check
(
Class
<?>
rawClass
)
{
return
ReflectionAccessFilterHelper
.
isAnyPlatformType
(
rawClass
)
?
FilterResult
.
BLOCK_ALL
:
FilterResult
.
INDECISIVE
;
}
};
/**
* Checks if reflection access should be allowed for a class.
*
* @param rawClass
* Class to check
* @return
* Result indicating whether reflection access is allowed
*/
FilterResult
check
(
Class
<?>
rawClass
);
}
sobot_gson/src/main/java/com/sobot/gson/SobotGsonUtil.java
0 → 100644
View file @
3b40792f
package
com
.
sobot
.
gson
;
import
android.util.Log
;
import
com.sobot.gson.reflect.TypeToken
;
import
java.lang.reflect.Type
;
import
java.util.ArrayList
;
import
java.util.List
;
import
java.util.Map
;
public
class
SobotGsonUtil
{
public
static
final
String
TAG
=
SobotGsonUtil
.
class
.
getSimpleName
();
// 无参的私有构造方法
private
SobotGsonUtil
()
{
}
// 不用创建对象,直接使用 gson. 就可以调用方法
private
static
Gson
gson
=
null
;
/**
* 默认的时间格式化
*/
private
static
final
String
DATE_FORMAT_DEFAULT
=
"yyyy-MM-dd HH:mm:ss"
;
/**
* 判断gson对象是否存在了,不存在则创建对象
*/
static
{
if
(
gson
==
null
)
{
// gson = new Gson();
// 当使用 GsonBuilder 方式时属性为空的时候输出来的json字符串是有键值key的,显示形式是"key":null,而直接 new 出来的就没有"key":null的
gson
=
buildGson
();
}
}
/**
* 默认的 GSON 初始化
*/
public
static
Gson
buildGson
()
{
Gson
gson
=
new
Gson
().
newBuilder
()
.
setDateFormat
(
DATE_FORMAT_DEFAULT
)
.
create
();
return
gson
;
}
/**
* 将对象转成json格式
* Bean To Json
*
* @param object
* @return String
*/
public
static
String
beanToJson
(
Object
object
)
{
String
jsonString
=
null
;
try
{
if
(
gson
!=
null
)
{
jsonString
=
gson
.
toJson
(
object
);
}
}
catch
(
Exception
e
)
{
Log
.
e
(
TAG
,
"Bean 转 Json 格式异常:"
+
e
);
}
return
jsonString
;
}
/**
* 将 json 转成特定的 cls 的对象
* Json To Bean
*
* @param jsonString
* @param cls
* @return
*/
public
static
<
T
>
T
jsonToBean
(
String
jsonString
,
Class
<
T
>
cls
)
{
T
t
=
null
;
try
{
if
(
gson
!=
null
)
{
// 传入json对象和对象类型,将json转成对象
t
=
gson
.
fromJson
(
jsonString
,
cls
);
}
}
catch
(
JsonSyntaxException
e
)
{
Log
.
e
(
TAG
,
"Json 转 Bean 非法json字符串:"
+
e
);
}
return
t
;
}
/**
* 将 json 转成特定的 cls 的对象
* 解决泛型问题
*
* @param jsonString
* @param type
* @return
*/
public
static
<
T
>
T
jsonToBeans
(
String
jsonString
,
Type
type
)
{
T
t
=
null
;
try
{
if
(
gson
!=
null
)
{
// 传入json对象和对象类型,将json转成对象
t
=
gson
.
fromJson
(
jsonString
,
type
);
}
}
catch
(
JsonSyntaxException
e
)
{
Log
.
e
(
TAG
,
"Json 转 Bean 非法json字符串:"
+
e
);
}
return
t
;
}
/**
* json字符串转成list
* 解决泛型问题
* 备注:
* List list=gson.fromJson(jsonString, new TypeToken<List>() {}.getType());
* 该方法会报泛型类型擦除问题
*
* @param jsonString
* @param cls
* @param
* @return
*/
public
static
List
jsonToList
(
String
jsonString
,
Class
cls
)
{
List
list
=
new
ArrayList
();
try
{
if
(
gson
!=
null
)
{
JsonArray
array
=
new
JsonParser
().
parse
(
jsonString
).
getAsJsonArray
();
for
(
final
JsonElement
elem
:
array
)
{
list
.
add
(
gson
.
fromJson
(
elem
,
cls
));
}
}
}
catch
(
JsonSyntaxException
e
)
{
Log
.
e
(
TAG
,
"Json 转 List 非法json字符串:"
+
e
);
}
return
list
;
}
/**
* json 字符串转成 list map
* Json To List<Map<String,T>>
*
* @param jsonString
* @return
*/
public
static
<
T
>
List
<
Map
<
String
,
T
>>
jsonToListMaps
(
String
jsonString
)
{
List
<
Map
<
String
,
T
>>
list
=
null
;
try
{
if
(
gson
!=
null
)
{
list
=
gson
.
fromJson
(
jsonString
,
new
TypeToken
<
List
<
Map
<
String
,
T
>>>()
{
}.
getType
());
}
}
catch
(
JsonSyntaxException
e
)
{
Log
.
e
(
TAG
,
"Json 转 List 非法json字符串:"
+
e
);
}
return
list
;
}
/**
* json 字符串转成 map 的
* Json To Map
*
* @param jsonString
* @return
*/
public
static
<
T
>
Map
<
String
,
T
>
jsonToMaps
(
String
jsonString
)
{
Map
<
String
,
T
>
map
=
null
;
try
{
if
(
gson
!=
null
)
{
map
=
gson
.
fromJson
(
jsonString
,
new
TypeToken
<
Map
<
String
,
T
>>()
{
}.
getType
());
}
}
catch
(
JsonSyntaxException
e
)
{
Log
.
e
(
TAG
,
"Json 转 Map 非法json字符串:"
+
e
);
}
return
map
;
}
}
\ No newline at end of file
sobot_gson/src/main/java/com/sobot/gson/ToNumberPolicy.java
0 → 100644
View file @
3b40792f
/*
* Copyright (C) 2021 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
.
sobot
.
gson
;
import
java.io.IOException
;
import
java.math.BigDecimal
;
import
com.sobot.gson.ToNumberStrategy
;
import
com.sobot.gson.internal.LazilyParsedNumber
;
import
com.sobot.gson.stream.JsonReader
;
import
com.sobot.gson.stream.MalformedJsonException
;
/**
* An enumeration that defines two standard number reading strategies and a couple of
* strategies to overcome some historical Gson limitations while deserializing numbers as
* {@link Object} and {@link Number}.
*
* @see ToNumberStrategy
*/
public
enum
ToNumberPolicy
implements
ToNumberStrategy
{
/**
* Using this policy will ensure that numbers will be read as {@link Double} values.
* This is the default strategy used during deserialization of numbers as {@link Object}.
*/
DOUBLE
{
@Override
public
Double
readNumber
(
JsonReader
in
)
throws
IOException
{
return
in
.
nextDouble
();
}
},
/**
* Using this policy will ensure that numbers will be read as a lazily parsed number backed
* by a string. This is the default strategy used during deserialization of numbers as
* {@link Number}.
*/
LAZILY_PARSED_NUMBER
{
@Override
public
Number
readNumber
(
JsonReader
in
)
throws
IOException
{
return
new
LazilyParsedNumber
(
in
.
nextString
());
}
},
/**
* Using this policy will ensure that numbers will be read as {@link Long} or {@link Double}
* values depending on how JSON numbers are represented: {@code Long} if the JSON number can
* be parsed as a {@code Long} value, or otherwise {@code Double} if it can be parsed as a
* {@code Double} value. If the parsed double-precision number results in a positive or negative
* infinity ({@link Double#isInfinite()}) or a NaN ({@link Double#isNaN()}) value and the
* {@code JsonReader} is not {@link JsonReader#isLenient() lenient}, a {@link MalformedJsonException}
* is thrown.
*/
LONG_OR_DOUBLE
{
@Override
public
Number
readNumber
(
JsonReader
in
)
throws
IOException
,
JsonParseException
{
String
value
=
in
.
nextString
();
try
{
return
Long
.
parseLong
(
value
);
}
catch
(
NumberFormatException
longE
)
{
try
{
Double
d
=
Double
.
valueOf
(
value
);
if
((
d
.
isInfinite
()
||
d
.
isNaN
())
&&
!
in
.
isLenient
())
{
throw
new
MalformedJsonException
(
"JSON forbids NaN and infinities: "
+
d
+
"; at path "
+
in
.
getPreviousPath
());
}
return
d
;
}
catch
(
NumberFormatException
doubleE
)
{
throw
new
JsonParseException
(
"Cannot parse "
+
value
+
"; at path "
+
in
.
getPreviousPath
(),
doubleE
);
}
}
}
},
/**
* Using this policy will ensure that numbers will be read as numbers of arbitrary length
* using {@link BigDecimal}.
*/
BIG_DECIMAL
{
@Override
public
BigDecimal
readNumber
(
JsonReader
in
)
throws
IOException
{
String
value
=
in
.
nextString
();
try
{
return
new
BigDecimal
(
value
);
}
catch
(
NumberFormatException
e
)
{
throw
new
JsonParseException
(
"Cannot parse "
+
value
+
"; at path "
+
in
.
getPreviousPath
(),
e
);
}
}
}
}
sobot_gson/src/main/java/com/sobot/gson/ToNumberStrategy.java
0 → 100644
View file @
3b40792f
/*
* Copyright (C) 2021 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
.
sobot
.
gson
;
import
java.io.IOException
;
import
com.sobot.gson.stream.JsonReader
;
/**
* A strategy that is used to control how numbers should be deserialized for {@link Object} and {@link Number}
* when a concrete type of the deserialized number is unknown in advance. By default, Gson uses the following
* deserialization strategies:
*
* <ul>
* <li>{@link Double} values are returned for JSON numbers if the deserialization type is declared as
* {@code Object}, see {@link ToNumberPolicy#DOUBLE};</li>
* <li>Lazily parsed number values are returned if the deserialization type is declared as {@code Number},
* see {@link ToNumberPolicy#LAZILY_PARSED_NUMBER}.</li>
* </ul>
*
* <p>For historical reasons, Gson does not support deserialization of arbitrary-length numbers for
* {@code Object} and {@code Number} by default, potentially causing precision loss. However,
* <a href="https://tools.ietf.org/html/rfc8259#section-6">RFC 8259</a> permits this:
*
* <pre>
* This specification allows implementations to set limits on the range
* and precision of numbers accepted. Since software that implements
* IEEE 754 binary64 (double precision) numbers [IEEE754] is generally
* available and widely used, good interoperability can be achieved by
* implementations that expect no more precision or range than these
* provide, in the sense that implementations will approximate JSON
* numbers within the expected precision. A JSON number such as 1E400
* or 3.141592653589793238462643383279 may indicate potential
* interoperability problems, since it suggests that the software that
* created it expects receiving software to have greater capabilities
* for numeric magnitude and precision than is widely available.
* </pre>
*
* <p>To overcome the precision loss, use for example {@link ToNumberPolicy#LONG_OR_DOUBLE} or
* {@link ToNumberPolicy#BIG_DECIMAL}.</p>
*
* @see ToNumberPolicy
* @see GsonBuilder#setObjectToNumberStrategy(ToNumberStrategy)
* @see GsonBuilder#setNumberToNumberStrategy(ToNumberStrategy)
*/
public
interface
ToNumberStrategy
{
/**
* Reads a number from the given JSON reader. A strategy is supposed to read a single value from the
* reader, and the read value is guaranteed never to be {@code null}.
*
* @param in JSON reader to read a number from
* @return number read from the JSON reader.
*/
public
Number
readNumber
(
JsonReader
in
)
throws
IOException
;
}
sobot_gson/src/main/java/com/sobot/gson/TypeAdapter.java
0 → 100644
View file @
3b40792f
/*
* 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
.
sobot
.
gson
;
import
com.sobot.gson.internal.bind.JsonTreeWriter
;
import
com.sobot.gson.internal.bind.JsonTreeReader
;
import
com.sobot.gson.stream.JsonReader
;
import
com.sobot.gson.stream.JsonToken
;
import
com.sobot.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.sobot.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.sobot.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
)
{
StringWriter
stringWriter
=
new
StringWriter
();
try
{
toJson
(
stringWriter
,
value
);
}
catch
(
IOException
e
)
{
throw
new
AssertionError
(
e
);
// No I/O writing to a StringWriter.
}
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(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
);
}
}
}
sobot_gson/src/main/java/com/sobot/gson/TypeAdapterFactory.java
0 → 100644
View file @
3b40792f
/*
* 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
.
sobot
.
gson
;
import
com.sobot.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 TypeAdapterFactory {
* 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<>();
* 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.sobot.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 TypeAdapterFactory {
* 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
);
}
sobot_gson/src/main/java/com/sobot/gson/annotations/Expose.java
0 → 100644
View file @
3b40792f
/*
* 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
.
sobot
.
gson
.
annotations
;
import
java.lang.annotation.Documented
;
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.sobot.gson.Gson}
* with a {@link com.sobot.gson.GsonBuilder} and invoke
* {@link com.sobot.gson.GsonBuilder#excludeFieldsWithoutExposeAnnotation()}
* method.</p>
*
* <p>Here is an example of how this annotation is meant to be used:
* <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>
* 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
*/
@Documented
@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
;
}
sobot_gson/src/main/java/com/sobot/gson/annotations/JsonAdapter.java
0 → 100644
View file @
3b40792f
/*
* 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
.
sobot
.
gson
.
annotations
;
import
com.sobot.gson.JsonDeserializer
;
import
com.sobot.gson.JsonSerializer
;
import
com.sobot.gson.TypeAdapter
;
import
com.sobot.gson.TypeAdapterFactory
;
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 Gson {@link TypeAdapter} to use with a class
* or field.
*
* <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>
*
* <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>
*
* It's possible to specify different type adapters on a field, that
* field's type, and in the {@link com.sobot.gson.GsonBuilder}. Field
* annotations take precedence over {@code GsonBuilder}-registered type
* adapters, which in turn take precedence over annotated types.
*
* <p>The class referenced by this annotation must be either a {@link
* TypeAdapter} or a {@link TypeAdapterFactory}, or must implement one
* or both of {@link JsonDeserializer} or {@link JsonSerializer}.
* Using {@link TypeAdapterFactory} makes it possible to delegate
* to the enclosing {@code Gson} instance.
*
* @since 2.3
*
* @author Inderjeet Singh
* @author Joel Leitch
* @author Jesse Wilson
*/
// Note that the above example is taken from AdaptAnnotationTest.
@Retention
(
RetentionPolicy
.
RUNTIME
)
@Target
({
ElementType
.
TYPE
,
ElementType
.
FIELD
})
public
@interface
JsonAdapter
{
/** Either a {@link TypeAdapter} or {@link TypeAdapterFactory}, or one or both of {@link JsonDeserializer} or {@link JsonSerializer}. */
Class
<?>
value
();
/** false, to be able to handle {@code null} values within the adapter, default value is true. */
boolean
nullSafe
()
default
true
;
}
sobot_gson/src/main/java/com/sobot/gson/annotations/SerializedName.java
0 → 100644
View file @
3b40792f
/*
* 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
.
sobot
.
gson
.
annotations
;
import
java.lang.annotation.Documented
;
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.sobot.gson.FieldNamingPolicy}, including
* the default field naming policy, that may have been set on the {@link com.sobot.gson.Gson}
* instance. A different naming policy can set using the {@code GsonBuilder} class. See
* {@link com.sobot.gson.GsonBuilder#setFieldNamingPolicy(com.sobot.gson.FieldNamingPolicy)}
* for more information.</p>
*
* <p>Here is an example of how this annotation is meant to be used:</p>
* <pre>
* public class MyClass {
* @SerializedName("name") String a;
* @SerializedName(value="name1", alternate={"name2", "name3"}) String b;
* String c;
*
* public MyClass(String a, String b, String c) {
* this.a = a;
* this.b = b;
* this.c = c;
* }
* }
* </pre>
*
* <p>The following shows the output that is generated when serializing an instance of the
* above example class:</p>
* <pre>
* MyClass target = new MyClass("v1", "v2", "v3");
* Gson gson = new Gson();
* String json = gson.toJson(target);
* System.out.println(json);
*
* ===== OUTPUT =====
* {"name":"v1","name1":"v2","c":"v3"}
* </pre>
*
* <p>NOTE: The value you specify in this annotation must be a valid JSON field name.</p>
* While deserializing, all values specified in the annotation will be deserialized into the field.
* For example:
* <pre>
* MyClass target = gson.fromJson("{'name1':'v1'}", MyClass.class);
* assertEquals("v1", target.b);
* target = gson.fromJson("{'name2':'v2'}", MyClass.class);
* assertEquals("v2", target.b);
* target = gson.fromJson("{'name3':'v3'}", MyClass.class);
* assertEquals("v3", target.b);
* </pre>
* Note that MyClass.b is now deserialized from either name1, name2 or name3.
*
* @see com.sobot.gson.FieldNamingPolicy
*
* @author Inderjeet Singh
* @author Joel Leitch
*/
@Documented
@Retention
(
RetentionPolicy
.
RUNTIME
)
@Target
({
ElementType
.
FIELD
,
ElementType
.
METHOD
})
public
@interface
SerializedName
{
/**
* @return the desired name of the field when it is serialized or deserialized
*/
String
value
();
/**
* @return the alternative names of the field when it is deserialized
*/
String
[]
alternate
()
default
{};
}
sobot_gson/src/main/java/com/sobot/gson/annotations/Since.java
0 → 100644
View file @
3b40792f
/*
* 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
.
sobot
.
gson
.
annotations
;
import
java.lang.annotation.Documented
;
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.sobot.gson.Gson} with a
* {@link com.sobot.gson.GsonBuilder} and invoke
* {@link com.sobot.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
*/
@Documented
@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
();
}
sobot_gson/src/main/java/com/sobot/gson/annotations/Until.java
0 → 100644
View file @
3b40792f
/*
* 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
.
sobot
.
gson
.
annotations
;
import
java.lang.annotation.Documented
;
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.sobot.gson.Gson} with a
* {@link com.sobot.gson.GsonBuilder} and invoke
* {@link com.sobot.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
*/
@Documented
@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
();
}
sobot_gson/src/main/java/com/sobot/gson/annotations/package-info.java
0 → 100644
View file @
3b40792f
/**
* This package provides annotations that can be used with {@link com.sobot.gson.Gson}.
*
* @author Inderjeet Singh, Joel Leitch
*/
package
com
.
sobot
.
gson
.
annotations
;
\ No newline at end of file
sobot_gson/src/main/java/com/sobot/gson/internal/$Gson$Preconditions.java
0 → 100644
View file @
3b40792f
/*
* 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
.
sobot
.
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
{
private
$Gson$Preconditions
()
{
throw
new
UnsupportedOperationException
();
}
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
();
}
}
}
sobot_gson/src/main/java/com/sobot/gson/internal/$Gson$Types.java
0 → 100644
View file @
3b40792f
/**
* 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
.
sobot
.
gson
.
internal
;
import
java.io.Serializable
;
import
java.lang.reflect.Array
;
import
java.lang.reflect.GenericArrayType
;
import
java.lang.reflect.GenericDeclaration
;
import
java.lang.reflect.Modifier
;
import
java.lang.reflect.ParameterizedType
;
import
java.lang.reflect.Type
;
import
java.lang.reflect.TypeVariable
;
import
java.lang.reflect.WildcardType
;
import
java.util.Arrays
;
import
java.util.Collection
;
import
java.util.HashMap
;
import
java.util.Map
;
import
java.util.NoSuchElementException
;
import
java.util.Properties
;
import
static
com
.
sobot
.
gson
.
internal
.
$Gson$Preconditions
.
checkArgument
;
import
static
com
.
sobot
.
gson
.
internal
.
$Gson$Preconditions
.
checkNotNull
;
/**
* 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
()
{
throw
new
UnsupportedOperationException
();
}
/**
* Returns a new parameterized type, applying {@code typeArguments} to
* {@code rawType} and enclosed by {@code ownerType}.
*
* @return a {@link 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 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
)
{
Type
[]
upperBounds
;
if
(
bound
instanceof
WildcardType
)
{
upperBounds
=
((
WildcardType
)
bound
).
getUpperBounds
();
}
else
{
upperBounds
=
new
Type
[]
{
bound
};
}
return
new
WildcardTypeImpl
(
upperBounds
,
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
)
{
Type
[]
lowerBounds
;
if
(
bound
instanceof
WildcardType
)
{
lowerBounds
=
((
WildcardType
)
bound
).
getLowerBounds
();
}
else
{
lowerBounds
=
new
Type
[]
{
bound
};
}
return
new
WildcardTypeImpl
(
new
Type
[]
{
Object
.
class
},
lowerBounds
);
}
/**
* Returns a type that is functionally equal but not necessarily equal
* according to {@link Object#equals(Object) Object.equals()}. The returned
* type is {@link 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
)
{
Type
[]
bounds
=
((
WildcardType
)
type
).
getUpperBounds
();
// Currently the JLS only permits one bound for wildcards so using first bound is safe
assert
bounds
.
length
==
1
;
return
getRawType
(
bounds
[
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
);
}
}
private
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
;
}
}
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>}.
*/
private
static
Type
getGenericSupertype
(
Type
context
,
Class
<?>
rawType
,
Class
<?>
supertype
)
{
if
(
supertype
==
rawType
)
{
return
context
;
}
// we skip searching through interfaces if unknown is an interface
if
(
supertype
.
isInterface
())
{
Class
<?>[]
interfaces
=
rawType
.
getInterfaces
();
for
(
int
i
=
0
,
length
=
interfaces
.
length
;
i
<
length
;
i
++)
{
if
(
interfaces
[
i
]
==
supertype
)
{
return
rawType
.
getGenericInterfaces
()[
i
];
}
else
if
(
supertype
.
isAssignableFrom
(
interfaces
[
i
]))
{
return
getGenericSupertype
(
rawType
.
getGenericInterfaces
()[
i
],
interfaces
[
i
],
supertype
);
}
}
}
// check our supertypes
if
(!
rawType
.
isInterface
())
{
while
(
rawType
!=
Object
.
class
)
{
Class
<?>
rawSupertype
=
rawType
.
getSuperclass
();
if
(
rawSupertype
==
supertype
)
{
return
rawType
.
getGenericSuperclass
();
}
else
if
(
supertype
.
isAssignableFrom
(
rawSupertype
))
{
return
getGenericSupertype
(
rawType
.
getGenericSuperclass
(),
rawSupertype
,
supertype
);
}
rawType
=
rawSupertype
;
}
}
// we can't resolve this further
return
supertype
;
}
/**
* 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.
*/
private
static
Type
getSupertype
(
Type
context
,
Class
<?>
contextRawType
,
Class
<?>
supertype
)
{
if
(
context
instanceof
WildcardType
)
{
// wildcards are useless for resolving supertypes. As the upper bound has the same raw type, use it instead
Type
[]
bounds
=
((
WildcardType
)
context
).
getUpperBounds
();
// Currently the JLS only permits one bound for wildcards so using first bound is safe
assert
bounds
.
length
==
1
;
context
=
bounds
[
0
];
}
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
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
)
{
return
resolve
(
context
,
contextRawType
,
toResolve
,
new
HashMap
<
TypeVariable
<?>,
Type
>());
}
private
static
Type
resolve
(
Type
context
,
Class
<?>
contextRawType
,
Type
toResolve
,
Map
<
TypeVariable
<?>,
Type
>
visitedTypeVariables
)
{
// this implementation is made a little more complicated in an attempt to avoid object-creation
TypeVariable
<?>
resolving
=
null
;
while
(
true
)
{
if
(
toResolve
instanceof
TypeVariable
)
{
TypeVariable
<?>
typeVariable
=
(
TypeVariable
<?>)
toResolve
;
Type
previouslyResolved
=
visitedTypeVariables
.
get
(
typeVariable
);
if
(
previouslyResolved
!=
null
)
{
// cannot reduce due to infinite recursion
return
(
previouslyResolved
==
Void
.
TYPE
)
?
toResolve
:
previouslyResolved
;
}
// Insert a placeholder to mark the fact that we are in the process of resolving this type
visitedTypeVariables
.
put
(
typeVariable
,
Void
.
TYPE
);
if
(
resolving
==
null
)
{
resolving
=
typeVariable
;
}
toResolve
=
resolveTypeVariable
(
context
,
contextRawType
,
typeVariable
);
if
(
toResolve
==
typeVariable
)
{
break
;
}
}
else
if
(
toResolve
instanceof
Class
&&
((
Class
<?>)
toResolve
).
isArray
())
{
Class
<?>
original
=
(
Class
<?>)
toResolve
;
Type
componentType
=
original
.
getComponentType
();
Type
newComponentType
=
resolve
(
context
,
contextRawType
,
componentType
,
visitedTypeVariables
);
toResolve
=
equal
(
componentType
,
newComponentType
)
?
original
:
arrayOf
(
newComponentType
);
break
;
}
else
if
(
toResolve
instanceof
GenericArrayType
)
{
GenericArrayType
original
=
(
GenericArrayType
)
toResolve
;
Type
componentType
=
original
.
getGenericComponentType
();
Type
newComponentType
=
resolve
(
context
,
contextRawType
,
componentType
,
visitedTypeVariables
);
toResolve
=
equal
(
componentType
,
newComponentType
)
?
original
:
arrayOf
(
newComponentType
);
break
;
}
else
if
(
toResolve
instanceof
ParameterizedType
)
{
ParameterizedType
original
=
(
ParameterizedType
)
toResolve
;
Type
ownerType
=
original
.
getOwnerType
();
Type
newOwnerType
=
resolve
(
context
,
contextRawType
,
ownerType
,
visitedTypeVariables
);
boolean
changed
=
!
equal
(
newOwnerType
,
ownerType
);
Type
[]
args
=
original
.
getActualTypeArguments
();
for
(
int
t
=
0
,
length
=
args
.
length
;
t
<
length
;
t
++)
{
Type
resolvedTypeArgument
=
resolve
(
context
,
contextRawType
,
args
[
t
],
visitedTypeVariables
);
if
(!
equal
(
resolvedTypeArgument
,
args
[
t
]))
{
if
(!
changed
)
{
args
=
args
.
clone
();
changed
=
true
;
}
args
[
t
]
=
resolvedTypeArgument
;
}
}
toResolve
=
changed
?
newParameterizedTypeWithOwner
(
newOwnerType
,
original
.
getRawType
(),
args
)
:
original
;
break
;
}
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
],
visitedTypeVariables
);
if
(
lowerBound
!=
originalLowerBound
[
0
])
{
toResolve
=
supertypeOf
(
lowerBound
);
break
;
}
}
else
if
(
originalUpperBound
.
length
==
1
)
{
Type
upperBound
=
resolve
(
context
,
contextRawType
,
originalUpperBound
[
0
],
visitedTypeVariables
);
if
(
upperBound
!=
originalUpperBound
[
0
])
{
toResolve
=
subtypeOf
(
upperBound
);
break
;
}
}
toResolve
=
original
;
break
;
}
else
{
break
;
}
}
// ensure that any in-process resolution gets updated with the final result
if
(
resolving
!=
null
)
{
visitedTypeVariables
.
put
(
resolving
,
toResolve
);
}
return
toResolve
;
}
private
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
,
length
=
array
.
length
;
i
<
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
;
}
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
,
length
=
this
.
typeArguments
.
length
;
t
<
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
);
}
private
static
int
hashCodeOrZero
(
Object
o
)
{
return
o
!=
null
?
o
.
hashCode
()
:
0
;
}
@Override
public
int
hashCode
()
{
return
Arrays
.
hashCode
(
typeArguments
)
^
rawType
.
hashCode
()
^
hashCodeOrZero
(
ownerType
);
}
@Override
public
String
toString
()
{
int
length
=
typeArguments
.
length
;
if
(
length
==
0
)
{
return
typeToString
(
rawType
);
}
StringBuilder
stringBuilder
=
new
StringBuilder
(
30
*
(
length
+
1
));
stringBuilder
.
append
(
typeToString
(
rawType
)).
append
(
"<"
).
append
(
typeToString
(
typeArguments
[
0
]));
for
(
int
i
=
1
;
i
<
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 target Java version supports - at most one
* bound, see also https://bugs.openjdk.java.net/browse/JDK-8250660. 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
;
}
}
sobot_gson/src/main/java/com/sobot/gson/internal/ConstructorConstructor.java
0 → 100644
View file @
3b40792f
/*
* 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
.
sobot
.
gson
.
internal
;
import
com.sobot.gson.InstanceCreator
;
import
com.sobot.gson.JsonIOException
;
import
com.sobot.gson.ReflectionAccessFilter
;
import
com.sobot.gson.ReflectionAccessFilter.FilterResult
;
import
com.sobot.gson.internal.ReflectionAccessFilterHelper
;
import
com.sobot.gson.internal.reflect.ReflectionHelper
;
import
com.sobot.gson.reflect.TypeToken
;
import
java.lang.reflect.Constructor
;
import
java.lang.reflect.InvocationTargetException
;
import
java.lang.reflect.Modifier
;
import
java.lang.reflect.ParameterizedType
;
import
java.lang.reflect.Type
;
import
java.util.ArrayDeque
;
import
java.util.ArrayList
;
import
java.util.Collection
;
import
java.util.EnumMap
;
import
java.util.EnumSet
;
import
java.util.LinkedHashMap
;
import
java.util.LinkedHashSet
;
import
java.util.List
;
import
java.util.Map
;
import
java.util.Queue
;
import
java.util.Set
;
import
java.util.SortedMap
;
import
java.util.SortedSet
;
import
java.util.TreeMap
;
import
java.util.TreeSet
;
import
java.util.concurrent.ConcurrentHashMap
;
import
java.util.concurrent.ConcurrentMap
;
import
java.util.concurrent.ConcurrentNavigableMap
;
import
java.util.concurrent.ConcurrentSkipListMap
;
/**
* Returns a function that can construct an instance of a requested type.
*/
public
final
class
ConstructorConstructor
{
private
final
Map
<
Type
,
InstanceCreator
<?>>
instanceCreators
;
private
final
boolean
useJdkUnsafe
;
private
final
List
<
ReflectionAccessFilter
>
reflectionFilters
;
public
ConstructorConstructor
(
Map
<
Type
,
InstanceCreator
<?>>
instanceCreators
,
boolean
useJdkUnsafe
,
List
<
ReflectionAccessFilter
>
reflectionFilters
)
{
this
.
instanceCreators
=
instanceCreators
;
this
.
useJdkUnsafe
=
useJdkUnsafe
;
this
.
reflectionFilters
=
reflectionFilters
;
}
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
);
}
};
}
// First consider special constructors before checking for no-args constructors
// below to avoid matching internal no-args constructors which might be added in
// future JDK versions
ObjectConstructor
<
T
>
specialConstructor
=
newSpecialCollectionConstructor
(
type
,
rawType
);
if
(
specialConstructor
!=
null
)
{
return
specialConstructor
;
}
FilterResult
filterResult
=
ReflectionAccessFilterHelper
.
getFilterResult
(
reflectionFilters
,
rawType
);
ObjectConstructor
<
T
>
defaultConstructor
=
newDefaultConstructor
(
rawType
,
filterResult
);
if
(
defaultConstructor
!=
null
)
{
return
defaultConstructor
;
}
ObjectConstructor
<
T
>
defaultImplementation
=
newDefaultImplementationConstructor
(
type
,
rawType
);
if
(
defaultImplementation
!=
null
)
{
return
defaultImplementation
;
}
// Check whether type is instantiable; otherwise ReflectionAccessFilter recommendation
// of adjusting filter suggested below is irrelevant since it would not solve the problem
final
String
exceptionMessage
=
UnsafeAllocator
.
checkInstantiable
(
rawType
);
if
(
exceptionMessage
!=
null
)
{
return
new
ObjectConstructor
<
T
>()
{
@Override
public
T
construct
()
{
throw
new
JsonIOException
(
exceptionMessage
);
}
};
}
// Consider usage of Unsafe as reflection, so don't use if BLOCK_ALL
// Additionally, since it is not calling any constructor at all, don't use if BLOCK_INACCESSIBLE
if
(
filterResult
==
FilterResult
.
ALLOW
)
{
// finally try unsafe
return
newUnsafeAllocator
(
rawType
);
}
else
{
final
String
message
=
"Unable to create instance of "
+
rawType
+
"; ReflectionAccessFilter "
+
"does not permit using reflection or Unsafe. Register an InstanceCreator or a TypeAdapter "
+
"for this type or adjust the access filter to allow using reflection."
;
return
new
ObjectConstructor
<
T
>()
{
@Override
public
T
construct
()
{
throw
new
JsonIOException
(
message
);
}
};
}
}
/**
* Creates constructors for special JDK collection types which do not have a public no-args constructor.
*/
private
static
<
T
>
ObjectConstructor
<
T
>
newSpecialCollectionConstructor
(
final
Type
type
,
Class
<?
super
T
>
rawType
)
{
if
(
EnumSet
.
class
.
isAssignableFrom
(
rawType
))
{
return
new
ObjectConstructor
<
T
>()
{
@Override
public
T
construct
()
{
if
(
type
instanceof
ParameterizedType
)
{
Type
elementType
=
((
ParameterizedType
)
type
).
getActualTypeArguments
()[
0
];
if
(
elementType
instanceof
Class
)
{
@SuppressWarnings
({
"unchecked"
,
"rawtypes"
})
T
set
=
(
T
)
EnumSet
.
noneOf
((
Class
)
elementType
);
return
set
;
}
else
{
throw
new
JsonIOException
(
"Invalid EnumSet type: "
+
type
.
toString
());
}
}
else
{
throw
new
JsonIOException
(
"Invalid EnumSet type: "
+
type
.
toString
());
}
}
};
}
// Only support creation of EnumMap, but not of custom subtypes; for them type parameters
// and constructor parameter might have completely different meaning
else
if
(
rawType
==
EnumMap
.
class
)
{
return
new
ObjectConstructor
<
T
>()
{
@Override
public
T
construct
()
{
if
(
type
instanceof
ParameterizedType
)
{
Type
elementType
=
((
ParameterizedType
)
type
).
getActualTypeArguments
()[
0
];
if
(
elementType
instanceof
Class
)
{
@SuppressWarnings
({
"unchecked"
,
"rawtypes"
})
T
map
=
(
T
)
new
EnumMap
((
Class
)
elementType
);
return
map
;
}
else
{
throw
new
JsonIOException
(
"Invalid EnumMap type: "
+
type
.
toString
());
}
}
else
{
throw
new
JsonIOException
(
"Invalid EnumMap type: "
+
type
.
toString
());
}
}
};
}
return
null
;
}
private
static
<
T
>
ObjectConstructor
<
T
>
newDefaultConstructor
(
Class
<?
super
T
>
rawType
,
FilterResult
filterResult
)
{
// Cannot invoke constructor of abstract class
if
(
Modifier
.
isAbstract
(
rawType
.
getModifiers
()))
{
return
null
;
}
final
Constructor
<?
super
T
>
constructor
;
try
{
constructor
=
rawType
.
getDeclaredConstructor
();
}
catch
(
NoSuchMethodException
e
)
{
return
null
;
}
boolean
canAccess
=
filterResult
==
FilterResult
.
ALLOW
||
(
ReflectionAccessFilterHelper
.
canAccess
(
constructor
,
null
)
// Be a bit more lenient here for BLOCK_ALL; if constructor is accessible and public then allow calling it
&&
(
filterResult
!=
FilterResult
.
BLOCK_ALL
||
Modifier
.
isPublic
(
constructor
.
getModifiers
())));
if
(!
canAccess
)
{
final
String
message
=
"Unable to invoke no-args constructor of "
+
rawType
+
"; "
+
"constructor is not accessible and ReflectionAccessFilter does not permit making "
+
"it accessible. Register an InstanceCreator or a TypeAdapter for this type, change "
+
"the visibility of the constructor or adjust the access filter."
;
return
new
ObjectConstructor
<
T
>()
{
@Override
public
T
construct
()
{
throw
new
JsonIOException
(
message
);
}
};
}
// Only try to make accessible if allowed; in all other cases checks above should
// have verified that constructor is accessible
if
(
filterResult
==
FilterResult
.
ALLOW
)
{
final
String
exceptionMessage
=
ReflectionHelper
.
tryMakeAccessible
(
constructor
);
if
(
exceptionMessage
!=
null
)
{
/*
* Create ObjectConstructor which throws exception.
* This keeps backward compatibility (compared to returning `null` which
* would then choose another way of creating object).
* And it supports types which are only serialized but not deserialized
* (compared to directly throwing exception here), e.g. when runtime type
* of object is inaccessible, but compile-time type is accessible.
*/
return
new
ObjectConstructor
<
T
>()
{
@Override
public
T
construct
()
{
// New exception is created every time to avoid keeping reference
// to exception with potentially long stack trace, causing a
// memory leak
throw
new
JsonIOException
(
exceptionMessage
);
}
};
}
}
return
new
ObjectConstructor
<
T
>()
{
@Override
public
T
construct
()
{
try
{
@SuppressWarnings
(
"unchecked"
)
// T is the same raw type as is requested
T
newInstance
=
(
T
)
constructor
.
newInstance
();
return
newInstance
;
}
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
ReflectionHelper
.
createExceptionForUnexpectedIllegalAccess
(
e
);
}
}
};
}
/**
* Constructors for common interface types like Map and List and their
* subtypes.
*/
@SuppressWarnings
(
"unchecked"
)
// use runtime checks to guarantee that 'T' is what it is
private
static
<
T
>
ObjectConstructor
<
T
>
newDefaultImplementationConstructor
(
final
Type
type
,
Class
<?
super
T
>
rawType
)
{
/*
* IMPORTANT: Must only create instances for classes with public no-args constructor.
* For classes with special constructors / factory methods (e.g. EnumSet)
* `newSpecialCollectionConstructor` defined above must be used, to avoid no-args
* constructor check (which is called before this method) detecting internal no-args
* constructors which might be added in a future JDK version
*/
if
(
Collection
.
class
.
isAssignableFrom
(
rawType
))
{
if
(
SortedSet
.
class
.
isAssignableFrom
(
rawType
))
{
return
new
ObjectConstructor
<
T
>()
{
@Override
public
T
construct
()
{
return
(
T
)
new
TreeSet
<>();
}
};
}
else
if
(
Set
.
class
.
isAssignableFrom
(
rawType
))
{
return
new
ObjectConstructor
<
T
>()
{
@Override
public
T
construct
()
{
return
(
T
)
new
LinkedHashSet
<>();
}
};
}
else
if
(
Queue
.
class
.
isAssignableFrom
(
rawType
))
{
return
new
ObjectConstructor
<
T
>()
{
@Override
public
T
construct
()
{
return
(
T
)
new
ArrayDeque
<>();
}
};
}
else
{
return
new
ObjectConstructor
<
T
>()
{
@Override
public
T
construct
()
{
return
(
T
)
new
ArrayList
<>();
}
};
}
}
if
(
Map
.
class
.
isAssignableFrom
(
rawType
))
{
if
(
ConcurrentNavigableMap
.
class
.
isAssignableFrom
(
rawType
))
{
return
new
ObjectConstructor
<
T
>()
{
@Override
public
T
construct
()
{
return
(
T
)
new
ConcurrentSkipListMap
<>();
}
};
}
else
if
(
ConcurrentMap
.
class
.
isAssignableFrom
(
rawType
))
{
return
new
ObjectConstructor
<
T
>()
{
@Override
public
T
construct
()
{
return
(
T
)
new
ConcurrentHashMap
<>();
}
};
}
else
if
(
SortedMap
.
class
.
isAssignableFrom
(
rawType
))
{
return
new
ObjectConstructor
<
T
>()
{
@Override
public
T
construct
()
{
return
(
T
)
new
TreeMap
<>();
}
};
}
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
<>();
}
};
}
else
{
return
new
ObjectConstructor
<
T
>()
{
@Override
public
T
construct
()
{
return
(
T
)
new
LinkedTreeMap
<>();
}
};
}
}
return
null
;
}
private
<
T
>
ObjectConstructor
<
T
>
newUnsafeAllocator
(
final
Class
<?
super
T
>
rawType
)
{
if
(
useJdkUnsafe
)
{
return
new
ObjectConstructor
<
T
>()
{
private
final
UnsafeAllocator
unsafeAllocator
=
UnsafeAllocator
.
create
();
@Override
public
T
construct
()
{
try
{
@SuppressWarnings
(
"unchecked"
)
T
newInstance
=
(
T
)
unsafeAllocator
.
newInstance
(
rawType
);
return
newInstance
;
}
catch
(
Exception
e
)
{
throw
new
RuntimeException
((
"Unable to create instance of "
+
rawType
+
". "
+
"Registering an InstanceCreator or a TypeAdapter for this type, or adding a no-args "
+
"constructor may fix this problem."
),
e
);
}
}
};
}
else
{
final
String
exceptionMessage
=
"Unable to create instance of "
+
rawType
+
"; usage of JDK Unsafe "
+
"is disabled. Registering an InstanceCreator or a TypeAdapter for this type, adding a no-args "
+
"constructor, or enabling usage of JDK Unsafe may fix this problem."
;
return
new
ObjectConstructor
<
T
>()
{
@Override
public
T
construct
()
{
throw
new
JsonIOException
(
exceptionMessage
);
}
};
}
}
@Override
public
String
toString
()
{
return
instanceCreators
.
toString
();
}
}
sobot_gson/src/main/java/com/sobot/gson/internal/Excluder.java
0 → 100644
View file @
3b40792f
/*
* 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
.
sobot
.
gson
.
internal
;
import
com.sobot.gson.ExclusionStrategy
;
import
com.sobot.gson.FieldAttributes
;
import
com.sobot.gson.Gson
;
import
com.sobot.gson.TypeAdapter
;
import
com.sobot.gson.TypeAdapterFactory
;
import
com.sobot.gson.annotations.Expose
;
import
com.sobot.gson.annotations.Since
;
import
com.sobot.gson.annotations.Until
;
import
com.sobot.gson.reflect.TypeToken
;
import
com.sobot.gson.stream.JsonReader
;
import
com.sobot.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
(
e
);
}
}
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
<>(
serializationStrategies
);
result
.
serializationStrategies
.
add
(
exclusionStrategy
);
}
if
(
deserialization
)
{
result
.
deserializationStrategies
=
new
ArrayList
<>(
deserializationStrategies
);
result
.
deserializationStrategies
.
add
(
exclusionStrategy
);
}
return
result
;
}
@Override
public
<
T
>
TypeAdapter
<
T
>
create
(
final
Gson
gson
,
final
TypeToken
<
T
>
type
)
{
Class
<?>
rawType
=
type
.
getRawType
();
boolean
excludeClass
=
excludeClassChecks
(
rawType
);
final
boolean
skipSerialize
=
excludeClass
||
excludeClassInStrategy
(
rawType
,
true
);
final
boolean
skipDeserialize
=
excludeClass
||
excludeClassInStrategy
(
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
(
isAnonymousOrNonStaticLocal
(
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
;
}
private
boolean
excludeClassChecks
(
Class
<?>
clazz
)
{
if
(
version
!=
Excluder
.
IGNORE_VERSIONS
&&
!
isValidVersion
(
clazz
.
getAnnotation
(
Since
.
class
),
clazz
.
getAnnotation
(
Until
.
class
)))
{
return
true
;
}
if
(!
serializeInnerClasses
&&
isInnerClass
(
clazz
))
{
return
true
;
}
if
(
isAnonymousOrNonStaticLocal
(
clazz
))
{
return
true
;
}
return
false
;
}
public
boolean
excludeClass
(
Class
<?>
clazz
,
boolean
serialize
)
{
return
excludeClassChecks
(
clazz
)
||
excludeClassInStrategy
(
clazz
,
serialize
);
}
private
boolean
excludeClassInStrategy
(
Class
<?>
clazz
,
boolean
serialize
)
{
List
<
ExclusionStrategy
>
list
=
serialize
?
serializationStrategies
:
deserializationStrategies
;
for
(
ExclusionStrategy
exclusionStrategy
:
list
)
{
if
(
exclusionStrategy
.
shouldSkipClass
(
clazz
))
{
return
true
;
}
}
return
false
;
}
private
boolean
isAnonymousOrNonStaticLocal
(
Class
<?>
clazz
)
{
return
!
Enum
.
class
.
isAssignableFrom
(
clazz
)
&&
!
isStatic
(
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
;
}
}
sobot_gson/src/main/java/com/sobot/gson/internal/JavaVersion.java
0 → 100644
View file @
3b40792f
/*
* Copyright (C) 2017 The Gson authors
*
* 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
.
sobot
.
gson
.
internal
;
/**
* Utility to check the major Java version of the current JVM.
*/
public
final
class
JavaVersion
{
// Oracle defines naming conventions at http://www.oracle.com/technetwork/java/javase/versioning-naming-139433.html
// However, many alternate implementations differ. For example, Debian used 9-debian as the version string
private
static
final
int
majorJavaVersion
=
determineMajorJavaVersion
();
private
static
int
determineMajorJavaVersion
()
{
String
javaVersion
=
System
.
getProperty
(
"java.version"
);
return
getMajorJavaVersion
(
javaVersion
);
}
// Visible for testing only
static
int
getMajorJavaVersion
(
String
javaVersion
)
{
int
version
=
parseDotted
(
javaVersion
);
if
(
version
==
-
1
)
{
version
=
extractBeginningInt
(
javaVersion
);
}
if
(
version
==
-
1
)
{
return
6
;
// Choose minimum supported JDK version as default
}
return
version
;
}
// Parses both legacy 1.8 style and newer 9.0.4 style
private
static
int
parseDotted
(
String
javaVersion
)
{
try
{
String
[]
parts
=
javaVersion
.
split
(
"[._]"
);
int
firstVer
=
Integer
.
parseInt
(
parts
[
0
]);
if
(
firstVer
==
1
&&
parts
.
length
>
1
)
{
return
Integer
.
parseInt
(
parts
[
1
]);
}
else
{
return
firstVer
;
}
}
catch
(
NumberFormatException
e
)
{
return
-
1
;
}
}
private
static
int
extractBeginningInt
(
String
javaVersion
)
{
try
{
StringBuilder
num
=
new
StringBuilder
();
for
(
int
i
=
0
;
i
<
javaVersion
.
length
();
++
i
)
{
char
c
=
javaVersion
.
charAt
(
i
);
if
(
Character
.
isDigit
(
c
))
{
num
.
append
(
c
);
}
else
{
break
;
}
}
return
Integer
.
parseInt
(
num
.
toString
());
}
catch
(
NumberFormatException
e
)
{
return
-
1
;
}
}
/**
* @return the major Java version, i.e. '8' for Java 1.8, '9' for Java 9 etc.
*/
public
static
int
getMajorJavaVersion
()
{
return
majorJavaVersion
;
}
/**
* @return {@code true} if the application is running on Java 9 or later; and {@code false} otherwise.
*/
public
static
boolean
isJava9OrLater
()
{
return
majorJavaVersion
>=
9
;
}
private
JavaVersion
()
{
}
}
sobot_
json/src/main/java/com/sobot/fastjson/JSONStreamAware
.java
→
sobot_
gson/src/main/java/com/sobot/gson/internal/JsonReaderInternalAccess
.java
100755 → 100644
View file @
3b40792f
/*
/*
* Copyright
1999-2101 Alibaba Group
.
* Copyright
(C) 2011 Google Inc
.
*
*
* Licensed under the Apache License, Version 2.0 (the "License");
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* you may not use this file except in compliance with the License.
...
@@ -13,19 +13,20 @@
...
@@ -13,19 +13,20 @@
* See the License for the specific language governing permissions and
* See the License for the specific language governing permissions and
* limitations under the License.
* limitations under the License.
*/
*/
package
com
.
sobot
.
fastjson
;
package
com
.
sobot
.
gson
.
internal
;
import
com.sobot.gson.stream.JsonReader
;
import
java.io.IOException
;
import
java.io.IOException
;
/**
/**
* Beans that support customized output of JSON text to a writer shall implement this interface.
* Internal-only APIs of JsonReader available only to other classes in Gson.
*
* @author wenshao[szujobs@hotmail.com]
*/
*/
public
interface
JSONStreamAware
{
public
abstract
class
JsonReaderInternalAccess
{
public
static
JsonReaderInternalAccess
INSTANCE
;
/**
/**
* write JSON string to out
.
* Changes the type of the current property name token to a string value
.
*/
*/
void
writeJSONString
(
Appendable
out
)
throws
IOException
;
public
abstract
void
promoteNameToValue
(
JsonReader
reader
)
throws
IOException
;
}
}
sobot_gson/src/main/java/com/sobot/gson/internal/LazilyParsedNumber.java
0 → 100644
View file @
3b40792f
/*
* 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
.
sobot
.
gson
.
internal
;
import
java.io.IOException
;
import
java.io.InvalidObjectException
;
import
java.io.ObjectInputStream
;
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
;
/** @param value must not be null */
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
);
}
private
void
readObject
(
ObjectInputStream
in
)
throws
IOException
{
// Don't permit directly deserializing this class; writeReplace() should have written a replacement
throw
new
InvalidObjectException
(
"Deserialization is unsupported"
);
}
@Override
public
int
hashCode
()
{
return
value
.
hashCode
();
}
@Override
public
boolean
equals
(
Object
obj
)
{
if
(
this
==
obj
)
{
return
true
;
}
if
(
obj
instanceof
LazilyParsedNumber
)
{
LazilyParsedNumber
other
=
(
LazilyParsedNumber
)
obj
;
return
value
==
other
.
value
||
value
.
equals
(
other
.
value
);
}
return
false
;
}
}
sobot_gson/src/main/java/com/sobot/gson/internal/LinkedTreeMap.java
0 → 100644
View file @
3b40792f
/*
* 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
.
sobot
.
gson
.
internal
;
import
java.io.IOException
;
import
java.io.InvalidObjectException
;
import
java.io.ObjectInputStream
;
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
<>();
/**
* 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
<>(
nearest
,
key
,
header
,
header
.
prev
);
root
=
created
;
}
else
{
created
=
new
Node
<>(
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
;
LinkedTreeMapIterator
()
{
}
@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
();
}
}
final
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
<>(
this
);
}
private
void
readObject
(
ObjectInputStream
in
)
throws
IOException
{
// Don't permit directly deserializing this class; writeReplace() should have written a replacement
throw
new
InvalidObjectException
(
"Deserialization is unsupported"
);
}
}
sobot_
json/src/main/java/com/sobot/fastjson/serializer/ObjectSerialize
r.java
→
sobot_
gson/src/main/java/com/sobot/gson/internal/ObjectConstructo
r.java
100755 → 100644
View file @
3b40792f
/*
/*
* Copyright
1999-2101 Alibaba Group
.
* Copyright
(C) 2008 Google Inc
.
*
*
* Licensed under the Apache License, Version 2.0 (the "License");
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* you may not use this file except in compliance with the License.
...
@@ -13,15 +13,21 @@
...
@@ -13,15 +13,21 @@
* See the License for the specific language governing permissions and
* See the License for the specific language governing permissions and
* limitations under the License.
* limitations under the License.
*/
*/
package
com
.
sobot
.
fastjson
.
serializer
;
import
java.io.IOException
;
package
com
.
sobot
.
gson
.
internal
;
import
java.lang.reflect.Type
;
/**
/**
* @author wenshao[szujobs@hotmail.com]
* 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
Object
Serializer
{
public
interface
Object
Constructor
<
T
>
{
void
write
(
JSONSerializer
serializer
,
Object
object
,
Object
fieldName
,
Type
fieldType
)
throws
IOException
;
/**
* Returns a new instance.
*/
public
T
construct
();
}
}
\ No newline at end of file
sobot_gson/src/main/java/com/sobot/gson/internal/PreJava9DateFormatProvider.java
0 → 100644
View file @
3b40792f
/*
* Copyright (C) 2017 The Gson authors
*
* 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
.
sobot
.
gson
.
internal
;
import
java.text.DateFormat
;
import
java.text.SimpleDateFormat
;
import
java.util.Locale
;
/**
* Provides DateFormats for US locale with patterns which were the default ones before Java 9.
*/
public
class
PreJava9DateFormatProvider
{
/**
* Returns the same DateFormat as {@code DateFormat.getDateInstance(style, Locale.US)} in Java 8 or below.
*/
public
static
DateFormat
getUSDateFormat
(
int
style
)
{
return
new
SimpleDateFormat
(
getDateFormatPattern
(
style
),
Locale
.
US
);
}
/**
* Returns the same DateFormat as {@code DateFormat.getDateTimeInstance(dateStyle, timeStyle, Locale.US)}
* in Java 8 or below.
*/
public
static
DateFormat
getUSDateTimeFormat
(
int
dateStyle
,
int
timeStyle
)
{
String
pattern
=
getDatePartOfDateTimePattern
(
dateStyle
)
+
" "
+
getTimePartOfDateTimePattern
(
timeStyle
);
return
new
SimpleDateFormat
(
pattern
,
Locale
.
US
);
}
private
static
String
getDateFormatPattern
(
int
style
)
{
switch
(
style
)
{
case
DateFormat
.
SHORT
:
return
"M/d/yy"
;
case
DateFormat
.
MEDIUM
:
return
"MMM d, y"
;
case
DateFormat
.
LONG
:
return
"MMMM d, y"
;
case
DateFormat
.
FULL
:
return
"EEEE, MMMM d, y"
;
default
:
throw
new
IllegalArgumentException
(
"Unknown DateFormat style: "
+
style
);
}
}
private
static
String
getDatePartOfDateTimePattern
(
int
dateStyle
)
{
switch
(
dateStyle
)
{
case
DateFormat
.
SHORT
:
return
"M/d/yy"
;
case
DateFormat
.
MEDIUM
:
return
"MMM d, yyyy"
;
case
DateFormat
.
LONG
:
return
"MMMM d, yyyy"
;
case
DateFormat
.
FULL
:
return
"EEEE, MMMM d, yyyy"
;
default
:
throw
new
IllegalArgumentException
(
"Unknown DateFormat style: "
+
dateStyle
);
}
}
private
static
String
getTimePartOfDateTimePattern
(
int
timeStyle
)
{
switch
(
timeStyle
)
{
case
DateFormat
.
SHORT
:
return
"h:mm a"
;
case
DateFormat
.
MEDIUM
:
return
"h:mm:ss a"
;
case
DateFormat
.
FULL
:
case
DateFormat
.
LONG
:
return
"h:mm:ss a z"
;
default
:
throw
new
IllegalArgumentException
(
"Unknown DateFormat style: "
+
timeStyle
);
}
}
}
sobot_gson/src/main/java/com/sobot/gson/internal/Primitives.java
0 → 100644
View file @
3b40792f
/*
* 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
.
sobot
.
gson
.
internal
;
import
java.lang.reflect.Type
;
/**
* Contains static utility methods pertaining to primitive types and their
* corresponding wrapper types.
*
* @author Kevin Bourrillion
*/
public
final
class
Primitives
{
private
Primitives
()
{}
/**
* Returns true if this type is a primitive.
*/
public
static
boolean
isPrimitive
(
Type
type
)
{
return
type
instanceof
Class
<?>
&&
((
Class
<?>)
type
).
isPrimitive
();
}
/**
* 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
type
==
Integer
.
class
||
type
==
Float
.
class
||
type
==
Byte
.
class
||
type
==
Double
.
class
||
type
==
Long
.
class
||
type
==
Character
.
class
||
type
==
Boolean
.
class
||
type
==
Short
.
class
||
type
==
Void
.
class
;
}
/**
* 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>
*/
@SuppressWarnings
(
"unchecked"
)
public
static
<
T
>
Class
<
T
>
wrap
(
Class
<
T
>
type
)
{
if
(
type
==
int
.
class
)
return
(
Class
<
T
>)
Integer
.
class
;
if
(
type
==
float
.
class
)
return
(
Class
<
T
>)
Float
.
class
;
if
(
type
==
byte
.
class
)
return
(
Class
<
T
>)
Byte
.
class
;
if
(
type
==
double
.
class
)
return
(
Class
<
T
>)
Double
.
class
;
if
(
type
==
long
.
class
)
return
(
Class
<
T
>)
Long
.
class
;
if
(
type
==
char
.
class
)
return
(
Class
<
T
>)
Character
.
class
;
if
(
type
==
boolean
.
class
)
return
(
Class
<
T
>)
Boolean
.
class
;
if
(
type
==
short
.
class
)
return
(
Class
<
T
>)
Short
.
class
;
if
(
type
==
void
.
class
)
return
(
Class
<
T
>)
Void
.
class
;
return
type
;
}
/**
* 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>
*/
@SuppressWarnings
(
"unchecked"
)
public
static
<
T
>
Class
<
T
>
unwrap
(
Class
<
T
>
type
)
{
if
(
type
==
Integer
.
class
)
return
(
Class
<
T
>)
int
.
class
;
if
(
type
==
Float
.
class
)
return
(
Class
<
T
>)
float
.
class
;
if
(
type
==
Byte
.
class
)
return
(
Class
<
T
>)
byte
.
class
;
if
(
type
==
Double
.
class
)
return
(
Class
<
T
>)
double
.
class
;
if
(
type
==
Long
.
class
)
return
(
Class
<
T
>)
long
.
class
;
if
(
type
==
Character
.
class
)
return
(
Class
<
T
>)
char
.
class
;
if
(
type
==
Boolean
.
class
)
return
(
Class
<
T
>)
boolean
.
class
;
if
(
type
==
Short
.
class
)
return
(
Class
<
T
>)
short
.
class
;
if
(
type
==
Void
.
class
)
return
(
Class
<
T
>)
void
.
class
;
return
type
;
}
}
sobot_gson/src/main/java/com/sobot/gson/internal/ReflectionAccessFilterHelper.java
0 → 100644
View file @
3b40792f
package
com
.
sobot
.
gson
.
internal
;
import
java.lang.reflect.AccessibleObject
;
import
java.lang.reflect.Method
;
import
java.util.List
;
import
com.sobot.gson.ReflectionAccessFilter
;
import
com.sobot.gson.ReflectionAccessFilter.FilterResult
;
/**
* Internal helper class for {@link ReflectionAccessFilter}.
*/
public
class
ReflectionAccessFilterHelper
{
private
ReflectionAccessFilterHelper
()
{
}
// Platform type detection is based on Moshi's Util.isPlatformType(Class)
// See https://github.com/square/moshi/blob/3c108919ee1cce88a433ffda04eeeddc0341eae7/moshi/src/main/java/com/squareup/moshi/internal/Util.java#L141
public
static
boolean
isJavaType
(
Class
<?>
c
)
{
return
isJavaType
(
c
.
getName
());
}
private
static
boolean
isJavaType
(
String
className
)
{
return
className
.
startsWith
(
"java."
)
||
className
.
startsWith
(
"javax."
);
}
public
static
boolean
isAndroidType
(
Class
<?>
c
)
{
return
isAndroidType
(
c
.
getName
());
}
private
static
boolean
isAndroidType
(
String
className
)
{
return
className
.
startsWith
(
"android."
)
||
className
.
startsWith
(
"androidx."
)
||
isJavaType
(
className
);
}
public
static
boolean
isAnyPlatformType
(
Class
<?>
c
)
{
String
className
=
c
.
getName
();
return
isAndroidType
(
className
)
// Covers Android and Java
||
className
.
startsWith
(
"kotlin."
)
||
className
.
startsWith
(
"kotlinx."
)
||
className
.
startsWith
(
"scala."
);
}
/**
* Gets the result of applying all filters until the first one returns a result
* other than {@link FilterResult#INDECISIVE}, or {@link FilterResult#ALLOW} if
* the list of filters is empty or all returned {@code INDECISIVE}.
*/
public
static
FilterResult
getFilterResult
(
List
<
ReflectionAccessFilter
>
reflectionFilters
,
Class
<?>
c
)
{
for
(
ReflectionAccessFilter
filter
:
reflectionFilters
)
{
FilterResult
result
=
filter
.
check
(
c
);
if
(
result
!=
FilterResult
.
INDECISIVE
)
{
return
result
;
}
}
return
FilterResult
.
ALLOW
;
}
/**
* See {@link AccessibleObject#canAccess(Object)} (Java >= 9)
*/
public
static
boolean
canAccess
(
AccessibleObject
accessibleObject
,
Object
object
)
{
return
AccessChecker
.
INSTANCE
.
canAccess
(
accessibleObject
,
object
);
}
private
static
abstract
class
AccessChecker
{
public
static
final
AccessChecker
INSTANCE
;
static
{
AccessChecker
accessChecker
=
null
;
// TODO: Ideally should use Multi-Release JAR for this version specific code
if
(
JavaVersion
.
isJava9OrLater
())
{
try
{
final
Method
canAccessMethod
=
AccessibleObject
.
class
.
getDeclaredMethod
(
"canAccess"
,
Object
.
class
);
accessChecker
=
new
AccessChecker
()
{
@Override
public
boolean
canAccess
(
AccessibleObject
accessibleObject
,
Object
object
)
{
try
{
return
(
Boolean
)
canAccessMethod
.
invoke
(
accessibleObject
,
object
);
}
catch
(
Exception
e
)
{
throw
new
RuntimeException
(
"Failed invoking canAccess"
,
e
);
}
}
};
}
catch
(
NoSuchMethodException
ignored
)
{
}
}
if
(
accessChecker
==
null
)
{
accessChecker
=
new
AccessChecker
()
{
@Override
public
boolean
canAccess
(
AccessibleObject
accessibleObject
,
Object
object
)
{
// Cannot determine whether object can be accessed, so assume it can be accessed
return
true
;
}
};
}
INSTANCE
=
accessChecker
;
}
public
abstract
boolean
canAccess
(
AccessibleObject
accessibleObject
,
Object
object
);
}
}
sobot_gson/src/main/java/com/sobot/gson/internal/Streams.java
0 → 100644
View file @
3b40792f
/*
* 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
.
sobot
.
gson
.
internal
;
import
com.sobot.gson.JsonElement
;
import
com.sobot.gson.JsonIOException
;
import
com.sobot.gson.JsonNull
;
import
com.sobot.gson.JsonParseException
;
import
com.sobot.gson.JsonSyntaxException
;
import
com.sobot.gson.internal.bind.TypeAdapters
;
import
com.sobot.gson.stream.JsonReader
;
import
com.sobot.gson.stream.JsonWriter
;
import
com.sobot.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
{
private
Streams
()
{
throw
new
UnsupportedOperationException
();
}
/**
* 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
);
}
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
();
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
);
}
}
}
}
sobot_gson/src/main/java/com/sobot/gson/internal/UnsafeAllocator.java
0 → 100644
View file @
3b40792f
/*
* 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
.
sobot
.
gson
.
internal
;
import
com.sobot.gson.internal.ConstructorConstructor
;
import
java.io.ObjectInputStream
;
import
java.io.ObjectStreamClass
;
import
java.lang.reflect.Field
;
import
java.lang.reflect.Method
;
import
java.lang.reflect.Modifier
;
/**
* Do sneaky things to allocate objects without invoking their constructors.
*
* @author Joel Leitch
* @author Jesse Wilson
*/
public
abstract
class
UnsafeAllocator
{
public
abstract
<
T
>
T
newInstance
(
Class
<
T
>
c
)
throws
Exception
;
/**
* Check if the class can be instantiated by Unsafe allocator. If the instance has interface or abstract modifiers
* return an exception message.
* @param c instance of the class to be checked
* @return if instantiable {@code null}, else a non-{@code null} exception message
*/
static
String
checkInstantiable
(
Class
<?>
c
)
{
int
modifiers
=
c
.
getModifiers
();
if
(
Modifier
.
isInterface
(
modifiers
))
{
return
"Interfaces can't be instantiated! Register an InstanceCreator "
+
"or a TypeAdapter for this type. Interface name: "
+
c
.
getName
();
}
if
(
Modifier
.
isAbstract
(
modifiers
))
{
return
"Abstract classes can't be instantiated! Register an InstanceCreator "
+
"or a TypeAdapter for this type. Class name: "
+
c
.
getName
();
}
return
null
;
}
/**
* Asserts that the class is instantiable. This check should have already occurred
* in {@link ConstructorConstructor}; this check here acts as safeguard since trying
* to use Unsafe for non-instantiable classes might crash the JVM on some devices.
*/
private
static
void
assertInstantiable
(
Class
<?>
c
)
{
String
exceptionMessage
=
checkInstantiable
(
c
);
if
(
exceptionMessage
!=
null
)
{
throw
new
AssertionError
(
"UnsafeAllocator is used for non-instantiable type: "
+
exceptionMessage
);
}
}
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
{
assertInstantiable
(
c
);
return
(
T
)
allocateInstance
.
invoke
(
unsafe
,
c
);
}
};
}
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
{
assertInstantiable
(
c
);
return
(
T
)
newInstance
.
invoke
(
null
,
c
,
constructorId
);
}
};
}
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
{
assertInstantiable
(
c
);
return
(
T
)
newInstance
.
invoke
(
null
,
c
,
Object
.
class
);
}
};
}
catch
(
Exception
ignored
)
{
}
// give up
return
new
UnsafeAllocator
()
{
@Override
public
<
T
>
T
newInstance
(
Class
<
T
>
c
)
{
throw
new
UnsupportedOperationException
(
"Cannot allocate "
+
c
+
". Usage of JDK sun.misc.Unsafe is enabled, "
+
"but it could not be used. Make sure your runtime is configured correctly."
);
}
};
}
}
sobot_gson/src/main/java/com/sobot/gson/internal/bind/ArrayTypeAdapter.java
0 → 100644
View file @
3b40792f
/*
* 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
.
sobot
.
gson
.
internal
.
bind
;
import
java.io.IOException
;
import
java.lang.reflect.Array
;
import
java.lang.reflect.GenericArrayType
;
import
java.lang.reflect.Type
;
import
java.util.ArrayList
;
import
java.util.List
;
import
com.sobot.gson.Gson
;
import
com.sobot.gson.TypeAdapter
;
import
com.sobot.gson.TypeAdapterFactory
;
import
com.sobot.gson.internal.
$Gson$Types
;
import
com.sobot.gson.internal.bind.TypeAdapterRuntimeTypeWrapper
;
import
com.sobot.gson.reflect.TypeToken
;
import
com.sobot.gson.stream.JsonReader
;
import
com.sobot.gson.stream.JsonToken
;
import
com.sobot.gson.stream.JsonWriter
;
/**
* Adapt an array of objects.
*/
public
final
class
ArrayTypeAdapter
<
E
>
extends
TypeAdapter
<
Object
>
{
public
static
final
TypeAdapterFactory
FACTORY
=
new
TypeAdapterFactory
()
{
@SuppressWarnings
({
"unchecked"
,
"rawtypes"
})
@Override
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
<>(
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
<>();
in
.
beginArray
();
while
(
in
.
hasNext
())
{
E
instance
=
componentTypeAdapter
.
read
(
in
);
list
.
add
(
instance
);
}
in
.
endArray
();
int
size
=
list
.
size
();
Object
array
=
Array
.
newInstance
(
componentType
,
size
);
for
(
int
i
=
0
;
i
<
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
();
}
}
sobot_gson/src/main/java/com/sobot/gson/internal/bind/CollectionTypeAdapterFactory.java
0 → 100644
View file @
3b40792f
/*
* 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
.
sobot
.
gson
.
internal
.
bind
;
import
com.sobot.gson.Gson
;
import
com.sobot.gson.TypeAdapter
;
import
com.sobot.gson.TypeAdapterFactory
;
import
com.sobot.gson.internal.
$Gson$Types
;
import
com.sobot.gson.internal.ConstructorConstructor
;
import
com.sobot.gson.internal.ObjectConstructor
;
import
com.sobot.gson.internal.bind.TypeAdapterRuntimeTypeWrapper
;
import
com.sobot.gson.reflect.TypeToken
;
import
com.sobot.gson.stream.JsonReader
;
import
com.sobot.gson.stream.JsonToken
;
import
com.sobot.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
<>(
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
();
}
}
}
sobot_gson/src/main/java/com/sobot/gson/internal/bind/DateTypeAdapter.java
0 → 100644
View file @
3b40792f
/*
* 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
.
sobot
.
gson
.
internal
.
bind
;
import
com.sobot.gson.Gson
;
import
com.sobot.gson.JsonSyntaxException
;
import
com.sobot.gson.TypeAdapter
;
import
com.sobot.gson.TypeAdapterFactory
;
import
com.sobot.gson.internal.JavaVersion
;
import
com.sobot.gson.internal.PreJava9DateFormatProvider
;
import
com.sobot.gson.internal.bind.util.ISO8601Utils
;
import
com.sobot.gson.reflect.TypeToken
;
import
com.sobot.gson.stream.JsonReader
;
import
com.sobot.gson.stream.JsonToken
;
import
com.sobot.gson.stream.JsonWriter
;
import
java.io.IOException
;
import
java.text.DateFormat
;
import
java.text.ParseException
;
import
java.text.ParsePosition
;
import
java.util.ArrayList
;
import
java.util.Date
;
import
java.util.List
;
import
java.util.Locale
;
/**
* 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
()
{
@SuppressWarnings
(
"unchecked"
)
// we use a runtime check to make sure the 'T's equal
@Override
public
<
T
>
TypeAdapter
<
T
>
create
(
Gson
gson
,
TypeToken
<
T
>
typeToken
)
{
return
typeToken
.
getRawType
()
==
Date
.
class
?
(
TypeAdapter
<
T
>)
new
DateTypeAdapter
()
:
null
;
}
};
/**
* List of 1 or more different date formats used for de-serialization attempts.
* The first of them (default US format) is used for serialization as well.
*/
private
final
List
<
DateFormat
>
dateFormats
=
new
ArrayList
<>();
public
DateTypeAdapter
()
{
dateFormats
.
add
(
DateFormat
.
getDateTimeInstance
(
DateFormat
.
DEFAULT
,
DateFormat
.
DEFAULT
,
Locale
.
US
));
if
(!
Locale
.
getDefault
().
equals
(
Locale
.
US
))
{
dateFormats
.
add
(
DateFormat
.
getDateTimeInstance
(
DateFormat
.
DEFAULT
,
DateFormat
.
DEFAULT
));
}
if
(
JavaVersion
.
isJava9OrLater
())
{
dateFormats
.
add
(
PreJava9DateFormatProvider
.
getUSDateTimeFormat
(
DateFormat
.
DEFAULT
,
DateFormat
.
DEFAULT
));
}
}
@Override
public
Date
read
(
JsonReader
in
)
throws
IOException
{
if
(
in
.
peek
()
==
JsonToken
.
NULL
)
{
in
.
nextNull
();
return
null
;
}
return
deserializeToDate
(
in
);
}
private
Date
deserializeToDate
(
JsonReader
in
)
throws
IOException
{
String
s
=
in
.
nextString
();
synchronized
(
dateFormats
)
{
for
(
DateFormat
dateFormat
:
dateFormats
)
{
try
{
return
dateFormat
.
parse
(
s
);
}
catch
(
ParseException
ignored
)
{}
}
}
try
{
return
ISO8601Utils
.
parse
(
s
,
new
ParsePosition
(
0
));
}
catch
(
ParseException
e
)
{
throw
new
JsonSyntaxException
(
"Failed parsing '"
+
s
+
"' as Date; at path "
+
in
.
getPreviousPath
(),
e
);
}
}
@Override
public
void
write
(
JsonWriter
out
,
Date
value
)
throws
IOException
{
if
(
value
==
null
)
{
out
.
nullValue
();
return
;
}
DateFormat
dateFormat
=
dateFormats
.
get
(
0
);
String
dateFormatAsString
;
synchronized
(
dateFormats
)
{
dateFormatAsString
=
dateFormat
.
format
(
value
);
}
out
.
value
(
dateFormatAsString
);
}
}
sobot_gson/src/main/java/com/sobot/gson/internal/bind/DefaultDateTypeAdapter.java
0 → 100644
View file @
3b40792f
/*
* 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
.
sobot
.
gson
.
internal
.
bind
;
import
java.io.IOException
;
import
java.text.DateFormat
;
import
java.text.ParseException
;
import
java.text.ParsePosition
;
import
java.text.SimpleDateFormat
;
import
java.util.ArrayList
;
import
java.util.Date
;
import
java.util.List
;
import
java.util.Locale
;
import
com.sobot.gson.JsonSyntaxException
;
import
com.sobot.gson.TypeAdapter
;
import
com.sobot.gson.TypeAdapterFactory
;
import
com.sobot.gson.internal.
$Gson$Preconditions
;
import
com.sobot.gson.internal.JavaVersion
;
import
com.sobot.gson.internal.PreJava9DateFormatProvider
;
import
com.sobot.gson.internal.bind.util.ISO8601Utils
;
import
com.sobot.gson.stream.JsonReader
;
import
com.sobot.gson.stream.JsonToken
;
import
com.sobot.gson.stream.JsonWriter
;
/**
* This type adapter supports subclasses of date by defining a
* {@link DateType} and then using its {@code createAdapterFactory}
* methods.
*
* @author Inderjeet Singh
* @author Joel Leitch
*/
public
final
class
DefaultDateTypeAdapter
<
T
extends
Date
>
extends
TypeAdapter
<
T
>
{
private
static
final
String
SIMPLE_NAME
=
"DefaultDateTypeAdapter"
;
public
static
abstract
class
DateType
<
T
extends
Date
>
{
public
static
final
DateType
<
Date
>
DATE
=
new
DateType
<
Date
>(
Date
.
class
)
{
@Override
protected
Date
deserialize
(
Date
date
)
{
return
date
;
}
};
private
final
Class
<
T
>
dateClass
;
protected
DateType
(
Class
<
T
>
dateClass
)
{
this
.
dateClass
=
dateClass
;
}
protected
abstract
T
deserialize
(
Date
date
);
private
final
TypeAdapterFactory
createFactory
(
DefaultDateTypeAdapter
<
T
>
adapter
)
{
return
TypeAdapters
.
newFactory
(
dateClass
,
adapter
);
}
public
final
TypeAdapterFactory
createAdapterFactory
(
String
datePattern
)
{
return
createFactory
(
new
DefaultDateTypeAdapter
<>(
this
,
datePattern
));
}
public
final
TypeAdapterFactory
createAdapterFactory
(
int
style
)
{
return
createFactory
(
new
DefaultDateTypeAdapter
<>(
this
,
style
));
}
public
final
TypeAdapterFactory
createAdapterFactory
(
int
dateStyle
,
int
timeStyle
)
{
return
createFactory
(
new
DefaultDateTypeAdapter
<>(
this
,
dateStyle
,
timeStyle
));
}
public
final
TypeAdapterFactory
createDefaultsAdapterFactory
()
{
return
createFactory
(
new
DefaultDateTypeAdapter
<>(
this
,
DateFormat
.
DEFAULT
,
DateFormat
.
DEFAULT
));
}
}
private
final
DateType
<
T
>
dateType
;
/**
* List of 1 or more different date formats used for de-serialization attempts.
* The first of them is used for serialization as well.
*/
private
final
List
<
DateFormat
>
dateFormats
=
new
ArrayList
<>();
private
DefaultDateTypeAdapter
(
DateType
<
T
>
dateType
,
String
datePattern
)
{
this
.
dateType
=
$Gson$Preconditions
.
checkNotNull
(
dateType
);
dateFormats
.
add
(
new
SimpleDateFormat
(
datePattern
,
Locale
.
US
));
if
(!
Locale
.
getDefault
().
equals
(
Locale
.
US
))
{
dateFormats
.
add
(
new
SimpleDateFormat
(
datePattern
));
}
}
private
DefaultDateTypeAdapter
(
DateType
<
T
>
dateType
,
int
style
)
{
this
.
dateType
=
$Gson$Preconditions
.
checkNotNull
(
dateType
);
dateFormats
.
add
(
DateFormat
.
getDateInstance
(
style
,
Locale
.
US
));
if
(!
Locale
.
getDefault
().
equals
(
Locale
.
US
))
{
dateFormats
.
add
(
DateFormat
.
getDateInstance
(
style
));
}
if
(
JavaVersion
.
isJava9OrLater
())
{
dateFormats
.
add
(
PreJava9DateFormatProvider
.
getUSDateFormat
(
style
));
}
}
private
DefaultDateTypeAdapter
(
DateType
<
T
>
dateType
,
int
dateStyle
,
int
timeStyle
)
{
this
.
dateType
=
$Gson$Preconditions
.
checkNotNull
(
dateType
);
dateFormats
.
add
(
DateFormat
.
getDateTimeInstance
(
dateStyle
,
timeStyle
,
Locale
.
US
));
if
(!
Locale
.
getDefault
().
equals
(
Locale
.
US
))
{
dateFormats
.
add
(
DateFormat
.
getDateTimeInstance
(
dateStyle
,
timeStyle
));
}
if
(
JavaVersion
.
isJava9OrLater
())
{
dateFormats
.
add
(
PreJava9DateFormatProvider
.
getUSDateTimeFormat
(
dateStyle
,
timeStyle
));
}
}
// These methods need to be synchronized since JDK DateFormat classes are not thread-safe
// See issue 162
@Override
public
void
write
(
JsonWriter
out
,
Date
value
)
throws
IOException
{
if
(
value
==
null
)
{
out
.
nullValue
();
return
;
}
DateFormat
dateFormat
=
dateFormats
.
get
(
0
);
String
dateFormatAsString
;
synchronized
(
dateFormats
)
{
dateFormatAsString
=
dateFormat
.
format
(
value
);
}
out
.
value
(
dateFormatAsString
);
}
@Override
public
T
read
(
JsonReader
in
)
throws
IOException
{
if
(
in
.
peek
()
==
JsonToken
.
NULL
)
{
in
.
nextNull
();
return
null
;
}
Date
date
=
deserializeToDate
(
in
);
return
dateType
.
deserialize
(
date
);
}
private
Date
deserializeToDate
(
JsonReader
in
)
throws
IOException
{
String
s
=
in
.
nextString
();
synchronized
(
dateFormats
)
{
for
(
DateFormat
dateFormat
:
dateFormats
)
{
try
{
return
dateFormat
.
parse
(
s
);
}
catch
(
ParseException
ignored
)
{}
}
}
try
{
return
ISO8601Utils
.
parse
(
s
,
new
ParsePosition
(
0
));
}
catch
(
ParseException
e
)
{
throw
new
JsonSyntaxException
(
"Failed parsing '"
+
s
+
"' as Date; at path "
+
in
.
getPreviousPath
(),
e
);
}
}
@Override
public
String
toString
()
{
DateFormat
defaultFormat
=
dateFormats
.
get
(
0
);
if
(
defaultFormat
instanceof
SimpleDateFormat
)
{
return
SIMPLE_NAME
+
'('
+
((
SimpleDateFormat
)
defaultFormat
).
toPattern
()
+
')'
;
}
else
{
return
SIMPLE_NAME
+
'('
+
defaultFormat
.
getClass
().
getSimpleName
()
+
')'
;
}
}
}
sobot_gson/src/main/java/com/sobot/gson/internal/bind/JsonAdapterAnnotationTypeAdapterFactory.java
0 → 100644
View file @
3b40792f
/*
* 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
.
sobot
.
gson
.
internal
.
bind
;
import
com.sobot.gson.Gson
;
import
com.sobot.gson.JsonDeserializer
;
import
com.sobot.gson.JsonSerializer
;
import
com.sobot.gson.TypeAdapter
;
import
com.sobot.gson.TypeAdapterFactory
;
import
com.sobot.gson.annotations.JsonAdapter
;
import
com.sobot.gson.internal.ConstructorConstructor
;
import
com.sobot.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
;
}
@SuppressWarnings
(
"unchecked"
)
@Override
public
<
T
>
TypeAdapter
<
T
>
create
(
Gson
gson
,
TypeToken
<
T
>
targetType
)
{
Class
<?
super
T
>
rawType
=
targetType
.
getRawType
();
JsonAdapter
annotation
=
rawType
.
getAnnotation
(
JsonAdapter
.
class
);
if
(
annotation
==
null
)
{
return
null
;
}
return
(
TypeAdapter
<
T
>)
getTypeAdapter
(
constructorConstructor
,
gson
,
targetType
,
annotation
);
}
@SuppressWarnings
({
"unchecked"
,
"rawtypes"
})
// Casts guarded by conditionals.
TypeAdapter
<?>
getTypeAdapter
(
ConstructorConstructor
constructorConstructor
,
Gson
gson
,
TypeToken
<?>
type
,
JsonAdapter
annotation
)
{
Object
instance
=
constructorConstructor
.
get
(
TypeToken
.
get
(
annotation
.
value
())).
construct
();
TypeAdapter
<?>
typeAdapter
;
if
(
instance
instanceof
TypeAdapter
)
{
typeAdapter
=
(
TypeAdapter
<?>)
instance
;
}
else
if
(
instance
instanceof
TypeAdapterFactory
)
{
typeAdapter
=
((
TypeAdapterFactory
)
instance
).
create
(
gson
,
type
);
}
else
if
(
instance
instanceof
JsonSerializer
||
instance
instanceof
JsonDeserializer
)
{
JsonSerializer
<?>
serializer
=
instance
instanceof
JsonSerializer
?
(
JsonSerializer
)
instance
:
null
;
JsonDeserializer
<?>
deserializer
=
instance
instanceof
JsonDeserializer
?
(
JsonDeserializer
)
instance
:
null
;
typeAdapter
=
new
TreeTypeAdapter
(
serializer
,
deserializer
,
gson
,
type
,
null
);
}
else
{
throw
new
IllegalArgumentException
(
"Invalid attempt to bind an instance of "
+
instance
.
getClass
().
getName
()
+
" as a @JsonAdapter for "
+
type
.
toString
()
+
". @JsonAdapter value must be a TypeAdapter, TypeAdapterFactory,"
+
" JsonSerializer or JsonDeserializer."
);
}
if
(
typeAdapter
!=
null
&&
annotation
.
nullSafe
())
{
typeAdapter
=
typeAdapter
.
nullSafe
();
}
return
typeAdapter
;
}
}
sobot_gson/src/main/java/com/sobot/gson/internal/bind/JsonTreeReader.java
0 → 100644
View file @
3b40792f
/*
* 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
.
sobot
.
gson
.
internal
.
bind
;
import
com.sobot.gson.JsonArray
;
import
com.sobot.gson.JsonElement
;
import
com.sobot.gson.JsonNull
;
import
com.sobot.gson.JsonObject
;
import
com.sobot.gson.JsonPrimitive
;
import
com.sobot.gson.stream.JsonReader
;
import
com.sobot.gson.stream.JsonToken
;
import
java.io.IOException
;
import
java.io.Reader
;
import
java.util.Iterator
;
import
java.util.Map
;
import
java.util.Arrays
;
/**
* 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
();
/*
* The nesting stack. Using a manual array rather than an ArrayList saves 20%.
*/
private
Object
[]
stack
=
new
Object
[
32
];
private
int
stackSize
=
0
;
/*
* The path members. It corresponds directly to stack: At indices where the
* stack contains an object (EMPTY_OBJECT, DANGLING_NAME or NONEMPTY_OBJECT),
* pathNames contains the name at this scope. Where it contains an array
* (EMPTY_ARRAY, NONEMPTY_ARRAY) pathIndices contains the current index in
* that array. Otherwise the value is undefined, and we take advantage of that
* by incrementing pathIndices when doing so isn't useful.
*/
private
String
[]
pathNames
=
new
String
[
32
];
private
int
[]
pathIndices
=
new
int
[
32
];
public
JsonTreeReader
(
JsonElement
element
)
{
super
(
UNREADABLE_READER
);
push
(
element
);
}
@Override
public
void
beginArray
()
throws
IOException
{
expect
(
JsonToken
.
BEGIN_ARRAY
);
JsonArray
array
=
(
JsonArray
)
peekStack
();
push
(
array
.
iterator
());
pathIndices
[
stackSize
-
1
]
=
0
;
}
@Override
public
void
endArray
()
throws
IOException
{
expect
(
JsonToken
.
END_ARRAY
);
popStack
();
// empty iterator
popStack
();
// array
if
(
stackSize
>
0
)
{
pathIndices
[
stackSize
-
1
]++;
}
}
@Override
public
void
beginObject
()
throws
IOException
{
expect
(
JsonToken
.
BEGIN_OBJECT
);
JsonObject
object
=
(
JsonObject
)
peekStack
();
push
(
object
.
entrySet
().
iterator
());
}
@Override
public
void
endObject
()
throws
IOException
{
expect
(
JsonToken
.
END_OBJECT
);
popStack
();
// empty iterator
popStack
();
// object
if
(
stackSize
>
0
)
{
pathIndices
[
stackSize
-
1
]++;
}
}
@Override
public
boolean
hasNext
()
throws
IOException
{
JsonToken
token
=
peek
();
return
token
!=
JsonToken
.
END_OBJECT
&&
token
!=
JsonToken
.
END_ARRAY
&&
token
!=
JsonToken
.
END_DOCUMENT
;
}
@Override
public
JsonToken
peek
()
throws
IOException
{
if
(
stackSize
==
0
)
{
return
JsonToken
.
END_DOCUMENT
;
}
Object
o
=
peekStack
();
if
(
o
instanceof
Iterator
)
{
boolean
isObject
=
stack
[
stackSize
-
2
]
instanceof
JsonObject
;
Iterator
<?>
iterator
=
(
Iterator
<?>)
o
;
if
(
iterator
.
hasNext
())
{
if
(
isObject
)
{
return
JsonToken
.
NAME
;
}
else
{
push
(
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
[
stackSize
-
1
];
}
private
Object
popStack
()
{
Object
result
=
stack
[--
stackSize
];
stack
[
stackSize
]
=
null
;
return
result
;
}
private
void
expect
(
JsonToken
expected
)
throws
IOException
{
if
(
peek
()
!=
expected
)
{
throw
new
IllegalStateException
(
"Expected "
+
expected
+
" but was "
+
peek
()
+
locationString
());
}
}
@Override
public
String
nextName
()
throws
IOException
{
expect
(
JsonToken
.
NAME
);
Iterator
<?>
i
=
(
Iterator
<?>)
peekStack
();
Map
.
Entry
<?,
?>
entry
=
(
Map
.
Entry
<?,
?>)
i
.
next
();
String
result
=
(
String
)
entry
.
getKey
();
pathNames
[
stackSize
-
1
]
=
result
;
push
(
entry
.
getValue
());
return
result
;
}
@Override
public
String
nextString
()
throws
IOException
{
JsonToken
token
=
peek
();
if
(
token
!=
JsonToken
.
STRING
&&
token
!=
JsonToken
.
NUMBER
)
{
throw
new
IllegalStateException
(
"Expected "
+
JsonToken
.
STRING
+
" but was "
+
token
+
locationString
());
}
String
result
=
((
JsonPrimitive
)
popStack
()).
getAsString
();
if
(
stackSize
>
0
)
{
pathIndices
[
stackSize
-
1
]++;
}
return
result
;
}
@Override
public
boolean
nextBoolean
()
throws
IOException
{
expect
(
JsonToken
.
BOOLEAN
);
boolean
result
=
((
JsonPrimitive
)
popStack
()).
getAsBoolean
();
if
(
stackSize
>
0
)
{
pathIndices
[
stackSize
-
1
]++;
}
return
result
;
}
@Override
public
void
nextNull
()
throws
IOException
{
expect
(
JsonToken
.
NULL
);
popStack
();
if
(
stackSize
>
0
)
{
pathIndices
[
stackSize
-
1
]++;
}
}
@Override
public
double
nextDouble
()
throws
IOException
{
JsonToken
token
=
peek
();
if
(
token
!=
JsonToken
.
NUMBER
&&
token
!=
JsonToken
.
STRING
)
{
throw
new
IllegalStateException
(
"Expected "
+
JsonToken
.
NUMBER
+
" but was "
+
token
+
locationString
());
}
double
result
=
((
JsonPrimitive
)
peekStack
()).
getAsDouble
();
if
(!
isLenient
()
&&
(
Double
.
isNaN
(
result
)
||
Double
.
isInfinite
(
result
)))
{
throw
new
NumberFormatException
(
"JSON forbids NaN and infinities: "
+
result
);
}
popStack
();
if
(
stackSize
>
0
)
{
pathIndices
[
stackSize
-
1
]++;
}
return
result
;
}
@Override
public
long
nextLong
()
throws
IOException
{
JsonToken
token
=
peek
();
if
(
token
!=
JsonToken
.
NUMBER
&&
token
!=
JsonToken
.
STRING
)
{
throw
new
IllegalStateException
(
"Expected "
+
JsonToken
.
NUMBER
+
" but was "
+
token
+
locationString
());
}
long
result
=
((
JsonPrimitive
)
peekStack
()).
getAsLong
();
popStack
();
if
(
stackSize
>
0
)
{
pathIndices
[
stackSize
-
1
]++;
}
return
result
;
}
@Override
public
int
nextInt
()
throws
IOException
{
JsonToken
token
=
peek
();
if
(
token
!=
JsonToken
.
NUMBER
&&
token
!=
JsonToken
.
STRING
)
{
throw
new
IllegalStateException
(
"Expected "
+
JsonToken
.
NUMBER
+
" but was "
+
token
+
locationString
());
}
int
result
=
((
JsonPrimitive
)
peekStack
()).
getAsInt
();
popStack
();
if
(
stackSize
>
0
)
{
pathIndices
[
stackSize
-
1
]++;
}
return
result
;
}
JsonElement
nextJsonElement
()
throws
IOException
{
final
JsonToken
peeked
=
peek
();
if
(
peeked
==
JsonToken
.
NAME
||
peeked
==
JsonToken
.
END_ARRAY
||
peeked
==
JsonToken
.
END_OBJECT
||
peeked
==
JsonToken
.
END_DOCUMENT
)
{
throw
new
IllegalStateException
(
"Unexpected "
+
peeked
+
" when reading a JsonElement."
);
}
final
JsonElement
element
=
(
JsonElement
)
peekStack
();
skipValue
();
return
element
;
}
@Override
public
void
close
()
throws
IOException
{
stack
=
new
Object
[]
{
SENTINEL_CLOSED
};
stackSize
=
1
;
}
@Override
public
void
skipValue
()
throws
IOException
{
if
(
peek
()
==
JsonToken
.
NAME
)
{
nextName
();
pathNames
[
stackSize
-
2
]
=
"null"
;
}
else
{
popStack
();
if
(
stackSize
>
0
)
{
pathNames
[
stackSize
-
1
]
=
"null"
;
}
}
if
(
stackSize
>
0
)
{
pathIndices
[
stackSize
-
1
]++;
}
}
@Override
public
String
toString
()
{
return
getClass
().
getSimpleName
()
+
locationString
();
}
public
void
promoteNameToValue
()
throws
IOException
{
expect
(
JsonToken
.
NAME
);
Iterator
<?>
i
=
(
Iterator
<?>)
peekStack
();
Map
.
Entry
<?,
?>
entry
=
(
Map
.
Entry
<?,
?>)
i
.
next
();
push
(
entry
.
getValue
());
push
(
new
JsonPrimitive
((
String
)
entry
.
getKey
()));
}
private
void
push
(
Object
newTop
)
{
if
(
stackSize
==
stack
.
length
)
{
int
newLength
=
stackSize
*
2
;
stack
=
Arrays
.
copyOf
(
stack
,
newLength
);
pathIndices
=
Arrays
.
copyOf
(
pathIndices
,
newLength
);
pathNames
=
Arrays
.
copyOf
(
pathNames
,
newLength
);
}
stack
[
stackSize
++]
=
newTop
;
}
private
String
getPath
(
boolean
usePreviousPath
)
{
StringBuilder
result
=
new
StringBuilder
().
append
(
'$'
);
for
(
int
i
=
0
;
i
<
stackSize
;
i
++)
{
if
(
stack
[
i
]
instanceof
JsonArray
)
{
if
(++
i
<
stackSize
&&
stack
[
i
]
instanceof
Iterator
)
{
int
pathIndex
=
pathIndices
[
i
];
// If index is last path element it points to next array element; have to decrement
// `- 1` covers case where iterator for next element is on stack
// `- 2` covers case where peek() already pushed next element onto stack
if
(
usePreviousPath
&&
pathIndex
>
0
&&
(
i
==
stackSize
-
1
||
i
==
stackSize
-
2
))
{
pathIndex
--;
}
result
.
append
(
'['
).
append
(
pathIndex
).
append
(
']'
);
}
}
else
if
(
stack
[
i
]
instanceof
JsonObject
)
{
if
(++
i
<
stackSize
&&
stack
[
i
]
instanceof
Iterator
)
{
result
.
append
(
'.'
);
if
(
pathNames
[
i
]
!=
null
)
{
result
.
append
(
pathNames
[
i
]);
}
}
}
}
return
result
.
toString
();
}
@Override
public
String
getPreviousPath
()
{
return
getPath
(
true
);
}
@Override
public
String
getPath
()
{
return
getPath
(
false
);
}
private
String
locationString
()
{
return
" at path "
+
getPath
();
}
}
sobot_gson/src/main/java/com/sobot/gson/internal/bind/JsonTreeWriter.java
0 → 100644
View file @
3b40792f
/*
* 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
.
sobot
.
gson
.
internal
.
bind
;
import
com.sobot.gson.JsonArray
;
import
com.sobot.gson.JsonElement
;
import
com.sobot.gson.JsonNull
;
import
com.sobot.gson.JsonObject
;
import
com.sobot.gson.JsonPrimitive
;
import
com.sobot.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
<>();
/** 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
(
name
==
null
)
{
throw
new
NullPointerException
(
"name == null"
);
}
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
(
Boolean
value
)
throws
IOException
{
if
(
value
==
null
)
{
return
nullValue
();
}
put
(
new
JsonPrimitive
(
value
));
return
this
;
}
@Override
public
JsonWriter
value
(
double
value
)
throws
IOException
{
if
(!
isLenient
()
&&
(
Double
.
isNaN
(
value
)
||
Double
.
isInfinite
(
value
)))
{
throw
new
IllegalArgumentException
(
"JSON forbids NaN and infinities: "
+
value
);
}
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
);
}
}
sobot_gson/src/main/java/com/sobot/gson/internal/bind/MapTypeAdapterFactory.java
0 → 100644
View file @
3b40792f
/*
* 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
.
sobot
.
gson
.
internal
.
bind
;
import
com.sobot.gson.Gson
;
import
com.sobot.gson.JsonElement
;
import
com.sobot.gson.JsonPrimitive
;
import
com.sobot.gson.JsonSyntaxException
;
import
com.sobot.gson.TypeAdapter
;
import
com.sobot.gson.TypeAdapterFactory
;
import
com.sobot.gson.internal.
$Gson$Types
;
import
com.sobot.gson.internal.ConstructorConstructor
;
import
com.sobot.gson.internal.JsonReaderInternalAccess
;
import
com.sobot.gson.internal.ObjectConstructor
;
import
com.sobot.gson.internal.Streams
;
import
com.sobot.gson.internal.bind.TypeAdapterRuntimeTypeWrapper
;
import
com.sobot.gson.reflect.TypeToken
;
import
com.sobot.gson.stream.JsonReader
;
import
com.sobot.gson.stream.JsonToken
;
import
com.sobot.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<>();
* 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.sobot.gson.JsonParseException: Expecting object found: "(5,6)"
* at com.sobot.gson.JsonObjectDeserializationVisitor.visitFieldUsingCustomHandler
* at com.sobot.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
;
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
;
}
Type
[]
keyAndValueTypes
=
$Gson$Types
.
getMapKeyAndValueTypes
(
type
,
rawType
);
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
<>(
context
,
keyTypeAdapter
,
keyType
);
this
.
valueTypeAdapter
=
new
TypeAdapterRuntimeTypeWrapper
<>(
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
<>(
map
.
size
());
List
<
V
>
values
=
new
ArrayList
<>(
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
,
size
=
keys
.
size
();
i
<
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
,
size
=
keys
.
size
();
i
<
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
();
}
}
}
}
sobot_gson/src/main/java/com/sobot/gson/internal/bind/NumberTypeAdapter.java
0 → 100644
View file @
3b40792f
/*
* Copyright (C) 2020 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
.
sobot
.
gson
.
internal
.
bind
;
import
com.sobot.gson.Gson
;
import
com.sobot.gson.JsonSyntaxException
;
import
com.sobot.gson.ToNumberStrategy
;
import
com.sobot.gson.ToNumberPolicy
;
import
com.sobot.gson.TypeAdapter
;
import
com.sobot.gson.TypeAdapterFactory
;
import
com.sobot.gson.reflect.TypeToken
;
import
com.sobot.gson.stream.JsonReader
;
import
com.sobot.gson.stream.JsonToken
;
import
com.sobot.gson.stream.JsonWriter
;
import
java.io.IOException
;
/**
* Type adapter for {@link Number}.
*/
public
final
class
NumberTypeAdapter
extends
TypeAdapter
<
Number
>
{
/**
* Gson default factory using {@link ToNumberPolicy#LAZILY_PARSED_NUMBER}.
*/
private
static
final
TypeAdapterFactory
LAZILY_PARSED_NUMBER_FACTORY
=
newFactory
(
ToNumberPolicy
.
LAZILY_PARSED_NUMBER
);
private
final
ToNumberStrategy
toNumberStrategy
;
private
NumberTypeAdapter
(
ToNumberStrategy
toNumberStrategy
)
{
this
.
toNumberStrategy
=
toNumberStrategy
;
}
private
static
TypeAdapterFactory
newFactory
(
ToNumberStrategy
toNumberStrategy
)
{
final
NumberTypeAdapter
adapter
=
new
NumberTypeAdapter
(
toNumberStrategy
);
return
new
TypeAdapterFactory
()
{
@SuppressWarnings
(
"unchecked"
)
@Override
public
<
T
>
TypeAdapter
<
T
>
create
(
Gson
gson
,
TypeToken
<
T
>
type
)
{
return
type
.
getRawType
()
==
Number
.
class
?
(
TypeAdapter
<
T
>)
adapter
:
null
;
}
};
}
public
static
TypeAdapterFactory
getFactory
(
ToNumberStrategy
toNumberStrategy
)
{
if
(
toNumberStrategy
==
ToNumberPolicy
.
LAZILY_PARSED_NUMBER
)
{
return
LAZILY_PARSED_NUMBER_FACTORY
;
}
else
{
return
newFactory
(
toNumberStrategy
);
}
}
@Override
public
Number
read
(
JsonReader
in
)
throws
IOException
{
JsonToken
jsonToken
=
in
.
peek
();
switch
(
jsonToken
)
{
case
NULL:
in
.
nextNull
();
return
null
;
case
NUMBER:
case
STRING:
return
toNumberStrategy
.
readNumber
(
in
);
default
:
throw
new
JsonSyntaxException
(
"Expecting number, got: "
+
jsonToken
+
"; at path "
+
in
.
getPath
());
}
}
@Override
public
void
write
(
JsonWriter
out
,
Number
value
)
throws
IOException
{
out
.
value
(
value
);
}
}
sobot_gson/src/main/java/com/sobot/gson/internal/bind/ObjectTypeAdapter.java
0 → 100644
View file @
3b40792f
/*
* 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
.
sobot
.
gson
.
internal
.
bind
;
import
com.sobot.gson.Gson
;
import
com.sobot.gson.ToNumberStrategy
;
import
com.sobot.gson.ToNumberPolicy
;
import
com.sobot.gson.TypeAdapter
;
import
com.sobot.gson.TypeAdapterFactory
;
import
com.sobot.gson.internal.LinkedTreeMap
;
import
com.sobot.gson.reflect.TypeToken
;
import
com.sobot.gson.stream.JsonReader
;
import
com.sobot.gson.stream.JsonToken
;
import
com.sobot.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
>
{
/**
* Gson default factory using {@link ToNumberPolicy#DOUBLE}.
*/
private
static
final
TypeAdapterFactory
DOUBLE_FACTORY
=
newFactory
(
ToNumberPolicy
.
DOUBLE
);
private
final
Gson
gson
;
private
final
ToNumberStrategy
toNumberStrategy
;
private
ObjectTypeAdapter
(
Gson
gson
,
ToNumberStrategy
toNumberStrategy
)
{
this
.
gson
=
gson
;
this
.
toNumberStrategy
=
toNumberStrategy
;
}
private
static
TypeAdapterFactory
newFactory
(
final
ToNumberStrategy
toNumberStrategy
)
{
return
new
TypeAdapterFactory
()
{
@SuppressWarnings
(
"unchecked"
)
@Override
public
<
T
>
TypeAdapter
<
T
>
create
(
Gson
gson
,
TypeToken
<
T
>
type
)
{
if
(
type
.
getRawType
()
==
Object
.
class
)
{
return
(
TypeAdapter
<
T
>)
new
ObjectTypeAdapter
(
gson
,
toNumberStrategy
);
}
return
null
;
}
};
}
public
static
TypeAdapterFactory
getFactory
(
ToNumberStrategy
toNumberStrategy
)
{
if
(
toNumberStrategy
==
ToNumberPolicy
.
DOUBLE
)
{
return
DOUBLE_FACTORY
;
}
else
{
return
newFactory
(
toNumberStrategy
);
}
}
@Override
public
Object
read
(
JsonReader
in
)
throws
IOException
{
JsonToken
token
=
in
.
peek
();
switch
(
token
)
{
case
BEGIN_ARRAY:
List
<
Object
>
list
=
new
ArrayList
<>();
in
.
beginArray
();
while
(
in
.
hasNext
())
{
list
.
add
(
read
(
in
));
}
in
.
endArray
();
return
list
;
case
BEGIN_OBJECT:
Map
<
String
,
Object
>
map
=
new
LinkedTreeMap
<>();
in
.
beginObject
();
while
(
in
.
hasNext
())
{
map
.
put
(
in
.
nextName
(),
read
(
in
));
}
in
.
endObject
();
return
map
;
case
STRING:
return
in
.
nextString
();
case
NUMBER:
return
toNumberStrategy
.
readNumber
(
in
);
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
);
}
}
sobot_gson/src/main/java/com/sobot/gson/internal/bind/ReflectiveTypeAdapterFactory.java
0 → 100644
View file @
3b40792f
/*
* 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
.
sobot
.
gson
.
internal
.
bind
;
import
com.sobot.gson.FieldNamingStrategy
;
import
com.sobot.gson.Gson
;
import
com.sobot.gson.JsonIOException
;
import
com.sobot.gson.JsonSyntaxException
;
import
com.sobot.gson.ReflectionAccessFilter
;
import
com.sobot.gson.ReflectionAccessFilter.FilterResult
;
import
com.sobot.gson.TypeAdapter
;
import
com.sobot.gson.TypeAdapterFactory
;
import
com.sobot.gson.annotations.JsonAdapter
;
import
com.sobot.gson.annotations.SerializedName
;
import
com.sobot.gson.internal.
$Gson$Types
;
import
com.sobot.gson.internal.ConstructorConstructor
;
import
com.sobot.gson.internal.Excluder
;
import
com.sobot.gson.internal.ObjectConstructor
;
import
com.sobot.gson.internal.Primitives
;
import
com.sobot.gson.internal.ReflectionAccessFilterHelper
;
import
com.sobot.gson.internal.bind.JsonAdapterAnnotationTypeAdapterFactory
;
import
com.sobot.gson.internal.bind.TypeAdapterRuntimeTypeWrapper
;
import
com.sobot.gson.internal.reflect.ReflectionHelper
;
import
com.sobot.gson.reflect.TypeToken
;
import
com.sobot.gson.stream.JsonReader
;
import
com.sobot.gson.stream.JsonToken
;
import
com.sobot.gson.stream.JsonWriter
;
import
java.io.IOException
;
import
java.lang.reflect.Field
;
import
java.lang.reflect.Modifier
;
import
java.lang.reflect.Type
;
import
java.util.ArrayList
;
import
java.util.Collections
;
import
java.util.LinkedHashMap
;
import
java.util.List
;
import
java.util.Map
;
/**
* 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
;
private
final
JsonAdapterAnnotationTypeAdapterFactory
jsonAdapterFactory
;
private
final
List
<
ReflectionAccessFilter
>
reflectionFilters
;
public
ReflectiveTypeAdapterFactory
(
ConstructorConstructor
constructorConstructor
,
FieldNamingStrategy
fieldNamingPolicy
,
Excluder
excluder
,
JsonAdapterAnnotationTypeAdapterFactory
jsonAdapterFactory
,
List
<
ReflectionAccessFilter
>
reflectionFilters
)
{
this
.
constructorConstructor
=
constructorConstructor
;
this
.
fieldNamingPolicy
=
fieldNamingPolicy
;
this
.
excluder
=
excluder
;
this
.
jsonAdapterFactory
=
jsonAdapterFactory
;
this
.
reflectionFilters
=
reflectionFilters
;
}
public
boolean
excludeField
(
Field
f
,
boolean
serialize
)
{
return
excludeField
(
f
,
serialize
,
excluder
);
}
static
boolean
excludeField
(
Field
f
,
boolean
serialize
,
Excluder
excluder
)
{
return
!
excluder
.
excludeClass
(
f
.
getType
(),
serialize
)
&&
!
excluder
.
excludeField
(
f
,
serialize
);
}
/** first element holds the default name */
private
List
<
String
>
getFieldNames
(
Field
f
)
{
SerializedName
annotation
=
f
.
getAnnotation
(
SerializedName
.
class
);
if
(
annotation
==
null
)
{
String
name
=
fieldNamingPolicy
.
translateName
(
f
);
return
Collections
.
singletonList
(
name
);
}
String
serializedName
=
annotation
.
value
();
String
[]
alternates
=
annotation
.
alternate
();
if
(
alternates
.
length
==
0
)
{
return
Collections
.
singletonList
(
serializedName
);
}
List
<
String
>
fieldNames
=
new
ArrayList
<>(
alternates
.
length
+
1
);
fieldNames
.
add
(
serializedName
);
for
(
String
alternate
:
alternates
)
{
fieldNames
.
add
(
alternate
);
}
return
fieldNames
;
}
@Override
public
<
T
>
TypeAdapter
<
T
>
create
(
Gson
gson
,
final
TypeToken
<
T
>
type
)
{
Class
<?
super
T
>
raw
=
type
.
getRawType
();
if
(!
Object
.
class
.
isAssignableFrom
(
raw
))
{
return
null
;
// it's a primitive!
}
FilterResult
filterResult
=
ReflectionAccessFilterHelper
.
getFilterResult
(
reflectionFilters
,
raw
);
if
(
filterResult
==
FilterResult
.
BLOCK_ALL
)
{
throw
new
JsonIOException
(
"ReflectionAccessFilter does not permit using reflection for "
+
raw
+
". Register a TypeAdapter for this type or adjust the access filter."
);
}
boolean
blockInaccessible
=
filterResult
==
FilterResult
.
BLOCK_INACCESSIBLE
;
ObjectConstructor
<
T
>
constructor
=
constructorConstructor
.
get
(
type
);
return
new
Adapter
<>(
constructor
,
getBoundFields
(
gson
,
type
,
raw
,
blockInaccessible
));
}
private
static
void
checkAccessible
(
Object
object
,
Field
field
)
{
if
(!
ReflectionAccessFilterHelper
.
canAccess
(
field
,
Modifier
.
isStatic
(
field
.
getModifiers
())
?
null
:
object
))
{
throw
new
JsonIOException
(
"Field '"
+
field
.
getDeclaringClass
().
getName
()
+
"#"
+
field
.
getName
()
+
"' is not accessible and ReflectionAccessFilter does not "
+
"permit making it accessible. Register a TypeAdapter for the declaring type "
+
"or adjust the access filter."
);
}
}
private
BoundField
createBoundField
(
final
Gson
context
,
final
Field
field
,
final
String
name
,
final
TypeToken
<?>
fieldType
,
boolean
serialize
,
boolean
deserialize
,
final
boolean
blockInaccessible
)
{
final
boolean
isPrimitive
=
Primitives
.
isPrimitive
(
fieldType
.
getRawType
());
// special casing primitives here saves ~5% on Android...
JsonAdapter
annotation
=
field
.
getAnnotation
(
JsonAdapter
.
class
);
TypeAdapter
<?>
mapped
=
null
;
if
(
annotation
!=
null
)
{
mapped
=
jsonAdapterFactory
.
getTypeAdapter
(
constructorConstructor
,
context
,
fieldType
,
annotation
);
}
final
boolean
jsonAdapterPresent
=
mapped
!=
null
;
if
(
mapped
==
null
)
mapped
=
context
.
getAdapter
(
fieldType
);
final
TypeAdapter
<?>
typeAdapter
=
mapped
;
return
new
BoundField
(
name
,
serialize
,
deserialize
)
{
@SuppressWarnings
({
"unchecked"
,
"rawtypes"
})
// the type adapter and field type always agree
@Override
void
write
(
JsonWriter
writer
,
Object
value
)
throws
IOException
,
IllegalAccessException
{
if
(!
serialized
)
return
;
if
(
blockInaccessible
)
{
checkAccessible
(
value
,
field
);
}
Object
fieldValue
=
field
.
get
(
value
);
if
(
fieldValue
==
value
)
{
// avoid direct recursion
return
;
}
writer
.
name
(
name
);
TypeAdapter
t
=
jsonAdapterPresent
?
typeAdapter
:
new
TypeAdapterRuntimeTypeWrapper
(
context
,
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
)
{
if
(
blockInaccessible
)
{
checkAccessible
(
value
,
field
);
}
field
.
set
(
value
,
fieldValue
);
}
}
};
}
private
Map
<
String
,
BoundField
>
getBoundFields
(
Gson
context
,
TypeToken
<?>
type
,
Class
<?>
raw
,
boolean
blockInaccessible
)
{
Map
<
String
,
BoundField
>
result
=
new
LinkedHashMap
<>();
if
(
raw
.
isInterface
())
{
return
result
;
}
Type
declaredType
=
type
.
getType
();
Class
<?>
originalRaw
=
raw
;
while
(
raw
!=
Object
.
class
)
{
Field
[]
fields
=
raw
.
getDeclaredFields
();
// For inherited fields, check if access to their declaring class is allowed
if
(
raw
!=
originalRaw
&&
fields
.
length
>
0
)
{
FilterResult
filterResult
=
ReflectionAccessFilterHelper
.
getFilterResult
(
reflectionFilters
,
raw
);
if
(
filterResult
==
FilterResult
.
BLOCK_ALL
)
{
throw
new
JsonIOException
(
"ReflectionAccessFilter does not permit using reflection for "
+
raw
+
" (supertype of "
+
originalRaw
+
"). Register a TypeAdapter for this type "
+
"or adjust the access filter."
);
}
blockInaccessible
=
filterResult
==
FilterResult
.
BLOCK_INACCESSIBLE
;
}
for
(
Field
field
:
fields
)
{
boolean
serialize
=
excludeField
(
field
,
true
);
boolean
deserialize
=
excludeField
(
field
,
false
);
if
(!
serialize
&&
!
deserialize
)
{
continue
;
}
// If blockInaccessible, skip and perform access check later
if
(!
blockInaccessible
)
{
ReflectionHelper
.
makeAccessible
(
field
);
}
Type
fieldType
=
$Gson$Types
.
resolve
(
type
.
getType
(),
raw
,
field
.
getGenericType
());
List
<
String
>
fieldNames
=
getFieldNames
(
field
);
BoundField
previous
=
null
;
for
(
int
i
=
0
,
size
=
fieldNames
.
size
();
i
<
size
;
++
i
)
{
String
name
=
fieldNames
.
get
(
i
);
if
(
i
!=
0
)
serialize
=
false
;
// only serialize the default name
BoundField
boundField
=
createBoundField
(
context
,
field
,
name
,
TypeToken
.
get
(
fieldType
),
serialize
,
deserialize
,
blockInaccessible
);
BoundField
replaced
=
result
.
put
(
name
,
boundField
);
if
(
previous
==
null
)
previous
=
replaced
;
}
if
(
previous
!=
null
)
{
throw
new
IllegalArgumentException
(
declaredType
+
" declares multiple JSON fields named "
+
previous
.
name
);
}
}
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
;
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
ReflectionHelper
.
createExceptionForUnexpectedIllegalAccess
(
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
())
{
boundField
.
write
(
out
,
value
);
}
}
catch
(
IllegalAccessException
e
)
{
throw
ReflectionHelper
.
createExceptionForUnexpectedIllegalAccess
(
e
);
}
out
.
endObject
();
}
}
}
sobot_gson/src/main/java/com/sobot/gson/internal/bind/TreeTypeAdapter.java
0 → 100644
View file @
3b40792f
/*
* 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
.
sobot
.
gson
.
internal
.
bind
;
import
com.sobot.gson.Gson
;
import
com.sobot.gson.JsonDeserializationContext
;
import
com.sobot.gson.JsonDeserializer
;
import
com.sobot.gson.JsonElement
;
import
com.sobot.gson.JsonParseException
;
import
com.sobot.gson.JsonSerializationContext
;
import
com.sobot.gson.JsonSerializer
;
import
com.sobot.gson.TypeAdapter
;
import
com.sobot.gson.TypeAdapterFactory
;
import
com.sobot.gson.internal.
$Gson$Preconditions
;
import
com.sobot.gson.internal.Streams
;
import
com.sobot.gson.reflect.TypeToken
;
import
com.sobot.gson.stream.JsonReader
;
import
com.sobot.gson.stream.JsonWriter
;
import
java.io.IOException
;
import
java.lang.reflect.Type
;
/**
* 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.
*/
public
final
class
TreeTypeAdapter
<
T
>
extends
TypeAdapter
<
T
>
{
private
final
JsonSerializer
<
T
>
serializer
;
private
final
JsonDeserializer
<
T
>
deserializer
;
final
Gson
gson
;
private
final
TypeToken
<
T
>
typeToken
;
private
final
TypeAdapterFactory
skipPast
;
private
final
GsonContextImpl
context
=
new
GsonContextImpl
();
/** The delegate is lazily created because it may not be needed, and creating it may fail. */
private
volatile
TypeAdapter
<
T
>
delegate
;
public
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
(),
context
);
}
@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
(),
context
);
Streams
.
write
(
tree
,
out
);
}
private
TypeAdapter
<
T
>
delegate
()
{
// A race might lead to `delegate` being assigned by multiple threads but the last assignment will stick
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
final
class
SingleTypeFactory
implements
TypeAdapterFactory
{
private
final
TypeToken
<?>
exactType
;
private
final
boolean
matchRawType
;
private
final
Class
<?>
hierarchyType
;
private
final
JsonSerializer
<?>
serializer
;
private
final
JsonDeserializer
<?>
deserializer
;
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
;
}
@SuppressWarnings
(
"unchecked"
)
// guarded by typeToken.equals() call
@Override
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
<>((
JsonSerializer
<
T
>)
serializer
,
(
JsonDeserializer
<
T
>)
deserializer
,
gson
,
type
,
this
)
:
null
;
}
}
private
final
class
GsonContextImpl
implements
JsonSerializationContext
,
JsonDeserializationContext
{
@Override
public
JsonElement
serialize
(
Object
src
)
{
return
gson
.
toJsonTree
(
src
);
}
@Override
public
JsonElement
serialize
(
Object
src
,
Type
typeOfSrc
)
{
return
gson
.
toJsonTree
(
src
,
typeOfSrc
);
}
@SuppressWarnings
(
"unchecked"
)
@Override
public
<
R
>
R
deserialize
(
JsonElement
json
,
Type
typeOfT
)
throws
JsonParseException
{
return
(
R
)
gson
.
fromJson
(
json
,
typeOfT
);
}
};
}
sobot_gson/src/main/java/com/sobot/gson/internal/bind/TypeAdapterRuntimeTypeWrapper.java
0 → 100644
View file @
3b40792f
/*
* 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
.
sobot
.
gson
.
internal
.
bind
;
import
java.io.IOException
;
import
java.lang.reflect.Type
;
import
java.lang.reflect.TypeVariable
;
import
com.sobot.gson.Gson
;
import
com.sobot.gson.TypeAdapter
;
import
com.sobot.gson.reflect.TypeToken
;
import
com.sobot.gson.stream.JsonReader
;
import
com.sobot.gson.stream.JsonWriter
;
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
;
}
}
sobot_gson/src/main/java/com/sobot/gson/internal/bind/TypeAdapters.java
0 → 100644
View file @
3b40792f
/*
* 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
.
sobot
.
gson
.
internal
.
bind
;
import
java.io.IOException
;
import
java.lang.reflect.AccessibleObject
;
import
java.lang.reflect.Field
;
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.security.AccessController
;
import
java.security.PrivilegedAction
;
import
java.util.ArrayList
;
import
java.util.BitSet
;
import
java.util.Calendar
;
import
java.util.Currency
;
import
java.util.GregorianCalendar
;
import
java.util.HashMap
;
import
java.util.List
;
import
java.util.Locale
;
import
java.util.Map
;
import
java.util.StringTokenizer
;
import
java.util.UUID
;
import
java.util.concurrent.atomic.AtomicBoolean
;
import
java.util.concurrent.atomic.AtomicInteger
;
import
java.util.concurrent.atomic.AtomicIntegerArray
;
import
com.sobot.gson.Gson
;
import
com.sobot.gson.JsonArray
;
import
com.sobot.gson.JsonElement
;
import
com.sobot.gson.JsonIOException
;
import
com.sobot.gson.JsonNull
;
import
com.sobot.gson.JsonObject
;
import
com.sobot.gson.JsonPrimitive
;
import
com.sobot.gson.JsonSyntaxException
;
import
com.sobot.gson.TypeAdapter
;
import
com.sobot.gson.TypeAdapterFactory
;
import
com.sobot.gson.annotations.SerializedName
;
import
com.sobot.gson.internal.LazilyParsedNumber
;
import
com.sobot.gson.internal.bind.JsonTreeReader
;
import
com.sobot.gson.reflect.TypeToken
;
import
com.sobot.gson.stream.JsonReader
;
import
com.sobot.gson.stream.JsonToken
;
import
com.sobot.gson.stream.JsonWriter
;
/**
* Type adapters for basic types.
*/
public
final
class
TypeAdapters
{
private
TypeAdapters
()
{
throw
new
UnsupportedOperationException
();
}
@SuppressWarnings
(
"rawtypes"
)
public
static
final
TypeAdapter
<
Class
>
CLASS
=
new
TypeAdapter
<
Class
>()
{
@Override
public
void
write
(
JsonWriter
out
,
Class
value
)
throws
IOException
{
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
{
throw
new
UnsupportedOperationException
(
"Attempted to deserialize a java.lang.Class. Forgot to register a type adapter?"
);
}
}.
nullSafe
();
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
{
BitSet
bitset
=
new
BitSet
();
in
.
beginArray
();
int
i
=
0
;
JsonToken
tokenType
=
in
.
peek
();
while
(
tokenType
!=
JsonToken
.
END_ARRAY
)
{
boolean
set
;
switch
(
tokenType
)
{
case
NUMBER:
case
STRING:
int
intValue
=
in
.
nextInt
();
if
(
intValue
==
0
)
{
set
=
false
;
}
else
if
(
intValue
==
1
)
{
set
=
true
;
}
else
{
throw
new
JsonSyntaxException
(
"Invalid bitset value "
+
intValue
+
", expected 0 or 1; at path "
+
in
.
getPreviousPath
());
}
break
;
case
BOOLEAN:
set
=
in
.
nextBoolean
();
break
;
default
:
throw
new
JsonSyntaxException
(
"Invalid bitset value type: "
+
tokenType
+
"; at path "
+
in
.
getPath
());
}
if
(
set
)
{
bitset
.
set
(
i
);
}
++
i
;
tokenType
=
in
.
peek
();
}
in
.
endArray
();
return
bitset
;
}
@Override
public
void
write
(
JsonWriter
out
,
BitSet
src
)
throws
IOException
{
out
.
beginArray
();
for
(
int
i
=
0
,
length
=
src
.
length
();
i
<
length
;
i
++)
{
int
value
=
(
src
.
get
(
i
))
?
1
:
0
;
out
.
value
(
value
);
}
out
.
endArray
();
}
}.
nullSafe
();
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
{
JsonToken
peek
=
in
.
peek
();
if
(
peek
==
JsonToken
.
NULL
)
{
in
.
nextNull
();
return
null
;
}
else
if
(
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
{
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
;
}
int
intValue
;
try
{
intValue
=
in
.
nextInt
();
}
catch
(
NumberFormatException
e
)
{
throw
new
JsonSyntaxException
(
e
);
}
// Allow up to 255 to support unsigned values
if
(
intValue
>
255
||
intValue
<
Byte
.
MIN_VALUE
)
{
throw
new
JsonSyntaxException
(
"Lossy conversion from "
+
intValue
+
" to byte; at path "
+
in
.
getPreviousPath
());
}
return
(
byte
)
intValue
;
}
@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
;
}
int
intValue
;
try
{
intValue
=
in
.
nextInt
();
}
catch
(
NumberFormatException
e
)
{
throw
new
JsonSyntaxException
(
e
);
}
// Allow up to 65535 to support unsigned values
if
(
intValue
>
65535
||
intValue
<
Short
.
MIN_VALUE
)
{
throw
new
JsonSyntaxException
(
"Lossy conversion from "
+
intValue
+
" to short; at path "
+
in
.
getPreviousPath
());
}
return
(
short
)
intValue
;
}
@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
<
AtomicInteger
>
ATOMIC_INTEGER
=
new
TypeAdapter
<
AtomicInteger
>()
{
@Override
public
AtomicInteger
read
(
JsonReader
in
)
throws
IOException
{
try
{
return
new
AtomicInteger
(
in
.
nextInt
());
}
catch
(
NumberFormatException
e
)
{
throw
new
JsonSyntaxException
(
e
);
}
}
@Override
public
void
write
(
JsonWriter
out
,
AtomicInteger
value
)
throws
IOException
{
out
.
value
(
value
.
get
());
}
}.
nullSafe
();
public
static
final
TypeAdapterFactory
ATOMIC_INTEGER_FACTORY
=
newFactory
(
AtomicInteger
.
class
,
TypeAdapters
.
ATOMIC_INTEGER
);
public
static
final
TypeAdapter
<
AtomicBoolean
>
ATOMIC_BOOLEAN
=
new
TypeAdapter
<
AtomicBoolean
>()
{
@Override
public
AtomicBoolean
read
(
JsonReader
in
)
throws
IOException
{
return
new
AtomicBoolean
(
in
.
nextBoolean
());
}
@Override
public
void
write
(
JsonWriter
out
,
AtomicBoolean
value
)
throws
IOException
{
out
.
value
(
value
.
get
());
}
}.
nullSafe
();
public
static
final
TypeAdapterFactory
ATOMIC_BOOLEAN_FACTORY
=
newFactory
(
AtomicBoolean
.
class
,
TypeAdapters
.
ATOMIC_BOOLEAN
);
public
static
final
TypeAdapter
<
AtomicIntegerArray
>
ATOMIC_INTEGER_ARRAY
=
new
TypeAdapter
<
AtomicIntegerArray
>()
{
@Override
public
AtomicIntegerArray
read
(
JsonReader
in
)
throws
IOException
{
List
<
Integer
>
list
=
new
ArrayList
<>();
in
.
beginArray
();
while
(
in
.
hasNext
())
{
try
{
int
integer
=
in
.
nextInt
();
list
.
add
(
integer
);
}
catch
(
NumberFormatException
e
)
{
throw
new
JsonSyntaxException
(
e
);
}
}
in
.
endArray
();
int
length
=
list
.
size
();
AtomicIntegerArray
array
=
new
AtomicIntegerArray
(
length
);
for
(
int
i
=
0
;
i
<
length
;
++
i
)
{
array
.
set
(
i
,
list
.
get
(
i
));
}
return
array
;
}
@Override
public
void
write
(
JsonWriter
out
,
AtomicIntegerArray
value
)
throws
IOException
{
out
.
beginArray
();
for
(
int
i
=
0
,
length
=
value
.
length
();
i
<
length
;
i
++)
{
out
.
value
(
value
.
get
(
i
));
}
out
.
endArray
();
}
}.
nullSafe
();
public
static
final
TypeAdapterFactory
ATOMIC_INTEGER_ARRAY_FACTORY
=
newFactory
(
AtomicIntegerArray
.
class
,
TypeAdapters
.
ATOMIC_INTEGER_ARRAY
);
public
static
final
TypeAdapter
<
Number
>
LONG
=
new
TypeAdapter
<
Number
>()
{
@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
<
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
+
"; at "
+
in
.
getPreviousPath
());
}
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
;
}
String
s
=
in
.
nextString
();
try
{
return
new
BigDecimal
(
s
);
}
catch
(
NumberFormatException
e
)
{
throw
new
JsonSyntaxException
(
"Failed parsing '"
+
s
+
"' as BigDecimal; at path "
+
in
.
getPreviousPath
(),
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
;
}
String
s
=
in
.
nextString
();
try
{
return
new
BigInteger
(
s
);
}
catch
(
NumberFormatException
e
)
{
throw
new
JsonSyntaxException
(
"Failed parsing '"
+
s
+
"' as BigInteger; at path "
+
in
.
getPreviousPath
(),
e
);
}
}
@Override
public
void
write
(
JsonWriter
out
,
BigInteger
value
)
throws
IOException
{
out
.
value
(
value
);
}
};
public
static
final
TypeAdapter
<
LazilyParsedNumber
>
LAZILY_PARSED_NUMBER
=
new
TypeAdapter
<
LazilyParsedNumber
>()
{
// Normally users should not be able to access and deserialize LazilyParsedNumber because
// it is an internal type, but implement this nonetheless in case there are legit corner
// cases where this is possible
@Override
public
LazilyParsedNumber
read
(
JsonReader
in
)
throws
IOException
{
if
(
in
.
peek
()
==
JsonToken
.
NULL
)
{
in
.
nextNull
();
return
null
;
}
return
new
LazilyParsedNumber
(
in
.
nextString
());
}
@Override
public
void
write
(
JsonWriter
out
,
LazilyParsedNumber
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
;
}
String
s
=
in
.
nextString
();
try
{
return
java
.
util
.
UUID
.
fromString
(
s
);
}
catch
(
IllegalArgumentException
e
)
{
throw
new
JsonSyntaxException
(
"Failed parsing '"
+
s
+
"' as UUID; at path "
+
in
.
getPreviousPath
(),
e
);
}
}
@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
TypeAdapter
<
Currency
>
CURRENCY
=
new
TypeAdapter
<
Currency
>()
{
@Override
public
Currency
read
(
JsonReader
in
)
throws
IOException
{
String
s
=
in
.
nextString
();
try
{
return
Currency
.
getInstance
(
s
);
}
catch
(
IllegalArgumentException
e
)
{
throw
new
JsonSyntaxException
(
"Failed parsing '"
+
s
+
"' as Currency; at path "
+
in
.
getPreviousPath
(),
e
);
}
}
@Override
public
void
write
(
JsonWriter
out
,
Currency
value
)
throws
IOException
{
out
.
value
(
value
.
getCurrencyCode
());
}
}.
nullSafe
();
public
static
final
TypeAdapterFactory
CURRENCY_FACTORY
=
newFactory
(
Currency
.
class
,
CURRENCY
);
public
static
final
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
{
if
(
in
instanceof
JsonTreeReader
)
{
return
((
JsonTreeReader
)
in
).
nextJsonElement
();
}
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
<>();
private
final
Map
<
String
,
T
>
stringToConstant
=
new
HashMap
<>();
private
final
Map
<
T
,
String
>
constantToName
=
new
HashMap
<>();
public
EnumTypeAdapter
(
final
Class
<
T
>
classOfT
)
{
try
{
// Uses reflection to find enum constants to work around name mismatches for obfuscated classes
// Reflection access might throw SecurityException, therefore run this in privileged context;
// should be acceptable because this only retrieves enum constants, but does not expose anything else
Field
[]
constantFields
=
AccessController
.
doPrivileged
(
new
PrivilegedAction
<
Field
[]>()
{
@Override
public
Field
[]
run
()
{
Field
[]
fields
=
classOfT
.
getDeclaredFields
();
ArrayList
<
Field
>
constantFieldsList
=
new
ArrayList
<>(
fields
.
length
);
for
(
Field
f
:
fields
)
{
if
(
f
.
isEnumConstant
())
{
constantFieldsList
.
add
(
f
);
}
}
Field
[]
constantFields
=
constantFieldsList
.
toArray
(
new
Field
[
0
]);
AccessibleObject
.
setAccessible
(
constantFields
,
true
);
return
constantFields
;
}
});
for
(
Field
constantField
:
constantFields
)
{
@SuppressWarnings
(
"unchecked"
)
T
constant
=
(
T
)(
constantField
.
get
(
null
));
String
name
=
constant
.
name
();
String
toStringVal
=
constant
.
toString
();
SerializedName
annotation
=
constantField
.
getAnnotation
(
SerializedName
.
class
);
if
(
annotation
!=
null
)
{
name
=
annotation
.
value
();
for
(
String
alternate
:
annotation
.
alternate
())
{
nameToConstant
.
put
(
alternate
,
constant
);
}
}
nameToConstant
.
put
(
name
,
constant
);
stringToConstant
.
put
(
toStringVal
,
constant
);
constantToName
.
put
(
constant
,
name
);
}
}
catch
(
IllegalAccessException
e
)
{
throw
new
AssertionError
(
e
);
}
}
@Override
public
T
read
(
JsonReader
in
)
throws
IOException
{
if
(
in
.
peek
()
==
JsonToken
.
NULL
)
{
in
.
nextNull
();
return
null
;
}
String
key
=
in
.
nextString
();
T
constant
=
nameToConstant
.
get
(
key
);
return
(
constant
==
null
)
?
stringToConstant
.
get
(
key
)
:
constant
;
}
@Override
public
void
write
(
JsonWriter
out
,
T
value
)
throws
IOException
{
out
.
value
(
value
==
null
?
null
:
constantToName
.
get
(
value
));
}
}
public
static
final
TypeAdapterFactory
ENUM_FACTORY
=
new
TypeAdapterFactory
()
{
@SuppressWarnings
({
"rawtypes"
,
"unchecked"
})
@Override
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
(
TypeAdapter
<
T
>)
new
EnumTypeAdapter
(
rawType
);
}
};
public
static
<
TT
>
TypeAdapterFactory
newFactory
(
final
TypeToken
<
TT
>
type
,
final
TypeAdapter
<
TT
>
typeAdapter
)
{
return
new
TypeAdapterFactory
()
{
@SuppressWarnings
(
"unchecked"
)
// we use a runtime check to make sure the 'T's equal
@Override
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
()
{
@SuppressWarnings
(
"unchecked"
)
// we use a runtime check to make sure the 'T's equal
@Override
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
()
{
@SuppressWarnings
(
"unchecked"
)
// we use a runtime check to make sure the 'T's equal
@Override
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
()
{
@SuppressWarnings
(
"unchecked"
)
// we use a runtime check to make sure the 'T's equal
@Override
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
+
"]"
;
}
};
}
/**
* Returns a factory for all subtypes of {@code typeAdapter}. We do a runtime check to confirm
* that the deserialized type matches the type requested.
*/
public
static
<
T1
>
TypeAdapterFactory
newTypeHierarchyFactory
(
final
Class
<
T1
>
clazz
,
final
TypeAdapter
<
T1
>
typeAdapter
)
{
return
new
TypeAdapterFactory
()
{
@SuppressWarnings
(
"unchecked"
)
@Override
public
<
T2
>
TypeAdapter
<
T2
>
create
(
Gson
gson
,
TypeToken
<
T2
>
typeToken
)
{
final
Class
<?
super
T2
>
requestedType
=
typeToken
.
getRawType
();
if
(!
clazz
.
isAssignableFrom
(
requestedType
))
{
return
null
;
}
return
(
TypeAdapter
<
T2
>)
new
TypeAdapter
<
T1
>()
{
@Override
public
void
write
(
JsonWriter
out
,
T1
value
)
throws
IOException
{
typeAdapter
.
write
(
out
,
value
);
}
@Override
public
T1
read
(
JsonReader
in
)
throws
IOException
{
T1
result
=
typeAdapter
.
read
(
in
);
if
(
result
!=
null
&&
!
requestedType
.
isInstance
(
result
))
{
throw
new
JsonSyntaxException
(
"Expected a "
+
requestedType
.
getName
()
+
" but was "
+
result
.
getClass
().
getName
()
+
"; at path "
+
in
.
getPreviousPath
());
}
return
result
;
}
};
}
@Override
public
String
toString
()
{
return
"Factory[typeHierarchy="
+
clazz
.
getName
()
+
",adapter="
+
typeAdapter
+
"]"
;
}
};
}
}
sobot_gson/src/main/java/com/sobot/gson/internal/bind/util/ISO8601Utils.java
0 → 100644
View file @
3b40792f
package
com
.
sobot
.
gson
.
internal
.
bind
.
util
;
import
java.text.ParseException
;
import
java.text.ParsePosition
;
import
java.util.*
;
/**
* Utilities methods for manipulating dates in iso8601 format. This is much much faster and GC friendly than using SimpleDateFormat so
* highly suitable if you (un)serialize lots of date objects.
*
* Supported parse format: [yyyy-MM-dd|yyyyMMdd][T(hh:mm[:ss[.sss]]|hhmm[ss[.sss]])]?[Z|[+-]hh[:]mm]]
*
* @see <a href="http://www.w3.org/TR/NOTE-datetime">this specification</a>
*/
//Date parsing code from Jackson databind ISO8601Utils.java
// https://github.com/FasterXML/jackson-databind/blob/master/src/main/java/com/fasterxml/jackson/databind/util/ISO8601Utils.java
public
class
ISO8601Utils
{
/**
* ID to represent the 'UTC' string, default timezone since Jackson 2.7
*
* @since 2.7
*/
private
static
final
String
UTC_ID
=
"UTC"
;
/**
* The UTC timezone, prefetched to avoid more lookups.
*
* @since 2.7
*/
private
static
final
TimeZone
TIMEZONE_UTC
=
TimeZone
.
getTimeZone
(
UTC_ID
);
/*
/**********************************************************
/* Formatting
/**********************************************************
*/
/**
* Format a date into 'yyyy-MM-ddThh:mm:ssZ' (default timezone, no milliseconds precision)
*
* @param date the date to format
* @return the date formatted as 'yyyy-MM-ddThh:mm:ssZ'
*/
public
static
String
format
(
Date
date
)
{
return
format
(
date
,
false
,
TIMEZONE_UTC
);
}
/**
* Format a date into 'yyyy-MM-ddThh:mm:ss[.sss]Z' (GMT timezone)
*
* @param date the date to format
* @param millis true to include millis precision otherwise false
* @return the date formatted as 'yyyy-MM-ddThh:mm:ss[.sss]Z'
*/
public
static
String
format
(
Date
date
,
boolean
millis
)
{
return
format
(
date
,
millis
,
TIMEZONE_UTC
);
}
/**
* Format date into yyyy-MM-ddThh:mm:ss[.sss][Z|[+-]hh:mm]
*
* @param date the date to format
* @param millis true to include millis precision otherwise false
* @param tz timezone to use for the formatting (UTC will produce 'Z')
* @return the date formatted as yyyy-MM-ddThh:mm:ss[.sss][Z|[+-]hh:mm]
*/
public
static
String
format
(
Date
date
,
boolean
millis
,
TimeZone
tz
)
{
Calendar
calendar
=
new
GregorianCalendar
(
tz
,
Locale
.
US
);
calendar
.
setTime
(
date
);
// estimate capacity of buffer as close as we can (yeah, that's pedantic ;)
int
capacity
=
"yyyy-MM-ddThh:mm:ss"
.
length
();
capacity
+=
millis
?
".sss"
.
length
()
:
0
;
capacity
+=
tz
.
getRawOffset
()
==
0
?
"Z"
.
length
()
:
"+hh:mm"
.
length
();
StringBuilder
formatted
=
new
StringBuilder
(
capacity
);
padInt
(
formatted
,
calendar
.
get
(
Calendar
.
YEAR
),
"yyyy"
.
length
());
formatted
.
append
(
'-'
);
padInt
(
formatted
,
calendar
.
get
(
Calendar
.
MONTH
)
+
1
,
"MM"
.
length
());
formatted
.
append
(
'-'
);
padInt
(
formatted
,
calendar
.
get
(
Calendar
.
DAY_OF_MONTH
),
"dd"
.
length
());
formatted
.
append
(
'T'
);
padInt
(
formatted
,
calendar
.
get
(
Calendar
.
HOUR_OF_DAY
),
"hh"
.
length
());
formatted
.
append
(
':'
);
padInt
(
formatted
,
calendar
.
get
(
Calendar
.
MINUTE
),
"mm"
.
length
());
formatted
.
append
(
':'
);
padInt
(
formatted
,
calendar
.
get
(
Calendar
.
SECOND
),
"ss"
.
length
());
if
(
millis
)
{
formatted
.
append
(
'.'
);
padInt
(
formatted
,
calendar
.
get
(
Calendar
.
MILLISECOND
),
"sss"
.
length
());
}
int
offset
=
tz
.
getOffset
(
calendar
.
getTimeInMillis
());
if
(
offset
!=
0
)
{
int
hours
=
Math
.
abs
((
offset
/
(
60
*
1000
))
/
60
);
int
minutes
=
Math
.
abs
((
offset
/
(
60
*
1000
))
%
60
);
formatted
.
append
(
offset
<
0
?
'-'
:
'+'
);
padInt
(
formatted
,
hours
,
"hh"
.
length
());
formatted
.
append
(
':'
);
padInt
(
formatted
,
minutes
,
"mm"
.
length
());
}
else
{
formatted
.
append
(
'Z'
);
}
return
formatted
.
toString
();
}
/*
/**********************************************************
/* Parsing
/**********************************************************
*/
/**
* Parse a date from ISO-8601 formatted string. It expects a format
* [yyyy-MM-dd|yyyyMMdd][T(hh:mm[:ss[.sss]]|hhmm[ss[.sss]])]?[Z|[+-]hh[:mm]]]
*
* @param date ISO string to parse in the appropriate format.
* @param pos The position to start parsing from, updated to where parsing stopped.
* @return the parsed date
* @throws ParseException if the date is not in the appropriate format
*/
public
static
Date
parse
(
String
date
,
ParsePosition
pos
)
throws
ParseException
{
Exception
fail
=
null
;
try
{
int
offset
=
pos
.
getIndex
();
// extract year
int
year
=
parseInt
(
date
,
offset
,
offset
+=
4
);
if
(
checkOffset
(
date
,
offset
,
'-'
))
{
offset
+=
1
;
}
// extract month
int
month
=
parseInt
(
date
,
offset
,
offset
+=
2
);
if
(
checkOffset
(
date
,
offset
,
'-'
))
{
offset
+=
1
;
}
// extract day
int
day
=
parseInt
(
date
,
offset
,
offset
+=
2
);
// default time value
int
hour
=
0
;
int
minutes
=
0
;
int
seconds
=
0
;
int
milliseconds
=
0
;
// always use 0 otherwise returned date will include millis of current time
// if the value has no time component (and no time zone), we are done
boolean
hasT
=
checkOffset
(
date
,
offset
,
'T'
);
if
(!
hasT
&&
(
date
.
length
()
<=
offset
))
{
Calendar
calendar
=
new
GregorianCalendar
(
year
,
month
-
1
,
day
);
pos
.
setIndex
(
offset
);
return
calendar
.
getTime
();
}
if
(
hasT
)
{
// extract hours, minutes, seconds and milliseconds
hour
=
parseInt
(
date
,
offset
+=
1
,
offset
+=
2
);
if
(
checkOffset
(
date
,
offset
,
':'
))
{
offset
+=
1
;
}
minutes
=
parseInt
(
date
,
offset
,
offset
+=
2
);
if
(
checkOffset
(
date
,
offset
,
':'
))
{
offset
+=
1
;
}
// second and milliseconds can be optional
if
(
date
.
length
()
>
offset
)
{
char
c
=
date
.
charAt
(
offset
);
if
(
c
!=
'Z'
&&
c
!=
'+'
&&
c
!=
'-'
)
{
seconds
=
parseInt
(
date
,
offset
,
offset
+=
2
);
if
(
seconds
>
59
&&
seconds
<
63
)
seconds
=
59
;
// truncate up to 3 leap seconds
// milliseconds can be optional in the format
if
(
checkOffset
(
date
,
offset
,
'.'
))
{
offset
+=
1
;
int
endOffset
=
indexOfNonDigit
(
date
,
offset
+
1
);
// assume at least one digit
int
parseEndOffset
=
Math
.
min
(
endOffset
,
offset
+
3
);
// parse up to 3 digits
int
fraction
=
parseInt
(
date
,
offset
,
parseEndOffset
);
// compensate for "missing" digits
switch
(
parseEndOffset
-
offset
)
{
// number of digits parsed
case
2
:
milliseconds
=
fraction
*
10
;
break
;
case
1
:
milliseconds
=
fraction
*
100
;
break
;
default
:
milliseconds
=
fraction
;
}
offset
=
endOffset
;
}
}
}
}
// extract timezone
if
(
date
.
length
()
<=
offset
)
{
throw
new
IllegalArgumentException
(
"No time zone indicator"
);
}
TimeZone
timezone
=
null
;
char
timezoneIndicator
=
date
.
charAt
(
offset
);
if
(
timezoneIndicator
==
'Z'
)
{
timezone
=
TIMEZONE_UTC
;
offset
+=
1
;
}
else
if
(
timezoneIndicator
==
'+'
||
timezoneIndicator
==
'-'
)
{
String
timezoneOffset
=
date
.
substring
(
offset
);
// When timezone has no minutes, we should append it, valid timezones are, for example: +00:00, +0000 and +00
timezoneOffset
=
timezoneOffset
.
length
()
>=
5
?
timezoneOffset
:
timezoneOffset
+
"00"
;
offset
+=
timezoneOffset
.
length
();
// 18-Jun-2015, tatu: Minor simplification, skip offset of "+0000"/"+00:00"
if
(
"+0000"
.
equals
(
timezoneOffset
)
||
"+00:00"
.
equals
(
timezoneOffset
))
{
timezone
=
TIMEZONE_UTC
;
}
else
{
// 18-Jun-2015, tatu: Looks like offsets only work from GMT, not UTC...
// not sure why, but that's the way it looks. Further, Javadocs for
// `java.util.TimeZone` specifically instruct use of GMT as base for
// custom timezones... odd.
String
timezoneId
=
"GMT"
+
timezoneOffset
;
// String timezoneId = "UTC" + timezoneOffset;
timezone
=
TimeZone
.
getTimeZone
(
timezoneId
);
String
act
=
timezone
.
getID
();
if
(!
act
.
equals
(
timezoneId
))
{
/* 22-Jan-2015, tatu: Looks like canonical version has colons, but we may be given
* one without. If so, don't sweat.
* Yes, very inefficient. Hopefully not hit often.
* If it becomes a perf problem, add 'loose' comparison instead.
*/
String
cleaned
=
act
.
replace
(
":"
,
""
);
if
(!
cleaned
.
equals
(
timezoneId
))
{
throw
new
IndexOutOfBoundsException
(
"Mismatching time zone indicator: "
+
timezoneId
+
" given, resolves to "
+
timezone
.
getID
());
}
}
}
}
else
{
throw
new
IndexOutOfBoundsException
(
"Invalid time zone indicator '"
+
timezoneIndicator
+
"'"
);
}
Calendar
calendar
=
new
GregorianCalendar
(
timezone
);
calendar
.
setLenient
(
false
);
calendar
.
set
(
Calendar
.
YEAR
,
year
);
calendar
.
set
(
Calendar
.
MONTH
,
month
-
1
);
calendar
.
set
(
Calendar
.
DAY_OF_MONTH
,
day
);
calendar
.
set
(
Calendar
.
HOUR_OF_DAY
,
hour
);
calendar
.
set
(
Calendar
.
MINUTE
,
minutes
);
calendar
.
set
(
Calendar
.
SECOND
,
seconds
);
calendar
.
set
(
Calendar
.
MILLISECOND
,
milliseconds
);
pos
.
setIndex
(
offset
);
return
calendar
.
getTime
();
// If we get a ParseException it'll already have the right message/offset.
// Other exception types can convert here.
}
catch
(
IndexOutOfBoundsException
e
)
{
fail
=
e
;
}
catch
(
NumberFormatException
e
)
{
fail
=
e
;
}
catch
(
IllegalArgumentException
e
)
{
fail
=
e
;
}
String
input
=
(
date
==
null
)
?
null
:
(
'"'
+
date
+
'"'
);
String
msg
=
fail
.
getMessage
();
if
(
msg
==
null
||
msg
.
isEmpty
())
{
msg
=
"("
+
fail
.
getClass
().
getName
()+
")"
;
}
ParseException
ex
=
new
ParseException
(
"Failed to parse date ["
+
input
+
"]: "
+
msg
,
pos
.
getIndex
());
ex
.
initCause
(
fail
);
throw
ex
;
}
/**
* Check if the expected character exist at the given offset in the value.
*
* @param value the string to check at the specified offset
* @param offset the offset to look for the expected character
* @param expected the expected character
* @return true if the expected character exist at the given offset
*/
private
static
boolean
checkOffset
(
String
value
,
int
offset
,
char
expected
)
{
return
(
offset
<
value
.
length
())
&&
(
value
.
charAt
(
offset
)
==
expected
);
}
/**
* Parse an integer located between 2 given offsets in a string
*
* @param value the string to parse
* @param beginIndex the start index for the integer in the string
* @param endIndex the end index for the integer in the string
* @return the int
* @throws NumberFormatException if the value is not a number
*/
private
static
int
parseInt
(
String
value
,
int
beginIndex
,
int
endIndex
)
throws
NumberFormatException
{
if
(
beginIndex
<
0
||
endIndex
>
value
.
length
()
||
beginIndex
>
endIndex
)
{
throw
new
NumberFormatException
(
value
);
}
// use same logic as in Integer.parseInt() but less generic we're not supporting negative values
int
i
=
beginIndex
;
int
result
=
0
;
int
digit
;
if
(
i
<
endIndex
)
{
digit
=
Character
.
digit
(
value
.
charAt
(
i
++),
10
);
if
(
digit
<
0
)
{
throw
new
NumberFormatException
(
"Invalid number: "
+
value
.
substring
(
beginIndex
,
endIndex
));
}
result
=
-
digit
;
}
while
(
i
<
endIndex
)
{
digit
=
Character
.
digit
(
value
.
charAt
(
i
++),
10
);
if
(
digit
<
0
)
{
throw
new
NumberFormatException
(
"Invalid number: "
+
value
.
substring
(
beginIndex
,
endIndex
));
}
result
*=
10
;
result
-=
digit
;
}
return
-
result
;
}
/**
* Zero pad a number to a specified length
*
* @param buffer buffer to use for padding
* @param value the integer value to pad if necessary.
* @param length the length of the string we should zero pad
*/
private
static
void
padInt
(
StringBuilder
buffer
,
int
value
,
int
length
)
{
String
strValue
=
Integer
.
toString
(
value
);
for
(
int
i
=
length
-
strValue
.
length
();
i
>
0
;
i
--)
{
buffer
.
append
(
'0'
);
}
buffer
.
append
(
strValue
);
}
/**
* Returns the index of the first character in the string that is not a digit, starting at offset.
*/
private
static
int
indexOfNonDigit
(
String
string
,
int
offset
)
{
for
(
int
i
=
offset
;
i
<
string
.
length
();
i
++)
{
char
c
=
string
.
charAt
(
i
);
if
(
c
<
'0'
||
c
>
'9'
)
return
i
;
}
return
string
.
length
();
}
}
sobot_gson/src/main/java/com/sobot/gson/internal/package-info.java
0 → 100644
View file @
3b40792f
/**
* 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
.
sobot
.
gson
.
internal
;
\ No newline at end of file
sobot_gson/src/main/java/com/sobot/gson/internal/reflect/ReflectionHelper.java
0 → 100644
View file @
3b40792f
package
com
.
sobot
.
gson
.
internal
.
reflect
;
import
com.sobot.gson.JsonIOException
;
import
java.lang.reflect.Constructor
;
import
java.lang.reflect.Field
;
public
class
ReflectionHelper
{
private
ReflectionHelper
()
{
}
/**
* Tries making the field accessible, wrapping any thrown exception in a
* {@link JsonIOException} with descriptive message.
*
* @param field field to make accessible
* @throws JsonIOException if making the field accessible fails
*/
public
static
void
makeAccessible
(
Field
field
)
throws
JsonIOException
{
try
{
field
.
setAccessible
(
true
);
}
catch
(
Exception
exception
)
{
throw
new
JsonIOException
(
"Failed making field '"
+
field
.
getDeclaringClass
().
getName
()
+
"#"
+
field
.
getName
()
+
"' accessible; either change its visibility or write a custom "
+
"TypeAdapter for its declaring type"
,
exception
);
}
}
/**
* Creates a string representation for a constructor.
* E.g.: {@code java.lang.String#String(char[], int, int)}
*/
private
static
String
constructorToString
(
Constructor
<?>
constructor
)
{
StringBuilder
stringBuilder
=
new
StringBuilder
(
constructor
.
getDeclaringClass
().
getName
())
.
append
(
'#'
)
.
append
(
constructor
.
getDeclaringClass
().
getSimpleName
())
.
append
(
'('
);
Class
<?>[]
parameters
=
constructor
.
getParameterTypes
();
for
(
int
i
=
0
;
i
<
parameters
.
length
;
i
++)
{
if
(
i
>
0
)
{
stringBuilder
.
append
(
", "
);
}
stringBuilder
.
append
(
parameters
[
i
].
getSimpleName
());
}
return
stringBuilder
.
append
(
')'
).
toString
();
}
/**
* Tries making the constructor accessible, returning an exception message
* if this fails.
*
* @param constructor constructor to make accessible
* @return exception message; {@code null} if successful, non-{@code null} if
* unsuccessful
*/
public
static
String
tryMakeAccessible
(
Constructor
<?>
constructor
)
{
try
{
constructor
.
setAccessible
(
true
);
return
null
;
}
catch
(
Exception
exception
)
{
return
"Failed making constructor '"
+
constructorToString
(
constructor
)
+
"' accessible; "
+
"either change its visibility or write a custom InstanceCreator or TypeAdapter for its declaring type: "
// Include the message since it might contain more detailed information
+
exception
.
getMessage
();
}
}
public
static
RuntimeException
createExceptionForUnexpectedIllegalAccess
(
IllegalAccessException
exception
)
{
throw
new
RuntimeException
(
"Unexpected IllegalAccessException occurred (Gson "
+
"2.9.0"
+
"). "
+
"Certain ReflectionAccessFilter features require Java >= 9 to work correctly. If you are not using "
+
"ReflectionAccessFilter, report this to the Gson maintainers."
,
exception
);
}
}
sobot_gson/src/main/java/com/sobot/gson/internal/sql/SqlDateTypeAdapter.java
0 → 100644
View file @
3b40792f
/*
* 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
.
sobot
.
gson
.
internal
.
sql
;
import
com.sobot.gson.Gson
;
import
com.sobot.gson.JsonSyntaxException
;
import
com.sobot.gson.TypeAdapter
;
import
com.sobot.gson.TypeAdapterFactory
;
import
com.sobot.gson.reflect.TypeToken
;
import
com.sobot.gson.stream.JsonReader
;
import
com.sobot.gson.stream.JsonToken
;
import
com.sobot.gson.stream.JsonWriter
;
import
java.io.IOException
;
import
java.text.DateFormat
;
import
java.text.ParseException
;
import
java.text.SimpleDateFormat
;
import
java.util.Date
;
/**
* 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.
*/
final
class
SqlDateTypeAdapter
extends
TypeAdapter
<
java
.
sql
.
Date
>
{
static
final
TypeAdapterFactory
FACTORY
=
new
TypeAdapterFactory
()
{
@SuppressWarnings
(
"unchecked"
)
// we use a runtime check to make sure the 'T's equal
@Override
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"
);
private
SqlDateTypeAdapter
()
{
}
@Override
public
java
.
sql
.
Date
read
(
JsonReader
in
)
throws
IOException
{
if
(
in
.
peek
()
==
JsonToken
.
NULL
)
{
in
.
nextNull
();
return
null
;
}
String
s
=
in
.
nextString
();
try
{
Date
utilDate
;
synchronized
(
this
)
{
utilDate
=
format
.
parse
(
s
);
}
return
new
java
.
sql
.
Date
(
utilDate
.
getTime
());
}
catch
(
ParseException
e
)
{
throw
new
JsonSyntaxException
(
"Failed parsing '"
+
s
+
"' as SQL Date; at path "
+
in
.
getPreviousPath
(),
e
);
}
}
@Override
public
void
write
(
JsonWriter
out
,
java
.
sql
.
Date
value
)
throws
IOException
{
if
(
value
==
null
)
{
out
.
nullValue
();
return
;
}
String
dateString
;
synchronized
(
this
)
{
dateString
=
format
.
format
(
value
);
}
out
.
value
(
dateString
);
}
}
sobot_gson/src/main/java/com/sobot/gson/internal/sql/SqlTimeTypeAdapter.java
0 → 100644
View file @
3b40792f
/*
* 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
.
sobot
.
gson
.
internal
.
sql
;
import
com.sobot.gson.Gson
;
import
com.sobot.gson.JsonSyntaxException
;
import
com.sobot.gson.TypeAdapter
;
import
com.sobot.gson.TypeAdapterFactory
;
import
com.sobot.gson.reflect.TypeToken
;
import
com.sobot.gson.stream.JsonReader
;
import
com.sobot.gson.stream.JsonToken
;
import
com.sobot.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 java.sql.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.
*/
final
class
SqlTimeTypeAdapter
extends
TypeAdapter
<
Time
>
{
static
final
TypeAdapterFactory
FACTORY
=
new
TypeAdapterFactory
()
{
@SuppressWarnings
(
"unchecked"
)
// we use a runtime check to make sure the 'T's equal
@Override
public
<
T
>
TypeAdapter
<
T
>
create
(
Gson
gson
,
TypeToken
<
T
>
typeToken
)
{
return
typeToken
.
getRawType
()
==
Time
.
class
?
(
TypeAdapter
<
T
>)
new
SqlTimeTypeAdapter
()
:
null
;
}
};
private
final
DateFormat
format
=
new
SimpleDateFormat
(
"hh:mm:ss a"
);
private
SqlTimeTypeAdapter
()
{
}
@Override
public
Time
read
(
JsonReader
in
)
throws
IOException
{
if
(
in
.
peek
()
==
JsonToken
.
NULL
)
{
in
.
nextNull
();
return
null
;
}
String
s
=
in
.
nextString
();
try
{
synchronized
(
this
)
{
Date
date
=
format
.
parse
(
s
);
return
new
Time
(
date
.
getTime
());
}
}
catch
(
ParseException
e
)
{
throw
new
JsonSyntaxException
(
"Failed parsing '"
+
s
+
"' as SQL Time; at path "
+
in
.
getPreviousPath
(),
e
);
}
}
@Override
public
void
write
(
JsonWriter
out
,
Time
value
)
throws
IOException
{
if
(
value
==
null
)
{
out
.
nullValue
();
return
;
}
String
timeString
;
synchronized
(
this
)
{
timeString
=
format
.
format
(
value
);
}
out
.
value
(
timeString
);
}
}
sobot_gson/src/main/java/com/sobot/gson/internal/sql/SqlTimestampTypeAdapter.java
0 → 100644
View file @
3b40792f
package
com
.
sobot
.
gson
.
internal
.
sql
;
import
com.sobot.gson.Gson
;
import
com.sobot.gson.TypeAdapter
;
import
com.sobot.gson.TypeAdapterFactory
;
import
com.sobot.gson.reflect.TypeToken
;
import
com.sobot.gson.stream.JsonReader
;
import
com.sobot.gson.stream.JsonWriter
;
import
java.io.IOException
;
import
java.sql.Timestamp
;
import
java.util.Date
;
class
SqlTimestampTypeAdapter
extends
TypeAdapter
<
Timestamp
>
{
static
final
TypeAdapterFactory
FACTORY
=
new
TypeAdapterFactory
()
{
@SuppressWarnings
(
"unchecked"
)
// we use a runtime check to make sure the 'T's equal
@Override
public
<
T
>
TypeAdapter
<
T
>
create
(
Gson
gson
,
TypeToken
<
T
>
typeToken
)
{
if
(
typeToken
.
getRawType
()
==
Timestamp
.
class
)
{
final
TypeAdapter
<
Date
>
dateTypeAdapter
=
gson
.
getAdapter
(
Date
.
class
);
return
(
TypeAdapter
<
T
>)
new
SqlTimestampTypeAdapter
(
dateTypeAdapter
);
}
else
{
return
null
;
}
}
};
private
final
TypeAdapter
<
Date
>
dateTypeAdapter
;
private
SqlTimestampTypeAdapter
(
TypeAdapter
<
Date
>
dateTypeAdapter
)
{
this
.
dateTypeAdapter
=
dateTypeAdapter
;
}
@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
);
}
}
sobot_gson/src/main/java/com/sobot/gson/internal/sql/SqlTypesSupport.java
0 → 100644
View file @
3b40792f
package
com
.
sobot
.
gson
.
internal
.
sql
;
import
java.sql.Timestamp
;
import
java.util.Date
;
import
com.sobot.gson.TypeAdapterFactory
;
import
com.sobot.gson.internal.bind.DefaultDateTypeAdapter.DateType
;
/**
* Encapsulates access to {@code java.sql} types, to allow Gson to
* work without the {@code java.sql} module being present.
* No {@link ClassNotFoundException}s will be thrown in case
* the {@code java.sql} module is not present.
*
* <p>If {@link #SUPPORTS_SQL_TYPES} is {@code true}, all other
* constants of this class will be non-{@code null}. However, if
* it is {@code false} all other constants will be {@code null} and
* there will be no support for {@code java.sql} types.
*/
public
final
class
SqlTypesSupport
{
/**
* {@code true} if {@code java.sql} types are supported,
* {@code false} otherwise
*/
public
static
final
boolean
SUPPORTS_SQL_TYPES
;
public
static
final
DateType
<?
extends
Date
>
DATE_DATE_TYPE
;
public
static
final
DateType
<?
extends
Date
>
TIMESTAMP_DATE_TYPE
;
public
static
final
TypeAdapterFactory
DATE_FACTORY
;
public
static
final
TypeAdapterFactory
TIME_FACTORY
;
public
static
final
TypeAdapterFactory
TIMESTAMP_FACTORY
;
static
{
boolean
sqlTypesSupport
;
try
{
Class
.
forName
(
"java.sql.Date"
);
sqlTypesSupport
=
true
;
}
catch
(
ClassNotFoundException
classNotFoundException
)
{
sqlTypesSupport
=
false
;
}
SUPPORTS_SQL_TYPES
=
sqlTypesSupport
;
if
(
SUPPORTS_SQL_TYPES
)
{
DATE_DATE_TYPE
=
new
DateType
<
java
.
sql
.
Date
>(
java
.
sql
.
Date
.
class
)
{
@Override
protected
java
.
sql
.
Date
deserialize
(
Date
date
)
{
return
new
java
.
sql
.
Date
(
date
.
getTime
());
}
};
TIMESTAMP_DATE_TYPE
=
new
DateType
<
Timestamp
>(
Timestamp
.
class
)
{
@Override
protected
Timestamp
deserialize
(
Date
date
)
{
return
new
Timestamp
(
date
.
getTime
());
}
};
DATE_FACTORY
=
SqlDateTypeAdapter
.
FACTORY
;
TIME_FACTORY
=
SqlTimeTypeAdapter
.
FACTORY
;
TIMESTAMP_FACTORY
=
SqlTimestampTypeAdapter
.
FACTORY
;
}
else
{
DATE_DATE_TYPE
=
null
;
TIMESTAMP_DATE_TYPE
=
null
;
DATE_FACTORY
=
null
;
TIME_FACTORY
=
null
;
TIMESTAMP_FACTORY
=
null
;
}
}
private
SqlTypesSupport
()
{
}
}
sobot_gson/src/main/java/com/sobot/gson/package-info.java
0 → 100644
View file @
3b40792f
/**
* This package provides the {@link com.sobot.gson.Gson} class to convert Json to Java and
* vice-versa.
*
* <p>The primary class to use is {@link com.sobot.gson.Gson} which can be constructed with
* {@code new Gson()} (using default settings) or by using {@link com.sobot.gson.GsonBuilder}
* (to configure various options such as using versioning and so on).</p>
*
* @author Inderjeet Singh, Joel Leitch
*/
package
com
.
sobot
.
gson
;
\ No newline at end of file
sobot_gson/src/main/java/com/sobot/gson/reflect/TypeToken.java
0 → 100644
View file @
3b40792f
/*
* 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
.
sobot
.
gson
.
reflect
;
import
com.sobot.gson.internal.
$Gson$Types
;
import
com.sobot.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>Capturing a type variable as type argument of a {@code TypeToken} should
* be avoided. Due to type erasure the runtime type of a type variable is not
* available to Gson and therefore it cannot provide the functionality one
* might expect, which gives a false sense of type-safety at compilation time
* and can lead to an unexpected {@code ClassCastException} at runtime.
*
* @author Bob Lee
* @author Sven Mawson
* @author Jesse Wilson
*/
public
class
TypeToken
<
T
>
{
private
final
Class
<?
super
T
>
rawType
;
private
final
Type
type
;
private
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
=
getTypeTokenTypeArgument
();
this
.
rawType
=
(
Class
<?
super
T
>)
$Gson$Types
.
getRawType
(
type
);
this
.
hashCode
=
type
.
hashCode
();
}
/**
* Unsafe. Constructs a type literal manually.
*/
@SuppressWarnings
(
"unchecked"
)
private
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
();
}
/**
* Verifies that {@code this} is an instance of a direct subclass of TypeToken and
* returns the type argument for {@code T} in {@link $Gson$Types#canonicalize
* canonical form}.
*/
private
Type
getTypeTokenTypeArgument
()
{
Type
superclass
=
getClass
().
getGenericSuperclass
();
if
(
superclass
instanceof
ParameterizedType
)
{
ParameterizedType
parameterized
=
(
ParameterizedType
)
superclass
;
if
(
parameterized
.
getRawType
()
==
TypeToken
.
class
)
{
return
$Gson$Types
.
canonicalize
(
parameterized
.
getActualTypeArguments
()[
0
]);
}
}
// Check for raw TypeToken as superclass
else
if
(
superclass
==
TypeToken
.
class
)
{
throw
new
IllegalStateException
(
"TypeToken must be created with a type argument: new TypeToken<...>() {}; "
+
"When using code shrinkers (ProGuard, R8, ...) make sure that generic signatures are preserved."
);
}
// User created subclass of subclass of TypeToken
throw
new
IllegalStateException
(
"Must only create direct subclasses of TypeToken"
);
}
/**
* 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
<>(
typeVarMap
)))
{
return
true
;
}
}
// Interfaces didn't work, try the superclass.
Type
sType
=
clazz
.
getGenericSuperclass
();
return
isAssignableFrom
(
sType
,
to
,
new
HashMap
<>(
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
<>(
type
);
}
/**
* Gets type literal for the given {@code Class} instance.
*/
public
static
<
T
>
TypeToken
<
T
>
get
(
Class
<
T
>
type
)
{
return
new
TypeToken
<>(
type
);
}
/**
* Gets type literal for the parameterized type represented by applying {@code typeArguments} to
* {@code rawType}.
*/
public
static
TypeToken
<?>
getParameterized
(
Type
rawType
,
Type
...
typeArguments
)
{
return
new
TypeToken
<>(
$Gson$Types
.
newParameterizedTypeWithOwner
(
null
,
rawType
,
typeArguments
));
}
/**
* Gets type literal for the array type whose elements are all instances of {@code componentType}.
*/
public
static
TypeToken
<?>
getArray
(
Type
componentType
)
{
return
new
TypeToken
<>(
$Gson$Types
.
arrayOf
(
componentType
));
}
}
sobot_gson/src/main/java/com/sobot/gson/reflect/package-info.java
0 → 100644
View file @
3b40792f
/**
* This package provides utility classes for finding type information for generic types.
*
* @author Inderjeet Singh, Joel Leitch
*/
package
com
.
sobot
.
gson
.
reflect
;
\ No newline at end of file
sobot_gson/src/main/java/com/sobot/gson/stream/JsonReader.java
0 → 100644
View file @
3b40792f
/*
* 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
.
sobot
.
gson
.
stream
;
import
com.sobot.gson.internal.JsonReaderInternalAccess
;
import
com.sobot.gson.internal.bind.JsonTreeReader
;
import
com.sobot.gson.stream.MalformedJsonException
;
import
java.io.Closeable
;
import
java.io.EOFException
;
import
java.io.IOException
;
import
java.io.Reader
;
import
java.util.Arrays
;
/**
* Reads a JSON (<a href="http://www.ietf.org/rfc/rfc7159.txt">RFC 7159</a>)
* encoded value as a stream of tokens. This stream includes both literal
* values (strings, numbers, booleans, and nulls) as well as the begin and
* end delimiters of objects and arrays. The tokens are traversed in
* depth-first order, the same order that they appear in the JSON document.
* Within JSON objects, name/value pairs are represented by a single token.
*
* <h3>Parsing JSON</h3>
* To create a recursive descent parser for your own JSON streams, first create
* an entry point method that creates a {@code JsonReader}.
*
* <p>Next, create handler methods for each structure in your JSON text. You'll
* need a method for each object type and for each array type.
* <ul>
* <li>Within <strong>array handling</strong> methods, first call {@link
* #beginArray} to consume the array's opening bracket. Then create a
* while loop that accumulates values, terminating when {@link #hasNext}
* is false. Finally, read the array's closing bracket by calling {@link
* #endArray}.
* <li>Within <strong>object handling</strong> methods, first call {@link
* #beginObject} to consume the object's opening brace. Then create a
* while loop that assigns values to local variables based on their name.
* This loop should terminate when {@link #hasNext} is false. Finally,
* read the object's closing brace by calling {@link #endObject}.
* </ul>
* <p>When a nested object or array is encountered, delegate to the
* corresponding handler method.
*
* <p>When an unknown name is encountered, strict parsers should fail with an
* exception. Lenient parsers should call {@link #skipValue()} to recursively
* skip the value's nested tokens, which may otherwise conflict.
*
* <p>If a value may be null, you should first check using {@link #peek()}.
* Null literals can be consumed using either {@link #nextNull()} or {@link
* #skipValue()}.
*
* <h3>Example</h3>
* Suppose we'd like to parse a stream of messages such as the following: <pre> {@code
* [
* {
* "id": 912345678901,
* "text": "How do I read a JSON stream in Java?",
* "geo": null,
* "user": {
* "name": "json_newb",
* "followers_count": 41
* }
* },
* {
* "id": 912345678902,
* "text": "@json_newb just use JsonReader!",
* "geo": [50.454722, -104.606667],
* "user": {
* "name": "jesse",
* "followers_count": 2
* }
* }
* ]}</pre>
* This code implements the parser for the above structure: <pre> {@code
*
* public List<Message> readJsonStream(InputStream in) throws IOException {
* JsonReader reader = new JsonReader(new InputStreamReader(in, "UTF-8"));
* try {
* return readMessagesArray(reader);
* } finally {
* reader.close();
* }
* }
*
* public List<Message> readMessagesArray(JsonReader reader) throws IOException {
* List<Message> messages = new ArrayList<>();
*
* reader.beginArray();
* while (reader.hasNext()) {
* messages.add(readMessage(reader));
* }
* reader.endArray();
* return messages;
* }
*
* public Message readMessage(JsonReader reader) throws IOException {
* long id = -1;
* String text = null;
* User user = null;
* List<Double> geo = null;
*
* reader.beginObject();
* while (reader.hasNext()) {
* String name = reader.nextName();
* if (name.equals("id")) {
* id = reader.nextLong();
* } else if (name.equals("text")) {
* text = reader.nextString();
* } else if (name.equals("geo") && reader.peek() != JsonToken.NULL) {
* geo = readDoublesArray(reader);
* } else if (name.equals("user")) {
* user = readUser(reader);
* } else {
* reader.skipValue();
* }
* }
* reader.endObject();
* return new Message(id, text, user, geo);
* }
*
* public List<Double> readDoublesArray(JsonReader reader) throws IOException {
* List<Double> doubles = new ArrayList<>();
*
* reader.beginArray();
* while (reader.hasNext()) {
* doubles.add(reader.nextDouble());
* }
* reader.endArray();
* return doubles;
* }
*
* public User readUser(JsonReader reader) throws IOException {
* String username = null;
* int followersCount = -1;
*
* reader.beginObject();
* while (reader.hasNext()) {
* String name = reader.nextName();
* if (name.equals("name")) {
* username = reader.nextString();
* } else if (name.equals("followers_count")) {
* followersCount = reader.nextInt();
* } else {
* reader.skipValue();
* }
* }
* reader.endObject();
* return new User(username, followersCount);
* }}</pre>
*
* <h3>Number Handling</h3>
* This reader permits numeric values to be read as strings and string values to
* be read as numbers. For example, both elements of the JSON array {@code
* [1, "1"]} may be read using either {@link #nextInt} or {@link #nextString}.
* This behavior is intended to prevent lossy numeric conversions: double is
* JavaScript's only numeric type and very large values like {@code
* 9007199254740993} cannot be represented exactly on that platform. To minimize
* precision loss, extremely large values should be written and read as strings
* in JSON.
*
* <h3 id="nonexecuteprefix">Non-Execute Prefix</h3>
* Web servers that serve private data using JSON may be vulnerable to <a
* href="http://en.wikipedia.org/wiki/JSON#Cross-site_request_forgery">Cross-site
* request forgery</a> attacks. In such an attack, a malicious site gains access
* to a private JSON file by executing it with an HTML {@code <script>} tag.
*
* <p>Prefixing JSON files with <code>")]}'\n"</code> makes them non-executable
* by {@code <script>} tags, disarming the attack. Since the prefix is malformed
* JSON, strict parsing fails when it is encountered. This class permits the
* non-execute prefix when {@link #setLenient(boolean) lenient parsing} is
* enabled.
*
* <p>Each {@code JsonReader} may be used to read a single JSON stream. Instances
* of this class are not thread safe.
*
* @author Jesse Wilson
* @since 1.6
*/
public
class
JsonReader
implements
Closeable
{
private
static
final
long
MIN_INCOMPLETE_INTEGER
=
Long
.
MIN_VALUE
/
10
;
private
static
final
int
PEEKED_NONE
=
0
;
private
static
final
int
PEEKED_BEGIN_OBJECT
=
1
;
private
static
final
int
PEEKED_END_OBJECT
=
2
;
private
static
final
int
PEEKED_BEGIN_ARRAY
=
3
;
private
static
final
int
PEEKED_END_ARRAY
=
4
;
private
static
final
int
PEEKED_TRUE
=
5
;
private
static
final
int
PEEKED_FALSE
=
6
;
private
static
final
int
PEEKED_NULL
=
7
;
private
static
final
int
PEEKED_SINGLE_QUOTED
=
8
;
private
static
final
int
PEEKED_DOUBLE_QUOTED
=
9
;
private
static
final
int
PEEKED_UNQUOTED
=
10
;
/** When this is returned, the string value is stored in peekedString. */
private
static
final
int
PEEKED_BUFFERED
=
11
;
private
static
final
int
PEEKED_SINGLE_QUOTED_NAME
=
12
;
private
static
final
int
PEEKED_DOUBLE_QUOTED_NAME
=
13
;
private
static
final
int
PEEKED_UNQUOTED_NAME
=
14
;
/** When this is returned, the integer value is stored in peekedLong. */
private
static
final
int
PEEKED_LONG
=
15
;
private
static
final
int
PEEKED_NUMBER
=
16
;
private
static
final
int
PEEKED_EOF
=
17
;
/* State machine when parsing numbers */
private
static
final
int
NUMBER_CHAR_NONE
=
0
;
private
static
final
int
NUMBER_CHAR_SIGN
=
1
;
private
static
final
int
NUMBER_CHAR_DIGIT
=
2
;
private
static
final
int
NUMBER_CHAR_DECIMAL
=
3
;
private
static
final
int
NUMBER_CHAR_FRACTION_DIGIT
=
4
;
private
static
final
int
NUMBER_CHAR_EXP_E
=
5
;
private
static
final
int
NUMBER_CHAR_EXP_SIGN
=
6
;
private
static
final
int
NUMBER_CHAR_EXP_DIGIT
=
7
;
/** The input JSON. */
private
final
Reader
in
;
/** True to accept non-spec compliant JSON */
private
boolean
lenient
=
false
;
static
final
int
BUFFER_SIZE
=
1024
;
/**
* Use a manual buffer to easily read and unread upcoming characters, and
* also so we can create strings without an intermediate StringBuilder.
* We decode literals directly out of this buffer, so it must be at least as
* long as the longest token that can be reported as a number.
*/
private
final
char
[]
buffer
=
new
char
[
BUFFER_SIZE
];
private
int
pos
=
0
;
private
int
limit
=
0
;
private
int
lineNumber
=
0
;
private
int
lineStart
=
0
;
int
peeked
=
PEEKED_NONE
;
/**
* A peeked value that was composed entirely of digits with an optional
* leading dash. Positive values may not have a leading 0.
*/
private
long
peekedLong
;
/**
* The number of characters in a peeked number literal. Increment 'pos' by
* this after reading a number.
*/
private
int
peekedNumberLength
;
/**
* A peeked string that should be parsed on the next double, long or string.
* This is populated before a numeric value is parsed and used if that parsing
* fails.
*/
private
String
peekedString
;
/*
* The nesting stack. Using a manual array rather than an ArrayList saves 20%.
*/
private
int
[]
stack
=
new
int
[
32
];
private
int
stackSize
=
0
;
{
stack
[
stackSize
++]
=
JsonScope
.
EMPTY_DOCUMENT
;
}
/*
* The path members. It corresponds directly to stack: At indices where the
* stack contains an object (EMPTY_OBJECT, DANGLING_NAME or NONEMPTY_OBJECT),
* pathNames contains the name at this scope. Where it contains an array
* (EMPTY_ARRAY, NONEMPTY_ARRAY) pathIndices contains the current index in
* that array. Otherwise the value is undefined, and we take advantage of that
* by incrementing pathIndices when doing so isn't useful.
*/
private
String
[]
pathNames
=
new
String
[
32
];
private
int
[]
pathIndices
=
new
int
[
32
];
/**
* Creates a new instance that reads a JSON-encoded stream from {@code in}.
*/
public
JsonReader
(
Reader
in
)
{
if
(
in
==
null
)
{
throw
new
NullPointerException
(
"in == null"
);
}
this
.
in
=
in
;
}
/**
* Configure this parser to be liberal in what it accepts. By default,
* this parser is strict and only accepts JSON as specified by <a
* href="http://www.ietf.org/rfc/rfc4627.txt">RFC 4627</a>. Setting the
* parser to lenient causes it to ignore the following syntax errors:
*
* <ul>
* <li>Streams that start with the <a href="#nonexecuteprefix">non-execute
* prefix</a>, <code>")]}'\n"</code>.
* <li>Streams that include multiple top-level values. With strict parsing,
* each stream must contain exactly one top-level value.
* <li>Top-level values of any type. With strict parsing, the top-level
* value must be an object or an array.
* <li>Numbers may be {@link Double#isNaN() NaNs} or {@link
* Double#isInfinite() infinities}.
* <li>End of line comments starting with {@code //} or {@code #} and
* ending with a newline character.
* <li>C-style comments starting with {@code /*} and ending with
* {@code *}{@code /}. Such comments may not be nested.
* <li>Names that are unquoted or {@code 'single quoted'}.
* <li>Strings that are unquoted or {@code 'single quoted'}.
* <li>Array elements separated by {@code ;} instead of {@code ,}.
* <li>Unnecessary array separators. These are interpreted as if null
* was the omitted value.
* <li>Names and values separated by {@code =} or {@code =>} instead of
* {@code :}.
* <li>Name/value pairs separated by {@code ;} instead of {@code ,}.
* </ul>
*/
public
final
void
setLenient
(
boolean
lenient
)
{
this
.
lenient
=
lenient
;
}
/**
* Returns true if this parser is liberal in what it accepts.
*/
public
final
boolean
isLenient
()
{
return
lenient
;
}
/**
* Consumes the next token from the JSON stream and asserts that it is the
* beginning of a new array.
*/
public
void
beginArray
()
throws
IOException
{
int
p
=
peeked
;
if
(
p
==
PEEKED_NONE
)
{
p
=
doPeek
();
}
if
(
p
==
PEEKED_BEGIN_ARRAY
)
{
push
(
JsonScope
.
EMPTY_ARRAY
);
pathIndices
[
stackSize
-
1
]
=
0
;
peeked
=
PEEKED_NONE
;
}
else
{
throw
new
IllegalStateException
(
"Expected BEGIN_ARRAY but was "
+
peek
()
+
locationString
());
}
}
/**
* Consumes the next token from the JSON stream and asserts that it is the
* end of the current array.
*/
public
void
endArray
()
throws
IOException
{
int
p
=
peeked
;
if
(
p
==
PEEKED_NONE
)
{
p
=
doPeek
();
}
if
(
p
==
PEEKED_END_ARRAY
)
{
stackSize
--;
pathIndices
[
stackSize
-
1
]++;
peeked
=
PEEKED_NONE
;
}
else
{
throw
new
IllegalStateException
(
"Expected END_ARRAY but was "
+
peek
()
+
locationString
());
}
}
/**
* Consumes the next token from the JSON stream and asserts that it is the
* beginning of a new object.
*/
public
void
beginObject
()
throws
IOException
{
int
p
=
peeked
;
if
(
p
==
PEEKED_NONE
)
{
p
=
doPeek
();
}
if
(
p
==
PEEKED_BEGIN_OBJECT
)
{
push
(
JsonScope
.
EMPTY_OBJECT
);
peeked
=
PEEKED_NONE
;
}
else
{
throw
new
IllegalStateException
(
"Expected BEGIN_OBJECT but was "
+
peek
()
+
locationString
());
}
}
/**
* Consumes the next token from the JSON stream and asserts that it is the
* end of the current object.
*/
public
void
endObject
()
throws
IOException
{
int
p
=
peeked
;
if
(
p
==
PEEKED_NONE
)
{
p
=
doPeek
();
}
if
(
p
==
PEEKED_END_OBJECT
)
{
stackSize
--;
pathNames
[
stackSize
]
=
null
;
// Free the last path name so that it can be garbage collected!
pathIndices
[
stackSize
-
1
]++;
peeked
=
PEEKED_NONE
;
}
else
{
throw
new
IllegalStateException
(
"Expected END_OBJECT but was "
+
peek
()
+
locationString
());
}
}
/**
* Returns true if the current array or object has another element.
*/
public
boolean
hasNext
()
throws
IOException
{
int
p
=
peeked
;
if
(
p
==
PEEKED_NONE
)
{
p
=
doPeek
();
}
return
p
!=
PEEKED_END_OBJECT
&&
p
!=
PEEKED_END_ARRAY
&&
p
!=
PEEKED_EOF
;
}
/**
* Returns the type of the next token without consuming it.
*/
public
JsonToken
peek
()
throws
IOException
{
int
p
=
peeked
;
if
(
p
==
PEEKED_NONE
)
{
p
=
doPeek
();
}
switch
(
p
)
{
case
PEEKED_BEGIN_OBJECT:
return
JsonToken
.
BEGIN_OBJECT
;
case
PEEKED_END_OBJECT:
return
JsonToken
.
END_OBJECT
;
case
PEEKED_BEGIN_ARRAY:
return
JsonToken
.
BEGIN_ARRAY
;
case
PEEKED_END_ARRAY:
return
JsonToken
.
END_ARRAY
;
case
PEEKED_SINGLE_QUOTED_NAME:
case
PEEKED_DOUBLE_QUOTED_NAME:
case
PEEKED_UNQUOTED_NAME:
return
JsonToken
.
NAME
;
case
PEEKED_TRUE:
case
PEEKED_FALSE:
return
JsonToken
.
BOOLEAN
;
case
PEEKED_NULL:
return
JsonToken
.
NULL
;
case
PEEKED_SINGLE_QUOTED:
case
PEEKED_DOUBLE_QUOTED:
case
PEEKED_UNQUOTED:
case
PEEKED_BUFFERED:
return
JsonToken
.
STRING
;
case
PEEKED_LONG:
case
PEEKED_NUMBER:
return
JsonToken
.
NUMBER
;
case
PEEKED_EOF:
return
JsonToken
.
END_DOCUMENT
;
default
:
throw
new
AssertionError
();
}
}
int
doPeek
()
throws
IOException
{
int
peekStack
=
stack
[
stackSize
-
1
];
if
(
peekStack
==
JsonScope
.
EMPTY_ARRAY
)
{
stack
[
stackSize
-
1
]
=
JsonScope
.
NONEMPTY_ARRAY
;
}
else
if
(
peekStack
==
JsonScope
.
NONEMPTY_ARRAY
)
{
// Look for a comma before the next element.
int
c
=
nextNonWhitespace
(
true
);
switch
(
c
)
{
case
']'
:
return
peeked
=
PEEKED_END_ARRAY
;
case
';'
:
checkLenient
();
// fall-through
case
','
:
break
;
default
:
throw
syntaxError
(
"Unterminated array"
);
}
}
else
if
(
peekStack
==
JsonScope
.
EMPTY_OBJECT
||
peekStack
==
JsonScope
.
NONEMPTY_OBJECT
)
{
stack
[
stackSize
-
1
]
=
JsonScope
.
DANGLING_NAME
;
// Look for a comma before the next element.
if
(
peekStack
==
JsonScope
.
NONEMPTY_OBJECT
)
{
int
c
=
nextNonWhitespace
(
true
);
switch
(
c
)
{
case
'}'
:
return
peeked
=
PEEKED_END_OBJECT
;
case
';'
:
checkLenient
();
// fall-through
case
','
:
break
;
default
:
throw
syntaxError
(
"Unterminated object"
);
}
}
int
c
=
nextNonWhitespace
(
true
);
switch
(
c
)
{
case
'"'
:
return
peeked
=
PEEKED_DOUBLE_QUOTED_NAME
;
case
'\''
:
checkLenient
();
return
peeked
=
PEEKED_SINGLE_QUOTED_NAME
;
case
'}'
:
if
(
peekStack
!=
JsonScope
.
NONEMPTY_OBJECT
)
{
return
peeked
=
PEEKED_END_OBJECT
;
}
else
{
throw
syntaxError
(
"Expected name"
);
}
default
:
checkLenient
();
pos
--;
// Don't consume the first character in an unquoted string.
if
(
isLiteral
((
char
)
c
))
{
return
peeked
=
PEEKED_UNQUOTED_NAME
;
}
else
{
throw
syntaxError
(
"Expected name"
);
}
}
}
else
if
(
peekStack
==
JsonScope
.
DANGLING_NAME
)
{
stack
[
stackSize
-
1
]
=
JsonScope
.
NONEMPTY_OBJECT
;
// Look for a colon before the value.
int
c
=
nextNonWhitespace
(
true
);
switch
(
c
)
{
case
':'
:
break
;
case
'='
:
checkLenient
();
if
((
pos
<
limit
||
fillBuffer
(
1
))
&&
buffer
[
pos
]
==
'>'
)
{
pos
++;
}
break
;
default
:
throw
syntaxError
(
"Expected ':'"
);
}
}
else
if
(
peekStack
==
JsonScope
.
EMPTY_DOCUMENT
)
{
if
(
lenient
)
{
consumeNonExecutePrefix
();
}
stack
[
stackSize
-
1
]
=
JsonScope
.
NONEMPTY_DOCUMENT
;
}
else
if
(
peekStack
==
JsonScope
.
NONEMPTY_DOCUMENT
)
{
int
c
=
nextNonWhitespace
(
false
);
if
(
c
==
-
1
)
{
return
peeked
=
PEEKED_EOF
;
}
else
{
checkLenient
();
pos
--;
}
}
else
if
(
peekStack
==
JsonScope
.
CLOSED
)
{
throw
new
IllegalStateException
(
"JsonReader is closed"
);
}
int
c
=
nextNonWhitespace
(
true
);
switch
(
c
)
{
case
']'
:
if
(
peekStack
==
JsonScope
.
EMPTY_ARRAY
)
{
return
peeked
=
PEEKED_END_ARRAY
;
}
// fall-through to handle ",]"
case
';'
:
case
','
:
// In lenient mode, a 0-length literal in an array means 'null'.
if
(
peekStack
==
JsonScope
.
EMPTY_ARRAY
||
peekStack
==
JsonScope
.
NONEMPTY_ARRAY
)
{
checkLenient
();
pos
--;
return
peeked
=
PEEKED_NULL
;
}
else
{
throw
syntaxError
(
"Unexpected value"
);
}
case
'\''
:
checkLenient
();
return
peeked
=
PEEKED_SINGLE_QUOTED
;
case
'"'
:
return
peeked
=
PEEKED_DOUBLE_QUOTED
;
case
'['
:
return
peeked
=
PEEKED_BEGIN_ARRAY
;
case
'{'
:
return
peeked
=
PEEKED_BEGIN_OBJECT
;
default
:
pos
--;
// Don't consume the first character in a literal value.
}
int
result
=
peekKeyword
();
if
(
result
!=
PEEKED_NONE
)
{
return
result
;
}
result
=
peekNumber
();
if
(
result
!=
PEEKED_NONE
)
{
return
result
;
}
if
(!
isLiteral
(
buffer
[
pos
]))
{
throw
syntaxError
(
"Expected value"
);
}
checkLenient
();
return
peeked
=
PEEKED_UNQUOTED
;
}
private
int
peekKeyword
()
throws
IOException
{
// Figure out which keyword we're matching against by its first character.
char
c
=
buffer
[
pos
];
String
keyword
;
String
keywordUpper
;
int
peeking
;
if
(
c
==
't'
||
c
==
'T'
)
{
keyword
=
"true"
;
keywordUpper
=
"TRUE"
;
peeking
=
PEEKED_TRUE
;
}
else
if
(
c
==
'f'
||
c
==
'F'
)
{
keyword
=
"false"
;
keywordUpper
=
"FALSE"
;
peeking
=
PEEKED_FALSE
;
}
else
if
(
c
==
'n'
||
c
==
'N'
)
{
keyword
=
"null"
;
keywordUpper
=
"NULL"
;
peeking
=
PEEKED_NULL
;
}
else
{
return
PEEKED_NONE
;
}
// Confirm that chars [1..length) match the keyword.
int
length
=
keyword
.
length
();
for
(
int
i
=
1
;
i
<
length
;
i
++)
{
if
(
pos
+
i
>=
limit
&&
!
fillBuffer
(
i
+
1
))
{
return
PEEKED_NONE
;
}
c
=
buffer
[
pos
+
i
];
if
(
c
!=
keyword
.
charAt
(
i
)
&&
c
!=
keywordUpper
.
charAt
(
i
))
{
return
PEEKED_NONE
;
}
}
if
((
pos
+
length
<
limit
||
fillBuffer
(
length
+
1
))
&&
isLiteral
(
buffer
[
pos
+
length
]))
{
return
PEEKED_NONE
;
// Don't match trues, falsey or nullsoft!
}
// We've found the keyword followed either by EOF or by a non-literal character.
pos
+=
length
;
return
peeked
=
peeking
;
}
private
int
peekNumber
()
throws
IOException
{
// Like nextNonWhitespace, this uses locals 'p' and 'l' to save inner-loop field access.
char
[]
buffer
=
this
.
buffer
;
int
p
=
pos
;
int
l
=
limit
;
long
value
=
0
;
// Negative to accommodate Long.MIN_VALUE more easily.
boolean
negative
=
false
;
boolean
fitsInLong
=
true
;
int
last
=
NUMBER_CHAR_NONE
;
int
i
=
0
;
charactersOfNumber:
for
(;
true
;
i
++)
{
if
(
p
+
i
==
l
)
{
if
(
i
==
buffer
.
length
)
{
// Though this looks like a well-formed number, it's too long to continue reading. Give up
// and let the application handle this as an unquoted literal.
return
PEEKED_NONE
;
}
if
(!
fillBuffer
(
i
+
1
))
{
break
;
}
p
=
pos
;
l
=
limit
;
}
char
c
=
buffer
[
p
+
i
];
switch
(
c
)
{
case
'-'
:
if
(
last
==
NUMBER_CHAR_NONE
)
{
negative
=
true
;
last
=
NUMBER_CHAR_SIGN
;
continue
;
}
else
if
(
last
==
NUMBER_CHAR_EXP_E
)
{
last
=
NUMBER_CHAR_EXP_SIGN
;
continue
;
}
return
PEEKED_NONE
;
case
'+'
:
if
(
last
==
NUMBER_CHAR_EXP_E
)
{
last
=
NUMBER_CHAR_EXP_SIGN
;
continue
;
}
return
PEEKED_NONE
;
case
'e'
:
case
'E'
:
if
(
last
==
NUMBER_CHAR_DIGIT
||
last
==
NUMBER_CHAR_FRACTION_DIGIT
)
{
last
=
NUMBER_CHAR_EXP_E
;
continue
;
}
return
PEEKED_NONE
;
case
'.'
:
if
(
last
==
NUMBER_CHAR_DIGIT
)
{
last
=
NUMBER_CHAR_DECIMAL
;
continue
;
}
return
PEEKED_NONE
;
default
:
if
(
c
<
'0'
||
c
>
'9'
)
{
if
(!
isLiteral
(
c
))
{
break
charactersOfNumber
;
}
return
PEEKED_NONE
;
}
if
(
last
==
NUMBER_CHAR_SIGN
||
last
==
NUMBER_CHAR_NONE
)
{
value
=
-(
c
-
'0'
);
last
=
NUMBER_CHAR_DIGIT
;
}
else
if
(
last
==
NUMBER_CHAR_DIGIT
)
{
if
(
value
==
0
)
{
return
PEEKED_NONE
;
// Leading '0' prefix is not allowed (since it could be octal).
}
long
newValue
=
value
*
10
-
(
c
-
'0'
);
fitsInLong
&=
value
>
MIN_INCOMPLETE_INTEGER
||
(
value
==
MIN_INCOMPLETE_INTEGER
&&
newValue
<
value
);
value
=
newValue
;
}
else
if
(
last
==
NUMBER_CHAR_DECIMAL
)
{
last
=
NUMBER_CHAR_FRACTION_DIGIT
;
}
else
if
(
last
==
NUMBER_CHAR_EXP_E
||
last
==
NUMBER_CHAR_EXP_SIGN
)
{
last
=
NUMBER_CHAR_EXP_DIGIT
;
}
}
}
// We've read a complete number. Decide if it's a PEEKED_LONG or a PEEKED_NUMBER.
if
(
last
==
NUMBER_CHAR_DIGIT
&&
fitsInLong
&&
(
value
!=
Long
.
MIN_VALUE
||
negative
)
&&
(
value
!=
0
||
false
==
negative
))
{
peekedLong
=
negative
?
value
:
-
value
;
pos
+=
i
;
return
peeked
=
PEEKED_LONG
;
}
else
if
(
last
==
NUMBER_CHAR_DIGIT
||
last
==
NUMBER_CHAR_FRACTION_DIGIT
||
last
==
NUMBER_CHAR_EXP_DIGIT
)
{
peekedNumberLength
=
i
;
return
peeked
=
PEEKED_NUMBER
;
}
else
{
return
PEEKED_NONE
;
}
}
private
boolean
isLiteral
(
char
c
)
throws
IOException
{
switch
(
c
)
{
case
'/'
:
case
'\\'
:
case
';'
:
case
'#'
:
case
'='
:
checkLenient
();
// fall-through
case
'{'
:
case
'}'
:
case
'['
:
case
']'
:
case
':'
:
case
','
:
case
' '
:
case
'\t'
:
case
'\f'
:
case
'\r'
:
case
'\n'
:
return
false
;
default
:
return
true
;
}
}
/**
* Returns the next token, a {@link com.sobot.gson.stream.JsonToken#NAME property name}, and
* consumes it.
*
* @throws IOException if the next token in the stream is not a property
* name.
*/
public
String
nextName
()
throws
IOException
{
int
p
=
peeked
;
if
(
p
==
PEEKED_NONE
)
{
p
=
doPeek
();
}
String
result
;
if
(
p
==
PEEKED_UNQUOTED_NAME
)
{
result
=
nextUnquotedValue
();
}
else
if
(
p
==
PEEKED_SINGLE_QUOTED_NAME
)
{
result
=
nextQuotedValue
(
'\''
);
}
else
if
(
p
==
PEEKED_DOUBLE_QUOTED_NAME
)
{
result
=
nextQuotedValue
(
'"'
);
}
else
{
throw
new
IllegalStateException
(
"Expected a name but was "
+
peek
()
+
locationString
());
}
peeked
=
PEEKED_NONE
;
pathNames
[
stackSize
-
1
]
=
result
;
return
result
;
}
/**
* Returns the {@link com.sobot.gson.stream.JsonToken#STRING string} value of the next token,
* consuming it. If the next token is a number, this method will return its
* string form.
*
* @throws IllegalStateException if the next token is not a string or if
* this reader is closed.
*/
public
String
nextString
()
throws
IOException
{
int
p
=
peeked
;
if
(
p
==
PEEKED_NONE
)
{
p
=
doPeek
();
}
String
result
;
if
(
p
==
PEEKED_UNQUOTED
)
{
result
=
nextUnquotedValue
();
}
else
if
(
p
==
PEEKED_SINGLE_QUOTED
)
{
result
=
nextQuotedValue
(
'\''
);
}
else
if
(
p
==
PEEKED_DOUBLE_QUOTED
)
{
result
=
nextQuotedValue
(
'"'
);
}
else
if
(
p
==
PEEKED_BUFFERED
)
{
result
=
peekedString
;
peekedString
=
null
;
}
else
if
(
p
==
PEEKED_LONG
)
{
result
=
Long
.
toString
(
peekedLong
);
}
else
if
(
p
==
PEEKED_NUMBER
)
{
result
=
new
String
(
buffer
,
pos
,
peekedNumberLength
);
pos
+=
peekedNumberLength
;
}
else
{
throw
new
IllegalStateException
(
"Expected a string but was "
+
peek
()
+
locationString
());
}
peeked
=
PEEKED_NONE
;
pathIndices
[
stackSize
-
1
]++;
return
result
;
}
/**
* Returns the {@link com.sobot.gson.stream.JsonToken#BOOLEAN boolean} value of the next token,
* consuming it.
*
* @throws IllegalStateException if the next token is not a boolean or if
* this reader is closed.
*/
public
boolean
nextBoolean
()
throws
IOException
{
int
p
=
peeked
;
if
(
p
==
PEEKED_NONE
)
{
p
=
doPeek
();
}
if
(
p
==
PEEKED_TRUE
)
{
peeked
=
PEEKED_NONE
;
pathIndices
[
stackSize
-
1
]++;
return
true
;
}
else
if
(
p
==
PEEKED_FALSE
)
{
peeked
=
PEEKED_NONE
;
pathIndices
[
stackSize
-
1
]++;
return
false
;
}
throw
new
IllegalStateException
(
"Expected a boolean but was "
+
peek
()
+
locationString
());
}
/**
* Consumes the next token from the JSON stream and asserts that it is a
* literal null.
*
* @throws IllegalStateException if the next token is not null or if this
* reader is closed.
*/
public
void
nextNull
()
throws
IOException
{
int
p
=
peeked
;
if
(
p
==
PEEKED_NONE
)
{
p
=
doPeek
();
}
if
(
p
==
PEEKED_NULL
)
{
peeked
=
PEEKED_NONE
;
pathIndices
[
stackSize
-
1
]++;
}
else
{
throw
new
IllegalStateException
(
"Expected null but was "
+
peek
()
+
locationString
());
}
}
/**
* Returns the {@link com.sobot.gson.stream.JsonToken#NUMBER double} value of the next token,
* consuming it. If the next token is a string, this method will attempt to
* parse it as a double using {@link Double#parseDouble(String)}.
*
* @throws IllegalStateException if the next token is not a literal value.
* @throws NumberFormatException if the next literal value cannot be parsed
* as a double, or is non-finite.
*/
public
double
nextDouble
()
throws
IOException
{
int
p
=
peeked
;
if
(
p
==
PEEKED_NONE
)
{
p
=
doPeek
();
}
if
(
p
==
PEEKED_LONG
)
{
peeked
=
PEEKED_NONE
;
pathIndices
[
stackSize
-
1
]++;
return
(
double
)
peekedLong
;
}
if
(
p
==
PEEKED_NUMBER
)
{
peekedString
=
new
String
(
buffer
,
pos
,
peekedNumberLength
);
pos
+=
peekedNumberLength
;
}
else
if
(
p
==
PEEKED_SINGLE_QUOTED
||
p
==
PEEKED_DOUBLE_QUOTED
)
{
peekedString
=
nextQuotedValue
(
p
==
PEEKED_SINGLE_QUOTED
?
'\''
:
'"'
);
}
else
if
(
p
==
PEEKED_UNQUOTED
)
{
peekedString
=
nextUnquotedValue
();
}
else
if
(
p
!=
PEEKED_BUFFERED
)
{
throw
new
IllegalStateException
(
"Expected a double but was "
+
peek
()
+
locationString
());
}
peeked
=
PEEKED_BUFFERED
;
double
result
=
Double
.
parseDouble
(
peekedString
);
// don't catch this NumberFormatException.
if
(!
lenient
&&
(
Double
.
isNaN
(
result
)
||
Double
.
isInfinite
(
result
)))
{
throw
new
MalformedJsonException
(
"JSON forbids NaN and infinities: "
+
result
+
locationString
());
}
peekedString
=
null
;
peeked
=
PEEKED_NONE
;
pathIndices
[
stackSize
-
1
]++;
return
result
;
}
/**
* Returns the {@link com.sobot.gson.stream.JsonToken#NUMBER long} value of the next token,
* consuming it. If the next token is a string, this method will attempt to
* parse it as a long. If the next token's numeric value cannot be exactly
* represented by a Java {@code long}, this method throws.
*
* @throws IllegalStateException if the next token is not a literal value.
* @throws NumberFormatException if the next literal value cannot be parsed
* as a number, or exactly represented as a long.
*/
public
long
nextLong
()
throws
IOException
{
int
p
=
peeked
;
if
(
p
==
PEEKED_NONE
)
{
p
=
doPeek
();
}
if
(
p
==
PEEKED_LONG
)
{
peeked
=
PEEKED_NONE
;
pathIndices
[
stackSize
-
1
]++;
return
peekedLong
;
}
if
(
p
==
PEEKED_NUMBER
)
{
peekedString
=
new
String
(
buffer
,
pos
,
peekedNumberLength
);
pos
+=
peekedNumberLength
;
}
else
if
(
p
==
PEEKED_SINGLE_QUOTED
||
p
==
PEEKED_DOUBLE_QUOTED
||
p
==
PEEKED_UNQUOTED
)
{
if
(
p
==
PEEKED_UNQUOTED
)
{
peekedString
=
nextUnquotedValue
();
}
else
{
peekedString
=
nextQuotedValue
(
p
==
PEEKED_SINGLE_QUOTED
?
'\''
:
'"'
);
}
try
{
long
result
=
Long
.
parseLong
(
peekedString
);
peeked
=
PEEKED_NONE
;
pathIndices
[
stackSize
-
1
]++;
return
result
;
}
catch
(
NumberFormatException
ignored
)
{
// Fall back to parse as a double below.
}
}
else
{
throw
new
IllegalStateException
(
"Expected a long but was "
+
peek
()
+
locationString
());
}
peeked
=
PEEKED_BUFFERED
;
double
asDouble
=
Double
.
parseDouble
(
peekedString
);
// don't catch this NumberFormatException.
long
result
=
(
long
)
asDouble
;
if
(
result
!=
asDouble
)
{
// Make sure no precision was lost casting to 'long'.
throw
new
NumberFormatException
(
"Expected a long but was "
+
peekedString
+
locationString
());
}
peekedString
=
null
;
peeked
=
PEEKED_NONE
;
pathIndices
[
stackSize
-
1
]++;
return
result
;
}
/**
* Returns the string up to but not including {@code quote}, unescaping any
* character escape sequences encountered along the way. The opening quote
* should have already been read. This consumes the closing quote, but does
* not include it in the returned string.
*
* @param quote either ' or ".
* @throws NumberFormatException if any unicode escape sequences are
* malformed.
*/
private
String
nextQuotedValue
(
char
quote
)
throws
IOException
{
// Like nextNonWhitespace, this uses locals 'p' and 'l' to save inner-loop field access.
char
[]
buffer
=
this
.
buffer
;
StringBuilder
builder
=
null
;
while
(
true
)
{
int
p
=
pos
;
int
l
=
limit
;
/* the index of the first character not yet appended to the builder. */
int
start
=
p
;
while
(
p
<
l
)
{
int
c
=
buffer
[
p
++];
if
(
c
==
quote
)
{
pos
=
p
;
int
len
=
p
-
start
-
1
;
if
(
builder
==
null
)
{
return
new
String
(
buffer
,
start
,
len
);
}
else
{
builder
.
append
(
buffer
,
start
,
len
);
return
builder
.
toString
();
}
}
else
if
(
c
==
'\\'
)
{
pos
=
p
;
int
len
=
p
-
start
-
1
;
if
(
builder
==
null
)
{
int
estimatedLength
=
(
len
+
1
)
*
2
;
builder
=
new
StringBuilder
(
Math
.
max
(
estimatedLength
,
16
));
}
builder
.
append
(
buffer
,
start
,
len
);
builder
.
append
(
readEscapeCharacter
());
p
=
pos
;
l
=
limit
;
start
=
p
;
}
else
if
(
c
==
'\n'
)
{
lineNumber
++;
lineStart
=
p
;
}
}
if
(
builder
==
null
)
{
int
estimatedLength
=
(
p
-
start
)
*
2
;
builder
=
new
StringBuilder
(
Math
.
max
(
estimatedLength
,
16
));
}
builder
.
append
(
buffer
,
start
,
p
-
start
);
pos
=
p
;
if
(!
fillBuffer
(
1
))
{
throw
syntaxError
(
"Unterminated string"
);
}
}
}
/**
* Returns an unquoted value as a string.
*/
@SuppressWarnings
(
"fallthrough"
)
private
String
nextUnquotedValue
()
throws
IOException
{
StringBuilder
builder
=
null
;
int
i
=
0
;
findNonLiteralCharacter:
while
(
true
)
{
for
(;
pos
+
i
<
limit
;
i
++)
{
switch
(
buffer
[
pos
+
i
])
{
case
'/'
:
case
'\\'
:
case
';'
:
case
'#'
:
case
'='
:
checkLenient
();
// fall-through
case
'{'
:
case
'}'
:
case
'['
:
case
']'
:
case
':'
:
case
','
:
case
' '
:
case
'\t'
:
case
'\f'
:
case
'\r'
:
case
'\n'
:
break
findNonLiteralCharacter
;
}
}
// Attempt to load the entire literal into the buffer at once.
if
(
i
<
buffer
.
length
)
{
if
(
fillBuffer
(
i
+
1
))
{
continue
;
}
else
{
break
;
}
}
// use a StringBuilder when the value is too long. This is too long to be a number!
if
(
builder
==
null
)
{
builder
=
new
StringBuilder
(
Math
.
max
(
i
,
16
));
}
builder
.
append
(
buffer
,
pos
,
i
);
pos
+=
i
;
i
=
0
;
if
(!
fillBuffer
(
1
))
{
break
;
}
}
String
result
=
(
null
==
builder
)
?
new
String
(
buffer
,
pos
,
i
)
:
builder
.
append
(
buffer
,
pos
,
i
).
toString
();
pos
+=
i
;
return
result
;
}
private
void
skipQuotedValue
(
char
quote
)
throws
IOException
{
// Like nextNonWhitespace, this uses locals 'p' and 'l' to save inner-loop field access.
char
[]
buffer
=
this
.
buffer
;
do
{
int
p
=
pos
;
int
l
=
limit
;
/* the index of the first character not yet appended to the builder. */
while
(
p
<
l
)
{
int
c
=
buffer
[
p
++];
if
(
c
==
quote
)
{
pos
=
p
;
return
;
}
else
if
(
c
==
'\\'
)
{
pos
=
p
;
readEscapeCharacter
();
p
=
pos
;
l
=
limit
;
}
else
if
(
c
==
'\n'
)
{
lineNumber
++;
lineStart
=
p
;
}
}
pos
=
p
;
}
while
(
fillBuffer
(
1
));
throw
syntaxError
(
"Unterminated string"
);
}
private
void
skipUnquotedValue
()
throws
IOException
{
do
{
int
i
=
0
;
for
(;
pos
+
i
<
limit
;
i
++)
{
switch
(
buffer
[
pos
+
i
])
{
case
'/'
:
case
'\\'
:
case
';'
:
case
'#'
:
case
'='
:
checkLenient
();
// fall-through
case
'{'
:
case
'}'
:
case
'['
:
case
']'
:
case
':'
:
case
','
:
case
' '
:
case
'\t'
:
case
'\f'
:
case
'\r'
:
case
'\n'
:
pos
+=
i
;
return
;
}
}
pos
+=
i
;
}
while
(
fillBuffer
(
1
));
}
/**
* Returns the {@link com.sobot.gson.stream.JsonToken#NUMBER int} value of the next token,
* consuming it. If the next token is a string, this method will attempt to
* parse it as an int. If the next token's numeric value cannot be exactly
* represented by a Java {@code int}, this method throws.
*
* @throws IllegalStateException if the next token is not a literal value.
* @throws NumberFormatException if the next literal value cannot be parsed
* as a number, or exactly represented as an int.
*/
public
int
nextInt
()
throws
IOException
{
int
p
=
peeked
;
if
(
p
==
PEEKED_NONE
)
{
p
=
doPeek
();
}
int
result
;
if
(
p
==
PEEKED_LONG
)
{
result
=
(
int
)
peekedLong
;
if
(
peekedLong
!=
result
)
{
// Make sure no precision was lost casting to 'int'.
throw
new
NumberFormatException
(
"Expected an int but was "
+
peekedLong
+
locationString
());
}
peeked
=
PEEKED_NONE
;
pathIndices
[
stackSize
-
1
]++;
return
result
;
}
if
(
p
==
PEEKED_NUMBER
)
{
peekedString
=
new
String
(
buffer
,
pos
,
peekedNumberLength
);
pos
+=
peekedNumberLength
;
}
else
if
(
p
==
PEEKED_SINGLE_QUOTED
||
p
==
PEEKED_DOUBLE_QUOTED
||
p
==
PEEKED_UNQUOTED
)
{
if
(
p
==
PEEKED_UNQUOTED
)
{
peekedString
=
nextUnquotedValue
();
}
else
{
peekedString
=
nextQuotedValue
(
p
==
PEEKED_SINGLE_QUOTED
?
'\''
:
'"'
);
}
try
{
result
=
Integer
.
parseInt
(
peekedString
);
peeked
=
PEEKED_NONE
;
pathIndices
[
stackSize
-
1
]++;
return
result
;
}
catch
(
NumberFormatException
ignored
)
{
// Fall back to parse as a double below.
}
}
else
{
throw
new
IllegalStateException
(
"Expected an int but was "
+
peek
()
+
locationString
());
}
peeked
=
PEEKED_BUFFERED
;
double
asDouble
=
Double
.
parseDouble
(
peekedString
);
// don't catch this NumberFormatException.
result
=
(
int
)
asDouble
;
if
(
result
!=
asDouble
)
{
// Make sure no precision was lost casting to 'int'.
throw
new
NumberFormatException
(
"Expected an int but was "
+
peekedString
+
locationString
());
}
peekedString
=
null
;
peeked
=
PEEKED_NONE
;
pathIndices
[
stackSize
-
1
]++;
return
result
;
}
/**
* Closes this JSON reader and the underlying {@link Reader}.
*/
@Override
public
void
close
()
throws
IOException
{
peeked
=
PEEKED_NONE
;
stack
[
0
]
=
JsonScope
.
CLOSED
;
stackSize
=
1
;
in
.
close
();
}
/**
* Skips the next value recursively. If it is an object or array, all nested
* elements are skipped. This method is intended for use when the JSON token
* stream contains unrecognized or unhandled values.
*/
public
void
skipValue
()
throws
IOException
{
int
count
=
0
;
do
{
int
p
=
peeked
;
if
(
p
==
PEEKED_NONE
)
{
p
=
doPeek
();
}
if
(
p
==
PEEKED_BEGIN_ARRAY
)
{
push
(
JsonScope
.
EMPTY_ARRAY
);
count
++;
}
else
if
(
p
==
PEEKED_BEGIN_OBJECT
)
{
push
(
JsonScope
.
EMPTY_OBJECT
);
count
++;
}
else
if
(
p
==
PEEKED_END_ARRAY
)
{
stackSize
--;
count
--;
}
else
if
(
p
==
PEEKED_END_OBJECT
)
{
stackSize
--;
count
--;
}
else
if
(
p
==
PEEKED_UNQUOTED_NAME
||
p
==
PEEKED_UNQUOTED
)
{
skipUnquotedValue
();
}
else
if
(
p
==
PEEKED_SINGLE_QUOTED
||
p
==
PEEKED_SINGLE_QUOTED_NAME
)
{
skipQuotedValue
(
'\''
);
}
else
if
(
p
==
PEEKED_DOUBLE_QUOTED
||
p
==
PEEKED_DOUBLE_QUOTED_NAME
)
{
skipQuotedValue
(
'"'
);
}
else
if
(
p
==
PEEKED_NUMBER
)
{
pos
+=
peekedNumberLength
;
}
peeked
=
PEEKED_NONE
;
}
while
(
count
!=
0
);
pathIndices
[
stackSize
-
1
]++;
pathNames
[
stackSize
-
1
]
=
"null"
;
}
private
void
push
(
int
newTop
)
{
if
(
stackSize
==
stack
.
length
)
{
int
newLength
=
stackSize
*
2
;
stack
=
Arrays
.
copyOf
(
stack
,
newLength
);
pathIndices
=
Arrays
.
copyOf
(
pathIndices
,
newLength
);
pathNames
=
Arrays
.
copyOf
(
pathNames
,
newLength
);
}
stack
[
stackSize
++]
=
newTop
;
}
/**
* Returns true once {@code limit - pos >= minimum}. If the data is
* exhausted before that many characters are available, this returns
* false.
*/
private
boolean
fillBuffer
(
int
minimum
)
throws
IOException
{
char
[]
buffer
=
this
.
buffer
;
lineStart
-=
pos
;
if
(
limit
!=
pos
)
{
limit
-=
pos
;
System
.
arraycopy
(
buffer
,
pos
,
buffer
,
0
,
limit
);
}
else
{
limit
=
0
;
}
pos
=
0
;
int
total
;
while
((
total
=
in
.
read
(
buffer
,
limit
,
buffer
.
length
-
limit
))
!=
-
1
)
{
limit
+=
total
;
// if this is the first read, consume an optional byte order mark (BOM) if it exists
if
(
lineNumber
==
0
&&
lineStart
==
0
&&
limit
>
0
&&
buffer
[
0
]
==
'\ufeff'
)
{
pos
++;
lineStart
++;
minimum
++;
}
if
(
limit
>=
minimum
)
{
return
true
;
}
}
return
false
;
}
/**
* Returns the next character in the stream that is neither whitespace nor a
* part of a comment. When this returns, the returned character is always at
* {@code buffer[pos-1]}; this means the caller can always push back the
* returned character by decrementing {@code pos}.
*/
private
int
nextNonWhitespace
(
boolean
throwOnEof
)
throws
IOException
{
/*
* This code uses ugly local variables 'p' and 'l' representing the 'pos'
* and 'limit' fields respectively. Using locals rather than fields saves
* a few field reads for each whitespace character in a pretty-printed
* document, resulting in a 5% speedup. We need to flush 'p' to its field
* before any (potentially indirect) call to fillBuffer() and reread both
* 'p' and 'l' after any (potentially indirect) call to the same method.
*/
char
[]
buffer
=
this
.
buffer
;
int
p
=
pos
;
int
l
=
limit
;
while
(
true
)
{
if
(
p
==
l
)
{
pos
=
p
;
if
(!
fillBuffer
(
1
))
{
break
;
}
p
=
pos
;
l
=
limit
;
}
int
c
=
buffer
[
p
++];
if
(
c
==
'\n'
)
{
lineNumber
++;
lineStart
=
p
;
continue
;
}
else
if
(
c
==
' '
||
c
==
'\r'
||
c
==
'\t'
)
{
continue
;
}
if
(
c
==
'/'
)
{
pos
=
p
;
if
(
p
==
l
)
{
pos
--;
// push back '/' so it's still in the buffer when this method returns
boolean
charsLoaded
=
fillBuffer
(
2
);
pos
++;
// consume the '/' again
if
(!
charsLoaded
)
{
return
c
;
}
}
checkLenient
();
char
peek
=
buffer
[
pos
];
switch
(
peek
)
{
case
'*'
:
// skip a /* c-style comment */
pos
++;
if
(!
skipTo
(
"*/"
))
{
throw
syntaxError
(
"Unterminated comment"
);
}
p
=
pos
+
2
;
l
=
limit
;
continue
;
case
'/'
:
// skip a // end-of-line comment
pos
++;
skipToEndOfLine
();
p
=
pos
;
l
=
limit
;
continue
;
default
:
return
c
;
}
}
else
if
(
c
==
'#'
)
{
pos
=
p
;
/*
* Skip a # hash end-of-line comment. The JSON RFC doesn't
* specify this behaviour, but it's required to parse
* existing documents. See http://b/2571423.
*/
checkLenient
();
skipToEndOfLine
();
p
=
pos
;
l
=
limit
;
}
else
{
pos
=
p
;
return
c
;
}
}
if
(
throwOnEof
)
{
throw
new
EOFException
(
"End of input"
+
locationString
());
}
else
{
return
-
1
;
}
}
private
void
checkLenient
()
throws
IOException
{
if
(!
lenient
)
{
throw
syntaxError
(
"Use JsonReader.setLenient(true) to accept malformed JSON"
);
}
}
/**
* Advances the position until after the next newline character. If the line
* is terminated by "\r\n", the '\n' must be consumed as whitespace by the
* caller.
*/
private
void
skipToEndOfLine
()
throws
IOException
{
while
(
pos
<
limit
||
fillBuffer
(
1
))
{
char
c
=
buffer
[
pos
++];
if
(
c
==
'\n'
)
{
lineNumber
++;
lineStart
=
pos
;
break
;
}
else
if
(
c
==
'\r'
)
{
break
;
}
}
}
/**
* @param toFind a string to search for. Must not contain a newline.
*/
private
boolean
skipTo
(
String
toFind
)
throws
IOException
{
int
length
=
toFind
.
length
();
outer:
for
(;
pos
+
length
<=
limit
||
fillBuffer
(
length
);
pos
++)
{
if
(
buffer
[
pos
]
==
'\n'
)
{
lineNumber
++;
lineStart
=
pos
+
1
;
continue
;
}
for
(
int
c
=
0
;
c
<
length
;
c
++)
{
if
(
buffer
[
pos
+
c
]
!=
toFind
.
charAt
(
c
))
{
continue
outer
;
}
}
return
true
;
}
return
false
;
}
@Override
public
String
toString
()
{
return
getClass
().
getSimpleName
()
+
locationString
();
}
String
locationString
()
{
int
line
=
lineNumber
+
1
;
int
column
=
pos
-
lineStart
+
1
;
return
" at line "
+
line
+
" column "
+
column
+
" path "
+
getPath
();
}
private
String
getPath
(
boolean
usePreviousPath
)
{
StringBuilder
result
=
new
StringBuilder
().
append
(
'$'
);
for
(
int
i
=
0
;
i
<
stackSize
;
i
++)
{
switch
(
stack
[
i
])
{
case
JsonScope
.
EMPTY_ARRAY
:
case
JsonScope
.
NONEMPTY_ARRAY
:
int
pathIndex
=
pathIndices
[
i
];
// If index is last path element it points to next array element; have to decrement
if
(
usePreviousPath
&&
pathIndex
>
0
&&
i
==
stackSize
-
1
)
{
pathIndex
--;
}
result
.
append
(
'['
).
append
(
pathIndex
).
append
(
']'
);
break
;
case
JsonScope
.
EMPTY_OBJECT
:
case
JsonScope
.
DANGLING_NAME
:
case
JsonScope
.
NONEMPTY_OBJECT
:
result
.
append
(
'.'
);
if
(
pathNames
[
i
]
!=
null
)
{
result
.
append
(
pathNames
[
i
]);
}
break
;
case
JsonScope
.
NONEMPTY_DOCUMENT
:
case
JsonScope
.
EMPTY_DOCUMENT
:
case
JsonScope
.
CLOSED
:
break
;
}
}
return
result
.
toString
();
}
/**
* Returns a <a href="https://goessner.net/articles/JsonPath/">JsonPath</a>
* in <i>dot-notation</i> to the previous (or current) location in the JSON document:
* <ul>
* <li>For JSON arrays the path points to the index of the previous element.<br>
* If no element has been consumed yet it uses the index 0 (even if there are no elements).</li>
* <li>For JSON objects the path points to the last property, or to the current
* property if its value has not been consumed yet.</li>
* </ul>
*
* <p>This method can be useful to add additional context to exception messages
* <i>after</i> a value has been consumed.
*/
public
String
getPreviousPath
()
{
return
getPath
(
true
);
}
/**
* Returns a <a href="https://goessner.net/articles/JsonPath/">JsonPath</a>
* in <i>dot-notation</i> to the next (or current) location in the JSON document:
* <ul>
* <li>For JSON arrays the path points to the index of the next element (even
* if there are no further elements).</li>
* <li>For JSON objects the path points to the last property, or to the current
* property if its value has not been consumed yet.</li>
* </ul>
*
* <p>This method can be useful to add additional context to exception messages
* <i>before</i> a value is consumed, for example when the {@linkplain #peek() peeked}
* token is unexpected.
*/
public
String
getPath
()
{
return
getPath
(
false
);
}
/**
* Unescapes the character identified by the character or characters that
* immediately follow a backslash. The backslash '\' should have already
* been read. This supports both unicode escapes "u000A" and two-character
* escapes "\n".
*
* @throws NumberFormatException if any unicode escape sequences are
* malformed.
*/
private
char
readEscapeCharacter
()
throws
IOException
{
if
(
pos
==
limit
&&
!
fillBuffer
(
1
))
{
throw
syntaxError
(
"Unterminated escape sequence"
);
}
char
escaped
=
buffer
[
pos
++];
switch
(
escaped
)
{
case
'u'
:
if
(
pos
+
4
>
limit
&&
!
fillBuffer
(
4
))
{
throw
syntaxError
(
"Unterminated escape sequence"
);
}
// Equivalent to Integer.parseInt(stringPool.get(buffer, pos, 4), 16);
char
result
=
0
;
for
(
int
i
=
pos
,
end
=
i
+
4
;
i
<
end
;
i
++)
{
char
c
=
buffer
[
i
];
result
<<=
4
;
if
(
c
>=
'0'
&&
c
<=
'9'
)
{
result
+=
(
c
-
'0'
);
}
else
if
(
c
>=
'a'
&&
c
<=
'f'
)
{
result
+=
(
c
-
'a'
+
10
);
}
else
if
(
c
>=
'A'
&&
c
<=
'F'
)
{
result
+=
(
c
-
'A'
+
10
);
}
else
{
throw
new
NumberFormatException
(
"\\u"
+
new
String
(
buffer
,
pos
,
4
));
}
}
pos
+=
4
;
return
result
;
case
't'
:
return
'\t'
;
case
'b'
:
return
'\b'
;
case
'n'
:
return
'\n'
;
case
'r'
:
return
'\r'
;
case
'f'
:
return
'\f'
;
case
'\n'
:
lineNumber
++;
lineStart
=
pos
;
// fall-through
case
'\''
:
case
'"'
:
case
'\\'
:
case
'/'
:
return
escaped
;
default
:
// throw error when none of the above cases are matched
throw
syntaxError
(
"Invalid escape sequence"
);
}
}
/**
* Throws a new IO exception with the given message and a context snippet
* with this reader's content.
*/
private
IOException
syntaxError
(
String
message
)
throws
IOException
{
throw
new
MalformedJsonException
(
message
+
locationString
());
}
/**
* Consumes the non-execute prefix if it exists.
*/
private
void
consumeNonExecutePrefix
()
throws
IOException
{
// fast forward through the leading whitespace
nextNonWhitespace
(
true
);
pos
--;
if
(
pos
+
5
>
limit
&&
!
fillBuffer
(
5
))
{
return
;
}
int
p
=
pos
;
char
[]
buf
=
buffer
;
if
(
buf
[
p
]
!=
')'
||
buf
[
p
+
1
]
!=
']'
||
buf
[
p
+
2
]
!=
'}'
||
buf
[
p
+
3
]
!=
'\''
||
buf
[
p
+
4
]
!=
'\n'
)
{
return
;
// not a security token!
}
// we consumed a security token!
pos
+=
5
;
}
static
{
JsonReaderInternalAccess
.
INSTANCE
=
new
JsonReaderInternalAccess
()
{
@Override
public
void
promoteNameToValue
(
JsonReader
reader
)
throws
IOException
{
if
(
reader
instanceof
JsonTreeReader
)
{
((
JsonTreeReader
)
reader
).
promoteNameToValue
();
return
;
}
int
p
=
reader
.
peeked
;
if
(
p
==
PEEKED_NONE
)
{
p
=
reader
.
doPeek
();
}
if
(
p
==
PEEKED_DOUBLE_QUOTED_NAME
)
{
reader
.
peeked
=
PEEKED_DOUBLE_QUOTED
;
}
else
if
(
p
==
PEEKED_SINGLE_QUOTED_NAME
)
{
reader
.
peeked
=
PEEKED_SINGLE_QUOTED
;
}
else
if
(
p
==
PEEKED_UNQUOTED_NAME
)
{
reader
.
peeked
=
PEEKED_UNQUOTED
;
}
else
{
throw
new
IllegalStateException
(
"Expected a name but was "
+
reader
.
peek
()
+
reader
.
locationString
());
}
}
};
}
}
sobot_gson/src/main/java/com/sobot/gson/stream/JsonScope.java
0 → 100644
View file @
3b40792f
/*
* 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
.
sobot
.
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
;
}
sobot_gson/src/main/java/com/sobot/gson/stream/JsonToken.java
0 → 100644
View file @
3b40792f
/*
* 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
.
sobot
.
gson
.
stream
;
import
com.sobot.gson.stream.JsonReader
;
import
com.sobot.gson.stream.JsonWriter
;
/**
* 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
}
sobot_gson/src/main/java/com/sobot/gson/stream/JsonWriter.java
0 → 100644
View file @
3b40792f
/*
* 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
.
sobot
.
gson
.
stream
;
import
java.io.Closeable
;
import
java.io.Flushable
;
import
java.io.IOException
;
import
java.io.Writer
;
import
java.math.BigDecimal
;
import
java.math.BigInteger
;
import
java.util.Arrays
;
import
java.util.concurrent.atomic.AtomicInteger
;
import
java.util.concurrent.atomic.AtomicLong
;
import
java.util.regex.Pattern
;
import
static
com
.
sobot
.
gson
.
stream
.
JsonScope
.
DANGLING_NAME
;
import
static
com
.
sobot
.
gson
.
stream
.
JsonScope
.
EMPTY_ARRAY
;
import
static
com
.
sobot
.
gson
.
stream
.
JsonScope
.
EMPTY_DOCUMENT
;
import
static
com
.
sobot
.
gson
.
stream
.
JsonScope
.
EMPTY_OBJECT
;
import
static
com
.
sobot
.
gson
.
stream
.
JsonScope
.
NONEMPTY_ARRAY
;
import
static
com
.
sobot
.
gson
.
stream
.
JsonScope
.
NONEMPTY_DOCUMENT
;
import
static
com
.
sobot
.
gson
.
stream
.
JsonScope
.
NONEMPTY_OBJECT
;
/**
* Writes a JSON (<a href="http://www.ietf.org/rfc/rfc7159.txt">RFC 7159</a>)
* encoded value to a stream, one token at a time. The stream includes both
* 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.setIndent(" ");
* 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
{
// Syntax as defined by https://datatracker.ietf.org/doc/html/rfc8259#section-6
private
static
final
Pattern
VALID_JSON_NUMBER_PATTERN
=
Pattern
.
compile
(
"-?(?:0|[1-9][0-9]*)(?:\\.[0-9]+)?(?:[eE][-+]?[0-9]+)?"
);
/*
* From RFC 7159, "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"
,
(
int
)
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/rfc7159.txt">RFC 7159</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
,
char
openBracket
)
throws
IOException
{
beforeValue
();
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
,
char
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
)
{
stack
=
Arrays
.
copyOf
(
stack
,
stackSize
*
2
);
}
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
();
string
(
value
);
return
this
;
}
/**
* Writes {@code value} directly to the writer without quoting or
* escaping.
*
* @param value the literal string value, or null to encode a null literal.
* @return this writer.
*/
public
JsonWriter
jsonValue
(
String
value
)
throws
IOException
{
if
(
value
==
null
)
{
return
nullValue
();
}
writeDeferredName
();
beforeValue
();
out
.
append
(
value
);
return
this
;
}
/**
* Encodes {@code null}.
*
* @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
();
out
.
write
(
"null"
);
return
this
;
}
/**
* Encodes {@code value}.
*
* @return this writer.
*/
public
JsonWriter
value
(
boolean
value
)
throws
IOException
{
writeDeferredName
();
beforeValue
();
out
.
write
(
value
?
"true"
:
"false"
);
return
this
;
}
/**
* Encodes {@code value}.
*
* @return this writer.
*/
public
JsonWriter
value
(
Boolean
value
)
throws
IOException
{
if
(
value
==
null
)
{
return
nullValue
();
}
writeDeferredName
();
beforeValue
();
out
.
write
(
value
?
"true"
:
"false"
);
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.
* @throws IllegalArgumentException if the value is NaN or Infinity and this writer is
* not {@link #setLenient(boolean) lenient}.
*/
public
JsonWriter
value
(
double
value
)
throws
IOException
{
writeDeferredName
();
if
(!
lenient
&&
(
Double
.
isNaN
(
value
)
||
Double
.
isInfinite
(
value
)))
{
throw
new
IllegalArgumentException
(
"Numeric values must be finite, but was "
+
value
);
}
beforeValue
();
out
.
append
(
Double
.
toString
(
value
));
return
this
;
}
/**
* Encodes {@code value}.
*
* @return this writer.
*/
public
JsonWriter
value
(
long
value
)
throws
IOException
{
writeDeferredName
();
beforeValue
();
out
.
write
(
Long
.
toString
(
value
));
return
this
;
}
/**
* Returns whether the {@code toString()} of {@code c} can be trusted to return
* a valid JSON number.
*/
private
static
boolean
isTrustedNumberType
(
Class
<?
extends
Number
>
c
)
{
// Note: Don't consider LazilyParsedNumber trusted because it could contain
// an arbitrary malformed string
return
c
==
Integer
.
class
||
c
==
Long
.
class
||
c
==
Double
.
class
||
c
==
Float
.
class
||
c
==
Byte
.
class
||
c
==
Short
.
class
||
c
==
BigDecimal
.
class
||
c
==
BigInteger
.
class
||
c
==
AtomicInteger
.
class
||
c
==
AtomicLong
.
class
;
}
/**
* Encodes {@code value}. The value is written by directly writing the {@link Number#toString()}
* result to JSON. Implementations must make sure that the result represents a valid JSON number.
*
* @param value a finite value. May not be {@link Double#isNaN() NaNs} or
* {@link Double#isInfinite() infinities}.
* @return this writer.
* @throws IllegalArgumentException if the value is NaN or Infinity and this writer is
* not {@link #setLenient(boolean) lenient}; or if the {@code toString()} result is not a
* valid JSON number.
*/
public
JsonWriter
value
(
Number
value
)
throws
IOException
{
if
(
value
==
null
)
{
return
nullValue
();
}
writeDeferredName
();
String
string
=
value
.
toString
();
if
(
string
.
equals
(
"-Infinity"
)
||
string
.
equals
(
"Infinity"
)
||
string
.
equals
(
"NaN"
))
{
if
(!
lenient
)
{
throw
new
IllegalArgumentException
(
"Numeric values must be finite, but was "
+
string
);
}
}
else
{
Class
<?
extends
Number
>
numberClass
=
value
.
getClass
();
// Validate that string is valid before writing it directly to JSON output
if
(!
isTrustedNumberType
(
numberClass
)
&&
!
VALID_JSON_NUMBER_PATTERN
.
matcher
(
string
).
matches
())
{
throw
new
IllegalArgumentException
(
"String created by "
+
numberClass
+
" is not a valid JSON number: "
+
string
);
}
}
beforeValue
();
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.
*/
@SuppressWarnings
(
"fallthrough"
)
private
void
beforeValue
()
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
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."
);
}
}
}
sobot_
json/src/main/java/com/sobot/fastjson/JSON
Exception.java
→
sobot_
gson/src/main/java/com/sobot/gson/stream/MalformedJson
Exception.java
100755 → 100644
View file @
3b40792f
/*
/*
* Copyright
1999-2101 Alibaba Group
.
* Copyright
(C) 2010 Google Inc
.
*
*
* Licensed under the Apache License, Version 2.0 (the "License");
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* you may not use this file except in compliance with the License.
...
@@ -13,19 +13,27 @@
...
@@ -13,19 +13,27 @@
* See the License for the specific language governing permissions and
* See the License for the specific language governing permissions and
* limitations under the License.
* limitations under the License.
*/
*/
package
com
.
sobot
.
fastjson
;
package
com
.
sobot
.
gson
.
stream
;
import
java.io.IOException
;
/**
/**
* @author wenshao[szujobs@hotmail.com]
* Thrown when a reader encounters malformed JSON. Some syntax errors can be
* ignored by calling {@link JsonReader#setLenient(boolean)}.
*/
*/
@SuppressWarnings
(
"serial"
)
public
final
class
MalformedJsonException
extends
IOException
{
public
class
JSONException
extends
RuntimeException
{
private
static
final
long
serialVersionUID
=
1L
;
public
MalformedJsonException
(
String
msg
)
{
super
(
msg
);
}
public
JSONException
(
String
message
)
{
public
MalformedJsonException
(
String
msg
,
Throwable
throwable
)
{
super
(
messag
e
);
super
(
msg
,
throwabl
e
);
}
}
public
JSONException
(
String
message
,
Throwable
cause
)
{
public
MalformedJsonException
(
Throwable
throwable
)
{
super
(
message
,
caus
e
);
super
(
throwabl
e
);
}
}
}
}
sobot_gson/src/main/java/com/sobot/gson/stream/package-info.java
0 → 100644
View file @
3b40792f
/**
* This package provides classes for processing JSON in an efficient streaming way.
*/
package
com
.
sobot
.
gson
.
stream
;
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment