New upstream version 2.9.0
tony mancill
1 year, 11 months ago
0 | --- | |
1 | name: Bug report | |
2 | about: Report a Gson bug. | |
3 | title: '' | |
4 | labels: bug | |
5 | assignees: '' | |
6 | ||
7 | --- | |
8 | ||
9 | # Gson version | |
10 | <!-- Gson version you are using, for example '2.8.8' --> | |
11 | ||
12 | ||
13 | # Java / Android version | |
14 | <!-- Version of the Java or Android platform on which the bug occurred --> | |
15 | ||
16 | ||
17 | # Used tools | |
18 | <!-- List relevant build tools and plugins with version number here which might affect Gson --> | |
19 | - [ ] Maven; version: | |
20 | - [ ] Gradle; version: | |
21 | - [ ] ProGuard (attach the configuration file please); version: | |
22 | - [ ] ... | |
23 | ||
24 | # Description | |
25 | <!-- Describe the bug you experienced --> | |
26 | ||
27 | ||
28 | ## Expected behavior | |
29 | <!-- What behavior did you expect? --> | |
30 | ||
31 | ||
32 | ## Actual behavior | |
33 | <!-- What happened instead? --> | |
34 | ||
35 | ||
36 | # Reproduction steps | |
37 | <!-- Provide exact reproduction steps for reproducing the bug --> | |
38 | <!-- Provide a short code snippet or link to a demo project --> | |
39 | ||
40 | 1. ... | |
41 | 2. ... | |
42 | ||
43 | # Exception stack trace | |
44 | <!-- In case an exception occurred, paste the COMPLETE exception stack trace in the code block below or attach it as file --> | |
45 | ||
46 | ``` | |
47 | ||
48 | ``` |
0 | contact_links: | |
1 | - name: Usage question | |
2 | url: https://stackoverflow.com/questions/tagged/gson | |
3 | about: Ask usage questions on StackOverflow. |
0 | --- | |
1 | name: Feature request | |
2 | about: Request a feature. ⚠️ Gson is in maintenance mode; large feature requests might be rejected. | |
3 | title: '' | |
4 | labels: enhancement | |
5 | assignees: '' | |
6 | ||
7 | --- | |
8 | ||
9 | # Problem solved by the feature | |
10 | <!-- Describe which problem the requested feature solves --> | |
11 | ||
12 | ||
13 | # Feature description | |
14 | <!-- Describe the feature --> | |
15 | ||
16 | ||
17 | # Alternatives / workarounds | |
18 | <!-- Describe alternatives or workarounds in case you are aware of any --> | |
19 |
0 | name: Build | |
1 | ||
2 | on: [push, pull_request] | |
3 | ||
4 | jobs: | |
5 | build: | |
6 | runs-on: ubuntu-latest | |
7 | ||
8 | steps: | |
9 | - uses: actions/checkout@v2 | |
10 | - name: Set up JDK 11 | |
11 | uses: actions/setup-java@v2 | |
12 | with: | |
13 | distribution: 'temurin' | |
14 | java-version: '11' | |
15 | cache: 'maven' | |
16 | - name: Build with Maven | |
17 | run: mvn --batch-mode --update-snapshots verify |
0 | name: CIFuzz | |
1 | on: [pull_request] | |
2 | jobs: | |
3 | Fuzzing: | |
4 | runs-on: ubuntu-latest | |
5 | steps: | |
6 | - name: Build Fuzzers | |
7 | id: build | |
8 | uses: google/oss-fuzz/infra/cifuzz/actions/build_fuzzers@master | |
9 | with: | |
10 | oss-fuzz-project-name: 'gson' | |
11 | dry-run: false | |
12 | language: jvm | |
13 | - name: Run Fuzzers | |
14 | uses: google/oss-fuzz/infra/cifuzz/actions/run_fuzzers@master | |
15 | with: | |
16 | oss-fuzz-project-name: 'gson' | |
17 | fuzz-seconds: 600 | |
18 | dry-run: false | |
19 | - name: Upload Crash | |
20 | uses: actions/upload-artifact@v1 | |
21 | if: failure() && steps.build.outcome == 'success' | |
22 | with: | |
23 | name: artifacts | |
24 | path: ./out/artifacts |
0 | name: "Validate Gradle Wrapper" | |
1 | on: [push, pull_request] | |
2 | ||
3 | jobs: | |
4 | validation: | |
5 | name: "Validation" | |
6 | runs-on: ubuntu-latest | |
7 | steps: | |
8 | - uses: actions/checkout@v2 | |
9 | - uses: gradle/wrapper-validation-action@v1 |
0 | language: java | |
1 | ||
2 | jdk: | |
3 | - openjdk11 | |
4 | ||
5 | install: mvn -f gson install -DskipTests=true | |
6 | script: mvn -f gson test | |
7 | ||
8 | branches: | |
9 | except: | |
10 | - gh-pages | |
11 | ||
12 | notifications: | |
13 | email: false | |
14 | ||
15 | sudo: false | |
16 | ||
17 | cache: | |
18 | directories: | |
19 | - $HOME/.m2 |
0 | 0 | Change Log |
1 | 1 | ========== |
2 | ||
3 | ## Version 2.8.9 | |
4 | ||
5 | * Make OSGi bundle's dependency on `sun.misc` optional (#1993). | |
6 | * Deprecate `Gson.excluder()` exposing internal `Excluder` class (#1986). | |
7 | * Prevent Java deserialization of internal classes (#1991). | |
8 | * Improve number strategy implementation (#1987). | |
9 | * Fix LongSerializationPolicy null handling being inconsistent with Gson (#1990). | |
10 | * Support arbitrary Number implementation for Object and Number deserialization (#1290). | |
11 | * Bump proguard-maven-plugin from 2.4.0 to 2.5.1 (#1980). | |
12 | * Don't exclude static local classes (#1969). | |
13 | * Fix `RuntimeTypeAdapterFactory` depending on internal `Streams` class (#1959). | |
14 | * Improve Maven build (#1964). | |
15 | * Make dependency on `java.sql` optional (#1707). | |
16 | ||
17 | ## Version 2.8.8 | |
18 | ||
19 | * Fixed issue with recursive types (#1390). | |
20 | * Better behaviour with Java 9+ and `Unsafe` if there is a security manager (#1712). | |
21 | * `EnumTypeAdapter` now works better when ProGuard has obfuscated enum fields (#1495). | |
2 | 22 | |
3 | 23 | ## Version 2.8.7 |
4 | 24 |
16 | 16 | Gradle: |
17 | 17 | ```gradle |
18 | 18 | dependencies { |
19 | implementation 'com.google.code.gson:gson:2.8.7' | |
19 | implementation 'com.google.code.gson:gson:2.8.9' | |
20 | 20 | } |
21 | 21 | ``` |
22 | 22 | |
25 | 25 | <dependency> |
26 | 26 | <groupId>com.google.code.gson</groupId> |
27 | 27 | <artifactId>gson</artifactId> |
28 | <version>2.8.7</version> | |
28 | <version>2.8.9</version> | |
29 | 29 | </dependency> |
30 | 30 | ``` |
31 | 31 | |
32 | 32 | [Gson jar downloads](https://maven-badges.herokuapp.com/maven-central/com.google.code.gson/gson) are available from Maven Central. |
33 | 33 | |
34 | [![Build Status](https://travis-ci.org/google/gson.svg?branch=master)](https://travis-ci.org/google/gson) | |
34 | ![Build Status](https://github.com/google/gson/actions/workflows/build.yml/badge.svg) | |
35 | ||
36 | ### Requirements | |
37 | #### Minimum Java version | |
38 | - Gson 2.9.0 and newer: Java 7 | |
39 | - Gson 2.8.9 and older: Java 6 | |
40 | ||
41 | Despite supporting older Java versions, Gson also provides a JPMS module descriptor (module name `com.google.gson`) for users of Java 9 or newer. | |
42 | ||
43 | #### JPMS dependencies (Java 9+) | |
44 | These are the optional Java Platform Module System (JPMS) JDK modules which Gson depends on. | |
45 | This only applies when running Java 9 or newer. | |
46 | ||
47 | - `java.sql` (optional since Gson 2.8.9) | |
48 | When this module is present, Gson provides default adapters for some SQL date and time classes. | |
49 | ||
50 | - `jdk.unsupported`, respectively class `sun.misc.Unsafe` (optional) | |
51 | When this module is present, Gson can use the `Unsafe` class to create instances of classes without no-args constructor. | |
52 | However, care should be taken when relying on this. `Unsafe` is not available in all environments and its usage has some pitfalls, | |
53 | see [`GsonBuilder.disableJdkUnsafe()`](https://javadoc.io/doc/com.google.code.gson/gson/latest/com.google.gson/com/google/gson/GsonBuilder.html#disableJdkUnsafe()). | |
35 | 54 | |
36 | 55 | ### Documentation |
37 | 56 | * [API Javadoc](https://www.javadoc.io/doc/com.google.code.gson/gson): Documentation for the current release |
39 | 58 | * [Change log](https://github.com/google/gson/blob/master/CHANGELOG.md): Changes in the recent versions |
40 | 59 | * [Design document](https://github.com/google/gson/blob/master/GsonDesignDocument.md): This document discusses issues we faced while designing Gson. It also includes a comparison of Gson with other Java libraries that can be used for Json conversion |
41 | 60 | |
42 | Please use the 'gson' tag on StackOverflow or the [google-gson Google group](https://groups.google.com/group/google-gson) to discuss Gson or to post questions. | |
61 | Please use the ['gson' tag on StackOverflow](https://stackoverflow.com/questions/tagged/gson) or the [google-gson Google group](https://groups.google.com/group/google-gson) to discuss Gson or to post questions. | |
43 | 62 | |
44 | 63 | ### Related Content Created by Third Parties |
45 | 64 | * [Gson Tutorial](https://www.studytrails.com/java/json/java-google-json-introduction/) by `StudyTrails` |
73 | 73 | ## <a name="TOC-Gson-With-Gradle"></a>Using Gson with Gradle/Android |
74 | 74 | ``` |
75 | 75 | dependencies { |
76 | implementation 'com.google.code.gson:gson:2.8.7' | |
76 | implementation 'com.google.code.gson:gson:2.8.9' | |
77 | 77 | } |
78 | 78 | ``` |
79 | 79 | ## <a name="TOC-Gson-With-Maven"></a>Using Gson with Maven |
85 | 85 | <dependency> |
86 | 86 | <groupId>com.google.code.gson</groupId> |
87 | 87 | <artifactId>gson</artifactId> |
88 | <version>2.8.7</version> | |
88 | <version>2.8.9</version> | |
89 | 89 | <scope>compile</scope> |
90 | 90 | </dependency> |
91 | 91 | </dependencies> |
428 | 428 | public Id<?> createInstance(Type type) { |
429 | 429 | Type[] typeParameters = ((ParameterizedType)type).getActualTypeArguments(); |
430 | 430 | Type idType = typeParameters[0]; // Id has only one parameterized type T |
431 | return Id.get((Class)idType, 0L); | |
431 | return new Id((Class)idType, 0L); | |
432 | 432 | } |
433 | 433 | } |
434 | 434 | ``` |
0 | buildscript { | |
1 | repositories { | |
2 | mavenCentral() | |
3 | } | |
4 | } | |
5 | ||
6 | allprojects { | |
7 | repositories { | |
8 | mavenCentral() | |
9 | } | |
10 | } | |
11 |
0 | # codegen | |
1 | ||
2 | This Maven module contains the source code for automatically generating Gson type adapters. | |
3 | ||
4 | :warning: This module is currently non-functional and might be removed in the future. |
0 | 0 | <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> |
1 | 1 | <modelVersion>4.0.0</modelVersion> |
2 | <groupId>com.google.code.gson</groupId> | |
2 | <parent> | |
3 | <groupId>com.google.code.gson</groupId> | |
4 | <artifactId>gson-parent</artifactId> | |
5 | <version>2.9.0</version> | |
6 | </parent> | |
7 | ||
3 | 8 | <artifactId>gson-codegen</artifactId> |
4 | <packaging>jar</packaging> | |
5 | <version>1.0-SNAPSHOT</version> | |
6 | 9 | <inceptionYear>2008</inceptionYear> |
7 | 10 | <name>Gson Code Gen</name> |
8 | <parent> | |
9 | <groupId>org.sonatype.oss</groupId> | |
10 | <artifactId>oss-parent</artifactId> | |
11 | <version>7</version> | |
12 | </parent> | |
13 | <url>http://code.google.com/p/google-gson/</url> | |
14 | 11 | <description>Google Gson grab bag of utilities, type adapters, etc.</description> |
15 | <properties> | |
16 | <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> | |
17 | </properties> | |
12 | ||
18 | 13 | <licenses> |
19 | 14 | <license> |
20 | <name>The Apache Software License, Version 2.0</name> | |
21 | <url>http://www.apache.org/licenses/LICENSE-2.0.txt</url> | |
22 | <distribution>repo</distribution> | |
15 | <name>Apache-2.0</name> | |
16 | <url>https://www.apache.org/licenses/LICENSE-2.0.txt</url> | |
23 | 17 | </license> |
24 | 18 | </licenses> |
25 | <scm> | |
26 | <connection>scm:svn:http://google-gson.googlecode.com/svn/trunk/extras</connection> | |
27 | <developerConnection>scm:svn:https://google-gson.googlecode.com/svn/trunk/extras</developerConnection> | |
28 | <url>http://google-gson.codegoogle.com/svn/trunk/extras</url> | |
29 | </scm> | |
30 | <issueManagement> | |
31 | <system>Google Code Issue Tracking</system> | |
32 | <url>http://code.google.com/p/google-gson/issues/list</url> | |
33 | </issueManagement> | |
19 | ||
34 | 20 | <organization> |
35 | 21 | <name>Google, Inc.</name> |
36 | <url>http://www.google.com</url> | |
22 | <url>https://www.google.com</url> | |
37 | 23 | </organization> |
24 | ||
38 | 25 | <dependencies> |
39 | 26 | <dependency> |
40 | 27 | <groupId>junit</groupId> |
41 | 28 | <artifactId>junit</artifactId> |
42 | <version>4.13.1</version> | |
43 | 29 | <scope>test</scope> |
44 | 30 | </dependency> |
45 | 31 | </dependencies> |
46 | <profiles> | |
47 | <!-- Activate PGP signing only when performing a release --> | |
48 | <profile> | |
49 | <id>release-sign-artifacts</id> | |
50 | <activation> | |
51 | <property> | |
52 | <name>performRelease</name> | |
53 | <value>true</value> | |
54 | </property> | |
55 | </activation> | |
56 | <build> | |
57 | <plugins> | |
58 | <plugin> | |
59 | <groupId>org.apache.maven.plugins</groupId> | |
60 | <artifactId>maven-gpg-plugin</artifactId> | |
61 | <version>1.4</version> | |
62 | <executions> | |
63 | <execution> | |
64 | <id>sign-artifacts</id> | |
65 | <phase>verify</phase> | |
66 | <goals> | |
67 | <goal>sign</goal> | |
68 | </goals> | |
69 | </execution> | |
70 | </executions> | |
71 | </plugin> | |
72 | </plugins> | |
73 | </build> | |
74 | </profile> | |
75 | </profiles> | |
32 | ||
76 | 33 | <build> |
77 | <defaultGoal>package</defaultGoal> | |
78 | 34 | <plugins> |
79 | 35 | <plugin> |
80 | 36 | <groupId>org.apache.maven.plugins</groupId> |
81 | 37 | <artifactId>maven-compiler-plugin</artifactId> |
82 | <version>2.5.1</version> | |
83 | <configuration> | |
84 | <source>1.6</source> | |
85 | <target>1.6</target> | |
86 | <compilerArgument>-proc:none</compilerArgument> | |
87 | </configuration> | |
88 | </plugin> | |
89 | <plugin> | |
90 | <groupId>org.apache.maven.plugins</groupId> | |
91 | <artifactId>maven-jar-plugin</artifactId> | |
92 | <version>2.4</version> | |
93 | 38 | <executions> |
39 | <!-- First compile without without annotation processing, then | |
40 | compile again, see https://stackoverflow.com/a/36250332 --> | |
94 | 41 | <execution> |
95 | <phase>package</phase> | |
42 | <id>default-compile</id> | |
43 | <configuration> | |
44 | <compilerArgument>-proc:none</compilerArgument> | |
45 | </configuration> | |
46 | </execution> | |
47 | <execution> | |
48 | <id>compile-project</id> | |
49 | <phase>compile</phase> | |
96 | 50 | <goals> |
97 | <goal>jar</goal> | |
51 | <goal>compile</goal> | |
98 | 52 | </goals> |
99 | 53 | </execution> |
100 | 54 | </executions> |
101 | <configuration> | |
102 | <archive> | |
103 | <addMavenDescriptor>false</addMavenDescriptor> | |
104 | </archive> | |
105 | </configuration> | |
106 | </plugin> | |
107 | <plugin> | |
108 | <groupId>org.apache.maven.plugins</groupId> | |
109 | <artifactId>maven-source-plugin</artifactId> | |
110 | <version>2.1.2</version> | |
111 | <executions> | |
112 | <execution> | |
113 | <id>attach-sources</id> | |
114 | <phase>verify</phase> | |
115 | <goals> | |
116 | <goal>jar</goal> | |
117 | </goals> | |
118 | </execution> | |
119 | </executions> | |
120 | </plugin> | |
121 | <plugin> | |
122 | <groupId>org.apache.maven.plugins</groupId> | |
123 | <artifactId>maven-javadoc-plugin</artifactId> | |
124 | <version>2.8.1</version> | |
125 | <executions> | |
126 | <execution> | |
127 | <id>attach-javadocs</id> | |
128 | <goals> | |
129 | <goal>jar</goal> | |
130 | </goals> | |
131 | </execution> | |
132 | </executions> | |
133 | <configuration> | |
134 | <links> | |
135 | <link>http://download.oracle.com/javase/1.5.0/docs/api/</link> | |
136 | </links> | |
137 | <version>true</version> | |
138 | <show>public</show> | |
139 | </configuration> | |
140 | </plugin> | |
141 | <plugin> | |
142 | <groupId>org.apache.maven.plugins</groupId> | |
143 | <artifactId>maven-eclipse-plugin</artifactId> | |
144 | <version>2.9</version> | |
145 | <configuration> | |
146 | <downloadSources>true</downloadSources> | |
147 | <downloadJavadocs>true</downloadJavadocs> | |
148 | <workspace> | |
149 | ../eclipse-ws/ | |
150 | </workspace> | |
151 | <workspaceCodeStylesURL> | |
152 | file:///${basedir}/../lib/gson-formatting-styles.xml | |
153 | </workspaceCodeStylesURL> | |
154 | </configuration> | |
155 | </plugin> | |
156 | <plugin> | |
157 | <groupId>org.apache.maven.plugins</groupId> | |
158 | <artifactId>maven-release-plugin</artifactId> | |
159 | <!-- version>2.3.2</version --> | |
160 | <configuration> | |
161 | <arguments>-DenableCiProfile=true</arguments> | |
162 | <tagBase>https://google-gson.googlecode.com/svn/tags/</tagBase> | |
163 | </configuration> | |
164 | 55 | </plugin> |
165 | 56 | </plugins> |
57 | ||
58 | <pluginManagement> | |
59 | <plugins> | |
60 | <plugin> | |
61 | <groupId>org.apache.maven.plugins</groupId> | |
62 | <artifactId>maven-deploy-plugin</artifactId> | |
63 | <version>3.0.0-M2</version> | |
64 | <configuration> | |
65 | <!-- Not deployed --> | |
66 | <skip>true</skip> | |
67 | </configuration> | |
68 | </plugin> | |
69 | </plugins> | |
70 | </pluginManagement> | |
166 | 71 | </build> |
72 | ||
167 | 73 | <developers> |
168 | 74 | <developer> |
169 | 75 | <name>Inderjeet Singh</name> |
52 | 52 | System.out.println("Generating type adapter: " + typeAdapterName + " in " + sourceFile.getName()); |
53 | 53 | |
54 | 54 | JavaWriter writer = new JavaWriter(sourceFile.openWriter()); |
55 | writer.addPackage(CodeGen.getPackage(type).getQualifiedName().toString()); | |
56 | writer.beginType(typeAdapterName, "class", FINAL, null); | |
57 | writer.endType(); | |
58 | writer.close(); | |
55 | try { | |
56 | writer.addPackage(CodeGen.getPackage(type).getQualifiedName().toString()); | |
57 | writer.beginType(typeAdapterName, "class", FINAL, null); | |
58 | writer.endType(); | |
59 | } finally { | |
60 | writer.close(); | |
61 | } | |
59 | 62 | } |
60 | 63 | } |
0 | # android-proguard-example | |
1 | ||
2 | Example Android project showing how to properly configure [ProGuard](https://www.guardsquare.com/proguard). | |
3 | ProGuard is a tool for 'shrinking' and obfuscating compiled classes. It can rename methods and fields, | |
4 | or remove them if they appear to be unused. This can cause issues for Gson which uses Java reflection to | |
5 | access the fields of a class. It is necessary to configure ProGuard to make sure that Gson works correctly. | |
6 | ||
7 | Also have a look at the [ProGuard manual](https://www.guardsquare.com/manual/configuration/usage#keepoverview) | |
8 | for more details on how ProGuard can be configured. | |
9 | ||
10 | The R8 code shrinker uses the same rule format as ProGuard, but there are differences between these two | |
11 | tools. Have a look at R8's Compatibility FAQ, and especially at the [Gson section](https://r8.googlesource.com/r8/+/refs/heads/main/compatibility-faq.md#gson). |
0 | # extras | |
1 | ||
2 | This Maven module contains the source code for supplementary Gson features which | |
3 | are not included by default. | |
4 | ||
5 | The artifacts created by this module are currently not deployed to Maven Central. |
0 | 0 | <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> |
1 | 1 | <modelVersion>4.0.0</modelVersion> |
2 | <groupId>com.google.code.gson</groupId> | |
2 | <parent> | |
3 | <groupId>com.google.code.gson</groupId> | |
4 | <artifactId>gson-parent</artifactId> | |
5 | <version>2.9.0</version> | |
6 | </parent> | |
7 | ||
3 | 8 | <artifactId>gson-extras</artifactId> |
4 | <packaging>jar</packaging> | |
5 | <version>1.0-SNAPSHOT</version> | |
6 | 9 | <inceptionYear>2008</inceptionYear> |
7 | 10 | <name>Gson Extras</name> |
8 | <parent> | |
9 | <groupId>org.sonatype.oss</groupId> | |
10 | <artifactId>oss-parent</artifactId> | |
11 | <version>9</version> | |
12 | </parent> | |
13 | <url>http://code.google.com/p/google-gson/</url> | |
14 | 11 | <description>Google Gson grab bag of utilities, type adapters, etc.</description> |
15 | <properties> | |
16 | <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> | |
17 | </properties> | |
12 | ||
18 | 13 | <licenses> |
19 | 14 | <license> |
20 | <name>The Apache Software License, Version 2.0</name> | |
21 | <url>http://www.apache.org/licenses/LICENSE-2.0.txt</url> | |
22 | <distribution>repo</distribution> | |
15 | <name>Apache-2.0</name> | |
16 | <url>https://www.apache.org/licenses/LICENSE-2.0.txt</url> | |
23 | 17 | </license> |
24 | 18 | </licenses> |
25 | <scm> | |
26 | <connection>scm:svn:http://google-gson.googlecode.com/svn/trunk/extras</connection> | |
27 | <developerConnection>scm:svn:https://google-gson.googlecode.com/svn/trunk/extras</developerConnection> | |
28 | <url>http://google-gson.codegoogle.com/svn/trunk/extras</url> | |
29 | </scm> | |
30 | <issueManagement> | |
31 | <system>Google Code Issue Tracking</system> | |
32 | <url>http://code.google.com/p/google-gson/issues/list</url> | |
33 | </issueManagement> | |
19 | ||
34 | 20 | <organization> |
35 | 21 | <name>Google, Inc.</name> |
36 | <url>http://www.google.com</url> | |
22 | <url>https://www.google.com</url> | |
37 | 23 | </organization> |
24 | ||
38 | 25 | <dependencies> |
39 | 26 | <dependency> |
40 | 27 | <groupId>com.google.code.gson</groupId> |
41 | 28 | <artifactId>gson</artifactId> |
42 | <version>2.7</version> | |
43 | <scope>compile</scope> | |
29 | <version>${project.parent.version}</version> | |
44 | 30 | </dependency> |
45 | 31 | <dependency> |
46 | 32 | <groupId>javax.annotation</groupId> |
50 | 36 | <dependency> |
51 | 37 | <groupId>junit</groupId> |
52 | 38 | <artifactId>junit</artifactId> |
53 | <version>4.13.1</version> | |
54 | 39 | <scope>test</scope> |
55 | 40 | </dependency> |
56 | 41 | </dependencies> |
57 | <profiles> | |
58 | <!-- Activate PGP signing only when performing a release --> | |
59 | <profile> | |
60 | <id>release-sign-artifacts</id> | |
61 | <activation> | |
62 | <property> | |
63 | <name>performRelease</name> | |
64 | <value>true</value> | |
65 | </property> | |
66 | </activation> | |
67 | <build> | |
68 | <plugins> | |
69 | <plugin> | |
70 | <groupId>org.apache.maven.plugins</groupId> | |
71 | <artifactId>maven-gpg-plugin</artifactId> | |
72 | <version>1.5</version> | |
73 | <executions> | |
74 | <execution> | |
75 | <id>sign-artifacts</id> | |
76 | <phase>verify</phase> | |
77 | <goals> | |
78 | <goal>sign</goal> | |
79 | </goals> | |
80 | </execution> | |
81 | </executions> | |
82 | </plugin> | |
83 | </plugins> | |
84 | </build> | |
85 | </profile> | |
86 | </profiles> | |
42 | ||
87 | 43 | <build> |
88 | <defaultGoal>package</defaultGoal> | |
89 | <plugins> | |
90 | <plugin> | |
91 | <groupId>org.apache.maven.plugins</groupId> | |
92 | <artifactId>maven-compiler-plugin</artifactId> | |
93 | <version>3.5.1</version> | |
94 | <configuration> | |
95 | <source>1.6</source> | |
96 | <target>1.6</target> | |
97 | </configuration> | |
98 | </plugin> | |
99 | <plugin> | |
100 | <groupId>org.apache.maven.plugins</groupId> | |
101 | <artifactId>maven-jar-plugin</artifactId> | |
102 | <version>3.0.2</version> | |
103 | <executions> | |
104 | <execution> | |
105 | <phase>package</phase> | |
106 | <goals> | |
107 | <goal>jar</goal> | |
108 | </goals> | |
109 | </execution> | |
110 | </executions> | |
111 | <configuration> | |
112 | <archive> | |
113 | <addMavenDescriptor>false</addMavenDescriptor> | |
114 | </archive> | |
115 | </configuration> | |
116 | </plugin> | |
117 | <plugin> | |
118 | <groupId>org.apache.maven.plugins</groupId> | |
119 | <artifactId>maven-source-plugin</artifactId> | |
120 | <version>3.0.1</version> | |
121 | <executions> | |
122 | <execution> | |
123 | <id>attach-sources</id> | |
124 | <phase>verify</phase> | |
125 | <goals> | |
126 | <goal>jar</goal> | |
127 | </goals> | |
128 | </execution> | |
129 | </executions> | |
130 | </plugin> | |
131 | <plugin> | |
132 | <groupId>org.apache.maven.plugins</groupId> | |
133 | <artifactId>maven-javadoc-plugin</artifactId> | |
134 | <version>2.10.4</version> | |
135 | <executions> | |
136 | <execution> | |
137 | <id>attach-javadocs</id> | |
138 | <goals> | |
139 | <goal>jar</goal> | |
140 | </goals> | |
141 | </execution> | |
142 | </executions> | |
143 | <configuration> | |
144 | <links> | |
145 | <link>http://download.oracle.com/javase/1.5.0/docs/api/</link> | |
146 | </links> | |
147 | <version>true</version> | |
148 | <show>public</show> | |
149 | </configuration> | |
150 | </plugin> | |
151 | <plugin> | |
152 | <groupId>org.apache.maven.plugins</groupId> | |
153 | <artifactId>maven-eclipse-plugin</artifactId> | |
154 | <version>2.10</version> | |
155 | <configuration> | |
156 | <downloadSources>true</downloadSources> | |
157 | <downloadJavadocs>true</downloadJavadocs> | |
158 | <workspace> | |
159 | ../eclipse-ws/ | |
160 | </workspace> | |
161 | <workspaceCodeStylesURL> | |
162 | file:///${basedir}/../lib/gson-formatting-styles.xml | |
163 | </workspaceCodeStylesURL> | |
164 | </configuration> | |
165 | </plugin> | |
166 | <plugin> | |
167 | <groupId>org.apache.maven.plugins</groupId> | |
168 | <artifactId>maven-release-plugin</artifactId> | |
169 | <!-- version>2.3.2</version --> | |
170 | <configuration> | |
171 | <arguments>-DenableCiProfile=true</arguments> | |
172 | <tagBase>https://google-gson.googlecode.com/svn/tags/</tagBase> | |
173 | </configuration> | |
174 | </plugin> | |
175 | </plugins> | |
44 | <pluginManagement> | |
45 | <plugins> | |
46 | <plugin> | |
47 | <groupId>org.apache.maven.plugins</groupId> | |
48 | <artifactId>maven-deploy-plugin</artifactId> | |
49 | <version>3.0.0-M2</version> | |
50 | <configuration> | |
51 | <!-- Currently not deployed --> | |
52 | <skip>true</skip> | |
53 | </configuration> | |
54 | </plugin> | |
55 | </plugins> | |
56 | </pluginManagement> | |
176 | 57 | </build> |
58 | ||
177 | 59 | <developers> |
178 | 60 | <developer> |
179 | 61 | <name>Inderjeet Singh</name> |
+1
-2
44 | 44 | collection.add(new Event("GREETINGS", "guest")); |
45 | 45 | String json = gson.toJson(collection); |
46 | 46 | System.out.println("Using Gson.toJson() on a raw collection: " + json); |
47 | JsonParser parser = new JsonParser(); | |
48 | JsonArray array = parser.parse(json).getAsJsonArray(); | |
47 | JsonArray array = JsonParser.parseString(json).getAsJsonArray(); | |
49 | 48 | String message = gson.fromJson(array.get(0), String.class); |
50 | 49 | int number = gson.fromJson(array.get(1), int.class); |
51 | 50 | Event event = gson.fromJson(array.get(2), Event.class); |
46 | 46 | |
47 | 47 | public GraphAdapterBuilder() { |
48 | 48 | this.instanceCreators = new HashMap<Type, InstanceCreator<?>>(); |
49 | this.constructorConstructor = new ConstructorConstructor(instanceCreators); | |
49 | this.constructorConstructor = new ConstructorConstructor(instanceCreators, true); | |
50 | 50 | } |
51 | 51 | public GraphAdapterBuilder addType(Type type) { |
52 | 52 | final ObjectConstructor<?> objectConstructor = constructorConstructor.get(TypeToken.get(type)); |
53 | 53 | InstanceCreator<Object> instanceCreator = new InstanceCreator<Object>() { |
54 | @Override | |
54 | 55 | public Object createInstance(Type type) { |
55 | 56 | return objectConstructor.construct(); |
56 | 57 | } |
82 | 83 | this.instanceCreators = instanceCreators; |
83 | 84 | } |
84 | 85 | |
86 | @Override | |
85 | 87 | public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type) { |
86 | 88 | if (!instanceCreators.containsKey(type.getType())) { |
87 | 89 | return null; |
211 | 213 | * that is only when we've called back into Gson to deserialize a tree. |
212 | 214 | */ |
213 | 215 | @SuppressWarnings("unchecked") |
216 | @Override | |
214 | 217 | public Object createInstance(Type type) { |
215 | 218 | Graph graph = graphThreadLocal.get(); |
216 | 219 | if (graph == null || graph.nextCreate == null) { |
27 | 27 | * after it has been deserialized from Json. |
28 | 28 | * Here is an example of how this annotation is used: |
29 | 29 | * <p>Here is an example of how this annotation is used: |
30 | * <p><pre> | |
31 | * @Intercept(postDeserialize=UserValidator.class) | |
30 | * <pre> | |
31 | * @Intercept(postDeserialize=UserValidator.class) | |
32 | 32 | * public class User { |
33 | 33 | * String name; |
34 | 34 | * String password; |
46 | 46 | * } |
47 | 47 | * } |
48 | 48 | * } |
49 | * </pre></p> | |
49 | * </pre> | |
50 | 50 | * |
51 | 51 | * @author Inderjeet Singh |
52 | 52 | */ |
26 | 26 | import com.google.gson.JsonPrimitive; |
27 | 27 | import com.google.gson.TypeAdapter; |
28 | 28 | import com.google.gson.TypeAdapterFactory; |
29 | import com.google.gson.internal.Streams; | |
30 | 29 | import com.google.gson.reflect.TypeToken; |
31 | 30 | import com.google.gson.stream.JsonReader; |
32 | 31 | import com.google.gson.stream.JsonWriter; |
203 | 202 | return registerSubtype(type, type.getSimpleName()); |
204 | 203 | } |
205 | 204 | |
205 | @Override | |
206 | 206 | public <R> TypeAdapter<R> create(Gson gson, TypeToken<R> type) { |
207 | 207 | if (type.getRawType() != baseType) { |
208 | 208 | return null; |
209 | 209 | } |
210 | 210 | |
211 | final TypeAdapter<JsonElement> jsonElementAdapter = gson.getAdapter(JsonElement.class); | |
211 | 212 | final Map<String, TypeAdapter<?>> labelToDelegate |
212 | 213 | = new LinkedHashMap<String, TypeAdapter<?>>(); |
213 | 214 | final Map<Class<?>, TypeAdapter<?>> subtypeToDelegate |
220 | 221 | |
221 | 222 | return new TypeAdapter<R>() { |
222 | 223 | @Override public R read(JsonReader in) throws IOException { |
223 | JsonElement jsonElement = Streams.parse(in); | |
224 | JsonElement jsonElement = jsonElementAdapter.read(in); | |
224 | 225 | JsonElement labelJsonElement; |
225 | 226 | if (maintainType) { |
226 | 227 | labelJsonElement = jsonElement.getAsJsonObject().get(typeFieldName); |
254 | 255 | JsonObject jsonObject = delegate.toJsonTree(value).getAsJsonObject(); |
255 | 256 | |
256 | 257 | if (maintainType) { |
257 | Streams.write(jsonObject, out); | |
258 | jsonElementAdapter.write(out, jsonObject); | |
258 | 259 | return; |
259 | 260 | } |
260 | 261 | |
269 | 270 | for (Map.Entry<String, JsonElement> e : jsonObject.entrySet()) { |
270 | 271 | clone.add(e.getKey(), e.getValue()); |
271 | 272 | } |
272 | Streams.write(clone, out); | |
273 | jsonElementAdapter.write(out, clone); | |
273 | 274 | } |
274 | 275 | }.nullSafe(); |
275 | 276 | } |
15 | 15 | |
16 | 16 | package com.google.gson.graph; |
17 | 17 | |
18 | import com.google.gson.Gson; | |
19 | import com.google.gson.GsonBuilder; | |
20 | import com.google.gson.reflect.TypeToken; | |
18 | import static org.junit.Assert.assertEquals; | |
19 | import static org.junit.Assert.assertSame; | |
20 | ||
21 | 21 | import java.lang.reflect.Type; |
22 | 22 | import java.util.ArrayList; |
23 | 23 | import java.util.Collections; |
24 | 24 | import java.util.List; |
25 | import junit.framework.TestCase; | |
26 | 25 | |
27 | public final class GraphAdapterBuilderTest extends TestCase { | |
26 | import org.junit.Test; | |
27 | ||
28 | import com.google.gson.Gson; | |
29 | import com.google.gson.GsonBuilder; | |
30 | import com.google.gson.reflect.TypeToken; | |
31 | ||
32 | public final class GraphAdapterBuilderTest { | |
33 | @Test | |
28 | 34 | public void testSerialization() { |
29 | 35 | Roshambo rock = new Roshambo("ROCK"); |
30 | 36 | Roshambo scissors = new Roshambo("SCISSORS"); |
45 | 51 | gson.toJson(rock).replace('"', '\'')); |
46 | 52 | } |
47 | 53 | |
54 | @Test | |
48 | 55 | public void testDeserialization() { |
49 | 56 | String json = "{'0x1':{'name':'ROCK','beats':'0x2'}," + |
50 | 57 | "'0x2':{'name':'SCISSORS','beats':'0x3'}," + |
65 | 72 | assertSame(rock, paper.beats); |
66 | 73 | } |
67 | 74 | |
68 | public void testSerializationDirectSelfReference() { | |
69 | Roshambo suicide = new Roshambo("SUICIDE"); | |
70 | suicide.beats = suicide; | |
71 | ||
72 | GsonBuilder gsonBuilder = new GsonBuilder(); | |
73 | new GraphAdapterBuilder() | |
74 | .addType(Roshambo.class) | |
75 | .registerOn(gsonBuilder); | |
76 | Gson gson = gsonBuilder.create(); | |
77 | ||
78 | assertEquals("{'0x1':{'name':'SUICIDE','beats':'0x1'}}", | |
79 | gson.toJson(suicide).replace('"', '\'')); | |
80 | } | |
81 | ||
75 | @Test | |
82 | 76 | public void testDeserializationDirectSelfReference() { |
83 | 77 | String json = "{'0x1':{'name':'SUICIDE','beats':'0x1'}}"; |
84 | 78 | |
93 | 87 | assertSame(suicide, suicide.beats); |
94 | 88 | } |
95 | 89 | |
90 | @Test | |
96 | 91 | public void testSerializeListOfLists() { |
97 | 92 | Type listOfListsType = new TypeToken<List<List<?>>>() {}.getType(); |
98 | 93 | Type listOfAnyType = new TypeToken<List<?>>() {}.getType(); |
112 | 107 | assertEquals("{'0x1':['0x1','0x2'],'0x2':[]}", json.replace('"', '\'')); |
113 | 108 | } |
114 | 109 | |
110 | @Test | |
115 | 111 | public void testDeserializeListOfLists() { |
116 | 112 | Type listOfAnyType = new TypeToken<List<?>>() {}.getType(); |
117 | 113 | Type listOfListsType = new TypeToken<List<List<?>>>() {}.getType(); |
129 | 125 | assertEquals(Collections.emptyList(), listOfLists.get(1)); |
130 | 126 | } |
131 | 127 | |
128 | @Test | |
132 | 129 | public void testSerializationWithMultipleTypes() { |
133 | 130 | Company google = new Company("Google"); |
134 | 131 | new Employee("Jesse", google); |
147 | 144 | gson.toJson(google).replace('"', '\'')); |
148 | 145 | } |
149 | 146 | |
147 | @Test | |
150 | 148 | public void testDeserializationWithMultipleTypes() { |
151 | 149 | GsonBuilder gsonBuilder = new GsonBuilder(); |
152 | 150 | new GraphAdapterBuilder() |
0 | #Fri Apr 27 17:41:01 PDT 2018 | |
1 | distributionBase=GRADLE_USER_HOME | |
2 | distributionPath=wrapper/dists | |
3 | zipStoreBase=GRADLE_USER_HOME | |
4 | zipStorePath=wrapper/dists | |
5 | distributionUrl=https\://services.gradle.org/distributions/gradle-4.4-all.zip |
0 | Gson is a Java library that can be used to convert Java Objects into their | |
1 | JSON representation. It can also be used to convert a JSON string to an | |
2 | equivalent Java object. Gson can work with arbitrary Java objects including | |
3 | pre-existing objects that you do not have source-code of. | |
4 | ||
5 | Complete Gson documentation is available at its project page | |
6 | https://github.com/google/gson |
0 | # gson | |
1 | ||
2 | This Maven module contains the Gson source code. The artifacts created by this module | |
3 | are deployed to Maven Central under the coordinates `com.google.code.gson:gson`. |
2 | 2 | Bundle-Description: ${project.description} |
3 | 3 | Bundle-Vendor: Google Gson Project |
4 | 4 | Bundle-ContactAddress: ${project.parent.url} |
5 | Bundle-RequiredExecutionEnvironment: JavaSE-1.6, JavaSE-1.7, JavaSE-1.8 | |
6 | Require-Capability: osgi.ee;filter:="(&(osgi.ee=JavaSE)(version=1.6))" | |
5 | Bundle-RequiredExecutionEnvironment: JavaSE-1.7, JavaSE-1.8 | |
6 | Require-Capability: osgi.ee;filter:="(&(osgi.ee=JavaSE)(version=1.7))" | |
7 | ||
8 | # Optional dependency for JDK's sun.misc.Unsafe | |
9 | # https://bnd.bndtools.org/chapters/920-faq.html#remove-unwanted-imports- | |
10 | Import-Package: sun.misc;resolution:=optional, * | |
7 | 11 | |
8 | 12 | -removeheaders: Private-Package |
9 | 13 |
3 | 3 | group = 'com.google.code.gson' |
4 | 4 | version = '2.8.6-SNAPSHOT' |
5 | 5 | |
6 | sourceCompatibility = 1.6 | |
7 | targetCompatibility = 1.6 | |
6 | sourceCompatibility = 1.7 | |
7 | targetCompatibility = 1.7 | |
8 | 8 | |
9 | 9 | sourceSets.main.java.exclude("**/module-info.java") |
10 | 10 | dependencies { |
3 | 3 | <parent> |
4 | 4 | <groupId>com.google.code.gson</groupId> |
5 | 5 | <artifactId>gson-parent</artifactId> |
6 | <version>2.8.8</version> | |
6 | <version>2.9.0</version> | |
7 | 7 | </parent> |
8 | 8 | |
9 | 9 | <artifactId>gson</artifactId> |
10 | 10 | <name>Gson</name> |
11 | ||
12 | <licenses> | |
13 | <license> | |
14 | <name>Apache-2.0</name> | |
15 | <url>https://www.apache.org/licenses/LICENSE-2.0.txt</url> | |
16 | </license> | |
17 | </licenses> | |
11 | 18 | |
12 | 19 | <dependencies> |
13 | 20 | <dependency> |
15 | 22 | <artifactId>junit</artifactId> |
16 | 23 | <scope>test</scope> |
17 | 24 | </dependency> |
18 | <dependency> | |
19 | <groupId>com.github.wvengen</groupId> | |
20 | <artifactId>proguard-maven-plugin</artifactId> | |
21 | <version>2.4.0</version> | |
22 | <scope>test</scope> | |
23 | </dependency> | |
24 | 25 | </dependencies> |
25 | ||
26 | ||
26 | 27 | <build> |
27 | 28 | <plugins> |
28 | 29 | <plugin> |
29 | 30 | <groupId>org.apache.maven.plugins</groupId> |
31 | <artifactId>maven-compiler-plugin</artifactId> | |
32 | <executions> | |
33 | <execution> | |
34 | <id>default-compile</id> | |
35 | <configuration> | |
36 | <excludes> | |
37 | <!-- module-info.java is compiled using ModiTect --> | |
38 | <exclude>module-info.java</exclude> | |
39 | </excludes> | |
40 | </configuration> | |
41 | </execution> | |
42 | </executions> | |
43 | </plugin> | |
44 | <!-- Note: Javadoc plugin has to be run in combination with >= `package` | |
45 | phase, e.g. `mvn package javadoc:javadoc`, otherwise it fails with | |
46 | "Aggregator report contains named and unnamed modules" --> | |
47 | <plugin> | |
48 | <groupId>org.apache.maven.plugins</groupId> | |
49 | <artifactId>maven-surefire-plugin</artifactId> | |
50 | <version>3.0.0-M5</version> | |
51 | <configuration> | |
52 | <!-- Deny illegal access, this is required for ReflectionAccessTest --> | |
53 | <!-- Requires Java >= 9; Important: In case future Java versions | |
54 | don't support this flag anymore, don't remove it unless CI also runs with | |
55 | that Java version. Ideally would use toolchain to specify that this should | |
56 | run with e.g. Java 11, but Maven toolchain requirements (unlike Gradle ones) | |
57 | don't seem to be portable (every developer would have to set up toolchain | |
58 | configuration locally). --> | |
59 | <argLine>--illegal-access=deny</argLine> | |
60 | </configuration> | |
61 | </plugin> | |
62 | <plugin> | |
63 | <groupId>org.apache.maven.plugins</groupId> | |
30 | 64 | <artifactId>maven-javadoc-plugin</artifactId> |
31 | 65 | <configuration> |
32 | <includePackageNames>com.google.gson</includePackageNames> | |
33 | 66 | <excludePackageNames>com.google.gson.internal:com.google.gson.internal.bind</excludePackageNames> |
34 | <links> | |
35 | <link>https://docs.oracle.com/javase/6/docs/api/</link> | |
36 | </links> | |
37 | </configuration> | |
67 | </configuration> | |
68 | </plugin> | |
69 | <!-- Add module-info to JAR, see https://github.com/moditect/moditect#adding-module-descriptors-to-existing-jar-files --> | |
70 | <!-- Uses ModiTect instead of separate maven-compiler-plugin executions | |
71 | for better Eclipse IDE support, see https://github.com/eclipse-m2e/m2e-core/issues/393 --> | |
72 | <plugin> | |
73 | <groupId>org.moditect</groupId> | |
74 | <artifactId>moditect-maven-plugin</artifactId> | |
75 | <version>1.0.0.RC2</version> | |
76 | <executions> | |
77 | <execution> | |
78 | <id>add-module-info</id> | |
79 | <phase>package</phase> | |
80 | <goals> | |
81 | <goal>add-module-info</goal> | |
82 | </goals> | |
83 | <configuration> | |
84 | <jvmVersion>9</jvmVersion> | |
85 | <module> | |
86 | <moduleInfoFile>${project.build.sourceDirectory}/module-info.java</moduleInfoFile> | |
87 | </module> | |
88 | </configuration> | |
89 | </execution> | |
90 | </executions> | |
38 | 91 | </plugin> |
39 | 92 | <plugin> |
40 | 93 | <groupId>biz.aQute.bnd</groupId> |
41 | 94 | <artifactId>bnd-maven-plugin</artifactId> |
42 | <version>5.3.0</version> | |
95 | <version>6.1.0</version> | |
43 | 96 | <executions> |
44 | 97 | <execution> |
45 | 98 | <goals> |
53 | 106 | <artifactId>maven-jar-plugin</artifactId> |
54 | 107 | <configuration> |
55 | 108 | <archive> |
109 | <!-- Use existing manifest generated by BND plugin --> | |
56 | 110 | <manifestFile>${project.build.outputDirectory}/META-INF/MANIFEST.MF</manifestFile> |
57 | 111 | </archive> |
58 | 112 | </configuration> |
103 | 157 | <plugin> |
104 | 158 | <groupId>com.github.wvengen</groupId> |
105 | 159 | <artifactId>proguard-maven-plugin</artifactId> |
160 | <version>2.5.3</version> | |
106 | 161 | <executions> |
107 | 162 | <execution> |
108 | 163 | <phase>process-test-classes</phase> |
109 | <goals><goal>proguard</goal></goals> | |
110 | </execution> | |
111 | </executions> | |
112 | <configuration> | |
113 | <proguardVersion>6.2.2</proguardVersion> | |
164 | <goals> | |
165 | <goal>proguard</goal> | |
166 | </goals> | |
167 | </execution> | |
168 | </executions> | |
169 | <configuration> | |
114 | 170 | <obfuscate>true</obfuscate> |
115 | 171 | <injar>test-classes-obfuscated-injar</injar> |
116 | 172 | <outjar>test-classes-obfuscated-outjar</outjar> |
121 | 177 | <lib>${java.home}/jmods/java.base.jmod</lib> |
122 | 178 | </libs> |
123 | 179 | </configuration> |
124 | <dependencies> | |
125 | <dependency> | |
126 | <groupId>net.sf.proguard</groupId> | |
127 | <artifactId>proguard-base</artifactId> | |
128 | <version>6.2.2</version> | |
129 | <scope>runtime</scope> | |
130 | </dependency> | |
131 | </dependencies> | |
132 | 180 | </plugin> |
133 | 181 | <plugin> |
134 | 182 | <artifactId>maven-resources-plugin</artifactId> |
0 | /* | |
1 | * Copyright (C) 2008 Google Inc. | |
2 | * | |
3 | * Licensed under the Apache License, Version 2.0 (the "License"); | |
4 | * you may not use this file except in compliance with the License. | |
5 | * You may obtain a copy of the License at | |
6 | * | |
7 | * http://www.apache.org/licenses/LICENSE-2.0 | |
8 | * | |
9 | * Unless required by applicable law or agreed to in writing, software | |
10 | * distributed under the License is distributed on an "AS IS" BASIS, | |
11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
12 | * See the License for the specific language governing permissions and | |
13 | * limitations under the License. | |
14 | */ | |
15 | ||
16 | package com.google.gson; | |
17 | ||
18 | import java.io.IOException; | |
19 | import java.sql.Timestamp; | |
20 | import java.text.DateFormat; | |
21 | import java.text.ParseException; | |
22 | import java.text.ParsePosition; | |
23 | import java.text.SimpleDateFormat; | |
24 | import java.util.ArrayList; | |
25 | import java.util.Date; | |
26 | import java.util.List; | |
27 | import java.util.Locale; | |
28 | ||
29 | import com.google.gson.internal.JavaVersion; | |
30 | import com.google.gson.internal.PreJava9DateFormatProvider; | |
31 | import com.google.gson.internal.bind.util.ISO8601Utils; | |
32 | import com.google.gson.stream.JsonReader; | |
33 | import com.google.gson.stream.JsonToken; | |
34 | import com.google.gson.stream.JsonWriter; | |
35 | ||
36 | /** | |
37 | * This type adapter supports three subclasses of date: Date, Timestamp, and | |
38 | * java.sql.Date. | |
39 | * | |
40 | * @author Inderjeet Singh | |
41 | * @author Joel Leitch | |
42 | */ | |
43 | final class DefaultDateTypeAdapter extends TypeAdapter<Date> { | |
44 | ||
45 | private static final String SIMPLE_NAME = "DefaultDateTypeAdapter"; | |
46 | ||
47 | private final Class<? extends Date> dateType; | |
48 | ||
49 | /** | |
50 | * List of 1 or more different date formats used for de-serialization attempts. | |
51 | * The first of them is used for serialization as well. | |
52 | */ | |
53 | private final List<DateFormat> dateFormats = new ArrayList<DateFormat>(); | |
54 | ||
55 | DefaultDateTypeAdapter(Class<? extends Date> dateType) { | |
56 | this.dateType = verifyDateType(dateType); | |
57 | dateFormats.add(DateFormat.getDateTimeInstance(DateFormat.DEFAULT, DateFormat.DEFAULT, Locale.US)); | |
58 | if (!Locale.getDefault().equals(Locale.US)) { | |
59 | dateFormats.add(DateFormat.getDateTimeInstance(DateFormat.DEFAULT, DateFormat.DEFAULT)); | |
60 | } | |
61 | if (JavaVersion.isJava9OrLater()) { | |
62 | dateFormats.add(PreJava9DateFormatProvider.getUSDateTimeFormat(DateFormat.DEFAULT, DateFormat.DEFAULT)); | |
63 | } | |
64 | } | |
65 | ||
66 | DefaultDateTypeAdapter(Class<? extends Date> dateType, String datePattern) { | |
67 | this.dateType = verifyDateType(dateType); | |
68 | dateFormats.add(new SimpleDateFormat(datePattern, Locale.US)); | |
69 | if (!Locale.getDefault().equals(Locale.US)) { | |
70 | dateFormats.add(new SimpleDateFormat(datePattern)); | |
71 | } | |
72 | } | |
73 | ||
74 | DefaultDateTypeAdapter(Class<? extends Date> dateType, int style) { | |
75 | this.dateType = verifyDateType(dateType); | |
76 | dateFormats.add(DateFormat.getDateInstance(style, Locale.US)); | |
77 | if (!Locale.getDefault().equals(Locale.US)) { | |
78 | dateFormats.add(DateFormat.getDateInstance(style)); | |
79 | } | |
80 | if (JavaVersion.isJava9OrLater()) { | |
81 | dateFormats.add(PreJava9DateFormatProvider.getUSDateFormat(style)); | |
82 | } | |
83 | } | |
84 | ||
85 | public DefaultDateTypeAdapter(int dateStyle, int timeStyle) { | |
86 | this(Date.class, dateStyle, timeStyle); | |
87 | } | |
88 | ||
89 | public DefaultDateTypeAdapter(Class<? extends Date> dateType, int dateStyle, int timeStyle) { | |
90 | this.dateType = verifyDateType(dateType); | |
91 | dateFormats.add(DateFormat.getDateTimeInstance(dateStyle, timeStyle, Locale.US)); | |
92 | if (!Locale.getDefault().equals(Locale.US)) { | |
93 | dateFormats.add(DateFormat.getDateTimeInstance(dateStyle, timeStyle)); | |
94 | } | |
95 | if (JavaVersion.isJava9OrLater()) { | |
96 | dateFormats.add(PreJava9DateFormatProvider.getUSDateTimeFormat(dateStyle, timeStyle)); | |
97 | } | |
98 | } | |
99 | ||
100 | private static Class<? extends Date> verifyDateType(Class<? extends Date> dateType) { | |
101 | if ( dateType != Date.class && dateType != java.sql.Date.class && dateType != Timestamp.class ) { | |
102 | throw new IllegalArgumentException("Date type must be one of " + Date.class + ", " + Timestamp.class + ", or " + java.sql.Date.class + " but was " + dateType); | |
103 | } | |
104 | return dateType; | |
105 | } | |
106 | ||
107 | // These methods need to be synchronized since JDK DateFormat classes are not thread-safe | |
108 | // See issue 162 | |
109 | @Override | |
110 | public void write(JsonWriter out, Date value) throws IOException { | |
111 | if (value == null) { | |
112 | out.nullValue(); | |
113 | return; | |
114 | } | |
115 | synchronized(dateFormats) { | |
116 | String dateFormatAsString = dateFormats.get(0).format(value); | |
117 | out.value(dateFormatAsString); | |
118 | } | |
119 | } | |
120 | ||
121 | @Override | |
122 | public Date read(JsonReader in) throws IOException { | |
123 | if (in.peek() == JsonToken.NULL) { | |
124 | in.nextNull(); | |
125 | return null; | |
126 | } | |
127 | Date date = deserializeToDate(in.nextString()); | |
128 | if (dateType == Date.class) { | |
129 | return date; | |
130 | } else if (dateType == Timestamp.class) { | |
131 | return new Timestamp(date.getTime()); | |
132 | } else if (dateType == java.sql.Date.class) { | |
133 | return new java.sql.Date(date.getTime()); | |
134 | } else { | |
135 | // This must never happen: dateType is guarded in the primary constructor | |
136 | throw new AssertionError(); | |
137 | } | |
138 | } | |
139 | ||
140 | private Date deserializeToDate(String s) { | |
141 | synchronized (dateFormats) { | |
142 | for (DateFormat dateFormat : dateFormats) { | |
143 | try { | |
144 | return dateFormat.parse(s); | |
145 | } catch (ParseException ignored) {} | |
146 | } | |
147 | try { | |
148 | return ISO8601Utils.parse(s, new ParsePosition(0)); | |
149 | } catch (ParseException e) { | |
150 | throw new JsonSyntaxException(s, e); | |
151 | } | |
152 | } | |
153 | } | |
154 | ||
155 | @Override | |
156 | public String toString() { | |
157 | DateFormat defaultFormat = dateFormats.get(0); | |
158 | if (defaultFormat instanceof SimpleDateFormat) { | |
159 | return SIMPLE_NAME + '(' + ((SimpleDateFormat) defaultFormat).toPattern() + ')'; | |
160 | } else { | |
161 | return SIMPLE_NAME + '(' + defaultFormat.getClass().getSimpleName() + ')'; | |
162 | } | |
163 | } | |
164 | } |
43 | 43 | * Using this naming policy with Gson will ensure that the first "letter" of the Java |
44 | 44 | * field name is capitalized when serialized to its JSON form. |
45 | 45 | * |
46 | * <p>Here's a few examples of the form "Java Field Name" ---> "JSON Field Name":</p> | |
46 | * <p>Here are a few examples of the form "Java Field Name" ---> "JSON Field Name":</p> | |
47 | 47 | * <ul> |
48 | 48 | * <li>someFieldName ---> SomeFieldName</li> |
49 | 49 | * <li>_someFieldName ---> _SomeFieldName</li> |
60 | 60 | * field name is capitalized when serialized to its JSON form and the words will be |
61 | 61 | * separated by a space. |
62 | 62 | * |
63 | * <p>Here's a few examples of the form "Java Field Name" ---> "JSON Field Name":</p> | |
63 | * <p>Here are a few examples of the form "Java Field Name" ---> "JSON Field Name":</p> | |
64 | 64 | * <ul> |
65 | 65 | * <li>someFieldName ---> Some Field Name</li> |
66 | 66 | * <li>_someFieldName ---> _Some Field Name</li> |
70 | 70 | */ |
71 | 71 | UPPER_CAMEL_CASE_WITH_SPACES() { |
72 | 72 | @Override public String translateName(Field f) { |
73 | return upperCaseFirstLetter(separateCamelCase(f.getName(), " ")); | |
73 | return upperCaseFirstLetter(separateCamelCase(f.getName(), ' ')); | |
74 | } | |
75 | }, | |
76 | ||
77 | /** | |
78 | * Using this naming policy with Gson will modify the Java Field name from its camel cased | |
79 | * form to an upper case field name where each word is separated by an underscore (_). | |
80 | * | |
81 | * <p>Here are a few examples of the form "Java Field Name" ---> "JSON Field Name":</p> | |
82 | * <ul> | |
83 | * <li>someFieldName ---> SOME_FIELD_NAME</li> | |
84 | * <li>_someFieldName ---> _SOME_FIELD_NAME</li> | |
85 | * <li>aStringField ---> A_STRING_FIELD</li> | |
86 | * <li>aURL ---> A_U_R_L</li> | |
87 | * </ul> | |
88 | */ | |
89 | UPPER_CASE_WITH_UNDERSCORES() { | |
90 | @Override public String translateName(Field f) { | |
91 | return separateCamelCase(f.getName(), '_').toUpperCase(Locale.ENGLISH); | |
74 | 92 | } |
75 | 93 | }, |
76 | 94 | |
78 | 96 | * Using this naming policy with Gson will modify the Java Field name from its camel cased |
79 | 97 | * form to a lower case field name where each word is separated by an underscore (_). |
80 | 98 | * |
81 | * <p>Here's a few examples of the form "Java Field Name" ---> "JSON Field Name":</p> | |
99 | * <p>Here are a few examples of the form "Java Field Name" ---> "JSON Field Name":</p> | |
82 | 100 | * <ul> |
83 | 101 | * <li>someFieldName ---> some_field_name</li> |
84 | 102 | * <li>_someFieldName ---> _some_field_name</li> |
88 | 106 | */ |
89 | 107 | LOWER_CASE_WITH_UNDERSCORES() { |
90 | 108 | @Override public String translateName(Field f) { |
91 | return separateCamelCase(f.getName(), "_").toLowerCase(Locale.ENGLISH); | |
109 | return separateCamelCase(f.getName(), '_').toLowerCase(Locale.ENGLISH); | |
92 | 110 | } |
93 | 111 | }, |
94 | 112 | |
96 | 114 | * Using this naming policy with Gson will modify the Java Field name from its camel cased |
97 | 115 | * form to a lower case field name where each word is separated by a dash (-). |
98 | 116 | * |
99 | * <p>Here's a few examples of the form "Java Field Name" ---> "JSON Field Name":</p> | |
117 | * <p>Here are a few examples of the form "Java Field Name" ---> "JSON Field Name":</p> | |
100 | 118 | * <ul> |
101 | 119 | * <li>someFieldName ---> some-field-name</li> |
102 | 120 | * <li>_someFieldName ---> _some-field-name</li> |
111 | 129 | */ |
112 | 130 | LOWER_CASE_WITH_DASHES() { |
113 | 131 | @Override public String translateName(Field f) { |
114 | return separateCamelCase(f.getName(), "-").toLowerCase(Locale.ENGLISH); | |
132 | return separateCamelCase(f.getName(), '-').toLowerCase(Locale.ENGLISH); | |
115 | 133 | } |
116 | 134 | }, |
117 | 135 | |
119 | 137 | * Using this naming policy with Gson will modify the Java Field name from its camel cased |
120 | 138 | * form to a lower case field name where each word is separated by a dot (.). |
121 | 139 | * |
122 | * <p>Here's a few examples of the form "Java Field Name" ---> "JSON Field Name":</p> | |
140 | * <p>Here are a few examples of the form "Java Field Name" ---> "JSON Field Name":</p> | |
123 | 141 | * <ul> |
124 | 142 | * <li>someFieldName ---> some.field.name</li> |
125 | 143 | * <li>_someFieldName ---> _some.field.name</li> |
134 | 152 | */ |
135 | 153 | LOWER_CASE_WITH_DOTS() { |
136 | 154 | @Override public String translateName(Field f) { |
137 | return separateCamelCase(f.getName(), ".").toLowerCase(Locale.ENGLISH); | |
155 | return separateCamelCase(f.getName(), '.').toLowerCase(Locale.ENGLISH); | |
138 | 156 | } |
139 | 157 | }; |
140 | 158 | |
141 | 159 | /** |
142 | 160 | * Converts the field name that uses camel-case define word separation into |
143 | * separate words that are separated by the provided {@code separatorString}. | |
144 | */ | |
145 | static String separateCamelCase(String name, String separator) { | |
161 | * separate words that are separated by the provided {@code separator}. | |
162 | */ | |
163 | static String separateCamelCase(String name, char separator) { | |
146 | 164 | StringBuilder translation = new StringBuilder(); |
147 | 165 | for (int i = 0, length = name.length(); i < length; i++) { |
148 | 166 | char character = name.charAt(i); |
157 | 175 | /** |
158 | 176 | * Ensures the JSON field names begins with an upper case letter. |
159 | 177 | */ |
160 | static String upperCaseFirstLetter(String name) { | |
161 | int firstLetterIndex = 0; | |
162 | int limit = name.length() - 1; | |
163 | for(; !Character.isLetter(name.charAt(firstLetterIndex)) && firstLetterIndex < limit; ++firstLetterIndex); | |
164 | ||
165 | char firstLetter = name.charAt(firstLetterIndex); | |
166 | if(Character.isUpperCase(firstLetter)) { //The letter is already uppercased, return the original | |
167 | return name; | |
168 | } | |
169 | ||
170 | char uppercased = Character.toUpperCase(firstLetter); | |
171 | if(firstLetterIndex == 0) { //First character in the string is the first letter, saves 1 substring | |
172 | return uppercased + name.substring(1); | |
173 | } | |
174 | ||
175 | return name.substring(0, firstLetterIndex) + uppercased + name.substring(firstLetterIndex + 1); | |
178 | static String upperCaseFirstLetter(String s) { | |
179 | int length = s.length(); | |
180 | for (int i = 0; i < length; i++) { | |
181 | char c = s.charAt(i); | |
182 | if (Character.isLetter(c)) { | |
183 | if (Character.isUpperCase(c)) { | |
184 | return s; | |
185 | } | |
186 | ||
187 | char uppercased = Character.toUpperCase(c); | |
188 | // For leading letter only need one substring | |
189 | if (i == 0) { | |
190 | return uppercased + s.substring(1); | |
191 | } else { | |
192 | return s.substring(0, i) + uppercased + s.substring(i + 1); | |
193 | } | |
194 | } | |
195 | } | |
196 | ||
197 | return s; | |
176 | 198 | } |
177 | 199 | } |
37 | 37 | import com.google.gson.internal.ConstructorConstructor; |
38 | 38 | import com.google.gson.internal.Excluder; |
39 | 39 | import com.google.gson.internal.GsonBuildConfig; |
40 | import com.google.gson.internal.LazilyParsedNumber; | |
40 | 41 | import com.google.gson.internal.Primitives; |
41 | 42 | import com.google.gson.internal.Streams; |
42 | 43 | import com.google.gson.internal.bind.ArrayTypeAdapter; |
46 | 47 | import com.google.gson.internal.bind.JsonTreeReader; |
47 | 48 | import com.google.gson.internal.bind.JsonTreeWriter; |
48 | 49 | import com.google.gson.internal.bind.MapTypeAdapterFactory; |
50 | import com.google.gson.internal.bind.NumberTypeAdapter; | |
49 | 51 | import com.google.gson.internal.bind.ObjectTypeAdapter; |
50 | 52 | import com.google.gson.internal.bind.ReflectiveTypeAdapterFactory; |
51 | import com.google.gson.internal.bind.SqlDateTypeAdapter; | |
52 | import com.google.gson.internal.bind.TimeTypeAdapter; | |
53 | 53 | import com.google.gson.internal.bind.TypeAdapters; |
54 | import com.google.gson.internal.sql.SqlTypesSupport; | |
54 | 55 | import com.google.gson.reflect.TypeToken; |
55 | 56 | import com.google.gson.stream.JsonReader; |
56 | 57 | import com.google.gson.stream.JsonToken; |
75 | 76 | * MyType target = new MyType(); |
76 | 77 | * String json = gson.toJson(target); // serializes target to Json |
77 | 78 | * MyType target2 = gson.fromJson(json, MyType.class); // deserializes json into target2 |
78 | * </pre></p> | |
79 | * </pre> | |
79 | 80 | * |
80 | 81 | * <p>If the object that your are serializing/deserializing is a {@code ParameterizedType} |
81 | 82 | * (i.e. contains at least one type parameter and may be an array) then you must use the |
90 | 91 | * Gson gson = new Gson(); |
91 | 92 | * String json = gson.toJson(target, listType); |
92 | 93 | * List<String> target2 = gson.fromJson(json, listType); |
93 | * </pre></p> | |
94 | * </pre> | |
94 | 95 | * |
95 | 96 | * <p>See the <a href="https://sites.google.com/site/gson/gson-user-guide">Gson User Guide</a> |
96 | 97 | * for a more complete set of examples.</p> |
109 | 110 | static final boolean DEFAULT_SERIALIZE_NULLS = false; |
110 | 111 | static final boolean DEFAULT_COMPLEX_MAP_KEYS = false; |
111 | 112 | static final boolean DEFAULT_SPECIALIZE_FLOAT_VALUES = false; |
113 | static final boolean DEFAULT_USE_JDK_UNSAFE = true; | |
114 | static final String DEFAULT_DATE_PATTERN = null; | |
115 | static final FieldNamingStrategy DEFAULT_FIELD_NAMING_STRATEGY = FieldNamingPolicy.IDENTITY; | |
116 | static final ToNumberStrategy DEFAULT_OBJECT_TO_NUMBER_STRATEGY = ToNumberPolicy.DOUBLE; | |
117 | static final ToNumberStrategy DEFAULT_NUMBER_TO_NUMBER_STRATEGY = ToNumberPolicy.LAZILY_PARSED_NUMBER; | |
112 | 118 | |
113 | 119 | private static final TypeToken<?> NULL_KEY_SURROGATE = TypeToken.get(Object.class); |
114 | 120 | private static final String JSON_NON_EXECUTABLE_PREFIX = ")]}'\n"; |
140 | 146 | final boolean prettyPrinting; |
141 | 147 | final boolean lenient; |
142 | 148 | final boolean serializeSpecialFloatingPointValues; |
149 | final boolean useJdkUnsafe; | |
143 | 150 | final String datePattern; |
144 | 151 | final int dateStyle; |
145 | 152 | final int timeStyle; |
146 | 153 | final LongSerializationPolicy longSerializationPolicy; |
147 | 154 | final List<TypeAdapterFactory> builderFactories; |
148 | 155 | final List<TypeAdapterFactory> builderHierarchyFactories; |
156 | final ToNumberStrategy objectToNumberStrategy; | |
157 | final ToNumberStrategy numberToNumberStrategy; | |
149 | 158 | |
150 | 159 | /** |
151 | 160 | * Constructs a Gson object with default configuration. The default configuration has the |
182 | 191 | * </ul> |
183 | 192 | */ |
184 | 193 | public Gson() { |
185 | this(Excluder.DEFAULT, FieldNamingPolicy.IDENTITY, | |
194 | this(Excluder.DEFAULT, DEFAULT_FIELD_NAMING_STRATEGY, | |
186 | 195 | Collections.<Type, InstanceCreator<?>>emptyMap(), DEFAULT_SERIALIZE_NULLS, |
187 | 196 | DEFAULT_COMPLEX_MAP_KEYS, DEFAULT_JSON_NON_EXECUTABLE, DEFAULT_ESCAPE_HTML, |
188 | 197 | DEFAULT_PRETTY_PRINT, DEFAULT_LENIENT, DEFAULT_SPECIALIZE_FLOAT_VALUES, |
189 | LongSerializationPolicy.DEFAULT, null, DateFormat.DEFAULT, DateFormat.DEFAULT, | |
198 | DEFAULT_USE_JDK_UNSAFE, | |
199 | LongSerializationPolicy.DEFAULT, DEFAULT_DATE_PATTERN, DateFormat.DEFAULT, DateFormat.DEFAULT, | |
190 | 200 | Collections.<TypeAdapterFactory>emptyList(), Collections.<TypeAdapterFactory>emptyList(), |
191 | Collections.<TypeAdapterFactory>emptyList()); | |
201 | Collections.<TypeAdapterFactory>emptyList(), DEFAULT_OBJECT_TO_NUMBER_STRATEGY, DEFAULT_NUMBER_TO_NUMBER_STRATEGY); | |
192 | 202 | } |
193 | 203 | |
194 | 204 | Gson(Excluder excluder, FieldNamingStrategy fieldNamingStrategy, |
195 | 205 | Map<Type, InstanceCreator<?>> instanceCreators, boolean serializeNulls, |
196 | 206 | boolean complexMapKeySerialization, boolean generateNonExecutableGson, boolean htmlSafe, |
197 | 207 | boolean prettyPrinting, boolean lenient, boolean serializeSpecialFloatingPointValues, |
208 | boolean useJdkUnsafe, | |
198 | 209 | LongSerializationPolicy longSerializationPolicy, String datePattern, int dateStyle, |
199 | 210 | int timeStyle, List<TypeAdapterFactory> builderFactories, |
200 | 211 | List<TypeAdapterFactory> builderHierarchyFactories, |
201 | List<TypeAdapterFactory> factoriesToBeAdded) { | |
212 | List<TypeAdapterFactory> factoriesToBeAdded, | |
213 | ToNumberStrategy objectToNumberStrategy, ToNumberStrategy numberToNumberStrategy) { | |
202 | 214 | this.excluder = excluder; |
203 | 215 | this.fieldNamingStrategy = fieldNamingStrategy; |
204 | 216 | this.instanceCreators = instanceCreators; |
205 | this.constructorConstructor = new ConstructorConstructor(instanceCreators); | |
217 | this.constructorConstructor = new ConstructorConstructor(instanceCreators, useJdkUnsafe); | |
206 | 218 | this.serializeNulls = serializeNulls; |
207 | 219 | this.complexMapKeySerialization = complexMapKeySerialization; |
208 | 220 | this.generateNonExecutableJson = generateNonExecutableGson; |
210 | 222 | this.prettyPrinting = prettyPrinting; |
211 | 223 | this.lenient = lenient; |
212 | 224 | this.serializeSpecialFloatingPointValues = serializeSpecialFloatingPointValues; |
225 | this.useJdkUnsafe = useJdkUnsafe; | |
213 | 226 | this.longSerializationPolicy = longSerializationPolicy; |
214 | 227 | this.datePattern = datePattern; |
215 | 228 | this.dateStyle = dateStyle; |
216 | 229 | this.timeStyle = timeStyle; |
217 | 230 | this.builderFactories = builderFactories; |
218 | 231 | this.builderHierarchyFactories = builderHierarchyFactories; |
232 | this.objectToNumberStrategy = objectToNumberStrategy; | |
233 | this.numberToNumberStrategy = numberToNumberStrategy; | |
219 | 234 | |
220 | 235 | List<TypeAdapterFactory> factories = new ArrayList<TypeAdapterFactory>(); |
221 | 236 | |
222 | 237 | // built-in type adapters that cannot be overridden |
223 | 238 | factories.add(TypeAdapters.JSON_ELEMENT_FACTORY); |
224 | factories.add(ObjectTypeAdapter.FACTORY); | |
239 | factories.add(ObjectTypeAdapter.getFactory(objectToNumberStrategy)); | |
225 | 240 | |
226 | 241 | // the excluder must precede all adapters that handle user-defined types |
227 | 242 | factories.add(excluder); |
241 | 256 | doubleAdapter(serializeSpecialFloatingPointValues))); |
242 | 257 | factories.add(TypeAdapters.newFactory(float.class, Float.class, |
243 | 258 | floatAdapter(serializeSpecialFloatingPointValues))); |
244 | factories.add(TypeAdapters.NUMBER_FACTORY); | |
259 | factories.add(NumberTypeAdapter.getFactory(numberToNumberStrategy)); | |
245 | 260 | factories.add(TypeAdapters.ATOMIC_INTEGER_FACTORY); |
246 | 261 | factories.add(TypeAdapters.ATOMIC_BOOLEAN_FACTORY); |
247 | 262 | factories.add(TypeAdapters.newFactory(AtomicLong.class, atomicLongAdapter(longAdapter))); |
252 | 267 | factories.add(TypeAdapters.STRING_BUFFER_FACTORY); |
253 | 268 | factories.add(TypeAdapters.newFactory(BigDecimal.class, TypeAdapters.BIG_DECIMAL)); |
254 | 269 | factories.add(TypeAdapters.newFactory(BigInteger.class, TypeAdapters.BIG_INTEGER)); |
270 | // Add adapter for LazilyParsedNumber because user can obtain it from Gson and then try to serialize it again | |
271 | factories.add(TypeAdapters.newFactory(LazilyParsedNumber.class, TypeAdapters.LAZILY_PARSED_NUMBER)); | |
255 | 272 | factories.add(TypeAdapters.URL_FACTORY); |
256 | 273 | factories.add(TypeAdapters.URI_FACTORY); |
257 | 274 | factories.add(TypeAdapters.UUID_FACTORY); |
261 | 278 | factories.add(TypeAdapters.BIT_SET_FACTORY); |
262 | 279 | factories.add(DateTypeAdapter.FACTORY); |
263 | 280 | factories.add(TypeAdapters.CALENDAR_FACTORY); |
264 | factories.add(TimeTypeAdapter.FACTORY); | |
265 | factories.add(SqlDateTypeAdapter.FACTORY); | |
266 | factories.add(TypeAdapters.TIMESTAMP_FACTORY); | |
281 | ||
282 | if (SqlTypesSupport.SUPPORTS_SQL_TYPES) { | |
283 | factories.add(SqlTypesSupport.TIME_FACTORY); | |
284 | factories.add(SqlTypesSupport.DATE_FACTORY); | |
285 | factories.add(SqlTypesSupport.TIMESTAMP_FACTORY); | |
286 | } | |
287 | ||
267 | 288 | factories.add(ArrayTypeAdapter.FACTORY); |
268 | 289 | factories.add(TypeAdapters.CLASS_FACTORY); |
269 | 290 | |
289 | 310 | return new GsonBuilder(this); |
290 | 311 | } |
291 | 312 | |
313 | /** | |
314 | * @deprecated This method by accident exposes an internal Gson class; it might be removed in a | |
315 | * future version. | |
316 | */ | |
317 | @Deprecated | |
292 | 318 | public Excluder excluder() { |
293 | 319 | return excluder; |
294 | 320 | } |
295 | 321 | |
322 | /** | |
323 | * Returns the field naming strategy used by this Gson instance. | |
324 | * | |
325 | * @see GsonBuilder#setFieldNamingStrategy(FieldNamingStrategy) | |
326 | */ | |
296 | 327 | public FieldNamingStrategy fieldNamingStrategy() { |
297 | 328 | return fieldNamingStrategy; |
298 | 329 | } |
299 | 330 | |
331 | /** | |
332 | * Returns whether this Gson instance is serializing JSON object properties with | |
333 | * {@code null} values, or just omits them. | |
334 | * | |
335 | * @see GsonBuilder#serializeNulls() | |
336 | */ | |
300 | 337 | public boolean serializeNulls() { |
301 | 338 | return serializeNulls; |
302 | 339 | } |
303 | 340 | |
341 | /** | |
342 | * Returns whether this Gson instance produces JSON output which is | |
343 | * HTML-safe, that means all HTML characters are escaped. | |
344 | * | |
345 | * @see GsonBuilder#disableHtmlEscaping() | |
346 | */ | |
304 | 347 | public boolean htmlSafe() { |
305 | 348 | return htmlSafe; |
306 | 349 | } |
516 | 559 | * read or written. |
517 | 560 | * @param skipPast The type adapter factory that needs to be skipped while searching for |
518 | 561 | * a matching type adapter. In most cases, you should just pass <i>this</i> (the type adapter |
519 | * factory from where {@link #getDelegateAdapter} method is being invoked). | |
562 | * factory from where {@code getDelegateAdapter} method is being invoked). | |
520 | 563 | * @param type Type for which the delegate adapter is being searched for. |
521 | 564 | * |
522 | 565 | * @since 2.2 |
746 | 789 | |
747 | 790 | /** |
748 | 791 | * Returns a new JSON writer configured for the settings on this Gson instance. |
792 | * | |
793 | * <p>The following settings are considered: | |
794 | * <ul> | |
795 | * <li>{@link GsonBuilder#disableHtmlEscaping()}</li> | |
796 | * <li>{@link GsonBuilder#generateNonExecutableJson()}</li> | |
797 | * <li>{@link GsonBuilder#serializeNulls()}</li> | |
798 | * <li>{@link GsonBuilder#setLenient()}</li> | |
799 | * <li>{@link GsonBuilder#setPrettyPrinting()}</li> | |
800 | * </ul> | |
749 | 801 | */ |
750 | 802 | public JsonWriter newJsonWriter(Writer writer) throws IOException { |
751 | 803 | if (generateNonExecutableJson) { |
755 | 807 | if (prettyPrinting) { |
756 | 808 | jsonWriter.setIndent(" "); |
757 | 809 | } |
810 | jsonWriter.setHtmlSafe(htmlSafe); | |
811 | jsonWriter.setLenient(lenient); | |
758 | 812 | jsonWriter.setSerializeNulls(serializeNulls); |
759 | 813 | return jsonWriter; |
760 | 814 | } |
761 | 815 | |
762 | 816 | /** |
763 | 817 | * Returns a new JSON reader configured for the settings on this Gson instance. |
818 | * | |
819 | * <p>The following settings are considered: | |
820 | * <ul> | |
821 | * <li>{@link GsonBuilder#setLenient()}</li> | |
822 | * </ul> | |
764 | 823 | */ |
765 | 824 | public JsonReader newJsonReader(Reader reader) { |
766 | 825 | JsonReader jsonReader = new JsonReader(reader); |
16 | 16 | package com.google.gson; |
17 | 17 | |
18 | 18 | import java.lang.reflect.Type; |
19 | import java.sql.Timestamp; | |
20 | 19 | import java.text.DateFormat; |
21 | 20 | import java.util.ArrayList; |
22 | 21 | import java.util.Collections; |
27 | 26 | |
28 | 27 | import com.google.gson.internal.$Gson$Preconditions; |
29 | 28 | import com.google.gson.internal.Excluder; |
29 | import com.google.gson.internal.bind.DefaultDateTypeAdapter; | |
30 | 30 | import com.google.gson.internal.bind.TreeTypeAdapter; |
31 | 31 | import com.google.gson.internal.bind.TypeAdapters; |
32 | import com.google.gson.internal.sql.SqlTypesSupport; | |
32 | 33 | import com.google.gson.reflect.TypeToken; |
33 | 34 | import com.google.gson.stream.JsonReader; |
34 | 35 | |
35 | 36 | import static com.google.gson.Gson.DEFAULT_COMPLEX_MAP_KEYS; |
37 | import static com.google.gson.Gson.DEFAULT_DATE_PATTERN; | |
36 | 38 | import static com.google.gson.Gson.DEFAULT_ESCAPE_HTML; |
37 | 39 | import static com.google.gson.Gson.DEFAULT_JSON_NON_EXECUTABLE; |
38 | 40 | import static com.google.gson.Gson.DEFAULT_LENIENT; |
41 | import static com.google.gson.Gson.DEFAULT_NUMBER_TO_NUMBER_STRATEGY; | |
42 | import static com.google.gson.Gson.DEFAULT_OBJECT_TO_NUMBER_STRATEGY; | |
39 | 43 | import static com.google.gson.Gson.DEFAULT_PRETTY_PRINT; |
40 | 44 | import static com.google.gson.Gson.DEFAULT_SERIALIZE_NULLS; |
41 | 45 | import static com.google.gson.Gson.DEFAULT_SPECIALIZE_FLOAT_VALUES; |
46 | import static com.google.gson.Gson.DEFAULT_USE_JDK_UNSAFE; | |
42 | 47 | |
43 | 48 | /** |
44 | 49 | * <p>Use this builder to construct a {@link Gson} instance when you need to set configuration |
59 | 64 | * .setPrettyPrinting() |
60 | 65 | * .setVersion(1.0) |
61 | 66 | * .create(); |
62 | * </pre></p> | |
67 | * </pre> | |
63 | 68 | * |
64 | 69 | * <p>NOTES: |
65 | 70 | * <ul> |
67 | 72 | * <li> The default serialization of {@link Date} and its subclasses in Gson does |
68 | 73 | * not contain time-zone information. So, if you are using date/time instances, |
69 | 74 | * use {@code GsonBuilder} and its {@code setDateFormat} methods.</li> |
70 | * </ul> | |
71 | * </p> | |
75 | * </ul> | |
72 | 76 | * |
73 | 77 | * @author Inderjeet Singh |
74 | 78 | * @author Joel Leitch |
84 | 88 | /** tree-style hierarchy factories. These come after factories for backwards compatibility. */ |
85 | 89 | private final List<TypeAdapterFactory> hierarchyFactories = new ArrayList<TypeAdapterFactory>(); |
86 | 90 | private boolean serializeNulls = DEFAULT_SERIALIZE_NULLS; |
87 | private String datePattern; | |
91 | private String datePattern = DEFAULT_DATE_PATTERN; | |
88 | 92 | private int dateStyle = DateFormat.DEFAULT; |
89 | 93 | private int timeStyle = DateFormat.DEFAULT; |
90 | 94 | private boolean complexMapKeySerialization = DEFAULT_COMPLEX_MAP_KEYS; |
93 | 97 | private boolean prettyPrinting = DEFAULT_PRETTY_PRINT; |
94 | 98 | private boolean generateNonExecutableJson = DEFAULT_JSON_NON_EXECUTABLE; |
95 | 99 | private boolean lenient = DEFAULT_LENIENT; |
100 | private boolean useJdkUnsafe = DEFAULT_USE_JDK_UNSAFE; | |
101 | private ToNumberStrategy objectToNumberStrategy = DEFAULT_OBJECT_TO_NUMBER_STRATEGY; | |
102 | private ToNumberStrategy numberToNumberStrategy = DEFAULT_NUMBER_TO_NUMBER_STRATEGY; | |
96 | 103 | |
97 | 104 | /** |
98 | 105 | * Creates a GsonBuilder instance that can be used to build Gson with various configuration |
126 | 133 | this.timeStyle = gson.timeStyle; |
127 | 134 | this.factories.addAll(gson.builderFactories); |
128 | 135 | this.hierarchyFactories.addAll(gson.builderHierarchyFactories); |
136 | this.useJdkUnsafe = gson.useJdkUnsafe; | |
137 | this.objectToNumberStrategy = gson.objectToNumberStrategy; | |
138 | this.numberToNumberStrategy = gson.numberToNumberStrategy; | |
129 | 139 | } |
130 | 140 | |
131 | 141 | /** |
245 | 255 | * original.put(new Point(8, 8), "b"); |
246 | 256 | * System.out.println(gson.toJson(original, type)); |
247 | 257 | * } |
258 | * </pre> | |
248 | 259 | * |
249 | 260 | * The JSON output would look as follows: |
250 | 261 | * <pre> {@code |
325 | 336 | } |
326 | 337 | |
327 | 338 | /** |
339 | * Configures Gson to apply a specific number strategy during deserialization of {@link Object}. | |
340 | * | |
341 | * @param objectToNumberStrategy the actual object-to-number strategy | |
342 | * @return a reference to this {@code GsonBuilder} object to fulfill the "Builder" pattern | |
343 | * @see ToNumberPolicy#DOUBLE The default object-to-number strategy | |
344 | */ | |
345 | public GsonBuilder setObjectToNumberStrategy(ToNumberStrategy objectToNumberStrategy) { | |
346 | this.objectToNumberStrategy = objectToNumberStrategy; | |
347 | return this; | |
348 | } | |
349 | ||
350 | /** | |
351 | * Configures Gson to apply a specific number strategy during deserialization of {@link Number}. | |
352 | * | |
353 | * @param numberToNumberStrategy the actual number-to-number strategy | |
354 | * @return a reference to this {@code GsonBuilder} object to fulfill the "Builder" pattern | |
355 | * @see ToNumberPolicy#LAZILY_PARSED_NUMBER The default number-to-number strategy | |
356 | */ | |
357 | public GsonBuilder setNumberToNumberStrategy(ToNumberStrategy numberToNumberStrategy) { | |
358 | this.numberToNumberStrategy = numberToNumberStrategy; | |
359 | return this; | |
360 | } | |
361 | ||
362 | /** | |
328 | 363 | * Configures Gson to apply a set of exclusion strategies during both serialization and |
329 | 364 | * deserialization. Each of the {@code strategies} will be applied as a disjunction rule. |
330 | 365 | * This means that if one of the {@code strategies} suggests that a field (or class) should be |
416 | 451 | * call this method or {@link #setDateFormat(int)} multiple times, but only the last invocation |
417 | 452 | * will be used to decide the serialization format. |
418 | 453 | * |
419 | * <p>The date format will be used to serialize and deserialize {@link java.util.Date}, {@link | |
420 | * java.sql.Timestamp} and {@link java.sql.Date}. | |
454 | * <p>The date format will be used to serialize and deserialize {@link java.util.Date} and in case | |
455 | * the {@code java.sql} module is present, also {@link java.sql.Timestamp} and {@link java.sql.Date}. | |
421 | 456 | * |
422 | 457 | * <p>Note that this pattern must abide by the convention provided by {@code SimpleDateFormat} |
423 | 458 | * class. See the documentation in {@link java.text.SimpleDateFormat} for more information on |
577 | 612 | } |
578 | 613 | |
579 | 614 | /** |
615 | * Disables usage of JDK's {@code sun.misc.Unsafe}. | |
616 | * | |
617 | * <p>By default Gson uses {@code Unsafe} to create instances of classes which don't have | |
618 | * a no-args constructor. However, {@code Unsafe} might not be available for all Java | |
619 | * runtimes. For example Android does not provide {@code Unsafe}, or only with limited | |
620 | * functionality. Additionally {@code Unsafe} creates instances without executing any | |
621 | * constructor or initializer block, or performing initialization of field values. This can | |
622 | * lead to surprising and difficult to debug errors. | |
623 | * Therefore, to get reliable behavior regardless of which runtime is used, and to detect | |
624 | * classes which cannot be deserialized in an early stage of development, this method allows | |
625 | * disabling usage of {@code Unsafe}. | |
626 | * | |
627 | * @return a reference to this {@code GsonBuilder} object to fulfill the "Builder" pattern | |
628 | */ | |
629 | public GsonBuilder disableJdkUnsafe() { | |
630 | this.useJdkUnsafe = false; | |
631 | return this; | |
632 | } | |
633 | ||
634 | /** | |
580 | 635 | * Creates a {@link Gson} instance based on the current configuration. This method is free of |
581 | 636 | * side-effects to this {@code GsonBuilder} instance and hence can be called multiple times. |
582 | 637 | * |
596 | 651 | return new Gson(excluder, fieldNamingPolicy, instanceCreators, |
597 | 652 | serializeNulls, complexMapKeySerialization, |
598 | 653 | generateNonExecutableJson, escapeHtmlChars, prettyPrinting, lenient, |
599 | serializeSpecialFloatingPointValues, longSerializationPolicy, | |
654 | serializeSpecialFloatingPointValues, useJdkUnsafe, longSerializationPolicy, | |
600 | 655 | datePattern, dateStyle, timeStyle, |
601 | this.factories, this.hierarchyFactories, factories); | |
602 | } | |
603 | ||
604 | @SuppressWarnings("unchecked") | |
656 | this.factories, this.hierarchyFactories, factories, objectToNumberStrategy, numberToNumberStrategy); | |
657 | } | |
658 | ||
605 | 659 | private void addTypeAdaptersForDate(String datePattern, int dateStyle, int timeStyle, |
606 | 660 | List<TypeAdapterFactory> factories) { |
607 | DefaultDateTypeAdapter dateTypeAdapter; | |
608 | TypeAdapter<Timestamp> timestampTypeAdapter; | |
609 | TypeAdapter<java.sql.Date> javaSqlDateTypeAdapter; | |
610 | if (datePattern != null && !"".equals(datePattern.trim())) { | |
611 | dateTypeAdapter = new DefaultDateTypeAdapter(Date.class, datePattern); | |
612 | timestampTypeAdapter = (TypeAdapter) new DefaultDateTypeAdapter(Timestamp.class, datePattern); | |
613 | javaSqlDateTypeAdapter = (TypeAdapter) new DefaultDateTypeAdapter(java.sql.Date.class, datePattern); | |
661 | TypeAdapterFactory dateAdapterFactory; | |
662 | boolean sqlTypesSupported = SqlTypesSupport.SUPPORTS_SQL_TYPES; | |
663 | TypeAdapterFactory sqlTimestampAdapterFactory = null; | |
664 | TypeAdapterFactory sqlDateAdapterFactory = null; | |
665 | ||
666 | if (datePattern != null && !datePattern.trim().isEmpty()) { | |
667 | dateAdapterFactory = DefaultDateTypeAdapter.DateType.DATE.createAdapterFactory(datePattern); | |
668 | ||
669 | if (sqlTypesSupported) { | |
670 | sqlTimestampAdapterFactory = SqlTypesSupport.TIMESTAMP_DATE_TYPE.createAdapterFactory(datePattern); | |
671 | sqlDateAdapterFactory = SqlTypesSupport.DATE_DATE_TYPE.createAdapterFactory(datePattern); | |
672 | } | |
614 | 673 | } else if (dateStyle != DateFormat.DEFAULT && timeStyle != DateFormat.DEFAULT) { |
615 | dateTypeAdapter = new DefaultDateTypeAdapter(Date.class, dateStyle, timeStyle); | |
616 | timestampTypeAdapter = (TypeAdapter) new DefaultDateTypeAdapter(Timestamp.class, dateStyle, timeStyle); | |
617 | javaSqlDateTypeAdapter = (TypeAdapter) new DefaultDateTypeAdapter(java.sql.Date.class, dateStyle, timeStyle); | |
674 | dateAdapterFactory = DefaultDateTypeAdapter.DateType.DATE.createAdapterFactory(dateStyle, timeStyle); | |
675 | ||
676 | if (sqlTypesSupported) { | |
677 | sqlTimestampAdapterFactory = SqlTypesSupport.TIMESTAMP_DATE_TYPE.createAdapterFactory(dateStyle, timeStyle); | |
678 | sqlDateAdapterFactory = SqlTypesSupport.DATE_DATE_TYPE.createAdapterFactory(dateStyle, timeStyle); | |
679 | } | |
618 | 680 | } else { |
619 | 681 | return; |
620 | 682 | } |
621 | 683 | |
622 | factories.add(TypeAdapters.newFactory(Date.class, dateTypeAdapter)); | |
623 | factories.add(TypeAdapters.newFactory(Timestamp.class, timestampTypeAdapter)); | |
624 | factories.add(TypeAdapters.newFactory(java.sql.Date.class, javaSqlDateTypeAdapter)); | |
684 | factories.add(dateAdapterFactory); | |
685 | if (sqlTypesSupported) { | |
686 | factories.add(sqlTimestampAdapterFactory); | |
687 | factories.add(sqlDateAdapterFactory); | |
688 | } | |
625 | 689 | } |
626 | 690 | } |
343 | 343 | @Override |
344 | 344 | public char getAsCharacter() { |
345 | 345 | if (elements.size() == 1) { |
346 | return elements.get(0).getAsCharacter(); | |
346 | JsonElement element = elements.get(0); | |
347 | @SuppressWarnings("deprecation") | |
348 | char result = element.getAsCharacter(); | |
349 | return result; | |
347 | 350 | } |
348 | 351 | throw new IllegalStateException(); |
349 | 352 | } |
25 | 25 | * <p>Let us look at example where defining a serializer will be useful. The {@code Id} class |
26 | 26 | * defined below has two fields: {@code clazz} and {@code value}.</p> |
27 | 27 | * |
28 | * <p><pre> | |
28 | * <pre> | |
29 | 29 | * public class Id<T> { |
30 | 30 | * private final Class<T> clazz; |
31 | 31 | * private final long value; |
39 | 39 | * return value; |
40 | 40 | * } |
41 | 41 | * } |
42 | * </pre></p> | |
42 | * </pre> | |
43 | 43 | * |
44 | 44 | * <p>The default serialization of {@code Id(com.foo.MyObject.class, 20L)} will be |
45 | 45 | * <code>{"clazz":com.foo.MyObject,"value":20}</code>. Suppose, you just want the output to be |
46 | 46 | * the value instead, which is {@code 20} in this case. You can achieve that by writing a custom |
47 | 47 | * serializer:</p> |
48 | 48 | * |
49 | * <p><pre> | |
49 | * <pre> | |
50 | 50 | * class IdSerializer implements JsonSerializer<Id>() { |
51 | 51 | * public JsonElement serialize(Id id, Type typeOfId, JsonSerializationContext context) { |
52 | 52 | * return new JsonPrimitive(id.getValue()); |
53 | 53 | * } |
54 | 54 | * } |
55 | * </pre></p> | |
55 | * </pre> | |
56 | 56 | * |
57 | 57 | * <p>You will also need to register {@code IdSerializer} with Gson as follows:</p> |
58 | 58 | * <pre> |
16 | 16 | package com.google.gson; |
17 | 17 | |
18 | 18 | /** |
19 | * Defines the expected format for a {@code long} or {@code Long} type when its serialized. | |
19 | * Defines the expected format for a {@code long} or {@code Long} type when it is serialized. | |
20 | 20 | * |
21 | 21 | * @since 1.3 |
22 | 22 | * |
25 | 25 | */ |
26 | 26 | public enum LongSerializationPolicy { |
27 | 27 | /** |
28 | * This is the "default" serialization policy that will output a {@code long} object as a JSON | |
28 | * This is the "default" serialization policy that will output a {@code Long} object as a JSON | |
29 | 29 | * number. For example, assume an object has a long field named "f" then the serialized output |
30 | 30 | * would be: |
31 | * {@code {"f":123}}. | |
31 | * {@code {"f":123}} | |
32 | * | |
33 | * <p>A {@code null} value is serialized as {@link JsonNull}. | |
32 | 34 | */ |
33 | 35 | DEFAULT() { |
34 | 36 | @Override public JsonElement serialize(Long value) { |
37 | if (value == null) { | |
38 | return JsonNull.INSTANCE; | |
39 | } | |
35 | 40 | return new JsonPrimitive(value); |
36 | 41 | } |
37 | 42 | }, |
39 | 44 | /** |
40 | 45 | * Serializes a long value as a quoted string. For example, assume an object has a long field |
41 | 46 | * named "f" then the serialized output would be: |
42 | * {@code {"f":"123"}}. | |
47 | * {@code {"f":"123"}} | |
48 | * | |
49 | * <p>A {@code null} value is serialized as {@link JsonNull}. | |
43 | 50 | */ |
44 | 51 | STRING() { |
45 | 52 | @Override public JsonElement serialize(Long value) { |
46 | return new JsonPrimitive(String.valueOf(value)); | |
53 | if (value == null) { | |
54 | return JsonNull.INSTANCE; | |
55 | } | |
56 | return new JsonPrimitive(value.toString()); | |
47 | 57 | } |
48 | 58 | }; |
49 | 59 |
0 | /* | |
1 | * Copyright (C) 2021 Google Inc. | |
2 | * | |
3 | * Licensed under the Apache License, Version 2.0 (the "License"); | |
4 | * you may not use this file except in compliance with the License. | |
5 | * You may obtain a copy of the License at | |
6 | * | |
7 | * http://www.apache.org/licenses/LICENSE-2.0 | |
8 | * | |
9 | * Unless required by applicable law or agreed to in writing, software | |
10 | * distributed under the License is distributed on an "AS IS" BASIS, | |
11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
12 | * See the License for the specific language governing permissions and | |
13 | * limitations under the License. | |
14 | */ | |
15 | ||
16 | package com.google.gson; | |
17 | ||
18 | import java.io.IOException; | |
19 | import java.math.BigDecimal; | |
20 | ||
21 | import com.google.gson.internal.LazilyParsedNumber; | |
22 | import com.google.gson.stream.JsonReader; | |
23 | import com.google.gson.stream.MalformedJsonException; | |
24 | ||
25 | /** | |
26 | * An enumeration that defines two standard number reading strategies and a couple of | |
27 | * strategies to overcome some historical Gson limitations while deserializing numbers as | |
28 | * {@link Object} and {@link Number}. | |
29 | * | |
30 | * @see ToNumberStrategy | |
31 | */ | |
32 | public enum ToNumberPolicy implements ToNumberStrategy { | |
33 | ||
34 | /** | |
35 | * Using this policy will ensure that numbers will be read as {@link Double} values. | |
36 | * This is the default strategy used during deserialization of numbers as {@link Object}. | |
37 | */ | |
38 | DOUBLE { | |
39 | @Override public Double readNumber(JsonReader in) throws IOException { | |
40 | return in.nextDouble(); | |
41 | } | |
42 | }, | |
43 | ||
44 | /** | |
45 | * Using this policy will ensure that numbers will be read as a lazily parsed number backed | |
46 | * by a string. This is the default strategy used during deserialization of numbers as | |
47 | * {@link Number}. | |
48 | */ | |
49 | LAZILY_PARSED_NUMBER { | |
50 | @Override public Number readNumber(JsonReader in) throws IOException { | |
51 | return new LazilyParsedNumber(in.nextString()); | |
52 | } | |
53 | }, | |
54 | ||
55 | /** | |
56 | * Using this policy will ensure that numbers will be read as {@link Long} or {@link Double} | |
57 | * values depending on how JSON numbers are represented: {@code Long} if the JSON number can | |
58 | * be parsed as a {@code Long} value, or otherwise {@code Double} if it can be parsed as a | |
59 | * {@code Double} value. If the parsed double-precision number results in a positive or negative | |
60 | * infinity ({@link Double#isInfinite()}) or a NaN ({@link Double#isNaN()}) value and the | |
61 | * {@code JsonReader} is not {@link JsonReader#isLenient() lenient}, a {@link MalformedJsonException} | |
62 | * is thrown. | |
63 | */ | |
64 | LONG_OR_DOUBLE { | |
65 | @Override public Number readNumber(JsonReader in) throws IOException, JsonParseException { | |
66 | String value = in.nextString(); | |
67 | try { | |
68 | return Long.parseLong(value); | |
69 | } catch (NumberFormatException longE) { | |
70 | try { | |
71 | Double d = Double.valueOf(value); | |
72 | if ((d.isInfinite() || d.isNaN()) && !in.isLenient()) { | |
73 | throw new MalformedJsonException("JSON forbids NaN and infinities: " + d + "; at path " + in.getPreviousPath()); | |
74 | } | |
75 | return d; | |
76 | } catch (NumberFormatException doubleE) { | |
77 | throw new JsonParseException("Cannot parse " + value + "; at path " + in.getPreviousPath(), doubleE); | |
78 | } | |
79 | } | |
80 | } | |
81 | }, | |
82 | ||
83 | /** | |
84 | * Using this policy will ensure that numbers will be read as numbers of arbitrary length | |
85 | * using {@link BigDecimal}. | |
86 | */ | |
87 | BIG_DECIMAL { | |
88 | @Override public BigDecimal readNumber(JsonReader in) throws IOException { | |
89 | String value = in.nextString(); | |
90 | try { | |
91 | return new BigDecimal(value); | |
92 | } catch (NumberFormatException e) { | |
93 | throw new JsonParseException("Cannot parse " + value + "; at path " + in.getPreviousPath(), e); | |
94 | } | |
95 | } | |
96 | } | |
97 | ||
98 | } |
0 | /* | |
1 | * Copyright (C) 2021 Google Inc. | |
2 | * | |
3 | * Licensed under the Apache License, Version 2.0 (the "License"); | |
4 | * you may not use this file except in compliance with the License. | |
5 | * You may obtain a copy of the License at | |
6 | * | |
7 | * http://www.apache.org/licenses/LICENSE-2.0 | |
8 | * | |
9 | * Unless required by applicable law or agreed to in writing, software | |
10 | * distributed under the License is distributed on an "AS IS" BASIS, | |
11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
12 | * See the License for the specific language governing permissions and | |
13 | * limitations under the License. | |
14 | */ | |
15 | ||
16 | package com.google.gson; | |
17 | ||
18 | import java.io.IOException; | |
19 | ||
20 | import com.google.gson.stream.JsonReader; | |
21 | ||
22 | /** | |
23 | * A strategy that is used to control how numbers should be deserialized for {@link Object} and {@link Number} | |
24 | * when a concrete type of the deserialized number is unknown in advance. By default, Gson uses the following | |
25 | * deserialization strategies: | |
26 | * | |
27 | * <ul> | |
28 | * <li>{@link Double} values are returned for JSON numbers if the deserialization type is declared as | |
29 | * {@code Object}, see {@link ToNumberPolicy#DOUBLE};</li> | |
30 | * <li>Lazily parsed number values are returned if the deserialization type is declared as {@code Number}, | |
31 | * see {@link ToNumberPolicy#LAZILY_PARSED_NUMBER}.</li> | |
32 | * </ul> | |
33 | * | |
34 | * <p>For historical reasons, Gson does not support deserialization of arbitrary-length numbers for | |
35 | * {@code Object} and {@code Number} by default, potentially causing precision loss. However, | |
36 | * <a href="https://tools.ietf.org/html/rfc8259#section-6">RFC 8259</a> permits this: | |
37 | * | |
38 | * <pre> | |
39 | * This specification allows implementations to set limits on the range | |
40 | * and precision of numbers accepted. Since software that implements | |
41 | * IEEE 754 binary64 (double precision) numbers [IEEE754] is generally | |
42 | * available and widely used, good interoperability can be achieved by | |
43 | * implementations that expect no more precision or range than these | |
44 | * provide, in the sense that implementations will approximate JSON | |
45 | * numbers within the expected precision. A JSON number such as 1E400 | |
46 | * or 3.141592653589793238462643383279 may indicate potential | |
47 | * interoperability problems, since it suggests that the software that | |
48 | * created it expects receiving software to have greater capabilities | |
49 | * for numeric magnitude and precision than is widely available. | |
50 | * </pre> | |
51 | * | |
52 | * <p>To overcome the precision loss, use for example {@link ToNumberPolicy#LONG_OR_DOUBLE} or | |
53 | * {@link ToNumberPolicy#BIG_DECIMAL}.</p> | |
54 | * | |
55 | * @see ToNumberPolicy | |
56 | * @see GsonBuilder#setObjectToNumberStrategy(ToNumberStrategy) | |
57 | * @see GsonBuilder#setNumberToNumberStrategy(ToNumberStrategy) | |
58 | */ | |
59 | public interface ToNumberStrategy { | |
60 | ||
61 | /** | |
62 | * Reads a number from the given JSON reader. A strategy is supposed to read a single value from the | |
63 | * reader, and the read value is guaranteed never to be {@code null}. | |
64 | * | |
65 | * @param in JSON reader to read a number from | |
66 | * @return number read from the JSON reader. | |
67 | */ | |
68 | public Number readNumber(JsonReader in) throws IOException; | |
69 | } |
31 | 31 | * method.</p> |
32 | 32 | * |
33 | 33 | * <p>Here is an example of how this annotation is meant to be used: |
34 | * <p><pre> | |
34 | * <pre> | |
35 | 35 | * public class User { |
36 | 36 | * @Expose private String firstName; |
37 | 37 | * @Expose(serialize = false) private String lastName; |
38 | 38 | * @Expose (serialize = false, deserialize = false) private String emailAddress; |
39 | 39 | * private String password; |
40 | 40 | * } |
41 | * </pre></p> | |
41 | * </pre> | |
42 | 42 | * If you created Gson with {@code new Gson()}, the {@code toJson()} and {@code fromJson()} |
43 | 43 | * methods will use the {@code password} field along-with {@code firstName}, {@code lastName}, |
44 | 44 | * and {@code emailAddress} for serialization and deserialization. However, if you created Gson |
338 | 338 | } |
339 | 339 | |
340 | 340 | public static Type resolve(Type context, Class<?> contextRawType, Type toResolve) { |
341 | ||
341 | 342 | return resolve(context, contextRawType, toResolve, new HashMap<TypeVariable<?>, Type>()); |
342 | 343 | } |
343 | 344 | |
572 | 573 | |
573 | 574 | /** |
574 | 575 | * The WildcardType interface supports multiple upper bounds and multiple |
575 | * lower bounds. We only support what the Java 6 language needs - at most one | |
576 | * bound. If a lower bound is set, the upper bound must be Object.class. | |
576 | * lower bounds. We only support what the target Java version supports - at most one | |
577 | * bound, see also https://bugs.openjdk.java.net/browse/JDK-8250660. If a lower bound | |
578 | * is set, the upper bound must be Object.class. | |
577 | 579 | */ |
578 | 580 | private static final class WildcardTypeImpl implements WildcardType, Serializable { |
579 | 581 | private final Type upperBound; |
17 | 17 | |
18 | 18 | import java.lang.reflect.Constructor; |
19 | 19 | import java.lang.reflect.InvocationTargetException; |
20 | import java.lang.reflect.Modifier; | |
20 | 21 | import java.lang.reflect.ParameterizedType; |
21 | 22 | import java.lang.reflect.Type; |
22 | 23 | import java.util.ArrayDeque; |
23 | 24 | import java.util.ArrayList; |
24 | 25 | import java.util.Collection; |
26 | import java.util.EnumMap; | |
25 | 27 | import java.util.EnumSet; |
26 | 28 | import java.util.LinkedHashMap; |
27 | 29 | import java.util.LinkedHashSet; |
39 | 41 | |
40 | 42 | import com.google.gson.InstanceCreator; |
41 | 43 | import com.google.gson.JsonIOException; |
42 | import com.google.gson.internal.reflect.ReflectionAccessor; | |
44 | import com.google.gson.internal.reflect.ReflectionHelper; | |
43 | 45 | import com.google.gson.reflect.TypeToken; |
44 | 46 | |
45 | 47 | /** |
47 | 49 | */ |
48 | 50 | public final class ConstructorConstructor { |
49 | 51 | private final Map<Type, InstanceCreator<?>> instanceCreators; |
50 | private final ReflectionAccessor accessor = ReflectionAccessor.getInstance(); | |
51 | ||
52 | public ConstructorConstructor(Map<Type, InstanceCreator<?>> instanceCreators) { | |
52 | private final boolean useJdkUnsafe; | |
53 | ||
54 | public ConstructorConstructor(Map<Type, InstanceCreator<?>> instanceCreators, boolean useJdkUnsafe) { | |
53 | 55 | this.instanceCreators = instanceCreators; |
56 | this.useJdkUnsafe = useJdkUnsafe; | |
54 | 57 | } |
55 | 58 | |
56 | 59 | public <T> ObjectConstructor<T> get(TypeToken<T> typeToken) { |
92 | 95 | } |
93 | 96 | |
94 | 97 | // finally try unsafe |
95 | return newUnsafeAllocator(type, rawType); | |
98 | return newUnsafeAllocator(rawType); | |
96 | 99 | } |
97 | 100 | |
98 | 101 | private <T> ObjectConstructor<T> newDefaultConstructor(Class<? super T> rawType) { |
102 | // Cannot invoke constructor of abstract class | |
103 | if (Modifier.isAbstract(rawType.getModifiers())) { | |
104 | return null; | |
105 | } | |
106 | ||
107 | final Constructor<? super T> constructor; | |
99 | 108 | try { |
100 | final Constructor<? super T> constructor = rawType.getDeclaredConstructor(); | |
101 | if (!constructor.isAccessible()) { | |
102 | accessor.makeAccessible(constructor); | |
103 | } | |
104 | return new ObjectConstructor<T>() { | |
105 | @SuppressWarnings("unchecked") // T is the same raw type as is requested | |
106 | @Override public T construct() { | |
107 | try { | |
108 | Object[] args = null; | |
109 | return (T) constructor.newInstance(args); | |
110 | } catch (InstantiationException e) { | |
111 | // TODO: JsonParseException ? | |
112 | throw new RuntimeException("Failed to invoke " + constructor + " with no args", e); | |
113 | } catch (InvocationTargetException e) { | |
114 | // TODO: don't wrap if cause is unchecked! | |
115 | // TODO: JsonParseException ? | |
116 | throw new RuntimeException("Failed to invoke " + constructor + " with no args", | |
117 | e.getTargetException()); | |
118 | } catch (IllegalAccessException e) { | |
119 | throw new AssertionError(e); | |
120 | } | |
121 | } | |
122 | }; | |
109 | constructor = rawType.getDeclaredConstructor(); | |
123 | 110 | } catch (NoSuchMethodException e) { |
124 | 111 | return null; |
125 | 112 | } |
113 | ||
114 | final String exceptionMessage = ReflectionHelper.tryMakeAccessible(constructor); | |
115 | if (exceptionMessage != null) { | |
116 | /* | |
117 | * Create ObjectConstructor which throws exception. | |
118 | * This keeps backward compatibility (compared to returning `null` which | |
119 | * would then choose another way of creating object). | |
120 | * And it supports types which are only serialized but not deserialized | |
121 | * (compared to directly throwing exception here), e.g. when runtime type | |
122 | * of object is inaccessible, but compile-time type is accessible. | |
123 | */ | |
124 | return new ObjectConstructor<T>() { | |
125 | @Override | |
126 | public T construct() { | |
127 | // New exception is created every time to avoid keeping reference | |
128 | // to exception with potentially long stack trace, causing a | |
129 | // memory leak | |
130 | throw new JsonIOException(exceptionMessage); | |
131 | } | |
132 | }; | |
133 | } | |
134 | ||
135 | return new ObjectConstructor<T>() { | |
136 | @Override public T construct() { | |
137 | try { | |
138 | @SuppressWarnings("unchecked") // T is the same raw type as is requested | |
139 | T newInstance = (T) constructor.newInstance(); | |
140 | return newInstance; | |
141 | } catch (InstantiationException e) { | |
142 | // TODO: JsonParseException ? | |
143 | throw new RuntimeException("Failed to invoke " + constructor + " with no args", e); | |
144 | } catch (InvocationTargetException e) { | |
145 | // TODO: don't wrap if cause is unchecked! | |
146 | // TODO: JsonParseException ? | |
147 | throw new RuntimeException("Failed to invoke " + constructor + " with no args", | |
148 | e.getTargetException()); | |
149 | } catch (IllegalAccessException e) { | |
150 | throw new AssertionError(e); | |
151 | } | |
152 | } | |
153 | }; | |
126 | 154 | } |
127 | 155 | |
128 | 156 | /** |
177 | 205 | } |
178 | 206 | |
179 | 207 | if (Map.class.isAssignableFrom(rawType)) { |
180 | if (ConcurrentNavigableMap.class.isAssignableFrom(rawType)) { | |
208 | // Only support creation of EnumMap, but not of custom subtypes; for them type parameters | |
209 | // and constructor parameter might have completely different meaning | |
210 | if (rawType == EnumMap.class) { | |
211 | return new ObjectConstructor<T>() { | |
212 | @Override public T construct() { | |
213 | if (type instanceof ParameterizedType) { | |
214 | Type elementType = ((ParameterizedType) type).getActualTypeArguments()[0]; | |
215 | if (elementType instanceof Class) { | |
216 | @SuppressWarnings("rawtypes") | |
217 | T map = (T) new EnumMap((Class) elementType); | |
218 | return map; | |
219 | } else { | |
220 | throw new JsonIOException("Invalid EnumMap type: " + type.toString()); | |
221 | } | |
222 | } else { | |
223 | throw new JsonIOException("Invalid EnumMap type: " + type.toString()); | |
224 | } | |
225 | } | |
226 | }; | |
227 | } else if (ConcurrentNavigableMap.class.isAssignableFrom(rawType)) { | |
181 | 228 | return new ObjectConstructor<T>() { |
182 | 229 | @Override public T construct() { |
183 | 230 | return (T) new ConcurrentSkipListMap<Object, Object>(); |
214 | 261 | return null; |
215 | 262 | } |
216 | 263 | |
217 | private <T> ObjectConstructor<T> newUnsafeAllocator( | |
218 | final Type type, final Class<? super T> rawType) { | |
219 | return new ObjectConstructor<T>() { | |
220 | private final UnsafeAllocator unsafeAllocator = UnsafeAllocator.create(); | |
221 | @SuppressWarnings("unchecked") | |
222 | @Override public T construct() { | |
223 | try { | |
224 | Object newInstance = unsafeAllocator.newInstance(rawType); | |
225 | return (T) newInstance; | |
226 | } catch (Exception e) { | |
227 | throw new RuntimeException(("Unable to invoke no-args constructor for " + type + ". " | |
228 | + "Registering an InstanceCreator with Gson for this type may fix this problem."), e); | |
229 | } | |
230 | } | |
231 | }; | |
264 | private <T> ObjectConstructor<T> newUnsafeAllocator(final Class<? super T> rawType) { | |
265 | if (useJdkUnsafe) { | |
266 | return new ObjectConstructor<T>() { | |
267 | private final UnsafeAllocator unsafeAllocator = UnsafeAllocator.create(); | |
268 | @Override public T construct() { | |
269 | try { | |
270 | @SuppressWarnings("unchecked") | |
271 | T newInstance = (T) unsafeAllocator.newInstance(rawType); | |
272 | return newInstance; | |
273 | } catch (Exception e) { | |
274 | throw new RuntimeException(("Unable to create instance of " + rawType + ". " | |
275 | + "Registering an InstanceCreator or a TypeAdapter for this type, or adding a no-args " | |
276 | + "constructor may fix this problem."), e); | |
277 | } | |
278 | } | |
279 | }; | |
280 | } else { | |
281 | final String exceptionMessage = "Unable to create instance of " + rawType + "; usage of JDK Unsafe " | |
282 | + "is disabled. Registering an InstanceCreator or a TypeAdapter for this type, adding a no-args " | |
283 | + "constructor, or enabling usage of JDK Unsafe may fix this problem."; | |
284 | return new ObjectConstructor<T>() { | |
285 | @Override public T construct() { | |
286 | throw new JsonIOException(exceptionMessage); | |
287 | } | |
288 | }; | |
289 | } | |
232 | 290 | } |
233 | 291 | |
234 | 292 | @Override public String toString() { |
172 | 172 | return true; |
173 | 173 | } |
174 | 174 | |
175 | if (isAnonymousOrLocal(field.getType())) { | |
175 | if (isAnonymousOrNonStaticLocal(field.getType())) { | |
176 | 176 | return true; |
177 | 177 | } |
178 | 178 | |
198 | 198 | return true; |
199 | 199 | } |
200 | 200 | |
201 | if (isAnonymousOrLocal(clazz)) { | |
201 | if (isAnonymousOrNonStaticLocal(clazz)) { | |
202 | 202 | return true; |
203 | 203 | } |
204 | 204 | |
220 | 220 | return false; |
221 | 221 | } |
222 | 222 | |
223 | private boolean isAnonymousOrLocal(Class<?> clazz) { | |
224 | return !Enum.class.isAssignableFrom(clazz) | |
223 | private boolean isAnonymousOrNonStaticLocal(Class<?> clazz) { | |
224 | return !Enum.class.isAssignableFrom(clazz) && !isStatic(clazz) | |
225 | 225 | && (clazz.isAnonymousClass() || clazz.isLocalClass()); |
226 | 226 | } |
227 | 227 |
14 | 14 | */ |
15 | 15 | package com.google.gson.internal; |
16 | 16 | |
17 | import java.io.IOException; | |
18 | import java.io.InvalidObjectException; | |
19 | import java.io.ObjectInputStream; | |
17 | 20 | import java.io.ObjectStreamException; |
18 | 21 | import java.math.BigDecimal; |
19 | 22 | |
76 | 79 | return new BigDecimal(value); |
77 | 80 | } |
78 | 81 | |
82 | private void readObject(ObjectInputStream in) throws IOException { | |
83 | // Don't permit directly deserializing this class; writeReplace() should have written a replacement | |
84 | throw new InvalidObjectException("Deserialization is unsupported"); | |
85 | } | |
86 | ||
79 | 87 | @Override |
80 | 88 | public int hashCode() { |
81 | 89 | return value.hashCode(); |
0 | /* | |
1 | * Copyright (C) 2010 The Android Open Source Project | |
2 | * Copyright (C) 2012 Google Inc. | |
3 | * | |
4 | * Licensed under the Apache License, Version 2.0 (the "License"); | |
5 | * you may not use this file except in compliance with the License. | |
6 | * You may obtain a copy of the License at | |
7 | * | |
8 | * http://www.apache.org/licenses/LICENSE-2.0 | |
9 | * | |
10 | * Unless required by applicable law or agreed to in writing, software | |
11 | * distributed under the License is distributed on an "AS IS" BASIS, | |
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | * See the License for the specific language governing permissions and | |
14 | * limitations under the License. | |
15 | */ | |
16 | ||
17 | package com.google.gson.internal; | |
18 | ||
19 | import java.io.ObjectStreamException; | |
20 | import java.io.Serializable; | |
21 | import java.util.AbstractMap; | |
22 | import java.util.AbstractSet; | |
23 | import java.util.Arrays; | |
24 | import java.util.Comparator; | |
25 | import java.util.ConcurrentModificationException; | |
26 | import java.util.Iterator; | |
27 | import java.util.LinkedHashMap; | |
28 | import java.util.NoSuchElementException; | |
29 | import java.util.Set; | |
30 | ||
31 | /** | |
32 | * A map of comparable keys to values. Unlike {@code TreeMap}, this class uses | |
33 | * insertion order for iteration order. Comparison order is only used as an | |
34 | * optimization for efficient insertion and removal. | |
35 | * | |
36 | * <p>This implementation was derived from Android 4.1's TreeMap and | |
37 | * LinkedHashMap classes. | |
38 | */ | |
39 | public final class LinkedHashTreeMap<K, V> extends AbstractMap<K, V> implements Serializable { | |
40 | @SuppressWarnings({ "unchecked", "rawtypes" }) // to avoid Comparable<Comparable<Comparable<...>>> | |
41 | private static final Comparator<Comparable> NATURAL_ORDER = new Comparator<Comparable>() { | |
42 | public int compare(Comparable a, Comparable b) { | |
43 | return a.compareTo(b); | |
44 | } | |
45 | }; | |
46 | ||
47 | Comparator<? super K> comparator; | |
48 | Node<K, V>[] table; | |
49 | final Node<K, V> header; | |
50 | int size = 0; | |
51 | int modCount = 0; | |
52 | int threshold; | |
53 | ||
54 | /** | |
55 | * Create a natural order, empty tree map whose keys must be mutually | |
56 | * comparable and non-null. | |
57 | */ | |
58 | @SuppressWarnings("unchecked") // unsafe! this assumes K is comparable | |
59 | public LinkedHashTreeMap() { | |
60 | this((Comparator<? super K>) NATURAL_ORDER); | |
61 | } | |
62 | ||
63 | /** | |
64 | * Create a tree map ordered by {@code comparator}. This map's keys may only | |
65 | * be null if {@code comparator} permits. | |
66 | * | |
67 | * @param comparator the comparator to order elements with, or {@code null} to | |
68 | * use the natural ordering. | |
69 | */ | |
70 | @SuppressWarnings({ "unchecked", "rawtypes" }) // unsafe! if comparator is null, this assumes K is comparable | |
71 | public LinkedHashTreeMap(Comparator<? super K> comparator) { | |
72 | this.comparator = comparator != null | |
73 | ? comparator | |
74 | : (Comparator) NATURAL_ORDER; | |
75 | this.header = new Node<K, V>(); | |
76 | this.table = new Node[16]; // TODO: sizing/resizing policies | |
77 | this.threshold = (table.length / 2) + (table.length / 4); // 3/4 capacity | |
78 | } | |
79 | ||
80 | @Override public int size() { | |
81 | return size; | |
82 | } | |
83 | ||
84 | @Override public V get(Object key) { | |
85 | Node<K, V> node = findByObject(key); | |
86 | return node != null ? node.value : null; | |
87 | } | |
88 | ||
89 | @Override public boolean containsKey(Object key) { | |
90 | return findByObject(key) != null; | |
91 | } | |
92 | ||
93 | @Override public V put(K key, V value) { | |
94 | if (key == null) { | |
95 | throw new NullPointerException("key == null"); | |
96 | } | |
97 | Node<K, V> created = find(key, true); | |
98 | V result = created.value; | |
99 | created.value = value; | |
100 | return result; | |
101 | } | |
102 | ||
103 | @Override public void clear() { | |
104 | Arrays.fill(table, null); | |
105 | size = 0; | |
106 | modCount++; | |
107 | ||
108 | // Clear all links to help GC | |
109 | Node<K, V> header = this.header; | |
110 | for (Node<K, V> e = header.next; e != header; ) { | |
111 | Node<K, V> next = e.next; | |
112 | e.next = e.prev = null; | |
113 | e = next; | |
114 | } | |
115 | ||
116 | header.next = header.prev = header; | |
117 | } | |
118 | ||
119 | @Override public V remove(Object key) { | |
120 | Node<K, V> node = removeInternalByKey(key); | |
121 | return node != null ? node.value : null; | |
122 | } | |
123 | ||
124 | /** | |
125 | * Returns the node at or adjacent to the given key, creating it if requested. | |
126 | * | |
127 | * @throws ClassCastException if {@code key} and the tree's keys aren't | |
128 | * mutually comparable. | |
129 | */ | |
130 | Node<K, V> find(K key, boolean create) { | |
131 | Comparator<? super K> comparator = this.comparator; | |
132 | Node<K, V>[] table = this.table; | |
133 | int hash = secondaryHash(key.hashCode()); | |
134 | int index = hash & (table.length - 1); | |
135 | Node<K, V> nearest = table[index]; | |
136 | int comparison = 0; | |
137 | ||
138 | if (nearest != null) { | |
139 | // Micro-optimization: avoid polymorphic calls to Comparator.compare(). | |
140 | @SuppressWarnings("unchecked") // Throws a ClassCastException below if there's trouble. | |
141 | Comparable<Object> comparableKey = (comparator == NATURAL_ORDER) | |
142 | ? (Comparable<Object>) key | |
143 | : null; | |
144 | ||
145 | while (true) { | |
146 | comparison = (comparableKey != null) | |
147 | ? comparableKey.compareTo(nearest.key) | |
148 | : comparator.compare(key, nearest.key); | |
149 | ||
150 | // We found the requested key. | |
151 | if (comparison == 0) { | |
152 | return nearest; | |
153 | } | |
154 | ||
155 | // If it exists, the key is in a subtree. Go deeper. | |
156 | Node<K, V> child = (comparison < 0) ? nearest.left : nearest.right; | |
157 | if (child == null) { | |
158 | break; | |
159 | } | |
160 | ||
161 | nearest = child; | |
162 | } | |
163 | } | |
164 | ||
165 | // The key doesn't exist in this tree. | |
166 | if (!create) { | |
167 | return null; | |
168 | } | |
169 | ||
170 | // Create the node and add it to the tree or the table. | |
171 | Node<K, V> header = this.header; | |
172 | Node<K, V> created; | |
173 | if (nearest == null) { | |
174 | // Check that the value is comparable if we didn't do any comparisons. | |
175 | if (comparator == NATURAL_ORDER && !(key instanceof Comparable)) { | |
176 | throw new ClassCastException(key.getClass().getName() + " is not Comparable"); | |
177 | } | |
178 | created = new Node<K, V>(nearest, key, hash, header, header.prev); | |
179 | table[index] = created; | |
180 | } else { | |
181 | created = new Node<K, V>(nearest, key, hash, header, header.prev); | |
182 | if (comparison < 0) { // nearest.key is higher | |
183 | nearest.left = created; | |
184 | } else { // comparison > 0, nearest.key is lower | |
185 | nearest.right = created; | |
186 | } | |
187 | rebalance(nearest, true); | |
188 | } | |
189 | ||
190 | if (size++ > threshold) { | |
191 | doubleCapacity(); | |
192 | } | |
193 | modCount++; | |
194 | ||
195 | return created; | |
196 | } | |
197 | ||
198 | @SuppressWarnings("unchecked") | |
199 | Node<K, V> findByObject(Object key) { | |
200 | try { | |
201 | return key != null ? find((K) key, false) : null; | |
202 | } catch (ClassCastException e) { | |
203 | return null; | |
204 | } | |
205 | } | |
206 | ||
207 | /** | |
208 | * Returns this map's entry that has the same key and value as {@code | |
209 | * entry}, or null if this map has no such entry. | |
210 | * | |
211 | * <p>This method uses the comparator for key equality rather than {@code | |
212 | * equals}. If this map's comparator isn't consistent with equals (such as | |
213 | * {@code String.CASE_INSENSITIVE_ORDER}), then {@code remove()} and {@code | |
214 | * contains()} will violate the collections API. | |
215 | */ | |
216 | Node<K, V> findByEntry(Entry<?, ?> entry) { | |
217 | Node<K, V> mine = findByObject(entry.getKey()); | |
218 | boolean valuesEqual = mine != null && equal(mine.value, entry.getValue()); | |
219 | return valuesEqual ? mine : null; | |
220 | } | |
221 | ||
222 | private boolean equal(Object a, Object b) { | |
223 | return a == b || (a != null && a.equals(b)); | |
224 | } | |
225 | ||
226 | /** | |
227 | * Applies a supplemental hash function to a given hashCode, which defends | |
228 | * against poor quality hash functions. This is critical because HashMap | |
229 | * uses power-of-two length hash tables, that otherwise encounter collisions | |
230 | * for hashCodes that do not differ in lower or upper bits. | |
231 | */ | |
232 | private static int secondaryHash(int h) { | |
233 | // Doug Lea's supplemental hash function | |
234 | h ^= (h >>> 20) ^ (h >>> 12); | |
235 | return h ^ (h >>> 7) ^ (h >>> 4); | |
236 | } | |
237 | ||
238 | /** | |
239 | * Removes {@code node} from this tree, rearranging the tree's structure as | |
240 | * necessary. | |
241 | * | |
242 | * @param unlink true to also unlink this node from the iteration linked list. | |
243 | */ | |
244 | void removeInternal(Node<K, V> node, boolean unlink) { | |
245 | if (unlink) { | |
246 | node.prev.next = node.next; | |
247 | node.next.prev = node.prev; | |
248 | node.next = node.prev = null; // Help the GC (for performance) | |
249 | } | |
250 | ||
251 | Node<K, V> left = node.left; | |
252 | Node<K, V> right = node.right; | |
253 | Node<K, V> originalParent = node.parent; | |
254 | if (left != null && right != null) { | |
255 | ||
256 | /* | |
257 | * To remove a node with both left and right subtrees, move an | |
258 | * adjacent node from one of those subtrees into this node's place. | |
259 | * | |
260 | * Removing the adjacent node may change this node's subtrees. This | |
261 | * node may no longer have two subtrees once the adjacent node is | |
262 | * gone! | |
263 | */ | |
264 | ||
265 | Node<K, V> adjacent = (left.height > right.height) ? left.last() : right.first(); | |
266 | removeInternal(adjacent, false); // takes care of rebalance and size-- | |
267 | ||
268 | int leftHeight = 0; | |
269 | left = node.left; | |
270 | if (left != null) { | |
271 | leftHeight = left.height; | |
272 | adjacent.left = left; | |
273 | left.parent = adjacent; | |
274 | node.left = null; | |
275 | } | |
276 | int rightHeight = 0; | |
277 | right = node.right; | |
278 | if (right != null) { | |
279 | rightHeight = right.height; | |
280 | adjacent.right = right; | |
281 | right.parent = adjacent; | |
282 | node.right = null; | |
283 | } | |
284 | adjacent.height = Math.max(leftHeight, rightHeight) + 1; | |
285 | replaceInParent(node, adjacent); | |
286 | return; | |
287 | } else if (left != null) { | |
288 | replaceInParent(node, left); | |
289 | node.left = null; | |
290 | } else if (right != null) { | |
291 | replaceInParent(node, right); | |
292 | node.right = null; | |
293 | } else { | |
294 | replaceInParent(node, null); | |
295 | } | |
296 | ||
297 | rebalance(originalParent, false); | |
298 | size--; | |
299 | modCount++; | |
300 | } | |
301 | ||
302 | Node<K, V> removeInternalByKey(Object key) { | |
303 | Node<K, V> node = findByObject(key); | |
304 | if (node != null) { | |
305 | removeInternal(node, true); | |
306 | } | |
307 | return node; | |
308 | } | |
309 | ||
310 | private void replaceInParent(Node<K, V> node, Node<K, V> replacement) { | |
311 | Node<K, V> parent = node.parent; | |
312 | node.parent = null; | |
313 | if (replacement != null) { | |
314 | replacement.parent = parent; | |
315 | } | |
316 | ||
317 | if (parent != null) { | |
318 | if (parent.left == node) { | |
319 | parent.left = replacement; | |
320 | } else { | |
321 | assert (parent.right == node); | |
322 | parent.right = replacement; | |
323 | } | |
324 | } else { | |
325 | int index = node.hash & (table.length - 1); | |
326 | table[index] = replacement; | |
327 | } | |
328 | } | |
329 | ||
330 | /** | |
331 | * Rebalances the tree by making any AVL rotations necessary between the | |
332 | * newly-unbalanced node and the tree's root. | |
333 | * | |
334 | * @param insert true if the node was unbalanced by an insert; false if it | |
335 | * was by a removal. | |
336 | */ | |
337 | private void rebalance(Node<K, V> unbalanced, boolean insert) { | |
338 | for (Node<K, V> node = unbalanced; node != null; node = node.parent) { | |
339 | Node<K, V> left = node.left; | |
340 | Node<K, V> right = node.right; | |
341 | int leftHeight = left != null ? left.height : 0; | |
342 | int rightHeight = right != null ? right.height : 0; | |
343 | ||
344 | int delta = leftHeight - rightHeight; | |
345 | if (delta == -2) { | |
346 | Node<K, V> rightLeft = right.left; | |
347 | Node<K, V> rightRight = right.right; | |
348 | int rightRightHeight = rightRight != null ? rightRight.height : 0; | |
349 | int rightLeftHeight = rightLeft != null ? rightLeft.height : 0; | |
350 | ||
351 | int rightDelta = rightLeftHeight - rightRightHeight; | |
352 | if (rightDelta == -1 || (rightDelta == 0 && !insert)) { | |
353 | rotateLeft(node); // AVL right right | |
354 | } else { | |
355 | assert (rightDelta == 1); | |
356 | rotateRight(right); // AVL right left | |
357 | rotateLeft(node); | |
358 | } | |
359 | if (insert) { | |
360 | break; // no further rotations will be necessary | |
361 | } | |
362 | ||
363 | } else if (delta == 2) { | |
364 | Node<K, V> leftLeft = left.left; | |
365 | Node<K, V> leftRight = left.right; | |
366 | int leftRightHeight = leftRight != null ? leftRight.height : 0; | |
367 | int leftLeftHeight = leftLeft != null ? leftLeft.height : 0; | |
368 | ||
369 | int leftDelta = leftLeftHeight - leftRightHeight; | |
370 | if (leftDelta == 1 || (leftDelta == 0 && !insert)) { | |
371 | rotateRight(node); // AVL left left | |
372 | } else { | |
373 | assert (leftDelta == -1); | |
374 | rotateLeft(left); // AVL left right | |
375 | rotateRight(node); | |
376 | } | |
377 | if (insert) { | |
378 | break; // no further rotations will be necessary | |
379 | } | |
380 | ||
381 | } else if (delta == 0) { | |
382 | node.height = leftHeight + 1; // leftHeight == rightHeight | |
383 | if (insert) { | |
384 | break; // the insert caused balance, so rebalancing is done! | |
385 | } | |
386 | ||
387 | } else { | |
388 | assert (delta == -1 || delta == 1); | |
389 | node.height = Math.max(leftHeight, rightHeight) + 1; | |
390 | if (!insert) { | |
391 | break; // the height hasn't changed, so rebalancing is done! | |
392 | } | |
393 | } | |
394 | } | |
395 | } | |
396 | ||
397 | /** | |
398 | * Rotates the subtree so that its root's right child is the new root. | |
399 | */ | |
400 | private void rotateLeft(Node<K, V> root) { | |
401 | Node<K, V> left = root.left; | |
402 | Node<K, V> pivot = root.right; | |
403 | Node<K, V> pivotLeft = pivot.left; | |
404 | Node<K, V> pivotRight = pivot.right; | |
405 | ||
406 | // move the pivot's left child to the root's right | |
407 | root.right = pivotLeft; | |
408 | if (pivotLeft != null) { | |
409 | pivotLeft.parent = root; | |
410 | } | |
411 | ||
412 | replaceInParent(root, pivot); | |
413 | ||
414 | // move the root to the pivot's left | |
415 | pivot.left = root; | |
416 | root.parent = pivot; | |
417 | ||
418 | // fix heights | |
419 | root.height = Math.max(left != null ? left.height : 0, | |
420 | pivotLeft != null ? pivotLeft.height : 0) + 1; | |
421 | pivot.height = Math.max(root.height, | |
422 | pivotRight != null ? pivotRight.height : 0) + 1; | |
423 | } | |
424 | ||
425 | /** | |
426 | * Rotates the subtree so that its root's left child is the new root. | |
427 | */ | |
428 | private void rotateRight(Node<K, V> root) { | |
429 | Node<K, V> pivot = root.left; | |
430 | Node<K, V> right = root.right; | |
431 | Node<K, V> pivotLeft = pivot.left; | |
432 | Node<K, V> pivotRight = pivot.right; | |
433 | ||
434 | // move the pivot's right child to the root's left | |
435 | root.left = pivotRight; | |
436 | if (pivotRight != null) { | |
437 | pivotRight.parent = root; | |
438 | } | |
439 | ||
440 | replaceInParent(root, pivot); | |
441 | ||
442 | // move the root to the pivot's right | |
443 | pivot.right = root; | |
444 | root.parent = pivot; | |
445 | ||
446 | // fixup heights | |
447 | root.height = Math.max(right != null ? right.height : 0, | |
448 | pivotRight != null ? pivotRight.height : 0) + 1; | |
449 | pivot.height = Math.max(root.height, | |
450 | pivotLeft != null ? pivotLeft.height : 0) + 1; | |
451 | } | |
452 | ||
453 | private EntrySet entrySet; | |
454 | private KeySet keySet; | |
455 | ||
456 | @Override public Set<Entry<K, V>> entrySet() { | |
457 | EntrySet result = entrySet; | |
458 | return result != null ? result : (entrySet = new EntrySet()); | |
459 | } | |
460 | ||
461 | @Override public Set<K> keySet() { | |
462 | KeySet result = keySet; | |
463 | return result != null ? result : (keySet = new KeySet()); | |
464 | } | |
465 | ||
466 | static final class Node<K, V> implements Entry<K, V> { | |
467 | Node<K, V> parent; | |
468 | Node<K, V> left; | |
469 | Node<K, V> right; | |
470 | Node<K, V> next; | |
471 | Node<K, V> prev; | |
472 | final K key; | |
473 | final int hash; | |
474 | V value; | |
475 | int height; | |
476 | ||
477 | /** Create the header entry */ | |
478 | Node() { | |
479 | key = null; | |
480 | hash = -1; | |
481 | next = prev = this; | |
482 | } | |
483 | ||
484 | /** Create a regular entry */ | |
485 | Node(Node<K, V> parent, K key, int hash, Node<K, V> next, Node<K, V> prev) { | |
486 | this.parent = parent; | |
487 | this.key = key; | |
488 | this.hash = hash; | |
489 | this.height = 1; | |
490 | this.next = next; | |
491 | this.prev = prev; | |
492 | prev.next = this; | |
493 | next.prev = this; | |
494 | } | |
495 | ||
496 | public K getKey() { | |
497 | return key; | |
498 | } | |
499 | ||
500 | public V getValue() { | |
501 | return value; | |
502 | } | |
503 | ||
504 | public V setValue(V value) { | |
505 | V oldValue = this.value; | |
506 | this.value = value; | |
507 | return oldValue; | |
508 | } | |
509 | ||
510 | @SuppressWarnings("rawtypes") | |
511 | @Override public boolean equals(Object o) { | |
512 | if (o instanceof Entry) { | |
513 | Entry other = (Entry) o; | |
514 | return (key == null ? other.getKey() == null : key.equals(other.getKey())) | |
515 | && (value == null ? other.getValue() == null : value.equals(other.getValue())); | |
516 | } | |
517 | return false; | |
518 | } | |
519 | ||
520 | @Override public int hashCode() { | |
521 | return (key == null ? 0 : key.hashCode()) | |
522 | ^ (value == null ? 0 : value.hashCode()); | |
523 | } | |
524 | ||
525 | @Override public String toString() { | |
526 | return key + "=" + value; | |
527 | } | |
528 | ||
529 | /** | |
530 | * Returns the first node in this subtree. | |
531 | */ | |
532 | public Node<K, V> first() { | |
533 | Node<K, V> node = this; | |
534 | Node<K, V> child = node.left; | |
535 | while (child != null) { | |
536 | node = child; | |
537 | child = node.left; | |
538 | } | |
539 | return node; | |
540 | } | |
541 | ||
542 | /** | |
543 | * Returns the last node in this subtree. | |
544 | */ | |
545 | public Node<K, V> last() { | |
546 | Node<K, V> node = this; | |
547 | Node<K, V> child = node.right; | |
548 | while (child != null) { | |
549 | node = child; | |
550 | child = node.right; | |
551 | } | |
552 | return node; | |
553 | } | |
554 | } | |
555 | ||
556 | private void doubleCapacity() { | |
557 | table = doubleCapacity(table); | |
558 | threshold = (table.length / 2) + (table.length / 4); // 3/4 capacity | |
559 | } | |
560 | ||
561 | /** | |
562 | * Returns a new array containing the same nodes as {@code oldTable}, but with | |
563 | * twice as many trees, each of (approximately) half the previous size. | |
564 | */ | |
565 | static <K, V> Node<K, V>[] doubleCapacity(Node<K, V>[] oldTable) { | |
566 | // TODO: don't do anything if we're already at MAX_CAPACITY | |
567 | int oldCapacity = oldTable.length; | |
568 | @SuppressWarnings("unchecked") // Arrays and generics don't get along. | |
569 | Node<K, V>[] newTable = new Node[oldCapacity * 2]; | |
570 | AvlIterator<K, V> iterator = new AvlIterator<K, V>(); | |
571 | AvlBuilder<K, V> leftBuilder = new AvlBuilder<K, V>(); | |
572 | AvlBuilder<K, V> rightBuilder = new AvlBuilder<K, V>(); | |
573 | ||
574 | // Split each tree into two trees. | |
575 | for (int i = 0; i < oldCapacity; i++) { | |
576 | Node<K, V> root = oldTable[i]; | |
577 | if (root == null) { | |
578 | continue; | |
579 | } | |
580 | ||
581 | // Compute the sizes of the left and right trees. | |
582 | iterator.reset(root); | |
583 | int leftSize = 0; | |
584 | int rightSize = 0; | |
585 | for (Node<K, V> node; (node = iterator.next()) != null; ) { | |
586 | if ((node.hash & oldCapacity) == 0) { | |
587 | leftSize++; | |
588 | } else { | |
589 | rightSize++; | |
590 | } | |
591 | } | |
592 | ||
593 | // Split the tree into two. | |
594 | leftBuilder.reset(leftSize); | |
595 | rightBuilder.reset(rightSize); | |
596 | iterator.reset(root); | |
597 | for (Node<K, V> node; (node = iterator.next()) != null; ) { | |
598 | if ((node.hash & oldCapacity) == 0) { | |
599 | leftBuilder.add(node); | |
600 | } else { | |
601 | rightBuilder.add(node); | |
602 | } | |
603 | } | |
604 | ||
605 | // Populate the enlarged array with these new roots. | |
606 | newTable[i] = leftSize > 0 ? leftBuilder.root() : null; | |
607 | newTable[i + oldCapacity] = rightSize > 0 ? rightBuilder.root() : null; | |
608 | } | |
609 | return newTable; | |
610 | } | |
611 | ||
612 | /** | |
613 | * Walks an AVL tree in iteration order. Once a node has been returned, its | |
614 | * left, right and parent links are <strong>no longer used</strong>. For this | |
615 | * reason it is safe to transform these links as you walk a tree. | |
616 | * | |
617 | * <p><strong>Warning:</strong> this iterator is destructive. It clears the | |
618 | * parent node of all nodes in the tree. It is an error to make a partial | |
619 | * iteration of a tree. | |
620 | */ | |
621 | static class AvlIterator<K, V> { | |
622 | /** This stack is a singly linked list, linked by the 'parent' field. */ | |
623 | private Node<K, V> stackTop; | |
624 | ||
625 | void reset(Node<K, V> root) { | |
626 | Node<K, V> stackTop = null; | |
627 | for (Node<K, V> n = root; n != null; n = n.left) { | |
628 | n.parent = stackTop; | |
629 | stackTop = n; // Stack push. | |
630 | } | |
631 | this.stackTop = stackTop; | |
632 | } | |
633 | ||
634 | public Node<K, V> next() { | |
635 | Node<K, V> stackTop = this.stackTop; | |
636 | if (stackTop == null) { | |
637 | return null; | |
638 | } | |
639 | Node<K, V> result = stackTop; | |
640 | stackTop = result.parent; | |
641 | result.parent = null; | |
642 | for (Node<K, V> n = result.right; n != null; n = n.left) { | |
643 | n.parent = stackTop; | |
644 | stackTop = n; // Stack push. | |
645 | } | |
646 | this.stackTop = stackTop; | |
647 | return result; | |
648 | } | |
649 | } | |
650 | ||
651 | /** | |
652 | * Builds AVL trees of a predetermined size by accepting nodes of increasing | |
653 | * value. To use: | |
654 | * <ol> | |
655 | * <li>Call {@link #reset} to initialize the target size <i>size</i>. | |
656 | * <li>Call {@link #add} <i>size</i> times with increasing values. | |
657 | * <li>Call {@link #root} to get the root of the balanced tree. | |
658 | * </ol> | |
659 | * | |
660 | * <p>The returned tree will satisfy the AVL constraint: for every node | |
661 | * <i>N</i>, the height of <i>N.left</i> and <i>N.right</i> is different by at | |
662 | * most 1. It accomplishes this by omitting deepest-level leaf nodes when | |
663 | * building trees whose size isn't a power of 2 minus 1. | |
664 | * | |
665 | * <p>Unlike rebuilding a tree from scratch, this approach requires no value | |
666 | * comparisons. Using this class to create a tree of size <i>S</i> is | |
667 | * {@code O(S)}. | |
668 | */ | |
669 | final static class AvlBuilder<K, V> { | |
670 | /** This stack is a singly linked list, linked by the 'parent' field. */ | |
671 | private Node<K, V> stack; | |
672 | private int leavesToSkip; | |
673 | private int leavesSkipped; | |
674 | private int size; | |
675 | ||
676 | void reset(int targetSize) { | |
677 | // compute the target tree size. This is a power of 2 minus one, like 15 or 31. | |
678 | int treeCapacity = Integer.highestOneBit(targetSize) * 2 - 1; | |
679 | leavesToSkip = treeCapacity - targetSize; | |
680 | size = 0; | |
681 | leavesSkipped = 0; | |
682 | stack = null; | |
683 | } | |
684 | ||
685 | void add(Node<K, V> node) { | |
686 | node.left = node.parent = node.right = null; | |
687 | node.height = 1; | |
688 | ||
689 | // Skip a leaf if necessary. | |
690 | if (leavesToSkip > 0 && (size & 1) == 0) { | |
691 | size++; | |
692 | leavesToSkip--; | |
693 | leavesSkipped++; | |
694 | } | |
695 | ||
696 | node.parent = stack; | |
697 | stack = node; // Stack push. | |
698 | size++; | |
699 | ||
700 | // Skip a leaf if necessary. | |
701 | if (leavesToSkip > 0 && (size & 1) == 0) { | |
702 | size++; | |
703 | leavesToSkip--; | |
704 | leavesSkipped++; | |
705 | } | |
706 | ||
707 | /* | |
708 | * Combine 3 nodes into subtrees whenever the size is one less than a | |
709 | * multiple of 4. For example we combine the nodes A, B, C into a | |
710 | * 3-element tree with B as the root. | |
711 | * | |
712 | * Combine two subtrees and a spare single value whenever the size is one | |
713 | * less than a multiple of 8. For example at 8 we may combine subtrees | |
714 | * (A B C) and (E F G) with D as the root to form ((A B C) D (E F G)). | |
715 | * | |
716 | * Just as we combine single nodes when size nears a multiple of 4, and | |
717 | * 3-element trees when size nears a multiple of 8, we combine subtrees of | |
718 | * size (N-1) whenever the total size is 2N-1 whenever N is a power of 2. | |
719 | */ | |
720 | for (int scale = 4; (size & scale - 1) == scale - 1; scale *= 2) { | |
721 | if (leavesSkipped == 0) { | |
722 | // Pop right, center and left, then make center the top of the stack. | |
723 | Node<K, V> right = stack; | |
724 | Node<K, V> center = right.parent; | |
725 | Node<K, V> left = center.parent; | |
726 | center.parent = left.parent; | |
727 | stack = center; | |
728 | // Construct a tree. | |
729 | center.left = left; | |
730 | center.right = right; | |
731 | center.height = right.height + 1; | |
732 | left.parent = center; | |
733 | right.parent = center; | |
734 | } else if (leavesSkipped == 1) { | |
735 | // Pop right and center, then make center the top of the stack. | |
736 | Node<K, V> right = stack; | |
737 | Node<K, V> center = right.parent; | |
738 | stack = center; | |
739 | // Construct a tree with no left child. | |
740 | center.right = right; | |
741 | center.height = right.height + 1; | |
742 | right.parent = center; | |
743 | leavesSkipped = 0; | |
744 | } else if (leavesSkipped == 2) { | |
745 | leavesSkipped = 0; | |
746 | } | |
747 | } | |
748 | } | |
749 | ||
750 | Node<K, V> root() { | |
751 | Node<K, V> stackTop = this.stack; | |
752 | if (stackTop.parent != null) { | |
753 | throw new IllegalStateException(); | |
754 | } | |
755 | return stackTop; | |
756 | } | |
757 | } | |
758 | ||
759 | private abstract class LinkedTreeMapIterator<T> implements Iterator<T> { | |
760 | Node<K, V> next = header.next; | |
761 | Node<K, V> lastReturned = null; | |
762 | int expectedModCount = modCount; | |
763 | ||
764 | LinkedTreeMapIterator() { | |
765 | } | |
766 | ||
767 | public final boolean hasNext() { | |
768 | return next != header; | |
769 | } | |
770 | ||
771 | final Node<K, V> nextNode() { | |
772 | Node<K, V> e = next; | |
773 | if (e == header) { | |
774 | throw new NoSuchElementException(); | |
775 | } | |
776 | if (modCount != expectedModCount) { | |
777 | throw new ConcurrentModificationException(); | |
778 | } | |
779 | next = e.next; | |
780 | return lastReturned = e; | |
781 | } | |
782 | ||
783 | public final void remove() { | |
784 | if (lastReturned == null) { | |
785 | throw new IllegalStateException(); | |
786 | } | |
787 | removeInternal(lastReturned, true); | |
788 | lastReturned = null; | |
789 | expectedModCount = modCount; | |
790 | } | |
791 | } | |
792 | ||
793 | final class EntrySet extends AbstractSet<Entry<K, V>> { | |
794 | @Override public int size() { | |
795 | return size; | |
796 | } | |
797 | ||
798 | @Override public Iterator<Entry<K, V>> iterator() { | |
799 | return new LinkedTreeMapIterator<Entry<K, V>>() { | |
800 | public Entry<K, V> next() { | |
801 | return nextNode(); | |
802 | } | |
803 | }; | |
804 | } | |
805 | ||
806 | @Override public boolean contains(Object o) { | |
807 | return o instanceof Entry && findByEntry((Entry<?, ?>) o) != null; | |
808 | } | |
809 | ||
810 | @Override public boolean remove(Object o) { | |
811 | if (!(o instanceof Entry)) { | |
812 | return false; | |
813 | } | |
814 | ||
815 | Node<K, V> node = findByEntry((Entry<?, ?>) o); | |
816 | if (node == null) { | |
817 | return false; | |
818 | } | |
819 | removeInternal(node, true); | |
820 | return true; | |
821 | } | |
822 | ||
823 | @Override public void clear() { | |
824 | LinkedHashTreeMap.this.clear(); | |
825 | } | |
826 | } | |
827 | ||
828 | final class KeySet extends AbstractSet<K> { | |
829 | @Override public int size() { | |
830 | return size; | |
831 | } | |
832 | ||
833 | @Override public Iterator<K> iterator() { | |
834 | return new LinkedTreeMapIterator<K>() { | |
835 | public K next() { | |
836 | return nextNode().key; | |
837 | } | |
838 | }; | |
839 | } | |
840 | ||
841 | @Override public boolean contains(Object o) { | |
842 | return containsKey(o); | |
843 | } | |
844 | ||
845 | @Override public boolean remove(Object key) { | |
846 | return removeInternalByKey(key) != null; | |
847 | } | |
848 | ||
849 | @Override public void clear() { | |
850 | LinkedHashTreeMap.this.clear(); | |
851 | } | |
852 | } | |
853 | ||
854 | /** | |
855 | * If somebody is unlucky enough to have to serialize one of these, serialize | |
856 | * it as a LinkedHashMap so that they won't need Gson on the other side to | |
857 | * deserialize it. Using serialization defeats our DoS defence, so most apps | |
858 | * shouldn't use it. | |
859 | */ | |
860 | private Object writeReplace() throws ObjectStreamException { | |
861 | return new LinkedHashMap<K, V>(this); | |
862 | } | |
863 | } |
16 | 16 | |
17 | 17 | package com.google.gson.internal; |
18 | 18 | |
19 | import java.io.IOException; | |
20 | import java.io.InvalidObjectException; | |
21 | import java.io.ObjectInputStream; | |
19 | 22 | import java.io.ObjectStreamException; |
20 | 23 | import java.io.Serializable; |
21 | 24 | import java.util.AbstractMap; |
626 | 629 | private Object writeReplace() throws ObjectStreamException { |
627 | 630 | return new LinkedHashMap<K, V>(this); |
628 | 631 | } |
632 | ||
633 | private void readObject(ObjectInputStream in) throws IOException { | |
634 | // Don't permit directly deserializing this class; writeReplace() should have written a replacement | |
635 | throw new InvalidObjectException("Deserialization is unsupported"); | |
636 | } | |
629 | 637 | } |
100 | 100 | return new UnsafeAllocator() { |
101 | 101 | @Override |
102 | 102 | public <T> T newInstance(Class<T> c) { |
103 | throw new UnsupportedOperationException("Cannot allocate " + c); | |
103 | throw new UnsupportedOperationException("Cannot allocate " + c + ". Usage of JDK sun.misc.Unsafe is enabled, " | |
104 | + "but it could not be used. Make sure your runtime is configured correctly."); | |
104 | 105 | } |
105 | 106 | }; |
106 | 107 | } |
71 | 71 | in.nextNull(); |
72 | 72 | return null; |
73 | 73 | } |
74 | return deserializeToDate(in.nextString()); | |
74 | return deserializeToDate(in); | |
75 | 75 | } |
76 | 76 | |
77 | private synchronized Date deserializeToDate(String json) { | |
78 | for (DateFormat dateFormat : dateFormats) { | |
79 | try { | |
80 | return dateFormat.parse(json); | |
81 | } catch (ParseException ignored) {} | |
77 | private Date deserializeToDate(JsonReader in) throws IOException { | |
78 | String s = in.nextString(); | |
79 | synchronized (dateFormats) { | |
80 | for (DateFormat dateFormat : dateFormats) { | |
81 | try { | |
82 | return dateFormat.parse(s); | |
83 | } catch (ParseException ignored) {} | |
84 | } | |
82 | 85 | } |
83 | 86 | try { |
84 | return ISO8601Utils.parse(json, new ParsePosition(0)); | |
87 | return ISO8601Utils.parse(s, new ParsePosition(0)); | |
85 | 88 | } catch (ParseException e) { |
86 | throw new JsonSyntaxException(json, e); | |
89 | throw new JsonSyntaxException("Failed parsing '" + s + "' as Date; at path " + in.getPreviousPath(), e); | |
87 | 90 | } |
88 | 91 | } |
89 | 92 | |
90 | @Override public synchronized void write(JsonWriter out, Date value) throws IOException { | |
93 | @Override public void write(JsonWriter out, Date value) throws IOException { | |
91 | 94 | if (value == null) { |
92 | 95 | out.nullValue(); |
93 | 96 | return; |
94 | 97 | } |
95 | String dateFormatAsString = dateFormats.get(0).format(value); | |
98 | ||
99 | DateFormat dateFormat = dateFormats.get(0); | |
100 | String dateFormatAsString; | |
101 | synchronized (dateFormats) { | |
102 | dateFormatAsString = dateFormat.format(value); | |
103 | } | |
96 | 104 | out.value(dateFormatAsString); |
97 | 105 | } |
98 | ||
99 | ||
100 | 106 | } |
0 | /* | |
1 | * Copyright (C) 2008 Google Inc. | |
2 | * | |
3 | * Licensed under the Apache License, Version 2.0 (the "License"); | |
4 | * you may not use this file except in compliance with the License. | |
5 | * You may obtain a copy of the License at | |
6 | * | |
7 | * http://www.apache.org/licenses/LICENSE-2.0 | |
8 | * | |
9 | * Unless required by applicable law or agreed to in writing, software | |
10 | * distributed under the License is distributed on an "AS IS" BASIS, | |
11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
12 | * See the License for the specific language governing permissions and | |
13 | * limitations under the License. | |
14 | */ | |
15 | ||
16 | package com.google.gson.internal.bind; | |
17 | ||
18 | import java.io.IOException; | |
19 | import java.text.DateFormat; | |
20 | import java.text.ParseException; | |
21 | import java.text.ParsePosition; | |
22 | import java.text.SimpleDateFormat; | |
23 | import java.util.ArrayList; | |
24 | import java.util.Date; | |
25 | import java.util.List; | |
26 | import java.util.Locale; | |
27 | ||
28 | import com.google.gson.JsonSyntaxException; | |
29 | import com.google.gson.TypeAdapter; | |
30 | import com.google.gson.TypeAdapterFactory; | |
31 | import com.google.gson.internal.$Gson$Preconditions; | |
32 | import com.google.gson.internal.JavaVersion; | |
33 | import com.google.gson.internal.PreJava9DateFormatProvider; | |
34 | import com.google.gson.internal.bind.util.ISO8601Utils; | |
35 | import com.google.gson.stream.JsonReader; | |
36 | import com.google.gson.stream.JsonToken; | |
37 | import com.google.gson.stream.JsonWriter; | |
38 | ||
39 | /** | |
40 | * This type adapter supports subclasses of date by defining a | |
41 | * {@link DefaultDateTypeAdapter.DateType} and then using its {@code createAdapterFactory} | |
42 | * methods. | |
43 | * | |
44 | * @author Inderjeet Singh | |
45 | * @author Joel Leitch | |
46 | */ | |
47 | public final class DefaultDateTypeAdapter<T extends Date> extends TypeAdapter<T> { | |
48 | private static final String SIMPLE_NAME = "DefaultDateTypeAdapter"; | |
49 | ||
50 | public static abstract class DateType<T extends Date> { | |
51 | public static final DateType<Date> DATE = new DateType<Date>(Date.class) { | |
52 | @Override protected Date deserialize(Date date) { | |
53 | return date; | |
54 | } | |
55 | }; | |
56 | ||
57 | private final Class<T> dateClass; | |
58 | ||
59 | protected DateType(Class<T> dateClass) { | |
60 | this.dateClass = dateClass; | |
61 | } | |
62 | ||
63 | protected abstract T deserialize(Date date); | |
64 | ||
65 | private final TypeAdapterFactory createFactory(DefaultDateTypeAdapter<T> adapter) { | |
66 | return TypeAdapters.newFactory(dateClass, adapter); | |
67 | } | |
68 | ||
69 | public final TypeAdapterFactory createAdapterFactory(String datePattern) { | |
70 | return createFactory(new DefaultDateTypeAdapter<T>(this, datePattern)); | |
71 | } | |
72 | ||
73 | public final TypeAdapterFactory createAdapterFactory(int style) { | |
74 | return createFactory(new DefaultDateTypeAdapter<T>(this, style)); | |
75 | } | |
76 | ||
77 | public final TypeAdapterFactory createAdapterFactory(int dateStyle, int timeStyle) { | |
78 | return createFactory(new DefaultDateTypeAdapter<T>(this, dateStyle, timeStyle)); | |
79 | } | |
80 | ||
81 | public final TypeAdapterFactory createDefaultsAdapterFactory() { | |
82 | return createFactory(new DefaultDateTypeAdapter<T>(this, DateFormat.DEFAULT, DateFormat.DEFAULT)); | |
83 | } | |
84 | } | |
85 | ||
86 | private final DateType<T> dateType; | |
87 | ||
88 | /** | |
89 | * List of 1 or more different date formats used for de-serialization attempts. | |
90 | * The first of them is used for serialization as well. | |
91 | */ | |
92 | private final List<DateFormat> dateFormats = new ArrayList<DateFormat>(); | |
93 | ||
94 | private DefaultDateTypeAdapter(DateType<T> dateType, String datePattern) { | |
95 | this.dateType = $Gson$Preconditions.checkNotNull(dateType); | |
96 | dateFormats.add(new SimpleDateFormat(datePattern, Locale.US)); | |
97 | if (!Locale.getDefault().equals(Locale.US)) { | |
98 | dateFormats.add(new SimpleDateFormat(datePattern)); | |
99 | } | |
100 | } | |
101 | ||
102 | private DefaultDateTypeAdapter(DateType<T> dateType, int style) { | |
103 | this.dateType = $Gson$Preconditions.checkNotNull(dateType); | |
104 | dateFormats.add(DateFormat.getDateInstance(style, Locale.US)); | |
105 | if (!Locale.getDefault().equals(Locale.US)) { | |
106 | dateFormats.add(DateFormat.getDateInstance(style)); | |
107 | } | |
108 | if (JavaVersion.isJava9OrLater()) { | |
109 | dateFormats.add(PreJava9DateFormatProvider.getUSDateFormat(style)); | |
110 | } | |
111 | } | |
112 | ||
113 | private DefaultDateTypeAdapter(DateType<T> dateType, int dateStyle, int timeStyle) { | |
114 | this.dateType = $Gson$Preconditions.checkNotNull(dateType); | |
115 | dateFormats.add(DateFormat.getDateTimeInstance(dateStyle, timeStyle, Locale.US)); | |
116 | if (!Locale.getDefault().equals(Locale.US)) { | |
117 | dateFormats.add(DateFormat.getDateTimeInstance(dateStyle, timeStyle)); | |
118 | } | |
119 | if (JavaVersion.isJava9OrLater()) { | |
120 | dateFormats.add(PreJava9DateFormatProvider.getUSDateTimeFormat(dateStyle, timeStyle)); | |
121 | } | |
122 | } | |
123 | ||
124 | // These methods need to be synchronized since JDK DateFormat classes are not thread-safe | |
125 | // See issue 162 | |
126 | @Override | |
127 | public void write(JsonWriter out, Date value) throws IOException { | |
128 | if (value == null) { | |
129 | out.nullValue(); | |
130 | return; | |
131 | } | |
132 | ||
133 | DateFormat dateFormat = dateFormats.get(0); | |
134 | String dateFormatAsString; | |
135 | synchronized (dateFormats) { | |
136 | dateFormatAsString = dateFormat.format(value); | |
137 | } | |
138 | out.value(dateFormatAsString); | |
139 | } | |
140 | ||
141 | @Override | |
142 | public T read(JsonReader in) throws IOException { | |
143 | if (in.peek() == JsonToken.NULL) { | |
144 | in.nextNull(); | |
145 | return null; | |
146 | } | |
147 | Date date = deserializeToDate(in); | |
148 | return dateType.deserialize(date); | |
149 | } | |
150 | ||
151 | private Date deserializeToDate(JsonReader in) throws IOException { | |
152 | String s = in.nextString(); | |
153 | synchronized (dateFormats) { | |
154 | for (DateFormat dateFormat : dateFormats) { | |
155 | try { | |
156 | return dateFormat.parse(s); | |
157 | } catch (ParseException ignored) {} | |
158 | } | |
159 | } | |
160 | ||
161 | try { | |
162 | return ISO8601Utils.parse(s, new ParsePosition(0)); | |
163 | } catch (ParseException e) { | |
164 | throw new JsonSyntaxException("Failed parsing '" + s + "' as Date; at path " + in.getPreviousPath(), e); | |
165 | } | |
166 | } | |
167 | ||
168 | @Override | |
169 | public String toString() { | |
170 | DateFormat defaultFormat = dateFormats.get(0); | |
171 | if (defaultFormat instanceof SimpleDateFormat) { | |
172 | return SIMPLE_NAME + '(' + ((SimpleDateFormat) defaultFormat).toPattern() + ')'; | |
173 | } else { | |
174 | return SIMPLE_NAME + '(' + defaultFormat.getClass().getSimpleName() + ')'; | |
175 | } | |
176 | } | |
177 | } |
100 | 100 | |
101 | 101 | @Override public boolean hasNext() throws IOException { |
102 | 102 | JsonToken token = peek(); |
103 | return token != JsonToken.END_OBJECT && token != JsonToken.END_ARRAY; | |
103 | return token != JsonToken.END_OBJECT && token != JsonToken.END_ARRAY && token != JsonToken.END_DOCUMENT; | |
104 | 104 | } |
105 | 105 | |
106 | 106 | @Override public JsonToken peek() throws IOException { |
248 | 248 | return result; |
249 | 249 | } |
250 | 250 | |
251 | JsonElement nextJsonElement() throws IOException { | |
252 | final JsonToken peeked = peek(); | |
253 | if (peeked == JsonToken.NAME | |
254 | || peeked == JsonToken.END_ARRAY | |
255 | || peeked == JsonToken.END_OBJECT | |
256 | || peeked == JsonToken.END_DOCUMENT) { | |
257 | throw new IllegalStateException("Unexpected " + peeked + " when reading a JsonElement."); | |
258 | } | |
259 | final JsonElement element = (JsonElement) peekStack(); | |
260 | skipValue(); | |
261 | return element; | |
262 | } | |
263 | ||
251 | 264 | @Override public void close() throws IOException { |
252 | 265 | stack = new Object[] { SENTINEL_CLOSED }; |
253 | 266 | stackSize = 1; |
269 | 282 | } |
270 | 283 | |
271 | 284 | @Override public String toString() { |
272 | return getClass().getSimpleName(); | |
285 | return getClass().getSimpleName() + locationString(); | |
273 | 286 | } |
274 | 287 | |
275 | 288 | public void promoteNameToValue() throws IOException { |
290 | 303 | stack[stackSize++] = newTop; |
291 | 304 | } |
292 | 305 | |
293 | @Override public String getPath() { | |
306 | private String getPath(boolean usePreviousPath) { | |
294 | 307 | StringBuilder result = new StringBuilder().append('$'); |
295 | 308 | for (int i = 0; i < stackSize; i++) { |
296 | 309 | if (stack[i] instanceof JsonArray) { |
297 | if (stack[++i] instanceof Iterator) { | |
298 | result.append('[').append(pathIndices[i]).append(']'); | |
310 | if (++i < stackSize && stack[i] instanceof Iterator) { | |
311 | int pathIndex = pathIndices[i]; | |
312 | // If index is last path element it points to next array element; have to decrement | |
313 | // `- 1` covers case where iterator for next element is on stack | |
314 | // `- 2` covers case where peek() already pushed next element onto stack | |
315 | if (usePreviousPath && pathIndex > 0 && (i == stackSize - 1 || i == stackSize - 2)) { | |
316 | pathIndex--; | |
317 | } | |
318 | result.append('[').append(pathIndex).append(']'); | |
299 | 319 | } |
300 | 320 | } else if (stack[i] instanceof JsonObject) { |
301 | if (stack[++i] instanceof Iterator) { | |
321 | if (++i < stackSize && stack[i] instanceof Iterator) { | |
302 | 322 | result.append('.'); |
303 | 323 | if (pathNames[i] != null) { |
304 | 324 | result.append(pathNames[i]); |
309 | 329 | return result.toString(); |
310 | 330 | } |
311 | 331 | |
332 | @Override public String getPreviousPath() { | |
333 | return getPath(true); | |
334 | } | |
335 | ||
336 | @Override public String getPath() { | |
337 | return getPath(false); | |
338 | } | |
339 | ||
312 | 340 | private String locationString() { |
313 | 341 | return " at path " + getPath(); |
314 | 342 | } |
0 | /* | |
1 | * Copyright (C) 2020 Google Inc. | |
2 | * | |
3 | * Licensed under the Apache License, Version 2.0 (the "License"); | |
4 | * you may not use this file except in compliance with the License. | |
5 | * You may obtain a copy of the License at | |
6 | * | |
7 | * http://www.apache.org/licenses/LICENSE-2.0 | |
8 | * | |
9 | * Unless required by applicable law or agreed to in writing, software | |
10 | * distributed under the License is distributed on an "AS IS" BASIS, | |
11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
12 | * See the License for the specific language governing permissions and | |
13 | * limitations under the License. | |
14 | */ | |
15 | ||
16 | package com.google.gson.internal.bind; | |
17 | ||
18 | import com.google.gson.Gson; | |
19 | import com.google.gson.JsonSyntaxException; | |
20 | import com.google.gson.ToNumberStrategy; | |
21 | import com.google.gson.ToNumberPolicy; | |
22 | import com.google.gson.TypeAdapter; | |
23 | import com.google.gson.TypeAdapterFactory; | |
24 | import com.google.gson.reflect.TypeToken; | |
25 | import com.google.gson.stream.JsonReader; | |
26 | import com.google.gson.stream.JsonToken; | |
27 | import com.google.gson.stream.JsonWriter; | |
28 | ||
29 | import java.io.IOException; | |
30 | ||
31 | /** | |
32 | * Type adapter for {@link Number}. | |
33 | */ | |
34 | public final class NumberTypeAdapter extends TypeAdapter<Number> { | |
35 | /** | |
36 | * Gson default factory using {@link ToNumberPolicy#LAZILY_PARSED_NUMBER}. | |
37 | */ | |
38 | private static final TypeAdapterFactory LAZILY_PARSED_NUMBER_FACTORY = newFactory(ToNumberPolicy.LAZILY_PARSED_NUMBER); | |
39 | ||
40 | private final ToNumberStrategy toNumberStrategy; | |
41 | ||
42 | private NumberTypeAdapter(ToNumberStrategy toNumberStrategy) { | |
43 | this.toNumberStrategy = toNumberStrategy; | |
44 | } | |
45 | ||
46 | private static TypeAdapterFactory newFactory(ToNumberStrategy toNumberStrategy) { | |
47 | final NumberTypeAdapter adapter = new NumberTypeAdapter(toNumberStrategy); | |
48 | return new TypeAdapterFactory() { | |
49 | @SuppressWarnings("unchecked") | |
50 | @Override public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type) { | |
51 | return type.getRawType() == Number.class ? (TypeAdapter<T>) adapter : null; | |
52 | } | |
53 | }; | |
54 | } | |
55 | ||
56 | public static TypeAdapterFactory getFactory(ToNumberStrategy toNumberStrategy) { | |
57 | if (toNumberStrategy == ToNumberPolicy.LAZILY_PARSED_NUMBER) { | |
58 | return LAZILY_PARSED_NUMBER_FACTORY; | |
59 | } else { | |
60 | return newFactory(toNumberStrategy); | |
61 | } | |
62 | } | |
63 | ||
64 | @Override public Number read(JsonReader in) throws IOException { | |
65 | JsonToken jsonToken = in.peek(); | |
66 | switch (jsonToken) { | |
67 | case NULL: | |
68 | in.nextNull(); | |
69 | return null; | |
70 | case NUMBER: | |
71 | case STRING: | |
72 | return toNumberStrategy.readNumber(in); | |
73 | default: | |
74 | throw new JsonSyntaxException("Expecting number, got: " + jsonToken + "; at path " + in.getPath()); | |
75 | } | |
76 | } | |
77 | ||
78 | @Override public void write(JsonWriter out, Number value) throws IOException { | |
79 | out.value(value); | |
80 | } | |
81 | } |
16 | 16 | package com.google.gson.internal.bind; |
17 | 17 | |
18 | 18 | import com.google.gson.Gson; |
19 | import com.google.gson.ToNumberStrategy; | |
20 | import com.google.gson.ToNumberPolicy; | |
19 | 21 | import com.google.gson.TypeAdapter; |
20 | 22 | import com.google.gson.TypeAdapterFactory; |
21 | 23 | import com.google.gson.internal.LinkedTreeMap; |
34 | 36 | * serialization and a primitive/Map/List on deserialization. |
35 | 37 | */ |
36 | 38 | public final class ObjectTypeAdapter extends TypeAdapter<Object> { |
37 | public static final TypeAdapterFactory FACTORY = new TypeAdapterFactory() { | |
38 | @SuppressWarnings("unchecked") | |
39 | @Override public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type) { | |
40 | if (type.getRawType() == Object.class) { | |
41 | return (TypeAdapter<T>) new ObjectTypeAdapter(gson); | |
42 | } | |
43 | return null; | |
44 | } | |
45 | }; | |
39 | /** | |
40 | * Gson default factory using {@link ToNumberPolicy#DOUBLE}. | |
41 | */ | |
42 | private static final TypeAdapterFactory DOUBLE_FACTORY = newFactory(ToNumberPolicy.DOUBLE); | |
46 | 43 | |
47 | 44 | private final Gson gson; |
45 | private final ToNumberStrategy toNumberStrategy; | |
48 | 46 | |
49 | ObjectTypeAdapter(Gson gson) { | |
47 | private ObjectTypeAdapter(Gson gson, ToNumberStrategy toNumberStrategy) { | |
50 | 48 | this.gson = gson; |
49 | this.toNumberStrategy = toNumberStrategy; | |
50 | } | |
51 | ||
52 | private static TypeAdapterFactory newFactory(final ToNumberStrategy toNumberStrategy) { | |
53 | return new TypeAdapterFactory() { | |
54 | @SuppressWarnings("unchecked") | |
55 | @Override public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type) { | |
56 | if (type.getRawType() == Object.class) { | |
57 | return (TypeAdapter<T>) new ObjectTypeAdapter(gson, toNumberStrategy); | |
58 | } | |
59 | return null; | |
60 | } | |
61 | }; | |
62 | } | |
63 | ||
64 | public static TypeAdapterFactory getFactory(ToNumberStrategy toNumberStrategy) { | |
65 | if (toNumberStrategy == ToNumberPolicy.DOUBLE) { | |
66 | return DOUBLE_FACTORY; | |
67 | } else { | |
68 | return newFactory(toNumberStrategy); | |
69 | } | |
51 | 70 | } |
52 | 71 | |
53 | 72 | @Override public Object read(JsonReader in) throws IOException { |
75 | 94 | return in.nextString(); |
76 | 95 | |
77 | 96 | case NUMBER: |
78 | return in.nextDouble(); | |
97 | return toNumberStrategy.readNumber(in); | |
79 | 98 | |
80 | 99 | case BOOLEAN: |
81 | 100 | return in.nextBoolean(); |
27 | 27 | import com.google.gson.internal.Excluder; |
28 | 28 | import com.google.gson.internal.ObjectConstructor; |
29 | 29 | import com.google.gson.internal.Primitives; |
30 | import com.google.gson.internal.reflect.ReflectionAccessor; | |
30 | import com.google.gson.internal.reflect.ReflectionHelper; | |
31 | 31 | import com.google.gson.reflect.TypeToken; |
32 | 32 | import com.google.gson.stream.JsonReader; |
33 | 33 | import com.google.gson.stream.JsonToken; |
49 | 49 | private final FieldNamingStrategy fieldNamingPolicy; |
50 | 50 | private final Excluder excluder; |
51 | 51 | private final JsonAdapterAnnotationTypeAdapterFactory jsonAdapterFactory; |
52 | private final ReflectionAccessor accessor = ReflectionAccessor.getInstance(); | |
53 | 52 | |
54 | 53 | public ReflectiveTypeAdapterFactory(ConstructorConstructor constructorConstructor, |
55 | 54 | FieldNamingStrategy fieldNamingPolicy, Excluder excluder, |
155 | 154 | if (!serialize && !deserialize) { |
156 | 155 | continue; |
157 | 156 | } |
158 | accessor.makeAccessible(field); | |
157 | ReflectionHelper.makeAccessible(field); | |
159 | 158 | Type fieldType = $Gson$Types.resolve(type.getType(), raw, field.getGenericType()); |
160 | 159 | List<String> fieldNames = getFieldNames(field); |
161 | 160 | BoundField previous = null; |
0 | /* | |
1 | * Copyright (C) 2011 Google Inc. | |
2 | * | |
3 | * Licensed under the Apache License, Version 2.0 (the "License"); | |
4 | * you may not use this file except in compliance with the License. | |
5 | * You may obtain a copy of the License at | |
6 | * | |
7 | * http://www.apache.org/licenses/LICENSE-2.0 | |
8 | * | |
9 | * Unless required by applicable law or agreed to in writing, software | |
10 | * distributed under the License is distributed on an "AS IS" BASIS, | |
11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
12 | * See the License for the specific language governing permissions and | |
13 | * limitations under the License. | |
14 | */ | |
15 | ||
16 | package com.google.gson.internal.bind; | |
17 | ||
18 | import com.google.gson.Gson; | |
19 | import com.google.gson.JsonSyntaxException; | |
20 | import com.google.gson.TypeAdapter; | |
21 | import com.google.gson.TypeAdapterFactory; | |
22 | import com.google.gson.reflect.TypeToken; | |
23 | import com.google.gson.stream.JsonReader; | |
24 | import com.google.gson.stream.JsonToken; | |
25 | import com.google.gson.stream.JsonWriter; | |
26 | import java.io.IOException; | |
27 | import java.text.DateFormat; | |
28 | import java.text.ParseException; | |
29 | import java.text.SimpleDateFormat; | |
30 | ||
31 | /** | |
32 | * Adapter for java.sql.Date. Although this class appears stateless, it is not. | |
33 | * DateFormat captures its time zone and locale when it is created, which gives | |
34 | * this class state. DateFormat isn't thread safe either, so this class has | |
35 | * to synchronize its read and write methods. | |
36 | */ | |
37 | public final class SqlDateTypeAdapter extends TypeAdapter<java.sql.Date> { | |
38 | public static final TypeAdapterFactory FACTORY = new TypeAdapterFactory() { | |
39 | @SuppressWarnings("unchecked") // we use a runtime check to make sure the 'T's equal | |
40 | @Override public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> typeToken) { | |
41 | return typeToken.getRawType() == java.sql.Date.class | |
42 | ? (TypeAdapter<T>) new SqlDateTypeAdapter() : null; | |
43 | } | |
44 | }; | |
45 | ||
46 | private final DateFormat format = new SimpleDateFormat("MMM d, yyyy"); | |
47 | ||
48 | @Override | |
49 | public synchronized java.sql.Date read(JsonReader in) throws IOException { | |
50 | if (in.peek() == JsonToken.NULL) { | |
51 | in.nextNull(); | |
52 | return null; | |
53 | } | |
54 | try { | |
55 | final long utilDate = format.parse(in.nextString()).getTime(); | |
56 | return new java.sql.Date(utilDate); | |
57 | } catch (ParseException e) { | |
58 | throw new JsonSyntaxException(e); | |
59 | } | |
60 | } | |
61 | ||
62 | @Override | |
63 | public synchronized void write(JsonWriter out, java.sql.Date value) throws IOException { | |
64 | out.value(value == null ? null : format.format(value)); | |
65 | } | |
66 | } |
0 | /* | |
1 | * Copyright (C) 2011 Google Inc. | |
2 | * | |
3 | * Licensed under the Apache License, Version 2.0 (the "License"); | |
4 | * you may not use this file except in compliance with the License. | |
5 | * You may obtain a copy of the License at | |
6 | * | |
7 | * http://www.apache.org/licenses/LICENSE-2.0 | |
8 | * | |
9 | * Unless required by applicable law or agreed to in writing, software | |
10 | * distributed under the License is distributed on an "AS IS" BASIS, | |
11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
12 | * See the License for the specific language governing permissions and | |
13 | * limitations under the License. | |
14 | */ | |
15 | ||
16 | package com.google.gson.internal.bind; | |
17 | ||
18 | import com.google.gson.Gson; | |
19 | import com.google.gson.JsonSyntaxException; | |
20 | import com.google.gson.TypeAdapter; | |
21 | import com.google.gson.TypeAdapterFactory; | |
22 | import com.google.gson.reflect.TypeToken; | |
23 | import com.google.gson.stream.JsonReader; | |
24 | import com.google.gson.stream.JsonToken; | |
25 | import com.google.gson.stream.JsonWriter; | |
26 | import java.io.IOException; | |
27 | import java.sql.Time; | |
28 | import java.text.DateFormat; | |
29 | import java.text.ParseException; | |
30 | import java.text.SimpleDateFormat; | |
31 | import java.util.Date; | |
32 | ||
33 | /** | |
34 | * Adapter for Time. Although this class appears stateless, it is not. | |
35 | * DateFormat captures its time zone and locale when it is created, which gives | |
36 | * this class state. DateFormat isn't thread safe either, so this class has | |
37 | * to synchronize its read and write methods. | |
38 | */ | |
39 | public final class TimeTypeAdapter extends TypeAdapter<Time> { | |
40 | public static final TypeAdapterFactory FACTORY = new TypeAdapterFactory() { | |
41 | @SuppressWarnings("unchecked") // we use a runtime check to make sure the 'T's equal | |
42 | @Override public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> typeToken) { | |
43 | return typeToken.getRawType() == Time.class ? (TypeAdapter<T>) new TimeTypeAdapter() : null; | |
44 | } | |
45 | }; | |
46 | ||
47 | private final DateFormat format = new SimpleDateFormat("hh:mm:ss a"); | |
48 | ||
49 | @Override public synchronized Time read(JsonReader in) throws IOException { | |
50 | if (in.peek() == JsonToken.NULL) { | |
51 | in.nextNull(); | |
52 | return null; | |
53 | } | |
54 | try { | |
55 | Date date = format.parse(in.nextString()); | |
56 | return new Time(date.getTime()); | |
57 | } catch (ParseException e) { | |
58 | throw new JsonSyntaxException(e); | |
59 | } | |
60 | } | |
61 | ||
62 | @Override public synchronized void write(JsonWriter out, Time value) throws IOException { | |
63 | out.value(value == null ? null : format.format(value)); | |
64 | } | |
65 | } |
46 | 46 | private final GsonContextImpl context = new GsonContextImpl(); |
47 | 47 | |
48 | 48 | /** The delegate is lazily created because it may not be needed, and creating it may fail. */ |
49 | private TypeAdapter<T> delegate; | |
49 | private volatile TypeAdapter<T> delegate; | |
50 | 50 | |
51 | 51 | public TreeTypeAdapter(JsonSerializer<T> serializer, JsonDeserializer<T> deserializer, |
52 | 52 | Gson gson, TypeToken<T> typeToken, TypeAdapterFactory skipPast) { |
82 | 82 | } |
83 | 83 | |
84 | 84 | private TypeAdapter<T> delegate() { |
85 | // A race might lead to `delegate` being assigned by multiple threads but the last assignment will stick | |
85 | 86 | TypeAdapter<T> d = delegate; |
86 | 87 | return d != null |
87 | 88 | ? d |
16 | 16 | package com.google.gson.internal.bind; |
17 | 17 | |
18 | 18 | import java.io.IOException; |
19 | import java.lang.reflect.AccessibleObject; | |
19 | 20 | import java.lang.reflect.Field; |
20 | 21 | import java.math.BigDecimal; |
21 | 22 | import java.math.BigInteger; |
25 | 26 | import java.net.URL; |
26 | 27 | import java.security.AccessController; |
27 | 28 | import java.security.PrivilegedAction; |
28 | import java.sql.Timestamp; | |
29 | 29 | import java.util.ArrayList; |
30 | 30 | import java.util.BitSet; |
31 | 31 | import java.util.Calendar; |
32 | 32 | import java.util.Currency; |
33 | import java.util.Date; | |
34 | 33 | import java.util.GregorianCalendar; |
35 | 34 | import java.util.HashMap; |
36 | 35 | import java.util.List; |
93 | 92 | boolean set; |
94 | 93 | switch (tokenType) { |
95 | 94 | case NUMBER: |
96 | set = in.nextInt() != 0; | |
95 | case STRING: | |
96 | int intValue = in.nextInt(); | |
97 | if (intValue == 0) { | |
98 | set = false; | |
99 | } else if (intValue == 1) { | |
100 | set = true; | |
101 | } else { | |
102 | throw new JsonSyntaxException("Invalid bitset value " + intValue + ", expected 0 or 1; at path " + in.getPreviousPath()); | |
103 | } | |
97 | 104 | break; |
98 | 105 | case BOOLEAN: |
99 | 106 | set = in.nextBoolean(); |
100 | 107 | break; |
101 | case STRING: | |
102 | String stringValue = in.nextString(); | |
103 | try { | |
104 | set = Integer.parseInt(stringValue) != 0; | |
105 | } catch (NumberFormatException e) { | |
106 | throw new JsonSyntaxException( | |
107 | "Error: Expecting: bitset number value (1, 0), Found: " + stringValue); | |
108 | } | |
109 | break; | |
110 | 108 | default: |
111 | throw new JsonSyntaxException("Invalid bitset value type: " + tokenType); | |
109 | throw new JsonSyntaxException("Invalid bitset value type: " + tokenType + "; at path " + in.getPath()); | |
112 | 110 | } |
113 | 111 | if (set) { |
114 | 112 | bitset.set(i); |
179 | 177 | in.nextNull(); |
180 | 178 | return null; |
181 | 179 | } |
182 | try { | |
183 | int intValue = in.nextInt(); | |
184 | return (byte) intValue; | |
180 | ||
181 | int intValue; | |
182 | try { | |
183 | intValue = in.nextInt(); | |
185 | 184 | } catch (NumberFormatException e) { |
186 | 185 | throw new JsonSyntaxException(e); |
187 | 186 | } |
187 | // Allow up to 255 to support unsigned values | |
188 | if (intValue > 255 || intValue < Byte.MIN_VALUE) { | |
189 | throw new JsonSyntaxException("Lossy conversion from " + intValue + " to byte; at path " + in.getPreviousPath()); | |
190 | } | |
191 | return (byte) intValue; | |
188 | 192 | } |
189 | 193 | @Override |
190 | 194 | public void write(JsonWriter out, Number value) throws IOException { |
202 | 206 | in.nextNull(); |
203 | 207 | return null; |
204 | 208 | } |
205 | try { | |
206 | return (short) in.nextInt(); | |
209 | ||
210 | int intValue; | |
211 | try { | |
212 | intValue = in.nextInt(); | |
207 | 213 | } catch (NumberFormatException e) { |
208 | 214 | throw new JsonSyntaxException(e); |
209 | 215 | } |
216 | // Allow up to 65535 to support unsigned values | |
217 | if (intValue > 65535 || intValue < Short.MIN_VALUE) { | |
218 | throw new JsonSyntaxException("Lossy conversion from " + intValue + " to short; at path " + in.getPreviousPath()); | |
219 | } | |
220 | return (short) intValue; | |
210 | 221 | } |
211 | 222 | @Override |
212 | 223 | public void write(JsonWriter out, Number value) throws IOException { |
344 | 355 | } |
345 | 356 | }; |
346 | 357 | |
347 | public static final TypeAdapter<Number> NUMBER = new TypeAdapter<Number>() { | |
348 | @Override | |
349 | public Number read(JsonReader in) throws IOException { | |
350 | JsonToken jsonToken = in.peek(); | |
351 | switch (jsonToken) { | |
352 | case NULL: | |
353 | in.nextNull(); | |
354 | return null; | |
355 | case NUMBER: | |
356 | case STRING: | |
357 | return new LazilyParsedNumber(in.nextString()); | |
358 | default: | |
359 | throw new JsonSyntaxException("Expecting number, got: " + jsonToken); | |
360 | } | |
361 | } | |
362 | @Override | |
363 | public void write(JsonWriter out, Number value) throws IOException { | |
364 | out.value(value); | |
365 | } | |
366 | }; | |
367 | ||
368 | public static final TypeAdapterFactory NUMBER_FACTORY = newFactory(Number.class, NUMBER); | |
369 | ||
370 | 358 | public static final TypeAdapter<Character> CHARACTER = new TypeAdapter<Character>() { |
371 | 359 | @Override |
372 | 360 | public Character read(JsonReader in) throws IOException { |
376 | 364 | } |
377 | 365 | String str = in.nextString(); |
378 | 366 | if (str.length() != 1) { |
379 | throw new JsonSyntaxException("Expecting character, got: " + str); | |
367 | throw new JsonSyntaxException("Expecting character, got: " + str + "; at " + in.getPreviousPath()); | |
380 | 368 | } |
381 | 369 | return str.charAt(0); |
382 | 370 | } |
408 | 396 | out.value(value); |
409 | 397 | } |
410 | 398 | }; |
411 | ||
399 | ||
412 | 400 | public static final TypeAdapter<BigDecimal> BIG_DECIMAL = new TypeAdapter<BigDecimal>() { |
413 | 401 | @Override public BigDecimal read(JsonReader in) throws IOException { |
414 | 402 | if (in.peek() == JsonToken.NULL) { |
415 | 403 | in.nextNull(); |
416 | 404 | return null; |
417 | 405 | } |
418 | try { | |
419 | return new BigDecimal(in.nextString()); | |
406 | String s = in.nextString(); | |
407 | try { | |
408 | return new BigDecimal(s); | |
420 | 409 | } catch (NumberFormatException e) { |
421 | throw new JsonSyntaxException(e); | |
410 | throw new JsonSyntaxException("Failed parsing '" + s + "' as BigDecimal; at path " + in.getPreviousPath(), e); | |
422 | 411 | } |
423 | 412 | } |
424 | 413 | |
426 | 415 | out.value(value); |
427 | 416 | } |
428 | 417 | }; |
429 | ||
418 | ||
430 | 419 | public static final TypeAdapter<BigInteger> BIG_INTEGER = new TypeAdapter<BigInteger>() { |
431 | 420 | @Override public BigInteger read(JsonReader in) throws IOException { |
432 | 421 | if (in.peek() == JsonToken.NULL) { |
433 | 422 | in.nextNull(); |
434 | 423 | return null; |
435 | 424 | } |
436 | try { | |
437 | return new BigInteger(in.nextString()); | |
425 | String s = in.nextString(); | |
426 | try { | |
427 | return new BigInteger(s); | |
438 | 428 | } catch (NumberFormatException e) { |
439 | throw new JsonSyntaxException(e); | |
429 | throw new JsonSyntaxException("Failed parsing '" + s + "' as BigInteger; at path " + in.getPreviousPath(), e); | |
440 | 430 | } |
441 | 431 | } |
442 | 432 | |
443 | 433 | @Override public void write(JsonWriter out, BigInteger value) throws IOException { |
434 | out.value(value); | |
435 | } | |
436 | }; | |
437 | ||
438 | public static final TypeAdapter<LazilyParsedNumber> LAZILY_PARSED_NUMBER = new TypeAdapter<LazilyParsedNumber>() { | |
439 | // Normally users should not be able to access and deserialize LazilyParsedNumber because | |
440 | // it is an internal type, but implement this nonetheless in case there are legit corner | |
441 | // cases where this is possible | |
442 | @Override public LazilyParsedNumber read(JsonReader in) throws IOException { | |
443 | if (in.peek() == JsonToken.NULL) { | |
444 | in.nextNull(); | |
445 | return null; | |
446 | } | |
447 | return new LazilyParsedNumber(in.nextString()); | |
448 | } | |
449 | ||
450 | @Override public void write(JsonWriter out, LazilyParsedNumber value) throws IOException { | |
444 | 451 | out.value(value); |
445 | 452 | } |
446 | 453 | }; |
549 | 556 | in.nextNull(); |
550 | 557 | return null; |
551 | 558 | } |
552 | return java.util.UUID.fromString(in.nextString()); | |
559 | String s = in.nextString(); | |
560 | try { | |
561 | return java.util.UUID.fromString(s); | |
562 | } catch (IllegalArgumentException e) { | |
563 | throw new JsonSyntaxException("Failed parsing '" + s + "' as UUID; at path " + in.getPreviousPath(), e); | |
564 | } | |
553 | 565 | } |
554 | 566 | @Override |
555 | 567 | public void write(JsonWriter out, UUID value) throws IOException { |
562 | 574 | public static final TypeAdapter<Currency> CURRENCY = new TypeAdapter<Currency>() { |
563 | 575 | @Override |
564 | 576 | public Currency read(JsonReader in) throws IOException { |
565 | return Currency.getInstance(in.nextString()); | |
577 | String s = in.nextString(); | |
578 | try { | |
579 | return Currency.getInstance(s); | |
580 | } catch (IllegalArgumentException e) { | |
581 | throw new JsonSyntaxException("Failed parsing '" + s + "' as Currency; at path " + in.getPreviousPath(), e); | |
582 | } | |
566 | 583 | } |
567 | 584 | @Override |
568 | 585 | public void write(JsonWriter out, Currency value) throws IOException { |
570 | 587 | } |
571 | 588 | }.nullSafe(); |
572 | 589 | public static final TypeAdapterFactory CURRENCY_FACTORY = newFactory(Currency.class, CURRENCY); |
573 | ||
574 | public static final TypeAdapterFactory TIMESTAMP_FACTORY = new TypeAdapterFactory() { | |
575 | @SuppressWarnings("unchecked") // we use a runtime check to make sure the 'T's equal | |
576 | @Override public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> typeToken) { | |
577 | if (typeToken.getRawType() != Timestamp.class) { | |
578 | return null; | |
579 | } | |
580 | ||
581 | final TypeAdapter<Date> dateTypeAdapter = gson.getAdapter(Date.class); | |
582 | return (TypeAdapter<T>) new TypeAdapter<Timestamp>() { | |
583 | @Override public Timestamp read(JsonReader in) throws IOException { | |
584 | Date date = dateTypeAdapter.read(in); | |
585 | return date != null ? new Timestamp(date.getTime()) : null; | |
586 | } | |
587 | ||
588 | @Override public void write(JsonWriter out, Timestamp value) throws IOException { | |
589 | dateTypeAdapter.write(out, value); | |
590 | } | |
591 | }; | |
592 | } | |
593 | }; | |
594 | 590 | |
595 | 591 | public static final TypeAdapter<Calendar> CALENDAR = new TypeAdapter<Calendar>() { |
596 | 592 | private static final String YEAR = "year"; |
699 | 695 | |
700 | 696 | public static final TypeAdapter<JsonElement> JSON_ELEMENT = new TypeAdapter<JsonElement>() { |
701 | 697 | @Override public JsonElement read(JsonReader in) throws IOException { |
698 | if (in instanceof JsonTreeReader) { | |
699 | return ((JsonTreeReader) in).nextJsonElement(); | |
700 | } | |
701 | ||
702 | 702 | switch (in.peek()) { |
703 | 703 | case STRING: |
704 | 704 | return new JsonPrimitive(in.nextString()); |
776 | 776 | private final Map<String, T> nameToConstant = new HashMap<String, T>(); |
777 | 777 | private final Map<T, String> constantToName = new HashMap<T, String>(); |
778 | 778 | |
779 | public EnumTypeAdapter(Class<T> classOfT) { | |
780 | try { | |
781 | for (final Field field : classOfT.getDeclaredFields()) { | |
782 | if (!field.isEnumConstant()) { | |
783 | continue; | |
779 | public EnumTypeAdapter(final Class<T> classOfT) { | |
780 | try { | |
781 | // Uses reflection to find enum constants to work around name mismatches for obfuscated classes | |
782 | // Reflection access might throw SecurityException, therefore run this in privileged context; | |
783 | // should be acceptable because this only retrieves enum constants, but does not expose anything else | |
784 | Field[] constantFields = AccessController.doPrivileged(new PrivilegedAction<Field[]>() { | |
785 | @Override public Field[] run() { | |
786 | Field[] fields = classOfT.getDeclaredFields(); | |
787 | ArrayList<Field> constantFieldsList = new ArrayList<Field>(fields.length); | |
788 | for (Field f : fields) { | |
789 | if (f.isEnumConstant()) { | |
790 | constantFieldsList.add(f); | |
791 | } | |
792 | } | |
793 | ||
794 | Field[] constantFields = constantFieldsList.toArray(new Field[0]); | |
795 | AccessibleObject.setAccessible(constantFields, true); | |
796 | return constantFields; | |
784 | 797 | } |
785 | AccessController.doPrivileged(new PrivilegedAction<Void>() { | |
786 | @Override public Void run() { | |
787 | field.setAccessible(true); | |
788 | return null; | |
789 | } | |
790 | }); | |
798 | }); | |
799 | for (Field constantField : constantFields) { | |
791 | 800 | @SuppressWarnings("unchecked") |
792 | T constant = (T)(field.get(null)); | |
801 | T constant = (T)(constantField.get(null)); | |
793 | 802 | String name = constant.name(); |
794 | SerializedName annotation = field.getAnnotation(SerializedName.class); | |
803 | SerializedName annotation = constantField.getAnnotation(SerializedName.class); | |
795 | 804 | if (annotation != null) { |
796 | 805 | name = annotation.value(); |
797 | 806 | for (String alternate : annotation.alternate()) { |
907 | 916 | T1 result = typeAdapter.read(in); |
908 | 917 | if (result != null && !requestedType.isInstance(result)) { |
909 | 918 | throw new JsonSyntaxException("Expected a " + requestedType.getName() |
910 | + " but was " + result.getClass().getName()); | |
919 | + " but was " + result.getClass().getName() + "; at path " + in.getPreviousPath()); | |
911 | 920 | } |
912 | 921 | return result; |
913 | 922 | } |
+0
-33
0 | /* | |
1 | * Copyright (C) 2017 The Gson authors | |
2 | * | |
3 | * Licensed under the Apache License, Version 2.0 (the "License"); | |
4 | * you may not use this file except in compliance with the License. | |
5 | * You may obtain a copy of the License at | |
6 | * | |
7 | * http://www.apache.org/licenses/LICENSE-2.0 | |
8 | * | |
9 | * Unless required by applicable law or agreed to in writing, software | |
10 | * distributed under the License is distributed on an "AS IS" BASIS, | |
11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
12 | * See the License for the specific language governing permissions and | |
13 | * limitations under the License. | |
14 | */ | |
15 | package com.google.gson.internal.reflect; | |
16 | ||
17 | import java.lang.reflect.AccessibleObject; | |
18 | ||
19 | /** | |
20 | * A basic implementation of {@link ReflectionAccessor} which is suitable for Java 8 and below. | |
21 | * <p> | |
22 | * This implementation just calls {@link AccessibleObject#setAccessible(boolean) setAccessible(true)}, which worked | |
23 | * fine before Java 9. | |
24 | */ | |
25 | final class PreJava9ReflectionAccessor extends ReflectionAccessor { | |
26 | ||
27 | /** {@inheritDoc} */ | |
28 | @Override | |
29 | public void makeAccessible(AccessibleObject ao) { | |
30 | ao.setAccessible(true); | |
31 | } | |
32 | } |
0 | /* | |
1 | * Copyright (C) 2017 The Gson authors | |
2 | * | |
3 | * Licensed under the Apache License, Version 2.0 (the "License"); | |
4 | * you may not use this file except in compliance with the License. | |
5 | * You may obtain a copy of the License at | |
6 | * | |
7 | * http://www.apache.org/licenses/LICENSE-2.0 | |
8 | * | |
9 | * Unless required by applicable law or agreed to in writing, software | |
10 | * distributed under the License is distributed on an "AS IS" BASIS, | |
11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
12 | * See the License for the specific language governing permissions and | |
13 | * limitations under the License. | |
14 | */ | |
15 | package com.google.gson.internal.reflect; | |
16 | ||
17 | import java.lang.reflect.AccessibleObject; | |
18 | ||
19 | import com.google.gson.internal.JavaVersion; | |
20 | ||
21 | /** | |
22 | * Provides a replacement for {@link AccessibleObject#setAccessible(boolean)}, which may be used to | |
23 | * avoid reflective access issues appeared in Java 9, like {@link java.lang.reflect.InaccessibleObjectException} | |
24 | * thrown or warnings like | |
25 | * <pre> | |
26 | * WARNING: An illegal reflective access operation has occurred | |
27 | * WARNING: Illegal reflective access by ... | |
28 | * </pre> | |
29 | * <p/> | |
30 | * Works both for Java 9 and earlier Java versions. | |
31 | */ | |
32 | public abstract class ReflectionAccessor { | |
33 | ||
34 | // the singleton instance, use getInstance() to obtain | |
35 | private static final ReflectionAccessor instance = JavaVersion.getMajorJavaVersion() < 9 ? new PreJava9ReflectionAccessor() : new UnsafeReflectionAccessor(); | |
36 | ||
37 | /** | |
38 | * Does the same as {@code ao.setAccessible(true)}, but never throws | |
39 | * {@link java.lang.reflect.InaccessibleObjectException} | |
40 | */ | |
41 | public abstract void makeAccessible(AccessibleObject ao); | |
42 | ||
43 | /** | |
44 | * Obtains a {@link ReflectionAccessor} instance suitable for the current Java version. | |
45 | * <p> | |
46 | * You may need one a reflective operation in your code throws {@link java.lang.reflect.InaccessibleObjectException}. | |
47 | * In such a case, use {@link ReflectionAccessor#makeAccessible(AccessibleObject)} on a field, method or constructor | |
48 | * (instead of basic {@link AccessibleObject#setAccessible(boolean)}). | |
49 | */ | |
50 | public static ReflectionAccessor getInstance() { | |
51 | return instance; | |
52 | } | |
53 | } |
0 | package com.google.gson.internal.reflect; | |
1 | ||
2 | import com.google.gson.JsonIOException; | |
3 | import java.lang.reflect.Constructor; | |
4 | import java.lang.reflect.Field; | |
5 | ||
6 | public class ReflectionHelper { | |
7 | private ReflectionHelper() { } | |
8 | ||
9 | /** | |
10 | * Tries making the field accessible, wrapping any thrown exception in a | |
11 | * {@link JsonIOException} with descriptive message. | |
12 | * | |
13 | * @param field field to make accessible | |
14 | * @throws JsonIOException if making the field accessible fails | |
15 | */ | |
16 | public static void makeAccessible(Field field) throws JsonIOException { | |
17 | try { | |
18 | field.setAccessible(true); | |
19 | } catch (Exception exception) { | |
20 | throw new JsonIOException("Failed making field '" + field.getDeclaringClass().getName() + "#" | |
21 | + field.getName() + "' accessible; either change its visibility or write a custom " | |
22 | + "TypeAdapter for its declaring type", exception); | |
23 | } | |
24 | } | |
25 | ||
26 | /** | |
27 | * Creates a string representation for a constructor. | |
28 | * E.g.: {@code java.lang.String#String(char[], int, int)} | |
29 | */ | |
30 | private static String constructorToString(Constructor<?> constructor) { | |
31 | StringBuilder stringBuilder = new StringBuilder(constructor.getDeclaringClass().getName()) | |
32 | .append('#') | |
33 | .append(constructor.getDeclaringClass().getSimpleName()) | |
34 | .append('('); | |
35 | Class<?>[] parameters = constructor.getParameterTypes(); | |
36 | for (int i = 0; i < parameters.length; i++) { | |
37 | if (i > 0) { | |
38 | stringBuilder.append(", "); | |
39 | } | |
40 | stringBuilder.append(parameters[i].getSimpleName()); | |
41 | } | |
42 | ||
43 | return stringBuilder.append(')').toString(); | |
44 | } | |
45 | ||
46 | /** | |
47 | * Tries making the constructor accessible, returning an exception message | |
48 | * if this fails. | |
49 | * | |
50 | * @param constructor constructor to make accessible | |
51 | * @return exception message; {@code null} if successful, non-{@code null} if | |
52 | * unsuccessful | |
53 | */ | |
54 | public static String tryMakeAccessible(Constructor<?> constructor) { | |
55 | try { | |
56 | constructor.setAccessible(true); | |
57 | return null; | |
58 | } catch (Exception exception) { | |
59 | return "Failed making constructor '" + constructorToString(constructor) + "' accessible; " | |
60 | + "either change its visibility or write a custom InstanceCreator or TypeAdapter for its declaring type: " | |
61 | // Include the message since it might contain more detailed information | |
62 | + exception.getMessage(); | |
63 | } | |
64 | } | |
65 | } |
0 | /* | |
1 | * Copyright (C) 2017 The Gson authors | |
2 | * | |
3 | * Licensed under the Apache License, Version 2.0 (the "License"); | |
4 | * you may not use this file except in compliance with the License. | |
5 | * You may obtain a copy of the License at | |
6 | * | |
7 | * http://www.apache.org/licenses/LICENSE-2.0 | |
8 | * | |
9 | * Unless required by applicable law or agreed to in writing, software | |
10 | * distributed under the License is distributed on an "AS IS" BASIS, | |
11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
12 | * See the License for the specific language governing permissions and | |
13 | * limitations under the License. | |
14 | */ | |
15 | package com.google.gson.internal.reflect; | |
16 | ||
17 | import java.lang.reflect.AccessibleObject; | |
18 | import java.lang.reflect.Field; | |
19 | import java.lang.reflect.Method; | |
20 | ||
21 | import com.google.gson.JsonIOException; | |
22 | ||
23 | /** | |
24 | * An implementation of {@link ReflectionAccessor} based on {@link Unsafe}. | |
25 | * <p> | |
26 | * NOTE: This implementation is designed for Java 9. Although it should work with earlier Java releases, it is better to | |
27 | * use {@link PreJava9ReflectionAccessor} for them. | |
28 | */ | |
29 | @SuppressWarnings({"unchecked", "rawtypes"}) | |
30 | final class UnsafeReflectionAccessor extends ReflectionAccessor { | |
31 | ||
32 | private static Class unsafeClass; | |
33 | private final Object theUnsafe = getUnsafeInstance(); | |
34 | private final Field overrideField = getOverrideField(); | |
35 | ||
36 | /** {@inheritDoc} */ | |
37 | @Override | |
38 | public void makeAccessible(AccessibleObject ao) { | |
39 | boolean success = makeAccessibleWithUnsafe(ao); | |
40 | if (!success) { | |
41 | try { | |
42 | // unsafe couldn't be found, so try using accessible anyway | |
43 | ao.setAccessible(true); | |
44 | } catch (SecurityException e) { | |
45 | throw new JsonIOException("Gson couldn't modify fields for " + ao | |
46 | + "\nand sun.misc.Unsafe not found.\nEither write a custom type adapter," | |
47 | + " or make fields accessible, or include sun.misc.Unsafe.", e); | |
48 | } | |
49 | } | |
50 | } | |
51 | ||
52 | // Visible for testing only | |
53 | boolean makeAccessibleWithUnsafe(AccessibleObject ao) { | |
54 | if (theUnsafe != null && overrideField != null) { | |
55 | try { | |
56 | Method method = unsafeClass.getMethod("objectFieldOffset", Field.class); | |
57 | long overrideOffset = (Long) method.invoke(theUnsafe, overrideField); // long overrideOffset = theUnsafe.objectFieldOffset(overrideField); | |
58 | Method putBooleanMethod = unsafeClass.getMethod("putBoolean", Object.class, long.class, boolean.class); | |
59 | putBooleanMethod.invoke(theUnsafe, ao, overrideOffset, true); // theUnsafe.putBoolean(ao, overrideOffset, true); | |
60 | return true; | |
61 | } catch (Exception ignored) { // do nothing | |
62 | } | |
63 | } | |
64 | return false; | |
65 | } | |
66 | ||
67 | private static Object getUnsafeInstance() { | |
68 | try { | |
69 | unsafeClass = Class.forName("sun.misc.Unsafe"); | |
70 | Field unsafeField = unsafeClass.getDeclaredField("theUnsafe"); | |
71 | unsafeField.setAccessible(true); | |
72 | return unsafeField.get(null); | |
73 | } catch (Exception e) { | |
74 | return null; | |
75 | } | |
76 | } | |
77 | ||
78 | private static Field getOverrideField() { | |
79 | try { | |
80 | return AccessibleObject.class.getDeclaredField("override"); | |
81 | } catch (Exception e) { | |
82 | return null; | |
83 | } | |
84 | } | |
85 | } |
0 | /* | |
1 | * Copyright (C) 2011 Google Inc. | |
2 | * | |
3 | * Licensed under the Apache License, Version 2.0 (the "License"); | |
4 | * you may not use this file except in compliance with the License. | |
5 | * You may obtain a copy of the License at | |
6 | * | |
7 | * http://www.apache.org/licenses/LICENSE-2.0 | |
8 | * | |
9 | * Unless required by applicable law or agreed to in writing, software | |
10 | * distributed under the License is distributed on an "AS IS" BASIS, | |
11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
12 | * See the License for the specific language governing permissions and | |
13 | * limitations under the License. | |
14 | */ | |
15 | ||
16 | package com.google.gson.internal.sql; | |
17 | ||
18 | import com.google.gson.Gson; | |
19 | import com.google.gson.JsonSyntaxException; | |
20 | import com.google.gson.TypeAdapter; | |
21 | import com.google.gson.TypeAdapterFactory; | |
22 | import com.google.gson.reflect.TypeToken; | |
23 | import com.google.gson.stream.JsonReader; | |
24 | import com.google.gson.stream.JsonToken; | |
25 | import com.google.gson.stream.JsonWriter; | |
26 | import java.io.IOException; | |
27 | import java.text.DateFormat; | |
28 | import java.text.ParseException; | |
29 | import java.text.SimpleDateFormat; | |
30 | import java.util.Date; | |
31 | ||
32 | /** | |
33 | * Adapter for java.sql.Date. Although this class appears stateless, it is not. | |
34 | * DateFormat captures its time zone and locale when it is created, which gives | |
35 | * this class state. DateFormat isn't thread safe either, so this class has | |
36 | * to synchronize its read and write methods. | |
37 | */ | |
38 | final class SqlDateTypeAdapter extends TypeAdapter<java.sql.Date> { | |
39 | static final TypeAdapterFactory FACTORY = new TypeAdapterFactory() { | |
40 | @SuppressWarnings("unchecked") // we use a runtime check to make sure the 'T's equal | |
41 | @Override public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> typeToken) { | |
42 | return typeToken.getRawType() == java.sql.Date.class | |
43 | ? (TypeAdapter<T>) new SqlDateTypeAdapter() : null; | |
44 | } | |
45 | }; | |
46 | ||
47 | private final DateFormat format = new SimpleDateFormat("MMM d, yyyy"); | |
48 | ||
49 | private SqlDateTypeAdapter() { | |
50 | } | |
51 | ||
52 | @Override | |
53 | public java.sql.Date read(JsonReader in) throws IOException { | |
54 | if (in.peek() == JsonToken.NULL) { | |
55 | in.nextNull(); | |
56 | return null; | |
57 | } | |
58 | String s = in.nextString(); | |
59 | try { | |
60 | Date utilDate; | |
61 | synchronized (this) { | |
62 | utilDate = format.parse(s); | |
63 | } | |
64 | return new java.sql.Date(utilDate.getTime()); | |
65 | } catch (ParseException e) { | |
66 | throw new JsonSyntaxException("Failed parsing '" + s + "' as SQL Date; at path " + in.getPreviousPath(), e); | |
67 | } | |
68 | } | |
69 | ||
70 | @Override | |
71 | public void write(JsonWriter out, java.sql.Date value) throws IOException { | |
72 | if (value == null) { | |
73 | out.nullValue(); | |
74 | return; | |
75 | } | |
76 | String dateString; | |
77 | synchronized (this) { | |
78 | dateString = format.format(value); | |
79 | } | |
80 | out.value(dateString); | |
81 | } | |
82 | } |
0 | /* | |
1 | * Copyright (C) 2011 Google Inc. | |
2 | * | |
3 | * Licensed under the Apache License, Version 2.0 (the "License"); | |
4 | * you may not use this file except in compliance with the License. | |
5 | * You may obtain a copy of the License at | |
6 | * | |
7 | * http://www.apache.org/licenses/LICENSE-2.0 | |
8 | * | |
9 | * Unless required by applicable law or agreed to in writing, software | |
10 | * distributed under the License is distributed on an "AS IS" BASIS, | |
11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
12 | * See the License for the specific language governing permissions and | |
13 | * limitations under the License. | |
14 | */ | |
15 | ||
16 | package com.google.gson.internal.sql; | |
17 | ||
18 | import com.google.gson.Gson; | |
19 | import com.google.gson.JsonSyntaxException; | |
20 | import com.google.gson.TypeAdapter; | |
21 | import com.google.gson.TypeAdapterFactory; | |
22 | import com.google.gson.reflect.TypeToken; | |
23 | import com.google.gson.stream.JsonReader; | |
24 | import com.google.gson.stream.JsonToken; | |
25 | import com.google.gson.stream.JsonWriter; | |
26 | import java.io.IOException; | |
27 | import java.sql.Time; | |
28 | import java.text.DateFormat; | |
29 | import java.text.ParseException; | |
30 | import java.text.SimpleDateFormat; | |
31 | import java.util.Date; | |
32 | ||
33 | /** | |
34 | * Adapter for java.sql.Time. Although this class appears stateless, it is not. | |
35 | * DateFormat captures its time zone and locale when it is created, which gives | |
36 | * this class state. DateFormat isn't thread safe either, so this class has | |
37 | * to synchronize its read and write methods. | |
38 | */ | |
39 | final class SqlTimeTypeAdapter extends TypeAdapter<Time> { | |
40 | static final TypeAdapterFactory FACTORY = new TypeAdapterFactory() { | |
41 | @SuppressWarnings("unchecked") // we use a runtime check to make sure the 'T's equal | |
42 | @Override public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> typeToken) { | |
43 | return typeToken.getRawType() == Time.class ? (TypeAdapter<T>) new SqlTimeTypeAdapter() : null; | |
44 | } | |
45 | }; | |
46 | ||
47 | private final DateFormat format = new SimpleDateFormat("hh:mm:ss a"); | |
48 | ||
49 | private SqlTimeTypeAdapter() { | |
50 | } | |
51 | ||
52 | @Override public Time read(JsonReader in) throws IOException { | |
53 | if (in.peek() == JsonToken.NULL) { | |
54 | in.nextNull(); | |
55 | return null; | |
56 | } | |
57 | String s = in.nextString(); | |
58 | try { | |
59 | synchronized (this) { | |
60 | Date date = format.parse(s); | |
61 | return new Time(date.getTime()); | |
62 | } | |
63 | } catch (ParseException e) { | |
64 | throw new JsonSyntaxException("Failed parsing '" + s + "' as SQL Time; at path " + in.getPreviousPath(), e); | |
65 | } | |
66 | } | |
67 | ||
68 | @Override public void write(JsonWriter out, Time value) throws IOException { | |
69 | if (value == null) { | |
70 | out.nullValue(); | |
71 | return; | |
72 | } | |
73 | String timeString; | |
74 | synchronized (this) { | |
75 | timeString = format.format(value); | |
76 | } | |
77 | out.value(timeString); | |
78 | } | |
79 | } |
0 | package com.google.gson.internal.sql; | |
1 | ||
2 | import com.google.gson.Gson; | |
3 | import com.google.gson.TypeAdapter; | |
4 | import com.google.gson.TypeAdapterFactory; | |
5 | import com.google.gson.reflect.TypeToken; | |
6 | import com.google.gson.stream.JsonReader; | |
7 | import com.google.gson.stream.JsonWriter; | |
8 | ||
9 | import java.io.IOException; | |
10 | import java.sql.Timestamp; | |
11 | import java.util.Date; | |
12 | ||
13 | class SqlTimestampTypeAdapter extends TypeAdapter<Timestamp> { | |
14 | static final TypeAdapterFactory FACTORY = new TypeAdapterFactory() { | |
15 | @SuppressWarnings("unchecked") // we use a runtime check to make sure the 'T's equal | |
16 | @Override public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> typeToken) { | |
17 | if (typeToken.getRawType() == Timestamp.class) { | |
18 | final TypeAdapter<Date> dateTypeAdapter = gson.getAdapter(Date.class); | |
19 | return (TypeAdapter<T>) new SqlTimestampTypeAdapter(dateTypeAdapter); | |
20 | } else { | |
21 | return null; | |
22 | } | |
23 | } | |
24 | }; | |
25 | ||
26 | private final TypeAdapter<Date> dateTypeAdapter; | |
27 | ||
28 | private SqlTimestampTypeAdapter(TypeAdapter<Date> dateTypeAdapter) { | |
29 | this.dateTypeAdapter = dateTypeAdapter; | |
30 | } | |
31 | ||
32 | @Override | |
33 | public Timestamp read(JsonReader in) throws IOException { | |
34 | Date date = dateTypeAdapter.read(in); | |
35 | return date != null ? new Timestamp(date.getTime()) : null; | |
36 | } | |
37 | ||
38 | @Override | |
39 | public void write(JsonWriter out, Timestamp value) throws IOException { | |
40 | dateTypeAdapter.write(out, value); | |
41 | } | |
42 | } |
0 | package com.google.gson.internal.sql; | |
1 | ||
2 | import java.sql.Timestamp; | |
3 | import java.util.Date; | |
4 | ||
5 | import com.google.gson.TypeAdapterFactory; | |
6 | import com.google.gson.internal.bind.DefaultDateTypeAdapter.DateType; | |
7 | ||
8 | /** | |
9 | * Encapsulates access to {@code java.sql} types, to allow Gson to | |
10 | * work without the {@code java.sql} module being present. | |
11 | * No {@link ClassNotFoundException}s will be thrown in case | |
12 | * the {@code java.sql} module is not present. | |
13 | * | |
14 | * <p>If {@link #SUPPORTS_SQL_TYPES} is {@code true}, all other | |
15 | * constants of this class will be non-{@code null}. However, if | |
16 | * it is {@code false} all other constants will be {@code null} and | |
17 | * there will be no support for {@code java.sql} types. | |
18 | */ | |
19 | public final class SqlTypesSupport { | |
20 | /** | |
21 | * {@code true} if {@code java.sql} types are supported, | |
22 | * {@code false} otherwise | |
23 | */ | |
24 | public static final boolean SUPPORTS_SQL_TYPES; | |
25 | ||
26 | public static final DateType<? extends Date> DATE_DATE_TYPE; | |
27 | public static final DateType<? extends Date> TIMESTAMP_DATE_TYPE; | |
28 | ||
29 | public static final TypeAdapterFactory DATE_FACTORY; | |
30 | public static final TypeAdapterFactory TIME_FACTORY; | |
31 | public static final TypeAdapterFactory TIMESTAMP_FACTORY; | |
32 | ||
33 | static { | |
34 | boolean sqlTypesSupport; | |
35 | try { | |
36 | Class.forName("java.sql.Date"); | |
37 | sqlTypesSupport = true; | |
38 | } catch (ClassNotFoundException classNotFoundException) { | |
39 | sqlTypesSupport = false; | |
40 | } | |
41 | SUPPORTS_SQL_TYPES = sqlTypesSupport; | |
42 | ||
43 | if (SUPPORTS_SQL_TYPES) { | |
44 | DATE_DATE_TYPE = new DateType<java.sql.Date>(java.sql.Date.class) { | |
45 | @Override protected java.sql.Date deserialize(Date date) { | |
46 | return new java.sql.Date(date.getTime()); | |
47 | } | |
48 | }; | |
49 | TIMESTAMP_DATE_TYPE = new DateType<Timestamp>(Timestamp.class) { | |
50 | @Override protected Timestamp deserialize(Date date) { | |
51 | return new Timestamp(date.getTime()); | |
52 | } | |
53 | }; | |
54 | ||
55 | DATE_FACTORY = SqlDateTypeAdapter.FACTORY; | |
56 | TIME_FACTORY = SqlTimeTypeAdapter.FACTORY; | |
57 | TIMESTAMP_FACTORY = SqlTimestampTypeAdapter.FACTORY; | |
58 | } else { | |
59 | DATE_DATE_TYPE = null; | |
60 | TIMESTAMP_DATE_TYPE = null; | |
61 | ||
62 | DATE_FACTORY = null; | |
63 | TIME_FACTORY = null; | |
64 | TIMESTAMP_FACTORY = null; | |
65 | } | |
66 | } | |
67 | ||
68 | private SqlTypesSupport() { | |
69 | } | |
70 | } |
169 | 169 | * precision loss, extremely large values should be written and read as strings |
170 | 170 | * in JSON. |
171 | 171 | * |
172 | * <a id="nonexecuteprefix"/><h3>Non-Execute Prefix</h3> | |
172 | * <h3 id="nonexecuteprefix">Non-Execute Prefix</h3> | |
173 | 173 | * Web servers that serve private data using JSON may be vulnerable to <a |
174 | 174 | * href="http://en.wikipedia.org/wiki/JSON#Cross-site_request_forgery">Cross-site |
175 | 175 | * request forgery</a> attacks. In such an attack, a malicious site gains access |
227 | 227 | /** True to accept non-spec compliant JSON */ |
228 | 228 | private boolean lenient = false; |
229 | 229 | |
230 | static final int BUFFER_SIZE = 1024; | |
230 | 231 | /** |
231 | 232 | * Use a manual buffer to easily read and unread upcoming characters, and |
232 | 233 | * also so we can create strings without an intermediate StringBuilder. |
233 | 234 | * We decode literals directly out of this buffer, so it must be at least as |
234 | 235 | * long as the longest token that can be reported as a number. |
235 | 236 | */ |
236 | private final char[] buffer = new char[1024]; | |
237 | private final char[] buffer = new char[BUFFER_SIZE]; | |
237 | 238 | private int pos = 0; |
238 | 239 | private int limit = 0; |
239 | 240 | |
411 | 412 | if (p == PEEKED_NONE) { |
412 | 413 | p = doPeek(); |
413 | 414 | } |
414 | return p != PEEKED_END_OBJECT && p != PEEKED_END_ARRAY; | |
415 | return p != PEEKED_END_OBJECT && p != PEEKED_END_ARRAY && p != PEEKED_EOF; | |
415 | 416 | } |
416 | 417 | |
417 | 418 | /** |
1084 | 1085 | break; |
1085 | 1086 | } |
1086 | 1087 | } |
1087 | ||
1088 | ||
1088 | 1089 | String result = (null == builder) ? new String(buffer, pos, i) : builder.append(buffer, pos, i).toString(); |
1089 | 1090 | pos += i; |
1090 | 1091 | return result; |
1453 | 1454 | return " at line " + line + " column " + column + " path " + getPath(); |
1454 | 1455 | } |
1455 | 1456 | |
1456 | /** | |
1457 | * Returns a <a href="http://goessner.net/articles/JsonPath/">JsonPath</a> to | |
1458 | * the current location in the JSON value. | |
1459 | */ | |
1460 | public String getPath() { | |
1457 | private String getPath(boolean usePreviousPath) { | |
1461 | 1458 | StringBuilder result = new StringBuilder().append('$'); |
1462 | for (int i = 0, size = stackSize; i < size; i++) { | |
1459 | for (int i = 0; i < stackSize; i++) { | |
1463 | 1460 | switch (stack[i]) { |
1464 | 1461 | case JsonScope.EMPTY_ARRAY: |
1465 | 1462 | case JsonScope.NONEMPTY_ARRAY: |
1466 | result.append('[').append(pathIndices[i]).append(']'); | |
1463 | int pathIndex = pathIndices[i]; | |
1464 | // If index is last path element it points to next array element; have to decrement | |
1465 | if (usePreviousPath && pathIndex > 0 && i == stackSize - 1) { | |
1466 | pathIndex--; | |
1467 | } | |
1468 | result.append('[').append(pathIndex).append(']'); | |
1467 | 1469 | break; |
1468 | ||
1469 | 1470 | case JsonScope.EMPTY_OBJECT: |
1470 | 1471 | case JsonScope.DANGLING_NAME: |
1471 | 1472 | case JsonScope.NONEMPTY_OBJECT: |
1474 | 1475 | result.append(pathNames[i]); |
1475 | 1476 | } |
1476 | 1477 | break; |
1477 | ||
1478 | 1478 | case JsonScope.NONEMPTY_DOCUMENT: |
1479 | 1479 | case JsonScope.EMPTY_DOCUMENT: |
1480 | 1480 | case JsonScope.CLOSED: |
1482 | 1482 | } |
1483 | 1483 | } |
1484 | 1484 | return result.toString(); |
1485 | } | |
1486 | ||
1487 | /** | |
1488 | * Returns a <a href="https://goessner.net/articles/JsonPath/">JsonPath</a> | |
1489 | * in <i>dot-notation</i> to the previous (or current) location in the JSON document: | |
1490 | * <ul> | |
1491 | * <li>For JSON arrays the path points to the index of the previous element.<br> | |
1492 | * If no element has been consumed yet it uses the index 0 (even if there are no elements).</li> | |
1493 | * <li>For JSON objects the path points to the last property, or to the current | |
1494 | * property if its value has not been consumed yet.</li> | |
1495 | * </ul> | |
1496 | * | |
1497 | * <p>This method can be useful to add additional context to exception messages | |
1498 | * <i>after</i> a value has been consumed. | |
1499 | */ | |
1500 | public String getPreviousPath() { | |
1501 | return getPath(true); | |
1502 | } | |
1503 | ||
1504 | /** | |
1505 | * Returns a <a href="https://goessner.net/articles/JsonPath/">JsonPath</a> | |
1506 | * in <i>dot-notation</i> to the next (or current) location in the JSON document: | |
1507 | * <ul> | |
1508 | * <li>For JSON arrays the path points to the index of the next element (even | |
1509 | * if there are no further elements).</li> | |
1510 | * <li>For JSON objects the path points to the last property, or to the current | |
1511 | * property if its value has not been consumed yet.</li> | |
1512 | * </ul> | |
1513 | * | |
1514 | * <p>This method can be useful to add additional context to exception messages | |
1515 | * <i>before</i> a value is consumed, for example when the {@linkplain #peek() peeked} | |
1516 | * token is unexpected. | |
1517 | */ | |
1518 | public String getPath() { | |
1519 | return getPath(false); | |
1485 | 1520 | } |
1486 | 1521 | |
1487 | 1522 | /** |
1545 | 1580 | case '\'': |
1546 | 1581 | case '"': |
1547 | 1582 | case '\\': |
1548 | case '/': | |
1549 | return escaped; | |
1583 | case '/': | |
1584 | return escaped; | |
1550 | 1585 | default: |
1551 | // throw error when none of the above cases are matched | |
1552 | throw syntaxError("Invalid escape sequence"); | |
1586 | // throw error when none of the above cases are matched | |
1587 | throw syntaxError("Invalid escape sequence"); | |
1553 | 1588 | } |
1554 | 1589 | } |
1555 | 1590 | |
1569 | 1604 | nextNonWhitespace(true); |
1570 | 1605 | pos--; |
1571 | 1606 | |
1607 | if (pos + 5 > limit && !fillBuffer(5)) { | |
1608 | return; | |
1609 | } | |
1610 | ||
1572 | 1611 | int p = pos; |
1573 | if (p + 5 > limit && !fillBuffer(5)) { | |
1574 | return; | |
1575 | } | |
1576 | ||
1577 | 1612 | char[] buf = buffer; |
1578 | 1613 | if(buf[p] != ')' || buf[p + 1] != ']' || buf[p + 2] != '}' || buf[p + 3] != '\'' || buf[p + 4] != '\n') { |
1579 | 1614 | return; // not a security token! |
19 | 19 | import java.io.Flushable; |
20 | 20 | import java.io.IOException; |
21 | 21 | import java.io.Writer; |
22 | import java.math.BigDecimal; | |
23 | import java.math.BigInteger; | |
22 | 24 | import java.util.Arrays; |
25 | import java.util.concurrent.atomic.AtomicInteger; | |
26 | import java.util.concurrent.atomic.AtomicLong; | |
27 | import java.util.regex.Pattern; | |
23 | 28 | |
24 | 29 | import static com.google.gson.stream.JsonScope.DANGLING_NAME; |
25 | 30 | import static com.google.gson.stream.JsonScope.EMPTY_ARRAY; |
129 | 134 | */ |
130 | 135 | public class JsonWriter implements Closeable, Flushable { |
131 | 136 | |
137 | // Syntax as defined by https://datatracker.ietf.org/doc/html/rfc8259#section-6 | |
138 | private static final Pattern VALID_JSON_NUMBER_PATTERN = Pattern.compile("-?(?:0|[1-9][0-9]*)(?:\\.[0-9]+)?(?:[eE][-+]?[0-9]+)?"); | |
139 | ||
132 | 140 | /* |
133 | 141 | * From RFC 7159, "All Unicode characters may be placed within the |
134 | 142 | * quotation marks except for the characters that must be escaped: |
487 | 495 | * @param value a finite value. May not be {@link Double#isNaN() NaNs} or |
488 | 496 | * {@link Double#isInfinite() infinities}. |
489 | 497 | * @return this writer. |
498 | * @throws IllegalArgumentException if the value is NaN or Infinity and this writer is | |
499 | * not {@link #setLenient(boolean) lenient}. | |
490 | 500 | */ |
491 | 501 | public JsonWriter value(double value) throws IOException { |
492 | 502 | writeDeferredName(); |
511 | 521 | } |
512 | 522 | |
513 | 523 | /** |
514 | * Encodes {@code value}. | |
524 | * Returns whether the {@code toString()} of {@code c} can be trusted to return | |
525 | * a valid JSON number. | |
526 | */ | |
527 | private static boolean isTrustedNumberType(Class<? extends Number> c) { | |
528 | // Note: Don't consider LazilyParsedNumber trusted because it could contain | |
529 | // an arbitrary malformed string | |
530 | return c == Integer.class || c == Long.class || c == Double.class || c == Float.class || c == Byte.class || c == Short.class | |
531 | || c == BigDecimal.class || c == BigInteger.class || c == AtomicInteger.class || c == AtomicLong.class; | |
532 | } | |
533 | ||
534 | /** | |
535 | * Encodes {@code value}. The value is written by directly writing the {@link Number#toString()} | |
536 | * result to JSON. Implementations must make sure that the result represents a valid JSON number. | |
515 | 537 | * |
516 | 538 | * @param value a finite value. May not be {@link Double#isNaN() NaNs} or |
517 | 539 | * {@link Double#isInfinite() infinities}. |
518 | 540 | * @return this writer. |
541 | * @throws IllegalArgumentException if the value is NaN or Infinity and this writer is | |
542 | * not {@link #setLenient(boolean) lenient}; or if the {@code toString()} result is not a | |
543 | * valid JSON number. | |
519 | 544 | */ |
520 | 545 | public JsonWriter value(Number value) throws IOException { |
521 | 546 | if (value == null) { |
524 | 549 | |
525 | 550 | writeDeferredName(); |
526 | 551 | String string = value.toString(); |
527 | if (!lenient | |
528 | && (string.equals("-Infinity") || string.equals("Infinity") || string.equals("NaN"))) { | |
529 | throw new IllegalArgumentException("Numeric values must be finite, but was " + value); | |
530 | } | |
552 | if (string.equals("-Infinity") || string.equals("Infinity") || string.equals("NaN")) { | |
553 | if (!lenient) { | |
554 | throw new IllegalArgumentException("Numeric values must be finite, but was " + string); | |
555 | } | |
556 | } else { | |
557 | Class<? extends Number> numberClass = value.getClass(); | |
558 | // Validate that string is valid before writing it directly to JSON output | |
559 | if (!isTrustedNumberType(numberClass) && !VALID_JSON_NUMBER_PATTERN.matcher(string).matches()) { | |
560 | throw new IllegalArgumentException("String created by " + numberClass + " is not a valid JSON number: " + string); | |
561 | } | |
562 | } | |
563 | ||
531 | 564 | beforeValue(); |
532 | 565 | out.append(string); |
533 | 566 | return this; |
29 | 29 | } |
30 | 30 | |
31 | 31 | public MalformedJsonException(String msg, Throwable throwable) { |
32 | super(msg); | |
33 | // Using initCause() instead of calling super() because Java 1.5 didn't retrofit IOException | |
34 | // with a constructor with Throwable. This was done in Java 1.6 | |
35 | initCause(throwable); | |
32 | super(msg, throwable); | |
36 | 33 | } |
37 | 34 | |
38 | 35 | public MalformedJsonException(Throwable throwable) { |
39 | // Using initCause() instead of calling super() because Java 1.5 didn't retrofit IOException | |
40 | // with a constructor with Throwable. This was done in Java 1.6 | |
41 | initCause(throwable); | |
36 | super(throwable); | |
42 | 37 | } |
43 | 38 | } |
0 | /** | |
1 | * This package provides classes for processing JSON in an efficient streaming way. | |
2 | */ | |
3 | package com.google.gson.stream; |
7 | 7 | exports com.google.gson.reflect; |
8 | 8 | exports com.google.gson.stream; |
9 | 9 | |
10 | requires transitive java.sql; | |
10 | // Optional dependency on java.sql | |
11 | requires static java.sql; | |
12 | ||
13 | // Optional dependency on jdk.unsupported for JDK's sun.misc.Unsafe | |
14 | requires static jdk.unsupported; | |
11 | 15 | } |
0 | /* | |
1 | * Copyright (C) 2008 Google Inc. | |
2 | * | |
3 | * Licensed under the Apache License, Version 2.0 (the "License"); | |
4 | * you may not use this file except in compliance with the License. | |
5 | * You may obtain a copy of the License at | |
6 | * | |
7 | * http://www.apache.org/licenses/LICENSE-2.0 | |
8 | * | |
9 | * Unless required by applicable law or agreed to in writing, software | |
10 | * distributed under the License is distributed on an "AS IS" BASIS, | |
11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
12 | * See the License for the specific language governing permissions and | |
13 | * limitations under the License. | |
14 | */ | |
15 | ||
16 | package com.google.gson; | |
17 | ||
18 | import java.io.IOException; | |
19 | import java.text.DateFormat; | |
20 | import java.text.SimpleDateFormat; | |
21 | import java.util.Date; | |
22 | import java.util.Locale; | |
23 | import java.util.TimeZone; | |
24 | ||
25 | import com.google.gson.internal.JavaVersion; | |
26 | ||
27 | import junit.framework.TestCase; | |
28 | ||
29 | /** | |
30 | * A simple unit test for the {@link DefaultDateTypeAdapter} class. | |
31 | * | |
32 | * @author Joel Leitch | |
33 | */ | |
34 | public class DefaultDateTypeAdapterTest extends TestCase { | |
35 | ||
36 | public void testFormattingInEnUs() { | |
37 | assertFormattingAlwaysEmitsUsLocale(Locale.US); | |
38 | } | |
39 | ||
40 | public void testFormattingInFr() { | |
41 | assertFormattingAlwaysEmitsUsLocale(Locale.FRANCE); | |
42 | } | |
43 | ||
44 | private void assertFormattingAlwaysEmitsUsLocale(Locale locale) { | |
45 | TimeZone defaultTimeZone = TimeZone.getDefault(); | |
46 | TimeZone.setDefault(TimeZone.getTimeZone("UTC")); | |
47 | Locale defaultLocale = Locale.getDefault(); | |
48 | Locale.setDefault(locale); | |
49 | try { | |
50 | String afterYearSep = JavaVersion.isJava9OrLater() ? ", " : " "; | |
51 | String afterYearLongSep = JavaVersion.isJava9OrLater() ? " at " : " "; | |
52 | String utcFull = JavaVersion.isJava9OrLater() ? "Coordinated Universal Time" : "UTC"; | |
53 | assertFormatted(String.format("Jan 1, 1970%s12:00:00 AM", afterYearSep), | |
54 | new DefaultDateTypeAdapter(Date.class)); | |
55 | assertFormatted("1/1/70", new DefaultDateTypeAdapter(Date.class, DateFormat.SHORT)); | |
56 | assertFormatted("Jan 1, 1970", new DefaultDateTypeAdapter(Date.class, DateFormat.MEDIUM)); | |
57 | assertFormatted("January 1, 1970", new DefaultDateTypeAdapter(Date.class, DateFormat.LONG)); | |
58 | assertFormatted(String.format("1/1/70%s12:00 AM", afterYearSep), | |
59 | new DefaultDateTypeAdapter(DateFormat.SHORT, DateFormat.SHORT)); | |
60 | assertFormatted(String.format("Jan 1, 1970%s12:00:00 AM", afterYearSep), | |
61 | new DefaultDateTypeAdapter(DateFormat.MEDIUM, DateFormat.MEDIUM)); | |
62 | assertFormatted(String.format("January 1, 1970%s12:00:00 AM UTC", afterYearLongSep), | |
63 | new DefaultDateTypeAdapter(DateFormat.LONG, DateFormat.LONG)); | |
64 | assertFormatted(String.format("Thursday, January 1, 1970%s12:00:00 AM %s", afterYearLongSep, utcFull), | |
65 | new DefaultDateTypeAdapter(DateFormat.FULL, DateFormat.FULL)); | |
66 | } finally { | |
67 | TimeZone.setDefault(defaultTimeZone); | |
68 | Locale.setDefault(defaultLocale); | |
69 | } | |
70 | } | |
71 | ||
72 | public void testParsingDatesFormattedWithSystemLocale() throws Exception { | |
73 | TimeZone defaultTimeZone = TimeZone.getDefault(); | |
74 | TimeZone.setDefault(TimeZone.getTimeZone("UTC")); | |
75 | Locale defaultLocale = Locale.getDefault(); | |
76 | Locale.setDefault(Locale.FRANCE); | |
77 | try { | |
78 | String afterYearSep = JavaVersion.isJava9OrLater() ? " à " : " "; | |
79 | assertParsed(String.format("1 janv. 1970%s00:00:00", afterYearSep), | |
80 | new DefaultDateTypeAdapter(Date.class)); | |
81 | assertParsed("01/01/70", new DefaultDateTypeAdapter(Date.class, DateFormat.SHORT)); | |
82 | assertParsed("1 janv. 1970", new DefaultDateTypeAdapter(Date.class, DateFormat.MEDIUM)); | |
83 | assertParsed("1 janvier 1970", new DefaultDateTypeAdapter(Date.class, DateFormat.LONG)); | |
84 | assertParsed("01/01/70 00:00", | |
85 | new DefaultDateTypeAdapter(DateFormat.SHORT, DateFormat.SHORT)); | |
86 | assertParsed(String.format("1 janv. 1970%s00:00:00", afterYearSep), | |
87 | new DefaultDateTypeAdapter(DateFormat.MEDIUM, DateFormat.MEDIUM)); | |
88 | assertParsed(String.format("1 janvier 1970%s00:00:00 UTC", afterYearSep), | |
89 | new DefaultDateTypeAdapter(DateFormat.LONG, DateFormat.LONG)); | |
90 | assertParsed(JavaVersion.isJava9OrLater() ? (JavaVersion.getMajorJavaVersion() <11 ? | |
91 | "jeudi 1 janvier 1970 à 00:00:00 Coordinated Universal Time" : | |
92 | "jeudi 1 janvier 1970 à 00:00:00 Temps universel coordonné") : | |
93 | "jeudi 1 janvier 1970 00 h 00 UTC", | |
94 | new DefaultDateTypeAdapter(DateFormat.FULL, DateFormat.FULL)); | |
95 | } finally { | |
96 | TimeZone.setDefault(defaultTimeZone); | |
97 | Locale.setDefault(defaultLocale); | |
98 | } | |
99 | } | |
100 | ||
101 | public void testParsingDatesFormattedWithUsLocale() throws Exception { | |
102 | TimeZone defaultTimeZone = TimeZone.getDefault(); | |
103 | TimeZone.setDefault(TimeZone.getTimeZone("UTC")); | |
104 | Locale defaultLocale = Locale.getDefault(); | |
105 | Locale.setDefault(Locale.US); | |
106 | try { | |
107 | assertParsed("Jan 1, 1970 0:00:00 AM", new DefaultDateTypeAdapter(Date.class)); | |
108 | assertParsed("1/1/70", new DefaultDateTypeAdapter(Date.class, DateFormat.SHORT)); | |
109 | assertParsed("Jan 1, 1970", new DefaultDateTypeAdapter(Date.class, DateFormat.MEDIUM)); | |
110 | assertParsed("January 1, 1970", new DefaultDateTypeAdapter(Date.class, DateFormat.LONG)); | |
111 | assertParsed("1/1/70 0:00 AM", | |
112 | new DefaultDateTypeAdapter(DateFormat.SHORT, DateFormat.SHORT)); | |
113 | assertParsed("Jan 1, 1970 0:00:00 AM", | |
114 | new DefaultDateTypeAdapter(DateFormat.MEDIUM, DateFormat.MEDIUM)); | |
115 | assertParsed("January 1, 1970 0:00:00 AM UTC", | |
116 | new DefaultDateTypeAdapter(DateFormat.LONG, DateFormat.LONG)); | |
117 | assertParsed("Thursday, January 1, 1970 0:00:00 AM UTC", | |
118 | new DefaultDateTypeAdapter(DateFormat.FULL, DateFormat.FULL)); | |
119 | } finally { | |
120 | TimeZone.setDefault(defaultTimeZone); | |
121 | Locale.setDefault(defaultLocale); | |
122 | } | |
123 | } | |
124 | ||
125 | public void testFormatUsesDefaultTimezone() throws Exception { | |
126 | TimeZone defaultTimeZone = TimeZone.getDefault(); | |
127 | TimeZone.setDefault(TimeZone.getTimeZone("America/Los_Angeles")); | |
128 | Locale defaultLocale = Locale.getDefault(); | |
129 | Locale.setDefault(Locale.US); | |
130 | try { | |
131 | String afterYearSep = JavaVersion.isJava9OrLater() ? ", " : " "; | |
132 | assertFormatted(String.format("Dec 31, 1969%s4:00:00 PM", afterYearSep), | |
133 | new DefaultDateTypeAdapter(Date.class)); | |
134 | assertParsed("Dec 31, 1969 4:00:00 PM", new DefaultDateTypeAdapter(Date.class)); | |
135 | } finally { | |
136 | TimeZone.setDefault(defaultTimeZone); | |
137 | Locale.setDefault(defaultLocale); | |
138 | } | |
139 | } | |
140 | ||
141 | public void testDateDeserializationISO8601() throws Exception { | |
142 | DefaultDateTypeAdapter adapter = new DefaultDateTypeAdapter(Date.class); | |
143 | assertParsed("1970-01-01T00:00:00.000Z", adapter); | |
144 | assertParsed("1970-01-01T00:00Z", adapter); | |
145 | assertParsed("1970-01-01T00:00:00+00:00", adapter); | |
146 | assertParsed("1970-01-01T01:00:00+01:00", adapter); | |
147 | assertParsed("1970-01-01T01:00:00+01", adapter); | |
148 | } | |
149 | ||
150 | public void testDateSerialization() throws Exception { | |
151 | int dateStyle = DateFormat.LONG; | |
152 | DefaultDateTypeAdapter dateTypeAdapter = new DefaultDateTypeAdapter(Date.class, dateStyle); | |
153 | DateFormat formatter = DateFormat.getDateInstance(dateStyle, Locale.US); | |
154 | Date currentDate = new Date(); | |
155 | ||
156 | String dateString = dateTypeAdapter.toJson(currentDate); | |
157 | assertEquals(toLiteral(formatter.format(currentDate)), dateString); | |
158 | } | |
159 | ||
160 | public void testDatePattern() throws Exception { | |
161 | String pattern = "yyyy-MM-dd"; | |
162 | DefaultDateTypeAdapter dateTypeAdapter = new DefaultDateTypeAdapter(Date.class, pattern); | |
163 | DateFormat formatter = new SimpleDateFormat(pattern); | |
164 | Date currentDate = new Date(); | |
165 | ||
166 | String dateString = dateTypeAdapter.toJson(currentDate); | |
167 | assertEquals(toLiteral(formatter.format(currentDate)), dateString); | |
168 | } | |
169 | ||
170 | @SuppressWarnings("unused") | |
171 | public void testInvalidDatePattern() throws Exception { | |
172 | try { | |
173 | new DefaultDateTypeAdapter(Date.class, "I am a bad Date pattern...."); | |
174 | fail("Invalid date pattern should fail."); | |
175 | } catch (IllegalArgumentException expected) { } | |
176 | } | |
177 | ||
178 | public void testNullValue() throws Exception { | |
179 | DefaultDateTypeAdapter adapter = new DefaultDateTypeAdapter(Date.class); | |
180 | assertNull(adapter.fromJson("null")); | |
181 | assertEquals("null", adapter.toJson(null)); | |
182 | } | |
183 | ||
184 | public void testUnexpectedToken() throws Exception { | |
185 | try { | |
186 | DefaultDateTypeAdapter adapter = new DefaultDateTypeAdapter(Date.class); | |
187 | adapter.fromJson("{}"); | |
188 | fail("Unexpected token should fail."); | |
189 | } catch (IllegalStateException expected) { } | |
190 | } | |
191 | ||
192 | private void assertFormatted(String formatted, DefaultDateTypeAdapter adapter) { | |
193 | assertEquals(toLiteral(formatted), adapter.toJson(new Date(0))); | |
194 | } | |
195 | ||
196 | private void assertParsed(String date, DefaultDateTypeAdapter adapter) throws IOException { | |
197 | assertEquals(date, new Date(0), adapter.fromJson(toLiteral(date))); | |
198 | assertEquals("ISO 8601", new Date(0), adapter.fromJson(toLiteral("1970-01-01T00:00:00Z"))); | |
199 | } | |
200 | ||
201 | private static String toLiteral(String s) { | |
202 | return '"' + s + '"'; | |
203 | } | |
204 | } |
0 | package com.google.gson; | |
1 | ||
2 | import static org.junit.Assert.assertEquals; | |
3 | import static org.junit.Assert.assertNotEquals; | |
4 | import java.lang.reflect.Field; | |
5 | import java.util.Locale; | |
6 | import org.junit.Test; | |
7 | import com.google.gson.functional.FieldNamingTest; | |
8 | ||
9 | /** | |
10 | * Performs tests directly against {@link FieldNamingPolicy}; for integration tests | |
11 | * see {@link FieldNamingTest}. | |
12 | */ | |
13 | public class FieldNamingPolicyTest { | |
14 | @Test | |
15 | public void testSeparateCamelCase() { | |
16 | // Map from original -> expected | |
17 | String[][] argumentPairs = { | |
18 | { "a", "a" }, | |
19 | { "ab", "ab" }, | |
20 | { "Ab", "Ab" }, | |
21 | { "aB", "a_B" }, | |
22 | { "AB", "A_B" }, | |
23 | { "A_B", "A__B" }, | |
24 | { "firstSecondThird", "first_Second_Third" }, | |
25 | { "__", "__" }, | |
26 | { "_123", "_123" } | |
27 | }; | |
28 | ||
29 | for (String[] pair : argumentPairs) { | |
30 | assertEquals(pair[1], FieldNamingPolicy.separateCamelCase(pair[0], '_')); | |
31 | } | |
32 | } | |
33 | ||
34 | @Test | |
35 | public void testUpperCaseFirstLetter() { | |
36 | // Map from original -> expected | |
37 | String[][] argumentPairs = { | |
38 | { "a", "A" }, | |
39 | { "ab", "Ab" }, | |
40 | { "AB", "AB" }, | |
41 | { "_a", "_A" }, | |
42 | { "_ab", "_Ab" }, | |
43 | { "__", "__" }, | |
44 | { "_1", "_1" }, | |
45 | // Not a letter, but has uppercase variant (should not be uppercased) | |
46 | // See https://github.com/google/gson/issues/1965 | |
47 | { "\u2170", "\u2170" }, | |
48 | { "_\u2170", "_\u2170" }, | |
49 | { "\u2170a", "\u2170A" }, | |
50 | }; | |
51 | ||
52 | for (String[] pair : argumentPairs) { | |
53 | assertEquals(pair[1], FieldNamingPolicy.upperCaseFirstLetter(pair[0])); | |
54 | } | |
55 | } | |
56 | ||
57 | /** | |
58 | * Upper casing policies should be unaffected by default Locale. | |
59 | */ | |
60 | @Test | |
61 | public void testUpperCasingLocaleIndependent() throws Exception { | |
62 | class Dummy { | |
63 | @SuppressWarnings("unused") | |
64 | int i; | |
65 | } | |
66 | ||
67 | FieldNamingPolicy[] policies = { | |
68 | FieldNamingPolicy.UPPER_CAMEL_CASE, | |
69 | FieldNamingPolicy.UPPER_CAMEL_CASE_WITH_SPACES, | |
70 | FieldNamingPolicy.UPPER_CASE_WITH_UNDERSCORES, | |
71 | }; | |
72 | ||
73 | Field field = Dummy.class.getDeclaredField("i"); | |
74 | String name = field.getName(); | |
75 | String expected = name.toUpperCase(Locale.ROOT); | |
76 | ||
77 | Locale oldLocale = Locale.getDefault(); | |
78 | // Set Turkish as Locale which has special case conversion rules | |
79 | Locale.setDefault(new Locale("tr")); | |
80 | ||
81 | try { | |
82 | // Verify that default Locale has different case conversion rules | |
83 | assertNotEquals("Test setup is broken", expected, name.toUpperCase()); | |
84 | ||
85 | for (FieldNamingPolicy policy : policies) { | |
86 | // Should ignore default Locale | |
87 | assertEquals("Unexpected conversion for " + policy, expected, policy.translateName(field)); | |
88 | } | |
89 | } finally { | |
90 | Locale.setDefault(oldLocale); | |
91 | } | |
92 | } | |
93 | ||
94 | /** | |
95 | * Lower casing policies should be unaffected by default Locale. | |
96 | */ | |
97 | @Test | |
98 | public void testLowerCasingLocaleIndependent() throws Exception { | |
99 | class Dummy { | |
100 | @SuppressWarnings("unused") | |
101 | int I; | |
102 | } | |
103 | ||
104 | FieldNamingPolicy[] policies = { | |
105 | FieldNamingPolicy.LOWER_CASE_WITH_DASHES, | |
106 | FieldNamingPolicy.LOWER_CASE_WITH_DOTS, | |
107 | FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES, | |
108 | }; | |
109 | ||
110 | Field field = Dummy.class.getDeclaredField("I"); | |
111 | String name = field.getName(); | |
112 | String expected = name.toLowerCase(Locale.ROOT); | |
113 | ||
114 | Locale oldLocale = Locale.getDefault(); | |
115 | // Set Turkish as Locale which has special case conversion rules | |
116 | Locale.setDefault(new Locale("tr")); | |
117 | ||
118 | try { | |
119 | // Verify that default Locale has different case conversion rules | |
120 | assertNotEquals("Test setup is broken", expected, name.toLowerCase()); | |
121 | ||
122 | for (FieldNamingPolicy policy : policies) { | |
123 | // Should ignore default Locale | |
124 | assertEquals("Unexpected conversion for " + policy, expected, policy.translateName(field)); | |
125 | } | |
126 | } finally { | |
127 | Locale.setDefault(oldLocale); | |
128 | } | |
129 | } | |
130 | } |
83 | 83 | static class HasTransients { |
84 | 84 | transient String a = "a"; |
85 | 85 | } |
86 | ||
87 | public void testDisableJdkUnsafe() { | |
88 | Gson gson = new GsonBuilder() | |
89 | .disableJdkUnsafe() | |
90 | .create(); | |
91 | try { | |
92 | gson.fromJson("{}", ClassWithoutNoArgsConstructor.class); | |
93 | fail("Expected exception"); | |
94 | } catch (JsonIOException expected) { | |
95 | assertEquals( | |
96 | "Unable to create instance of class com.google.gson.GsonBuilderTest$ClassWithoutNoArgsConstructor; " | |
97 | + "usage of JDK Unsafe is disabled. Registering an InstanceCreator or a TypeAdapter for this type, " | |
98 | + "adding a no-args constructor, or enabling usage of JDK Unsafe may fix this problem.", | |
99 | expected.getMessage() | |
100 | ); | |
101 | } | |
102 | } | |
103 | ||
104 | private static class ClassWithoutNoArgsConstructor { | |
105 | @SuppressWarnings("unused") | |
106 | public ClassWithoutNoArgsConstructor(String s) { | |
107 | } | |
108 | } | |
86 | 109 | } |
18 | 18 | import com.google.gson.internal.Excluder; |
19 | 19 | import com.google.gson.stream.JsonReader; |
20 | 20 | import com.google.gson.stream.JsonWriter; |
21 | import com.google.gson.stream.MalformedJsonException; | |
21 | 22 | import java.io.IOException; |
23 | import java.io.StringReader; | |
24 | import java.io.StringWriter; | |
22 | 25 | import java.lang.reflect.Field; |
23 | 26 | import java.lang.reflect.Type; |
24 | 27 | import java.text.DateFormat; |
43 | 46 | } |
44 | 47 | }; |
45 | 48 | |
49 | private static final ToNumberStrategy CUSTOM_OBJECT_TO_NUMBER_STRATEGY = ToNumberPolicy.DOUBLE; | |
50 | private static final ToNumberStrategy CUSTOM_NUMBER_TO_NUMBER_STRATEGY = ToNumberPolicy.LAZILY_PARSED_NUMBER; | |
51 | ||
46 | 52 | public void testOverridesDefaultExcluder() { |
47 | 53 | Gson gson = new Gson(CUSTOM_EXCLUDER, CUSTOM_FIELD_NAMING_STRATEGY, |
48 | 54 | new HashMap<Type, InstanceCreator<?>>(), true, false, true, false, |
49 | true, true, false, LongSerializationPolicy.DEFAULT, null, DateFormat.DEFAULT, | |
55 | true, true, false, true, LongSerializationPolicy.DEFAULT, null, DateFormat.DEFAULT, | |
50 | 56 | DateFormat.DEFAULT, new ArrayList<TypeAdapterFactory>(), |
51 | new ArrayList<TypeAdapterFactory>(), new ArrayList<TypeAdapterFactory>()); | |
57 | new ArrayList<TypeAdapterFactory>(), new ArrayList<TypeAdapterFactory>(), | |
58 | CUSTOM_OBJECT_TO_NUMBER_STRATEGY, CUSTOM_NUMBER_TO_NUMBER_STRATEGY); | |
52 | 59 | |
53 | assertEquals(CUSTOM_EXCLUDER, gson.excluder()); | |
60 | assertEquals(CUSTOM_EXCLUDER, gson.excluder); | |
54 | 61 | assertEquals(CUSTOM_FIELD_NAMING_STRATEGY, gson.fieldNamingStrategy()); |
55 | 62 | assertEquals(true, gson.serializeNulls()); |
56 | 63 | assertEquals(false, gson.htmlSafe()); |
59 | 66 | public void testClonedTypeAdapterFactoryListsAreIndependent() { |
60 | 67 | Gson original = new Gson(CUSTOM_EXCLUDER, CUSTOM_FIELD_NAMING_STRATEGY, |
61 | 68 | new HashMap<Type, InstanceCreator<?>>(), true, false, true, false, |
62 | true, true, false, LongSerializationPolicy.DEFAULT, null, DateFormat.DEFAULT, | |
69 | true, true, false, true, LongSerializationPolicy.DEFAULT, null, DateFormat.DEFAULT, | |
63 | 70 | DateFormat.DEFAULT, new ArrayList<TypeAdapterFactory>(), |
64 | new ArrayList<TypeAdapterFactory>(), new ArrayList<TypeAdapterFactory>()); | |
71 | new ArrayList<TypeAdapterFactory>(), new ArrayList<TypeAdapterFactory>(), | |
72 | CUSTOM_OBJECT_TO_NUMBER_STRATEGY, CUSTOM_NUMBER_TO_NUMBER_STRATEGY); | |
65 | 73 | |
66 | 74 | Gson clone = original.newBuilder() |
67 | 75 | .registerTypeAdapter(Object.class, new TestTypeAdapter()) |
76 | 84 | } |
77 | 85 | @Override public Object read(JsonReader in) throws IOException { return null; } |
78 | 86 | } |
87 | ||
88 | public void testNewJsonWriter_Default() throws IOException { | |
89 | StringWriter writer = new StringWriter(); | |
90 | JsonWriter jsonWriter = new Gson().newJsonWriter(writer); | |
91 | jsonWriter.beginObject(); | |
92 | jsonWriter.name("test"); | |
93 | jsonWriter.nullValue(); | |
94 | jsonWriter.name("<test2"); | |
95 | jsonWriter.value(true); | |
96 | jsonWriter.endObject(); | |
97 | ||
98 | try { | |
99 | // Additional top-level value | |
100 | jsonWriter.value(1); | |
101 | fail(); | |
102 | } catch (IllegalStateException expected) { | |
103 | assertEquals("JSON must have only one top-level value.", expected.getMessage()); | |
104 | } | |
105 | ||
106 | jsonWriter.close(); | |
107 | assertEquals("{\"\\u003ctest2\":true}", writer.toString()); | |
108 | } | |
109 | ||
110 | public void testNewJsonWriter_Custom() throws IOException { | |
111 | StringWriter writer = new StringWriter(); | |
112 | JsonWriter jsonWriter = new GsonBuilder() | |
113 | .disableHtmlEscaping() | |
114 | .generateNonExecutableJson() | |
115 | .setPrettyPrinting() | |
116 | .serializeNulls() | |
117 | .setLenient() | |
118 | .create() | |
119 | .newJsonWriter(writer); | |
120 | jsonWriter.beginObject(); | |
121 | jsonWriter.name("test"); | |
122 | jsonWriter.nullValue(); | |
123 | jsonWriter.name("<test2"); | |
124 | jsonWriter.value(true); | |
125 | jsonWriter.endObject(); | |
126 | ||
127 | // Additional top-level value | |
128 | jsonWriter.value(1); | |
129 | ||
130 | jsonWriter.close(); | |
131 | assertEquals(")]}'\n{\n \"test\": null,\n \"<test2\": true\n}1", writer.toString()); | |
132 | } | |
133 | ||
134 | public void testNewJsonReader_Default() throws IOException { | |
135 | String json = "test"; // String without quotes | |
136 | JsonReader jsonReader = new Gson().newJsonReader(new StringReader(json)); | |
137 | try { | |
138 | jsonReader.nextString(); | |
139 | fail(); | |
140 | } catch (MalformedJsonException expected) { | |
141 | } | |
142 | jsonReader.close(); | |
143 | } | |
144 | ||
145 | public void testNewJsonReader_Custom() throws IOException { | |
146 | String json = "test"; // String without quotes | |
147 | JsonReader jsonReader = new GsonBuilder() | |
148 | .setLenient() | |
149 | .create() | |
150 | .newJsonReader(new StringReader(json)); | |
151 | assertEquals("test", jsonReader.nextString()); | |
152 | jsonReader.close(); | |
153 | } | |
79 | 154 | } |
103 | 103 | JsonElement jsonElement = jsonObj.get(propertyName); |
104 | 104 | assertNotNull(jsonElement); |
105 | 105 | assertEquals(String.valueOf(value), jsonElement.getAsString()); |
106 | assertEquals(value, jsonElement.getAsCharacter()); | |
106 | ||
107 | @SuppressWarnings("deprecation") | |
108 | char character = jsonElement.getAsCharacter(); | |
109 | assertEquals(value, character); | |
107 | 110 | } |
108 | 111 | |
109 | 112 | /** |
28 | 28 | public void testDefaultLongSerialization() throws Exception { |
29 | 29 | JsonElement element = LongSerializationPolicy.DEFAULT.serialize(1556L); |
30 | 30 | assertTrue(element.isJsonPrimitive()); |
31 | ||
31 | ||
32 | 32 | JsonPrimitive jsonPrimitive = element.getAsJsonPrimitive(); |
33 | 33 | assertFalse(jsonPrimitive.isString()); |
34 | 34 | assertTrue(jsonPrimitive.isNumber()); |
35 | 35 | assertEquals(1556L, element.getAsLong()); |
36 | 36 | } |
37 | ||
37 | ||
38 | 38 | public void testDefaultLongSerializationIntegration() { |
39 | 39 | Gson gson = new GsonBuilder() |
40 | .setLongSerializationPolicy(LongSerializationPolicy.DEFAULT) | |
41 | .create(); | |
40 | .setLongSerializationPolicy(LongSerializationPolicy.DEFAULT) | |
41 | .create(); | |
42 | 42 | assertEquals("[1]", gson.toJson(new long[] { 1L }, long[].class)); |
43 | 43 | assertEquals("[1]", gson.toJson(new Long[] { 1L }, Long[].class)); |
44 | } | |
45 | ||
46 | public void testDefaultLongSerializationNull() { | |
47 | LongSerializationPolicy policy = LongSerializationPolicy.DEFAULT; | |
48 | assertTrue(policy.serialize(null).isJsonNull()); | |
49 | ||
50 | Gson gson = new GsonBuilder() | |
51 | .setLongSerializationPolicy(policy) | |
52 | .create(); | |
53 | assertEquals("null", gson.toJson(null, Long.class)); | |
44 | 54 | } |
45 | 55 | |
46 | 56 | public void testStringLongSerialization() throws Exception { |
55 | 65 | |
56 | 66 | public void testStringLongSerializationIntegration() { |
57 | 67 | Gson gson = new GsonBuilder() |
58 | .setLongSerializationPolicy(LongSerializationPolicy.STRING) | |
59 | .create(); | |
68 | .setLongSerializationPolicy(LongSerializationPolicy.STRING) | |
69 | .create(); | |
60 | 70 | assertEquals("[\"1\"]", gson.toJson(new long[] { 1L }, long[].class)); |
61 | 71 | assertEquals("[\"1\"]", gson.toJson(new Long[] { 1L }, Long[].class)); |
62 | 72 | } |
73 | ||
74 | public void testStringLongSerializationNull() { | |
75 | LongSerializationPolicy policy = LongSerializationPolicy.STRING; | |
76 | assertTrue(policy.serialize(null).isJsonNull()); | |
77 | ||
78 | Gson gson = new GsonBuilder() | |
79 | .setLongSerializationPolicy(policy) | |
80 | .create(); | |
81 | assertEquals("null", gson.toJson(null, Long.class)); | |
82 | } | |
63 | 83 | } |
0 | /* | |
1 | * Copyright (C) 2021 Google Inc. | |
2 | * | |
3 | * Licensed under the Apache License, Version 2.0 (the "License"); | |
4 | * you may not use this file except in compliance with the License. | |
5 | * You may obtain a copy of the License at | |
6 | * | |
7 | * http://www.apache.org/licenses/LICENSE-2.0 | |
8 | * | |
9 | * Unless required by applicable law or agreed to in writing, software | |
10 | * distributed under the License is distributed on an "AS IS" BASIS, | |
11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
12 | * See the License for the specific language governing permissions and | |
13 | * limitations under the License. | |
14 | */ | |
15 | ||
16 | package com.google.gson; | |
17 | ||
18 | import java.io.IOException; | |
19 | import java.io.StringReader; | |
20 | import java.math.BigDecimal; | |
21 | import com.google.gson.internal.LazilyParsedNumber; | |
22 | import com.google.gson.stream.JsonReader; | |
23 | import com.google.gson.stream.MalformedJsonException; | |
24 | import junit.framework.TestCase; | |
25 | ||
26 | public class ToNumberPolicyTest extends TestCase { | |
27 | public void testDouble() throws IOException { | |
28 | ToNumberStrategy strategy = ToNumberPolicy.DOUBLE; | |
29 | assertEquals(10.1, strategy.readNumber(fromString("10.1"))); | |
30 | assertEquals(3.141592653589793D, strategy.readNumber(fromString("3.141592653589793238462643383279"))); | |
31 | try { | |
32 | strategy.readNumber(fromString("1e400")); | |
33 | fail(); | |
34 | } catch (MalformedJsonException expected) { | |
35 | assertEquals("JSON forbids NaN and infinities: Infinity at line 1 column 6 path $", expected.getMessage()); | |
36 | } | |
37 | try { | |
38 | strategy.readNumber(fromString("\"not-a-number\"")); | |
39 | fail(); | |
40 | } catch (NumberFormatException expected) { | |
41 | } | |
42 | } | |
43 | ||
44 | public void testLazilyParsedNumber() throws IOException { | |
45 | ToNumberStrategy strategy = ToNumberPolicy.LAZILY_PARSED_NUMBER; | |
46 | assertEquals(new LazilyParsedNumber("10.1"), strategy.readNumber(fromString("10.1"))); | |
47 | assertEquals(new LazilyParsedNumber("3.141592653589793238462643383279"), strategy.readNumber(fromString("3.141592653589793238462643383279"))); | |
48 | assertEquals(new LazilyParsedNumber("1e400"), strategy.readNumber(fromString("1e400"))); | |
49 | } | |
50 | ||
51 | public void testLongOrDouble() throws IOException { | |
52 | ToNumberStrategy strategy = ToNumberPolicy.LONG_OR_DOUBLE; | |
53 | assertEquals(10L, strategy.readNumber(fromString("10"))); | |
54 | assertEquals(10.1, strategy.readNumber(fromString("10.1"))); | |
55 | assertEquals(3.141592653589793D, strategy.readNumber(fromString("3.141592653589793238462643383279"))); | |
56 | try { | |
57 | strategy.readNumber(fromString("1e400")); | |
58 | fail(); | |
59 | } catch (MalformedJsonException expected) { | |
60 | assertEquals("JSON forbids NaN and infinities: Infinity; at path $", expected.getMessage()); | |
61 | } | |
62 | try { | |
63 | strategy.readNumber(fromString("\"not-a-number\"")); | |
64 | fail(); | |
65 | } catch (JsonParseException expected) { | |
66 | assertEquals("Cannot parse not-a-number; at path $", expected.getMessage()); | |
67 | } | |
68 | ||
69 | assertEquals(Double.NaN, strategy.readNumber(fromStringLenient("NaN"))); | |
70 | assertEquals(Double.POSITIVE_INFINITY, strategy.readNumber(fromStringLenient("Infinity"))); | |
71 | assertEquals(Double.NEGATIVE_INFINITY, strategy.readNumber(fromStringLenient("-Infinity"))); | |
72 | try { | |
73 | strategy.readNumber(fromString("NaN")); | |
74 | fail(); | |
75 | } catch (MalformedJsonException expected) { | |
76 | assertEquals("Use JsonReader.setLenient(true) to accept malformed JSON at line 1 column 1 path $", expected.getMessage()); | |
77 | } | |
78 | try { | |
79 | strategy.readNumber(fromString("Infinity")); | |
80 | fail(); | |
81 | } catch (MalformedJsonException expected) { | |
82 | assertEquals("Use JsonReader.setLenient(true) to accept malformed JSON at line 1 column 1 path $", expected.getMessage()); | |
83 | } | |
84 | try { | |
85 | strategy.readNumber(fromString("-Infinity")); | |
86 | fail(); | |
87 | } catch (MalformedJsonException expected) { | |
88 | assertEquals("Use JsonReader.setLenient(true) to accept malformed JSON at line 1 column 1 path $", expected.getMessage()); | |
89 | } | |
90 | } | |
91 | ||
92 | public void testBigDecimal() throws IOException { | |
93 | ToNumberStrategy strategy = ToNumberPolicy.BIG_DECIMAL; | |
94 | assertEquals(new BigDecimal("10.1"), strategy.readNumber(fromString("10.1"))); | |
95 | assertEquals(new BigDecimal("3.141592653589793238462643383279"), strategy.readNumber(fromString("3.141592653589793238462643383279"))); | |
96 | assertEquals(new BigDecimal("1e400"), strategy.readNumber(fromString("1e400"))); | |
97 | ||
98 | try { | |
99 | strategy.readNumber(fromString("\"not-a-number\"")); | |
100 | fail(); | |
101 | } catch (JsonParseException expected) { | |
102 | assertEquals("Cannot parse not-a-number; at path $", expected.getMessage()); | |
103 | } | |
104 | } | |
105 | ||
106 | public void testNullsAreNeverExpected() throws IOException { | |
107 | try { | |
108 | ToNumberPolicy.DOUBLE.readNumber(fromString("null")); | |
109 | fail(); | |
110 | } catch (IllegalStateException expected) { | |
111 | } | |
112 | try { | |
113 | ToNumberPolicy.LAZILY_PARSED_NUMBER.readNumber(fromString("null")); | |
114 | fail(); | |
115 | } catch (IllegalStateException expected) { | |
116 | } | |
117 | try { | |
118 | ToNumberPolicy.LONG_OR_DOUBLE.readNumber(fromString("null")); | |
119 | fail(); | |
120 | } catch (IllegalStateException expected) { | |
121 | } | |
122 | try { | |
123 | ToNumberPolicy.BIG_DECIMAL.readNumber(fromString("null")); | |
124 | fail(); | |
125 | } catch (IllegalStateException expected) { | |
126 | } | |
127 | } | |
128 | ||
129 | private static JsonReader fromString(String json) { | |
130 | return new JsonReader(new StringReader(json)); | |
131 | } | |
132 | ||
133 | private static JsonReader fromStringLenient(String json) { | |
134 | JsonReader jsonReader = fromString(json); | |
135 | jsonReader.setLenient(true); | |
136 | return jsonReader; | |
137 | } | |
138 | } |
36 | 36 | import com.google.gson.JsonPrimitive; |
37 | 37 | import com.google.gson.JsonSerializationContext; |
38 | 38 | import com.google.gson.JsonSerializer; |
39 | import com.google.gson.common.MoreAsserts; | |
40 | 39 | import com.google.gson.common.TestTypes.BagOfPrimitives; |
41 | 40 | import com.google.gson.reflect.TypeToken; |
42 | 41 |
38 | 38 | import java.net.InetAddress; |
39 | 39 | import java.net.URI; |
40 | 40 | import java.net.URL; |
41 | import java.sql.Time; | |
42 | import java.sql.Timestamp; | |
43 | 41 | import java.text.DateFormat; |
44 | 42 | import java.util.ArrayList; |
45 | 43 | import java.util.Arrays; |
68 | 66 | public class DefaultTypeAdaptersTest extends TestCase { |
69 | 67 | private Gson gson; |
70 | 68 | private TimeZone oldTimeZone; |
69 | private Locale oldLocale; | |
71 | 70 | |
72 | 71 | @Override |
73 | 72 | protected void setUp() throws Exception { |
74 | 73 | super.setUp(); |
75 | 74 | this.oldTimeZone = TimeZone.getDefault(); |
76 | 75 | TimeZone.setDefault(TimeZone.getTimeZone("America/Los_Angeles")); |
76 | this.oldLocale = Locale.getDefault(); | |
77 | 77 | Locale.setDefault(Locale.US); |
78 | 78 | gson = new Gson(); |
79 | 79 | } |
81 | 81 | @Override |
82 | 82 | protected void tearDown() throws Exception { |
83 | 83 | TimeZone.setDefault(oldTimeZone); |
84 | Locale.setDefault(oldLocale); | |
84 | 85 | super.tearDown(); |
85 | 86 | } |
86 | 87 | |
87 | 88 | public void testClassSerialization() { |
88 | 89 | try { |
89 | 90 | gson.toJson(String.class); |
90 | } catch (UnsupportedOperationException expected) {} | |
91 | fail(); | |
92 | } catch (UnsupportedOperationException expected) { | |
93 | } | |
91 | 94 | // Override with a custom type adapter for class. |
92 | 95 | gson = new GsonBuilder().registerTypeAdapter(Class.class, new MyClassTypeAdapter()).create(); |
93 | 96 | assertEquals("\"java.lang.String\"", gson.toJson(String.class)); |
96 | 99 | public void testClassDeserialization() { |
97 | 100 | try { |
98 | 101 | gson.fromJson("String.class", String.class.getClass()); |
99 | } catch (UnsupportedOperationException expected) {} | |
102 | fail(); | |
103 | } catch (UnsupportedOperationException expected) { | |
104 | } | |
100 | 105 | // Override with a custom type adapter for class. |
101 | 106 | gson = new GsonBuilder().registerTypeAdapter(Class.class, new MyClassTypeAdapter()).create(); |
102 | 107 | assertEquals(String.class, gson.fromJson("java.lang.String", Class.class)); |
145 | 150 | URI target = gson.fromJson(json, URI.class); |
146 | 151 | assertEquals(uriValue, target.toASCIIString()); |
147 | 152 | } |
148 | ||
153 | ||
149 | 154 | public void testNullSerialization() throws Exception { |
150 | 155 | testNullSerializationAndDeserialization(Boolean.class); |
151 | 156 | testNullSerializationAndDeserialization(Byte.class); |
174 | 179 | testNullSerializationAndDeserialization(Date.class); |
175 | 180 | testNullSerializationAndDeserialization(GregorianCalendar.class); |
176 | 181 | testNullSerializationAndDeserialization(Calendar.class); |
177 | testNullSerializationAndDeserialization(Time.class); | |
178 | testNullSerializationAndDeserialization(Timestamp.class); | |
179 | testNullSerializationAndDeserialization(java.sql.Date.class); | |
180 | testNullSerializationAndDeserialization(Enum.class); | |
181 | 182 | testNullSerializationAndDeserialization(Class.class); |
182 | 183 | } |
183 | 184 | |
184 | 185 | private void testNullSerializationAndDeserialization(Class<?> c) { |
186 | testNullSerializationAndDeserialization(gson, c); | |
187 | } | |
188 | ||
189 | public static void testNullSerializationAndDeserialization(Gson gson, Class<?> c) { | |
185 | 190 | assertEquals("null", gson.toJson(null, c)); |
186 | 191 | assertEquals(null, gson.fromJson("null", c)); |
187 | 192 | } |
268 | 273 | ClassWithBigInteger actual = gson.fromJson(json, ClassWithBigInteger.class); |
269 | 274 | assertEquals(expected.value, actual.value); |
270 | 275 | } |
271 | ||
276 | ||
272 | 277 | public void testOverrideBigIntegerTypeAdapter() throws Exception { |
273 | 278 | gson = new GsonBuilder() |
274 | 279 | .registerTypeAdapter(BigInteger.class, new NumberAsStringAdapter(BigInteger.class)) |
324 | 329 | |
325 | 330 | json = "[true,false,true,true,true,true,false,false,true,false,false]"; |
326 | 331 | assertEquals(expected, gson.fromJson(json, BitSet.class)); |
332 | ||
333 | try { | |
334 | gson.fromJson("[1, []]", BitSet.class); | |
335 | fail(); | |
336 | } catch (JsonSyntaxException e) { | |
337 | assertEquals("Invalid bitset value type: BEGIN_ARRAY; at path $[1]", e.getMessage()); | |
338 | } | |
339 | ||
340 | try { | |
341 | gson.fromJson("[1, 2]", BitSet.class); | |
342 | fail(); | |
343 | } catch (JsonSyntaxException e) { | |
344 | assertEquals("Invalid bitset value 2, expected 0 or 1; at path $[1]", e.getMessage()); | |
345 | } | |
327 | 346 | } |
328 | 347 | |
329 | 348 | public void testDefaultDateSerialization() { |
346 | 365 | // Date can not directly be compared with another instance since the deserialization loses the |
347 | 366 | // millisecond portion. |
348 | 367 | @SuppressWarnings("deprecation") |
349 | private void assertEqualsDate(Date date, int year, int month, int day) { | |
368 | public static void assertEqualsDate(Date date, int year, int month, int day) { | |
350 | 369 | assertEquals(year-1900, date.getYear()); |
351 | 370 | assertEquals(month, date.getMonth()); |
352 | 371 | assertEquals(day, date.getDate()); |
353 | 372 | } |
354 | 373 | |
355 | 374 | @SuppressWarnings("deprecation") |
356 | private void assertEqualsTime(Date date, int hours, int minutes, int seconds) { | |
375 | public static void assertEqualsTime(Date date, int hours, int minutes, int seconds) { | |
357 | 376 | assertEquals(hours, date.getHours()); |
358 | 377 | assertEquals(minutes, date.getMinutes()); |
359 | 378 | assertEquals(seconds, date.getSeconds()); |
360 | } | |
361 | ||
362 | public void testDefaultJavaSqlDateSerialization() { | |
363 | java.sql.Date instant = new java.sql.Date(1259875082000L); | |
364 | String json = gson.toJson(instant); | |
365 | assertEquals("\"Dec 3, 2009\"", json); | |
366 | } | |
367 | ||
368 | public void testDefaultJavaSqlDateDeserialization() { | |
369 | String json = "'Dec 3, 2009'"; | |
370 | java.sql.Date extracted = gson.fromJson(json, java.sql.Date.class); | |
371 | assertEqualsDate(extracted, 2009, 11, 3); | |
372 | } | |
373 | ||
374 | public void testDefaultJavaSqlTimestampSerialization() { | |
375 | Timestamp now = new java.sql.Timestamp(1259875082000L); | |
376 | String json = gson.toJson(now); | |
377 | if (JavaVersion.isJava9OrLater()) { | |
378 | assertEquals("\"Dec 3, 2009, 1:18:02 PM\"", json); | |
379 | } else { | |
380 | assertEquals("\"Dec 3, 2009 1:18:02 PM\"", json); | |
381 | } | |
382 | } | |
383 | ||
384 | public void testDefaultJavaSqlTimestampDeserialization() { | |
385 | String json = "'Dec 3, 2009 1:18:02 PM'"; | |
386 | Timestamp extracted = gson.fromJson(json, Timestamp.class); | |
387 | assertEqualsDate(extracted, 2009, 11, 3); | |
388 | assertEqualsTime(extracted, 13, 18, 2); | |
389 | } | |
390 | ||
391 | public void testDefaultJavaSqlTimeSerialization() { | |
392 | Time now = new Time(1259875082000L); | |
393 | String json = gson.toJson(now); | |
394 | assertEquals("\"01:18:02 PM\"", json); | |
395 | } | |
396 | ||
397 | public void testDefaultJavaSqlTimeDeserialization() { | |
398 | String json = "'1:18:02 PM'"; | |
399 | Time extracted = gson.fromJson(json, Time.class); | |
400 | assertEqualsTime(extracted, 13, 18, 2); | |
401 | 379 | } |
402 | 380 | |
403 | 381 | public void testDefaultDateSerializationUsingBuilder() throws Exception { |
523 | 501 | } |
524 | 502 | } |
525 | 503 | |
526 | // http://code.google.com/p/google-gson/issues/detail?id=230 | |
527 | public void testTimestampSerialization() throws Exception { | |
528 | TimeZone defaultTimeZone = TimeZone.getDefault(); | |
529 | TimeZone.setDefault(TimeZone.getTimeZone("UTC")); | |
530 | Locale defaultLocale = Locale.getDefault(); | |
531 | Locale.setDefault(Locale.US); | |
532 | try { | |
533 | Timestamp timestamp = new Timestamp(0L); | |
534 | Gson gson = new GsonBuilder().setDateFormat("yyyy-MM-dd").create(); | |
535 | String json = gson.toJson(timestamp, Timestamp.class); | |
536 | assertEquals("\"1970-01-01\"", json); | |
537 | assertEquals(0, gson.fromJson("\"1970-01-01\"", Timestamp.class).getTime()); | |
538 | } finally { | |
539 | TimeZone.setDefault(defaultTimeZone); | |
540 | Locale.setDefault(defaultLocale); | |
541 | } | |
542 | } | |
543 | ||
544 | // http://code.google.com/p/google-gson/issues/detail?id=230 | |
545 | public void testSqlDateSerialization() throws Exception { | |
546 | TimeZone defaultTimeZone = TimeZone.getDefault(); | |
547 | TimeZone.setDefault(TimeZone.getTimeZone("UTC")); | |
548 | Locale defaultLocale = Locale.getDefault(); | |
549 | Locale.setDefault(Locale.US); | |
550 | try { | |
551 | java.sql.Date sqlDate = new java.sql.Date(0L); | |
552 | Gson gson = new GsonBuilder().setDateFormat("yyyy-MM-dd").create(); | |
553 | String json = gson.toJson(sqlDate, Timestamp.class); | |
554 | assertEquals("\"1970-01-01\"", json); | |
555 | assertEquals(0, gson.fromJson("\"1970-01-01\"", java.sql.Date.class).getTime()); | |
556 | } finally { | |
557 | TimeZone.setDefault(defaultTimeZone); | |
558 | Locale.setDefault(defaultLocale); | |
559 | } | |
560 | } | |
561 | ||
562 | 504 | public void testJsonPrimitiveSerialization() { |
563 | 505 | assertEquals("5", gson.toJson(new JsonPrimitive(5), JsonElement.class)); |
564 | 506 | assertEquals("true", gson.toJson(new JsonPrimitive(true), JsonElement.class)); |
636 | 578 | gson.fromJson("\"abc\"", JsonObject.class); |
637 | 579 | fail(); |
638 | 580 | } catch (JsonSyntaxException expected) { |
639 | assertEquals("Expected a com.google.gson.JsonObject but was com.google.gson.JsonPrimitive", | |
581 | assertEquals("Expected a com.google.gson.JsonObject but was com.google.gson.JsonPrimitive; at path $", | |
640 | 582 | expected.getMessage()); |
641 | 583 | } |
642 | 584 | } |
14 | 14 | */ |
15 | 15 | |
16 | 16 | package com.google.gson.functional; |
17 | ||
18 | import java.lang.reflect.Type; | |
19 | import java.util.ArrayList; | |
20 | import java.util.Collection; | |
21 | import java.util.EnumSet; | |
22 | import java.util.Set; | |
23 | 17 | |
24 | 18 | import com.google.gson.Gson; |
25 | 19 | import com.google.gson.GsonBuilder; |
33 | 27 | import com.google.gson.annotations.SerializedName; |
34 | 28 | import com.google.gson.common.MoreAsserts; |
35 | 29 | import com.google.gson.reflect.TypeToken; |
36 | ||
30 | import java.lang.reflect.Type; | |
31 | import java.util.ArrayList; | |
32 | import java.util.Collection; | |
33 | import java.util.Collections; | |
34 | import java.util.EnumMap; | |
35 | import java.util.EnumSet; | |
36 | import java.util.Map; | |
37 | import java.util.Set; | |
37 | 38 | import junit.framework.TestCase; |
38 | 39 | /** |
39 | 40 | * Functional tests for Java 5.0 enums. |
151 | 152 | public void testEnumSet() { |
152 | 153 | EnumSet<Roshambo> foo = EnumSet.of(Roshambo.ROCK, Roshambo.PAPER); |
153 | 154 | String json = gson.toJson(foo); |
155 | assertEquals("[\"ROCK\",\"PAPER\"]", json); | |
156 | ||
154 | 157 | Type type = new TypeToken<EnumSet<Roshambo>>() {}.getType(); |
155 | 158 | EnumSet<Roshambo> bar = gson.fromJson(json, type); |
156 | 159 | assertTrue(bar.contains(Roshambo.ROCK)); |
157 | 160 | assertTrue(bar.contains(Roshambo.PAPER)); |
158 | 161 | assertFalse(bar.contains(Roshambo.SCISSORS)); |
162 | } | |
163 | ||
164 | public void testEnumMap() throws Exception { | |
165 | EnumMap<MyEnum, String> map = new EnumMap<MyEnum, String>(MyEnum.class); | |
166 | map.put(MyEnum.VALUE1, "test"); | |
167 | String json = gson.toJson(map); | |
168 | assertEquals("{\"VALUE1\":\"test\"}", json); | |
169 | ||
170 | Type type = new TypeToken<EnumMap<MyEnum, String>>() {}.getType(); | |
171 | EnumMap<?, ?> actualMap = gson.fromJson("{\"VALUE1\":\"test\"}", type); | |
172 | Map<?, ?> expectedMap = Collections.singletonMap(MyEnum.VALUE1, "test"); | |
173 | assertEquals(expectedMap, actualMap); | |
159 | 174 | } |
160 | 175 | |
161 | 176 | public enum Roshambo { |
199 | 214 | } |
200 | 215 | |
201 | 216 | public void testEnumClassWithFields() { |
202 | assertEquals("\"RED\"", gson.toJson(Color.RED)); | |
203 | assertEquals("red", gson.fromJson("RED", Color.class).value); | |
217 | assertEquals("\"RED\"", gson.toJson(Color.RED)); | |
218 | assertEquals("red", gson.fromJson("RED", Color.class).value); | |
204 | 219 | } |
205 | 220 | |
206 | 221 | public enum Color { |
207 | RED("red", 1), BLUE("blue", 2), GREEN("green", 3); | |
208 | String value; | |
209 | int index; | |
210 | private Color(String value, int index) { | |
211 | this.value = value; | |
212 | this.index = index; | |
213 | } | |
222 | RED("red", 1), BLUE("blue", 2), GREEN("green", 3); | |
223 | String value; | |
224 | int index; | |
225 | private Color(String value, int index) { | |
226 | this.value = value; | |
227 | this.index = index; | |
228 | } | |
214 | 229 | } |
215 | 230 | } |
20 | 20 | import static com.google.gson.FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES; |
21 | 21 | import static com.google.gson.FieldNamingPolicy.UPPER_CAMEL_CASE; |
22 | 22 | import static com.google.gson.FieldNamingPolicy.UPPER_CAMEL_CASE_WITH_SPACES; |
23 | import static com.google.gson.FieldNamingPolicy.UPPER_CASE_WITH_UNDERSCORES; | |
23 | 24 | |
24 | 25 | import com.google.gson.FieldNamingPolicy; |
25 | 26 | import com.google.gson.Gson; |
49 | 50 | assertEquals("{'Lower Camel':1,'Upper Camel':2,'_Lower Camel Leading Underscore':3," + |
50 | 51 | "'_ Upper Camel Leading Underscore':4,'Lower_words':5,'U P P E R_ W O R D S':6," + |
51 | 52 | "'annotatedName':7,'Lower Id':8,'_9':9}", |
53 | gson.toJson(new TestNames()).replace('\"', '\'')); | |
54 | } | |
55 | ||
56 | public void testUpperCaseWithUnderscores() { | |
57 | Gson gson = getGsonWithNamingPolicy(UPPER_CASE_WITH_UNDERSCORES); | |
58 | assertEquals("{'LOWER_CAMEL':1,'UPPER_CAMEL':2,'_LOWER_CAMEL_LEADING_UNDERSCORE':3," + | |
59 | "'__UPPER_CAMEL_LEADING_UNDERSCORE':4,'LOWER_WORDS':5,'U_P_P_E_R__W_O_R_D_S':6," + | |
60 | "'annotatedName':7,'LOWER_ID':8,'_9':9}", | |
52 | 61 | gson.toJson(new TestNames()).replace('\"', '\'')); |
53 | 62 | } |
54 | 63 |
140 | 140 | assertEquals("someValue", deserializedObject.someConstantStringInstanceField); |
141 | 141 | } |
142 | 142 | |
143 | public void testGsonWithUpperCaseUnderscorePolicySerialization() { | |
144 | Gson gson = builder.setFieldNamingPolicy(FieldNamingPolicy.UPPER_CASE_WITH_UNDERSCORES) | |
145 | .create(); | |
146 | StringWrapper target = new StringWrapper("blah"); | |
147 | assertEquals("{\"SOME_CONSTANT_STRING_INSTANCE_FIELD\":\"" | |
148 | + target.someConstantStringInstanceField + "\"}", gson.toJson(target)); | |
149 | } | |
150 | ||
151 | public void testGsonWithUpperCaseUnderscorePolicyDeserialiation() { | |
152 | Gson gson = builder.setFieldNamingPolicy(FieldNamingPolicy.UPPER_CASE_WITH_UNDERSCORES) | |
153 | .create(); | |
154 | String target = "{\"SOME_CONSTANT_STRING_INSTANCE_FIELD\":\"someValue\"}"; | |
155 | StringWrapper deserializedObject = gson.fromJson(target, StringWrapper.class); | |
156 | assertEquals("someValue", deserializedObject.someConstantStringInstanceField); | |
157 | } | |
158 | ||
143 | 159 | public void testDeprecatedNamingStrategy() throws Exception { |
144 | 160 | Gson gson = builder.setFieldNamingStrategy(new UpperCaseNamingStrategy()).create(); |
145 | 161 | ClassWithDuplicateFields target = new ClassWithDuplicateFields(10); |
54 | 54 | */ |
55 | 55 | public class ObjectTest extends TestCase { |
56 | 56 | private Gson gson; |
57 | private TimeZone oldTimeZone = TimeZone.getDefault(); | |
57 | private TimeZone oldTimeZone; | |
58 | private Locale oldLocale; | |
58 | 59 | |
59 | 60 | @Override |
60 | 61 | protected void setUp() throws Exception { |
61 | 62 | super.setUp(); |
62 | 63 | gson = new Gson(); |
63 | 64 | |
65 | oldTimeZone = TimeZone.getDefault(); | |
64 | 66 | TimeZone.setDefault(TimeZone.getTimeZone("America/Los_Angeles")); |
67 | oldLocale = Locale.getDefault(); | |
65 | 68 | Locale.setDefault(Locale.US); |
66 | 69 | } |
67 | 70 | |
68 | 71 | @Override |
69 | 72 | protected void tearDown() throws Exception { |
70 | 73 | TimeZone.setDefault(oldTimeZone); |
74 | Locale.setDefault(oldLocale); | |
71 | 75 | super.tearDown(); |
72 | 76 | } |
77 | ||
73 | 78 | public void testJsonInSingleQuotesDeserialization() { |
74 | 79 | String json = "{'stringValue':'no message','intValue':10,'longValue':20}"; |
75 | 80 | BagOfPrimitives target = gson.fromJson(json, BagOfPrimitives.class); |
15 | 15 | |
16 | 16 | package com.google.gson.functional; |
17 | 17 | |
18 | import static org.junit.Assert.assertArrayEquals; | |
19 | ||
18 | 20 | import com.google.gson.Gson; |
19 | 21 | import com.google.gson.GsonBuilder; |
20 | 22 | import com.google.gson.JsonPrimitive; |
21 | 23 | import com.google.gson.JsonSyntaxException; |
22 | 24 | import com.google.gson.LongSerializationPolicy; |
25 | import com.google.gson.internal.LazilyParsedNumber; | |
23 | 26 | import com.google.gson.reflect.TypeToken; |
24 | 27 | import java.io.Serializable; |
25 | 28 | import java.io.StringReader; |
62 | 65 | assertEquals("1", gson.toJson(1, Byte.class)); |
63 | 66 | } |
64 | 67 | |
68 | public void testByteDeserialization() { | |
69 | Byte boxed = gson.fromJson("1", Byte.class); | |
70 | assertEquals(1, (byte)boxed); | |
71 | byte primitive = gson.fromJson("1", byte.class); | |
72 | assertEquals(1, primitive); | |
73 | ||
74 | byte[] bytes = gson.fromJson("[-128, 0, 127, 255]", byte[].class); | |
75 | assertArrayEquals(new byte[] {-128, 0, 127, -1}, bytes); | |
76 | } | |
77 | ||
78 | public void testByteDeserializationLossy() { | |
79 | try { | |
80 | gson.fromJson("-129", byte.class); | |
81 | fail(); | |
82 | } catch (JsonSyntaxException e) { | |
83 | assertEquals("Lossy conversion from -129 to byte; at path $", e.getMessage()); | |
84 | } | |
85 | ||
86 | try { | |
87 | gson.fromJson("256", byte.class); | |
88 | fail(); | |
89 | } catch (JsonSyntaxException e) { | |
90 | assertEquals("Lossy conversion from 256 to byte; at path $", e.getMessage()); | |
91 | } | |
92 | ||
93 | try { | |
94 | gson.fromJson("2147483648", byte.class); | |
95 | fail(); | |
96 | } catch (JsonSyntaxException e) { | |
97 | assertEquals("java.lang.NumberFormatException: Expected an int but was 2147483648 at line 1 column 11 path $", e.getMessage()); | |
98 | } | |
99 | } | |
100 | ||
65 | 101 | public void testShortSerialization() { |
66 | 102 | assertEquals("1", gson.toJson(1, short.class)); |
67 | 103 | assertEquals("1", gson.toJson(1, Short.class)); |
68 | 104 | } |
69 | 105 | |
70 | public void testByteDeserialization() { | |
71 | Byte target = gson.fromJson("1", Byte.class); | |
72 | assertEquals(1, (byte)target); | |
73 | byte primitive = gson.fromJson("1", byte.class); | |
106 | public void testShortDeserialization() { | |
107 | Short boxed = gson.fromJson("1", Short.class); | |
108 | assertEquals(1, (short)boxed); | |
109 | short primitive = gson.fromJson("1", short.class); | |
74 | 110 | assertEquals(1, primitive); |
111 | ||
112 | short[] shorts = gson.fromJson("[-32768, 0, 32767, 65535]", short[].class); | |
113 | assertArrayEquals(new short[] {-32768, 0, 32767, -1}, shorts); | |
114 | } | |
115 | ||
116 | public void testShortDeserializationLossy() { | |
117 | try { | |
118 | gson.fromJson("-32769", short.class); | |
119 | fail(); | |
120 | } catch (JsonSyntaxException e) { | |
121 | assertEquals("Lossy conversion from -32769 to short; at path $", e.getMessage()); | |
122 | } | |
123 | ||
124 | try { | |
125 | gson.fromJson("65536", short.class); | |
126 | fail(); | |
127 | } catch (JsonSyntaxException e) { | |
128 | assertEquals("Lossy conversion from 65536 to short; at path $", e.getMessage()); | |
129 | } | |
130 | ||
131 | try { | |
132 | gson.fromJson("2147483648", short.class); | |
133 | fail(); | |
134 | } catch (JsonSyntaxException e) { | |
135 | assertEquals("java.lang.NumberFormatException: Expected an int but was 2147483648 at line 1 column 11 path $", e.getMessage()); | |
136 | } | |
75 | 137 | } |
76 | 138 | |
77 | 139 | public void testPrimitiveIntegerAutoboxedInASingleElementArraySerialization() { |
331 | 393 | } catch (JsonSyntaxException expected) { } |
332 | 394 | } |
333 | 395 | |
396 | public void testLazilyParsedNumberSerialization() { | |
397 | LazilyParsedNumber target = new LazilyParsedNumber("1.5"); | |
398 | String actual = gson.toJson(target); | |
399 | assertEquals("1.5", actual); | |
400 | } | |
401 | ||
402 | public void testLazilyParsedNumberDeserialization() { | |
403 | LazilyParsedNumber expected = new LazilyParsedNumber("1.5"); | |
404 | LazilyParsedNumber actual = gson.fromJson("1.5", LazilyParsedNumber.class); | |
405 | assertEquals(expected, actual); | |
406 | } | |
407 | ||
334 | 408 | public void testMoreSpecificSerialization() { |
335 | 409 | Gson gson = new Gson(); |
336 | 410 | String expected = "This is a string"; |
0 | package com.google.gson.functional; | |
1 | ||
2 | import static org.junit.Assert.assertEquals; | |
3 | import static org.junit.Assert.assertNull; | |
4 | import static org.junit.Assert.assertTrue; | |
5 | import static org.junit.Assert.fail; | |
6 | ||
7 | import com.google.gson.Gson; | |
8 | import com.google.gson.GsonBuilder; | |
9 | import com.google.gson.JsonIOException; | |
10 | import com.google.gson.TypeAdapter; | |
11 | import com.google.gson.stream.JsonReader; | |
12 | import com.google.gson.stream.JsonWriter; | |
13 | import java.io.IOException; | |
14 | import java.lang.reflect.ReflectPermission; | |
15 | import java.net.URL; | |
16 | import java.net.URLClassLoader; | |
17 | import java.security.Permission; | |
18 | import java.util.Collections; | |
19 | import java.util.concurrent.atomic.AtomicBoolean; | |
20 | import org.junit.Test; | |
21 | ||
22 | public class ReflectionAccessTest { | |
23 | @SuppressWarnings("unused") | |
24 | private static class ClassWithPrivateMembers { | |
25 | private String s; | |
26 | ||
27 | private ClassWithPrivateMembers() { | |
28 | } | |
29 | } | |
30 | ||
31 | private static Class<?> loadClassWithDifferentClassLoader(Class<?> c) throws Exception { | |
32 | URL url = c.getProtectionDomain().getCodeSource().getLocation(); | |
33 | URLClassLoader classLoader = new URLClassLoader(new URL[] { url }, null); | |
34 | return classLoader.loadClass(c.getName()); | |
35 | } | |
36 | ||
37 | @Test | |
38 | public void testRestrictiveSecurityManager() throws Exception { | |
39 | // Must use separate class loader, otherwise permission is not checked, see Class.getDeclaredFields() | |
40 | Class<?> clazz = loadClassWithDifferentClassLoader(ClassWithPrivateMembers.class); | |
41 | ||
42 | final Permission accessDeclaredMembers = new RuntimePermission("accessDeclaredMembers"); | |
43 | final Permission suppressAccessChecks = new ReflectPermission("suppressAccessChecks"); | |
44 | SecurityManager original = System.getSecurityManager(); | |
45 | SecurityManager restrictiveManager = new SecurityManager() { | |
46 | @Override | |
47 | public void checkPermission(Permission perm) { | |
48 | if (accessDeclaredMembers.equals(perm)) { | |
49 | throw new SecurityException("Gson: no-member-access"); | |
50 | } | |
51 | if (suppressAccessChecks.equals(perm)) { | |
52 | throw new SecurityException("Gson: no-suppress-access-check"); | |
53 | } | |
54 | } | |
55 | }; | |
56 | System.setSecurityManager(restrictiveManager); | |
57 | ||
58 | try { | |
59 | Gson gson = new Gson(); | |
60 | try { | |
61 | // Getting reflection based adapter should fail | |
62 | gson.getAdapter(clazz); | |
63 | fail(); | |
64 | } catch (SecurityException e) { | |
65 | assertEquals("Gson: no-member-access", e.getMessage()); | |
66 | } | |
67 | ||
68 | final AtomicBoolean wasReadCalled = new AtomicBoolean(false); | |
69 | gson = new GsonBuilder() | |
70 | .registerTypeAdapter(clazz, new TypeAdapter<Object>() { | |
71 | @Override | |
72 | public void write(JsonWriter out, Object value) throws IOException { | |
73 | out.value("custom-write"); | |
74 | } | |
75 | ||
76 | @Override | |
77 | public Object read(JsonReader in) throws IOException { | |
78 | in.skipValue(); | |
79 | wasReadCalled.set(true); | |
80 | return null; | |
81 | }} | |
82 | ) | |
83 | .create(); | |
84 | ||
85 | assertEquals("\"custom-write\"", gson.toJson(null, clazz)); | |
86 | assertNull(gson.fromJson("{}", clazz)); | |
87 | assertTrue(wasReadCalled.get()); | |
88 | } finally { | |
89 | System.setSecurityManager(original); | |
90 | } | |
91 | } | |
92 | ||
93 | /** | |
94 | * Test serializing an instance of a non-accessible internal class, but where | |
95 | * Gson supports serializing one of its superinterfaces. | |
96 | * | |
97 | * <p>Here {@link Collections#emptyList()} is used which returns an instance | |
98 | * of the internal class {@code java.util.Collections.EmptyList}. Gson should | |
99 | * serialize the object as {@code List} despite the internal class not being | |
100 | * accessible. | |
101 | * | |
102 | * <p>See https://github.com/google/gson/issues/1875 | |
103 | */ | |
104 | @Test | |
105 | public void testSerializeInternalImplementationObject() { | |
106 | Gson gson = new Gson(); | |
107 | String json = gson.toJson(Collections.emptyList()); | |
108 | assertEquals("[]", json); | |
109 | ||
110 | // But deserialization should fail | |
111 | Class<?> internalClass = Collections.emptyList().getClass(); | |
112 | try { | |
113 | gson.fromJson("{}", internalClass); | |
114 | fail("Missing exception; test has to be run with `--illegal-access=deny`"); | |
115 | } catch (JsonIOException expected) { | |
116 | assertTrue(expected.getMessage().startsWith( | |
117 | "Failed making constructor 'java.util.Collections$EmptyList#EmptyList()' accessible; " | |
118 | + "either change its visibility or write a custom InstanceCreator or TypeAdapter for its declaring type" | |
119 | )); | |
120 | } | |
121 | } | |
122 | } |
0 | // Copyright (C) 2014 Trymph Inc. | |
1 | package com.google.gson.functional; | |
2 | ||
3 | import java.io.IOException; | |
4 | ||
5 | import junit.framework.TestCase; | |
6 | ||
7 | import com.google.gson.Gson; | |
8 | import com.google.gson.annotations.SerializedName; | |
9 | ||
10 | @SuppressWarnings("serial") | |
11 | public final class ThrowableFunctionalTest extends TestCase { | |
12 | private final Gson gson = new Gson(); | |
13 | ||
14 | public void testExceptionWithoutCause() { | |
15 | RuntimeException e = new RuntimeException("hello"); | |
16 | String json = gson.toJson(e); | |
17 | assertTrue(json.contains("hello")); | |
18 | ||
19 | e = gson.fromJson("{'detailMessage':'hello'}", RuntimeException.class); | |
20 | assertEquals("hello", e.getMessage()); | |
21 | } | |
22 | ||
23 | public void testExceptionWithCause() { | |
24 | Exception e = new Exception("top level", new IOException("io error")); | |
25 | String json = gson.toJson(e); | |
26 | assertTrue(json.contains("{\"detailMessage\":\"top level\",\"cause\":{\"detailMessage\":\"io error\"")); | |
27 | ||
28 | e = gson.fromJson("{'detailMessage':'top level','cause':{'detailMessage':'io error'}}", Exception.class); | |
29 | assertEquals("top level", e.getMessage()); | |
30 | assertTrue(e.getCause() instanceof Throwable); // cause is not parameterized so type info is lost | |
31 | assertEquals("io error", e.getCause().getMessage()); | |
32 | } | |
33 | ||
34 | public void testSerializedNameOnExceptionFields() { | |
35 | MyException e = new MyException(); | |
36 | String json = gson.toJson(e); | |
37 | assertTrue(json.contains("{\"my_custom_name\":\"myCustomMessageValue\"")); | |
38 | } | |
39 | ||
40 | public void testErrorWithoutCause() { | |
41 | OutOfMemoryError e = new OutOfMemoryError("hello"); | |
42 | String json = gson.toJson(e); | |
43 | assertTrue(json.contains("hello")); | |
44 | ||
45 | e = gson.fromJson("{'detailMessage':'hello'}", OutOfMemoryError.class); | |
46 | assertEquals("hello", e.getMessage()); | |
47 | } | |
48 | ||
49 | public void testErrornWithCause() { | |
50 | Error e = new Error("top level", new IOException("io error")); | |
51 | String json = gson.toJson(e); | |
52 | assertTrue(json.contains("top level")); | |
53 | assertTrue(json.contains("io error")); | |
54 | ||
55 | e = gson.fromJson("{'detailMessage':'top level','cause':{'detailMessage':'io error'}}", Error.class); | |
56 | assertEquals("top level", e.getMessage()); | |
57 | assertTrue(e.getCause() instanceof Throwable); // cause is not parameterized so type info is lost | |
58 | assertEquals("io error", e.getCause().getMessage()); | |
59 | } | |
60 | ||
61 | private static final class MyException extends Throwable { | |
62 | @SerializedName("my_custom_name") String myCustomMessage = "myCustomMessageValue"; | |
63 | } | |
64 | } |
0 | /* | |
1 | * Copyright (C) 2021 Google Inc. | |
2 | * | |
3 | * Licensed under the Apache License, Version 2.0 (the "License"); | |
4 | * you may not use this file except in compliance with the License. | |
5 | * You may obtain a copy of the License at | |
6 | * | |
7 | * http://www.apache.org/licenses/LICENSE-2.0 | |
8 | * | |
9 | * Unless required by applicable law or agreed to in writing, software | |
10 | * distributed under the License is distributed on an "AS IS" BASIS, | |
11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
12 | * See the License for the specific language governing permissions and | |
13 | * limitations under the License. | |
14 | */ | |
15 | ||
16 | package com.google.gson.functional; | |
17 | ||
18 | import java.lang.reflect.Type; | |
19 | import java.math.BigDecimal; | |
20 | import java.util.Arrays; | |
21 | import java.util.Collection; | |
22 | import java.util.LinkedList; | |
23 | import java.util.List; | |
24 | import com.google.gson.Gson; | |
25 | import com.google.gson.GsonBuilder; | |
26 | import com.google.gson.ToNumberPolicy; | |
27 | import com.google.gson.ToNumberStrategy; | |
28 | import com.google.gson.internal.LazilyParsedNumber; | |
29 | import com.google.gson.reflect.TypeToken; | |
30 | import com.google.gson.stream.JsonReader; | |
31 | import junit.framework.TestCase; | |
32 | ||
33 | public class ToNumberPolicyFunctionalTest extends TestCase { | |
34 | public void testDefault() { | |
35 | Gson gson = new Gson(); | |
36 | assertEquals(null, gson.fromJson("null", Object.class)); | |
37 | assertEquals(10D, gson.fromJson("10", Object.class)); | |
38 | assertEquals(null, gson.fromJson("null", Number.class)); | |
39 | assertEquals(new LazilyParsedNumber("10"), gson.fromJson("10", Number.class)); | |
40 | } | |
41 | ||
42 | public void testAsDoubles() { | |
43 | Gson gson = new GsonBuilder() | |
44 | .setObjectToNumberStrategy(ToNumberPolicy.DOUBLE) | |
45 | .setNumberToNumberStrategy(ToNumberPolicy.DOUBLE) | |
46 | .create(); | |
47 | assertEquals(null, gson.fromJson("null", Object.class)); | |
48 | assertEquals(10.0, gson.fromJson("10", Object.class)); | |
49 | assertEquals(null, gson.fromJson("null", Number.class)); | |
50 | assertEquals(10.0, gson.fromJson("10", Number.class)); | |
51 | } | |
52 | ||
53 | public void testAsLazilyParsedNumbers() { | |
54 | Gson gson = new GsonBuilder() | |
55 | .setObjectToNumberStrategy(ToNumberPolicy.LAZILY_PARSED_NUMBER) | |
56 | .setNumberToNumberStrategy(ToNumberPolicy.LAZILY_PARSED_NUMBER) | |
57 | .create(); | |
58 | assertEquals(null, gson.fromJson("null", Object.class)); | |
59 | assertEquals(new LazilyParsedNumber("10"), gson.fromJson("10", Object.class)); | |
60 | assertEquals(null, gson.fromJson("null", Number.class)); | |
61 | assertEquals(new LazilyParsedNumber("10"), gson.fromJson("10", Number.class)); | |
62 | } | |
63 | ||
64 | public void testAsLongsOrDoubles() { | |
65 | Gson gson = new GsonBuilder() | |
66 | .setObjectToNumberStrategy(ToNumberPolicy.LONG_OR_DOUBLE) | |
67 | .setNumberToNumberStrategy(ToNumberPolicy.LONG_OR_DOUBLE) | |
68 | .create(); | |
69 | assertEquals(null, gson.fromJson("null", Object.class)); | |
70 | assertEquals(10L, gson.fromJson("10", Object.class)); | |
71 | assertEquals(10.0, gson.fromJson("10.0", Object.class)); | |
72 | assertEquals(null, gson.fromJson("null", Number.class)); | |
73 | assertEquals(10L, gson.fromJson("10", Number.class)); | |
74 | assertEquals(10.0, gson.fromJson("10.0", Number.class)); | |
75 | } | |
76 | ||
77 | public void testAsBigDecimals() { | |
78 | Gson gson = new GsonBuilder() | |
79 | .setObjectToNumberStrategy(ToNumberPolicy.BIG_DECIMAL) | |
80 | .setNumberToNumberStrategy(ToNumberPolicy.BIG_DECIMAL) | |
81 | .create(); | |
82 | assertEquals(null, gson.fromJson("null", Object.class)); | |
83 | assertEquals(new BigDecimal("10"), gson.fromJson("10", Object.class)); | |
84 | assertEquals(new BigDecimal("10.0"), gson.fromJson("10.0", Object.class)); | |
85 | assertEquals(null, gson.fromJson("null", Number.class)); | |
86 | assertEquals(new BigDecimal("10"), gson.fromJson("10", Number.class)); | |
87 | assertEquals(new BigDecimal("10.0"), gson.fromJson("10.0", Number.class)); | |
88 | assertEquals(new BigDecimal("3.141592653589793238462643383279"), gson.fromJson("3.141592653589793238462643383279", BigDecimal.class)); | |
89 | assertEquals(new BigDecimal("1e400"), gson.fromJson("1e400", BigDecimal.class)); | |
90 | } | |
91 | ||
92 | public void testAsListOfLongsOrDoubles() { | |
93 | Gson gson = new GsonBuilder() | |
94 | .setObjectToNumberStrategy(ToNumberPolicy.LONG_OR_DOUBLE) | |
95 | .setNumberToNumberStrategy(ToNumberPolicy.LONG_OR_DOUBLE) | |
96 | .create(); | |
97 | List<Object> expected = new LinkedList<Object>(); | |
98 | expected.add(null); | |
99 | expected.add(10L); | |
100 | expected.add(10.0); | |
101 | Type objectCollectionType = new TypeToken<Collection<Object>>() { }.getType(); | |
102 | Collection<Object> objects = gson.fromJson("[null,10,10.0]", objectCollectionType); | |
103 | assertEquals(expected, objects); | |
104 | Type numberCollectionType = new TypeToken<Collection<Number>>() { }.getType(); | |
105 | Collection<Object> numbers = gson.fromJson("[null,10,10.0]", numberCollectionType); | |
106 | assertEquals(expected, numbers); | |
107 | } | |
108 | ||
109 | public void testCustomStrategiesCannotAffectConcreteDeclaredNumbers() { | |
110 | ToNumberStrategy fail = new ToNumberStrategy() { | |
111 | @Override | |
112 | public Byte readNumber(JsonReader in) { | |
113 | throw new UnsupportedOperationException(); | |
114 | } | |
115 | }; | |
116 | Gson gson = new GsonBuilder() | |
117 | .setObjectToNumberStrategy(fail) | |
118 | .setNumberToNumberStrategy(fail) | |
119 | .create(); | |
120 | List<Object> numbers = gson.fromJson("[null, 10, 20, 30]", new TypeToken<List<Byte>>() {}.getType()); | |
121 | assertEquals(Arrays.asList(null, (byte) 10, (byte) 20, (byte) 30), numbers); | |
122 | try { | |
123 | gson.fromJson("[null, 10, 20, 30]", new TypeToken<List<Object>>() {}.getType()); | |
124 | fail(); | |
125 | } catch (UnsupportedOperationException ex) { | |
126 | } | |
127 | try { | |
128 | gson.fromJson("[null, 10, 20, 30]", new TypeToken<List<Number>>() {}.getType()); | |
129 | fail(); | |
130 | } catch (UnsupportedOperationException ex) { | |
131 | } | |
132 | } | |
133 | } |
56 | 56 | .registerTypeAdapter(Id.class, new IdTreeTypeAdapter()) |
57 | 57 | .create(); |
58 | 58 | course = new Course<HistoryCourse>(COURSE_ID, 4, |
59 | new Assignment<HistoryCourse>(null, null), createList(STUDENT1, STUDENT2)); | |
59 | new Assignment<HistoryCourse>(null, null), Arrays.asList(STUDENT1, STUDENT2)); | |
60 | 60 | } |
61 | 61 | |
62 | 62 | public void testSerializeId() { |
170 | 170 | private static class HistoryCourse { |
171 | 171 | int numClasses; |
172 | 172 | } |
173 | ||
174 | private static <T> List<T> createList(T ...items) { | |
175 | return Arrays.asList(items); | |
176 | } | |
177 | 173 | } |
0 | package com.google.gson.internal; | |
1 | ||
2 | import static org.junit.Assert.assertEquals; | |
3 | import static org.junit.Assert.fail; | |
4 | ||
5 | import java.lang.reflect.Type; | |
6 | import java.util.Collections; | |
7 | import java.util.Map; | |
8 | ||
9 | import org.junit.Test; | |
10 | ||
11 | import com.google.gson.InstanceCreator; | |
12 | import com.google.gson.reflect.TypeToken; | |
13 | ||
14 | public class ConstructorConstructorTest { | |
15 | private static final Map<Type, InstanceCreator<?>> NO_INSTANCE_CREATORS = Collections.emptyMap(); | |
16 | ||
17 | private abstract static class AbstractClass { | |
18 | @SuppressWarnings("unused") | |
19 | public AbstractClass() { } | |
20 | } | |
21 | private interface Interface { } | |
22 | ||
23 | /** | |
24 | * Verify that ConstructorConstructor does not try to invoke no-arg constructor | |
25 | * of abstract class. | |
26 | */ | |
27 | @Test | |
28 | public void testGet_AbstractClassNoArgConstructor() { | |
29 | ConstructorConstructor constructorFactory = new ConstructorConstructor(NO_INSTANCE_CREATORS, true); | |
30 | ObjectConstructor<AbstractClass> constructor = constructorFactory.get(TypeToken.get(AbstractClass.class)); | |
31 | try { | |
32 | constructor.construct(); | |
33 | fail("Expected exception"); | |
34 | } catch (RuntimeException exception) { | |
35 | assertEquals( | |
36 | "Unable to create instance of class com.google.gson.internal.ConstructorConstructorTest$AbstractClass. " | |
37 | + "Registering an InstanceCreator or a TypeAdapter for this type, or adding a no-args constructor may fix this problem.", | |
38 | exception.getMessage() | |
39 | ); | |
40 | } | |
41 | } | |
42 | ||
43 | @Test | |
44 | public void testGet_Interface() { | |
45 | ConstructorConstructor constructorFactory = new ConstructorConstructor(NO_INSTANCE_CREATORS, true); | |
46 | ObjectConstructor<Interface> constructor = constructorFactory.get(TypeToken.get(Interface.class)); | |
47 | try { | |
48 | constructor.construct(); | |
49 | fail("Expected exception"); | |
50 | } catch (RuntimeException exception) { | |
51 | assertEquals( | |
52 | "Unable to create instance of interface com.google.gson.internal.ConstructorConstructorTest$Interface. " | |
53 | + "Registering an InstanceCreator or a TypeAdapter for this type, or adding a no-args constructor may fix this problem.", | |
54 | exception.getMessage() | |
55 | ); | |
56 | } | |
57 | } | |
58 | } |
17 | 17 | import static org.junit.Assert.*; |
18 | 18 | |
19 | 19 | import org.junit.Test; |
20 | ||
21 | import com.google.gson.internal.JavaVersion; | |
22 | 20 | |
23 | 21 | /** |
24 | 22 | * Unit and functional tests for {@link JavaVersion} |
14 | 14 | */ |
15 | 15 | package com.google.gson.internal; |
16 | 16 | |
17 | import java.io.ByteArrayInputStream; | |
18 | import java.io.ByteArrayOutputStream; | |
19 | import java.io.IOException; | |
20 | import java.io.ObjectInputStream; | |
21 | import java.io.ObjectOutputStream; | |
22 | import java.math.BigDecimal; | |
23 | ||
17 | 24 | import junit.framework.TestCase; |
18 | 25 | |
19 | 26 | public class LazilyParsedNumberTest extends TestCase { |
28 | 35 | LazilyParsedNumber n1Another = new LazilyParsedNumber("1"); |
29 | 36 | assertTrue(n1.equals(n1Another)); |
30 | 37 | } |
38 | ||
39 | public void testJavaSerialization() throws IOException, ClassNotFoundException { | |
40 | ByteArrayOutputStream out = new ByteArrayOutputStream(); | |
41 | ObjectOutputStream objOut = new ObjectOutputStream(out); | |
42 | objOut.writeObject(new LazilyParsedNumber("123")); | |
43 | objOut.close(); | |
44 | ||
45 | ObjectInputStream objIn = new ObjectInputStream(new ByteArrayInputStream(out.toByteArray())); | |
46 | Number deserialized = (Number) objIn.readObject(); | |
47 | assertEquals(new BigDecimal("123"), deserialized); | |
48 | } | |
31 | 49 | } |
0 | /* | |
1 | * Copyright (C) 2012 Google Inc. | |
2 | * | |
3 | * Licensed under the Apache License, Version 2.0 (the "License"); | |
4 | * you may not use this file except in compliance with the License. | |
5 | * You may obtain a copy of the License at | |
6 | * | |
7 | * http://www.apache.org/licenses/LICENSE-2.0 | |
8 | * | |
9 | * Unless required by applicable law or agreed to in writing, software | |
10 | * distributed under the License is distributed on an "AS IS" BASIS, | |
11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
12 | * See the License for the specific language governing permissions and | |
13 | * limitations under the License. | |
14 | */ | |
15 | ||
16 | package com.google.gson.internal; | |
17 | ||
18 | import com.google.gson.common.MoreAsserts; | |
19 | import com.google.gson.internal.LinkedHashTreeMap.AvlBuilder; | |
20 | import com.google.gson.internal.LinkedHashTreeMap.AvlIterator; | |
21 | import com.google.gson.internal.LinkedHashTreeMap.Node; | |
22 | import java.util.ArrayList; | |
23 | import java.util.Arrays; | |
24 | import java.util.Iterator; | |
25 | import java.util.Map; | |
26 | import java.util.Random; | |
27 | import junit.framework.TestCase; | |
28 | ||
29 | public final class LinkedHashTreeMapTest extends TestCase { | |
30 | public void testIterationOrder() { | |
31 | LinkedHashTreeMap<String, String> map = new LinkedHashTreeMap<String, String>(); | |
32 | map.put("a", "android"); | |
33 | map.put("c", "cola"); | |
34 | map.put("b", "bbq"); | |
35 | assertIterationOrder(map.keySet(), "a", "c", "b"); | |
36 | assertIterationOrder(map.values(), "android", "cola", "bbq"); | |
37 | } | |
38 | ||
39 | public void testRemoveRootDoesNotDoubleUnlink() { | |
40 | LinkedHashTreeMap<String, String> map = new LinkedHashTreeMap<String, String>(); | |
41 | map.put("a", "android"); | |
42 | map.put("c", "cola"); | |
43 | map.put("b", "bbq"); | |
44 | Iterator<Map.Entry<String,String>> it = map.entrySet().iterator(); | |
45 | it.next(); | |
46 | it.next(); | |
47 | it.next(); | |
48 | it.remove(); | |
49 | assertIterationOrder(map.keySet(), "a", "c"); | |
50 | } | |
51 | ||
52 | public void testPutNullKeyFails() { | |
53 | LinkedHashTreeMap<String, String> map = new LinkedHashTreeMap<String, String>(); | |
54 | try { | |
55 | map.put(null, "android"); | |
56 | fail(); | |
57 | } catch (NullPointerException expected) { | |
58 | } | |
59 | } | |
60 | ||
61 | public void testPutNonComparableKeyFails() { | |
62 | LinkedHashTreeMap<Object, String> map = new LinkedHashTreeMap<Object, String>(); | |
63 | try { | |
64 | map.put(new Object(), "android"); | |
65 | fail(); | |
66 | } catch (ClassCastException expected) {} | |
67 | } | |
68 | ||
69 | public void testContainsNonComparableKeyReturnsFalse() { | |
70 | LinkedHashTreeMap<String, String> map = new LinkedHashTreeMap<String, String>(); | |
71 | map.put("a", "android"); | |
72 | assertFalse(map.containsKey(new Object())); | |
73 | } | |
74 | ||
75 | public void testContainsNullKeyIsAlwaysFalse() { | |
76 | LinkedHashTreeMap<String, String> map = new LinkedHashTreeMap<String, String>(); | |
77 | map.put("a", "android"); | |
78 | assertFalse(map.containsKey(null)); | |
79 | } | |
80 | ||
81 | public void testPutOverrides() throws Exception { | |
82 | LinkedHashTreeMap<String, String> map = new LinkedHashTreeMap<String, String>(); | |
83 | assertNull(map.put("d", "donut")); | |
84 | assertNull(map.put("e", "eclair")); | |
85 | assertNull(map.put("f", "froyo")); | |
86 | assertEquals(3, map.size()); | |
87 | ||
88 | assertEquals("donut", map.get("d")); | |
89 | assertEquals("donut", map.put("d", "done")); | |
90 | assertEquals(3, map.size()); | |
91 | } | |
92 | ||
93 | public void testEmptyStringValues() { | |
94 | LinkedHashTreeMap<String, String> map = new LinkedHashTreeMap<String, String>(); | |
95 | map.put("a", ""); | |
96 | assertTrue(map.containsKey("a")); | |
97 | assertEquals("", map.get("a")); | |
98 | } | |
99 | ||
100 | // NOTE that this does not happen every time, but given the below predictable random, | |
101 | // this test will consistently fail (assuming the initial size is 16 and rehashing | |
102 | // size remains at 3/4) | |
103 | public void testForceDoublingAndRehash() throws Exception { | |
104 | Random random = new Random(1367593214724L); | |
105 | LinkedHashTreeMap<String, String> map = new LinkedHashTreeMap<String, String>(); | |
106 | String[] keys = new String[1000]; | |
107 | for (int i = 0; i < keys.length; i++) { | |
108 | keys[i] = Integer.toString(Math.abs(random.nextInt()), 36) + "-" + i; | |
109 | map.put(keys[i], "" + i); | |
110 | } | |
111 | ||
112 | for (int i = 0; i < keys.length; i++) { | |
113 | String key = keys[i]; | |
114 | assertTrue(map.containsKey(key)); | |
115 | assertEquals("" + i, map.get(key)); | |
116 | } | |
117 | } | |
118 | ||
119 | public void testClear() { | |
120 | LinkedHashTreeMap<String, String> map = new LinkedHashTreeMap<String, String>(); | |
121 | map.put("a", "android"); | |
122 | map.put("c", "cola"); | |
123 | map.put("b", "bbq"); | |
124 | map.clear(); | |
125 | assertIterationOrder(map.keySet()); | |
126 | assertEquals(0, map.size()); | |
127 | } | |
128 | ||
129 | public void testEqualsAndHashCode() throws Exception { | |
130 | LinkedHashTreeMap<String, Integer> map1 = new LinkedHashTreeMap<String, Integer>(); | |
131 | map1.put("A", 1); | |
132 | map1.put("B", 2); | |
133 | map1.put("C", 3); | |
134 | map1.put("D", 4); | |
135 | ||
136 | LinkedHashTreeMap<String, Integer> map2 = new LinkedHashTreeMap<String, Integer>(); | |
137 | map2.put("C", 3); | |
138 | map2.put("B", 2); | |
139 | map2.put("D", 4); | |
140 | map2.put("A", 1); | |
141 | ||
142 | MoreAsserts.assertEqualsAndHashCode(map1, map2); | |
143 | } | |
144 | ||
145 | public void testAvlWalker() { | |
146 | assertAvlWalker(node(node("a"), "b", node("c")), | |
147 | "a", "b", "c"); | |
148 | assertAvlWalker(node(node(node("a"), "b", node("c")), "d", node(node("e"), "f", node("g"))), | |
149 | "a", "b", "c", "d", "e", "f", "g"); | |
150 | assertAvlWalker(node(node(null, "a", node("b")), "c", node(node("d"), "e", null)), | |
151 | "a", "b", "c", "d", "e"); | |
152 | assertAvlWalker(node(null, "a", node(null, "b", node(null, "c", node("d")))), | |
153 | "a", "b", "c", "d"); | |
154 | assertAvlWalker(node(node(node(node("a"), "b", null), "c", null), "d", null), | |
155 | "a", "b", "c", "d"); | |
156 | } | |
157 | ||
158 | private void assertAvlWalker(Node<String, String> root, String... values) { | |
159 | AvlIterator<String, String> iterator = new AvlIterator<String, String>(); | |
160 | iterator.reset(root); | |
161 | for (String value : values) { | |
162 | assertEquals(value, iterator.next().getKey()); | |
163 | } | |
164 | assertNull(iterator.next()); | |
165 | } | |
166 | ||
167 | public void testAvlBuilder() { | |
168 | assertAvlBuilder(1, "a"); | |
169 | assertAvlBuilder(2, "(. a b)"); | |
170 | assertAvlBuilder(3, "(a b c)"); | |
171 | assertAvlBuilder(4, "(a b (. c d))"); | |
172 | assertAvlBuilder(5, "(a b (c d e))"); | |
173 | assertAvlBuilder(6, "((. a b) c (d e f))"); | |
174 | assertAvlBuilder(7, "((a b c) d (e f g))"); | |
175 | assertAvlBuilder(8, "((a b c) d (e f (. g h)))"); | |
176 | assertAvlBuilder(9, "((a b c) d (e f (g h i)))"); | |
177 | assertAvlBuilder(10, "((a b c) d ((. e f) g (h i j)))"); | |
178 | assertAvlBuilder(11, "((a b c) d ((e f g) h (i j k)))"); | |
179 | assertAvlBuilder(12, "((a b (. c d)) e ((f g h) i (j k l)))"); | |
180 | assertAvlBuilder(13, "((a b (c d e)) f ((g h i) j (k l m)))"); | |
181 | assertAvlBuilder(14, "(((. a b) c (d e f)) g ((h i j) k (l m n)))"); | |
182 | assertAvlBuilder(15, "(((a b c) d (e f g)) h ((i j k) l (m n o)))"); | |
183 | assertAvlBuilder(16, "(((a b c) d (e f g)) h ((i j k) l (m n (. o p))))"); | |
184 | assertAvlBuilder(30, "((((. a b) c (d e f)) g ((h i j) k (l m n))) o " | |
185 | + "(((p q r) s (t u v)) w ((x y z) A (B C D))))"); | |
186 | assertAvlBuilder(31, "((((a b c) d (e f g)) h ((i j k) l (m n o))) p " | |
187 | + "(((q r s) t (u v w)) x ((y z A) B (C D E))))"); | |
188 | } | |
189 | ||
190 | private void assertAvlBuilder(int size, String expected) { | |
191 | char[] values = "abcdefghijklmnopqrstuvwxyzABCDE".toCharArray(); | |
192 | AvlBuilder<String, String> avlBuilder = new AvlBuilder<String, String>(); | |
193 | avlBuilder.reset(size); | |
194 | for (int i = 0; i < size; i++) { | |
195 | avlBuilder.add(node(Character.toString(values[i]))); | |
196 | } | |
197 | assertTree(expected, avlBuilder.root()); | |
198 | } | |
199 | ||
200 | public void testDoubleCapacity() { | |
201 | @SuppressWarnings("unchecked") // Arrays and generics don't get along. | |
202 | Node<String, String>[] oldTable = new Node[1]; | |
203 | oldTable[0] = node(node(node("a"), "b", node("c")), "d", node(node("e"), "f", node("g"))); | |
204 | ||
205 | Node<String, String>[] newTable = LinkedHashTreeMap.doubleCapacity(oldTable); | |
206 | assertTree("(b d f)", newTable[0]); // Even hash codes! | |
207 | assertTree("(a c (. e g))", newTable[1]); // Odd hash codes! | |
208 | } | |
209 | ||
210 | public void testDoubleCapacityAllNodesOnLeft() { | |
211 | @SuppressWarnings("unchecked") // Arrays and generics don't get along. | |
212 | Node<String, String>[] oldTable = new Node[1]; | |
213 | oldTable[0] = node(node("b"), "d", node("f")); | |
214 | ||
215 | Node<String, String>[] newTable = LinkedHashTreeMap.doubleCapacity(oldTable); | |
216 | assertTree("(b d f)", newTable[0]); // Even hash codes! | |
217 | assertNull(newTable[1]); // Odd hash codes! | |
218 | ||
219 | for (Node<?, ?> node : newTable) { | |
220 | if (node != null) { | |
221 | assertConsistent(node); | |
222 | } | |
223 | } | |
224 | } | |
225 | ||
226 | private static final Node<String, String> head = new Node<String, String>(); | |
227 | ||
228 | private Node<String, String> node(String value) { | |
229 | return new Node<String, String>(null, value, value.hashCode(), head, head); | |
230 | } | |
231 | ||
232 | private Node<String, String> node(Node<String, String> left, String value, | |
233 | Node<String, String> right) { | |
234 | Node<String, String> result = node(value); | |
235 | if (left != null) { | |
236 | result.left = left; | |
237 | left.parent = result; | |
238 | } | |
239 | if (right != null) { | |
240 | result.right = right; | |
241 | right.parent = result; | |
242 | } | |
243 | return result; | |
244 | } | |
245 | ||
246 | private void assertTree(String expected, Node<?, ?> root) { | |
247 | assertEquals(expected, toString(root)); | |
248 | assertConsistent(root); | |
249 | } | |
250 | ||
251 | private void assertConsistent(Node<?, ?> node) { | |
252 | int leftHeight = 0; | |
253 | if (node.left != null) { | |
254 | assertConsistent(node.left); | |
255 | assertSame(node, node.left.parent); | |
256 | leftHeight = node.left.height; | |
257 | } | |
258 | int rightHeight = 0; | |
259 | if (node.right != null) { | |
260 | assertConsistent(node.right); | |
261 | assertSame(node, node.right.parent); | |
262 | rightHeight = node.right.height; | |
263 | } | |
264 | if (node.parent != null) { | |
265 | assertTrue(node.parent.left == node || node.parent.right == node); | |
266 | } | |
267 | if (Math.max(leftHeight, rightHeight) + 1 != node.height) { | |
268 | fail(); | |
269 | } | |
270 | } | |
271 | ||
272 | private String toString(Node<?, ?> root) { | |
273 | if (root == null) { | |
274 | return "."; | |
275 | } else if (root.left == null && root.right == null) { | |
276 | return String.valueOf(root.key); | |
277 | } else { | |
278 | return String.format("(%s %s %s)", toString(root.left), root.key, toString(root.right)); | |
279 | } | |
280 | } | |
281 | ||
282 | private <T> void assertIterationOrder(Iterable<T> actual, T... expected) { | |
283 | ArrayList<T> actualList = new ArrayList<T>(); | |
284 | for (T t : actual) { | |
285 | actualList.add(t); | |
286 | } | |
287 | assertEquals(Arrays.asList(expected), actualList); | |
288 | } | |
289 | } |
15 | 15 | |
16 | 16 | package com.google.gson.internal; |
17 | 17 | |
18 | import java.io.ByteArrayInputStream; | |
19 | import java.io.ByteArrayOutputStream; | |
20 | import java.io.IOException; | |
21 | import java.io.ObjectInputStream; | |
22 | import java.io.ObjectOutputStream; | |
18 | 23 | import java.util.ArrayList; |
19 | 24 | import java.util.Arrays; |
25 | import java.util.Collections; | |
20 | 26 | import java.util.Iterator; |
21 | 27 | import java.util.Map; |
22 | 28 | import java.util.Random; |
139 | 145 | MoreAsserts.assertEqualsAndHashCode(map1, map2); |
140 | 146 | } |
141 | 147 | |
142 | private <T> void assertIterationOrder(Iterable<T> actual, T... expected) { | |
148 | public void testJavaSerialization() throws IOException, ClassNotFoundException { | |
149 | ByteArrayOutputStream out = new ByteArrayOutputStream(); | |
150 | ObjectOutputStream objOut = new ObjectOutputStream(out); | |
151 | Map<String, Integer> map = new LinkedTreeMap<String, Integer>(); | |
152 | map.put("a", 1); | |
153 | objOut.writeObject(map); | |
154 | objOut.close(); | |
155 | ||
156 | ObjectInputStream objIn = new ObjectInputStream(new ByteArrayInputStream(out.toByteArray())); | |
157 | @SuppressWarnings("unchecked") | |
158 | Map<String, Integer> deserialized = (Map<String, Integer>) objIn.readObject(); | |
159 | assertEquals(Collections.singletonMap("a", 1), deserialized); | |
160 | } | |
161 | ||
162 | @SafeVarargs | |
163 | private final <T> void assertIterationOrder(Iterable<T> actual, T... expected) { | |
143 | 164 | ArrayList<T> actualList = new ArrayList<T>(); |
144 | 165 | for (T t : actual) { |
145 | 166 | actualList.add(t); |
0 | /* | |
1 | * Copyright (C) 2008 Google Inc. | |
2 | * | |
3 | * Licensed under the Apache License, Version 2.0 (the "License"); | |
4 | * you may not use this file except in compliance with the License. | |
5 | * You may obtain a copy of the License at | |
6 | * | |
7 | * http://www.apache.org/licenses/LICENSE-2.0 | |
8 | * | |
9 | * Unless required by applicable law or agreed to in writing, software | |
10 | * distributed under the License is distributed on an "AS IS" BASIS, | |
11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
12 | * See the License for the specific language governing permissions and | |
13 | * limitations under the License. | |
14 | */ | |
15 | ||
16 | package com.google.gson.internal.bind; | |
17 | ||
18 | import java.io.IOException; | |
19 | import java.text.DateFormat; | |
20 | import java.text.SimpleDateFormat; | |
21 | import java.util.Date; | |
22 | import java.util.Locale; | |
23 | import java.util.TimeZone; | |
24 | ||
25 | import com.google.gson.Gson; | |
26 | import com.google.gson.TypeAdapter; | |
27 | import com.google.gson.TypeAdapterFactory; | |
28 | import com.google.gson.internal.JavaVersion; | |
29 | import com.google.gson.internal.bind.DefaultDateTypeAdapter.DateType; | |
30 | import com.google.gson.reflect.TypeToken; | |
31 | ||
32 | import junit.framework.TestCase; | |
33 | ||
34 | /** | |
35 | * A simple unit test for the {@link DefaultDateTypeAdapter} class. | |
36 | * | |
37 | * @author Joel Leitch | |
38 | */ | |
39 | public class DefaultDateTypeAdapterTest extends TestCase { | |
40 | ||
41 | public void testFormattingInEnUs() { | |
42 | assertFormattingAlwaysEmitsUsLocale(Locale.US); | |
43 | } | |
44 | ||
45 | public void testFormattingInFr() { | |
46 | assertFormattingAlwaysEmitsUsLocale(Locale.FRANCE); | |
47 | } | |
48 | ||
49 | private void assertFormattingAlwaysEmitsUsLocale(Locale locale) { | |
50 | TimeZone defaultTimeZone = TimeZone.getDefault(); | |
51 | TimeZone.setDefault(TimeZone.getTimeZone("UTC")); | |
52 | Locale defaultLocale = Locale.getDefault(); | |
53 | Locale.setDefault(locale); | |
54 | try { | |
55 | String afterYearSep = JavaVersion.isJava9OrLater() ? ", " : " "; | |
56 | String afterYearLongSep = JavaVersion.isJava9OrLater() ? " at " : " "; | |
57 | String utcFull = JavaVersion.isJava9OrLater() ? "Coordinated Universal Time" : "UTC"; | |
58 | assertFormatted(String.format("Jan 1, 1970%s12:00:00 AM", afterYearSep), | |
59 | DateType.DATE.createDefaultsAdapterFactory()); | |
60 | assertFormatted("1/1/70", DateType.DATE.createAdapterFactory(DateFormat.SHORT)); | |
61 | assertFormatted("Jan 1, 1970", DateType.DATE.createAdapterFactory(DateFormat.MEDIUM)); | |
62 | assertFormatted("January 1, 1970", DateType.DATE.createAdapterFactory(DateFormat.LONG)); | |
63 | assertFormatted(String.format("1/1/70%s12:00 AM", afterYearSep), | |
64 | DateType.DATE.createAdapterFactory(DateFormat.SHORT, DateFormat.SHORT)); | |
65 | assertFormatted(String.format("Jan 1, 1970%s12:00:00 AM", afterYearSep), | |
66 | DateType.DATE.createAdapterFactory(DateFormat.MEDIUM, DateFormat.MEDIUM)); | |
67 | assertFormatted(String.format("January 1, 1970%s12:00:00 AM UTC", afterYearLongSep), | |
68 | DateType.DATE.createAdapterFactory(DateFormat.LONG, DateFormat.LONG)); | |
69 | assertFormatted(String.format("Thursday, January 1, 1970%s12:00:00 AM %s", afterYearLongSep, utcFull), | |
70 | DateType.DATE.createAdapterFactory(DateFormat.FULL, DateFormat.FULL)); | |
71 | } finally { | |
72 | TimeZone.setDefault(defaultTimeZone); | |
73 | Locale.setDefault(defaultLocale); | |
74 | } | |
75 | } | |
76 | ||
77 | public void testParsingDatesFormattedWithSystemLocale() throws Exception { | |
78 | TimeZone defaultTimeZone = TimeZone.getDefault(); | |
79 | TimeZone.setDefault(TimeZone.getTimeZone("UTC")); | |
80 | Locale defaultLocale = Locale.getDefault(); | |
81 | Locale.setDefault(Locale.FRANCE); | |
82 | try { | |
83 | String afterYearSep = JavaVersion.isJava9OrLater() ? " à " : " "; | |
84 | assertParsed(String.format("1 janv. 1970%s00:00:00", afterYearSep), | |
85 | DateType.DATE.createDefaultsAdapterFactory()); | |
86 | assertParsed("01/01/70", DateType.DATE.createAdapterFactory(DateFormat.SHORT)); | |
87 | assertParsed("1 janv. 1970", DateType.DATE.createAdapterFactory(DateFormat.MEDIUM)); | |
88 | assertParsed("1 janvier 1970", DateType.DATE.createAdapterFactory(DateFormat.LONG)); | |
89 | assertParsed("01/01/70 00:00", | |
90 | DateType.DATE.createAdapterFactory(DateFormat.SHORT, DateFormat.SHORT)); | |
91 | assertParsed(String.format("1 janv. 1970%s00:00:00", afterYearSep), | |
92 | DateType.DATE.createAdapterFactory(DateFormat.MEDIUM, DateFormat.MEDIUM)); | |
93 | assertParsed(String.format("1 janvier 1970%s00:00:00 UTC", afterYearSep), | |
94 | DateType.DATE.createAdapterFactory(DateFormat.LONG, DateFormat.LONG)); | |
95 | assertParsed(JavaVersion.isJava9OrLater() ? (JavaVersion.getMajorJavaVersion() <11 ? | |
96 | "jeudi 1 janvier 1970 à 00:00:00 Coordinated Universal Time" : | |
97 | "jeudi 1 janvier 1970 à 00:00:00 Temps universel coordonné") : | |
98 | "jeudi 1 janvier 1970 00 h 00 UTC", | |
99 | DateType.DATE.createAdapterFactory(DateFormat.FULL, DateFormat.FULL)); | |
100 | } finally { | |
101 | TimeZone.setDefault(defaultTimeZone); | |
102 | Locale.setDefault(defaultLocale); | |
103 | } | |
104 | } | |
105 | ||
106 | public void testParsingDatesFormattedWithUsLocale() throws Exception { | |
107 | TimeZone defaultTimeZone = TimeZone.getDefault(); | |
108 | TimeZone.setDefault(TimeZone.getTimeZone("UTC")); | |
109 | Locale defaultLocale = Locale.getDefault(); | |
110 | Locale.setDefault(Locale.US); | |
111 | try { | |
112 | assertParsed("Jan 1, 1970 0:00:00 AM", DateType.DATE.createDefaultsAdapterFactory()); | |
113 | assertParsed("1/1/70", DateType.DATE.createAdapterFactory(DateFormat.SHORT)); | |
114 | assertParsed("Jan 1, 1970", DateType.DATE.createAdapterFactory(DateFormat.MEDIUM)); | |
115 | assertParsed("January 1, 1970", DateType.DATE.createAdapterFactory(DateFormat.LONG)); | |
116 | assertParsed("1/1/70 0:00 AM", | |
117 | DateType.DATE.createAdapterFactory(DateFormat.SHORT, DateFormat.SHORT)); | |
118 | assertParsed("Jan 1, 1970 0:00:00 AM", | |
119 | DateType.DATE.createAdapterFactory(DateFormat.MEDIUM, DateFormat.MEDIUM)); | |
120 | assertParsed("January 1, 1970 0:00:00 AM UTC", | |
121 | DateType.DATE.createAdapterFactory(DateFormat.LONG, DateFormat.LONG)); | |
122 | assertParsed("Thursday, January 1, 1970 0:00:00 AM UTC", | |
123 | DateType.DATE.createAdapterFactory(DateFormat.FULL, DateFormat.FULL)); | |
124 | } finally { | |
125 | TimeZone.setDefault(defaultTimeZone); | |
126 | Locale.setDefault(defaultLocale); | |
127 | } | |
128 | } | |
129 | ||
130 | public void testFormatUsesDefaultTimezone() throws Exception { | |
131 | TimeZone defaultTimeZone = TimeZone.getDefault(); | |
132 | TimeZone.setDefault(TimeZone.getTimeZone("America/Los_Angeles")); | |
133 | Locale defaultLocale = Locale.getDefault(); | |
134 | Locale.setDefault(Locale.US); | |
135 | try { | |
136 | String afterYearSep = JavaVersion.isJava9OrLater() ? ", " : " "; | |
137 | assertFormatted(String.format("Dec 31, 1969%s4:00:00 PM", afterYearSep), | |
138 | DateType.DATE.createDefaultsAdapterFactory()); | |
139 | assertParsed("Dec 31, 1969 4:00:00 PM", DateType.DATE.createDefaultsAdapterFactory()); | |
140 | } finally { | |
141 | TimeZone.setDefault(defaultTimeZone); | |
142 | Locale.setDefault(defaultLocale); | |
143 | } | |
144 | } | |
145 | ||
146 | public void testDateDeserializationISO8601() throws Exception { | |
147 | TypeAdapterFactory adapterFactory = DateType.DATE.createDefaultsAdapterFactory(); | |
148 | assertParsed("1970-01-01T00:00:00.000Z", adapterFactory); | |
149 | assertParsed("1970-01-01T00:00Z", adapterFactory); | |
150 | assertParsed("1970-01-01T00:00:00+00:00", adapterFactory); | |
151 | assertParsed("1970-01-01T01:00:00+01:00", adapterFactory); | |
152 | assertParsed("1970-01-01T01:00:00+01", adapterFactory); | |
153 | } | |
154 | ||
155 | public void testDateSerialization() throws Exception { | |
156 | int dateStyle = DateFormat.LONG; | |
157 | TypeAdapter<Date> dateTypeAdapter = dateAdapter(DateType.DATE.createAdapterFactory(dateStyle)); | |
158 | DateFormat formatter = DateFormat.getDateInstance(dateStyle, Locale.US); | |
159 | Date currentDate = new Date(); | |
160 | ||
161 | String dateString = dateTypeAdapter.toJson(currentDate); | |
162 | assertEquals(toLiteral(formatter.format(currentDate)), dateString); | |
163 | } | |
164 | ||
165 | public void testDatePattern() throws Exception { | |
166 | String pattern = "yyyy-MM-dd"; | |
167 | TypeAdapter<Date> dateTypeAdapter = dateAdapter(DateType.DATE.createAdapterFactory(pattern)); | |
168 | DateFormat formatter = new SimpleDateFormat(pattern); | |
169 | Date currentDate = new Date(); | |
170 | ||
171 | String dateString = dateTypeAdapter.toJson(currentDate); | |
172 | assertEquals(toLiteral(formatter.format(currentDate)), dateString); | |
173 | } | |
174 | ||
175 | public void testInvalidDatePattern() throws Exception { | |
176 | try { | |
177 | DateType.DATE.createAdapterFactory("I am a bad Date pattern...."); | |
178 | fail("Invalid date pattern should fail."); | |
179 | } catch (IllegalArgumentException expected) { } | |
180 | } | |
181 | ||
182 | public void testNullValue() throws Exception { | |
183 | TypeAdapter<Date> adapter = dateAdapter(DateType.DATE.createDefaultsAdapterFactory()); | |
184 | assertNull(adapter.fromJson("null")); | |
185 | assertEquals("null", adapter.toJson(null)); | |
186 | } | |
187 | ||
188 | public void testUnexpectedToken() throws Exception { | |
189 | try { | |
190 | TypeAdapter<Date> adapter = dateAdapter(DateType.DATE.createDefaultsAdapterFactory()); | |
191 | adapter.fromJson("{}"); | |
192 | fail("Unexpected token should fail."); | |
193 | } catch (IllegalStateException expected) { } | |
194 | } | |
195 | ||
196 | private static TypeAdapter<Date> dateAdapter(TypeAdapterFactory adapterFactory) { | |
197 | TypeAdapter<Date> adapter = adapterFactory.create(new Gson(), TypeToken.get(Date.class)); | |
198 | assertNotNull(adapter); | |
199 | return adapter; | |
200 | } | |
201 | ||
202 | private static void assertFormatted(String formatted, TypeAdapterFactory adapterFactory) { | |
203 | TypeAdapter<Date> adapter = dateAdapter(adapterFactory); | |
204 | assertEquals(toLiteral(formatted), adapter.toJson(new Date(0))); | |
205 | } | |
206 | ||
207 | private static void assertParsed(String date, TypeAdapterFactory adapterFactory) throws IOException { | |
208 | TypeAdapter<Date> adapter = dateAdapter(adapterFactory); | |
209 | assertEquals(date, new Date(0), adapter.fromJson(toLiteral(date))); | |
210 | assertEquals("ISO 8601", new Date(0), adapter.fromJson(toLiteral("1970-01-01T00:00:00Z"))); | |
211 | } | |
212 | ||
213 | private static String toLiteral(String s) { | |
214 | return '"' + s + '"'; | |
215 | } | |
216 | } |
17 | 17 | |
18 | 18 | import com.google.gson.JsonElement; |
19 | 19 | import com.google.gson.JsonParser; |
20 | import com.google.gson.JsonPrimitive; | |
20 | 21 | import com.google.gson.stream.JsonToken; |
21 | 22 | import java.io.IOException; |
22 | 23 | import junit.framework.TestCase; |
297 | 298 | reader.endArray(); |
298 | 299 | } |
299 | 300 | |
301 | public void testNextJsonElement() throws IOException { | |
302 | final JsonElement element = JsonParser.parseString("{\"A\": 1, \"B\" : {}, \"C\" : []}"); | |
303 | JsonTreeReader reader = new JsonTreeReader(element); | |
304 | reader.beginObject(); | |
305 | try { | |
306 | reader.nextJsonElement(); | |
307 | fail(); | |
308 | } catch (IllegalStateException expected) { | |
309 | } | |
310 | reader.nextName(); | |
311 | assertEquals(reader.nextJsonElement(), new JsonPrimitive(1)); | |
312 | reader.nextName(); | |
313 | reader.beginObject(); | |
314 | try { | |
315 | reader.nextJsonElement(); | |
316 | fail(); | |
317 | } catch (IllegalStateException expected) { | |
318 | } | |
319 | reader.endObject(); | |
320 | reader.nextName(); | |
321 | reader.beginArray(); | |
322 | try { | |
323 | reader.nextJsonElement(); | |
324 | fail(); | |
325 | } catch (IllegalStateException expected) { | |
326 | } | |
327 | reader.endArray(); | |
328 | reader.endObject(); | |
329 | try { | |
330 | reader.nextJsonElement(); | |
331 | fail(); | |
332 | } catch (IllegalStateException expected) { | |
333 | } | |
334 | } | |
335 | ||
300 | 336 | public void testEarlyClose() throws IOException { |
301 | 337 | JsonElement element = JsonParser.parseString("[1, 2, 3]"); |
302 | 338 | JsonTreeReader reader = new JsonTreeReader(element); |
46 | 46 | in.skipValue(); |
47 | 47 | assertEquals(JsonToken.END_DOCUMENT, in.peek()); |
48 | 48 | } |
49 | ||
50 | public void testHasNext_endOfDocument() throws IOException { | |
51 | JsonTreeReader reader = new JsonTreeReader(new JsonObject()); | |
52 | reader.beginObject(); | |
53 | reader.endObject(); | |
54 | assertFalse(reader.hasNext()); | |
55 | } | |
49 | 56 | } |
35 | 35 | |
36 | 36 | @SuppressWarnings("unused") |
37 | 37 | private static class Foo1<A> { |
38 | public Foo2<? extends A> foo2; | |
38 | public Foo2<? extends A> foo2; | |
39 | 39 | } |
40 | 40 | @SuppressWarnings("unused") |
41 | 41 | private static class Foo2<B> { |
47 | 47 | */ |
48 | 48 | |
49 | 49 | public void testRecursiveResolveSimple() { |
50 | @SuppressWarnings("rawtypes") | |
50 | 51 | TypeAdapter<Foo1> adapter = new Gson().getAdapter(Foo1.class); |
51 | assertNotNull(adapter); | |
52 | } | |
53 | ||
54 | /** | |
55 | * Real-world samples, found in Issues #603 and #440. | |
56 | */ | |
57 | ||
58 | public void testIssue603PrintStream() { | |
59 | TypeAdapter<PrintStream> adapter = new Gson().getAdapter(PrintStream.class); | |
60 | assertNotNull(adapter); | |
61 | } | |
62 | ||
63 | public void testIssue440WeakReference() throws Exception { | |
64 | TypeAdapter<WeakReference> adapter = new Gson().getAdapter(WeakReference.class); | |
65 | 52 | assertNotNull(adapter); |
66 | 53 | } |
67 | 54 | |
104 | 91 | } |
105 | 92 | |
106 | 93 | public void testRecursiveTypeVariablesResolve1() throws Exception { |
94 | @SuppressWarnings("rawtypes") | |
107 | 95 | TypeAdapter<TestType> adapter = new Gson().getAdapter(TestType.class); |
108 | 96 | assertNotNull(adapter); |
109 | 97 | } |
110 | 98 | |
111 | 99 | public void testRecursiveTypeVariablesResolve12() throws Exception { |
100 | @SuppressWarnings("rawtypes") | |
112 | 101 | TypeAdapter<TestType2> adapter = new Gson().getAdapter(TestType2.class); |
113 | 102 | assertNotNull(adapter); |
114 | 103 | } |
0 | 0 | package com.google.gson.internal.bind.util; |
1 | 1 | |
2 | import org.junit.Rule; | |
3 | 2 | import org.junit.Test; |
4 | import org.junit.rules.ExpectedException; | |
5 | ||
3 | import org.junit.function.ThrowingRunnable; | |
6 | 4 | import java.text.ParseException; |
7 | 5 | import java.text.ParsePosition; |
8 | 6 | import java.util.*; |
9 | 7 | |
10 | 8 | import static org.junit.Assert.assertEquals; |
9 | import static org.junit.Assert.assertThrows; | |
11 | 10 | |
12 | 11 | public class ISO8601UtilsTest { |
13 | ||
14 | @Rule | |
15 | public final ExpectedException exception = ExpectedException.none(); | |
16 | 12 | |
17 | 13 | private static TimeZone utcTimeZone() { |
18 | 14 | return TimeZone.getTimeZone("UTC"); |
86 | 82 | |
87 | 83 | @Test |
88 | 84 | public void testDateParseInvalidTime() throws ParseException { |
89 | String dateStr = "2018-06-25T61:60:62-03:00"; | |
90 | exception.expect(ParseException.class); | |
91 | ISO8601Utils.parse(dateStr, new ParsePosition(0)); | |
85 | final String dateStr = "2018-06-25T61:60:62-03:00"; | |
86 | assertThrows(ParseException.class, new ThrowingRunnable() { | |
87 | @Override | |
88 | public void run() throws Throwable { | |
89 | ISO8601Utils.parse(dateStr, new ParsePosition(0)); | |
90 | } | |
91 | }); | |
92 | 92 | } |
93 | 93 | } |
+0
-77
0 | /* | |
1 | * Copyright (C) 2018 The Gson authors | |
2 | * | |
3 | * Licensed under the Apache License, Version 2.0 (the "License"); | |
4 | * you may not use this file except in compliance with the License. | |
5 | * You may obtain a copy of the License at | |
6 | * | |
7 | * http://www.apache.org/licenses/LICENSE-2.0 | |
8 | * | |
9 | * Unless required by applicable law or agreed to in writing, software | |
10 | * distributed under the License is distributed on an "AS IS" BASIS, | |
11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
12 | * See the License for the specific language governing permissions and | |
13 | * limitations under the License. | |
14 | */ | |
15 | package com.google.gson.internal.reflect; | |
16 | ||
17 | import static org.junit.Assert.assertFalse; | |
18 | import static org.junit.Assert.assertTrue; | |
19 | import static org.junit.Assert.fail; | |
20 | ||
21 | import java.lang.reflect.Field; | |
22 | import java.security.Permission; | |
23 | ||
24 | import org.junit.Test; | |
25 | ||
26 | /** | |
27 | * Unit tests for {@link UnsafeReflectionAccessor} | |
28 | * | |
29 | * @author Inderjeet Singh | |
30 | */ | |
31 | public class UnsafeReflectionAccessorTest { | |
32 | ||
33 | @Test | |
34 | public void testMakeAccessibleWithUnsafe() throws Exception { | |
35 | UnsafeReflectionAccessor accessor = new UnsafeReflectionAccessor(); | |
36 | Field field = ClassWithPrivateFinalFields.class.getDeclaredField("a"); | |
37 | try { | |
38 | boolean success = accessor.makeAccessibleWithUnsafe(field); | |
39 | assertTrue(success); | |
40 | } catch (Exception e) { | |
41 | fail("Unsafe didn't work on the JDK"); | |
42 | } | |
43 | } | |
44 | ||
45 | @Test | |
46 | public void testMakeAccessibleWithRestrictiveSecurityManager() throws Exception { | |
47 | final Permission accessDeclaredMembers = new RuntimePermission("accessDeclaredMembers"); | |
48 | final SecurityManager original = System.getSecurityManager(); | |
49 | SecurityManager restrictiveManager = new SecurityManager() { | |
50 | @Override | |
51 | public void checkPermission(Permission perm) { | |
52 | if (accessDeclaredMembers.equals(perm)) { | |
53 | throw new SecurityException("nope"); | |
54 | } | |
55 | } | |
56 | }; | |
57 | System.setSecurityManager(restrictiveManager); | |
58 | ||
59 | try { | |
60 | UnsafeReflectionAccessor accessor = new UnsafeReflectionAccessor(); | |
61 | Field field = ClassWithPrivateFinalFields.class.getDeclaredField("a"); | |
62 | assertFalse("override field should have been inaccessible", accessor.makeAccessibleWithUnsafe(field)); | |
63 | accessor.makeAccessible(field); | |
64 | } finally { | |
65 | System.setSecurityManager(original); | |
66 | } | |
67 | } | |
68 | ||
69 | @SuppressWarnings("unused") | |
70 | private static final class ClassWithPrivateFinalFields { | |
71 | private final String a; | |
72 | public ClassWithPrivateFinalFields(String a) { | |
73 | this.a = a; | |
74 | } | |
75 | } | |
76 | } |
0 | package com.google.gson.internal.sql; | |
1 | ||
2 | import java.sql.Date; | |
3 | import java.sql.Time; | |
4 | import java.sql.Timestamp; | |
5 | import java.util.Locale; | |
6 | import java.util.TimeZone; | |
7 | ||
8 | import com.google.gson.Gson; | |
9 | import com.google.gson.GsonBuilder; | |
10 | import com.google.gson.functional.DefaultTypeAdaptersTest; | |
11 | import com.google.gson.internal.JavaVersion; | |
12 | ||
13 | import junit.framework.TestCase; | |
14 | ||
15 | public class SqlTypesGsonTest extends TestCase { | |
16 | private Gson gson; | |
17 | private TimeZone oldTimeZone; | |
18 | private Locale oldLocale; | |
19 | ||
20 | @Override | |
21 | protected void setUp() throws Exception { | |
22 | super.setUp(); | |
23 | this.oldTimeZone = TimeZone.getDefault(); | |
24 | TimeZone.setDefault(TimeZone.getTimeZone("America/Los_Angeles")); | |
25 | this.oldLocale = Locale.getDefault(); | |
26 | Locale.setDefault(Locale.US); | |
27 | gson = new Gson(); | |
28 | } | |
29 | ||
30 | @Override | |
31 | protected void tearDown() throws Exception { | |
32 | super.tearDown(); | |
33 | TimeZone.setDefault(oldTimeZone); | |
34 | Locale.setDefault(oldLocale); | |
35 | } | |
36 | ||
37 | public void testNullSerializationAndDeserialization() { | |
38 | testNullSerializationAndDeserialization(Date.class); | |
39 | testNullSerializationAndDeserialization(Time.class); | |
40 | testNullSerializationAndDeserialization(Timestamp.class); | |
41 | } | |
42 | ||
43 | private void testNullSerializationAndDeserialization(Class<?> c) { | |
44 | DefaultTypeAdaptersTest.testNullSerializationAndDeserialization(gson, c); | |
45 | } | |
46 | ||
47 | public void testDefaultSqlDateSerialization() { | |
48 | java.sql.Date instant = new java.sql.Date(1259875082000L); | |
49 | String json = gson.toJson(instant); | |
50 | assertEquals("\"Dec 3, 2009\"", json); | |
51 | } | |
52 | ||
53 | public void testDefaultSqlDateDeserialization() { | |
54 | String json = "'Dec 3, 2009'"; | |
55 | java.sql.Date extracted = gson.fromJson(json, java.sql.Date.class); | |
56 | DefaultTypeAdaptersTest.assertEqualsDate(extracted, 2009, 11, 3); | |
57 | } | |
58 | ||
59 | // http://code.google.com/p/google-gson/issues/detail?id=230 | |
60 | public void testSqlDateSerialization() throws Exception { | |
61 | TimeZone defaultTimeZone = TimeZone.getDefault(); | |
62 | TimeZone.setDefault(TimeZone.getTimeZone("UTC")); | |
63 | Locale defaultLocale = Locale.getDefault(); | |
64 | Locale.setDefault(Locale.US); | |
65 | try { | |
66 | java.sql.Date sqlDate = new java.sql.Date(0L); | |
67 | Gson gson = new GsonBuilder().setDateFormat("yyyy-MM-dd").create(); | |
68 | String json = gson.toJson(sqlDate, Timestamp.class); | |
69 | assertEquals("\"1970-01-01\"", json); | |
70 | assertEquals(0, gson.fromJson("\"1970-01-01\"", java.sql.Date.class).getTime()); | |
71 | } finally { | |
72 | TimeZone.setDefault(defaultTimeZone); | |
73 | Locale.setDefault(defaultLocale); | |
74 | } | |
75 | } | |
76 | ||
77 | public void testDefaultSqlTimeSerialization() { | |
78 | Time now = new Time(1259875082000L); | |
79 | String json = gson.toJson(now); | |
80 | assertEquals("\"01:18:02 PM\"", json); | |
81 | } | |
82 | ||
83 | public void testDefaultSqlTimeDeserialization() { | |
84 | String json = "'1:18:02 PM'"; | |
85 | Time extracted = gson.fromJson(json, Time.class); | |
86 | DefaultTypeAdaptersTest.assertEqualsTime(extracted, 13, 18, 2); | |
87 | } | |
88 | ||
89 | public void testDefaultSqlTimestampSerialization() { | |
90 | Timestamp now = new java.sql.Timestamp(1259875082000L); | |
91 | String json = gson.toJson(now); | |
92 | if (JavaVersion.isJava9OrLater()) { | |
93 | assertEquals("\"Dec 3, 2009, 1:18:02 PM\"", json); | |
94 | } else { | |
95 | assertEquals("\"Dec 3, 2009 1:18:02 PM\"", json); | |
96 | } | |
97 | } | |
98 | ||
99 | public void testDefaultSqlTimestampDeserialization() { | |
100 | String json = "'Dec 3, 2009 1:18:02 PM'"; | |
101 | Timestamp extracted = gson.fromJson(json, Timestamp.class); | |
102 | DefaultTypeAdaptersTest.assertEqualsDate(extracted, 2009, 11, 3); | |
103 | DefaultTypeAdaptersTest.assertEqualsTime(extracted, 13, 18, 2); | |
104 | } | |
105 | ||
106 | // http://code.google.com/p/google-gson/issues/detail?id=230 | |
107 | public void testTimestampSerialization() throws Exception { | |
108 | TimeZone defaultTimeZone = TimeZone.getDefault(); | |
109 | TimeZone.setDefault(TimeZone.getTimeZone("UTC")); | |
110 | Locale defaultLocale = Locale.getDefault(); | |
111 | Locale.setDefault(Locale.US); | |
112 | try { | |
113 | Timestamp timestamp = new Timestamp(0L); | |
114 | Gson gson = new GsonBuilder().setDateFormat("yyyy-MM-dd").create(); | |
115 | String json = gson.toJson(timestamp, Timestamp.class); | |
116 | assertEquals("\"1970-01-01\"", json); | |
117 | assertEquals(0, gson.fromJson("\"1970-01-01\"", Timestamp.class).getTime()); | |
118 | } finally { | |
119 | TimeZone.setDefault(defaultTimeZone); | |
120 | Locale.setDefault(defaultLocale); | |
121 | } | |
122 | } | |
123 | } |
0 | package com.google.gson.internal.sql; | |
1 | ||
2 | import junit.framework.TestCase; | |
3 | ||
4 | public class SqlTypesSupportTest extends TestCase { | |
5 | public void testSupported() { | |
6 | assertTrue(SqlTypesSupport.SUPPORTS_SQL_TYPES); | |
7 | ||
8 | assertNotNull(SqlTypesSupport.DATE_DATE_TYPE); | |
9 | assertNotNull(SqlTypesSupport.TIMESTAMP_DATE_TYPE); | |
10 | ||
11 | assertNotNull(SqlTypesSupport.DATE_FACTORY); | |
12 | assertNotNull(SqlTypesSupport.TIME_FACTORY); | |
13 | assertNotNull(SqlTypesSupport.TIMESTAMP_FACTORY); | |
14 | } | |
15 | } |
0 | /* | |
1 | * Copyright (C) 2016 Google Inc. | |
2 | * | |
3 | * Licensed under the Apache License, Version 2.0 (the "License"); | |
4 | * you may not use this file except in compliance with the License. | |
5 | * You may obtain a copy of the License at | |
6 | * | |
7 | * http://www.apache.org/licenses/LICENSE-2.0 | |
8 | * | |
9 | * Unless required by applicable law or agreed to in writing, software | |
10 | * distributed under the License is distributed on an "AS IS" BASIS, | |
11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
12 | * See the License for the specific language governing permissions and | |
13 | * limitations under the License. | |
14 | */ | |
15 | package com.google.gson.regression; | |
16 | ||
17 | import java.io.InputStream; | |
18 | import java.io.IOException; | |
19 | import java.net.URL; | |
20 | import java.util.ArrayList; | |
21 | import java.util.Collections; | |
22 | import java.util.List; | |
23 | import java.util.jar.Manifest; | |
24 | ||
25 | import junit.framework.TestCase; | |
26 | ||
27 | public class OSGiTest extends TestCase { | |
28 | public void testComGoogleGsonAnnotationsPackage() throws Exception { | |
29 | Manifest mf = findManifest("com.google.gson"); | |
30 | String importPkg = mf.getMainAttributes().getValue("Import-Package"); | |
31 | assertNotNull("Import-Package statement is there", importPkg); | |
32 | assertSubstring("There should be com.google.gson.annotations dependency", importPkg, "com.google.gson.annotations"); | |
33 | } | |
34 | ||
35 | public void testSunMiscImportPackage() throws Exception { | |
36 | Manifest mf = findManifest("com.google.gson"); | |
37 | String importPkg = mf.getMainAttributes().getValue("Import-Package"); | |
38 | assertNotNull("Import-Package statement is there", importPkg); | |
39 | for (String dep : importPkg.split(",")) { | |
40 | if (dep.contains("sun.misc")) { | |
41 | assertSubstring("sun.misc import is optional", dep, "resolution:=optional"); | |
42 | return; | |
43 | } | |
44 | } | |
45 | fail("There should be sun.misc dependency, but was: " + importPkg); | |
46 | } | |
47 | ||
48 | private Manifest findManifest(String pkg) throws IOException { | |
49 | List<URL> urls = new ArrayList<URL>(); | |
50 | for (URL u : Collections.list(getClass().getClassLoader().getResources("META-INF/MANIFEST.MF"))) { | |
51 | InputStream is = u.openStream(); | |
52 | Manifest mf = new Manifest(is); | |
53 | is.close(); | |
54 | if (pkg.equals(mf.getMainAttributes().getValue("Bundle-SymbolicName"))) { | |
55 | return mf; | |
56 | } | |
57 | urls.add(u); | |
58 | } | |
59 | fail("Cannot find " + pkg + " OSGi bundle manifest among: " + urls); | |
60 | return null; | |
61 | } | |
62 | ||
63 | private static void assertSubstring(String msg, String wholeText, String subString) { | |
64 | if (wholeText.contains(subString)) { | |
65 | return; | |
66 | } | |
67 | fail(msg + ". Expecting " + subString + " but was: " + wholeText); | |
68 | } | |
69 | } |
45 | 45 | |
46 | 46 | @Test public void path() throws IOException { |
47 | 47 | JsonReader reader = factory.create("{\"a\":[2,true,false,null,\"b\",{\"c\":\"d\"},[3]]}"); |
48 | assertEquals("$", reader.getPath()); | |
49 | reader.beginObject(); | |
48 | assertEquals("$", reader.getPreviousPath()); | |
49 | assertEquals("$", reader.getPath()); | |
50 | reader.beginObject(); | |
51 | assertEquals("$.", reader.getPreviousPath()); | |
50 | 52 | assertEquals("$.", reader.getPath()); |
51 | 53 | reader.nextName(); |
52 | assertEquals("$.a", reader.getPath()); | |
53 | reader.beginArray(); | |
54 | assertEquals("$.a", reader.getPreviousPath()); | |
55 | assertEquals("$.a", reader.getPath()); | |
56 | reader.beginArray(); | |
57 | assertEquals("$.a[0]", reader.getPreviousPath()); | |
54 | 58 | assertEquals("$.a[0]", reader.getPath()); |
55 | 59 | reader.nextInt(); |
60 | assertEquals("$.a[0]", reader.getPreviousPath()); | |
56 | 61 | assertEquals("$.a[1]", reader.getPath()); |
57 | 62 | reader.nextBoolean(); |
63 | assertEquals("$.a[1]", reader.getPreviousPath()); | |
58 | 64 | assertEquals("$.a[2]", reader.getPath()); |
59 | 65 | reader.nextBoolean(); |
66 | assertEquals("$.a[2]", reader.getPreviousPath()); | |
60 | 67 | assertEquals("$.a[3]", reader.getPath()); |
61 | 68 | reader.nextNull(); |
69 | assertEquals("$.a[3]", reader.getPreviousPath()); | |
62 | 70 | assertEquals("$.a[4]", reader.getPath()); |
63 | 71 | reader.nextString(); |
72 | assertEquals("$.a[4]", reader.getPreviousPath()); | |
64 | 73 | assertEquals("$.a[5]", reader.getPath()); |
65 | 74 | reader.beginObject(); |
75 | assertEquals("$.a[5].", reader.getPreviousPath()); | |
66 | 76 | assertEquals("$.a[5].", reader.getPath()); |
67 | 77 | reader.nextName(); |
78 | assertEquals("$.a[5].c", reader.getPreviousPath()); | |
68 | 79 | assertEquals("$.a[5].c", reader.getPath()); |
69 | 80 | reader.nextString(); |
81 | assertEquals("$.a[5].c", reader.getPreviousPath()); | |
70 | 82 | assertEquals("$.a[5].c", reader.getPath()); |
71 | 83 | reader.endObject(); |
84 | assertEquals("$.a[5]", reader.getPreviousPath()); | |
72 | 85 | assertEquals("$.a[6]", reader.getPath()); |
73 | 86 | reader.beginArray(); |
87 | assertEquals("$.a[6][0]", reader.getPreviousPath()); | |
74 | 88 | assertEquals("$.a[6][0]", reader.getPath()); |
75 | 89 | reader.nextInt(); |
90 | assertEquals("$.a[6][0]", reader.getPreviousPath()); | |
76 | 91 | assertEquals("$.a[6][1]", reader.getPath()); |
77 | 92 | reader.endArray(); |
93 | assertEquals("$.a[6]", reader.getPreviousPath()); | |
78 | 94 | assertEquals("$.a[7]", reader.getPath()); |
79 | 95 | reader.endArray(); |
80 | assertEquals("$.a", reader.getPath()); | |
81 | reader.endObject(); | |
96 | assertEquals("$.a", reader.getPreviousPath()); | |
97 | assertEquals("$.a", reader.getPath()); | |
98 | reader.endObject(); | |
99 | assertEquals("$", reader.getPreviousPath()); | |
82 | 100 | assertEquals("$", reader.getPath()); |
83 | 101 | } |
84 | 102 | |
85 | 103 | @Test public void objectPath() throws IOException { |
86 | 104 | JsonReader reader = factory.create("{\"a\":1,\"b\":2}"); |
87 | assertEquals("$", reader.getPath()); | |
88 | ||
89 | reader.peek(); | |
90 | assertEquals("$", reader.getPath()); | |
91 | reader.beginObject(); | |
105 | assertEquals("$", reader.getPreviousPath()); | |
106 | assertEquals("$", reader.getPath()); | |
107 | ||
108 | reader.peek(); | |
109 | assertEquals("$", reader.getPreviousPath()); | |
110 | assertEquals("$", reader.getPath()); | |
111 | reader.beginObject(); | |
112 | assertEquals("$.", reader.getPreviousPath()); | |
92 | 113 | assertEquals("$.", reader.getPath()); |
93 | 114 | |
94 | 115 | reader.peek(); |
116 | assertEquals("$.", reader.getPreviousPath()); | |
95 | 117 | assertEquals("$.", reader.getPath()); |
96 | 118 | reader.nextName(); |
97 | assertEquals("$.a", reader.getPath()); | |
98 | ||
99 | reader.peek(); | |
100 | assertEquals("$.a", reader.getPath()); | |
101 | reader.nextInt(); | |
102 | assertEquals("$.a", reader.getPath()); | |
103 | ||
104 | reader.peek(); | |
105 | assertEquals("$.a", reader.getPath()); | |
106 | reader.nextName(); | |
107 | assertEquals("$.b", reader.getPath()); | |
108 | ||
109 | reader.peek(); | |
110 | assertEquals("$.b", reader.getPath()); | |
111 | reader.nextInt(); | |
112 | assertEquals("$.b", reader.getPath()); | |
113 | ||
114 | reader.peek(); | |
115 | assertEquals("$.b", reader.getPath()); | |
116 | reader.endObject(); | |
117 | assertEquals("$", reader.getPath()); | |
118 | ||
119 | reader.peek(); | |
119 | assertEquals("$.a", reader.getPreviousPath()); | |
120 | assertEquals("$.a", reader.getPath()); | |
121 | ||
122 | reader.peek(); | |
123 | assertEquals("$.a", reader.getPreviousPath()); | |
124 | assertEquals("$.a", reader.getPath()); | |
125 | reader.nextInt(); | |
126 | assertEquals("$.a", reader.getPreviousPath()); | |
127 | assertEquals("$.a", reader.getPath()); | |
128 | ||
129 | reader.peek(); | |
130 | assertEquals("$.a", reader.getPreviousPath()); | |
131 | assertEquals("$.a", reader.getPath()); | |
132 | reader.nextName(); | |
133 | assertEquals("$.b", reader.getPreviousPath()); | |
134 | assertEquals("$.b", reader.getPath()); | |
135 | ||
136 | reader.peek(); | |
137 | assertEquals("$.b", reader.getPreviousPath()); | |
138 | assertEquals("$.b", reader.getPath()); | |
139 | reader.nextInt(); | |
140 | assertEquals("$.b", reader.getPreviousPath()); | |
141 | assertEquals("$.b", reader.getPath()); | |
142 | ||
143 | reader.peek(); | |
144 | assertEquals("$.b", reader.getPreviousPath()); | |
145 | assertEquals("$.b", reader.getPath()); | |
146 | reader.endObject(); | |
147 | assertEquals("$", reader.getPreviousPath()); | |
148 | assertEquals("$", reader.getPath()); | |
149 | ||
150 | reader.peek(); | |
151 | assertEquals("$", reader.getPreviousPath()); | |
120 | 152 | assertEquals("$", reader.getPath()); |
121 | 153 | reader.close(); |
154 | assertEquals("$", reader.getPreviousPath()); | |
122 | 155 | assertEquals("$", reader.getPath()); |
123 | 156 | } |
124 | 157 | |
125 | 158 | @Test public void arrayPath() throws IOException { |
126 | 159 | JsonReader reader = factory.create("[1,2]"); |
127 | assertEquals("$", reader.getPath()); | |
128 | ||
129 | reader.peek(); | |
130 | assertEquals("$", reader.getPath()); | |
131 | reader.beginArray(); | |
160 | assertEquals("$", reader.getPreviousPath()); | |
161 | assertEquals("$", reader.getPath()); | |
162 | ||
163 | reader.peek(); | |
164 | assertEquals("$", reader.getPreviousPath()); | |
165 | assertEquals("$", reader.getPath()); | |
166 | reader.beginArray(); | |
167 | assertEquals("$[0]", reader.getPreviousPath()); | |
132 | 168 | assertEquals("$[0]", reader.getPath()); |
133 | 169 | |
134 | 170 | reader.peek(); |
171 | assertEquals("$[0]", reader.getPreviousPath()); | |
135 | 172 | assertEquals("$[0]", reader.getPath()); |
136 | 173 | reader.nextInt(); |
137 | assertEquals("$[1]", reader.getPath()); | |
138 | ||
139 | reader.peek(); | |
140 | assertEquals("$[1]", reader.getPath()); | |
141 | reader.nextInt(); | |
142 | assertEquals("$[2]", reader.getPath()); | |
143 | ||
144 | reader.peek(); | |
145 | assertEquals("$[2]", reader.getPath()); | |
146 | reader.endArray(); | |
147 | assertEquals("$", reader.getPath()); | |
148 | ||
149 | reader.peek(); | |
174 | assertEquals("$[0]", reader.getPreviousPath()); | |
175 | assertEquals("$[1]", reader.getPath()); | |
176 | ||
177 | reader.peek(); | |
178 | assertEquals("$[0]", reader.getPreviousPath()); | |
179 | assertEquals("$[1]", reader.getPath()); | |
180 | reader.nextInt(); | |
181 | assertEquals("$[1]", reader.getPreviousPath()); | |
182 | assertEquals("$[2]", reader.getPath()); | |
183 | ||
184 | reader.peek(); | |
185 | assertEquals("$[1]", reader.getPreviousPath()); | |
186 | assertEquals("$[2]", reader.getPath()); | |
187 | reader.endArray(); | |
188 | assertEquals("$", reader.getPreviousPath()); | |
189 | assertEquals("$", reader.getPath()); | |
190 | ||
191 | reader.peek(); | |
192 | assertEquals("$", reader.getPreviousPath()); | |
150 | 193 | assertEquals("$", reader.getPath()); |
151 | 194 | reader.close(); |
195 | assertEquals("$", reader.getPreviousPath()); | |
152 | 196 | assertEquals("$", reader.getPath()); |
153 | 197 | } |
154 | 198 | |
159 | 203 | reader.setLenient(true); |
160 | 204 | reader.beginArray(); |
161 | 205 | reader.endArray(); |
162 | assertEquals("$", reader.getPath()); | |
163 | reader.beginArray(); | |
164 | reader.endArray(); | |
206 | assertEquals("$", reader.getPreviousPath()); | |
207 | assertEquals("$", reader.getPath()); | |
208 | reader.beginArray(); | |
209 | reader.endArray(); | |
210 | assertEquals("$", reader.getPreviousPath()); | |
165 | 211 | assertEquals("$", reader.getPath()); |
166 | 212 | } |
167 | 213 | |
170 | 216 | reader.beginArray(); |
171 | 217 | reader.skipValue(); |
172 | 218 | reader.skipValue(); |
219 | assertEquals("$[1]", reader.getPreviousPath()); | |
173 | 220 | assertEquals("$[2]", reader.getPath()); |
174 | 221 | } |
175 | 222 | |
177 | 224 | JsonReader reader = factory.create("{\"a\":1}"); |
178 | 225 | reader.beginObject(); |
179 | 226 | reader.skipValue(); |
227 | assertEquals("$.null", reader.getPreviousPath()); | |
180 | 228 | assertEquals("$.null", reader.getPath()); |
181 | 229 | } |
182 | 230 | |
183 | 231 | @Test public void skipObjectValues() throws IOException { |
184 | 232 | JsonReader reader = factory.create("{\"a\":1,\"b\":2}"); |
185 | 233 | reader.beginObject(); |
234 | assertEquals("$.", reader.getPreviousPath()); | |
186 | 235 | assertEquals("$.", reader.getPath()); |
187 | 236 | reader.nextName(); |
188 | 237 | reader.skipValue(); |
238 | assertEquals("$.null", reader.getPreviousPath()); | |
189 | 239 | assertEquals("$.null", reader.getPath()); |
190 | 240 | reader.nextName(); |
241 | assertEquals("$.b", reader.getPreviousPath()); | |
191 | 242 | assertEquals("$.b", reader.getPath()); |
192 | 243 | } |
193 | 244 | |
195 | 246 | JsonReader reader = factory.create("[[1,2,3],4]"); |
196 | 247 | reader.beginArray(); |
197 | 248 | reader.skipValue(); |
249 | assertEquals("$[0]", reader.getPreviousPath()); | |
198 | 250 | assertEquals("$[1]", reader.getPath()); |
199 | 251 | } |
200 | 252 | |
201 | 253 | @Test public void arrayOfObjects() throws IOException { |
202 | 254 | JsonReader reader = factory.create("[{},{},{}]"); |
203 | 255 | reader.beginArray(); |
256 | assertEquals("$[0]", reader.getPreviousPath()); | |
204 | 257 | assertEquals("$[0]", reader.getPath()); |
205 | 258 | reader.beginObject(); |
259 | assertEquals("$[0].", reader.getPreviousPath()); | |
206 | 260 | assertEquals("$[0].", reader.getPath()); |
207 | 261 | reader.endObject(); |
208 | assertEquals("$[1]", reader.getPath()); | |
209 | reader.beginObject(); | |
262 | assertEquals("$[0]", reader.getPreviousPath()); | |
263 | assertEquals("$[1]", reader.getPath()); | |
264 | reader.beginObject(); | |
265 | assertEquals("$[1].", reader.getPreviousPath()); | |
210 | 266 | assertEquals("$[1].", reader.getPath()); |
211 | 267 | reader.endObject(); |
212 | assertEquals("$[2]", reader.getPath()); | |
213 | reader.beginObject(); | |
268 | assertEquals("$[1]", reader.getPreviousPath()); | |
269 | assertEquals("$[2]", reader.getPath()); | |
270 | reader.beginObject(); | |
271 | assertEquals("$[2].", reader.getPreviousPath()); | |
214 | 272 | assertEquals("$[2].", reader.getPath()); |
215 | 273 | reader.endObject(); |
274 | assertEquals("$[2]", reader.getPreviousPath()); | |
216 | 275 | assertEquals("$[3]", reader.getPath()); |
217 | 276 | reader.endArray(); |
277 | assertEquals("$", reader.getPreviousPath()); | |
218 | 278 | assertEquals("$", reader.getPath()); |
219 | 279 | } |
220 | 280 | |
221 | 281 | @Test public void arrayOfArrays() throws IOException { |
222 | 282 | JsonReader reader = factory.create("[[],[],[]]"); |
223 | 283 | reader.beginArray(); |
284 | assertEquals("$[0]", reader.getPreviousPath()); | |
224 | 285 | assertEquals("$[0]", reader.getPath()); |
225 | 286 | reader.beginArray(); |
287 | assertEquals("$[0][0]", reader.getPreviousPath()); | |
226 | 288 | assertEquals("$[0][0]", reader.getPath()); |
227 | 289 | reader.endArray(); |
228 | assertEquals("$[1]", reader.getPath()); | |
229 | reader.beginArray(); | |
290 | assertEquals("$[0]", reader.getPreviousPath()); | |
291 | assertEquals("$[1]", reader.getPath()); | |
292 | reader.beginArray(); | |
293 | assertEquals("$[1][0]", reader.getPreviousPath()); | |
230 | 294 | assertEquals("$[1][0]", reader.getPath()); |
231 | 295 | reader.endArray(); |
232 | assertEquals("$[2]", reader.getPath()); | |
233 | reader.beginArray(); | |
296 | assertEquals("$[1]", reader.getPreviousPath()); | |
297 | assertEquals("$[2]", reader.getPath()); | |
298 | reader.beginArray(); | |
299 | assertEquals("$[2][0]", reader.getPreviousPath()); | |
234 | 300 | assertEquals("$[2][0]", reader.getPath()); |
235 | 301 | reader.endArray(); |
302 | assertEquals("$[2]", reader.getPreviousPath()); | |
236 | 303 | assertEquals("$[3]", reader.getPath()); |
237 | 304 | reader.endArray(); |
238 | assertEquals("$", reader.getPath()); | |
239 | } | |
240 | ||
241 | enum Factory { | |
305 | assertEquals("$", reader.getPreviousPath()); | |
306 | assertEquals("$", reader.getPath()); | |
307 | } | |
308 | ||
309 | public enum Factory { | |
242 | 310 | STRING_READER { |
243 | 311 | @Override public JsonReader create(String data) { |
244 | 312 | return new JsonReader(new StringReader(data)); |
71 | 71 | assertEquals(JsonToken.END_DOCUMENT, reader.peek()); |
72 | 72 | } |
73 | 73 | |
74 | public void testHasNextEndOfDocument() throws IOException { | |
75 | JsonReader reader = new JsonReader(reader("{}")); | |
76 | reader.beginObject(); | |
77 | reader.endObject(); | |
78 | assertFalse(reader.hasNext()); | |
79 | } | |
80 | ||
74 | 81 | public void testSkipArray() throws IOException { |
75 | 82 | JsonReader reader = new JsonReader(reader( |
76 | 83 | "{\"a\": [\"one\", \"two\", \"three\"], \"b\": 123}")); |
187 | 194 | } catch (IOException expected) { |
188 | 195 | } |
189 | 196 | } |
190 | ||
197 | ||
191 | 198 | @SuppressWarnings("unused") |
192 | 199 | public void testNulls() { |
193 | 200 | try { |
303 | 310 | + "1.7976931348623157E308," |
304 | 311 | + "4.9E-324," |
305 | 312 | + "0.0," |
313 | + "0.00," | |
306 | 314 | + "-0.5," |
307 | 315 | + "2.2250738585072014E-308," |
308 | 316 | + "3.141592653589793," |
309 | + "2.718281828459045]"; | |
317 | + "2.718281828459045," | |
318 | + "0," | |
319 | + "0.01," | |
320 | + "0e0," | |
321 | + "1e+0," | |
322 | + "1e-0," | |
323 | + "1e0000," // leading 0 is allowed for exponent | |
324 | + "1e00001," | |
325 | + "1e+1]"; | |
310 | 326 | JsonReader reader = new JsonReader(reader(json)); |
311 | 327 | reader.beginArray(); |
312 | 328 | assertEquals(-0.0, reader.nextDouble()); |
314 | 330 | assertEquals(1.7976931348623157E308, reader.nextDouble()); |
315 | 331 | assertEquals(4.9E-324, reader.nextDouble()); |
316 | 332 | assertEquals(0.0, reader.nextDouble()); |
333 | assertEquals(0.0, reader.nextDouble()); | |
317 | 334 | assertEquals(-0.5, reader.nextDouble()); |
318 | 335 | assertEquals(2.2250738585072014E-308, reader.nextDouble()); |
319 | 336 | assertEquals(3.141592653589793, reader.nextDouble()); |
320 | 337 | assertEquals(2.718281828459045, reader.nextDouble()); |
338 | assertEquals(0.0, reader.nextDouble()); | |
339 | assertEquals(0.01, reader.nextDouble()); | |
340 | assertEquals(0.0, reader.nextDouble()); | |
341 | assertEquals(1.0, reader.nextDouble()); | |
342 | assertEquals(1.0, reader.nextDouble()); | |
343 | assertEquals(1.0, reader.nextDouble()); | |
344 | assertEquals(10.0, reader.nextDouble()); | |
345 | assertEquals(10.0, reader.nextDouble()); | |
321 | 346 | reader.endArray(); |
322 | 347 | assertEquals(JsonToken.END_DOCUMENT, reader.peek()); |
323 | 348 | } |
466 | 491 | assertNotANumber("-"); |
467 | 492 | assertNotANumber("."); |
468 | 493 | |
494 | // plus sign is not allowed for integer part | |
495 | assertNotANumber("+1"); | |
496 | ||
497 | // leading 0 is not allowed for integer part | |
498 | assertNotANumber("00"); | |
499 | assertNotANumber("01"); | |
500 | ||
469 | 501 | // exponent lacks digit |
470 | 502 | assertNotANumber("e"); |
471 | 503 | assertNotANumber("0e"); |
500 | 532 | } |
501 | 533 | |
502 | 534 | private void assertNotANumber(String s) throws IOException { |
503 | JsonReader reader = new JsonReader(reader("[" + s + "]")); | |
504 | reader.setLenient(true); | |
505 | reader.beginArray(); | |
535 | JsonReader reader = new JsonReader(reader(s)); | |
536 | reader.setLenient(true); | |
506 | 537 | assertEquals(JsonToken.STRING, reader.peek()); |
507 | 538 | assertEquals(s, reader.nextString()); |
508 | reader.endArray(); | |
539 | ||
540 | JsonReader strictReader = new JsonReader(reader(s)); | |
541 | try { | |
542 | strictReader.nextDouble(); | |
543 | fail("Should have failed reading " + s + " as double"); | |
544 | } catch (MalformedJsonException e) { | |
545 | } | |
509 | 546 | } |
510 | 547 | |
511 | 548 | public void testPeekingUnquotedStringsPrefixedWithIntegers() throws IOException { |
560 | 597 | } catch (NumberFormatException expected) { |
561 | 598 | } |
562 | 599 | } |
563 | ||
600 | ||
564 | 601 | /** |
565 | 602 | * Issue 1053, negative zero. |
566 | 603 | * @throws Exception |
567 | 604 | */ |
568 | 605 | public void testNegativeZero() throws Exception { |
569 | JsonReader reader = new JsonReader(reader("[-0]")); | |
570 | reader.setLenient(false); | |
571 | reader.beginArray(); | |
572 | assertEquals(NUMBER, reader.peek()); | |
573 | assertEquals("-0", reader.nextString()); | |
606 | JsonReader reader = new JsonReader(reader("[-0]")); | |
607 | reader.setLenient(false); | |
608 | reader.beginArray(); | |
609 | assertEquals(NUMBER, reader.peek()); | |
610 | assertEquals("-0", reader.nextString()); | |
574 | 611 | } |
575 | 612 | |
576 | 613 | /** |
1729 | 1766 | } |
1730 | 1767 | } |
1731 | 1768 | |
1769 | /** | |
1770 | * Regression test for an issue with buffer filling and consumeNonExecutePrefix. | |
1771 | */ | |
1772 | public void testReadAcrossBuffers() throws IOException { | |
1773 | StringBuilder sb = new StringBuilder("#"); | |
1774 | for (int i = 0; i < JsonReader.BUFFER_SIZE - 3; i++) { | |
1775 | sb.append(' '); | |
1776 | } | |
1777 | sb.append("\n)]}'\n3"); | |
1778 | JsonReader reader = new JsonReader(reader(sb.toString())); | |
1779 | reader.setLenient(true); | |
1780 | JsonToken token = reader.peek(); | |
1781 | assertEquals(JsonToken.NUMBER, token); | |
1782 | } | |
1783 | ||
1732 | 1784 | private void assertDocument(String document, Object... expectations) throws IOException { |
1733 | 1785 | JsonReader reader = new JsonReader(reader(document)); |
1734 | 1786 | reader.setLenient(true); |
15 | 15 | |
16 | 16 | package com.google.gson.stream; |
17 | 17 | |
18 | import junit.framework.TestCase; | |
19 | ||
18 | import com.google.gson.internal.LazilyParsedNumber; | |
20 | 19 | import java.io.IOException; |
21 | 20 | import java.io.StringWriter; |
22 | 21 | import java.math.BigDecimal; |
23 | 22 | import java.math.BigInteger; |
23 | import junit.framework.TestCase; | |
24 | 24 | |
25 | 25 | @SuppressWarnings("resource") |
26 | 26 | public final class JsonWriterTest extends TestCase { |
179 | 179 | jsonWriter.value(Double.NaN); |
180 | 180 | fail(); |
181 | 181 | } catch (IllegalArgumentException expected) { |
182 | assertEquals("Numeric values must be finite, but was NaN", expected.getMessage()); | |
182 | 183 | } |
183 | 184 | try { |
184 | 185 | jsonWriter.value(Double.NEGATIVE_INFINITY); |
185 | 186 | fail(); |
186 | 187 | } catch (IllegalArgumentException expected) { |
188 | assertEquals("Numeric values must be finite, but was -Infinity", expected.getMessage()); | |
187 | 189 | } |
188 | 190 | try { |
189 | 191 | jsonWriter.value(Double.POSITIVE_INFINITY); |
190 | 192 | fail(); |
191 | 193 | } catch (IllegalArgumentException expected) { |
192 | } | |
193 | } | |
194 | ||
195 | public void testNonFiniteBoxedDoubles() throws IOException { | |
194 | assertEquals("Numeric values must be finite, but was Infinity", expected.getMessage()); | |
195 | } | |
196 | } | |
197 | ||
198 | public void testNonFiniteNumbers() throws IOException { | |
196 | 199 | StringWriter stringWriter = new StringWriter(); |
197 | 200 | JsonWriter jsonWriter = new JsonWriter(stringWriter); |
198 | 201 | jsonWriter.beginArray(); |
200 | 203 | jsonWriter.value(Double.valueOf(Double.NaN)); |
201 | 204 | fail(); |
202 | 205 | } catch (IllegalArgumentException expected) { |
206 | assertEquals("Numeric values must be finite, but was NaN", expected.getMessage()); | |
203 | 207 | } |
204 | 208 | try { |
205 | 209 | jsonWriter.value(Double.valueOf(Double.NEGATIVE_INFINITY)); |
206 | 210 | fail(); |
207 | 211 | } catch (IllegalArgumentException expected) { |
212 | assertEquals("Numeric values must be finite, but was -Infinity", expected.getMessage()); | |
208 | 213 | } |
209 | 214 | try { |
210 | 215 | jsonWriter.value(Double.valueOf(Double.POSITIVE_INFINITY)); |
211 | 216 | fail(); |
212 | 217 | } catch (IllegalArgumentException expected) { |
218 | assertEquals("Numeric values must be finite, but was Infinity", expected.getMessage()); | |
219 | } | |
220 | try { | |
221 | jsonWriter.value(new LazilyParsedNumber("Infinity")); | |
222 | fail(); | |
223 | } catch (IllegalArgumentException expected) { | |
224 | assertEquals("Numeric values must be finite, but was Infinity", expected.getMessage()); | |
213 | 225 | } |
214 | 226 | } |
215 | 227 | |
225 | 237 | assertEquals("[NaN,-Infinity,Infinity]", stringWriter.toString()); |
226 | 238 | } |
227 | 239 | |
228 | public void testNonFiniteBoxedDoublesWhenLenient() throws IOException { | |
240 | public void testNonFiniteNumbersWhenLenient() throws IOException { | |
229 | 241 | StringWriter stringWriter = new StringWriter(); |
230 | 242 | JsonWriter jsonWriter = new JsonWriter(stringWriter); |
231 | 243 | jsonWriter.setLenient(true); |
233 | 245 | jsonWriter.value(Double.valueOf(Double.NaN)); |
234 | 246 | jsonWriter.value(Double.valueOf(Double.NEGATIVE_INFINITY)); |
235 | 247 | jsonWriter.value(Double.valueOf(Double.POSITIVE_INFINITY)); |
236 | jsonWriter.endArray(); | |
237 | assertEquals("[NaN,-Infinity,Infinity]", stringWriter.toString()); | |
248 | jsonWriter.value(new LazilyParsedNumber("Infinity")); | |
249 | jsonWriter.endArray(); | |
250 | assertEquals("[NaN,-Infinity,Infinity,Infinity]", stringWriter.toString()); | |
238 | 251 | } |
239 | 252 | |
240 | 253 | public void testDoubles() throws IOException { |
295 | 308 | + "9223372036854775808," |
296 | 309 | + "-9223372036854775809," |
297 | 310 | + "3.141592653589793238462643383]", stringWriter.toString()); |
311 | } | |
312 | ||
313 | /** | |
314 | * Tests writing {@code Number} instances which are not one of the standard JDK ones. | |
315 | */ | |
316 | public void testNumbersCustomClass() throws IOException { | |
317 | String[] validNumbers = { | |
318 | "-0.0", | |
319 | "1.0", | |
320 | "1.7976931348623157E308", | |
321 | "4.9E-324", | |
322 | "0.0", | |
323 | "0.00", | |
324 | "-0.5", | |
325 | "2.2250738585072014E-308", | |
326 | "3.141592653589793", | |
327 | "2.718281828459045", | |
328 | "0", | |
329 | "0.01", | |
330 | "0e0", | |
331 | "1e+0", | |
332 | "1e-0", | |
333 | "1e0000", // leading 0 is allowed for exponent | |
334 | "1e00001", | |
335 | "1e+1", | |
336 | }; | |
337 | ||
338 | for (String validNumber : validNumbers) { | |
339 | StringWriter stringWriter = new StringWriter(); | |
340 | JsonWriter jsonWriter = new JsonWriter(stringWriter); | |
341 | ||
342 | jsonWriter.value(new LazilyParsedNumber(validNumber)); | |
343 | jsonWriter.close(); | |
344 | ||
345 | assertEquals(validNumber, stringWriter.toString()); | |
346 | } | |
347 | } | |
348 | ||
349 | public void testMalformedNumbers() throws IOException { | |
350 | String[] malformedNumbers = { | |
351 | "some text", | |
352 | "", | |
353 | ".", | |
354 | "00", | |
355 | "01", | |
356 | "-00", | |
357 | "-", | |
358 | "--1", | |
359 | "+1", // plus sign is not allowed for integer part | |
360 | "+", | |
361 | "1,0", | |
362 | "1,000", | |
363 | "0.", // decimal digit is required | |
364 | ".1", // integer part is required | |
365 | "e1", | |
366 | ".e1", | |
367 | ".1e1", | |
368 | "1e-", | |
369 | "1e+", | |
370 | "1e--1", | |
371 | "1e+-1", | |
372 | "1e1e1", | |
373 | "1+e1", | |
374 | "1e1.0", | |
375 | }; | |
376 | ||
377 | for (String malformedNumber : malformedNumbers) { | |
378 | JsonWriter jsonWriter = new JsonWriter(new StringWriter()); | |
379 | try { | |
380 | jsonWriter.value(new LazilyParsedNumber(malformedNumber)); | |
381 | fail("Should have failed writing malformed number: " + malformedNumber); | |
382 | } catch (IllegalArgumentException e) { | |
383 | assertEquals("String created by class com.google.gson.internal.LazilyParsedNumber is not a valid JSON number: " + malformedNumber, e.getMessage()); | |
384 | } | |
385 | } | |
298 | 386 | } |
299 | 387 | |
300 | 388 | public void testBooleans() throws IOException { |
2 | 2 | -optimizations !code/simplification/arithmetic,!code/simplification/cast,!field/*,!class/merging/* |
3 | 3 | -optimizationpasses 5 |
4 | 4 | -allowaccessmodification |
5 | # On Windows mixed case class names might cause problems | |
6 | -dontusemixedcaseclassnames | |
5 | 7 | -keepattributes *Annotation*,Signature,InnerClasses,EnclosingMethod |
6 | 8 | -keepclassmembers enum * { |
7 | 9 | public static **[] values(); |
16 | 18 | |
17 | 19 | -dontwarn com.google.gson.functional.EnumWithObfuscatedTest |
18 | 20 | -dontwarn junit.framework.TestCase |
19 | ||
21 | # Ignore notes about duplicate JDK classes | |
22 | -dontnote module-info,jdk.internal.** |
0 | # metrics | |
1 | ||
2 | This Maven module contains the source code for running internal benchmark tests against Gson. |
0 | <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | |
1 | xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> | |
0 | <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> | |
2 | 1 | <modelVersion>4.0.0</modelVersion> |
3 | <groupId>com.google.code.gson</groupId> | |
2 | <parent> | |
3 | <groupId>com.google.code.gson</groupId> | |
4 | <artifactId>gson-parent</artifactId> | |
5 | <version>2.9.0</version> | |
6 | </parent> | |
7 | ||
4 | 8 | <artifactId>gson-metrics</artifactId> |
5 | <packaging>jar</packaging> | |
6 | <version>1.0-SNAPSHOT</version> | |
7 | 9 | <inceptionYear>2011</inceptionYear> |
8 | 10 | <name>Gson Metrics</name> |
9 | <parent> | |
10 | <groupId>org.sonatype.oss</groupId> | |
11 | <artifactId>oss-parent</artifactId> | |
12 | <version>5</version> | |
13 | </parent> | |
14 | <url>http://code.google.com/p/google-gson/</url> | |
15 | 11 | <description>Performance Metrics for Google Gson library</description> |
16 | <properties> | |
17 | <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> | |
18 | </properties> | |
12 | ||
19 | 13 | <licenses> |
20 | 14 | <license> |
21 | <name>The Apache Software License, Version 2.0</name> | |
22 | <url>http://www.apache.org/licenses/LICENSE-2.0.txt</url> | |
23 | <distribution>repo</distribution> | |
15 | <name>Apache-2.0</name> | |
16 | <url>https://www.apache.org/licenses/LICENSE-2.0.txt</url> | |
24 | 17 | </license> |
25 | 18 | </licenses> |
26 | <scm> | |
27 | <connection>scm:svn:http://google-gson.googlecode.com/svn/trunk/metrics</connection> | |
28 | <developerConnection>scm:svn:https://google-gson.googlecode.com/svn/trunk/metrics</developerConnection> | |
29 | <url>http://google-gson.codegoogle.com/svn/trunk/metrics</url> | |
30 | </scm> | |
31 | <issueManagement> | |
32 | <system>Google Code Issue Tracking</system> | |
33 | <url>http://code.google.com/p/google-gson/issues/list</url> | |
34 | </issueManagement> | |
19 | ||
35 | 20 | <organization> |
36 | 21 | <name>Google, Inc.</name> |
37 | <url>http://www.google.com</url> | |
22 | <url>https://www.google.com</url> | |
38 | 23 | </organization> |
24 | ||
39 | 25 | <dependencies> |
40 | 26 | <dependency> |
41 | 27 | <groupId>com.google.code.gson</groupId> |
42 | 28 | <artifactId>gson</artifactId> |
43 | <version>1.7.2-SNAPSHOT</version> | |
29 | <version>${project.parent.version}</version> | |
44 | 30 | </dependency> |
45 | 31 | <dependency> |
46 | <groupId>com.google.code.caliper</groupId> | |
47 | <artifactId>caliper</artifactId> | |
48 | <version>1.0-SNAPSHOT</version> | |
32 | <groupId>com.fasterxml.jackson.core</groupId> | |
33 | <artifactId>jackson-databind</artifactId> | |
34 | <version>2.13.1</version> | |
49 | 35 | </dependency> |
50 | 36 | <dependency> |
51 | <groupId>junit</groupId> | |
52 | <artifactId>junit</artifactId> | |
53 | <version>4.13.1</version> | |
54 | <scope>test</scope> | |
37 | <groupId>com.google.caliper</groupId> | |
38 | <artifactId>caliper</artifactId> | |
39 | <version>1.0-beta-3</version> | |
55 | 40 | </dependency> |
56 | 41 | </dependencies> |
42 | ||
57 | 43 | <build> |
58 | <defaultGoal>package</defaultGoal> | |
59 | <plugins> | |
60 | <plugin> | |
61 | <groupId>org.apache.maven.plugins</groupId> | |
62 | <artifactId>maven-compiler-plugin</artifactId> | |
63 | <version>2.3.2</version> | |
64 | <configuration> | |
65 | <source>1.6</source> | |
66 | <target>1.6</target> | |
67 | </configuration> | |
68 | </plugin> | |
69 | <plugin> | |
70 | <groupId>org.apache.maven.plugins</groupId> | |
71 | <artifactId>maven-eclipse-plugin</artifactId> | |
72 | <version>2.8</version> | |
73 | <configuration> | |
74 | <downloadSources>true</downloadSources> | |
75 | <downloadJavadocs>true</downloadJavadocs> | |
76 | <workspace>../eclipse-ws/</workspace> | |
77 | <workspaceCodeStylesURL> | |
78 | file:///${basedir}/../lib/gson-formatting-styles.xml | |
79 | </workspaceCodeStylesURL> | |
80 | </configuration> | |
81 | </plugin> | |
82 | <plugin> | |
83 | <groupId>org.apache.maven.plugins</groupId> | |
84 | <artifactId>maven-release-plugin</artifactId> | |
85 | <version>2.1</version> | |
86 | <configuration> | |
87 | <arguments>-DenableCiProfile=true</arguments> | |
88 | <tagBase>https://google-gson.googlecode.com/svn/tags/</tagBase> | |
89 | </configuration> | |
90 | </plugin> | |
91 | <plugin> | |
92 | <groupId>org.apache.maven.plugins</groupId> | |
93 | <artifactId>maven-source-plugin</artifactId> | |
94 | <version>2.1.2</version> | |
95 | <executions> | |
96 | <execution> | |
97 | <id>attach-sources</id> | |
98 | <goals> | |
99 | <goal>jar</goal> | |
100 | </goals> | |
101 | </execution> | |
102 | </executions> | |
103 | </plugin> | |
104 | <plugin> | |
105 | <groupId>org.apache.maven.plugins</groupId> | |
106 | <artifactId>maven-javadoc-plugin</artifactId> | |
107 | <version>2.7</version> | |
108 | <executions> | |
109 | <execution> | |
110 | <id>attach-javadocs</id> | |
111 | <goals> | |
112 | <goal>jar</goal> | |
113 | </goals> | |
114 | </execution> | |
115 | </executions> | |
116 | <configuration> | |
117 | <links> | |
118 | <link>http://download.oracle.com/javase/1.5.0/docs/api/</link> | |
119 | </links> | |
120 | <version>true</version> | |
121 | <show>public</show> | |
122 | </configuration> | |
123 | </plugin> | |
124 | </plugins> | |
44 | <pluginManagement> | |
45 | <plugins> | |
46 | <plugin> | |
47 | <groupId>org.apache.maven.plugins</groupId> | |
48 | <artifactId>maven-deploy-plugin</artifactId> | |
49 | <version>3.0.0-M2</version> | |
50 | <configuration> | |
51 | <!-- Not deployed --> | |
52 | <skip>true</skip> | |
53 | </configuration> | |
54 | </plugin> | |
55 | </plugins> | |
56 | </pluginManagement> | |
125 | 57 | </build> |
58 | ||
126 | 59 | <developers> |
127 | 60 | <developer> |
128 | 61 | <name>Inderjeet Singh</name> |
+7
-9
14 | 14 | */ |
15 | 15 | package com.google.gson.metrics; |
16 | 16 | |
17 | import com.google.caliper.BeforeExperiment; | |
18 | import com.google.gson.Gson; | |
19 | import com.google.gson.stream.JsonReader; | |
17 | 20 | import java.io.IOException; |
18 | 21 | import java.io.StringReader; |
19 | 22 | import java.lang.reflect.Field; |
20 | ||
21 | import com.google.caliper.Runner; | |
22 | import com.google.caliper.SimpleBenchmark; | |
23 | import com.google.gson.Gson; | |
24 | import com.google.gson.stream.JsonReader; | |
25 | 23 | |
26 | 24 | /** |
27 | 25 | * Caliper based micro benchmarks for Gson |
30 | 28 | * @author Jesse Wilson |
31 | 29 | * @author Joel Leitch |
32 | 30 | */ |
33 | public class BagOfPrimitivesDeserializationBenchmark extends SimpleBenchmark { | |
31 | public class BagOfPrimitivesDeserializationBenchmark { | |
34 | 32 | |
35 | 33 | private Gson gson; |
36 | 34 | private String json; |
37 | 35 | |
38 | 36 | public static void main(String[] args) { |
39 | Runner.main(BagOfPrimitivesDeserializationBenchmark.class, args); | |
37 | NonUploadingCaliperRunner.run(BagOfPrimitivesDeserializationBenchmark.class, args); | |
40 | 38 | } |
41 | 39 | |
42 | @Override | |
43 | protected void setUp() throws Exception { | |
40 | @BeforeExperiment | |
41 | void setUp() throws Exception { | |
44 | 42 | this.gson = new Gson(); |
45 | 43 | BagOfPrimitives bag = new BagOfPrimitives(10L, 1, false, "foo"); |
46 | 44 | this.json = gson.toJson(bag); |
+8
-10
14 | 14 | */ |
15 | 15 | package com.google.gson.metrics; |
16 | 16 | |
17 | import com.google.caliper.BeforeExperiment; | |
18 | import com.google.gson.Gson; | |
19 | import com.google.gson.reflect.TypeToken; | |
20 | import com.google.gson.stream.JsonReader; | |
17 | 21 | import java.io.IOException; |
18 | 22 | import java.io.StringReader; |
19 | 23 | import java.lang.reflect.Field; |
21 | 25 | import java.util.ArrayList; |
22 | 26 | import java.util.List; |
23 | 27 | |
24 | import com.google.caliper.Runner; | |
25 | import com.google.caliper.SimpleBenchmark; | |
26 | import com.google.gson.Gson; | |
27 | import com.google.gson.reflect.TypeToken; | |
28 | import com.google.gson.stream.JsonReader; | |
29 | ||
30 | 28 | /** |
31 | 29 | * Caliper based micro benchmarks for Gson |
32 | 30 | * |
33 | 31 | * @author Inderjeet Singh |
34 | 32 | */ |
35 | public class CollectionsDeserializationBenchmark extends SimpleBenchmark { | |
33 | public class CollectionsDeserializationBenchmark { | |
36 | 34 | |
37 | 35 | private static final Type LIST_TYPE = new TypeToken<List<BagOfPrimitives>>(){}.getType(); |
38 | 36 | private Gson gson; |
39 | 37 | private String json; |
40 | 38 | |
41 | 39 | public static void main(String[] args) { |
42 | Runner.main(CollectionsDeserializationBenchmark.class, args); | |
40 | NonUploadingCaliperRunner.run(CollectionsDeserializationBenchmark.class, args); | |
43 | 41 | } |
44 | 42 | |
45 | @Override | |
46 | protected void setUp() throws Exception { | |
43 | @BeforeExperiment | |
44 | void setUp() throws Exception { | |
47 | 45 | this.gson = new Gson(); |
48 | 46 | List<BagOfPrimitives> bags = new ArrayList<BagOfPrimitives>(); |
49 | 47 | for (int i = 0; i < 100; ++i) { |
0 | package com.google.gson.metrics; | |
1 | ||
2 | import com.google.caliper.runner.CaliperMain; | |
3 | ||
4 | class NonUploadingCaliperRunner { | |
5 | private static String[] concat(String first, String... others) { | |
6 | if (others.length == 0) { | |
7 | return new String[] { first }; | |
8 | } else { | |
9 | String[] result = new String[others.length + 1]; | |
10 | result[0] = first; | |
11 | System.arraycopy(others, 0, result, 1, others.length); | |
12 | return result; | |
13 | } | |
14 | } | |
15 | ||
16 | public static void run(Class<?> c, String[] args) { | |
17 | // Disable result upload; Caliper uploads results to webapp by default, see https://github.com/google/caliper/issues/356 | |
18 | CaliperMain.main(c, concat("-Cresults.upload.options.url=", args)); | |
19 | } | |
20 | } |
15 | 15 | |
16 | 16 | package com.google.gson.metrics; |
17 | 17 | |
18 | import com.fasterxml.jackson.annotation.JsonProperty; | |
19 | import com.fasterxml.jackson.core.JsonFactory; | |
20 | import com.fasterxml.jackson.core.JsonFactoryBuilder; | |
21 | import com.fasterxml.jackson.core.type.TypeReference; | |
22 | import com.fasterxml.jackson.databind.DeserializationFeature; | |
23 | import com.fasterxml.jackson.databind.MapperFeature; | |
24 | import com.fasterxml.jackson.databind.ObjectMapper; | |
25 | import com.fasterxml.jackson.databind.json.JsonMapper; | |
26 | import com.google.caliper.BeforeExperiment; | |
18 | 27 | import com.google.caliper.Param; |
19 | import com.google.caliper.Runner; | |
20 | import com.google.caliper.SimpleBenchmark; | |
21 | 28 | import com.google.gson.Gson; |
22 | 29 | import com.google.gson.GsonBuilder; |
23 | 30 | import com.google.gson.JsonParser; |
24 | 31 | import com.google.gson.annotations.SerializedName; |
25 | 32 | import com.google.gson.reflect.TypeToken; |
26 | 33 | import java.io.CharArrayReader; |
34 | import java.io.File; | |
27 | 35 | import java.io.IOException; |
28 | import java.io.InputStream; | |
29 | 36 | import java.io.InputStreamReader; |
30 | 37 | import java.io.Reader; |
31 | 38 | import java.io.StringWriter; |
32 | 39 | import java.lang.reflect.Type; |
40 | import java.net.URL; | |
33 | 41 | import java.text.SimpleDateFormat; |
34 | 42 | import java.util.Date; |
35 | 43 | import java.util.List; |
36 | import org.codehaus.jackson.JsonFactory; | |
37 | import org.codehaus.jackson.annotate.JsonProperty; | |
38 | import org.codehaus.jackson.map.DeserializationConfig; | |
39 | import org.codehaus.jackson.map.ObjectMapper; | |
40 | import org.codehaus.jackson.type.TypeReference; | |
44 | import java.util.Locale; | |
45 | import java.util.zip.ZipEntry; | |
46 | import java.util.zip.ZipFile; | |
41 | 47 | |
42 | 48 | /** |
43 | 49 | * Measure Gson and Jackson parsing and binding performance. |
46 | 52 | * That file contains Twitter feed data, which is representative of what |
47 | 53 | * applications will be parsing. |
48 | 54 | */ |
49 | public final class ParseBenchmark extends SimpleBenchmark { | |
55 | public final class ParseBenchmark { | |
50 | 56 | @Param Document document; |
51 | 57 | @Param Api api; |
52 | 58 | |
101 | 107 | private char[] text; |
102 | 108 | private Parser parser; |
103 | 109 | |
104 | @Override protected void setUp() throws Exception { | |
105 | text = resourceToString("/" + document.name() + ".json").toCharArray(); | |
110 | @BeforeExperiment | |
111 | void setUp() throws Exception { | |
112 | text = resourceToString(document.name() + ".json").toCharArray(); | |
106 | 113 | parser = api.newParser(); |
107 | 114 | } |
108 | 115 | |
112 | 119 | } |
113 | 120 | } |
114 | 121 | |
115 | private static String resourceToString(String path) throws Exception { | |
116 | InputStream in = ParseBenchmark.class.getResourceAsStream(path); | |
117 | if (in == null) { | |
118 | throw new IllegalArgumentException("No such file: " + path); | |
119 | } | |
120 | ||
121 | Reader reader = new InputStreamReader(in, "UTF-8"); | |
122 | char[] buffer = new char[8192]; | |
123 | StringWriter writer = new StringWriter(); | |
124 | int count; | |
125 | while ((count = reader.read(buffer)) != -1) { | |
126 | writer.write(buffer, 0, count); | |
127 | } | |
128 | reader.close(); | |
129 | return writer.toString(); | |
122 | private static File getResourceFile(String path) throws Exception { | |
123 | URL url = ParseBenchmark.class.getResource(path); | |
124 | if (url == null) { | |
125 | throw new IllegalArgumentException("Resource " + path + " does not exist"); | |
126 | } | |
127 | File file = new File(url.toURI()); | |
128 | if (!file.isFile()) { | |
129 | throw new IllegalArgumentException("Resource " + path + " is not a file"); | |
130 | } | |
131 | return file; | |
132 | } | |
133 | ||
134 | private static String resourceToString(String fileName) throws Exception { | |
135 | ZipFile zipFile = new ZipFile(getResourceFile("/ParseBenchmarkData.zip")); | |
136 | try { | |
137 | ZipEntry zipEntry = zipFile.getEntry(fileName); | |
138 | Reader reader = new InputStreamReader(zipFile.getInputStream(zipEntry)); | |
139 | char[] buffer = new char[8192]; | |
140 | StringWriter writer = new StringWriter(); | |
141 | int count; | |
142 | while ((count = reader.read(buffer)) != -1) { | |
143 | writer.write(buffer, 0, count); | |
144 | } | |
145 | reader.close(); | |
146 | return writer.toString(); | |
147 | ||
148 | } finally { | |
149 | zipFile.close(); | |
150 | } | |
130 | 151 | } |
131 | 152 | |
132 | 153 | public static void main(String[] args) throws Exception { |
133 | Runner.main(ParseBenchmark.class, args); | |
154 | NonUploadingCaliperRunner.run(ParseBenchmark.class, args); | |
134 | 155 | } |
135 | 156 | |
136 | 157 | interface Parser { |
138 | 159 | } |
139 | 160 | |
140 | 161 | private static class GsonStreamParser implements Parser { |
162 | @Override | |
141 | 163 | public void parse(char[] data, Document document) throws Exception { |
142 | 164 | com.google.gson.stream.JsonReader jsonReader |
143 | 165 | = new com.google.gson.stream.JsonReader(new CharArrayReader(data)); |
185 | 207 | } |
186 | 208 | |
187 | 209 | private static class GsonSkipParser implements Parser { |
210 | @Override | |
188 | 211 | public void parse(char[] data, Document document) throws Exception { |
189 | 212 | com.google.gson.stream.JsonReader jsonReader |
190 | 213 | = new com.google.gson.stream.JsonReader(new CharArrayReader(data)); |
194 | 217 | } |
195 | 218 | |
196 | 219 | private static class JacksonStreamParser implements Parser { |
197 | public void parse(char[] data, Document document) throws Exception { | |
198 | JsonFactory jsonFactory = new JsonFactory(); | |
199 | org.codehaus.jackson.JsonParser jp = jsonFactory.createJsonParser(new CharArrayReader(data)); | |
200 | jp.configure(org.codehaus.jackson.JsonParser.Feature.CANONICALIZE_FIELD_NAMES, false); | |
220 | @Override | |
221 | public void parse(char[] data, Document document) throws Exception { | |
222 | JsonFactory jsonFactory = new JsonFactoryBuilder().configure(JsonFactory.Feature.CANONICALIZE_FIELD_NAMES, false).build(); | |
223 | com.fasterxml.jackson.core.JsonParser jp = jsonFactory.createParser(new CharArrayReader(data)); | |
201 | 224 | int depth = 0; |
202 | 225 | do { |
203 | 226 | switch (jp.nextToken()) { |
226 | 249 | } |
227 | 250 | |
228 | 251 | private static class GsonDomParser implements Parser { |
229 | public void parse(char[] data, Document document) throws Exception { | |
230 | new JsonParser().parse(new CharArrayReader(data)); | |
252 | @Override | |
253 | public void parse(char[] data, Document document) throws Exception { | |
254 | JsonParser.parseReader(new CharArrayReader(data)); | |
231 | 255 | } |
232 | 256 | } |
233 | 257 | |
236 | 260 | .setDateFormat("EEE MMM dd HH:mm:ss Z yyyy") |
237 | 261 | .create(); |
238 | 262 | |
263 | @Override | |
239 | 264 | public void parse(char[] data, Document document) throws Exception { |
240 | 265 | gson.fromJson(new CharArrayReader(data), document.gsonType); |
241 | 266 | } |
242 | 267 | } |
243 | 268 | |
244 | 269 | private static class JacksonBindParser implements Parser { |
245 | private static ObjectMapper mapper = new ObjectMapper(); | |
270 | private static final ObjectMapper mapper; | |
246 | 271 | |
247 | 272 | static { |
248 | mapper.configure(DeserializationConfig.Feature.FAIL_ON_UNKNOWN_PROPERTIES, false); | |
249 | mapper.configure(DeserializationConfig.Feature.AUTO_DETECT_FIELDS, true); | |
250 | mapper.setDateFormat(new SimpleDateFormat("EEE MMM dd HH:mm:ss Z yyyy")); | |
251 | } | |
252 | ||
273 | mapper = JsonMapper.builder() | |
274 | .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false) | |
275 | .configure(MapperFeature.AUTO_DETECT_FIELDS, true) | |
276 | .build(); | |
277 | mapper.setDateFormat(new SimpleDateFormat("EEE MMM dd HH:mm:ss Z yyyy", Locale.ENGLISH)); | |
278 | } | |
279 | ||
280 | @Override | |
253 | 281 | public void parse(char[] data, Document document) throws Exception { |
254 | 282 | mapper.readValue(new CharArrayReader(data), document.jacksonType); |
255 | 283 | } |
Binary diff not shown
14 | 14 | */ |
15 | 15 | package com.google.gson.metrics; |
16 | 16 | |
17 | import com.google.caliper.BeforeExperiment; | |
17 | 18 | import com.google.caliper.Param; |
18 | import com.google.caliper.Runner; | |
19 | import com.google.caliper.SimpleBenchmark; | |
20 | 19 | import com.google.gson.Gson; |
21 | 20 | import com.google.gson.GsonBuilder; |
22 | 21 | |
27 | 26 | * @author Jesse Wilson |
28 | 27 | * @author Joel Leitch |
29 | 28 | */ |
30 | public class SerializationBenchmark extends SimpleBenchmark { | |
29 | public class SerializationBenchmark { | |
31 | 30 | |
32 | 31 | private Gson gson; |
33 | 32 | private BagOfPrimitives bag; |
35 | 34 | private boolean pretty; |
36 | 35 | |
37 | 36 | public static void main(String[] args) { |
38 | Runner.main(SerializationBenchmark.class, args); | |
37 | NonUploadingCaliperRunner.run(SerializationBenchmark.class, args); | |
39 | 38 | } |
40 | 39 | |
41 | @Override | |
42 | protected void setUp() throws Exception { | |
40 | @BeforeExperiment | |
41 | void setUp() throws Exception { | |
43 | 42 | this.gson = pretty ? new GsonBuilder().setPrettyPrinting().create() : new Gson(); |
44 | 43 | this.bag = new BagOfPrimitives(10L, 1, false, "foo"); |
45 | 44 | } |
Binary diff not shown
10 | 10 | |
11 | 11 | <groupId>com.google.code.gson</groupId> |
12 | 12 | <artifactId>gson-parent</artifactId> |
13 | <version>2.8.8</version> | |
13 | <version>2.9.0</version> | |
14 | 14 | <packaging>pom</packaging> |
15 | 15 | |
16 | 16 | <name>Gson Parent</name> |
19 | 19 | |
20 | 20 | <modules> |
21 | 21 | <module>gson</module> |
22 | <module>extras</module> | |
23 | <module>codegen</module> | |
24 | <module>metrics</module> | |
25 | <module>proto</module> | |
22 | 26 | </modules> |
23 | 27 | |
24 | 28 | <properties> |
25 | 29 | <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> |
26 | <java.version>1.6</java.version> | |
30 | <javaVersion>7</javaVersion> | |
27 | 31 | </properties> |
28 | 32 | |
29 | 33 | <scm> |
30 | 34 | <url>https://github.com/google/gson/</url> |
31 | 35 | <connection>scm:git:https://github.com/google/gson.git</connection> |
32 | 36 | <developerConnection>scm:git:git@github.com:google/gson.git</developerConnection> |
33 | <tag>gson-parent-2.8.8</tag> | |
37 | <tag>gson-parent-2.9.0</tag> | |
34 | 38 | </scm> |
35 | 39 | |
36 | 40 | <issueManagement> |
40 | 44 | |
41 | 45 | <licenses> |
42 | 46 | <license> |
43 | <name>Apache 2.0</name> | |
47 | <name>Apache-2.0</name> | |
44 | 48 | <url>https://www.apache.org/licenses/LICENSE-2.0.txt</url> |
45 | 49 | </license> |
46 | 50 | </licenses> |
62 | 66 | <plugin> |
63 | 67 | <groupId>org.apache.maven.plugins</groupId> |
64 | 68 | <artifactId>maven-compiler-plugin</artifactId> |
65 | <version>3.8.1</version> | |
66 | <executions> | |
67 | <execution> | |
68 | <id>default-compile</id> | |
69 | <configuration> | |
70 | <jdkToolchain> | |
71 | <version>9</version> | |
72 | </jdkToolchain> | |
73 | <release>9</release> | |
74 | </configuration> | |
75 | </execution> | |
76 | <execution> | |
77 | <id>base-compile</id> | |
78 | <goals> | |
79 | <goal>compile</goal> | |
80 | </goals> | |
81 | <configuration> | |
82 | <excludes> | |
83 | <exclude>module-info.java</exclude> | |
84 | </excludes> | |
85 | </configuration> | |
86 | </execution> | |
87 | </executions> | |
69 | <version>3.9.0</version> | |
88 | 70 | <configuration> |
71 | <release>${javaVersion}</release> | |
89 | 72 | <jdkToolchain> |
90 | <version>[1.5,9)</version> | |
73 | <version>[11,)</version> | |
91 | 74 | </jdkToolchain> |
92 | <source>1.6</source> | |
93 | <target>1.6</target> | |
94 | 75 | </configuration> |
95 | 76 | </plugin> |
96 | 77 | <plugin> |
97 | 78 | <groupId>org.apache.maven.plugins</groupId> |
98 | 79 | <artifactId>maven-javadoc-plugin</artifactId> |
99 | <version>3.3.0</version> | |
80 | <version>3.3.1</version> | |
81 | <configuration> | |
82 | <jdkToolchain> | |
83 | <version>[11,)</version> | |
84 | </jdkToolchain> | |
85 | <!-- Exclude `missing` group because some tags have been omitted when they are redundant --> | |
86 | <doclint>all,-missing</doclint> | |
87 | <!-- Link against newer Java API Javadoc because most users likely | |
88 | use a newer Java version than the one used for building this project --> | |
89 | <detectJavaApiLink>false</detectJavaApiLink> | |
90 | <links> | |
91 | <link>https://docs.oracle.com/en/java/javase/11/docs/api/</link> | |
92 | </links> | |
93 | <!-- Disable detection of offline links between Maven modules: | |
94 | (1) Only `gson` module is published, so for other modules Javadoc links don't | |
95 | matter much at the moment; (2) The derived URL for the modules is based on | |
96 | the project URL (= Gson GitHub repo) which is incorrect because it is not | |
97 | hosting the Javadoc (3) It might fail due to https://bugs.openjdk.java.net/browse/JDK-8212233 --> | |
98 | <detectOfflineLinks>false</detectOfflineLinks> | |
99 | </configuration> | |
100 | 100 | </plugin> |
101 | 101 | <plugin> |
102 | 102 | <groupId>org.apache.maven.plugins</groupId> |
103 | 103 | <artifactId>maven-jar-plugin</artifactId> |
104 | </plugin> | |
105 | <plugin> | |
106 | <groupId>org.apache.felix</groupId> | |
107 | <artifactId>maven-bundle-plugin</artifactId> | |
108 | <version>5.1.2</version> | |
109 | <inherited>true</inherited> | |
104 | <version>3.2.2</version> | |
110 | 105 | </plugin> |
111 | 106 | </plugins> |
112 | 107 | </pluginManagement> |
119 | 114 | <dependency> |
120 | 115 | <groupId>org.apache.maven.scm</groupId> |
121 | 116 | <artifactId>maven-scm-api</artifactId> |
122 | <version>1.11.3</version> | |
117 | <version>1.12.2</version> | |
123 | 118 | </dependency> |
124 | 119 | <dependency> |
125 | 120 | <groupId>org.apache.maven.scm</groupId> |
126 | 121 | <artifactId>maven-scm-provider-gitexe</artifactId> |
127 | <version>1.11.3</version> | |
122 | <version>1.12.2</version> | |
128 | 123 | </dependency> |
129 | 124 | </dependencies> |
130 | 125 | <configuration> |
133 | 128 | </plugin> |
134 | 129 | </plugins> |
135 | 130 | </build> |
136 | <profiles> | |
137 | <profile> | |
138 | <id>doclint-java8-disable</id> | |
139 | <activation> | |
140 | <jdk>[1.8,)</jdk> | |
141 | </activation> | |
142 | <build> | |
143 | <plugins> | |
144 | <plugin> | |
145 | <groupId>org.apache.maven.plugins</groupId> | |
146 | <artifactId>maven-javadoc-plugin</artifactId> | |
147 | <configuration> | |
148 | <additionalparam>-Xdoclint:none</additionalparam> | |
149 | </configuration> | |
150 | </plugin> | |
151 | </plugins> | |
152 | </build> | |
153 | </profile> | |
154 | </profiles> | |
155 | 131 | </project> |
0 | # proto | |
1 | ||
2 | This Maven module contains the source code for a JSON serializer and deserializer for | |
3 | [Protocol Buffers (protobuf)](https://developers.google.com/protocol-buffers/docs/javatutorial) | |
4 | messages. | |
5 | ||
6 | The artifacts created by this module are currently not deployed to Maven Central. |
0 | <?xml version="1.0" encoding="UTF-8"?> | |
1 | ||
2 | <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | |
3 | xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> | |
4 | ||
5 | <modelVersion>4.0.0</modelVersion> | |
6 | <groupId>com.google.code.gson</groupId> | |
7 | <artifactId>proto</artifactId> | |
8 | <packaging>jar</packaging> | |
9 | <version>0.6-SNAPSHOT</version> | |
10 | <name>Gson Protobuf Support</name> | |
11 | <description>Gson support for Protobufs</description> | |
12 | <properties> | |
13 | <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> | |
14 | </properties> | |
15 | <distributionManagement> | |
16 | <repository> | |
17 | <id>local.repo</id> | |
18 | <name>file repository to svn</name> | |
19 | <url>file://${basedir}/../../mavenrepo</url> | |
20 | </repository> | |
21 | </distributionManagement> | |
22 | <repositories> | |
23 | <repository> | |
24 | <id>gson</id> | |
25 | <url>http://google-gson.googlecode.com/svn/mavenrepo</url> | |
26 | <snapshots> | |
27 | <enabled>true</enabled> | |
28 | </snapshots> | |
29 | <releases> | |
30 | <enabled>true</enabled> | |
31 | </releases> | |
32 | </repository> | |
33 | </repositories> | |
34 | <licenses> | |
35 | <license> | |
36 | <name>The Apache Software License, Version 2.0</name> | |
37 | <url>http://www.apache.org/licenses/LICENSE-2.0.txt</url> | |
38 | <distribution>repo</distribution> | |
39 | </license> | |
40 | </licenses> | |
41 | <scm> | |
42 | <connection>scm:svn:http://google-gson.googlecode.com/svn/trunk/proto</connection> | |
43 | <developerConnection>scm:svn:https://google-gson.googlecode.com/svn/trunk/proto</developerConnection> | |
44 | <url>http://google-gson.codegoogle.com/svn/trunk/proto</url> | |
45 | </scm> | |
46 | <issueManagement> | |
47 | <system>Google Code Issue Tracking</system> | |
48 | <url>http://code.google.com/p/google-gson/issues/list</url> | |
49 | </issueManagement> | |
50 | ||
51 | <dependencies> | |
52 | ||
53 | <!-- Gson: Java to Json conversion --> | |
54 | <dependency> | |
55 | <groupId>com.google.code.gson</groupId> | |
56 | <artifactId>gson</artifactId> | |
57 | <version>2.4</version> | |
58 | <scope>compile</scope> | |
59 | </dependency> | |
60 | ||
61 | <dependency> | |
62 | <groupId>com.google.protobuf</groupId> | |
63 | <artifactId>protobuf-java</artifactId> | |
64 | <version>2.6.1</version> | |
65 | <scope>compile</scope> | |
66 | </dependency> | |
67 | ||
68 | <dependency> | |
69 | <groupId>com.google.guava</groupId> | |
70 | <artifactId>guava</artifactId> | |
71 | <version>18.0</version> | |
72 | <scope>compile</scope> | |
73 | </dependency> | |
74 | ||
75 | <dependency> | |
76 | <groupId>junit</groupId> | |
77 | <artifactId>junit</artifactId> | |
78 | <version>4.13.1</version> | |
79 | <scope>test</scope> | |
80 | </dependency> | |
81 | ||
82 | <dependency> | |
83 | <groupId>com.google.truth</groupId> | |
84 | <artifactId>truth</artifactId> | |
85 | <version>0.27</version> | |
86 | <scope>test</scope> | |
87 | </dependency> | |
88 | </dependencies> | |
89 | ||
90 | <build> | |
91 | <finalName>gson-proto</finalName> | |
92 | <plugins> | |
93 | <plugin> | |
94 | <artifactId>maven-antrun-plugin</artifactId> | |
95 | <version>1.8</version> | |
96 | <executions> | |
97 | <execution> | |
98 | <id>compile-protoc</id> | |
99 | <phase>generate-sources</phase> | |
100 | <configuration> | |
101 | <tasks> | |
102 | <mkdir dir="target/generated" /> | |
103 | <path id="proto.path"> | |
104 | <fileset dir="src/main/protobuf"> | |
105 | <include name="**/*.proto" /> | |
106 | </fileset> | |
107 | </path> | |
108 | <pathconvert pathsep=" " property="proto.files" refid="proto.path" /> | |
109 | <exec executable="protoc" failonerror="true"> | |
110 | <arg value="--java_out=src/main/java" /> | |
111 | <arg value="--proto_path=/usr/include" /> | |
112 | <arg value="-I${project.basedir}/src/main/protobuf" /> | |
113 | <arg line="${proto.files}" /> | |
114 | </exec> | |
115 | </tasks> | |
116 | </configuration> | |
117 | <goals> | |
118 | <goal>run</goal> | |
119 | </goals> | |
120 | </execution> | |
121 | </executions> | |
122 | </plugin> | |
123 | <plugin> | |
124 | <groupId>org.apache.maven.plugins</groupId> | |
125 | <artifactId>maven-compiler-plugin</artifactId> | |
126 | <version>2.3.2</version> | |
127 | <configuration> | |
128 | <source>1.6</source> | |
129 | <target>1.6</target> | |
130 | </configuration> | |
131 | </plugin> | |
132 | <plugin> | |
133 | <groupId>org.apache.maven.plugins</groupId> | |
134 | <artifactId>maven-eclipse-plugin</artifactId> | |
135 | <version>2.8</version> | |
136 | <configuration> | |
137 | <downloadSources>true</downloadSources> | |
138 | <downloadJavadocs>true</downloadJavadocs> | |
139 | <workspace>../eclipse-ws</workspace> | |
140 | <workspaceCodeStylesURL>file:///${basedir}/../lib/gson-formatting-styles.xml</workspaceCodeStylesURL> | |
141 | </configuration> | |
142 | </plugin> | |
143 | <plugin> | |
144 | <groupId>org.apache.maven.plugins</groupId> | |
145 | <artifactId>maven-install-plugin</artifactId> | |
146 | <version>2.5.2</version> | |
147 | <!-- configuration> | |
148 | <updateReleaseInfo>true</updateReleaseInfo> | |
149 | <createChecksum>true</createChecksum> | |
150 | <groupId>${groupId}</groupId> | |
151 | <artifactId>${artifactId}</artifactId> | |
152 | <version>${version}</version> | |
153 | <packaging>jar</packaging> | |
154 | <classifier>sources</classifier> | |
155 | <file>target/proto-sources.jar</file> | |
156 | </configuration --> | |
157 | </plugin> | |
158 | <plugin> | |
159 | <groupId>org.apache.maven.plugins</groupId> | |
160 | <artifactId>maven-release-plugin</artifactId> | |
161 | <version>2.1</version> | |
162 | <configuration> | |
163 | <arguments>-DenableCiProfile=true</arguments> | |
164 | <tagBase>https://google-gson.googlecode.com/svn/tags/</tagBase> | |
165 | </configuration> | |
166 | </plugin> | |
167 | <plugin> | |
168 | <groupId>org.apache.maven.plugins</groupId> | |
169 | <artifactId>maven-source-plugin</artifactId> | |
170 | <version>2.1.2</version> | |
171 | <executions> | |
172 | <execution> | |
173 | <id>attach-sources</id> | |
174 | <goals> | |
175 | <goal>jar</goal> | |
176 | </goals> | |
177 | </execution> | |
178 | </executions> | |
179 | </plugin> | |
180 | <plugin> | |
181 | <groupId>org.apache.maven.plugins</groupId> | |
182 | <artifactId>maven-javadoc-plugin</artifactId> | |
183 | <version>2.7</version> | |
184 | <executions> | |
185 | <execution> | |
186 | <id>attach-javadocs</id> | |
187 | <goals> | |
188 | <goal>jar</goal> | |
189 | </goals> | |
190 | </execution> | |
191 | </executions> | |
192 | <configuration> | |
193 | <links> | |
194 | <link>http://download.oracle.com/javase/1.5.0/docs/api/</link> | |
195 | </links> | |
196 | <version>true</version> | |
197 | <show>public</show> | |
198 | </configuration> | |
199 | </plugin> | |
200 | <plugin> | |
201 | <artifactId>maven-assembly-plugin</artifactId> | |
202 | <configuration> | |
203 | <descriptor>src/main/resources/assembly-descriptor.xml</descriptor> | |
204 | <finalName>proto-${project.version}</finalName> | |
205 | <outputDirectory>target/dist</outputDirectory> | |
206 | <workDirectory>target/assembly/work</workDirectory> | |
207 | </configuration> | |
208 | </plugin> | |
209 | </plugins> | |
210 | </build> | |
211 | <developers> | |
212 | <developer> | |
213 | <name>Inderjeet Singh</name> | |
214 | <organization>Google Inc.</organization> | |
215 | </developer> | |
216 | </developers> | |
217 | </project> | |
0 | <?xml version="1.0" encoding="UTF-8"?> | |
1 | ||
2 | <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> | |
3 | ||
4 | <modelVersion>4.0.0</modelVersion> | |
5 | <parent> | |
6 | <groupId>com.google.code.gson</groupId> | |
7 | <artifactId>gson-parent</artifactId> | |
8 | <version>2.9.0</version> | |
9 | </parent> | |
10 | ||
11 | <artifactId>proto</artifactId> | |
12 | <name>Gson Protobuf Support</name> | |
13 | <description>Gson support for Protobufs</description> | |
14 | ||
15 | <licenses> | |
16 | <license> | |
17 | <name>Apache-2.0</name> | |
18 | <url>https://www.apache.org/licenses/LICENSE-2.0.txt</url> | |
19 | </license> | |
20 | </licenses> | |
21 | ||
22 | <dependencies> | |
23 | <dependency> | |
24 | <groupId>com.google.code.gson</groupId> | |
25 | <artifactId>gson</artifactId> | |
26 | <version>${project.parent.version}</version> | |
27 | </dependency> | |
28 | ||
29 | <dependency> | |
30 | <groupId>com.google.protobuf</groupId> | |
31 | <artifactId>protobuf-java</artifactId> | |
32 | <version>4.0.0-rc-2</version> | |
33 | </dependency> | |
34 | ||
35 | <dependency> | |
36 | <groupId>com.google.guava</groupId> | |
37 | <artifactId>guava</artifactId> | |
38 | <version>31.0.1-jre</version> | |
39 | </dependency> | |
40 | ||
41 | <dependency> | |
42 | <groupId>junit</groupId> | |
43 | <artifactId>junit</artifactId> | |
44 | <scope>test</scope> | |
45 | </dependency> | |
46 | ||
47 | <dependency> | |
48 | <groupId>com.google.truth</groupId> | |
49 | <artifactId>truth</artifactId> | |
50 | <version>1.1.3</version> | |
51 | <scope>test</scope> | |
52 | </dependency> | |
53 | </dependencies> | |
54 | ||
55 | <build> | |
56 | <finalName>gson-proto</finalName> | |
57 | <!-- Setup based on https://quarkus.io/guides/grpc-getting-started#generating-java-files-from-proto-with-protobuf-maven-plugin --> | |
58 | <extensions> | |
59 | <extension> | |
60 | <groupId>kr.motd.maven</groupId> | |
61 | <artifactId>os-maven-plugin</artifactId> | |
62 | <version>1.7.0</version> | |
63 | </extension> | |
64 | </extensions> | |
65 | ||
66 | <plugins> | |
67 | <plugin> | |
68 | <groupId>org.xolstice.maven.plugins</groupId> | |
69 | <artifactId>protobuf-maven-plugin</artifactId> | |
70 | <version>0.6.1</version> | |
71 | <configuration> | |
72 | <protocArtifact>com.google.protobuf:protoc:3.17.3:exe:${os.detected.classifier}</protocArtifact> | |
73 | </configuration> | |
74 | <executions> | |
75 | <execution> | |
76 | <goals> | |
77 | <goal>compile</goal> | |
78 | <goal>test-compile</goal> | |
79 | </goals> | |
80 | </execution> | |
81 | </executions> | |
82 | </plugin> | |
83 | </plugins> | |
84 | ||
85 | <pluginManagement> | |
86 | <plugins> | |
87 | <plugin> | |
88 | <groupId>org.apache.maven.plugins</groupId> | |
89 | <artifactId>maven-deploy-plugin</artifactId> | |
90 | <version>3.0.0-M2</version> | |
91 | <configuration> | |
92 | <!-- Not deployed --> | |
93 | <skip>true</skip> | |
94 | </configuration> | |
95 | </plugin> | |
96 | </plugins> | |
97 | </pluginManagement> | |
98 | </build> | |
99 | ||
100 | <developers> | |
101 | <developer> | |
102 | <name>Inderjeet Singh</name> | |
103 | <organization>Google Inc.</organization> | |
104 | </developer> | |
105 | </developers> | |
106 | </project> |
36 | 36 | import com.google.protobuf.DynamicMessage; |
37 | 37 | import com.google.protobuf.Extension; |
38 | 38 | import com.google.protobuf.Message; |
39 | ||
40 | 39 | import java.lang.reflect.Field; |
41 | 40 | import java.lang.reflect.InvocationTargetException; |
42 | 41 | import java.lang.reflect.Method; |
75 | 74 | /** |
76 | 75 | * Determines how enum <u>values</u> should be serialized. |
77 | 76 | */ |
78 | public static enum EnumSerialization { | |
77 | public enum EnumSerialization { | |
79 | 78 | /** |
80 | 79 | * Serializes and deserializes enum values using their <b>number</b>. When this is used, custom |
81 | 80 | * value names set on enums are ignored. |
116 | 115 | * For example, if you use the following parameters: {@link CaseFormat#LOWER_UNDERSCORE}, |
117 | 116 | * {@link CaseFormat#LOWER_CAMEL}, the following conversion will occur: |
118 | 117 | * |
119 | * <pre> | |
118 | * <pre>{@code | |
120 | 119 | * PROTO <-> JSON |
121 | 120 | * my_field myField |
122 | 121 | * foo foo |
123 | 122 | * n__id_ct nIdCt |
124 | * </pre> | |
123 | * }</pre> | |
125 | 124 | */ |
126 | 125 | public Builder setFieldNameSerializationFormat(CaseFormat fromFieldNameFormat, |
127 | 126 | CaseFormat toFieldNameFormat) { |
191 | 190 | private static final com.google.protobuf.Descriptors.FieldDescriptor.Type ENUM_TYPE = |
192 | 191 | com.google.protobuf.Descriptors.FieldDescriptor.Type.ENUM; |
193 | 192 | |
194 | private static final ConcurrentMap<String, Map<Class<?>, Method>> mapOfMapOfMethods = | |
193 | private static final ConcurrentMap<String, ConcurrentMap<Class<?>, Method>> mapOfMapOfMethods = | |
195 | 194 | new MapMaker().makeMap(); |
196 | 195 | |
197 | 196 | private final EnumSerialization enumSerialization; |
308 | 307 | } |
309 | 308 | } |
310 | 309 | } |
311 | return (Message) protoBuilder.build(); | |
310 | return protoBuilder.build(); | |
312 | 311 | } catch (SecurityException e) { |
313 | 312 | throw new JsonParseException(e); |
314 | 313 | } catch (NoSuchMethodException e) { |
388 | 387 | EnumValueDescriptor fieldValue = desc.findValueByNumber(jsonElement.getAsInt()); |
389 | 388 | if (fieldValue == null) { |
390 | 389 | throw new IllegalArgumentException( |
391 | String.format("Unrecognized enum value: %s", jsonElement.getAsInt())); | |
390 | String.format("Unrecognized enum value: %d", jsonElement.getAsInt())); | |
392 | 391 | } |
393 | 392 | return fieldValue; |
394 | 393 | } |
396 | 395 | |
397 | 396 | private static Method getCachedMethod(Class<?> clazz, String methodName, |
398 | 397 | Class<?>... methodParamTypes) throws NoSuchMethodException { |
399 | Map<Class<?>, Method> mapOfMethods = mapOfMapOfMethods.get(methodName); | |
398 | ConcurrentMap<Class<?>, Method> mapOfMethods = mapOfMapOfMethods.get(methodName); | |
400 | 399 | if (mapOfMethods == null) { |
401 | 400 | mapOfMethods = new MapMaker().makeMap(); |
402 | Map<Class<?>, Method> previous = | |
401 | ConcurrentMap<Class<?>, Method> previous = | |
403 | 402 | mapOfMapOfMethods.putIfAbsent(methodName, mapOfMethods); |
404 | 403 | mapOfMethods = previous == null ? mapOfMethods : previous; |
405 | 404 | } |
0 | // | |
1 | // Copyright (C) 2010 Google Inc. | |
2 | // | |
3 | // Licensed under the Apache License, Version 2.0 (the "License"); | |
4 | // you may not use this file except in compliance with the License. | |
5 | // You may obtain a copy of the License at | |
6 | // | |
7 | // http://www.apache.org/licenses/LICENSE-2.0 | |
8 | // | |
9 | // Unless required by applicable law or agreed to in writing, software | |
10 | // distributed under the License is distributed on an "AS IS" BASIS, | |
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
12 | // See the License for the specific language governing permissions and | |
13 | // limitations under the License. | |
14 | // | |
15 | ||
16 | syntax = "proto2"; | |
17 | ||
18 | package google.gson.protobuf.generated; | |
19 | option java_package = "com.google.gson.protobuf.generated"; | |
20 | ||
21 | import "google/protobuf/descriptor.proto"; | |
22 | ||
23 | extend google.protobuf.FieldOptions { | |
24 | // Indicates a field name that overrides the default for serialization | |
25 | optional string serialized_name = 92066888; | |
26 | } | |
27 | ||
28 | extend google.protobuf.EnumValueOptions { | |
29 | // Indicates a field value that overrides the default for serialization | |
30 | optional string serialized_value = 92066888; | |
31 | } |
0 | // | |
1 | // Copyright (C) 2010 Google Inc. | |
2 | // | |
3 | // Licensed under the Apache License, Version 2.0 (the "License"); | |
4 | // you may not use this file except in compliance with the License. | |
5 | // You may obtain a copy of the License at | |
6 | // | |
7 | // http://www.apache.org/licenses/LICENSE-2.0 | |
8 | // | |
9 | // Unless required by applicable law or agreed to in writing, software | |
10 | // distributed under the License is distributed on an "AS IS" BASIS, | |
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
12 | // See the License for the specific language governing permissions and | |
13 | // limitations under the License. | |
14 | // | |
15 | ||
16 | syntax = "proto2"; | |
17 | ||
18 | package google.gson.protobuf.generated; | |
19 | option java_package = "com.google.gson.protobuf.generated"; | |
20 | ||
21 | import "annotations.proto"; | |
22 | ||
23 | message SimpleProto { | |
24 | optional string msg = 1; | |
25 | optional int32 count = 2; | |
26 | } | |
27 | ||
28 | message ProtoWithDifferentCaseFormat { | |
29 | repeated string name_that_tests_case_format = 1; | |
30 | optional string another_field = 2; | |
31 | } | |
32 | ||
33 | message ProtoWithRepeatedFields { | |
34 | repeated int64 numbers = 1; | |
35 | repeated SimpleProto simples = 2; | |
36 | optional string name = 3; | |
37 | } | |
38 | ||
39 | // -- A more complex message with annotations and nested protos | |
40 | ||
41 | message OuterMessage { | |
42 | optional int32 month = 1; | |
43 | optional int32 year = 2; | |
44 | optional int64 long_timestamp = 3 [(serialized_name) = "timeStamp"]; | |
45 | optional string country_code_5f55 = 4; | |
46 | } | |
47 | ||
48 | message ProtoWithAnnotations { | |
49 | optional string id = 1; | |
50 | optional OuterMessage outer_message = 2 [(serialized_name) = "expiration_date"]; | |
51 | ||
52 | message InnerMessage { | |
53 | optional int32 n__id_ct = 1; | |
54 | ||
55 | enum Type { | |
56 | UNKNOWN = 0; | |
57 | TEXT = 1 [(serialized_value) = "text/plain"]; | |
58 | IMAGE = 2 [(serialized_value) = "image/png"]; | |
59 | } | |
60 | optional Type content = 2; | |
61 | ||
62 | message Data { | |
63 | optional string data = 1; | |
64 | optional int32 width = 2; | |
65 | optional int32 height = 3; | |
66 | } | |
67 | repeated Data data = 3 [(serialized_name) = "$binary_data$"]; | |
68 | } | |
69 | optional InnerMessage inner_message_1 = 3; | |
70 | optional InnerMessage inner_message_2 = 4; | |
71 | }⏎ |
0 | // | |
1 | // Copyright (C) 2010 Google Inc. | |
2 | // | |
3 | // Licensed under the Apache License, Version 2.0 (the "License"); | |
4 | // you may not use this file except in compliance with the License. | |
5 | // You may obtain a copy of the License at | |
6 | // | |
7 | // http://www.apache.org/licenses/LICENSE-2.0 | |
8 | // | |
9 | // Unless required by applicable law or agreed to in writing, software | |
10 | // distributed under the License is distributed on an "AS IS" BASIS, | |
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
12 | // See the License for the specific language governing permissions and | |
13 | // limitations under the License. | |
14 | // | |
15 | ||
16 | syntax = "proto2"; | |
17 | ||
18 | package google.gson.protobuf.generated; | |
19 | option java_package = "com.google.gson.protobuf.generated"; | |
20 | ||
21 | import "google/protobuf/descriptor.proto"; | |
22 | ||
23 | extend google.protobuf.FieldOptions { | |
24 | // Indicates a field name that overrides the default for serialization | |
25 | optional string serialized_name = 92066888; | |
26 | } | |
27 | ||
28 | extend google.protobuf.EnumValueOptions { | |
29 | // Indicates a field value that overrides the default for serialization | |
30 | optional string serialized_value = 92066888; | |
31 | } |
0 | // | |
1 | // Copyright (C) 2010 Google Inc. | |
2 | // | |
3 | // Licensed under the Apache License, Version 2.0 (the "License"); | |
4 | // you may not use this file except in compliance with the License. | |
5 | // You may obtain a copy of the License at | |
6 | // | |
7 | // http://www.apache.org/licenses/LICENSE-2.0 | |
8 | // | |
9 | // Unless required by applicable law or agreed to in writing, software | |
10 | // distributed under the License is distributed on an "AS IS" BASIS, | |
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
12 | // See the License for the specific language governing permissions and | |
13 | // limitations under the License. | |
14 | // | |
15 | ||
16 | package google.gson.protobuf.generated; | |
17 | option java_package = "com.google.gson.protobuf.generated"; | |
18 | ||
19 | import "annotations.proto"; | |
20 | ||
21 | message SimpleProto { | |
22 | optional string msg = 1; | |
23 | optional int32 count = 2; | |
24 | } | |
25 | ||
26 | message ProtoWithDifferentCaseFormat { | |
27 | repeated string name_that_tests_case_format = 1; | |
28 | optional string another_field = 2; | |
29 | } | |
30 | ||
31 | message ProtoWithRepeatedFields { | |
32 | repeated int64 numbers = 1; | |
33 | repeated SimpleProto simples = 2; | |
34 | optional string name = 3; | |
35 | } | |
36 | ||
37 | // -- A more complex message with annotations and nested protos | |
38 | ||
39 | message OuterMessage { | |
40 | optional int32 month = 1; | |
41 | optional int32 year = 2; | |
42 | optional int64 long_timestamp = 3 [(serialized_name) = "timeStamp"]; | |
43 | optional string country_code_5f55 = 4; | |
44 | } | |
45 | ||
46 | message ProtoWithAnnotations { | |
47 | optional string id = 1; | |
48 | optional OuterMessage outer_message = 2 [(serialized_name) = "expiration_date"]; | |
49 | ||
50 | message InnerMessage { | |
51 | optional int32 n__id_ct = 1; | |
52 | ||
53 | enum Type { | |
54 | UNKNOWN = 0; | |
55 | TEXT = 1 [(serialized_value) = "text/plain"]; | |
56 | IMAGE = 2 [(serialized_value) = "image/png"]; | |
57 | } | |
58 | optional Type content = 2; | |
59 | ||
60 | message Data { | |
61 | optional string data = 1; | |
62 | optional int32 width = 2; | |
63 | optional int32 height = 3; | |
64 | } | |
65 | repeated Data data = 3 [(serialized_name) = "$binary_data$"]; | |
66 | } | |
67 | optional InnerMessage inner_message_1 = 3; | |
68 | optional InnerMessage inner_message_2 = 4; | |
69 | }⏎ |
+6
-7
15 | 15 | package com.google.gson.protobuf.functional; |
16 | 16 | |
17 | 17 | import static com.google.common.truth.Truth.assertThat; |
18 | import static com.google.common.truth.Truth.assert_; | |
18 | import static com.google.common.truth.Truth.assertWithMessage; | |
19 | 19 | |
20 | 20 | import com.google.common.base.CaseFormat; |
21 | 21 | import com.google.gson.Gson; |
29 | 29 | import com.google.gson.protobuf.generated.Bag.ProtoWithAnnotations.InnerMessage; |
30 | 30 | import com.google.gson.protobuf.generated.Bag.ProtoWithAnnotations.InnerMessage.Data; |
31 | 31 | import com.google.gson.protobuf.generated.Bag.ProtoWithAnnotations.InnerMessage.Type; |
32 | import com.google.protobuf.GeneratedMessage; | |
33 | ||
32 | import com.google.protobuf.GeneratedMessageV3; | |
34 | 33 | import junit.framework.TestCase; |
35 | 34 | |
36 | 35 | /** |
51 | 50 | .addSerializedNameExtension(Annotations.serializedName) |
52 | 51 | .addSerializedEnumValueExtension(Annotations.serializedValue); |
53 | 52 | gson = new GsonBuilder() |
54 | .registerTypeHierarchyAdapter(GeneratedMessage.class, protoTypeAdapter.build()) | |
53 | .registerTypeHierarchyAdapter(GeneratedMessageV3.class, protoTypeAdapter.build()) | |
55 | 54 | .create(); |
56 | 55 | gsonWithEnumNumbers = new GsonBuilder() |
57 | .registerTypeHierarchyAdapter(GeneratedMessage.class, protoTypeAdapter | |
56 | .registerTypeHierarchyAdapter(GeneratedMessageV3.class, protoTypeAdapter | |
58 | 57 | .setEnumSerialization(EnumSerialization.NUMBER) |
59 | 58 | .build()) |
60 | 59 | .create(); |
61 | 60 | gsonWithLowerHyphen = new GsonBuilder() |
62 | .registerTypeHierarchyAdapter(GeneratedMessage.class, protoTypeAdapter | |
61 | .registerTypeHierarchyAdapter(GeneratedMessageV3.class, protoTypeAdapter | |
63 | 62 | .setFieldNameSerializationFormat(CaseFormat.LOWER_UNDERSCORE, CaseFormat.LOWER_HYPHEN) |
64 | 63 | .build()) |
65 | 64 | .create(); |
156 | 155 | + "}"); |
157 | 156 | try { |
158 | 157 | gson.fromJson(json, InnerMessage.class); |
159 | assert_().fail("Should have thrown"); | |
158 | assertWithMessage("Should have thrown").fail(); | |
160 | 159 | } catch (JsonParseException e) { |
161 | 160 | // expected |
162 | 161 | } |
+3
-4
23 | 23 | import com.google.gson.protobuf.generated.Bag.ProtoWithDifferentCaseFormat; |
24 | 24 | import com.google.gson.protobuf.generated.Bag.ProtoWithRepeatedFields; |
25 | 25 | import com.google.gson.protobuf.generated.Bag.SimpleProto; |
26 | import com.google.protobuf.GeneratedMessage; | |
27 | ||
26 | import com.google.protobuf.GeneratedMessageV3; | |
28 | 27 | import junit.framework.TestCase; |
29 | 28 | |
30 | 29 | /** |
41 | 40 | super.setUp(); |
42 | 41 | gson = |
43 | 42 | new GsonBuilder() |
44 | .registerTypeHierarchyAdapter(GeneratedMessage.class, | |
43 | .registerTypeHierarchyAdapter(GeneratedMessageV3.class, | |
45 | 44 | ProtoTypeAdapter.newBuilder() |
46 | 45 | .setEnumSerialization(EnumSerialization.NUMBER) |
47 | 46 | .build()) |
49 | 48 | upperCamelGson = |
50 | 49 | new GsonBuilder() |
51 | 50 | .registerTypeHierarchyAdapter( |
52 | GeneratedMessage.class, ProtoTypeAdapter.newBuilder() | |
51 | GeneratedMessageV3.class, ProtoTypeAdapter.newBuilder() | |
53 | 52 | .setFieldNameSerializationFormat( |
54 | 53 | CaseFormat.LOWER_UNDERSCORE, CaseFormat.UPPER_CAMEL) |
55 | 54 | .build()) |
+2
-3
20 | 20 | import com.google.gson.protobuf.ProtoTypeAdapter.EnumSerialization; |
21 | 21 | import com.google.gson.protobuf.generated.Bag.SimpleProto; |
22 | 22 | import com.google.protobuf.Descriptors.Descriptor; |
23 | import com.google.protobuf.GeneratedMessage; | |
24 | ||
23 | import com.google.protobuf.GeneratedMessageV3; | |
25 | 24 | import junit.framework.TestCase; |
26 | 25 | |
27 | 26 | public class ProtosWithPrimitiveTypesTest extends TestCase { |
31 | 30 | protected void setUp() throws Exception { |
32 | 31 | super.setUp(); |
33 | 32 | gson = new GsonBuilder().registerTypeHierarchyAdapter( |
34 | GeneratedMessage.class, ProtoTypeAdapter.newBuilder() | |
33 | GeneratedMessageV3.class, ProtoTypeAdapter.newBuilder() | |
35 | 34 | .setEnumSerialization(EnumSerialization.NUMBER) |
36 | 35 | .build()) |
37 | 36 | .create(); |