diff --git a/README.md b/README.md index 7418a27af7..a39a9ea680 100644 --- a/README.md +++ b/README.md @@ -47,10 +47,10 @@ Despite supporting older Java versions, Gson also provides a JPMS module descrip These are the optional Java Platform Module System (JPMS) JDK modules which Gson depends on. This only applies when running Java 9 or newer. -- `java.sql` (optional since Gson 2.8.9) +- `java.sql` (optional since Gson 2.8.9)\ When this module is present, Gson provides default adapters for some SQL date and time classes. -- `jdk.unsupported`, respectively class `sun.misc.Unsafe` (optional) +- `jdk.unsupported`, respectively class `sun.misc.Unsafe` (optional)\ When this module is present, Gson can use the `Unsafe` class to create instances of classes without no-args constructor. However, care should be taken when relying on this. `Unsafe` is not available in all environments and its usage has some pitfalls, see [`GsonBuilder.disableJdkUnsafe()`](https://javadoc.io/doc/com.google.code.gson/gson/latest/com.google.gson/com/google/gson/GsonBuilder.html#disableJdkUnsafe()). @@ -87,7 +87,7 @@ JDK 11 or newer is required for building, JDK 17 is recommended. ### Contributing -See the [contributing guide](https://github.com/google/.github/blob/master/CONTRIBUTING.md). +See the [contributing guide](https://github.com/google/.github/blob/master/CONTRIBUTING.md).\ Please perform a quick search to check if there are already existing issues or pull requests related to your contribution. Keep in mind that Gson is in maintenance mode. If you want to add a new feature, please first search for existing GitHub issues, or create a new one to discuss the feature and get feedback. diff --git a/Troubleshooting.md b/Troubleshooting.md index 91fd22110a..8289c93c15 100644 --- a/Troubleshooting.md +++ b/Troubleshooting.md @@ -127,8 +127,8 @@ For example, let's assume you want to deserialize the following JSON data: } ``` -This will fail with an exception similar to this one: `MalformedJsonException: Use JsonReader.setStrictness(Strictness.LENIENT) to accept malformed JSON at line 5 column 4 path $.languages[2]` -The problem here is the trailing comma (`,`) after `"French"`, trailing commas are not allowed by the JSON specification. The location information "line 5 column 4" points to the `]` in the JSON data (with some slight inaccuracies) because Gson expected another value after `,` instead of the closing `]`. The JSONPath `$.languages[2]` in the exception message also points there: `$.` refers to the root object, `languages` refers to its member of that name and `[2]` refers to the (missing) third value in the JSON array value of that member (numbering starts at 0, so it is `[2]` instead of `[3]`). +This will fail with an exception similar to this one: `MalformedJsonException: Use JsonReader.setStrictness(Strictness.LENIENT) to accept malformed JSON at line 5 column 4 path $.languages[2]` +The problem here is the trailing comma (`,`) after `"French"`, trailing commas are not allowed by the JSON specification. The location information "line 5 column 4" points to the `]` in the JSON data (with some slight inaccuracies) because Gson expected another value after `,` instead of the closing `]`. The JSONPath `$.languages[2]` in the exception message also points there: `$.` refers to the root object, `languages` refers to its member of that name and `[2]` refers to the (missing) third value in the JSON array value of that member (numbering starts at 0, so it is `[2]` instead of `[3]`). The proper solution here is to fix the malformed JSON data. To spot syntax errors in the JSON data easily you can open it in an editor with support for JSON, for example Visual Studio Code. It will highlight within the JSON data the error location and show why the JSON data is considered invalid. @@ -178,8 +178,8 @@ And you want to deserialize the following JSON data: } ``` -This will fail with an exception similar to this one: `IllegalStateException: Expected a string but was BEGIN_ARRAY at line 2 column 17 path $.languages` -This means Gson expected a JSON string value but found the beginning of a JSON array (`[`). The location information "line 2 column 17" points to the `[` in the JSON data (with some slight inaccuracies), so does the JSONPath `$.languages` in the exception message. It refers to the `languages` member of the root object (`$.`). +This will fail with an exception similar to this one: `IllegalStateException: Expected a string but was BEGIN_ARRAY at line 2 column 17 path $.languages` +This means Gson expected a JSON string value but found the beginning of a JSON array (`[`). The location information "line 2 column 17" points to the `[` in the JSON data (with some slight inaccuracies), so does the JSONPath `$.languages` in the exception message. It refers to the `languages` member of the root object (`$.`). The solution here is to change in the `WebPage` class the field `String languages` to `List languages`. ## `IllegalStateException`: "Expected ... but was NULL" @@ -287,7 +287,7 @@ This will not initialize arbitrary classes, and it will throw a `ClassCastExcept ## `IllegalStateException`: 'TypeToken must be created with a type argument'
`RuntimeException`: 'Missing type parameter' -**Symptom:** An `IllegalStateException` with the message 'TypeToken must be created with a type argument' is thrown. +**Symptom:** An `IllegalStateException` with the message 'TypeToken must be created with a type argument' is thrown. For older Gson versions a `RuntimeException` with message 'Missing type parameter' is thrown. **Reason:** diff --git a/UserGuide.md b/UserGuide.md index aaf948bc29..aa0b831c84 100644 --- a/UserGuide.md +++ b/UserGuide.md @@ -405,7 +405,7 @@ gson.registerTypeAdapter(MyType.class, new MyDeserializer()); gson.registerTypeAdapter(MyType.class, new MyInstanceCreator()); ``` -`registerTypeAdapter` call checks +`registerTypeAdapter` call checks 1. if the type adapter implements more than one of these interfaces, in that case it registers the adapter for all of them. 2. if the type adapter is for the Object class or JsonElement or any of its subclasses, in that case it throws IllegalArgumentException because overriding the built-in adapters for these types is not supported. diff --git a/extras/src/main/java/com/google/gson/extras/examples/rawcollections/RawCollectionsExample.java b/extras/src/main/java/com/google/gson/extras/examples/rawcollections/RawCollectionsExample.java index 9ea350cae8..6568c0b146 100644 --- a/extras/src/main/java/com/google/gson/extras/examples/rawcollections/RawCollectionsExample.java +++ b/extras/src/main/java/com/google/gson/extras/examples/rawcollections/RawCollectionsExample.java @@ -15,28 +15,29 @@ */ package com.google.gson.extras.examples.rawcollections; -import java.util.ArrayList; -import java.util.Collection; - import com.google.gson.Gson; import com.google.gson.JsonArray; import com.google.gson.JsonParser; +import java.util.ArrayList; +import java.util.Collection; public class RawCollectionsExample { static class Event { private String name; private String source; + private Event(String name, String source) { this.name = name; this.source = source; } + @Override public String toString() { return String.format("(name=%s, source=%s)", name, source); } } - @SuppressWarnings({ "unchecked", "rawtypes" }) + @SuppressWarnings({"unchecked", "rawtypes"}) public static void main(String[] args) { Gson gson = new Gson(); Collection collection = new ArrayList(); diff --git a/extras/src/main/java/com/google/gson/graph/GraphAdapterBuilder.java b/extras/src/main/java/com/google/gson/graph/GraphAdapterBuilder.java index 7cc724b8b1..dba381531e 100644 --- a/extras/src/main/java/com/google/gson/graph/GraphAdapterBuilder.java +++ b/extras/src/main/java/com/google/gson/graph/GraphAdapterBuilder.java @@ -38,26 +38,28 @@ import java.util.Map; import java.util.Queue; -/** - * Writes a graph of objects as a list of named nodes. - */ +/** Writes a graph of objects as a list of named nodes. */ // TODO: proper documentation public final class GraphAdapterBuilder { private final Map> instanceCreators; private final ConstructorConstructor constructorConstructor; public GraphAdapterBuilder() { - this.instanceCreators = new HashMap<>(); - this.constructorConstructor = new ConstructorConstructor(instanceCreators, true, Collections.emptyList()); + this.instanceCreators = new HashMap<>(); + this.constructorConstructor = + new ConstructorConstructor( + instanceCreators, true, Collections.emptyList()); } + public GraphAdapterBuilder addType(Type type) { final ObjectConstructor objectConstructor = constructorConstructor.get(TypeToken.get(type)); - InstanceCreator instanceCreator = new InstanceCreator() { - @Override - public Object createInstance(Type type) { - return objectConstructor.construct(); - } - }; + InstanceCreator instanceCreator = + new InstanceCreator() { + @Override + public Object createInstance(Type type) { + return objectConstructor.construct(); + } + }; return addType(type, instanceCreator); } @@ -79,6 +81,7 @@ public void registerOn(GsonBuilder gsonBuilder) { static class Factory implements TypeAdapterFactory, InstanceCreator { private final Map> instanceCreators; + @SuppressWarnings("ThreadLocalUsage") private final ThreadLocal graphThreadLocal = new ThreadLocal<>(); @@ -95,7 +98,8 @@ public TypeAdapter create(Gson gson, TypeToken type) { final TypeAdapter typeAdapter = gson.getDelegateAdapter(this, type); final TypeAdapter elementAdapter = gson.getAdapter(JsonElement.class); return new TypeAdapter() { - @Override public void write(JsonWriter out, T value) throws IOException { + @Override + public void write(JsonWriter out, T value) throws IOException { if (value == null) { out.nullValue(); return; @@ -144,7 +148,8 @@ public TypeAdapter create(Gson gson, TypeToken type) { } } - @Override public T read(JsonReader in) throws IOException { + @Override + public T read(JsonReader in) throws IOException { if (in.peek() == JsonToken.NULL) { in.nextNull(); return null; @@ -207,13 +212,12 @@ public TypeAdapter create(Gson gson, TypeToken type) { } /** - * Hook for the graph adapter to get a reference to a deserialized value - * before that value is fully populated. This is useful to deserialize - * values that directly or indirectly reference themselves: we can hand - * out an instance before read() returns. + * Hook for the graph adapter to get a reference to a deserialized value before that value is + * fully populated. This is useful to deserialize values that directly or indirectly reference + * themselves: we can hand out an instance before read() returns. * - *

Gson should only ever call this method when we're expecting it to; - * that is only when we've called back into Gson to deserialize a tree. + *

Gson should only ever call this method when we're expecting it to; that is only when we've + * called back into Gson to deserialize a tree. */ @Override public Object createInstance(Type type) { @@ -231,22 +235,17 @@ public Object createInstance(Type type) { static class Graph { /** - * The graph elements. On serialization keys are objects (using an identity - * hash map) and on deserialization keys are the string names (using a - * standard hash map). + * The graph elements. On serialization keys are objects (using an identity hash map) and on + * deserialization keys are the string names (using a standard hash map). */ private final Map> map; - /** - * The queue of elements to write during serialization. Unused during - * deserialization. - */ + /** The queue of elements to write during serialization. Unused during deserialization. */ private final Queue> queue = new ArrayDeque<>(); /** - * The instance currently being deserialized. Used as a backdoor between - * the graph traversal (which needs to know instances) and instance creators - * which create them. + * The instance currently being deserialized. Used as a backdoor between the graph traversal + * (which needs to know instances) and instance creators which create them. */ private Element nextCreate; @@ -254,37 +253,24 @@ private Graph(Map> map) { this.map = map; } - /** - * Returns a unique name for an element to be inserted into the graph. - */ + /** Returns a unique name for an element to be inserted into the graph. */ public String nextName() { return "0x" + Integer.toHexString(map.size() + 1); } } - /** - * An element of the graph during serialization or deserialization. - */ + /** An element of the graph during serialization or deserialization. */ static class Element { - /** - * This element's name in the top level graph object. - */ + /** This element's name in the top level graph object. */ private final String id; - /** - * The value if known. During deserialization this is lazily populated. - */ + /** The value if known. During deserialization this is lazily populated. */ private T value; - /** - * This element's type adapter if known. During deserialization this is - * lazily populated. - */ + /** This element's type adapter if known. During deserialization this is lazily populated. */ private TypeAdapter typeAdapter; - /** - * The element to deserialize. Unused in serialization. - */ + /** The element to deserialize. Unused in serialization. */ private final JsonElement element; Element(T value, String id, TypeAdapter typeAdapter, JsonElement element) { diff --git a/extras/src/main/java/com/google/gson/interceptors/Intercept.java b/extras/src/main/java/com/google/gson/interceptors/Intercept.java index fef29cbf0b..5d5f69a2c8 100644 --- a/extras/src/main/java/com/google/gson/interceptors/Intercept.java +++ b/extras/src/main/java/com/google/gson/interceptors/Intercept.java @@ -21,13 +21,13 @@ import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; - /** - * Use this annotation to indicate various interceptors for class instances after - * they have been processed by Gson. For example, you can use it to validate an instance - * after it has been deserialized from Json. - * Here is an example of how this annotation is used: + * Use this annotation to indicate various interceptors for class instances after they have been + * processed by Gson. For example, you can use it to validate an instance after it has been + * deserialized from Json. Here is an example of how this annotation is used: + * *

Here is an example of how this annotation is used: + * *

  * @Intercept(postDeserialize=UserValidator.class)
  * public class User {
@@ -56,8 +56,8 @@
 public @interface Intercept {
 
   /**
-   * Specify the class that provides the methods that should be invoked after an instance
-   * has been deserialized.
+   * Specify the class that provides the methods that should be invoked after an instance has been
+   * deserialized.
    */
   @SuppressWarnings("rawtypes")
   public Class postDeserialize();
diff --git a/extras/src/main/java/com/google/gson/interceptors/InterceptorFactory.java b/extras/src/main/java/com/google/gson/interceptors/InterceptorFactory.java
index 2d6060cad5..7868a13352 100644
--- a/extras/src/main/java/com/google/gson/interceptors/InterceptorFactory.java
+++ b/extras/src/main/java/com/google/gson/interceptors/InterceptorFactory.java
@@ -24,11 +24,10 @@
 import com.google.gson.stream.JsonWriter;
 import java.io.IOException;
 
-/**
- * A type adapter factory that implements {@code @Intercept}.
- */
+/** A type adapter factory that implements {@code @Intercept}. */
 public final class InterceptorFactory implements TypeAdapterFactory {
-  @Override public  TypeAdapter create(Gson gson, TypeToken type) {
+  @Override
+  public  TypeAdapter create(Gson gson, TypeToken type) {
     Intercept intercept = type.getRawType().getAnnotation(Intercept.class);
     if (intercept == null) {
       return null;
@@ -52,11 +51,13 @@ public InterceptorAdapter(TypeAdapter delegate, Intercept intercept) {
       }
     }
 
-    @Override public void write(JsonWriter out, T value) throws IOException {
+    @Override
+    public void write(JsonWriter out, T value) throws IOException {
       delegate.write(out, value);
     }
 
-    @Override public T read(JsonReader in) throws IOException {
+    @Override
+    public T read(JsonReader in) throws IOException {
       T result = delegate.read(in);
       postDeserializer.postDeserialize(result);
       return result;
diff --git a/extras/src/main/java/com/google/gson/interceptors/JsonPostDeserializer.java b/extras/src/main/java/com/google/gson/interceptors/JsonPostDeserializer.java
index 0f3a72ca82..2a8fcf96c9 100644
--- a/extras/src/main/java/com/google/gson/interceptors/JsonPostDeserializer.java
+++ b/extras/src/main/java/com/google/gson/interceptors/JsonPostDeserializer.java
@@ -18,16 +18,14 @@
 import com.google.gson.InstanceCreator;
 
 /**
- * This interface is implemented by a class that wishes to inspect or modify an object
- * after it has been deserialized. You must define a no-args constructor or register an
- * {@link InstanceCreator} for such a class.
+ * This interface is implemented by a class that wishes to inspect or modify an object after it has
+ * been deserialized. You must define a no-args constructor or register an {@link InstanceCreator}
+ * for such a class.
  *
  * @author Inderjeet Singh
  */
 public interface JsonPostDeserializer {
 
-  /**
-   * This method is called by Gson after the object has been deserialized from Json.
-   */
+  /** This method is called by Gson after the object has been deserialized from Json. */
   public void postDeserialize(T object);
 }
diff --git a/extras/src/main/java/com/google/gson/typeadapters/PostConstructAdapterFactory.java b/extras/src/main/java/com/google/gson/typeadapters/PostConstructAdapterFactory.java
index 450ebbab2d..7a3234073f 100644
--- a/extras/src/main/java/com/google/gson/typeadapters/PostConstructAdapterFactory.java
+++ b/extras/src/main/java/com/google/gson/typeadapters/PostConstructAdapterFactory.java
@@ -16,61 +16,63 @@
 
 package com.google.gson.typeadapters;
 
-import java.io.IOException;
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
-
-import javax.annotation.PostConstruct;
-
 import com.google.gson.Gson;
 import com.google.gson.TypeAdapter;
 import com.google.gson.TypeAdapterFactory;
 import com.google.gson.reflect.TypeToken;
 import com.google.gson.stream.JsonReader;
 import com.google.gson.stream.JsonWriter;
+import java.io.IOException;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import javax.annotation.PostConstruct;
 
 public class PostConstructAdapterFactory implements TypeAdapterFactory {
-    // copied from https://gist.github.com/swankjesse/20df26adaf639ed7fd160f145a0b661a
-    @Override
-    public  TypeAdapter create(Gson gson, TypeToken type) {
-        for (Class t = type.getRawType(); (t != Object.class) && (t.getSuperclass() != null); t = t.getSuperclass()) {
-            for (Method m : t.getDeclaredMethods()) {
-                if (m.isAnnotationPresent(PostConstruct.class)) {
-                    m.setAccessible(true);
-                    TypeAdapter delegate = gson.getDelegateAdapter(this, type);
-                    return new PostConstructAdapter<>(delegate, m);
-                }
-            }
+  // copied from https://gist.github.com/swankjesse/20df26adaf639ed7fd160f145a0b661a
+  @Override
+  public  TypeAdapter create(Gson gson, TypeToken type) {
+    for (Class t = type.getRawType();
+        (t != Object.class) && (t.getSuperclass() != null);
+        t = t.getSuperclass()) {
+      for (Method m : t.getDeclaredMethods()) {
+        if (m.isAnnotationPresent(PostConstruct.class)) {
+          m.setAccessible(true);
+          TypeAdapter delegate = gson.getDelegateAdapter(this, type);
+          return new PostConstructAdapter<>(delegate, m);
         }
-        return null;
+      }
     }
+    return null;
+  }
 
-    final static class PostConstructAdapter extends TypeAdapter {
-        private final TypeAdapter delegate;
-        private final Method method;
+  static final class PostConstructAdapter extends TypeAdapter {
+    private final TypeAdapter delegate;
+    private final Method method;
 
-        public PostConstructAdapter(TypeAdapter delegate, Method method) {
-            this.delegate = delegate;
-            this.method = method;
-        }
+    public PostConstructAdapter(TypeAdapter delegate, Method method) {
+      this.delegate = delegate;
+      this.method = method;
+    }
 
-        @Override public T read(JsonReader in) throws IOException {
-            T result = delegate.read(in);
-            if (result != null) {
-                try {
-                    method.invoke(result);
-                } catch (IllegalAccessException e) {
-                    throw new AssertionError();
-                } catch (InvocationTargetException e) {
-                    if (e.getCause() instanceof RuntimeException) throw (RuntimeException) e.getCause();
-                    throw new RuntimeException(e.getCause());
-                }
-            }
-            return result;
+    @Override
+    public T read(JsonReader in) throws IOException {
+      T result = delegate.read(in);
+      if (result != null) {
+        try {
+          method.invoke(result);
+        } catch (IllegalAccessException e) {
+          throw new AssertionError();
+        } catch (InvocationTargetException e) {
+          if (e.getCause() instanceof RuntimeException) throw (RuntimeException) e.getCause();
+          throw new RuntimeException(e.getCause());
         }
+      }
+      return result;
+    }
 
-        @Override public void write(JsonWriter out, T value) throws IOException {
-            delegate.write(out, value);
-        }
+    @Override
+    public void write(JsonWriter out, T value) throws IOException {
+      delegate.write(out, value);
     }
+  }
 }
diff --git a/extras/src/main/java/com/google/gson/typeadapters/RuntimeTypeAdapterFactory.java b/extras/src/main/java/com/google/gson/typeadapters/RuntimeTypeAdapterFactory.java
index 29fbc69268..fa70755abd 100644
--- a/extras/src/main/java/com/google/gson/typeadapters/RuntimeTypeAdapterFactory.java
+++ b/extras/src/main/java/com/google/gson/typeadapters/RuntimeTypeAdapterFactory.java
@@ -32,104 +32,127 @@
 import java.util.Map;
 
 /**
- * Adapts values whose runtime type may differ from their declaration type. This
- * is necessary when a field's type is not the same type that GSON should create
- * when deserializing that field. For example, consider these types:
- * 
   {@code
- *   abstract class Shape {
- *     int x;
- *     int y;
- *   }
- *   class Circle extends Shape {
- *     int radius;
- *   }
- *   class Rectangle extends Shape {
- *     int width;
- *     int height;
- *   }
- *   class Diamond extends Shape {
- *     int width;
- *     int height;
+ * Adapts values whose runtime type may differ from their declaration type. This is necessary when a
+ * field's type is not the same type that GSON should create when deserializing that field. For
+ * example, consider these types:
+ *
+ * 
{@code
+ * abstract class Shape {
+ *   int x;
+ *   int y;
+ * }
+ * class Circle extends Shape {
+ *   int radius;
+ * }
+ * class Rectangle extends Shape {
+ *   int width;
+ *   int height;
+ * }
+ * class Diamond extends Shape {
+ *   int width;
+ *   int height;
+ * }
+ * class Drawing {
+ *   Shape bottomShape;
+ *   Shape topShape;
+ * }
+ * }
+ * + *

Without additional type information, the serialized JSON is ambiguous. Is the bottom shape in + * this drawing a rectangle or a diamond? + * + *

{@code
+ * {
+ *   "bottomShape": {
+ *     "width": 10,
+ *     "height": 5,
+ *     "x": 0,
+ *     "y": 0
+ *   },
+ *   "topShape": {
+ *     "radius": 2,
+ *     "x": 4,
+ *     "y": 1
  *   }
- *   class Drawing {
- *     Shape bottomShape;
- *     Shape topShape;
+ * }
+ * }
+ * + * This class addresses this problem by adding type information to the serialized JSON and honoring + * that type information when the JSON is deserialized: + * + *
{@code
+ * {
+ *   "bottomShape": {
+ *     "type": "Diamond",
+ *     "width": 10,
+ *     "height": 5,
+ *     "x": 0,
+ *     "y": 0
+ *   },
+ *   "topShape": {
+ *     "type": "Circle",
+ *     "radius": 2,
+ *     "x": 4,
+ *     "y": 1
  *   }
+ * }
  * }
- *

Without additional type information, the serialized JSON is ambiguous. Is - * the bottom shape in this drawing a rectangle or a diamond?

   {@code
- *   {
- *     "bottomShape": {
- *       "width": 10,
- *       "height": 5,
- *       "x": 0,
- *       "y": 0
- *     },
- *     "topShape": {
- *       "radius": 2,
- *       "x": 4,
- *       "y": 1
- *     }
- *   }}
- * This class addresses this problem by adding type information to the - * serialized JSON and honoring that type information when the JSON is - * deserialized:
   {@code
- *   {
- *     "bottomShape": {
- *       "type": "Diamond",
- *       "width": 10,
- *       "height": 5,
- *       "x": 0,
- *       "y": 0
- *     },
- *     "topShape": {
- *       "type": "Circle",
- *       "radius": 2,
- *       "x": 4,
- *       "y": 1
- *     }
- *   }}
- * Both the type field name ({@code "type"}) and the type labels ({@code - * "Rectangle"}) are configurable. + * + * Both the type field name ({@code "type"}) and the type labels ({@code "Rectangle"}) are + * configurable. * *

Registering Types

- * Create a {@code RuntimeTypeAdapterFactory} by passing the base type and type field - * name to the {@link #of} factory method. If you don't supply an explicit type - * field name, {@code "type"} will be used.
   {@code
- *   RuntimeTypeAdapterFactory shapeAdapterFactory
- *       = RuntimeTypeAdapterFactory.of(Shape.class, "type");
+ *
+ * Create a {@code RuntimeTypeAdapterFactory} by passing the base type and type field name to the
+ * {@link #of} factory method. If you don't supply an explicit type field name, {@code "type"} will
+ * be used.
+ *
+ * 
{@code
+ * RuntimeTypeAdapterFactory shapeAdapterFactory
+ *     = RuntimeTypeAdapterFactory.of(Shape.class, "type");
  * }
- * Next register all of your subtypes. Every subtype must be explicitly - * registered. This protects your application from injection attacks. If you - * don't supply an explicit type label, the type's simple name will be used. - *
   {@code
- *   shapeAdapterFactory.registerSubtype(Rectangle.class, "Rectangle");
- *   shapeAdapterFactory.registerSubtype(Circle.class, "Circle");
- *   shapeAdapterFactory.registerSubtype(Diamond.class, "Diamond");
+ *
+ * Next register all of your subtypes. Every subtype must be explicitly registered. This protects
+ * your application from injection attacks. If you don't supply an explicit type label, the type's
+ * simple name will be used.
+ *
+ * 
{@code
+ * shapeAdapterFactory.registerSubtype(Rectangle.class, "Rectangle");
+ * shapeAdapterFactory.registerSubtype(Circle.class, "Circle");
+ * shapeAdapterFactory.registerSubtype(Diamond.class, "Diamond");
  * }
+ * * Finally, register the type adapter factory in your application's GSON builder: - *
   {@code
- *   Gson gson = new GsonBuilder()
- *       .registerTypeAdapterFactory(shapeAdapterFactory)
- *       .create();
+ *
+ * 
{@code
+ * Gson gson = new GsonBuilder()
+ *     .registerTypeAdapterFactory(shapeAdapterFactory)
+ *     .create();
  * }
- * Like {@code GsonBuilder}, this API supports chaining:
   {@code
- *   RuntimeTypeAdapterFactory shapeAdapterFactory = RuntimeTypeAdapterFactory.of(Shape.class)
- *       .registerSubtype(Rectangle.class)
- *       .registerSubtype(Circle.class)
- *       .registerSubtype(Diamond.class);
+ *
+ * Like {@code GsonBuilder}, this API supports chaining:
+ *
+ * 
{@code
+ * RuntimeTypeAdapterFactory shapeAdapterFactory = RuntimeTypeAdapterFactory.of(Shape.class)
+ *     .registerSubtype(Rectangle.class)
+ *     .registerSubtype(Circle.class)
+ *     .registerSubtype(Diamond.class);
  * }
* *

Serialization and deserialization

- * In order to serialize and deserialize a polymorphic object, - * you must specify the base type explicitly. - *
   {@code
- *   Diamond diamond = new Diamond();
- *   String json = gson.toJson(diamond, Shape.class);
+ *
+ * In order to serialize and deserialize a polymorphic object, you must specify the base type
+ * explicitly.
+ *
+ * 
{@code
+ * Diamond diamond = new Diamond();
+ * String json = gson.toJson(diamond, Shape.class);
  * }
+ * * And then: - *
   {@code
- *   Shape shape = gson.fromJson(json, Shape.class);
+ *
+ * 
{@code
+ * Shape shape = gson.fromJson(json, Shape.class);
  * }
*/ public final class RuntimeTypeAdapterFactory implements TypeAdapterFactory { @@ -140,8 +163,7 @@ public final class RuntimeTypeAdapterFactory implements TypeAdapterFactory { private final boolean maintainType; private boolean recognizeSubtypes; - private RuntimeTypeAdapterFactory( - Class baseType, String typeFieldName, boolean maintainType) { + private RuntimeTypeAdapterFactory(Class baseType, String typeFieldName, boolean maintainType) { if (typeFieldName == null || baseType == null) { throw new NullPointerException(); } @@ -151,34 +173,35 @@ private RuntimeTypeAdapterFactory( } /** - * Creates a new runtime type adapter using for {@code baseType} using {@code - * typeFieldName} as the type field name. Type field names are case sensitive. + * Creates a new runtime type adapter using for {@code baseType} using {@code typeFieldName} as + * the type field name. Type field names are case sensitive. * * @param maintainType true if the type field should be included in deserialized objects */ - public static RuntimeTypeAdapterFactory of(Class baseType, String typeFieldName, boolean maintainType) { + public static RuntimeTypeAdapterFactory of( + Class baseType, String typeFieldName, boolean maintainType) { return new RuntimeTypeAdapterFactory<>(baseType, typeFieldName, maintainType); } /** - * Creates a new runtime type adapter using for {@code baseType} using {@code - * typeFieldName} as the type field name. Type field names are case sensitive. + * Creates a new runtime type adapter using for {@code baseType} using {@code typeFieldName} as + * the type field name. Type field names are case sensitive. */ public static RuntimeTypeAdapterFactory of(Class baseType, String typeFieldName) { return new RuntimeTypeAdapterFactory<>(baseType, typeFieldName, false); } /** - * Creates a new runtime type adapter for {@code baseType} using {@code "type"} as - * the type field name. + * Creates a new runtime type adapter for {@code baseType} using {@code "type"} as the type field + * name. */ public static RuntimeTypeAdapterFactory of(Class baseType) { return new RuntimeTypeAdapterFactory<>(baseType, "type", false); } /** - * Ensures that this factory will handle not just the given {@code baseType}, but any subtype - * of that type. + * Ensures that this factory will handle not just the given {@code baseType}, but any subtype of + * that type. */ @CanIgnoreReturnValue public RuntimeTypeAdapterFactory recognizeSubtypes() { @@ -187,11 +210,10 @@ public RuntimeTypeAdapterFactory recognizeSubtypes() { } /** - * Registers {@code type} identified by {@code label}. Labels are case - * sensitive. + * Registers {@code type} identified by {@code label}. Labels are case sensitive. * - * @throws IllegalArgumentException if either {@code type} or {@code label} - * have already been registered on this type adapter. + * @throws IllegalArgumentException if either {@code type} or {@code label} have already been + * registered on this type adapter. */ @CanIgnoreReturnValue public RuntimeTypeAdapterFactory registerSubtype(Class type, String label) { @@ -207,11 +229,11 @@ public RuntimeTypeAdapterFactory registerSubtype(Class type, Str } /** - * Registers {@code type} identified by its {@link Class#getSimpleName simple - * name}. Labels are case sensitive. + * Registers {@code type} identified by its {@link Class#getSimpleName simple name}. Labels are + * case sensitive. * - * @throws IllegalArgumentException if either {@code type} or its simple name - * have already been registered on this type adapter. + * @throws IllegalArgumentException if either {@code type} or its simple name have already been + * registered on this type adapter. */ @CanIgnoreReturnValue public RuntimeTypeAdapterFactory registerSubtype(Class type) { @@ -240,37 +262,46 @@ public TypeAdapter create(Gson gson, TypeToken type) { } return new TypeAdapter() { - @Override public R read(JsonReader in) throws IOException { + @Override + public R read(JsonReader in) throws IOException { JsonElement jsonElement = jsonElementAdapter.read(in); JsonElement labelJsonElement; if (maintainType) { - labelJsonElement = jsonElement.getAsJsonObject().get(typeFieldName); + labelJsonElement = jsonElement.getAsJsonObject().get(typeFieldName); } else { - labelJsonElement = jsonElement.getAsJsonObject().remove(typeFieldName); + labelJsonElement = jsonElement.getAsJsonObject().remove(typeFieldName); } if (labelJsonElement == null) { - throw new JsonParseException("cannot deserialize " + baseType - + " because it does not define a field named " + typeFieldName); + throw new JsonParseException( + "cannot deserialize " + + baseType + + " because it does not define a field named " + + typeFieldName); } String label = labelJsonElement.getAsString(); @SuppressWarnings("unchecked") // registration requires that subtype extends T TypeAdapter delegate = (TypeAdapter) labelToDelegate.get(label); if (delegate == null) { - throw new JsonParseException("cannot deserialize " + baseType + " subtype named " - + label + "; did you forget to register a subtype?"); + throw new JsonParseException( + "cannot deserialize " + + baseType + + " subtype named " + + label + + "; did you forget to register a subtype?"); } return delegate.fromJsonTree(jsonElement); } - @Override public void write(JsonWriter out, R value) throws IOException { + @Override + public void write(JsonWriter out, R value) throws IOException { Class srcType = value.getClass(); String label = subtypeToLabel.get(srcType); @SuppressWarnings("unchecked") // registration requires that subtype extends T TypeAdapter delegate = (TypeAdapter) subtypeToDelegate.get(srcType); if (delegate == null) { - throw new JsonParseException("cannot serialize " + srcType.getName() - + "; did you forget to register a subtype?"); + throw new JsonParseException( + "cannot serialize " + srcType.getName() + "; did you forget to register a subtype?"); } JsonObject jsonObject = delegate.toJsonTree(value).getAsJsonObject(); @@ -282,8 +313,11 @@ public TypeAdapter create(Gson gson, TypeToken type) { JsonObject clone = new JsonObject(); if (jsonObject.has(typeFieldName)) { - throw new JsonParseException("cannot serialize " + srcType.getName() - + " because it already defines a field named " + typeFieldName); + throw new JsonParseException( + "cannot serialize " + + srcType.getName() + + " because it already defines a field named " + + typeFieldName); } clone.add(typeFieldName, new JsonPrimitive(label)); diff --git a/extras/src/main/java/com/google/gson/typeadapters/UtcDateTypeAdapter.java b/extras/src/main/java/com/google/gson/typeadapters/UtcDateTypeAdapter.java index fb4196f409..b3e01a1881 100644 --- a/extras/src/main/java/com/google/gson/typeadapters/UtcDateTypeAdapter.java +++ b/extras/src/main/java/com/google/gson/typeadapters/UtcDateTypeAdapter.java @@ -16,6 +16,10 @@ package com.google.gson.typeadapters; +import com.google.gson.JsonParseException; +import com.google.gson.TypeAdapter; +import com.google.gson.stream.JsonReader; +import com.google.gson.stream.JsonWriter; import java.io.IOException; import java.text.ParseException; import java.text.ParsePosition; @@ -24,11 +28,6 @@ import java.util.GregorianCalendar; import java.util.Locale; import java.util.TimeZone; -import com.google.gson.JsonParseException; -import com.google.gson.TypeAdapter; -import com.google.gson.stream.JsonReader; -import com.google.gson.stream.JsonToken; -import com.google.gson.stream.JsonWriter; public final class UtcDateTypeAdapter extends TypeAdapter { private final TimeZone UTC_TIME_ZONE = TimeZone.getTimeZone("UTC"); @@ -74,45 +73,46 @@ public Date read(JsonReader in) throws IOException { * @return the date formatted as yyyy-MM-ddThh:mm:ss[.sss][Z|[+-]hh:mm] */ private static String format(Date date, boolean millis, TimeZone tz) { - Calendar calendar = new GregorianCalendar(tz, Locale.US); - calendar.setTime(date); + Calendar calendar = new GregorianCalendar(tz, Locale.US); + calendar.setTime(date); - // estimate capacity of buffer as close as we can (yeah, that's pedantic ;) - int capacity = "yyyy-MM-ddThh:mm:ss".length(); - capacity += millis ? ".sss".length() : 0; - capacity += tz.getRawOffset() == 0 ? "Z".length() : "+hh:mm".length(); - StringBuilder formatted = new StringBuilder(capacity); + // estimate capacity of buffer as close as we can (yeah, that's pedantic ;) + int capacity = "yyyy-MM-ddThh:mm:ss".length(); + capacity += millis ? ".sss".length() : 0; + capacity += tz.getRawOffset() == 0 ? "Z".length() : "+hh:mm".length(); + StringBuilder formatted = new StringBuilder(capacity); - padInt(formatted, calendar.get(Calendar.YEAR), "yyyy".length()); - formatted.append('-'); - padInt(formatted, calendar.get(Calendar.MONTH) + 1, "MM".length()); - formatted.append('-'); - padInt(formatted, calendar.get(Calendar.DAY_OF_MONTH), "dd".length()); - formatted.append('T'); - padInt(formatted, calendar.get(Calendar.HOUR_OF_DAY), "hh".length()); - formatted.append(':'); - padInt(formatted, calendar.get(Calendar.MINUTE), "mm".length()); - formatted.append(':'); - padInt(formatted, calendar.get(Calendar.SECOND), "ss".length()); - if (millis) { - formatted.append('.'); - padInt(formatted, calendar.get(Calendar.MILLISECOND), "sss".length()); - } + padInt(formatted, calendar.get(Calendar.YEAR), "yyyy".length()); + formatted.append('-'); + padInt(formatted, calendar.get(Calendar.MONTH) + 1, "MM".length()); + formatted.append('-'); + padInt(formatted, calendar.get(Calendar.DAY_OF_MONTH), "dd".length()); + formatted.append('T'); + padInt(formatted, calendar.get(Calendar.HOUR_OF_DAY), "hh".length()); + formatted.append(':'); + padInt(formatted, calendar.get(Calendar.MINUTE), "mm".length()); + formatted.append(':'); + padInt(formatted, calendar.get(Calendar.SECOND), "ss".length()); + if (millis) { + formatted.append('.'); + padInt(formatted, calendar.get(Calendar.MILLISECOND), "sss".length()); + } - int offset = tz.getOffset(calendar.getTimeInMillis()); - if (offset != 0) { - int hours = Math.abs((offset / (60 * 1000)) / 60); - int minutes = Math.abs((offset / (60 * 1000)) % 60); - formatted.append(offset < 0 ? '-' : '+'); - padInt(formatted, hours, "hh".length()); - formatted.append(':'); - padInt(formatted, minutes, "mm".length()); - } else { - formatted.append('Z'); - } + int offset = tz.getOffset(calendar.getTimeInMillis()); + if (offset != 0) { + int hours = Math.abs((offset / (60 * 1000)) / 60); + int minutes = Math.abs((offset / (60 * 1000)) % 60); + formatted.append(offset < 0 ? '-' : '+'); + padInt(formatted, hours, "hh".length()); + formatted.append(':'); + padInt(formatted, minutes, "mm".length()); + } else { + formatted.append('Z'); + } - return formatted.toString(); + return formatted.toString(); } + /** * Zero pad a number to a specified length * @@ -121,11 +121,11 @@ private static String format(Date date, boolean millis, TimeZone tz) { * @param length the length of the string we should zero pad */ private static void padInt(StringBuilder buffer, int value, int length) { - String strValue = Integer.toString(value); - for (int i = length - strValue.length(); i > 0; i--) { - buffer.append('0'); - } - buffer.append(strValue); + String strValue = Integer.toString(value); + for (int i = length - strValue.length(); i > 0; i--) { + buffer.append('0'); + } + buffer.append(strValue); } /** @@ -160,7 +160,8 @@ private static Date parse(String date, ParsePosition pos) throws ParseException int hour = 0; int minutes = 0; int seconds = 0; - int milliseconds = 0; // always use 0 otherwise returned date will include millis of current time + // always use 0 otherwise returned date will include millis of current time + int milliseconds = 0; if (checkOffset(date, offset, 'T')) { // extract hours, minutes, seconds and milliseconds @@ -230,7 +231,8 @@ private static Date parse(String date, ParsePosition pos) throws ParseException fail = e; } String input = (date == null) ? null : ("'" + date + "'"); - throw new ParseException("Failed to parse date [" + input + "]: " + fail.getMessage(), pos.getIndex()); + throw new ParseException( + "Failed to parse date [" + input + "]: " + fail.getMessage(), pos.getIndex()); } /** @@ -254,7 +256,8 @@ private static boolean checkOffset(String value, int offset, char expected) { * @return the int * @throws NumberFormatException if the value is not a number */ - private static int parseInt(String value, int beginIndex, int endIndex) throws NumberFormatException { + private static int parseInt(String value, int beginIndex, int endIndex) + throws NumberFormatException { if (beginIndex < 0 || endIndex > value.length() || beginIndex > endIndex) { throw new NumberFormatException(value); } diff --git a/extras/src/test/java/com/google/gson/graph/GraphAdapterBuilderTest.java b/extras/src/test/java/com/google/gson/graph/GraphAdapterBuilderTest.java index 9c9503ccfa..5f4b986f53 100644 --- a/extras/src/test/java/com/google/gson/graph/GraphAdapterBuilderTest.java +++ b/extras/src/test/java/com/google/gson/graph/GraphAdapterBuilderTest.java @@ -19,17 +19,15 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertSame; +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.reflect.TypeToken; import java.lang.reflect.Type; import java.util.ArrayList; import java.util.Collections; import java.util.List; - import org.junit.Test; -import com.google.gson.Gson; -import com.google.gson.GsonBuilder; -import com.google.gson.reflect.TypeToken; - public final class GraphAdapterBuilderTest { @Test public void testSerialization() { @@ -41,27 +39,25 @@ public void testSerialization() { paper.beats = rock; GsonBuilder gsonBuilder = new GsonBuilder(); - new GraphAdapterBuilder() - .addType(Roshambo.class) - .registerOn(gsonBuilder); + new GraphAdapterBuilder().addType(Roshambo.class).registerOn(gsonBuilder); Gson gson = gsonBuilder.create(); - assertEquals("{'0x1':{'name':'ROCK','beats':'0x2'}," + - "'0x2':{'name':'SCISSORS','beats':'0x3'}," + - "'0x3':{'name':'PAPER','beats':'0x1'}}", + assertEquals( + "{'0x1':{'name':'ROCK','beats':'0x2'}," + + "'0x2':{'name':'SCISSORS','beats':'0x3'}," + + "'0x3':{'name':'PAPER','beats':'0x1'}}", gson.toJson(rock).replace('"', '\'')); } @Test public void testDeserialization() { - String json = "{'0x1':{'name':'ROCK','beats':'0x2'}," + - "'0x2':{'name':'SCISSORS','beats':'0x3'}," + - "'0x3':{'name':'PAPER','beats':'0x1'}}"; + String json = + "{'0x1':{'name':'ROCK','beats':'0x2'}," + + "'0x2':{'name':'SCISSORS','beats':'0x3'}," + + "'0x3':{'name':'PAPER','beats':'0x1'}}"; GsonBuilder gsonBuilder = new GsonBuilder(); - new GraphAdapterBuilder() - .addType(Roshambo.class) - .registerOn(gsonBuilder); + new GraphAdapterBuilder().addType(Roshambo.class).registerOn(gsonBuilder); Gson gson = gsonBuilder.create(); Roshambo rock = gson.fromJson(json, Roshambo.class); @@ -78,9 +74,7 @@ public void testDeserializationDirectSelfReference() { String json = "{'0x1':{'name':'SUICIDE','beats':'0x1'}}"; GsonBuilder gsonBuilder = new GsonBuilder(); - new GraphAdapterBuilder() - .addType(Roshambo.class) - .registerOn(gsonBuilder); + new GraphAdapterBuilder().addType(Roshambo.class).registerOn(gsonBuilder); Gson gson = gsonBuilder.create(); Roshambo suicide = gson.fromJson(json, Roshambo.class); @@ -140,9 +134,10 @@ public void testSerializationWithMultipleTypes() { .registerOn(gsonBuilder); Gson gson = gsonBuilder.create(); - assertEquals("{'0x1':{'name':'Google','employees':['0x2','0x3']}," - + "'0x2':{'name':'Jesse','company':'0x1'}," - + "'0x3':{'name':'Joel','company':'0x1'}}", + assertEquals( + "{'0x1':{'name':'Google','employees':['0x2','0x3']}," + + "'0x2':{'name':'Jesse','company':'0x1'}," + + "'0x3':{'name':'Joel','company':'0x1'}}", gson.toJson(google).replace('"', '\'')); } @@ -155,9 +150,10 @@ public void testDeserializationWithMultipleTypes() { .registerOn(gsonBuilder); Gson gson = gsonBuilder.create(); - String json = "{'0x1':{'name':'Google','employees':['0x2','0x3']}," - + "'0x2':{'name':'Jesse','company':'0x1'}," - + "'0x3':{'name':'Joel','company':'0x1'}}"; + String json = + "{'0x1':{'name':'Google','employees':['0x2','0x3']}," + + "'0x2':{'name':'Jesse','company':'0x1'}," + + "'0x3':{'name':'Joel','company':'0x1'}}"; Company company = gson.fromJson(json, Company.class); assertEquals("Google", company.name); Employee jesse = company.employees.get(0); @@ -171,6 +167,7 @@ public void testDeserializationWithMultipleTypes() { static class Roshambo { String name; Roshambo beats; + Roshambo(String name) { this.name = name; } @@ -179,6 +176,7 @@ static class Roshambo { static class Employee { final String name; final Company company; + Employee(String name, Company company) { this.name = name; this.company = company; @@ -189,6 +187,7 @@ static class Employee { static class Company { final String name; final List employees = new ArrayList<>(); + Company(String name) { this.name = name; } diff --git a/extras/src/test/java/com/google/gson/interceptors/InterceptorTest.java b/extras/src/test/java/com/google/gson/interceptors/InterceptorTest.java index 83fa8615bf..2030d337cd 100644 --- a/extras/src/test/java/com/google/gson/interceptors/InterceptorTest.java +++ b/extras/src/test/java/com/google/gson/interceptors/InterceptorTest.java @@ -46,10 +46,11 @@ public final class InterceptorTest { @Before public void setUp() throws Exception { - this.gson = new GsonBuilder() - .registerTypeAdapterFactory(new InterceptorFactory()) - .enableComplexMapKeySerialization() - .create(); + this.gson = + new GsonBuilder() + .registerTypeAdapterFactory(new InterceptorFactory()) + .enableComplexMapKeySerialization() + .create(); } @Test @@ -57,7 +58,8 @@ public void testExceptionsPropagated() { try { gson.fromJson("{}", User.class); fail(); - } catch (JsonParseException expected) {} + } catch (JsonParseException expected) { + } } @Test @@ -68,27 +70,33 @@ public void testTopLevelClass() { @Test public void testList() { - List list = gson.fromJson("[{name:'bob',password:'pwd'}]", new TypeToken>(){}.getType()); + List list = + gson.fromJson("[{name:'bob',password:'pwd'}]", new TypeToken>() {}.getType()); User user = list.get(0); assertEquals(User.DEFAULT_EMAIL, user.email); } @Test public void testCollection() { - Collection list = gson.fromJson("[{name:'bob',password:'pwd'}]", new TypeToken>(){}.getType()); + Collection list = + gson.fromJson( + "[{name:'bob',password:'pwd'}]", new TypeToken>() {}.getType()); User user = list.iterator().next(); assertEquals(User.DEFAULT_EMAIL, user.email); } @Test public void testMapKeyAndValues() { - Type mapType = new TypeToken>(){}.getType(); + Type mapType = new TypeToken>() {}.getType(); try { gson.fromJson("[[{name:'bob',password:'pwd'},{}]]", mapType); fail(); - } catch (JsonSyntaxException expected) {} - Map map = gson.fromJson("[[{name:'bob',password:'pwd'},{city:'Mountain View',state:'CA',zip:'94043'}]]", - mapType); + } catch (JsonSyntaxException expected) { + } + Map map = + gson.fromJson( + "[[{name:'bob',password:'pwd'},{city:'Mountain View',state:'CA',zip:'94043'}]]", + mapType); Entry entry = map.entrySet().iterator().next(); assertEquals(User.DEFAULT_EMAIL, entry.getKey().email); assertEquals(Address.DEFAULT_FIRST_LINE, entry.getValue().firstLine); @@ -102,24 +110,29 @@ public void testField() { @Test public void testCustomTypeAdapter() { - Gson gson = new GsonBuilder() - .registerTypeAdapter(User.class, new TypeAdapter() { - @Override public void write(JsonWriter out, User value) throws IOException { - throw new UnsupportedOperationException(); - } - - @Override public User read(JsonReader in) throws IOException { - in.beginObject(); - String unused1 = in.nextName(); - String name = in.nextString(); - String unused2 = in.nextName(); - String password = in.nextString(); - in.endObject(); - return new User(name, password); - } - }) - .registerTypeAdapterFactory(new InterceptorFactory()) - .create(); + Gson gson = + new GsonBuilder() + .registerTypeAdapter( + User.class, + new TypeAdapter() { + @Override + public void write(JsonWriter out, User value) throws IOException { + throw new UnsupportedOperationException(); + } + + @Override + public User read(JsonReader in) throws IOException { + in.beginObject(); + String unused1 = in.nextName(); + String name = in.nextString(); + String unused2 = in.nextName(); + String password = in.nextString(); + in.endObject(); + return new User(name, password); + } + }) + .registerTypeAdapterFactory(new InterceptorFactory()) + .create(); UserGroup userGroup = gson.fromJson("{user:{name:'bob',password:'pwd'}}", UserGroup.class); assertEquals(User.DEFAULT_EMAIL, userGroup.user.email); } @@ -145,6 +158,7 @@ private static final class User { String password; String email; Address address; + public User(String name, String password) { this.name = name; this.password = password; @@ -152,7 +166,8 @@ public User(String name, String password) { } public static final class UserValidator implements JsonPostDeserializer { - @Override public void postDeserialize(User user) { + @Override + public void postDeserialize(User user) { if (user.name == null || user.password == null) { throw new JsonSyntaxException("name and password are required fields."); } @@ -172,7 +187,8 @@ private static final class Address { } public static final class AddressValidator implements JsonPostDeserializer
{ - @Override public void postDeserialize(Address address) { + @Override + public void postDeserialize(Address address) { if (address.city == null || address.state == null || address.zip == null) { throw new JsonSyntaxException("Address city, state and zip are required fields."); } diff --git a/extras/src/test/java/com/google/gson/typeadapters/PostConstructAdapterFactoryTest.java b/extras/src/test/java/com/google/gson/typeadapters/PostConstructAdapterFactoryTest.java index a4b9d04e7f..c8ccb6bf79 100644 --- a/extras/src/test/java/com/google/gson/typeadapters/PostConstructAdapterFactoryTest.java +++ b/extras/src/test/java/com/google/gson/typeadapters/PostConstructAdapterFactoryTest.java @@ -29,90 +29,96 @@ public class PostConstructAdapterFactoryTest { @Test public void test() throws Exception { - Gson gson = new GsonBuilder() - .registerTypeAdapterFactory(new PostConstructAdapterFactory()) - .create(); - gson.fromJson("{\"bread\": \"white\", \"cheese\": \"cheddar\"}", Sandwich.class); - try { - gson.fromJson("{\"bread\": \"cheesey bread\", \"cheese\": \"swiss\"}", Sandwich.class); - fail(); - } catch (IllegalArgumentException expected) { - assertEquals("too cheesey", expected.getMessage()); - } + Gson gson = + new GsonBuilder().registerTypeAdapterFactory(new PostConstructAdapterFactory()).create(); + gson.fromJson("{\"bread\": \"white\", \"cheese\": \"cheddar\"}", Sandwich.class); + try { + gson.fromJson("{\"bread\": \"cheesey bread\", \"cheese\": \"swiss\"}", Sandwich.class); + fail(); + } catch (IllegalArgumentException expected) { + assertEquals("too cheesey", expected.getMessage()); } + } @Test public void testList() { - MultipleSandwiches sandwiches = new MultipleSandwiches(Arrays.asList( - new Sandwich("white", "cheddar"), - new Sandwich("whole wheat", "swiss"))); + MultipleSandwiches sandwiches = + new MultipleSandwiches( + Arrays.asList(new Sandwich("white", "cheddar"), new Sandwich("whole wheat", "swiss"))); - Gson gson = new GsonBuilder().registerTypeAdapterFactory(new PostConstructAdapterFactory()).create(); + Gson gson = + new GsonBuilder().registerTypeAdapterFactory(new PostConstructAdapterFactory()).create(); - // Throws NullPointerException without the fix in https://github.com/google/gson/pull/1103 - String json = gson.toJson(sandwiches); - assertEquals("{\"sandwiches\":[{\"bread\":\"white\",\"cheese\":\"cheddar\"},{\"bread\":\"whole wheat\",\"cheese\":\"swiss\"}]}", json); + // Throws NullPointerException without the fix in https://github.com/google/gson/pull/1103 + String json = gson.toJson(sandwiches); + assertEquals( + "{\"sandwiches\":[{\"bread\":\"white\",\"cheese\":\"cheddar\"},{\"bread\":\"whole" + + " wheat\",\"cheese\":\"swiss\"}]}", + json); - MultipleSandwiches sandwichesFromJson = gson.fromJson(json, MultipleSandwiches.class); - assertEquals(sandwiches, sandwichesFromJson); - } + MultipleSandwiches sandwichesFromJson = gson.fromJson(json, MultipleSandwiches.class); + assertEquals(sandwiches, sandwichesFromJson); + } @SuppressWarnings({"overrides", "EqualsHashCode"}) // for missing hashCode() override static class Sandwich { - public String bread; - public String cheese; + public String bread; + public String cheese; - public Sandwich(String bread, String cheese) { - this.bread = bread; - this.cheese = cheese; - } + public Sandwich(String bread, String cheese) { + this.bread = bread; + this.cheese = cheese; + } - @PostConstruct private void validate() { - if (bread.equals("cheesey bread") && cheese != null) { - throw new IllegalArgumentException("too cheesey"); - } - } + @PostConstruct + private void validate() { + if (bread.equals("cheesey bread") && cheese != null) { + throw new IllegalArgumentException("too cheesey"); + } + } - @Override - public boolean equals(Object o) { - if (o == this) { - return true; - } - if (!(o instanceof Sandwich)) { - return false; - } - final Sandwich other = (Sandwich) o; - if (this.bread == null ? other.bread != null : !this.bread.equals(other.bread)) { - return false; - } - if (this.cheese == null ? other.cheese != null : !this.cheese.equals(other.cheese)) { - return false; - } - return true; - } + @Override + public boolean equals(Object o) { + if (o == this) { + return true; + } + if (!(o instanceof Sandwich)) { + return false; + } + final Sandwich other = (Sandwich) o; + if (this.bread == null ? other.bread != null : !this.bread.equals(other.bread)) { + return false; + } + if (this.cheese == null ? other.cheese != null : !this.cheese.equals(other.cheese)) { + return false; + } + return true; } + } - @SuppressWarnings({"overrides", "EqualsHashCode"}) // for missing hashCode() override - static class MultipleSandwiches { - public List sandwiches; + @SuppressWarnings({"overrides", "EqualsHashCode"}) // for missing hashCode() override + static class MultipleSandwiches { + public List sandwiches; - public MultipleSandwiches(List sandwiches) { - this.sandwiches = sandwiches; - } + public MultipleSandwiches(List sandwiches) { + this.sandwiches = sandwiches; + } - @Override - public boolean equals(Object o) { - if (o == this) { - return true; - } - if (!(o instanceof MultipleSandwiches)) { - return false; - } - final MultipleSandwiches other = (MultipleSandwiches) o; - if (this.sandwiches == null ? other.sandwiches != null : !this.sandwiches.equals(other.sandwiches)) { - return false; - } - return true; - } + @Override + public boolean equals(Object o) { + if (o == this) { + return true; + } + if (!(o instanceof MultipleSandwiches)) { + return false; + } + final MultipleSandwiches other = (MultipleSandwiches) o; + if (this.sandwiches == null + ? other.sandwiches != null + : !this.sandwiches.equals(other.sandwiches)) { + return false; + } + return true; } + } } diff --git a/extras/src/test/java/com/google/gson/typeadapters/RuntimeTypeAdapterFactoryTest.java b/extras/src/test/java/com/google/gson/typeadapters/RuntimeTypeAdapterFactoryTest.java index 1221b47bcc..b4018caedc 100644 --- a/extras/src/test/java/com/google/gson/typeadapters/RuntimeTypeAdapterFactoryTest.java +++ b/extras/src/test/java/com/google/gson/typeadapters/RuntimeTypeAdapterFactoryTest.java @@ -31,18 +31,16 @@ public final class RuntimeTypeAdapterFactoryTest { @Test public void testRuntimeTypeAdapter() { - RuntimeTypeAdapterFactory rta = RuntimeTypeAdapterFactory.of( - BillingInstrument.class) - .registerSubtype(CreditCard.class); - Gson gson = new GsonBuilder() - .registerTypeAdapterFactory(rta) - .create(); + RuntimeTypeAdapterFactory rta = + RuntimeTypeAdapterFactory.of(BillingInstrument.class).registerSubtype(CreditCard.class); + Gson gson = new GsonBuilder().registerTypeAdapterFactory(rta).create(); CreditCard original = new CreditCard("Jesse", 234); - assertEquals("{\"type\":\"CreditCard\",\"cvv\":234,\"ownerName\":\"Jesse\"}", + assertEquals( + "{\"type\":\"CreditCard\",\"cvv\":234,\"ownerName\":\"Jesse\"}", gson.toJson(original, BillingInstrument.class)); - BillingInstrument deserialized = gson.fromJson( - "{type:'CreditCard',cvv:234,ownerName:'Jesse'}", BillingInstrument.class); + BillingInstrument deserialized = + gson.fromJson("{type:'CreditCard',cvv:234,ownerName:'Jesse'}", BillingInstrument.class); assertEquals("Jesse", deserialized.ownerName); assertTrue(deserialized instanceof CreditCard); } @@ -52,37 +50,34 @@ public void testRuntimeTypeAdapterRecognizeSubtypes() { // We don't have an explicit factory for CreditCard.class, but we do have one for // BillingInstrument.class that has recognizeSubtypes(). So it should recognize CreditCard, and // when we call gson.toJson(original) below, without an explicit type, it should be invoked. - RuntimeTypeAdapterFactory rta = RuntimeTypeAdapterFactory.of( - BillingInstrument.class) - .recognizeSubtypes() - .registerSubtype(CreditCard.class); - Gson gson = new GsonBuilder() - .registerTypeAdapterFactory(rta) - .create(); + RuntimeTypeAdapterFactory rta = + RuntimeTypeAdapterFactory.of(BillingInstrument.class) + .recognizeSubtypes() + .registerSubtype(CreditCard.class); + Gson gson = new GsonBuilder().registerTypeAdapterFactory(rta).create(); CreditCard original = new CreditCard("Jesse", 234); - assertEquals("{\"type\":\"CreditCard\",\"cvv\":234,\"ownerName\":\"Jesse\"}", - gson.toJson(original)); - BillingInstrument deserialized = gson.fromJson( - "{type:'CreditCard',cvv:234,ownerName:'Jesse'}", BillingInstrument.class); + assertEquals( + "{\"type\":\"CreditCard\",\"cvv\":234,\"ownerName\":\"Jesse\"}", gson.toJson(original)); + BillingInstrument deserialized = + gson.fromJson("{type:'CreditCard',cvv:234,ownerName:'Jesse'}", BillingInstrument.class); assertEquals("Jesse", deserialized.ownerName); assertTrue(deserialized instanceof CreditCard); } @Test public void testRuntimeTypeIsBaseType() { - TypeAdapterFactory rta = RuntimeTypeAdapterFactory.of( - BillingInstrument.class) - .registerSubtype(BillingInstrument.class); - Gson gson = new GsonBuilder() - .registerTypeAdapterFactory(rta) - .create(); + TypeAdapterFactory rta = + RuntimeTypeAdapterFactory.of(BillingInstrument.class) + .registerSubtype(BillingInstrument.class); + Gson gson = new GsonBuilder().registerTypeAdapterFactory(rta).create(); BillingInstrument original = new BillingInstrument("Jesse"); - assertEquals("{\"type\":\"BillingInstrument\",\"ownerName\":\"Jesse\"}", + assertEquals( + "{\"type\":\"BillingInstrument\",\"ownerName\":\"Jesse\"}", gson.toJson(original, BillingInstrument.class)); - BillingInstrument deserialized = gson.fromJson( - "{type:'BillingInstrument',ownerName:'Jesse'}", BillingInstrument.class); + BillingInstrument deserialized = + gson.fromJson("{type:'BillingInstrument',ownerName:'Jesse'}", BillingInstrument.class); assertEquals("Jesse", deserialized.ownerName); } @@ -106,8 +101,8 @@ public void testNullTypeFieldName() { @Test public void testNullSubtype() { - RuntimeTypeAdapterFactory rta = RuntimeTypeAdapterFactory.of( - BillingInstrument.class); + RuntimeTypeAdapterFactory rta = + RuntimeTypeAdapterFactory.of(BillingInstrument.class); try { rta.registerSubtype(null); fail(); @@ -117,8 +112,8 @@ public void testNullSubtype() { @Test public void testNullLabel() { - RuntimeTypeAdapterFactory rta = RuntimeTypeAdapterFactory.of( - BillingInstrument.class); + RuntimeTypeAdapterFactory rta = + RuntimeTypeAdapterFactory.of(BillingInstrument.class); try { rta.registerSubtype(CreditCard.class, null); fail(); @@ -128,8 +123,8 @@ public void testNullLabel() { @Test public void testDuplicateSubtype() { - RuntimeTypeAdapterFactory rta = RuntimeTypeAdapterFactory.of( - BillingInstrument.class); + RuntimeTypeAdapterFactory rta = + RuntimeTypeAdapterFactory.of(BillingInstrument.class); rta.registerSubtype(CreditCard.class, "CC"); try { rta.registerSubtype(CreditCard.class, "Visa"); @@ -140,8 +135,8 @@ public void testDuplicateSubtype() { @Test public void testDuplicateLabel() { - RuntimeTypeAdapterFactory rta = RuntimeTypeAdapterFactory.of( - BillingInstrument.class); + RuntimeTypeAdapterFactory rta = + RuntimeTypeAdapterFactory.of(BillingInstrument.class); rta.registerSubtype(CreditCard.class, "CC"); try { rta.registerSubtype(BankTransfer.class, "CC"); @@ -152,11 +147,9 @@ public void testDuplicateLabel() { @Test public void testDeserializeMissingTypeField() { - TypeAdapterFactory billingAdapter = RuntimeTypeAdapterFactory.of(BillingInstrument.class) - .registerSubtype(CreditCard.class); - Gson gson = new GsonBuilder() - .registerTypeAdapterFactory(billingAdapter) - .create(); + TypeAdapterFactory billingAdapter = + RuntimeTypeAdapterFactory.of(BillingInstrument.class).registerSubtype(CreditCard.class); + Gson gson = new GsonBuilder().registerTypeAdapterFactory(billingAdapter).create(); try { gson.fromJson("{ownerName:'Jesse'}", BillingInstrument.class); fail(); @@ -166,11 +159,9 @@ public void testDeserializeMissingTypeField() { @Test public void testDeserializeMissingSubtype() { - TypeAdapterFactory billingAdapter = RuntimeTypeAdapterFactory.of(BillingInstrument.class) - .registerSubtype(BankTransfer.class); - Gson gson = new GsonBuilder() - .registerTypeAdapterFactory(billingAdapter) - .create(); + TypeAdapterFactory billingAdapter = + RuntimeTypeAdapterFactory.of(BillingInstrument.class).registerSubtype(BankTransfer.class); + Gson gson = new GsonBuilder().registerTypeAdapterFactory(billingAdapter).create(); try { gson.fromJson("{type:'CreditCard',ownerName:'Jesse'}", BillingInstrument.class); fail(); @@ -180,11 +171,9 @@ public void testDeserializeMissingSubtype() { @Test public void testSerializeMissingSubtype() { - TypeAdapterFactory billingAdapter = RuntimeTypeAdapterFactory.of(BillingInstrument.class) - .registerSubtype(BankTransfer.class); - Gson gson = new GsonBuilder() - .registerTypeAdapterFactory(billingAdapter) - .create(); + TypeAdapterFactory billingAdapter = + RuntimeTypeAdapterFactory.of(BillingInstrument.class).registerSubtype(BankTransfer.class); + Gson gson = new GsonBuilder().registerTypeAdapterFactory(billingAdapter).create(); try { gson.toJson(new CreditCard("Jesse", 456), BillingInstrument.class); fail(); @@ -194,11 +183,10 @@ public void testSerializeMissingSubtype() { @Test public void testSerializeCollidingTypeFieldName() { - TypeAdapterFactory billingAdapter = RuntimeTypeAdapterFactory.of(BillingInstrument.class, "cvv") - .registerSubtype(CreditCard.class); - Gson gson = new GsonBuilder() - .registerTypeAdapterFactory(billingAdapter) - .create(); + TypeAdapterFactory billingAdapter = + RuntimeTypeAdapterFactory.of(BillingInstrument.class, "cvv") + .registerSubtype(CreditCard.class); + Gson gson = new GsonBuilder().registerTypeAdapterFactory(billingAdapter).create(); try { gson.toJson(new CreditCard("Jesse", 456), BillingInstrument.class); fail(); @@ -208,19 +196,21 @@ public void testSerializeCollidingTypeFieldName() { @Test public void testSerializeWrappedNullValue() { - TypeAdapterFactory billingAdapter = RuntimeTypeAdapterFactory.of(BillingInstrument.class) - .registerSubtype(CreditCard.class) - .registerSubtype(BankTransfer.class); - Gson gson = new GsonBuilder() - .registerTypeAdapterFactory(billingAdapter) - .create(); - String serialized = gson.toJson(new BillingInstrumentWrapper(null), BillingInstrumentWrapper.class); - BillingInstrumentWrapper deserialized = gson.fromJson(serialized, BillingInstrumentWrapper.class); + TypeAdapterFactory billingAdapter = + RuntimeTypeAdapterFactory.of(BillingInstrument.class) + .registerSubtype(CreditCard.class) + .registerSubtype(BankTransfer.class); + Gson gson = new GsonBuilder().registerTypeAdapterFactory(billingAdapter).create(); + String serialized = + gson.toJson(new BillingInstrumentWrapper(null), BillingInstrumentWrapper.class); + BillingInstrumentWrapper deserialized = + gson.fromJson(serialized, BillingInstrumentWrapper.class); assertNull(deserialized.instrument); } static class BillingInstrumentWrapper { BillingInstrument instrument; + BillingInstrumentWrapper(BillingInstrument instrument) { this.instrument = instrument; } @@ -228,6 +218,7 @@ static class BillingInstrumentWrapper { static class BillingInstrument { private final String ownerName; + BillingInstrument(String ownerName) { this.ownerName = ownerName; } @@ -235,6 +226,7 @@ static class BillingInstrument { static class CreditCard extends BillingInstrument { int cvv; + CreditCard(String ownerName, int cvv) { super(ownerName); this.cvv = cvv; @@ -243,6 +235,7 @@ static class CreditCard extends BillingInstrument { static class BankTransfer extends BillingInstrument { int bankAccount; + BankTransfer(String ownerName, int bankAccount) { super(ownerName); this.bankAccount = bankAccount; diff --git a/extras/src/test/java/com/google/gson/typeadapters/UtcDateTypeAdapterTest.java b/extras/src/test/java/com/google/gson/typeadapters/UtcDateTypeAdapterTest.java index 23d5aeba6e..771e7648e9 100644 --- a/extras/src/test/java/com/google/gson/typeadapters/UtcDateTypeAdapterTest.java +++ b/extras/src/test/java/com/google/gson/typeadapters/UtcDateTypeAdapterTest.java @@ -31,9 +31,8 @@ @SuppressWarnings("JavaUtilDate") public final class UtcDateTypeAdapterTest { - private final Gson gson = new GsonBuilder() - .registerTypeAdapter(Date.class, new UtcDateTypeAdapter()) - .create(); + private final Gson gson = + new GsonBuilder().registerTypeAdapter(Date.class, new UtcDateTypeAdapter()).create(); @Test public void testLocalTimeZone() { @@ -56,21 +55,21 @@ public void testDifferentTimeZones() { } /** - * JDK 1.7 introduced support for XXX format to indicate UTC date. But Android is older JDK. - * We want to make sure that this date is parseable in Android. + * JDK 1.7 introduced support for XXX format to indicate UTC date. But Android is older JDK. We + * want to make sure that this date is parseable in Android. */ @Test public void testUtcDatesOnJdkBefore1_7() { - Gson gson = new GsonBuilder() - .registerTypeAdapter(Date.class, new UtcDateTypeAdapter()) - .create(); + Gson gson = + new GsonBuilder().registerTypeAdapter(Date.class, new UtcDateTypeAdapter()).create(); Date unused = gson.fromJson("'2014-12-05T04:00:00.000Z'", Date.class); } @Test public void testUtcWithJdk7Default() { Date expected = new Date(); - SimpleDateFormat iso8601Format = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSXXX", Locale.US); + SimpleDateFormat iso8601Format = + new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSXXX", Locale.US); iso8601Format.setTimeZone(TimeZone.getTimeZone("UTC")); String expectedJson = "\"" + iso8601Format.format(expected) + "\""; String actualJson = gson.toJson(expected); @@ -91,7 +90,9 @@ public void testWellFormedParseException() { gson.fromJson("2017-06-20T14:32:30", Date.class); fail("No exception"); } catch (JsonParseException exe) { - assertEquals("java.text.ParseException: Failed to parse date ['2017-06-20T14']: 2017-06-20T14", exe.getMessage()); + assertEquals( + "java.text.ParseException: Failed to parse date ['2017-06-20T14']: 2017-06-20T14", + exe.getMessage()); } } } diff --git a/graal-native-image-test/src/test/java/com/google/gson/native_test/Java17RecordReflectionTest.java b/graal-native-image-test/src/test/java/com/google/gson/native_test/Java17RecordReflectionTest.java index 51ea11d199..ea9123628d 100644 --- a/graal-native-image-test/src/test/java/com/google/gson/native_test/Java17RecordReflectionTest.java +++ b/graal-native-image-test/src/test/java/com/google/gson/native_test/Java17RecordReflectionTest.java @@ -28,8 +28,7 @@ import org.junit.jupiter.api.Test; class Java17RecordReflectionTest { - public record PublicRecord(int i) { - } + public record PublicRecord(int i) {} @Test void testPublicRecord() { @@ -39,8 +38,7 @@ void testPublicRecord() { } // Private record has implicit private canonical constructor - private record PrivateRecord(int i) { - } + private record PrivateRecord(int i) {} @Test void testPrivateRecord() { @@ -51,8 +49,7 @@ void testPrivateRecord() { @Test void testLocalRecord() { - record LocalRecordDeserialization(int i) { - } + record LocalRecordDeserialization(int i) {} Gson gson = new Gson(); LocalRecordDeserialization r = gson.fromJson("{\"i\":1}", LocalRecordDeserialization.class); @@ -61,20 +58,19 @@ record LocalRecordDeserialization(int i) { @Test void testLocalRecordSerialization() { - record LocalRecordSerialization(int i) { - } + record LocalRecordSerialization(int i) {} Gson gson = new Gson(); assertThat(gson.toJson(new LocalRecordSerialization(1))).isEqualTo("{\"i\":1}"); } - private record RecordWithSerializedName(@SerializedName("custom-name") int i) { - } + private record RecordWithSerializedName(@SerializedName("custom-name") int i) {} @Test void testSerializedName() { Gson gson = new Gson(); - RecordWithSerializedName r = gson.fromJson("{\"custom-name\":1}", RecordWithSerializedName.class); + RecordWithSerializedName r = + gson.fromJson("{\"custom-name\":1}", RecordWithSerializedName.class); assertThat(r.i).isEqualTo(1); assertThat(gson.toJson(new RecordWithSerializedName(2))).isEqualTo("{\"custom-name\":2}"); @@ -133,9 +129,7 @@ void testCustomClassAdapter() { } private record RecordWithCustomFieldAdapter( - @JsonAdapter(RecordWithCustomFieldAdapter.CustomAdapter.class) - int i - ) { + @JsonAdapter(RecordWithCustomFieldAdapter.CustomAdapter.class) int i) { private static class CustomAdapter extends TypeAdapter { @Override public Integer read(JsonReader in) throws IOException { @@ -158,24 +152,27 @@ void testCustomFieldAdapter() { assertThat(gson.toJson(new RecordWithCustomFieldAdapter(1))).isEqualTo("{\"i\":7}"); } - private record RecordWithRegisteredAdapter(int i) { - } + private record RecordWithRegisteredAdapter(int i) {} @Test void testCustomAdapter() { - Gson gson = new GsonBuilder() - .registerTypeAdapter(RecordWithRegisteredAdapter.class, new TypeAdapter() { - @Override - public RecordWithRegisteredAdapter read(JsonReader in) throws IOException { - return new RecordWithRegisteredAdapter(in.nextInt() + 5); - } - - @Override - public void write(JsonWriter out, RecordWithRegisteredAdapter value) throws IOException { - out.value(value.i + 6); - } - }) - .create(); + Gson gson = + new GsonBuilder() + .registerTypeAdapter( + RecordWithRegisteredAdapter.class, + new TypeAdapter() { + @Override + public RecordWithRegisteredAdapter read(JsonReader in) throws IOException { + return new RecordWithRegisteredAdapter(in.nextInt() + 5); + } + + @Override + public void write(JsonWriter out, RecordWithRegisteredAdapter value) + throws IOException { + out.value(value.i + 6); + } + }) + .create(); RecordWithRegisteredAdapter r = gson.fromJson("1", RecordWithRegisteredAdapter.class); assertThat(r.i).isEqualTo(6); diff --git a/graal-native-image-test/src/test/java/com/google/gson/native_test/ReflectionTest.java b/graal-native-image-test/src/test/java/com/google/gson/native_test/ReflectionTest.java index 3a2a5f48eb..055f452e25 100644 --- a/graal-native-image-test/src/test/java/com/google/gson/native_test/ReflectionTest.java +++ b/graal-native-image-test/src/test/java/com/google/gson/native_test/ReflectionTest.java @@ -56,7 +56,8 @@ private ClassWithCustomDefaultConstructor() { void testCustomDefaultConstructor() { Gson gson = new Gson(); - ClassWithCustomDefaultConstructor c = gson.fromJson("{\"i\":2}", ClassWithCustomDefaultConstructor.class); + ClassWithCustomDefaultConstructor c = + gson.fromJson("{\"i\":2}", ClassWithCustomDefaultConstructor.class); assertThat(c.i).isEqualTo(2); c = gson.fromJson("{}", ClassWithCustomDefaultConstructor.class); @@ -75,34 +76,41 @@ private ClassWithoutDefaultConstructor(int i) { /** * Tests deserializing a class without default constructor. * - *

This should use JDK Unsafe, and would normally require specifying {@code "unsafeAllocated": true} - * in the reflection metadata for GraalVM, though for some reason it also seems to work without it? Possibly - * because GraalVM seems to have special support for Gson, see its class {@code com.oracle.svm.thirdparty.gson.GsonFeature}. + *

This should use JDK Unsafe, and would normally require specifying {@code "unsafeAllocated": + * true} in the reflection metadata for GraalVM, though for some reason it also seems to work + * without it? Possibly because GraalVM seems to have special support for Gson, see its class + * {@code com.oracle.svm.thirdparty.gson.GsonFeature}. */ @Test void testClassWithoutDefaultConstructor() { Gson gson = new Gson(); - ClassWithoutDefaultConstructor c = gson.fromJson("{\"i\":1}", ClassWithoutDefaultConstructor.class); + ClassWithoutDefaultConstructor c = + gson.fromJson("{\"i\":1}", ClassWithoutDefaultConstructor.class); assertThat(c.i).isEqualTo(1); c = gson.fromJson("{}", ClassWithoutDefaultConstructor.class); - // Class is instantiated with JDK Unsafe, so field keeps its default value instead of assigned -1 + // Class is instantiated with JDK Unsafe, so field keeps its default value instead of assigned + // -1 assertThat(c.i).isEqualTo(0); } @Test void testInstanceCreator() { - Gson gson = new GsonBuilder() - .registerTypeAdapter(ClassWithoutDefaultConstructor.class, new InstanceCreator() { - @Override - public ClassWithoutDefaultConstructor createInstance(Type type) { - return new ClassWithoutDefaultConstructor(-2); - } - }) - .create(); - - ClassWithoutDefaultConstructor c = gson.fromJson("{\"i\":1}", ClassWithoutDefaultConstructor.class); + Gson gson = + new GsonBuilder() + .registerTypeAdapter( + ClassWithoutDefaultConstructor.class, + new InstanceCreator() { + @Override + public ClassWithoutDefaultConstructor createInstance(Type type) { + return new ClassWithoutDefaultConstructor(-2); + } + }) + .create(); + + ClassWithoutDefaultConstructor c = + gson.fromJson("{\"i\":1}", ClassWithoutDefaultConstructor.class); assertThat(c.i).isEqualTo(1); c = gson.fromJson("{}", ClassWithoutDefaultConstructor.class); @@ -220,19 +228,23 @@ private ClassWithRegisteredAdapter(int i) { @Test void testCustomAdapter() { - Gson gson = new GsonBuilder() - .registerTypeAdapter(ClassWithRegisteredAdapter.class, new TypeAdapter() { - @Override - public ClassWithRegisteredAdapter read(JsonReader in) throws IOException { - return new ClassWithRegisteredAdapter(in.nextInt() + 5); - } - - @Override - public void write(JsonWriter out, ClassWithRegisteredAdapter value) throws IOException { - out.value(value.i + 6); - } - }) - .create(); + Gson gson = + new GsonBuilder() + .registerTypeAdapter( + ClassWithRegisteredAdapter.class, + new TypeAdapter() { + @Override + public ClassWithRegisteredAdapter read(JsonReader in) throws IOException { + return new ClassWithRegisteredAdapter(in.nextInt() + 5); + } + + @Override + public void write(JsonWriter out, ClassWithRegisteredAdapter value) + throws IOException { + out.value(value.i + 6); + } + }) + .create(); ClassWithRegisteredAdapter c = gson.fromJson("1", ClassWithRegisteredAdapter.class); assertThat(c.i).isEqualTo(6); @@ -244,12 +256,17 @@ public void write(JsonWriter out, ClassWithRegisteredAdapter value) throws IOExc void testGenerics() { Gson gson = new Gson(); - List list = gson.fromJson("[{\"i\":1}]", new TypeToken>() {}); + List list = + gson.fromJson("[{\"i\":1}]", new TypeToken>() {}); assertThat(list).hasSize(1); assertThat(list.get(0).i).isEqualTo(1); @SuppressWarnings("unchecked") - List list2 = (List) gson.fromJson("[{\"i\":1}]", TypeToken.getParameterized(List.class, ClassWithDefaultConstructor.class)); + List list2 = + (List) + gson.fromJson( + "[{\"i\":1}]", + TypeToken.getParameterized(List.class, ClassWithDefaultConstructor.class)); assertThat(list2).hasSize(1); assertThat(list2.get(0).i).isEqualTo(1); } diff --git a/gson/pom.xml b/gson/pom.xml index 8529bb1304..172d3b3c48 100644 --- a/gson/pom.xml +++ b/gson/pom.xml @@ -75,7 +75,7 @@ org.codehaus.mojo templating-maven-plugin - 1.0.0 + 3.0.0 filtering-java-templates diff --git a/gson/src/main/java/com/google/gson/ExclusionStrategy.java b/gson/src/main/java/com/google/gson/ExclusionStrategy.java index 516666ee76..4f5d36b28d 100644 --- a/gson/src/main/java/com/google/gson/ExclusionStrategy.java +++ b/gson/src/main/java/com/google/gson/ExclusionStrategy.java @@ -17,12 +17,13 @@ package com.google.gson; /** - * A strategy (or policy) definition that is used to decide whether or not a field or - * class should be serialized or deserialized as part of the JSON output/input. + * A strategy (or policy) definition that is used to decide whether or not a field or class should + * be serialized or deserialized as part of the JSON output/input. * *

The following are a few examples that shows how you can use this exclusion mechanism. * *

Exclude fields and objects based on a particular class type: + * *

  * private static class SpecificClassExclusionStrategy implements ExclusionStrategy {
  *   private final Class<?> excludedThisClass;
@@ -42,6 +43,7 @@
  * 
* *

Excludes fields and objects based on a particular annotation: + * *

  * public @interface FooAnnotation {
  *   // some implementation here
@@ -59,9 +61,10 @@
  * }
  * 
* - *

Now if you want to configure {@code Gson} to use a user defined exclusion strategy, then - * the {@code GsonBuilder} is required. The following is an example of how you can use the - * {@code GsonBuilder} to configure Gson to use one of the above samples: + *

Now if you want to configure {@code Gson} to use a user defined exclusion strategy, then the + * {@code GsonBuilder} is required. The following is an example of how you can use the {@code + * GsonBuilder} to configure Gson to use one of the above samples: + * *

  * ExclusionStrategy excludeStrings = new UserDefinedExclusionStrategy(String.class);
  * Gson gson = new GsonBuilder()
@@ -70,10 +73,10 @@
  * 
* *

For certain model classes, you may only want to serialize a field, but exclude it for - * deserialization. To do that, you can write an {@code ExclusionStrategy} as per normal; - * however, you would register it with the - * {@link GsonBuilder#addDeserializationExclusionStrategy(ExclusionStrategy)} method. - * For example: + * deserialization. To do that, you can write an {@code ExclusionStrategy} as per normal; however, + * you would register it with the {@link + * GsonBuilder#addDeserializationExclusionStrategy(ExclusionStrategy)} method. For example: + * *

  * ExclusionStrategy excludeStrings = new UserDefinedExclusionStrategy(String.class);
  * Gson gson = new GsonBuilder()
@@ -83,11 +86,9 @@
  *
  * @author Inderjeet Singh
  * @author Joel Leitch
- *
  * @see GsonBuilder#setExclusionStrategies(ExclusionStrategy...)
  * @see GsonBuilder#addDeserializationExclusionStrategy(ExclusionStrategy)
  * @see GsonBuilder#addSerializationExclusionStrategy(ExclusionStrategy)
- *
  * @since 1.4
  */
 public interface ExclusionStrategy {
diff --git a/gson/src/main/java/com/google/gson/FieldAttributes.java b/gson/src/main/java/com/google/gson/FieldAttributes.java
index 4dce2b7cc4..b4d3709fb0 100644
--- a/gson/src/main/java/com/google/gson/FieldAttributes.java
+++ b/gson/src/main/java/com/google/gson/FieldAttributes.java
@@ -30,7 +30,6 @@
  *
  * @author Inderjeet Singh
  * @author Joel Leitch
- *
  * @since 1.4
  */
 public final class FieldAttributes {
@@ -67,6 +66,7 @@ public String getName() {
    * Returns the declared generic type of the field.
    *
    * 

For example, assume the following class definition: + * *

    * public class Foo {
    *   private String bar;
@@ -76,8 +76,8 @@ public String getName() {
    * Type listParameterizedType = new TypeToken<List<String>>() {}.getType();
    * 
* - *

This method would return {@code String.class} for the {@code bar} field and - * {@code listParameterizedType} for the {@code red} field. + *

This method would return {@code String.class} for the {@code bar} field and {@code + * listParameterizedType} for the {@code red} field. * * @return the specific type declared for this field */ @@ -89,6 +89,7 @@ public Type getDeclaredType() { * Returns the {@code Class} object that was declared for this field. * *

For example, assume the following class definition: + * *

    * public class Foo {
    *   private String bar;
@@ -96,8 +97,8 @@ public Type getDeclaredType() {
    * }
    * 
* - *

This method would return {@code String.class} for the {@code bar} field and - * {@code List.class} for the {@code red} field. + *

This method would return {@code String.class} for the {@code bar} field and {@code + * List.class} for the {@code red} field. * * @return the specific class object that was declared for the field */ @@ -106,8 +107,8 @@ public Class getDeclaredClass() { } /** - * Returns the {@code T} annotation object from this field if it exists; otherwise returns - * {@code null}. + * Returns the {@code T} annotation object from this field if it exists; otherwise returns {@code + * null}. * * @param annotation the class of the annotation that will be retrieved * @return the annotation instance if it is bound to the field; otherwise {@code null} @@ -130,6 +131,7 @@ public Collection getAnnotations() { * Returns {@code true} if the field is defined with the {@code modifier}. * *

This method is meant to be called as: + * *

    * boolean hasPublicModifier = fieldAttribute.hasModifier(java.lang.reflect.Modifier.PUBLIC);
    * 
diff --git a/gson/src/main/java/com/google/gson/FieldNamingPolicy.java b/gson/src/main/java/com/google/gson/FieldNamingPolicy.java index cd42f42cb6..e6d452d1a9 100644 --- a/gson/src/main/java/com/google/gson/FieldNamingPolicy.java +++ b/gson/src/main/java/com/google/gson/FieldNamingPolicy.java @@ -20,150 +20,161 @@ import java.util.Locale; /** - * An enumeration that defines a few standard naming conventions for JSON field names. - * This enumeration should be used in conjunction with {@link com.google.gson.GsonBuilder} - * to configure a {@link com.google.gson.Gson} instance to properly translate Java field - * names into the desired JSON field names. + * An enumeration that defines a few standard naming conventions for JSON field names. This + * enumeration should be used in conjunction with {@link com.google.gson.GsonBuilder} to configure a + * {@link com.google.gson.Gson} instance to properly translate Java field names into the desired + * JSON field names. * * @author Inderjeet Singh * @author Joel Leitch */ public enum FieldNamingPolicy implements FieldNamingStrategy { - /** - * Using this naming policy with Gson will ensure that the field name is - * unchanged. - */ + /** Using this naming policy with Gson will ensure that the field name is unchanged. */ IDENTITY() { - @Override public String translateName(Field f) { + @Override + public String translateName(Field f) { return f.getName(); } }, /** - * Using this naming policy with Gson will ensure that the first "letter" of the Java - * field name is capitalized when serialized to its JSON form. + * Using this naming policy with Gson will ensure that the first "letter" of the Java field name + * is capitalized when serialized to its JSON form. + * + *

Here are a few examples of the form "Java Field Name" ---> "JSON Field Name": * - *

Here are a few examples of the form "Java Field Name" ---> "JSON Field Name":

*
    - *
  • someFieldName ---> SomeFieldName
  • - *
  • _someFieldName ---> _SomeFieldName
  • + *
  • someFieldName ---> SomeFieldName + *
  • _someFieldName ---> _SomeFieldName *
*/ UPPER_CAMEL_CASE() { - @Override public String translateName(Field f) { + @Override + public String translateName(Field f) { return upperCaseFirstLetter(f.getName()); } }, /** - * Using this naming policy with Gson will ensure that the first "letter" of the Java - * field name is capitalized when serialized to its JSON form and the words will be - * separated by a space. + * Using this naming policy with Gson will ensure that the first "letter" of the Java field name + * is capitalized when serialized to its JSON form and the words will be separated by a space. + * + *

Here are a few examples of the form "Java Field Name" ---> "JSON Field Name": * - *

Here are a few examples of the form "Java Field Name" ---> "JSON Field Name":

*
    - *
  • someFieldName ---> Some Field Name
  • - *
  • _someFieldName ---> _Some Field Name
  • + *
  • someFieldName ---> Some Field Name + *
  • _someFieldName ---> _Some Field Name *
* * @since 1.4 */ UPPER_CAMEL_CASE_WITH_SPACES() { - @Override public String translateName(Field f) { + @Override + public String translateName(Field f) { return upperCaseFirstLetter(separateCamelCase(f.getName(), ' ')); } }, /** - * Using this naming policy with Gson will modify the Java Field name from its camel cased - * form to an upper case field name where each word is separated by an underscore (_). + * Using this naming policy with Gson will modify the Java Field name from its camel cased form to + * an upper case field name where each word is separated by an underscore (_). + * + *

Here are a few examples of the form "Java Field Name" ---> "JSON Field Name": * - *

Here are a few examples of the form "Java Field Name" ---> "JSON Field Name":

*
    - *
  • someFieldName ---> SOME_FIELD_NAME
  • - *
  • _someFieldName ---> _SOME_FIELD_NAME
  • - *
  • aStringField ---> A_STRING_FIELD
  • - *
  • aURL ---> A_U_R_L
  • + *
  • someFieldName ---> SOME_FIELD_NAME + *
  • _someFieldName ---> _SOME_FIELD_NAME + *
  • aStringField ---> A_STRING_FIELD + *
  • aURL ---> A_U_R_L *
* * @since 2.9.0 */ UPPER_CASE_WITH_UNDERSCORES() { - @Override public String translateName(Field f) { + @Override + public String translateName(Field f) { return separateCamelCase(f.getName(), '_').toUpperCase(Locale.ENGLISH); } }, /** - * Using this naming policy with Gson will modify the Java Field name from its camel cased - * form to a lower case field name where each word is separated by an underscore (_). + * Using this naming policy with Gson will modify the Java Field name from its camel cased form to + * a lower case field name where each word is separated by an underscore (_). + * + *

Here are a few examples of the form "Java Field Name" ---> "JSON Field Name": * - *

Here are a few examples of the form "Java Field Name" ---> "JSON Field Name":

*
    - *
  • someFieldName ---> some_field_name
  • - *
  • _someFieldName ---> _some_field_name
  • - *
  • aStringField ---> a_string_field
  • - *
  • aURL ---> a_u_r_l
  • + *
  • someFieldName ---> some_field_name + *
  • _someFieldName ---> _some_field_name + *
  • aStringField ---> a_string_field + *
  • aURL ---> a_u_r_l *
*/ LOWER_CASE_WITH_UNDERSCORES() { - @Override public String translateName(Field f) { + @Override + public String translateName(Field f) { return separateCamelCase(f.getName(), '_').toLowerCase(Locale.ENGLISH); } }, /** - * Using this naming policy with Gson will modify the Java Field name from its camel cased - * form to a lower case field name where each word is separated by a dash (-). + * Using this naming policy with Gson will modify the Java Field name from its camel cased form to + * a lower case field name where each word is separated by a dash (-). + * + *

Here are a few examples of the form "Java Field Name" ---> "JSON Field Name": * - *

Here are a few examples of the form "Java Field Name" ---> "JSON Field Name":

*
    - *
  • someFieldName ---> some-field-name
  • - *
  • _someFieldName ---> _some-field-name
  • - *
  • aStringField ---> a-string-field
  • - *
  • aURL ---> a-u-r-l
  • + *
  • someFieldName ---> some-field-name + *
  • _someFieldName ---> _some-field-name + *
  • aStringField ---> a-string-field + *
  • aURL ---> a-u-r-l *
+ * * Using dashes in JavaScript is not recommended since dash is also used for a minus sign in * expressions. This requires that a field named with dashes is always accessed as a quoted - * property like {@code myobject['my-field']}. Accessing it as an object field - * {@code myobject.my-field} will result in an unintended JavaScript expression. + * property like {@code myobject['my-field']}. Accessing it as an object field {@code + * myobject.my-field} will result in an unintended JavaScript expression. * * @since 1.4 */ LOWER_CASE_WITH_DASHES() { - @Override public String translateName(Field f) { + @Override + public String translateName(Field f) { return separateCamelCase(f.getName(), '-').toLowerCase(Locale.ENGLISH); } }, /** - * Using this naming policy with Gson will modify the Java Field name from its camel cased - * form to a lower case field name where each word is separated by a dot (.). + * Using this naming policy with Gson will modify the Java Field name from its camel cased form to + * a lower case field name where each word is separated by a dot (.). + * + *

Here are a few examples of the form "Java Field Name" ---> "JSON Field Name": * - *

Here are a few examples of the form "Java Field Name" ---> "JSON Field Name":

*
    - *
  • someFieldName ---> some.field.name
  • - *
  • _someFieldName ---> _some.field.name
  • - *
  • aStringField ---> a.string.field
  • - *
  • aURL ---> a.u.r.l
  • + *
  • someFieldName ---> some.field.name + *
  • _someFieldName ---> _some.field.name + *
  • aStringField ---> a.string.field + *
  • aURL ---> a.u.r.l *
+ * * Using dots in JavaScript is not recommended since dot is also used for a member sign in - * expressions. This requires that a field named with dots is always accessed as a quoted - * property like {@code myobject['my.field']}. Accessing it as an object field - * {@code myobject.my.field} will result in an unintended JavaScript expression. + * expressions. This requires that a field named with dots is always accessed as a quoted property + * like {@code myobject['my.field']}. Accessing it as an object field {@code myobject.my.field} + * will result in an unintended JavaScript expression. * * @since 2.8.4 */ LOWER_CASE_WITH_DOTS() { - @Override public String translateName(Field f) { + @Override + public String translateName(Field f) { return separateCamelCase(f.getName(), '.').toLowerCase(Locale.ENGLISH); } }; /** - * Converts the field name that uses camel-case define word separation into - * separate words that are separated by the provided {@code separator}. + * Converts the field name that uses camel-case define word separation into separate words that + * are separated by the provided {@code separator}. */ static String separateCamelCase(String name, char separator) { StringBuilder translation = new StringBuilder(); @@ -177,9 +188,7 @@ static String separateCamelCase(String name, char separator) { return translation.toString(); } - /** - * Ensures the JSON field names begins with an upper case letter. - */ + /** Ensures the JSON field names begins with an upper case letter. */ static String upperCaseFirstLetter(String s) { int length = s.length(); for (int i = 0; i < length; i++) { diff --git a/gson/src/main/java/com/google/gson/FieldNamingStrategy.java b/gson/src/main/java/com/google/gson/FieldNamingStrategy.java index f2f7c489ac..541588696e 100644 --- a/gson/src/main/java/com/google/gson/FieldNamingStrategy.java +++ b/gson/src/main/java/com/google/gson/FieldNamingStrategy.java @@ -20,8 +20,8 @@ /** * A mechanism for providing custom field naming in Gson. This allows the client code to translate - * field names into a particular convention that is not supported as a normal Java field - * declaration rules. For example, Java does not support "-" characters in a field name. + * field names into a particular convention that is not supported as a normal Java field declaration + * rules. For example, Java does not support "-" characters in a field name. * * @author Inderjeet Singh * @author Joel Leitch diff --git a/gson/src/main/java/com/google/gson/FormattingStyle.java b/gson/src/main/java/com/google/gson/FormattingStyle.java index ff031cd4bc..11b9490812 100644 --- a/gson/src/main/java/com/google/gson/FormattingStyle.java +++ b/gson/src/main/java/com/google/gson/FormattingStyle.java @@ -22,8 +22,9 @@ /** * A class used to control what the serialization output looks like. * - *

It currently has the following configuration methods, but more methods - * might be added in the future: + *

It currently has the following configuration methods, but more methods might be added in the + * future: + * *

    *
  • {@link #withNewline(String)} *
  • {@link #withIndent(String)} @@ -33,7 +34,6 @@ * @see GsonBuilder#setFormattingStyle(FormattingStyle) * @see JsonWriter#setFormattingStyle(FormattingStyle) * @see Wikipedia Newline article - * * @since $next-version$ */ public class FormattingStyle { @@ -43,6 +43,7 @@ public class FormattingStyle { /** * The default compact formatting style: + * *
      *
    • no newline *
    • no indent @@ -53,14 +54,14 @@ public class FormattingStyle { /** * The default pretty printing formatting style: + * *
        *
      • {@code "\n"} as newline *
      • two spaces as indent *
      • a space between {@code ':'} and the subsequent value *
      */ - public static final FormattingStyle PRETTY = - new FormattingStyle("\n", " ", true); + public static final FormattingStyle PRETTY = new FormattingStyle("\n", " ", true); private FormattingStyle(String newline, String indent, boolean spaceAfterSeparators) { Objects.requireNonNull(newline, "newline == null"); @@ -81,11 +82,11 @@ private FormattingStyle(String newline, String indent, boolean spaceAfterSeparat /** * Creates a {@link FormattingStyle} with the specified newline setting. * - *

      It can be used to accommodate certain OS convention, for example - * hardcode {@code "\n"} for Linux and macOS, {@code "\r\n"} for Windows, or - * call {@link java.lang.System#lineSeparator()} to match the current OS.

      + *

      It can be used to accommodate certain OS convention, for example hardcode {@code "\n"} for + * Linux and macOS, {@code "\r\n"} for Windows, or call {@link java.lang.System#lineSeparator()} + * to match the current OS. * - *

      Only combinations of {@code \n} and {@code \r} are allowed.

      + *

      Only combinations of {@code \n} and {@code \r} are allowed. * * @param newline the string value that will be used as newline. * @return a newly created {@link FormattingStyle} @@ -97,7 +98,7 @@ public FormattingStyle withNewline(String newline) { /** * Creates a {@link FormattingStyle} with the specified indent string. * - *

      Only combinations of spaces and tabs allowed in indent.

      + *

      Only combinations of spaces and tabs allowed in indent. * * @param indent the string value that will be used as indent. * @return a newly created {@link FormattingStyle} @@ -107,12 +108,12 @@ public FormattingStyle withIndent(String indent) { } /** - * Creates a {@link FormattingStyle} which either uses a space after - * the separators {@code ','} and {@code ':'} in the JSON output, or not. + * Creates a {@link FormattingStyle} which either uses a space after the separators {@code ','} + * and {@code ':'} in the JSON output, or not. * - *

      This setting has no effect on the {@linkplain #withNewline(String) configured newline}. - * If a non-empty newline is configured, it will always be added after - * {@code ','} and no space is added after the {@code ','} in that case.

      + *

      This setting has no effect on the {@linkplain #withNewline(String) configured newline}. If a + * non-empty newline is configured, it will always be added after {@code ','} and no space is + * added after the {@code ','} in that case. * * @param spaceAfterSeparators whether to output a space after {@code ','} and {@code ':'}. * @return a newly created {@link FormattingStyle} @@ -139,9 +140,7 @@ public String getIndent() { return this.indent; } - /** - * Returns whether a space will be used after {@code ','} and {@code ':'}. - */ + /** Returns whether a space will be used after {@code ','} and {@code ':'}. */ public boolean usesSpaceAfterSeparators() { return this.spaceAfterSeparators; } diff --git a/gson/src/main/java/com/google/gson/Gson.java b/gson/src/main/java/com/google/gson/Gson.java index 1921eca3a5..1c6d949ce5 100644 --- a/gson/src/main/java/com/google/gson/Gson.java +++ b/gson/src/main/java/com/google/gson/Gson.java @@ -63,15 +63,14 @@ import java.util.concurrent.atomic.AtomicLongArray; /** - * This is the main class for using Gson. Gson is typically used by first constructing a - * Gson instance and then invoking {@link #toJson(Object)} or {@link #fromJson(String, Class)} - * methods on it. Gson instances are Thread-safe so you can reuse them freely across multiple - * threads. + * This is the main class for using Gson. Gson is typically used by first constructing a Gson + * instance and then invoking {@link #toJson(Object)} or {@link #fromJson(String, Class)} methods on + * it. Gson instances are Thread-safe so you can reuse them freely across multiple threads. * - *

      You can create a Gson instance by invoking {@code new Gson()} if the default configuration - * is all you need. You can also use {@link GsonBuilder} to build a Gson instance with various + *

      You can create a Gson instance by invoking {@code new Gson()} if the default configuration is + * all you need. You can also use {@link GsonBuilder} to build a Gson instance with various * configuration options such as versioning support, pretty printing, custom newline, custom indent, - * custom {@link JsonSerializer}s, {@link JsonDeserializer}s, and {@link InstanceCreator}s.

      + * custom {@link JsonSerializer}s, {@link JsonDeserializer}s, and {@link InstanceCreator}s. * *

      Here is an example of how Gson is used for a simple Class: * @@ -82,13 +81,13 @@ * MyType target2 = gson.fromJson(json, MyType.class); // deserializes json into target2 *

* - *

If the type of the object that you are converting is a {@code ParameterizedType} - * (i.e. has at least one type argument, for example {@code List}) then for - * deserialization you must use a {@code fromJson} method with {@link Type} or {@link TypeToken} - * parameter to specify the parameterized type. For serialization specifying a {@code Type} - * or {@code TypeToken} is optional, otherwise Gson will use the runtime type of the object. - * {@link TypeToken} is a class provided by Gson which helps creating parameterized types. - * Here is an example showing how this can be done: + *

If the type of the object that you are converting is a {@code ParameterizedType} (i.e. has at + * least one type argument, for example {@code List}) then for deserialization you must use + * a {@code fromJson} method with {@link Type} or {@link TypeToken} parameter to specify the + * parameterized type. For serialization specifying a {@code Type} or {@code TypeToken} is optional, + * otherwise Gson will use the runtime type of the object. {@link TypeToken} is a class provided by + * Gson which helps creating parameterized types. Here is an example showing how this can be done: + * *

  * TypeToken<List<MyType>> listType = new TypeToken<List<MyType>>() {};
  * List<MyType> target = new LinkedList<MyType>();
@@ -104,50 +103,53 @@
  * 
* *

See the Gson User Guide - * for a more complete set of examples.

+ * for a more complete set of examples. * *

JSON Strictness handling

- * For legacy reasons most of the {@code Gson} methods allow JSON data which does not - * comply with the JSON specification when no explicit {@linkplain Strictness strictness} is set (the default). - * To specify the strictness of a {@code Gson} instance, you should set it through - * {@link GsonBuilder#setStrictness(Strictness)}. * - *

For older Gson versions, which don't have the strictness mode API, the following - * workarounds can be used: + * For legacy reasons most of the {@code Gson} methods allow JSON data which does not comply with + * the JSON specification when no explicit {@linkplain Strictness strictness} is set (the default). + * To specify the strictness of a {@code Gson} instance, you should set it through {@link + * GsonBuilder#setStrictness(Strictness)}. + * + *

For older Gson versions, which don't have the strictness mode API, the following workarounds + * can be used: * *

Serialization

+ * *
    *
  1. Use {@link #getAdapter(Class)} to obtain the adapter for the type to be serialized *
  2. When using an existing {@code JsonWriter}, manually apply the writer settings of this * {@code Gson} instance listed by {@link #newJsonWriter(Writer)}.
    - * Otherwise, when not using an existing {@code JsonWriter}, use {@link #newJsonWriter(Writer)} - * to construct one. + * Otherwise, when not using an existing {@code JsonWriter}, use {@link + * #newJsonWriter(Writer)} to construct one. *
  3. Call {@link TypeAdapter#write(JsonWriter, Object)} *
* *

Deserialization

+ * *
    *
  1. Use {@link #getAdapter(Class)} to obtain the adapter for the type to be deserialized *
  2. When using an existing {@code JsonReader}, manually apply the reader settings of this * {@code Gson} instance listed by {@link #newJsonReader(Reader)}.
    - * Otherwise, when not using an existing {@code JsonReader}, use {@link #newJsonReader(Reader)} - * to construct one. + * Otherwise, when not using an existing {@code JsonReader}, use {@link + * #newJsonReader(Reader)} to construct one. *
  3. Call {@link TypeAdapter#read(JsonReader)} *
  4. Call {@link JsonReader#peek()} and verify that the result is {@link JsonToken#END_DOCUMENT} * to make sure there is no trailing data *
* - * Note that the {@code JsonReader} created this way is only 'legacy strict', it mostly adheres - * to the JSON specification but allows small deviations. See {@link JsonReader#setStrictness(Strictness)} - * for details. + * Note that the {@code JsonReader} created this way is only 'legacy strict', it mostly adheres to + * the JSON specification but allows small deviations. See {@link + * JsonReader#setStrictness(Strictness)} for details. * * @see TypeToken - * * @author Inderjeet Singh * @author Joel Leitch * @author Jesse Wilson */ public final class Gson { + static final boolean DEFAULT_JSON_NON_EXECUTABLE = false; // Strictness of `null` is the legacy mode where some Gson APIs are always lenient static final Strictness DEFAULT_STRICTNESS = null; @@ -160,26 +162,28 @@ public final class Gson { static final String DEFAULT_DATE_PATTERN = null; static final FieldNamingStrategy DEFAULT_FIELD_NAMING_STRATEGY = FieldNamingPolicy.IDENTITY; static final ToNumberStrategy DEFAULT_OBJECT_TO_NUMBER_STRATEGY = ToNumberPolicy.DOUBLE; - static final ToNumberStrategy DEFAULT_NUMBER_TO_NUMBER_STRATEGY = ToNumberPolicy.LAZILY_PARSED_NUMBER; + static final ToNumberStrategy DEFAULT_NUMBER_TO_NUMBER_STRATEGY = + ToNumberPolicy.LAZILY_PARSED_NUMBER; private static final String JSON_NON_EXECUTABLE_PREFIX = ")]}'\n"; /** - * This thread local guards against reentrant calls to {@link #getAdapter(TypeToken)}. - * In certain object graphs, creating an adapter for a type may recursively - * require an adapter for the same type! Without intervention, the recursive - * lookup would stack overflow. We cheat by returning a proxy type adapter, - * {@link FutureTypeAdapter}, which is wired up once the initial adapter has + * This thread local guards against reentrant calls to {@link #getAdapter(TypeToken)}. In certain + * object graphs, creating an adapter for a type may recursively require an adapter for the same + * type! Without intervention, the recursive lookup would stack overflow. We cheat by returning a + * proxy type adapter, {@link FutureTypeAdapter}, which is wired up once the initial adapter has * been created. * - *

The map stores the type adapters for ongoing {@code getAdapter} calls, - * with the type token provided to {@code getAdapter} as key and either - * {@code FutureTypeAdapter} or a regular {@code TypeAdapter} as value. + *

The map stores the type adapters for ongoing {@code getAdapter} calls, with the type token + * provided to {@code getAdapter} as key and either {@code FutureTypeAdapter} or a regular {@code + * TypeAdapter} as value. */ @SuppressWarnings("ThreadLocalUsage") - private final ThreadLocal, TypeAdapter>> threadLocalAdapterResults = new ThreadLocal<>(); + private final ThreadLocal, TypeAdapter>> threadLocalAdapterResults = + new ThreadLocal<>(); - private final ConcurrentMap, TypeAdapter> typeTokenCache = new ConcurrentHashMap<>(); + private final ConcurrentMap, TypeAdapter> typeTokenCache = + new ConcurrentHashMap<>(); private final ConstructorConstructor constructorConstructor; private final JsonAdapterAnnotationTypeAdapterFactory jsonAdapterFactory; @@ -210,68 +214,95 @@ public final class Gson { /** * Constructs a Gson object with default configuration. The default configuration has the * following settings: + * *

    *
  • The JSON generated by toJson methods is in compact representation. This - * means that all the unneeded white-space is removed. You can change this behavior with - * {@link GsonBuilder#setPrettyPrinting()}.
  • + * means that all the unneeded white-space is removed. You can change this behavior with + * {@link GsonBuilder#setPrettyPrinting()}. *
  • When the JSON generated contains more than one line, the kind of newline and indent to - * use can be configured with {@link GsonBuilder#setFormattingStyle(FormattingStyle)}.
  • - *
  • The generated JSON omits all the fields that are null. Note that nulls in arrays are - * kept as is since an array is an ordered list. Moreover, if a field is not null, but its - * generated JSON is empty, the field is kept. You can configure Gson to serialize null values - * by setting {@link GsonBuilder#serializeNulls()}.
  • - *
  • Gson provides default serialization and deserialization for Enums, {@link Map}, - * {@link java.net.URL}, {@link java.net.URI}, {@link java.util.Locale}, {@link java.util.Date}, - * {@link java.math.BigDecimal}, and {@link java.math.BigInteger} classes. If you would prefer - * to change the default representation, you can do so by registering a type adapter through - * {@link GsonBuilder#registerTypeAdapter(Type, Object)}.
  • + * use can be configured with {@link GsonBuilder#setFormattingStyle(FormattingStyle)}. + *
  • The generated JSON omits all the fields that are null. Note that nulls in arrays are kept + * as is since an array is an ordered list. Moreover, if a field is not null, but its + * generated JSON is empty, the field is kept. You can configure Gson to serialize null + * values by setting {@link GsonBuilder#serializeNulls()}. + *
  • Gson provides default serialization and deserialization for Enums, {@link Map}, {@link + * java.net.URL}, {@link java.net.URI}, {@link java.util.Locale}, {@link java.util.Date}, + * {@link java.math.BigDecimal}, and {@link java.math.BigInteger} classes. If you would + * prefer to change the default representation, you can do so by registering a type adapter + * through {@link GsonBuilder#registerTypeAdapter(Type, Object)}. *
  • The default Date format is same as {@link java.text.DateFormat#DEFAULT}. This format - * ignores the millisecond portion of the date during serialization. You can change - * this by invoking {@link GsonBuilder#setDateFormat(int)} or - * {@link GsonBuilder#setDateFormat(String)}.
  • - *
  • By default, Gson ignores the {@link com.google.gson.annotations.Expose} annotation. - * You can enable Gson to serialize/deserialize only those fields marked with this annotation - * through {@link GsonBuilder#excludeFieldsWithoutExposeAnnotation()}.
  • + * ignores the millisecond portion of the date during serialization. You can change this by + * invoking {@link GsonBuilder#setDateFormat(int)} or {@link + * GsonBuilder#setDateFormat(String)}. + *
  • By default, Gson ignores the {@link com.google.gson.annotations.Expose} annotation. You + * can enable Gson to serialize/deserialize only those fields marked with this annotation + * through {@link GsonBuilder#excludeFieldsWithoutExposeAnnotation()}. *
  • By default, Gson ignores the {@link com.google.gson.annotations.Since} annotation. You - * can enable Gson to use this annotation through {@link GsonBuilder#setVersion(double)}.
  • + * can enable Gson to use this annotation through {@link GsonBuilder#setVersion(double)}. *
  • The default field naming policy for the output JSON is same as in Java. So, a Java class - * field versionNumber will be output as "versionNumber" in - * JSON. The same rules are applied for mapping incoming JSON to the Java classes. You can - * change this policy through {@link GsonBuilder#setFieldNamingPolicy(FieldNamingPolicy)}.
  • + * field versionNumber will be output as "versionNumber" + * in JSON. The same rules are applied for mapping incoming JSON to the Java classes. You + * can change this policy through {@link + * GsonBuilder#setFieldNamingPolicy(FieldNamingPolicy)}. *
  • By default, Gson excludes transient or static fields from - * consideration for serialization and deserialization. You can change this behavior through - * {@link GsonBuilder#excludeFieldsWithModifiers(int...)}.
  • - *
  • No explicit strictness is set. You can change this by calling - * {@link GsonBuilder#setStrictness(Strictness)}.
  • + * consideration for serialization and deserialization. You can change this behavior through + * {@link GsonBuilder#excludeFieldsWithModifiers(int...)}. + *
  • No explicit strictness is set. You can change this by calling {@link + * GsonBuilder#setStrictness(Strictness)}. *
*/ public Gson() { - this(Excluder.DEFAULT, DEFAULT_FIELD_NAMING_STRATEGY, - Collections.>emptyMap(), DEFAULT_SERIALIZE_NULLS, - DEFAULT_COMPLEX_MAP_KEYS, DEFAULT_JSON_NON_EXECUTABLE, DEFAULT_ESCAPE_HTML, - DEFAULT_FORMATTING_STYLE, DEFAULT_STRICTNESS, DEFAULT_SPECIALIZE_FLOAT_VALUES, + this( + Excluder.DEFAULT, + DEFAULT_FIELD_NAMING_STRATEGY, + Collections.>emptyMap(), + DEFAULT_SERIALIZE_NULLS, + DEFAULT_COMPLEX_MAP_KEYS, + DEFAULT_JSON_NON_EXECUTABLE, + DEFAULT_ESCAPE_HTML, + DEFAULT_FORMATTING_STYLE, + DEFAULT_STRICTNESS, + DEFAULT_SPECIALIZE_FLOAT_VALUES, DEFAULT_USE_JDK_UNSAFE, - LongSerializationPolicy.DEFAULT, DEFAULT_DATE_PATTERN, DateFormat.DEFAULT, DateFormat.DEFAULT, - Collections.emptyList(), Collections.emptyList(), - Collections.emptyList(), DEFAULT_OBJECT_TO_NUMBER_STRATEGY, DEFAULT_NUMBER_TO_NUMBER_STRATEGY, + LongSerializationPolicy.DEFAULT, + DEFAULT_DATE_PATTERN, + DateFormat.DEFAULT, + DateFormat.DEFAULT, + Collections.emptyList(), + Collections.emptyList(), + Collections.emptyList(), + DEFAULT_OBJECT_TO_NUMBER_STRATEGY, + DEFAULT_NUMBER_TO_NUMBER_STRATEGY, Collections.emptyList()); } - Gson(Excluder excluder, FieldNamingStrategy fieldNamingStrategy, - Map> instanceCreators, boolean serializeNulls, - boolean complexMapKeySerialization, boolean generateNonExecutableGson, boolean htmlSafe, - FormattingStyle formattingStyle, Strictness strictness, boolean serializeSpecialFloatingPointValues, + Gson( + Excluder excluder, + FieldNamingStrategy fieldNamingStrategy, + Map> instanceCreators, + boolean serializeNulls, + boolean complexMapKeySerialization, + boolean generateNonExecutableGson, + boolean htmlSafe, + FormattingStyle formattingStyle, + Strictness strictness, + boolean serializeSpecialFloatingPointValues, boolean useJdkUnsafe, - LongSerializationPolicy longSerializationPolicy, String datePattern, int dateStyle, - int timeStyle, List builderFactories, + LongSerializationPolicy longSerializationPolicy, + String datePattern, + int dateStyle, + int timeStyle, + List builderFactories, List builderHierarchyFactories, List factoriesToBeAdded, - ToNumberStrategy objectToNumberStrategy, ToNumberStrategy numberToNumberStrategy, + ToNumberStrategy objectToNumberStrategy, + ToNumberStrategy numberToNumberStrategy, List reflectionFilters) { this.excluder = excluder; this.fieldNamingStrategy = fieldNamingStrategy; this.instanceCreators = instanceCreators; - this.constructorConstructor = new ConstructorConstructor(instanceCreators, useJdkUnsafe, reflectionFilters); + this.constructorConstructor = + new ConstructorConstructor(instanceCreators, useJdkUnsafe, reflectionFilters); this.serializeNulls = serializeNulls; this.complexMapKeySerialization = complexMapKeySerialization; this.generateNonExecutableJson = generateNonExecutableGson; @@ -310,23 +341,28 @@ public Gson() { factories.add(TypeAdapters.SHORT_FACTORY); TypeAdapter longAdapter = longAdapter(longSerializationPolicy); factories.add(TypeAdapters.newFactory(long.class, Long.class, longAdapter)); - factories.add(TypeAdapters.newFactory(double.class, Double.class, - doubleAdapter(serializeSpecialFloatingPointValues))); - factories.add(TypeAdapters.newFactory(float.class, Float.class, - floatAdapter(serializeSpecialFloatingPointValues))); + factories.add( + TypeAdapters.newFactory( + double.class, Double.class, doubleAdapter(serializeSpecialFloatingPointValues))); + factories.add( + TypeAdapters.newFactory( + float.class, Float.class, floatAdapter(serializeSpecialFloatingPointValues))); factories.add(NumberTypeAdapter.getFactory(numberToNumberStrategy)); factories.add(TypeAdapters.ATOMIC_INTEGER_FACTORY); factories.add(TypeAdapters.ATOMIC_BOOLEAN_FACTORY); factories.add(TypeAdapters.newFactory(AtomicLong.class, atomicLongAdapter(longAdapter))); - factories.add(TypeAdapters.newFactory(AtomicLongArray.class, atomicLongArrayAdapter(longAdapter))); + factories.add( + TypeAdapters.newFactory(AtomicLongArray.class, atomicLongArrayAdapter(longAdapter))); factories.add(TypeAdapters.ATOMIC_INTEGER_ARRAY_FACTORY); factories.add(TypeAdapters.CHARACTER_FACTORY); factories.add(TypeAdapters.STRING_BUILDER_FACTORY); factories.add(TypeAdapters.STRING_BUFFER_FACTORY); factories.add(TypeAdapters.newFactory(BigDecimal.class, TypeAdapters.BIG_DECIMAL)); factories.add(TypeAdapters.newFactory(BigInteger.class, TypeAdapters.BIG_INTEGER)); - // Add adapter for LazilyParsedNumber because user can obtain it from Gson and then try to serialize it again - factories.add(TypeAdapters.newFactory(LazilyParsedNumber.class, TypeAdapters.LAZILY_PARSED_NUMBER)); + // Add adapter for LazilyParsedNumber because user can obtain it from Gson and then try to + // serialize it again + factories.add( + TypeAdapters.newFactory(LazilyParsedNumber.class, TypeAdapters.LAZILY_PARSED_NUMBER)); factories.add(TypeAdapters.URL_FACTORY); factories.add(TypeAdapters.URI_FACTORY); factories.add(TypeAdapters.UUID_FACTORY); @@ -352,8 +388,13 @@ public Gson() { this.jsonAdapterFactory = new JsonAdapterAnnotationTypeAdapterFactory(constructorConstructor); factories.add(jsonAdapterFactory); factories.add(TypeAdapters.ENUM_FACTORY); - factories.add(new ReflectiveTypeAdapterFactory( - constructorConstructor, fieldNamingStrategy, excluder, jsonAdapterFactory, reflectionFilters)); + factories.add( + new ReflectiveTypeAdapterFactory( + constructorConstructor, + fieldNamingStrategy, + excluder, + jsonAdapterFactory, + reflectionFilters)); this.factories = Collections.unmodifiableList(factories); } @@ -371,7 +412,7 @@ public GsonBuilder newBuilder() { /** * @deprecated This method by accident exposes an internal Gson class; it might be removed in a - * future version. + * future version. */ @Deprecated public Excluder excluder() { @@ -388,8 +429,8 @@ public FieldNamingStrategy fieldNamingStrategy() { } /** - * Returns whether this Gson instance is serializing JSON object properties with - * {@code null} values, or just omits them. + * Returns whether this Gson instance is serializing JSON object properties with {@code null} + * values, or just omits them. * * @see GsonBuilder#serializeNulls() */ @@ -398,8 +439,8 @@ public boolean serializeNulls() { } /** - * Returns whether this Gson instance produces JSON output which is - * HTML-safe, that means all HTML characters are escaped. + * Returns whether this Gson instance produces JSON output which is HTML-safe, that means all HTML + * characters are escaped. * * @see GsonBuilder#disableHtmlEscaping() */ @@ -412,14 +453,17 @@ private TypeAdapter doubleAdapter(boolean serializeSpecialFloatingPointV return TypeAdapters.DOUBLE; } return new TypeAdapter() { - @Override public Double read(JsonReader in) throws IOException { + @Override + public Double read(JsonReader in) throws IOException { if (in.peek() == JsonToken.NULL) { in.nextNull(); return null; } return in.nextDouble(); } - @Override public void write(JsonWriter out, Number value) throws IOException { + + @Override + public void write(JsonWriter out, Number value) throws IOException { if (value == null) { out.nullValue(); return; @@ -436,14 +480,17 @@ private TypeAdapter floatAdapter(boolean serializeSpecialFloatingPointVa return TypeAdapters.FLOAT; } return new TypeAdapter() { - @Override public Float read(JsonReader in) throws IOException { + @Override + public Float read(JsonReader in) throws IOException { if (in.peek() == JsonToken.NULL) { in.nextNull(); return null; } return (float) in.nextDouble(); } - @Override public void write(JsonWriter out, Number value) throws IOException { + + @Override + public void write(JsonWriter out, Number value) throws IOException { if (value == null) { out.nullValue(); return; @@ -460,9 +507,10 @@ private TypeAdapter floatAdapter(boolean serializeSpecialFloatingPointVa static void checkValidFloatingPoint(double value) { if (Double.isNaN(value) || Double.isInfinite(value)) { - throw new IllegalArgumentException(value - + " is not a valid double value as per JSON specification. To override this" - + " behavior, use GsonBuilder.serializeSpecialFloatingPointValues() method."); + throw new IllegalArgumentException( + value + + " is not a valid double value as per JSON specification. To override this" + + " behavior, use GsonBuilder.serializeSpecialFloatingPointValues() method."); } } @@ -471,14 +519,17 @@ private static TypeAdapter longAdapter(LongSerializationPolicy longSeria return TypeAdapters.LONG; } return new TypeAdapter() { - @Override public Number read(JsonReader in) throws IOException { + @Override + public Number read(JsonReader in) throws IOException { if (in.peek() == JsonToken.NULL) { in.nextNull(); return null; } return in.nextLong(); } - @Override public void write(JsonWriter out, Number value) throws IOException { + + @Override + public void write(JsonWriter out, Number value) throws IOException { if (value == null) { out.nullValue(); return; @@ -490,31 +541,38 @@ private static TypeAdapter longAdapter(LongSerializationPolicy longSeria private static TypeAdapter atomicLongAdapter(final TypeAdapter longAdapter) { return new TypeAdapter() { - @Override public void write(JsonWriter out, AtomicLong value) throws IOException { + @Override + public void write(JsonWriter out, AtomicLong value) throws IOException { longAdapter.write(out, value.get()); } - @Override public AtomicLong read(JsonReader in) throws IOException { + + @Override + public AtomicLong read(JsonReader in) throws IOException { Number value = longAdapter.read(in); return new AtomicLong(value.longValue()); } }.nullSafe(); } - private static TypeAdapter atomicLongArrayAdapter(final TypeAdapter longAdapter) { + private static TypeAdapter atomicLongArrayAdapter( + final TypeAdapter longAdapter) { return new TypeAdapter() { - @Override public void write(JsonWriter out, AtomicLongArray value) throws IOException { + @Override + public void write(JsonWriter out, AtomicLongArray value) throws IOException { out.beginArray(); for (int i = 0, length = value.length(); i < length; i++) { longAdapter.write(out, value.get(i)); } out.endArray(); } - @Override public AtomicLongArray read(JsonReader in) throws IOException { + + @Override + public AtomicLongArray read(JsonReader in) throws IOException { List list = new ArrayList<>(); in.beginArray(); while (in.hasNext()) { - long value = longAdapter.read(in).longValue(); - list.add(value); + long value = longAdapter.read(in).longValue(); + list.add(value); } in.endArray(); int length = list.size(); @@ -530,13 +588,13 @@ private static TypeAdapter atomicLongArrayAdapter(final TypeAda /** * Returns the type adapter for {@code type}. * - *

When calling this method concurrently from multiple threads and requesting - * an adapter for the same type this method may return different {@code TypeAdapter} - * instances. However, that should normally not be an issue because {@code TypeAdapter} - * implementations are supposed to be stateless. + *

When calling this method concurrently from multiple threads and requesting an adapter for + * the same type this method may return different {@code TypeAdapter} instances. However, that + * should normally not be an issue because {@code TypeAdapter} implementations are supposed to be + * stateless. * - * @throws IllegalArgumentException if this Gson instance cannot serialize and - * deserialize {@code type}. + * @throws IllegalArgumentException if this Gson instance cannot serialize and deserialize {@code + * type}. */ public TypeAdapter getAdapter(TypeToken type) { Objects.requireNonNull(type, "type must not be null"); @@ -583,7 +641,8 @@ public TypeAdapter getAdapter(TypeToken type) { } if (candidate == null) { - throw new IllegalArgumentException("GSON (" + GsonBuildConfig.VERSION + ") cannot handle " + type); + throw new IllegalArgumentException( + "GSON (" + GsonBuildConfig.VERSION + ") cannot handle " + type); } if (isInitialAdapterRequest) { @@ -599,14 +658,16 @@ public TypeAdapter getAdapter(TypeToken type) { } /** - * This method is used to get an alternate type adapter for the specified type. This is used - * to access a type adapter that is overridden by a {@link TypeAdapterFactory} that you - * may have registered. This feature is typically used when you want to register a type - * adapter that does a little bit of work but then delegates further processing to the Gson - * default type adapter. Here is an example: - *

Let's say we want to write a type adapter that counts the number of objects being read - * from or written to JSON. We can achieve this by writing a type adapter factory that uses - * the getDelegateAdapter method: + * This method is used to get an alternate type adapter for the specified type. This is used to + * access a type adapter that is overridden by a {@link TypeAdapterFactory} that you may have + * registered. This feature is typically used when you want to register a type adapter that does a + * little bit of work but then delegates further processing to the Gson default type adapter. Here + * is an example: + * + *

Let's say we want to write a type adapter that counts the number of objects being read from + * or written to JSON. We can achieve this by writing a type adapter factory that uses the + * getDelegateAdapter method: + * *

{@code
    * class StatsTypeAdapterFactory implements TypeAdapterFactory {
    *   public int numReads = 0;
@@ -626,7 +687,9 @@ public  TypeAdapter getAdapter(TypeToken type) {
    *   }
    * }
    * }
+ * * This factory can now be used like this: + * *
{@code
    * StatsTypeAdapterFactory stats = new StatsTypeAdapterFactory();
    * Gson gson = new GsonBuilder().registerTypeAdapterFactory(stats).create();
@@ -634,26 +697,25 @@ public  TypeAdapter getAdapter(TypeToken type) {
    * System.out.println("Num JSON reads: " + stats.numReads);
    * System.out.println("Num JSON writes: " + stats.numWrites);
    * }
+ * * Note that this call will skip all factories registered before {@code skipPast}. In case of - * multiple TypeAdapterFactories registered it is up to the caller of this function to ensure - * that the order of registration does not prevent this method from reaching a factory they - * would expect to reply from this call. - * Note that since you can not override the type adapter factories for some types, see - * {@link GsonBuilder#registerTypeAdapter(Type, Object)}, our stats factory will not count - * the number of instances of those types that will be read or written. - * - *

If {@code skipPast} is a factory which has neither been registered on the {@link GsonBuilder} - * nor specified with the {@link JsonAdapter @JsonAdapter} annotation on a class, then this - * method behaves as if {@link #getAdapter(TypeToken)} had been called. This also means that - * for fields with {@code @JsonAdapter} annotation this method behaves normally like {@code getAdapter} - * (except for corner cases where a custom {@link InstanceCreator} is used to create an - * instance of the factory). - * - * @param skipPast The type adapter factory that needs to be skipped while searching for - * a matching type adapter. In most cases, you should just pass this (the type adapter - * factory from where {@code getDelegateAdapter} method is being invoked). + * multiple TypeAdapterFactories registered it is up to the caller of this function to ensure that + * the order of registration does not prevent this method from reaching a factory they would + * expect to reply from this call. Note that since you can not override the type adapter factories + * for some types, see {@link GsonBuilder#registerTypeAdapter(Type, Object)}, our stats factory + * will not count the number of instances of those types that will be read or written. + * + *

If {@code skipPast} is a factory which has neither been registered on the {@link + * GsonBuilder} nor specified with the {@link JsonAdapter @JsonAdapter} annotation on a class, + * then this method behaves as if {@link #getAdapter(TypeToken)} had been called. This also means + * that for fields with {@code @JsonAdapter} annotation this method behaves normally like {@code + * getAdapter} (except for corner cases where a custom {@link InstanceCreator} is used to create + * an instance of the factory). + * + * @param skipPast The type adapter factory that needs to be skipped while searching for a + * matching type adapter. In most cases, you should just pass this (the type adapter + * factory from where {@code getDelegateAdapter} method is being invoked). * @param type Type for which the delegate adapter is being searched for. - * * @since 2.2 */ public TypeAdapter getDelegateAdapter(TypeAdapterFactory skipPast, TypeToken type) { @@ -690,8 +752,8 @@ public TypeAdapter getDelegateAdapter(TypeAdapterFactory skipPast, TypeTo /** * Returns the type adapter for {@code type}. * - * @throws IllegalArgumentException if this Gson instance cannot serialize and - * deserialize {@code type}. + * @throws IllegalArgumentException if this Gson instance cannot serialize and deserialize {@code + * type}. */ public TypeAdapter getAdapter(Class type) { return getAdapter(TypeToken.get(type)); @@ -702,14 +764,13 @@ public TypeAdapter getAdapter(Class type) { * {@link JsonElement}s. This method should be used when the specified object is not a generic * type. This method uses {@link Class#getClass()} to get the type for the specified object, but * the {@code getClass()} loses the generic type information because of the Type Erasure feature - * of Java. Note that this method works fine if any of the object fields are of generic type, - * just the object itself should not be of a generic type. If the object is of generic type, use - * {@link #toJsonTree(Object, Type)} instead. + * of Java. Note that this method works fine if any of the object fields are of generic type, just + * the object itself should not be of a generic type. If the object is of generic type, use {@link + * #toJsonTree(Object, Type)} instead. * * @param src the object for which JSON representation is to be created * @return JSON representation of {@code src}. * @since 1.4 - * * @see #toJsonTree(Object, Type) */ public JsonElement toJsonTree(Object src) { @@ -726,15 +787,15 @@ public JsonElement toJsonTree(Object src) { * instead. * * @param src the object for which JSON representation is to be created - * @param typeOfSrc The specific genericized type of src. You can obtain - * this type by using the {@link com.google.gson.reflect.TypeToken} class. For example, - * to get the type for {@code Collection}, you should use: - *

+   * @param typeOfSrc The specific genericized type of src. You can obtain this type by using the
+   *     {@link com.google.gson.reflect.TypeToken} class. For example, to get the type for {@code
+   *     Collection}, you should use:
+   *     
    * Type typeOfSrc = new TypeToken<Collection<Foo>>(){}.getType();
    * 
+ * * @return JSON representation of {@code src}. * @since 1.4 - * * @see #toJsonTree(Object) */ public JsonElement toJsonTree(Object src, Type typeOfSrc) { @@ -744,18 +805,17 @@ public JsonElement toJsonTree(Object src, Type typeOfSrc) { } /** - * This method serializes the specified object into its equivalent JSON representation. - * This method should be used when the specified object is not a generic type. This method uses - * {@link Class#getClass()} to get the type for the specified object, but the - * {@code getClass()} loses the generic type information because of the Type Erasure feature - * of Java. Note that this method works fine if any of the object fields are of generic type, - * just the object itself should not be of a generic type. If the object is of generic type, use - * {@link #toJson(Object, Type)} instead. If you want to write out the object to a - * {@link Writer}, use {@link #toJson(Object, Appendable)} instead. + * This method serializes the specified object into its equivalent JSON representation. This + * method should be used when the specified object is not a generic type. This method uses {@link + * Class#getClass()} to get the type for the specified object, but the {@code getClass()} loses + * the generic type information because of the Type Erasure feature of Java. Note that this method + * works fine if any of the object fields are of generic type, just the object itself should not + * be of a generic type. If the object is of generic type, use {@link #toJson(Object, Type)} + * instead. If you want to write out the object to a {@link Writer}, use {@link #toJson(Object, + * Appendable)} instead. * * @param src the object for which JSON representation is to be created * @return JSON representation of {@code src}. - * * @see #toJson(Object, Appendable) * @see #toJson(Object, Type) */ @@ -773,14 +833,14 @@ public String toJson(Object src) { * the object to a {@link Appendable}, use {@link #toJson(Object, Type, Appendable)} instead. * * @param src the object for which JSON representation is to be created - * @param typeOfSrc The specific genericized type of src. You can obtain - * this type by using the {@link com.google.gson.reflect.TypeToken} class. For example, - * to get the type for {@code Collection}, you should use: - *
+   * @param typeOfSrc The specific genericized type of src. You can obtain this type by using the
+   *     {@link com.google.gson.reflect.TypeToken} class. For example, to get the type for {@code
+   *     Collection}, you should use:
+   *     
    * Type typeOfSrc = new TypeToken<Collection<Foo>>(){}.getType();
    * 
- * @return JSON representation of {@code src}. * + * @return JSON representation of {@code src}. * @see #toJson(Object, Type, Appendable) * @see #toJson(Object) */ @@ -791,20 +851,18 @@ public String toJson(Object src, Type typeOfSrc) { } /** - * This method serializes the specified object into its equivalent JSON representation and - * writes it to the writer. - * This method should be used when the specified object is not a generic type. This method uses - * {@link Class#getClass()} to get the type for the specified object, but the - * {@code getClass()} loses the generic type information because of the Type Erasure feature - * of Java. Note that this method works fine if any of the object fields are of generic type, - * just the object itself should not be of a generic type. If the object is of generic type, use - * {@link #toJson(Object, Type, Appendable)} instead. + * This method serializes the specified object into its equivalent JSON representation and writes + * it to the writer. This method should be used when the specified object is not a generic type. + * This method uses {@link Class#getClass()} to get the type for the specified object, but the + * {@code getClass()} loses the generic type information because of the Type Erasure feature of + * Java. Note that this method works fine if any of the object fields are of generic type, just + * the object itself should not be of a generic type. If the object is of generic type, use {@link + * #toJson(Object, Type, Appendable)} instead. * * @param src the object for which JSON representation is to be created * @param writer Writer to which the JSON representation needs to be written * @throws JsonIOException if there was a problem writing to the writer * @since 1.2 - * * @see #toJson(Object) * @see #toJson(Object, Type, Appendable) */ @@ -818,21 +876,21 @@ public void toJson(Object src, Appendable writer) throws JsonIOException { /** * This method serializes the specified object, including those of generic types, into its - * equivalent JSON representation and writes it to the writer. - * This method must be used if the specified object is a generic type. For non-generic objects, - * use {@link #toJson(Object, Appendable)} instead. + * equivalent JSON representation and writes it to the writer. This method must be used if the + * specified object is a generic type. For non-generic objects, use {@link #toJson(Object, + * Appendable)} instead. * * @param src the object for which JSON representation is to be created - * @param typeOfSrc The specific genericized type of src. You can obtain - * this type by using the {@link com.google.gson.reflect.TypeToken} class. For example, - * to get the type for {@code Collection}, you should use: - *
+   * @param typeOfSrc The specific genericized type of src. You can obtain this type by using the
+   *     {@link com.google.gson.reflect.TypeToken} class. For example, to get the type for {@code
+   *     Collection}, you should use:
+   *     
    * Type typeOfSrc = new TypeToken<Collection<Foo>>(){}.getType();
    * 
+ * * @param writer Writer to which the JSON representation of src needs to be written * @throws JsonIOException if there was a problem writing to the writer * @since 1.2 - * * @see #toJson(Object, Type) * @see #toJson(Object, Appendable) */ @@ -846,24 +904,24 @@ public void toJson(Object src, Type typeOfSrc, Appendable writer) throws JsonIOE } /** - * Writes the JSON representation of {@code src} of type {@code typeOfSrc} to - * {@code writer}. + * Writes the JSON representation of {@code src} of type {@code typeOfSrc} to {@code writer}. * - *

If the {@code Gson} instance has an {@linkplain GsonBuilder#setStrictness(Strictness) explicit strictness setting}, - * this setting will be used for writing the JSON regardless of the {@linkplain JsonWriter#getStrictness() strictness} - * of the provided {@link JsonWriter}. For legacy reasons, if the {@code Gson} instance has no explicit strictness setting - * and the writer does not have the strictness {@link Strictness#STRICT}, the JSON will be written in {@link Strictness#LENIENT} - * mode.
- * Note that in all cases the old strictness setting of the writer will be restored when this method returns. + *

If the {@code Gson} instance has an {@linkplain GsonBuilder#setStrictness(Strictness) + * explicit strictness setting}, this setting will be used for writing the JSON regardless of the + * {@linkplain JsonWriter#getStrictness() strictness} of the provided {@link JsonWriter}. For + * legacy reasons, if the {@code Gson} instance has no explicit strictness setting and the writer + * does not have the strictness {@link Strictness#STRICT}, the JSON will be written in {@link + * Strictness#LENIENT} mode.
+ * Note that in all cases the old strictness setting of the writer will be restored when this + * method returns. * *

The 'HTML-safe' and 'serialize {@code null}' settings of this {@code Gson} instance - * (configured by the {@link GsonBuilder}) are applied, and the original settings of the - * writer are restored once this method returns. + * (configured by the {@link GsonBuilder}) are applied, and the original settings of the writer + * are restored once this method returns. * * @param src the object for which JSON representation is to be created * @param typeOfSrc the type of the object to be written * @param writer Writer to which the JSON representation of src needs to be written - * * @throws JsonIOException if there was a problem writing to the writer */ public void toJson(Object src, Type typeOfSrc, JsonWriter writer) throws JsonIOException { @@ -887,7 +945,8 @@ public void toJson(Object src, Type typeOfSrc, JsonWriter writer) throws JsonIOE } catch (IOException e) { throw new JsonIOException(e); } catch (AssertionError e) { - throw new AssertionError("AssertionError (GSON " + GsonBuildConfig.VERSION + "): " + e.getMessage(), e); + throw new AssertionError( + "AssertionError (GSON " + GsonBuildConfig.VERSION + "): " + e.getMessage(), e); } finally { writer.setStrictness(oldStrictness); writer.setHtmlSafe(oldHtmlSafe); @@ -929,16 +988,17 @@ public void toJson(JsonElement jsonElement, Appendable writer) throws JsonIOExce * Returns a new JSON writer configured for the settings on this Gson instance. * *

The following settings are considered: + * *

    - *
  • {@link GsonBuilder#disableHtmlEscaping()}
  • - *
  • {@link GsonBuilder#generateNonExecutableJson()}
  • - *
  • {@link GsonBuilder#serializeNulls()}
  • - *
  • {@link GsonBuilder#setStrictness(Strictness)}. If no - * {@linkplain GsonBuilder#setStrictness(Strictness) explicit strictness has been set} the created - * writer will have a strictness of {@link Strictness#LEGACY_STRICT}. Otherwise, the strictness of - * the {@code Gson} instance will be used for the created writer.
  • - *
  • {@link GsonBuilder#setPrettyPrinting()}
  • - *
  • {@link GsonBuilder#setFormattingStyle(FormattingStyle)}
  • + *
  • {@link GsonBuilder#disableHtmlEscaping()} + *
  • {@link GsonBuilder#generateNonExecutableJson()} + *
  • {@link GsonBuilder#serializeNulls()} + *
  • {@link GsonBuilder#setStrictness(Strictness)}. If no {@linkplain + * GsonBuilder#setStrictness(Strictness) explicit strictness has been set} the created + * writer will have a strictness of {@link Strictness#LEGACY_STRICT}. Otherwise, the + * strictness of the {@code Gson} instance will be used for the created writer. + *
  • {@link GsonBuilder#setPrettyPrinting()} + *
  • {@link GsonBuilder#setFormattingStyle(FormattingStyle)} *
*/ public JsonWriter newJsonWriter(Writer writer) throws IOException { @@ -957,11 +1017,12 @@ public JsonWriter newJsonWriter(Writer writer) throws IOException { * Returns a new JSON reader configured for the settings on this Gson instance. * *

The following settings are considered: + * *

    - *
  • {@link GsonBuilder#setStrictness(Strictness)}. If no - * {@linkplain GsonBuilder#setStrictness(Strictness) explicit strictness has been set} the created - * reader will have a strictness of {@link Strictness#LEGACY_STRICT}. Otherwise, the strictness of - * the {@code Gson} instance will be used for the created reader.
  • + *
  • {@link GsonBuilder#setStrictness(Strictness)}. If no {@linkplain + * GsonBuilder#setStrictness(Strictness) explicit strictness has been set} the created + * reader will have a strictness of {@link Strictness#LEGACY_STRICT}. Otherwise, the + * strictness of the {@code Gson} instance will be used for the created reader. *
*/ public JsonReader newJsonReader(Reader reader) { @@ -973,16 +1034,18 @@ public JsonReader newJsonReader(Reader reader) { /** * Writes the JSON for {@code jsonElement} to {@code writer}. * - *

If the {@code Gson} instance has an {@linkplain GsonBuilder#setStrictness(Strictness) explicit strictness setting}, - * this setting will be used for writing the JSON regardless of the {@linkplain JsonWriter#getStrictness() strictness} - * of the provided {@link JsonWriter}. For legacy reasons, if the {@code Gson} instance has no explicit strictness setting - * and the writer does not have the strictness {@link Strictness#STRICT}, the JSON will be written in {@link Strictness#LENIENT} - * mode.
- * Note that in all cases the old strictness setting of the writer will be restored when this method returns. + *

If the {@code Gson} instance has an {@linkplain GsonBuilder#setStrictness(Strictness) + * explicit strictness setting}, this setting will be used for writing the JSON regardless of the + * {@linkplain JsonWriter#getStrictness() strictness} of the provided {@link JsonWriter}. For + * legacy reasons, if the {@code Gson} instance has no explicit strictness setting and the writer + * does not have the strictness {@link Strictness#STRICT}, the JSON will be written in {@link + * Strictness#LENIENT} mode.
+ * Note that in all cases the old strictness setting of the writer will be restored when this + * method returns. * *

The 'HTML-safe' and 'serialize {@code null}' settings of this {@code Gson} instance - * (configured by the {@link GsonBuilder}) are applied, and the original settings of the - * writer are restored once this method returns. + * (configured by the {@link GsonBuilder}) are applied, and the original settings of the writer + * are restored once this method returns. * * @param jsonElement the JSON element to be written * @param writer the JSON writer to which the provided element will be written @@ -1007,7 +1070,8 @@ public void toJson(JsonElement jsonElement, JsonWriter writer) throws JsonIOExce } catch (IOException e) { throw new JsonIOException(e); } catch (AssertionError e) { - throw new AssertionError("AssertionError (GSON " + GsonBuildConfig.VERSION + "): " + e.getMessage(), e); + throw new AssertionError( + "AssertionError (GSON " + GsonBuildConfig.VERSION + "): " + e.getMessage(), e); } finally { writer.setStrictness(oldStrictness); writer.setHtmlSafe(oldHtmlSafe); @@ -1019,11 +1083,11 @@ public void toJson(JsonElement jsonElement, JsonWriter writer) throws JsonIOExce * This method deserializes the specified JSON into an object of the specified class. It is not * suitable to use if the specified class is a generic type since it will not have the generic * type information because of the Type Erasure feature of Java. Therefore, this method should not - * be used if the desired type is a generic type. Note that this method works fine if any of - * the fields of the specified object are generics, just the object itself should not be a - * generic type. For the cases when the object is of generic type, invoke - * {@link #fromJson(String, TypeToken)}. If you have the JSON in a {@link Reader} instead of - * a String, use {@link #fromJson(Reader, Class)} instead. + * be used if the desired type is a generic type. Note that this method works fine if any of the + * fields of the specified object are generics, just the object itself should not be a generic + * type. For the cases when the object is of generic type, invoke {@link #fromJson(String, + * TypeToken)}. If you have the JSON in a {@link Reader} instead of a String, use {@link + * #fromJson(Reader, Class)} instead. * *

An exception is thrown if the JSON string has multiple top-level JSON elements, or if there * is trailing data. Use {@link #fromJson(JsonReader, Type)} if this behavior is not desired. @@ -1031,11 +1095,10 @@ public void toJson(JsonElement jsonElement, JsonWriter writer) throws JsonIOExce * @param the type of the desired object * @param json the string from which the object is to be deserialized * @param classOfT the class of T - * @return an object of type T from the string. Returns {@code null} if {@code json} is {@code null} - * or if {@code json} is empty. + * @return an object of type T from the string. Returns {@code null} if {@code json} is {@code + * null} or if {@code json} is empty. * @throws JsonSyntaxException if json is not a valid representation for an object of type - * classOfT - * + * classOfT * @see #fromJson(Reader, Class) * @see #fromJson(String, TypeToken) */ @@ -1046,26 +1109,24 @@ public T fromJson(String json, Class classOfT) throws JsonSyntaxException /** * This method deserializes the specified JSON into an object of the specified type. This method - * is useful if the specified object is a generic type. For non-generic objects, use - * {@link #fromJson(String, Class)} instead. If you have the JSON in a {@link Reader} instead of - * a String, use {@link #fromJson(Reader, Type)} instead. + * is useful if the specified object is a generic type. For non-generic objects, use {@link + * #fromJson(String, Class)} instead. If you have the JSON in a {@link Reader} instead of a + * String, use {@link #fromJson(Reader, Type)} instead. * - *

Since {@code Type} is not parameterized by T, this method is not type-safe and - * should be used carefully. If you are creating the {@code Type} from a {@link TypeToken}, - * prefer using {@link #fromJson(String, TypeToken)} instead since its return type is based - * on the {@code TypeToken} and is therefore more type-safe. + *

Since {@code Type} is not parameterized by T, this method is not type-safe and should be + * used carefully. If you are creating the {@code Type} from a {@link TypeToken}, prefer using + * {@link #fromJson(String, TypeToken)} instead since its return type is based on the {@code + * TypeToken} and is therefore more type-safe. * - *

An exception is thrown if the JSON string has multiple top-level JSON elements, - * or if there is trailing data. Use {@link #fromJson(JsonReader, Type)} if this behavior is - * not desired. + *

An exception is thrown if the JSON string has multiple top-level JSON elements, or if there + * is trailing data. Use {@link #fromJson(JsonReader, Type)} if this behavior is not desired. * * @param the type of the desired object * @param json the string from which the object is to be deserialized * @param typeOfT The specific genericized type of src - * @return an object of type T from the string. Returns {@code null} if {@code json} is {@code null} - * or if {@code json} is empty. + * @return an object of type T from the string. Returns {@code null} if {@code json} is {@code + * null} or if {@code json} is empty. * @throws JsonSyntaxException if json is not a valid representation for an object of type typeOfT - * * @see #fromJson(Reader, Type) * @see #fromJson(String, Class) * @see #fromJson(String, TypeToken) @@ -1077,9 +1138,9 @@ public T fromJson(String json, Type typeOfT) throws JsonSyntaxException { /** * This method deserializes the specified JSON into an object of the specified type. This method - * is useful if the specified object is a generic type. For non-generic objects, use - * {@link #fromJson(String, Class)} instead. If you have the JSON in a {@link Reader} instead of - * a String, use {@link #fromJson(Reader, TypeToken)} instead. + * is useful if the specified object is a generic type. For non-generic objects, use {@link + * #fromJson(String, Class)} instead. If you have the JSON in a {@link Reader} instead of a + * String, use {@link #fromJson(Reader, TypeToken)} instead. * *

An exception is thrown if the JSON string has multiple top-level JSON elements, or if there * is trailing data. Use {@link #fromJson(JsonReader, TypeToken)} if this behavior is not desired. @@ -1087,15 +1148,16 @@ public T fromJson(String json, Type typeOfT) throws JsonSyntaxException { * @param the type of the desired object * @param json the string from which the object is to be deserialized * @param typeOfT The specific genericized type of src. You should create an anonymous subclass of - * {@code TypeToken} with the specific generic type arguments. For example, to get the type for - * {@code Collection}, you should use: - *

+   *     {@code TypeToken} with the specific generic type arguments. For example, to get the type
+   *     for {@code Collection}, you should use:
+   *     
    * new TypeToken<Collection<Foo>>(){}
    * 
- * @return an object of type T from the string. Returns {@code null} if {@code json} is {@code null} - * or if {@code json} is empty. - * @throws JsonSyntaxException if json is not a valid representation for an object of the type typeOfT * + * @return an object of type T from the string. Returns {@code null} if {@code json} is {@code + * null} or if {@code json} is empty. + * @throws JsonSyntaxException if json is not a valid representation for an object of the type + * typeOfT * @see #fromJson(Reader, TypeToken) * @see #fromJson(String, Class) * @since 2.10 @@ -1112,14 +1174,14 @@ public T fromJson(String json, TypeToken typeOfT) throws JsonSyntaxExcept * This method deserializes the JSON read from the specified reader into an object of the * specified class. It is not suitable to use if the specified class is a generic type since it * will not have the generic type information because of the Type Erasure feature of Java. - * Therefore, this method should not be used if the desired type is a generic type. Note that - * this method works fine if any of the fields of the specified object are generics, just the - * object itself should not be a generic type. For the cases when the object is of generic type, - * invoke {@link #fromJson(Reader, TypeToken)}. If you have the JSON in a String form instead of a - * {@link Reader}, use {@link #fromJson(String, Class)} instead. + * Therefore, this method should not be used if the desired type is a generic type. Note that this + * method works fine if any of the fields of the specified object are generics, just the object + * itself should not be a generic type. For the cases when the object is of generic type, invoke + * {@link #fromJson(Reader, TypeToken)}. If you have the JSON in a String form instead of a {@link + * Reader}, use {@link #fromJson(String, Class)} instead. * - *

An exception is thrown if the JSON data has multiple top-level JSON elements, or if there - * is trailing data. Use {@link #fromJson(JsonReader, Type)} if this behavior is not desired. + *

An exception is thrown if the JSON data has multiple top-level JSON elements, or if there is + * trailing data. Use {@link #fromJson(JsonReader, Type)} if this behavior is not desired. * * @param the type of the desired object * @param json the reader producing the JSON from which the object is to be deserialized. @@ -1128,11 +1190,11 @@ public T fromJson(String json, TypeToken typeOfT) throws JsonSyntaxExcept * @throws JsonIOException if there was a problem reading from the Reader * @throws JsonSyntaxException if json is not a valid representation for an object of type typeOfT * @since 1.2 - * * @see #fromJson(String, Class) * @see #fromJson(Reader, TypeToken) */ - public T fromJson(Reader json, Class classOfT) throws JsonSyntaxException, JsonIOException { + public T fromJson(Reader json, Class classOfT) + throws JsonSyntaxException, JsonIOException { T object = fromJson(json, TypeToken.get(classOfT)); return Primitives.wrap(classOfT).cast(object); } @@ -1143,13 +1205,13 @@ public T fromJson(Reader json, Class classOfT) throws JsonSyntaxException * non-generic objects, use {@link #fromJson(Reader, Class)} instead. If you have the JSON in a * String form instead of a {@link Reader}, use {@link #fromJson(String, Type)} instead. * - *

Since {@code Type} is not parameterized by T, this method is not type-safe and - * should be used carefully. If you are creating the {@code Type} from a {@link TypeToken}, - * prefer using {@link #fromJson(Reader, TypeToken)} instead since its return type is based - * on the {@code TypeToken} and is therefore more type-safe. + *

Since {@code Type} is not parameterized by T, this method is not type-safe and should be + * used carefully. If you are creating the {@code Type} from a {@link TypeToken}, prefer using + * {@link #fromJson(Reader, TypeToken)} instead since its return type is based on the {@code + * TypeToken} and is therefore more type-safe. * - *

An exception is thrown if the JSON data has multiple top-level JSON elements, or if there - * is trailing data. Use {@link #fromJson(JsonReader, Type)} if this behavior is not desired. + *

An exception is thrown if the JSON data has multiple top-level JSON elements, or if there is + * trailing data. Use {@link #fromJson(JsonReader, Type)} if this behavior is not desired. * * @param the type of the desired object * @param json the reader producing JSON from which the object is to be deserialized @@ -1158,7 +1220,6 @@ public T fromJson(Reader json, Class classOfT) throws JsonSyntaxException * @throws JsonIOException if there was a problem reading from the Reader * @throws JsonSyntaxException if json is not a valid representation for an object of type typeOfT * @since 1.2 - * * @see #fromJson(String, Type) * @see #fromJson(Reader, Class) * @see #fromJson(Reader, TypeToken) @@ -1174,26 +1235,28 @@ public T fromJson(Reader json, Type typeOfT) throws JsonIOException, JsonSyn * non-generic objects, use {@link #fromJson(Reader, Class)} instead. If you have the JSON in a * String form instead of a {@link Reader}, use {@link #fromJson(String, TypeToken)} instead. * - *

An exception is thrown if the JSON data has multiple top-level JSON elements, or if there - * is trailing data. Use {@link #fromJson(JsonReader, TypeToken)} if this behavior is not desired. + *

An exception is thrown if the JSON data has multiple top-level JSON elements, or if there is + * trailing data. Use {@link #fromJson(JsonReader, TypeToken)} if this behavior is not desired. * * @param the type of the desired object * @param json the reader producing JSON from which the object is to be deserialized * @param typeOfT The specific genericized type of src. You should create an anonymous subclass of - * {@code TypeToken} with the specific generic type arguments. For example, to get the type for - * {@code Collection}, you should use: - *

+   *     {@code TypeToken} with the specific generic type arguments. For example, to get the type
+   *     for {@code Collection}, you should use:
+   *     
    * new TypeToken<Collection<Foo>>(){}
    * 
+ * * @return an object of type T from the Reader. Returns {@code null} if {@code json} is at EOF. * @throws JsonIOException if there was a problem reading from the Reader - * @throws JsonSyntaxException if json is not a valid representation for an object of type of typeOfT - * + * @throws JsonSyntaxException if json is not a valid representation for an object of type of + * typeOfT * @see #fromJson(String, TypeToken) * @see #fromJson(Reader, Class) * @since 2.10 */ - public T fromJson(Reader json, TypeToken typeOfT) throws JsonIOException, JsonSyntaxException { + public T fromJson(Reader json, TypeToken typeOfT) + throws JsonIOException, JsonSyntaxException { JsonReader jsonReader = newJsonReader(json); T object = fromJson(jsonReader, typeOfT); assertFullConsumption(object, jsonReader); @@ -1213,77 +1276,85 @@ private static void assertFullConsumption(Object obj, JsonReader reader) { } // fromJson(JsonReader, Class) is unfortunately missing and cannot be added now without breaking - // source compatibility in certain cases, see https://github.com/google/gson/pull/1700#discussion_r973764414 + // source compatibility in certain cases, see + // https://github.com/google/gson/pull/1700#discussion_r973764414 /** - * Reads the next JSON value from {@code reader} and converts it to an object - * of type {@code typeOfT}. Returns {@code null}, if the {@code reader} is at EOF. + * Reads the next JSON value from {@code reader} and converts it to an object of type {@code + * typeOfT}. Returns {@code null}, if the {@code reader} is at EOF. * - *

Since {@code Type} is not parameterized by T, this method is not type-safe and - * should be used carefully. If you are creating the {@code Type} from a {@link TypeToken}, - * prefer using {@link #fromJson(JsonReader, TypeToken)} instead since its return type is based - * on the {@code TypeToken} and is therefore more type-safe. If the provided type is a - * {@code Class} the {@code TypeToken} can be created with {@link TypeToken#get(Class)}. + *

Since {@code Type} is not parameterized by T, this method is not type-safe and should be + * used carefully. If you are creating the {@code Type} from a {@link TypeToken}, prefer using + * {@link #fromJson(JsonReader, TypeToken)} instead since its return type is based on the {@code + * TypeToken} and is therefore more type-safe. If the provided type is a {@code Class} the {@code + * TypeToken} can be created with {@link TypeToken#get(Class)}. * *

Unlike the other {@code fromJson} methods, no exception is thrown if the JSON data has * multiple top-level JSON elements, or if there is trailing data. * - *

If the {@code Gson} instance has an {@linkplain GsonBuilder#setStrictness(Strictness) explicit strictness setting}, - * this setting will be used for reading the JSON regardless of the {@linkplain JsonReader#getStrictness() strictness} - * of the provided {@link JsonReader}. For legacy reasons, if the {@code Gson} instance has no explicit strictness setting - * and the reader does not have the strictness {@link Strictness#STRICT}, the JSON will be written in {@link Strictness#LENIENT} - * mode.
- * Note that in all cases the old strictness setting of the reader will be restored when this method returns. + *

If the {@code Gson} instance has an {@linkplain GsonBuilder#setStrictness(Strictness) + * explicit strictness setting}, this setting will be used for reading the JSON regardless of the + * {@linkplain JsonReader#getStrictness() strictness} of the provided {@link JsonReader}. For + * legacy reasons, if the {@code Gson} instance has no explicit strictness setting and the reader + * does not have the strictness {@link Strictness#STRICT}, the JSON will be written in {@link + * Strictness#LENIENT} mode.
+ * Note that in all cases the old strictness setting of the reader will be restored when this + * method returns. * * @param the type of the desired object * @param reader the reader whose next JSON value should be deserialized * @param typeOfT The specific genericized type of src - * @return an object of type T from the JsonReader. Returns {@code null} if {@code reader} is at EOF. + * @return an object of type T from the JsonReader. Returns {@code null} if {@code reader} is at + * EOF. * @throws JsonIOException if there was a problem reading from the JsonReader * @throws JsonSyntaxException if json is not a valid representation for an object of type typeOfT - * * @see #fromJson(Reader, Type) * @see #fromJson(JsonReader, TypeToken) */ @SuppressWarnings({"unchecked", "TypeParameterUnusedInFormals"}) - public T fromJson(JsonReader reader, Type typeOfT) throws JsonIOException, JsonSyntaxException { + public T fromJson(JsonReader reader, Type typeOfT) + throws JsonIOException, JsonSyntaxException { return (T) fromJson(reader, TypeToken.get(typeOfT)); } /** - * Reads the next JSON value from {@code reader} and converts it to an object - * of type {@code typeOfT}. Returns {@code null}, if the {@code reader} is at EOF. - * This method is useful if the specified object is a generic type. For non-generic objects, - * {@link #fromJson(JsonReader, Type)} can be called, or {@link TypeToken#get(Class)} can - * be used to create the type token. + * Reads the next JSON value from {@code reader} and converts it to an object of type {@code + * typeOfT}. Returns {@code null}, if the {@code reader} is at EOF. This method is useful if the + * specified object is a generic type. For non-generic objects, {@link #fromJson(JsonReader, + * Type)} can be called, or {@link TypeToken#get(Class)} can be used to create the type token. * *

Unlike the other {@code fromJson} methods, no exception is thrown if the JSON data has * multiple top-level JSON elements, or if there is trailing data. * - *

If the {@code Gson} instance has an {@linkplain GsonBuilder#setStrictness(Strictness) explicit strictness setting}, - * this setting will be used for reading the JSON regardless of the {@linkplain JsonReader#getStrictness() strictness} - * of the provided {@link JsonReader}. For legacy reasons, if the {@code Gson} instance has no explicit strictness setting - * and the reader does not have the strictness {@link Strictness#STRICT}, the JSON will be written in {@link Strictness#LENIENT} - * mode.
- * Note that in all cases the old strictness setting of the reader will be restored when this method returns. + *

If the {@code Gson} instance has an {@linkplain GsonBuilder#setStrictness(Strictness) + * explicit strictness setting}, this setting will be used for reading the JSON regardless of the + * {@linkplain JsonReader#getStrictness() strictness} of the provided {@link JsonReader}. For + * legacy reasons, if the {@code Gson} instance has no explicit strictness setting and the reader + * does not have the strictness {@link Strictness#STRICT}, the JSON will be written in {@link + * Strictness#LENIENT} mode.
+ * Note that in all cases the old strictness setting of the reader will be restored when this + * method returns. * * @param the type of the desired object * @param reader the reader whose next JSON value should be deserialized * @param typeOfT The specific genericized type of src. You should create an anonymous subclass of - * {@code TypeToken} with the specific generic type arguments. For example, to get the type for - * {@code Collection}, you should use: - *

+   *     {@code TypeToken} with the specific generic type arguments. For example, to get the type
+   *     for {@code Collection}, you should use:
+   *     
    * new TypeToken<Collection<Foo>>(){}
    * 
- * @return an object of type T from the JsonReader. Returns {@code null} if {@code reader} is at EOF. - * @throws JsonIOException if there was a problem reading from the JsonReader - * @throws JsonSyntaxException if json is not a valid representation for an object of the type typeOfT * + * @return an object of type T from the JsonReader. Returns {@code null} if {@code reader} is at + * EOF. + * @throws JsonIOException if there was a problem reading from the JsonReader + * @throws JsonSyntaxException if json is not a valid representation for an object of the type + * typeOfT * @see #fromJson(Reader, TypeToken) * @see #fromJson(JsonReader, Type) * @since 2.10 */ - public T fromJson(JsonReader reader, TypeToken typeOfT) throws JsonIOException, JsonSyntaxException { + public T fromJson(JsonReader reader, TypeToken typeOfT) + throws JsonIOException, JsonSyntaxException { boolean isEmpty = true; Strictness oldStrictness = reader.getStrictness(); @@ -1313,7 +1384,8 @@ public T fromJson(JsonReader reader, TypeToken typeOfT) throws JsonIOExce // TODO(inder): Figure out whether it is indeed right to rethrow this as JsonSyntaxException throw new JsonSyntaxException(e); } catch (AssertionError e) { - throw new AssertionError("AssertionError (GSON " + GsonBuildConfig.VERSION + "): " + e.getMessage(), e); + throw new AssertionError( + "AssertionError (GSON " + GsonBuildConfig.VERSION + "): " + e.getMessage(), e); } finally { reader.setStrictness(oldStrictness); } @@ -1323,20 +1395,20 @@ public T fromJson(JsonReader reader, TypeToken typeOfT) throws JsonIOExce * This method deserializes the JSON read from the specified parse tree into an object of the * specified type. It is not suitable to use if the specified class is a generic type since it * will not have the generic type information because of the Type Erasure feature of Java. - * Therefore, this method should not be used if the desired type is a generic type. Note that - * this method works fine if any of the fields of the specified object are generics, just the - * object itself should not be a generic type. For the cases when the object is of generic type, - * invoke {@link #fromJson(JsonElement, TypeToken)}. + * Therefore, this method should not be used if the desired type is a generic type. Note that this + * method works fine if any of the fields of the specified object are generics, just the object + * itself should not be a generic type. For the cases when the object is of generic type, invoke + * {@link #fromJson(JsonElement, TypeToken)}. * * @param the type of the desired object - * @param json the root of the parse tree of {@link JsonElement}s from which the object is to - * be deserialized + * @param json the root of the parse tree of {@link JsonElement}s from which the object is to be + * deserialized * @param classOfT The class of T * @return an object of type T from the JSON. Returns {@code null} if {@code json} is {@code null} - * or if {@code json} is empty. - * @throws JsonSyntaxException if json is not a valid representation for an object of type classOfT + * or if {@code json} is empty. + * @throws JsonSyntaxException if json is not a valid representation for an object of type + * classOfT * @since 1.3 - * * @see #fromJson(Reader, Class) * @see #fromJson(JsonElement, TypeToken) */ @@ -1350,20 +1422,19 @@ public T fromJson(JsonElement json, Class classOfT) throws JsonSyntaxExce * specified type. This method is useful if the specified object is a generic type. For * non-generic objects, use {@link #fromJson(JsonElement, Class)} instead. * - *

Since {@code Type} is not parameterized by T, this method is not type-safe and - * should be used carefully. If you are creating the {@code Type} from a {@link TypeToken}, - * prefer using {@link #fromJson(JsonElement, TypeToken)} instead since its return type is based - * on the {@code TypeToken} and is therefore more type-safe. + *

Since {@code Type} is not parameterized by T, this method is not type-safe and should be + * used carefully. If you are creating the {@code Type} from a {@link TypeToken}, prefer using + * {@link #fromJson(JsonElement, TypeToken)} instead since its return type is based on the {@code + * TypeToken} and is therefore more type-safe. * * @param the type of the desired object - * @param json the root of the parse tree of {@link JsonElement}s from which the object is to - * be deserialized + * @param json the root of the parse tree of {@link JsonElement}s from which the object is to be + * deserialized * @param typeOfT The specific genericized type of src * @return an object of type T from the JSON. Returns {@code null} if {@code json} is {@code null} - * or if {@code json} is empty. + * or if {@code json} is empty. * @throws JsonSyntaxException if json is not a valid representation for an object of type typeOfT * @since 1.3 - * * @see #fromJson(Reader, Type) * @see #fromJson(JsonElement, Class) * @see #fromJson(JsonElement, TypeToken) @@ -1379,18 +1450,18 @@ public T fromJson(JsonElement json, Type typeOfT) throws JsonSyntaxException * non-generic objects, use {@link #fromJson(JsonElement, Class)} instead. * * @param the type of the desired object - * @param json the root of the parse tree of {@link JsonElement}s from which the object is to - * be deserialized + * @param json the root of the parse tree of {@link JsonElement}s from which the object is to be + * deserialized * @param typeOfT The specific genericized type of src. You should create an anonymous subclass of - * {@code TypeToken} with the specific generic type arguments. For example, to get the type for - * {@code Collection}, you should use: - *

+   *     {@code TypeToken} with the specific generic type arguments. For example, to get the type
+   *     for {@code Collection}, you should use:
+   *     
    * new TypeToken<Collection<Foo>>(){}
    * 
+ * * @return an object of type T from the JSON. Returns {@code null} if {@code json} is {@code null} - * or if {@code json} is empty. + * or if {@code json} is empty. * @throws JsonSyntaxException if json is not a valid representation for an object of type typeOfT - * * @see #fromJson(Reader, TypeToken) * @see #fromJson(JsonElement, Class) * @since 2.10 @@ -1405,9 +1476,8 @@ public T fromJson(JsonElement json, TypeToken typeOfT) throws JsonSyntaxE /** * Proxy type adapter for cyclic type graphs. * - *

Important: Setting the delegate adapter is not thread-safe; instances of - * {@code FutureTypeAdapter} must only be published to other threads after the delegate - * has been set. + *

Important: Setting the delegate adapter is not thread-safe; instances of {@code + * FutureTypeAdapter} must only be published to other threads after the delegate has been set. * * @see Gson#threadLocalAdapterResults */ @@ -1424,32 +1494,40 @@ public void setDelegate(TypeAdapter typeAdapter) { private TypeAdapter delegate() { TypeAdapter delegate = this.delegate; if (delegate == null) { - // Can occur when adapter is leaked to other thread or when adapter is used for (de-)serialization + // Can occur when adapter is leaked to other thread or when adapter is used for + // (de-)serialization // directly within the TypeAdapterFactory which requested it - throw new IllegalStateException("Adapter for type with cyclic dependency has been used" - + " before dependency has been resolved"); + throw new IllegalStateException( + "Adapter for type with cyclic dependency has been used" + + " before dependency has been resolved"); } return delegate; } - @Override public TypeAdapter getSerializationDelegate() { + @Override + public TypeAdapter getSerializationDelegate() { return delegate(); } - @Override public T read(JsonReader in) throws IOException { + @Override + public T read(JsonReader in) throws IOException { return delegate().read(in); } - @Override public void write(JsonWriter out, T value) throws IOException { + @Override + public void write(JsonWriter out, T value) throws IOException { delegate().write(out, value); } } @Override public String toString() { - return "{serializeNulls:" + serializeNulls - + ",factories:" + factories - + ",instanceCreators:" + constructorConstructor + return "{serializeNulls:" + + serializeNulls + + ",factories:" + + factories + + ",instanceCreators:" + + constructorConstructor + "}"; } } diff --git a/gson/src/main/java/com/google/gson/GsonBuilder.java b/gson/src/main/java/com/google/gson/GsonBuilder.java index cf5a4c5ca2..7ebdad4e30 100644 --- a/gson/src/main/java/com/google/gson/GsonBuilder.java +++ b/gson/src/main/java/com/google/gson/GsonBuilder.java @@ -41,7 +41,6 @@ import com.google.gson.reflect.TypeToken; import com.google.gson.stream.JsonReader; import com.google.gson.stream.JsonWriter; -import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import java.text.DateFormat; import java.text.SimpleDateFormat; @@ -55,10 +54,10 @@ import java.util.Objects; /** - *

Use this builder to construct a {@link Gson} instance when you need to set configuration - * options other than the default. For {@link Gson} with default configuration, it is simpler to - * use {@code new Gson()}. {@code GsonBuilder} is best used by creating it, and then invoking its - * various configuration methods, and finally calling create.

+ * Use this builder to construct a {@link Gson} instance when you need to set configuration options + * other than the default. For {@link Gson} with default configuration, it is simpler to use {@code + * new Gson()}. {@code GsonBuilder} is best used by creating it, and then invoking its various + * configuration methods, and finally calling create. * *

The following example shows how to use the {@code GsonBuilder} to construct a Gson instance: * @@ -75,15 +74,16 @@ *

* *

Notes: + * *

    - *
  • The order of invocation of configuration methods does not matter.
  • - *
  • The default serialization of {@link Date} and its subclasses in Gson does - * not contain time-zone information. So, if you are using date/time instances, - * use {@code GsonBuilder} and its {@code setDateFormat} methods.
  • - *
  • By default no explicit {@link Strictness} is set; some of the {@link Gson} methods - * behave as if {@link Strictness#LEGACY_STRICT} was used whereas others behave as - * if {@link Strictness#LENIENT} was used. Prefer explicitly setting a strictness - * with {@link #setStrictness(Strictness)} to avoid this legacy behavior. + *
  • The order of invocation of configuration methods does not matter. + *
  • The default serialization of {@link Date} and its subclasses in Gson does not contain + * time-zone information. So, if you are using date/time instances, use {@code GsonBuilder} + * and its {@code setDateFormat} methods. + *
  • By default no explicit {@link Strictness} is set; some of the {@link Gson} methods behave + * as if {@link Strictness#LEGACY_STRICT} was used whereas others behave as if {@link + * Strictness#LENIENT} was used. Prefer explicitly setting a strictness with {@link + * #setStrictness(Strictness)} to avoid this legacy behavior. *
* * @author Inderjeet Singh @@ -96,8 +96,10 @@ public final class GsonBuilder { private FieldNamingStrategy fieldNamingPolicy = FieldNamingPolicy.IDENTITY; private final Map> instanceCreators = new HashMap<>(); private final List factories = new ArrayList<>(); + /** tree-style hierarchy factories. These come after factories for backwards compatibility. */ private final List hierarchyFactories = new ArrayList<>(); + private boolean serializeNulls = DEFAULT_SERIALIZE_NULLS; private String datePattern = DEFAULT_DATE_PATTERN; private int dateStyle = DateFormat.DEFAULT; @@ -115,16 +117,14 @@ public final class GsonBuilder { /** * Creates a GsonBuilder instance that can be used to build Gson with various configuration - * settings. GsonBuilder follows the builder pattern, and it is typically used by first - * invoking various configuration methods to set desired options, and finally calling - * {@link #create()}. + * settings. GsonBuilder follows the builder pattern, and it is typically used by first invoking + * various configuration methods to set desired options, and finally calling {@link #create()}. */ - public GsonBuilder() { - } + public GsonBuilder() {} /** - * Constructs a GsonBuilder instance from a Gson instance. The newly constructed GsonBuilder - * has the same configuration as the previously built Gson instance. + * Constructs a GsonBuilder instance from a Gson instance. The newly constructed GsonBuilder has + * the same configuration as the previously built Gson instance. * * @param gson the gson instance whose configuration should be applied to a new GsonBuilder. */ @@ -152,13 +152,13 @@ public GsonBuilder() { } /** - * Configures Gson to enable versioning support. Versioning support works based on the - * annotation types {@link Since} and {@link Until}. It allows including or excluding fields - * and classes based on the specified version. See the documentation of these annotation - * types for more information. + * Configures Gson to enable versioning support. Versioning support works based on the annotation + * types {@link Since} and {@link Until}. It allows including or excluding fields and classes + * based on the specified version. See the documentation of these annotation types for more + * information. * - *

By default versioning support is disabled and usage of {@code @Since} and {@code @Until} - * has no effect. + *

By default versioning support is disabled and usage of {@code @Since} and {@code @Until} has + * no effect. * * @param version the version number to use. * @return a reference to this {@code GsonBuilder} object to fulfill the "Builder" pattern @@ -180,13 +180,13 @@ public GsonBuilder setVersion(double version) { * Gson will exclude all fields marked {@code transient} or {@code static}. This method will * override that behavior. * - *

This is a convenience method which behaves as if an {@link ExclusionStrategy} which - * excludes these fields was {@linkplain #setExclusionStrategies(ExclusionStrategy...) registered with this builder}. + *

This is a convenience method which behaves as if an {@link ExclusionStrategy} which excludes + * these fields was {@linkplain #setExclusionStrategies(ExclusionStrategy...) registered with this + * builder}. * - * @param modifiers the field modifiers. You must use the modifiers specified in the - * {@link java.lang.reflect.Modifier} class. For example, - * {@link java.lang.reflect.Modifier#TRANSIENT}, - * {@link java.lang.reflect.Modifier#STATIC}. + * @param modifiers the field modifiers. You must use the modifiers specified in the {@link + * java.lang.reflect.Modifier} class. For example, {@link + * java.lang.reflect.Modifier#TRANSIENT}, {@link java.lang.reflect.Modifier#STATIC}. * @return a reference to this {@code GsonBuilder} object to fulfill the "Builder" pattern */ @CanIgnoreReturnValue @@ -198,9 +198,8 @@ public GsonBuilder excludeFieldsWithModifiers(int... modifiers) { /** * Makes the output JSON non-executable in Javascript by prefixing the generated JSON with some - * special text. This prevents attacks from third-party sites through script sourcing. See - * Gson Issue 42 - * for details. + * special text. This prevents attacks from third-party sites through script sourcing. See Gson Issue 42 for details. * * @return a reference to this {@code GsonBuilder} object to fulfill the "Builder" pattern * @since 1.3 @@ -216,7 +215,8 @@ public GsonBuilder generateNonExecutableJson() { * that do not have the {@link com.google.gson.annotations.Expose} annotation. * *

This is a convenience method which behaves as if an {@link ExclusionStrategy} which excludes - * these fields was {@linkplain #setExclusionStrategies(ExclusionStrategy...) registered with this builder}. + * these fields was {@linkplain #setExclusionStrategies(ExclusionStrategy...) registered with this + * builder}. * * @return a reference to this {@code GsonBuilder} object to fulfill the "Builder" pattern */ @@ -227,8 +227,8 @@ public GsonBuilder excludeFieldsWithoutExposeAnnotation() { } /** - * Configure Gson to serialize null fields. By default, Gson omits all fields that are null - * during serialization. + * Configure Gson to serialize null fields. By default, Gson omits all fields that are null during + * serialization. * * @return a reference to this {@code GsonBuilder} object to fulfill the "Builder" pattern * @since 1.2 @@ -240,79 +240,81 @@ public GsonBuilder serializeNulls() { } /** - * Enabling this feature will only change the serialized form if the map key is - * a complex type (i.e. non-primitive) in its serialized JSON - * form. The default implementation of map serialization uses {@code toString()} - * on the key; however, when this is called then one of the following cases - * apply: + * Enabling this feature will only change the serialized form if the map key is a complex type + * (i.e. non-primitive) in its serialized JSON form. The default implementation + * of map serialization uses {@code toString()} on the key; however, when this is called then one + * of the following cases apply: * *

Maps as JSON objects * - *

For this case, assume that a type adapter is registered to serialize and - * deserialize some {@code Point} class, which contains an x and y coordinate, - * to/from the JSON Primitive string value {@code "(x,y)"}. The Java map would - * then be serialized as a {@link JsonObject}. + *

For this case, assume that a type adapter is registered to serialize and deserialize some + * {@code Point} class, which contains an x and y coordinate, to/from the JSON Primitive string + * value {@code "(x,y)"}. The Java map would then be serialized as a {@link JsonObject}. * *

Below is an example: - *

  {@code
-   *   Gson gson = new GsonBuilder()
-   *       .register(Point.class, new MyPointTypeAdapter())
-   *       .enableComplexMapKeySerialization()
-   *       .create();
-   *
-   *   Map original = new LinkedHashMap<>();
-   *   original.put(new Point(5, 6), "a");
-   *   original.put(new Point(8, 8), "b");
-   *   System.out.println(gson.toJson(original, type));
+   *
+   * 
{@code
+   * Gson gson = new GsonBuilder()
+   *     .register(Point.class, new MyPointTypeAdapter())
+   *     .enableComplexMapKeySerialization()
+   *     .create();
+   *
+   * Map original = new LinkedHashMap<>();
+   * original.put(new Point(5, 6), "a");
+   * original.put(new Point(8, 8), "b");
+   * System.out.println(gson.toJson(original, type));
    * }
- * The above code prints this JSON object:
  {@code
-   *   {
-   *     "(5,6)": "a",
-   *     "(8,8)": "b"
-   *   }
+   *
+   * The above code prints this JSON object:
+   *
+   * 
{@code
+   * {
+   *   "(5,6)": "a",
+   *   "(8,8)": "b"
+   * }
    * }
* *

Maps as JSON arrays * - *

For this case, assume that a type adapter was NOT registered for some - * {@code Point} class, but rather the default Gson serialization is applied. - * In this case, some {@code new Point(2,3)} would serialize as {@code - * {"x":2,"y":3}}. + *

For this case, assume that a type adapter was NOT registered for some {@code Point} class, + * but rather the default Gson serialization is applied. In this case, some {@code new Point(2,3)} + * would serialize as {@code {"x":2,"y":3}}. * - *

Given the assumption above, a {@code Map} will be - * serialized as an array of arrays (can be viewed as an entry set of pairs). + *

Given the assumption above, a {@code Map} will be serialized as an array of + * arrays (can be viewed as an entry set of pairs). * *

Below is an example of serializing complex types as JSON arrays: - *

 {@code
-   *   Gson gson = new GsonBuilder()
-   *       .enableComplexMapKeySerialization()
-   *       .create();
-   *
-   *   Map original = new LinkedHashMap<>();
-   *   original.put(new Point(5, 6), "a");
-   *   original.put(new Point(8, 8), "b");
-   *   System.out.println(gson.toJson(original, type));
-   * }
-   * 
+ * + *
{@code
+   * Gson gson = new GsonBuilder()
+   *     .enableComplexMapKeySerialization()
+   *     .create();
+   *
+   * Map original = new LinkedHashMap<>();
+   * original.put(new Point(5, 6), "a");
+   * original.put(new Point(8, 8), "b");
+   * System.out.println(gson.toJson(original, type));
+   * }
* * The JSON output would look as follows: - *
   {@code
+   *
+   * 
{@code
+   * [
+   *   [
+   *     {
+   *       "x": 5,
+   *       "y": 6
+   *     },
+   *     "a"
+   *   ],
    *   [
-   *     [
-   *       {
-   *         "x": 5,
-   *         "y": 6
-   *       },
-   *       "a"
-   *     ],
-   *     [
-   *       {
-   *         "x": 8,
-   *         "y": 8
-   *       },
-   *       "b"
-   *     ]
+   *     {
+   *       "x": 8,
+   *       "y": 8
+   *     },
+   *     "b"
    *   ]
+   * ]
    * }
* * @return a reference to this {@code GsonBuilder} object to fulfill the "Builder" pattern @@ -325,20 +327,22 @@ public GsonBuilder enableComplexMapKeySerialization() { } /** - * Configures Gson to exclude inner classes (= non-{@code static} nested classes) during serialization - * and deserialization. This is a convenience method which behaves as if an {@link ExclusionStrategy} - * which excludes inner classes was {@linkplain #setExclusionStrategies(ExclusionStrategy...) registered with this builder}. - * This means inner classes will be serialized as JSON {@code null}, and will be deserialized as - * Java {@code null} with their JSON data being ignored. And fields with an inner class as type will - * be ignored during serialization and deserialization. + * Configures Gson to exclude inner classes (= non-{@code static} nested classes) during + * serialization and deserialization. This is a convenience method which behaves as if an {@link + * ExclusionStrategy} which excludes inner classes was {@linkplain + * #setExclusionStrategies(ExclusionStrategy...) registered with this builder}. This means inner + * classes will be serialized as JSON {@code null}, and will be deserialized as Java {@code null} + * with their JSON data being ignored. And fields with an inner class as type will be ignored + * during serialization and deserialization. * *

By default Gson serializes and deserializes inner classes, but ignores references to the - * enclosing instance. Deserialization might not be possible at all when {@link #disableJdkUnsafe()} - * is used (and no custom {@link InstanceCreator} is registered), or it can lead to unexpected - * {@code NullPointerException}s when the deserialized instance is used afterwards. + * enclosing instance. Deserialization might not be possible at all when {@link + * #disableJdkUnsafe()} is used (and no custom {@link InstanceCreator} is registered), or it can + * lead to unexpected {@code NullPointerException}s when the deserialized instance is used + * afterwards. * - *

In general using inner classes with Gson should be avoided; they should be converted to {@code static} - * nested classes if possible. + *

In general using inner classes with Gson should be avoided; they should be converted to + * {@code static} nested classes if possible. * * @return a reference to this {@code GsonBuilder} object to fulfill the "Builder" pattern * @since 1.3 @@ -375,12 +379,12 @@ public GsonBuilder setFieldNamingPolicy(FieldNamingPolicy namingConvention) { } /** - * Configures Gson to apply a specific naming strategy to an object's fields during - * serialization and deserialization. + * Configures Gson to apply a specific naming strategy to an object's fields during serialization + * and deserialization. * - *

The created Gson instance might only use the field naming strategy once for a - * field and cache the result. It is not guaranteed that the strategy will be used - * again every time the value of a field is serialized or deserialized. + *

The created Gson instance might only use the field naming strategy once for a field and + * cache the result. It is not guaranteed that the strategy will be used again every time the + * value of a field is serialized or deserialized. * * @param fieldNamingStrategy the naming strategy to apply to the fields * @return a reference to this {@code GsonBuilder} object to fulfill the "Builder" pattern @@ -422,25 +426,24 @@ public GsonBuilder setNumberToNumberStrategy(ToNumberStrategy numberToNumberStra /** * Configures Gson to apply a set of exclusion strategies during both serialization and - * deserialization. Each of the {@code strategies} will be applied as a disjunction rule. - * This means that if one of the {@code strategies} suggests that a field (or class) should be - * skipped then that field (or object) is skipped during serialization/deserialization. - * The strategies are added to the existing strategies (if any); the existing strategies - * are not replaced. - * - *

Fields are excluded for serialization and deserialization when - * {@link ExclusionStrategy#shouldSkipField(FieldAttributes) shouldSkipField} returns {@code true}, - * or when {@link ExclusionStrategy#shouldSkipClass(Class) shouldSkipClass} returns {@code true} - * for the field type. Gson behaves as if the field did not exist; its value is not serialized - * and on deserialization if a JSON member with this name exists it is skipped by default.
- * When objects of an excluded type (as determined by - * {@link ExclusionStrategy#shouldSkipClass(Class) shouldSkipClass}) are serialized a - * JSON null is written to output, and when deserialized the JSON value is skipped and - * {@code null} is returned. - * - *

The created Gson instance might only use an exclusion strategy once for a field or - * class and cache the result. It is not guaranteed that the strategy will be used again - * every time the value of a field or a class is serialized or deserialized. + * deserialization. Each of the {@code strategies} will be applied as a disjunction rule. This + * means that if one of the {@code strategies} suggests that a field (or class) should be skipped + * then that field (or object) is skipped during serialization/deserialization. The strategies are + * added to the existing strategies (if any); the existing strategies are not replaced. + * + *

Fields are excluded for serialization and deserialization when {@link + * ExclusionStrategy#shouldSkipField(FieldAttributes) shouldSkipField} returns {@code true}, or + * when {@link ExclusionStrategy#shouldSkipClass(Class) shouldSkipClass} returns {@code true} for + * the field type. Gson behaves as if the field did not exist; its value is not serialized and on + * deserialization if a JSON member with this name exists it is skipped by default.
+ * When objects of an excluded type (as determined by {@link + * ExclusionStrategy#shouldSkipClass(Class) shouldSkipClass}) are serialized a JSON null is + * written to output, and when deserialized the JSON value is skipped and {@code null} is + * returned. + * + *

The created Gson instance might only use an exclusion strategy once for a field or class and + * cache the result. It is not guaranteed that the strategy will be used again every time the + * value of a field or a class is serialized or deserialized. * * @param strategies the set of strategy object to apply during object (de)serialization. * @return a reference to this {@code GsonBuilder} object to fulfill the "Builder" pattern @@ -456,15 +459,14 @@ public GsonBuilder setExclusionStrategies(ExclusionStrategy... strategies) { } /** - * Configures Gson to apply the passed in exclusion strategy during serialization. - * If this method is invoked numerous times with different exclusion strategy objects - * then the exclusion strategies that were added will be applied as a disjunction rule. - * This means that if one of the added exclusion strategies suggests that a field (or - * class) should be skipped then that field (or object) is skipped during its - * serialization. + * Configures Gson to apply the passed in exclusion strategy during serialization. If this method + * is invoked numerous times with different exclusion strategy objects then the exclusion + * strategies that were added will be applied as a disjunction rule. This means that if one of the + * added exclusion strategies suggests that a field (or class) should be skipped then that field + * (or object) is skipped during its serialization. * - *

See the documentation of {@link #setExclusionStrategies(ExclusionStrategy...)} - * for a detailed description of the effect of exclusion strategies. + *

See the documentation of {@link #setExclusionStrategies(ExclusionStrategy...)} for a + * detailed description of the effect of exclusion strategies. * * @param strategy an exclusion strategy to apply during serialization. * @return a reference to this {@code GsonBuilder} object to fulfill the "Builder" pattern @@ -478,15 +480,14 @@ public GsonBuilder addSerializationExclusionStrategy(ExclusionStrategy strategy) } /** - * Configures Gson to apply the passed in exclusion strategy during deserialization. - * If this method is invoked numerous times with different exclusion strategy objects - * then the exclusion strategies that were added will be applied as a disjunction rule. - * This means that if one of the added exclusion strategies suggests that a field (or - * class) should be skipped then that field (or object) is skipped during its - * deserialization. + * Configures Gson to apply the passed in exclusion strategy during deserialization. If this + * method is invoked numerous times with different exclusion strategy objects then the exclusion + * strategies that were added will be applied as a disjunction rule. This means that if one of the + * added exclusion strategies suggests that a field (or class) should be skipped then that field + * (or object) is skipped during its deserialization. * - *

See the documentation of {@link #setExclusionStrategies(ExclusionStrategy...)} - * for a detailed description of the effect of exclusion strategies. + *

See the documentation of {@link #setExclusionStrategies(ExclusionStrategy...)} for a + * detailed description of the effect of exclusion strategies. * * @param strategy an exclusion strategy to apply during deserialization. * @return a reference to this {@code GsonBuilder} object to fulfill the "Builder" pattern @@ -514,8 +515,9 @@ public GsonBuilder setPrettyPrinting() { } /** - * Configures Gson to output JSON that uses a certain kind of formatting style (for example newline and indent). - * This option only affects JSON serialization. By default Gson produces compact JSON output without any formatting. + * Configures Gson to output JSON that uses a certain kind of formatting style (for example + * newline and indent). This option only affects JSON serialization. By default Gson produces + * compact JSON output without any formatting. * * @param formattingStyle the formatting style to use. * @return a reference to this {@code GsonBuilder} object to fulfill the "Builder" pattern @@ -530,16 +532,17 @@ public GsonBuilder setFormattingStyle(FormattingStyle formattingStyle) { /** * Sets the strictness of this builder to {@link Strictness#LENIENT}. * - * @deprecated This method is equivalent to calling {@link #setStrictness(Strictness)} with - * {@link Strictness#LENIENT}: {@code setStrictness(Strictness.LENIENT)} - * + * @deprecated This method is equivalent to calling {@link #setStrictness(Strictness)} with {@link + * Strictness#LENIENT}: {@code setStrictness(Strictness.LENIENT)} * @return a reference to this {@code GsonBuilder} object to fulfill the "Builder" pattern. * @see JsonReader#setStrictness(Strictness) * @see JsonWriter#setStrictness(Strictness) * @see #setStrictness(Strictness) */ @Deprecated - @InlineMe(replacement = "this.setStrictness(Strictness.LENIENT)", imports = "com.google.gson.Strictness") + @InlineMe( + replacement = "this.setStrictness(Strictness.LENIENT)", + imports = "com.google.gson.Strictness") @CanIgnoreReturnValue public GsonBuilder setLenient() { return setStrictness(Strictness.LENIENT); @@ -548,10 +551,9 @@ public GsonBuilder setLenient() { /** * Sets the strictness of this builder to the provided parameter. * - *

This changes how strict the - * RFC 8259 JSON specification is enforced when parsing or - * writing JSON. For details on this, refer to {@link JsonReader#setStrictness(Strictness)} and - * {@link JsonWriter#setStrictness(Strictness)}.

+ *

This changes how strict the RFC 8259 JSON + * specification is enforced when parsing or writing JSON. For details on this, refer to + * {@link JsonReader#setStrictness(Strictness)} and {@link JsonWriter#setStrictness(Strictness)}. * * @param strictness the new strictness mode. May not be {@code null}. * @return a reference to this {@code GsonBuilder} object to fulfill the "Builder" pattern. @@ -584,11 +586,12 @@ public GsonBuilder disableHtmlEscaping() { * will be used to decide the serialization format. * *

The date format will be used to serialize and deserialize {@link java.util.Date} and in case - * the {@code java.sql} module is present, also {@link java.sql.Timestamp} and {@link java.sql.Date}. + * the {@code java.sql} module is present, also {@link java.sql.Timestamp} and {@link + * java.sql.Date}. * *

Note that this pattern must abide by the convention provided by {@code SimpleDateFormat} * class. See the documentation in {@link java.text.SimpleDateFormat} for more information on - * valid date and time patterns.

+ * valid date and time patterns. * * @param pattern the pattern that dates will be serialized/deserialized to/from * @return a reference to this {@code GsonBuilder} object to fulfill the "Builder" pattern @@ -609,16 +612,16 @@ public GsonBuilder setDateFormat(String pattern) { } /** - * Configures Gson to serialize {@code Date} objects according to the style value provided. - * You can call this method or {@link #setDateFormat(String)} multiple times, but only the last + * Configures Gson to serialize {@code Date} objects according to the style value provided. You + * can call this method or {@link #setDateFormat(String)} multiple times, but only the last * invocation will be used to decide the serialization format. * - *

Note that this style value should be one of the predefined constants in the - * {@code DateFormat} class. See the documentation in {@link java.text.DateFormat} for more - * information on the valid style constants.

+ *

Note that this style value should be one of the predefined constants in the {@code + * DateFormat} class. See the documentation in {@link java.text.DateFormat} for more information + * on the valid style constants. * * @param style the predefined date style that date objects will be serialized/deserialized - * to/from + * to/from * @return a reference to this {@code GsonBuilder} object to fulfill the "Builder" pattern * @since 1.2 */ @@ -630,16 +633,16 @@ public GsonBuilder setDateFormat(int style) { } /** - * Configures Gson to serialize {@code Date} objects according to the style value provided. - * You can call this method or {@link #setDateFormat(String)} multiple times, but only the last + * Configures Gson to serialize {@code Date} objects according to the style value provided. You + * can call this method or {@link #setDateFormat(String)} multiple times, but only the last * invocation will be used to decide the serialization format. * - *

Note that this style value should be one of the predefined constants in the - * {@code DateFormat} class. See the documentation in {@link java.text.DateFormat} for more - * information on the valid style constants.

+ *

Note that this style value should be one of the predefined constants in the {@code + * DateFormat} class. See the documentation in {@link java.text.DateFormat} for more information + * on the valid style constants. * * @param dateStyle the predefined date style that date objects will be serialized/deserialized - * to/from + * to/from * @param timeStyle the predefined style for the time portion of the date objects * @return a reference to this {@code GsonBuilder} object to fulfill the "Builder" pattern * @since 1.2 @@ -663,25 +666,27 @@ public GsonBuilder setDateFormat(int dateStyle, int timeStyle) { * types! For example, applications registering {@code boolean.class} should also register {@code * Boolean.class}. * - *

{@link JsonSerializer} and {@link JsonDeserializer} are made "{@code null}-safe". This - * means when trying to serialize {@code null}, Gson will write a JSON {@code null} and the - * serializer is not called. Similarly when deserializing a JSON {@code null}, Gson will emit - * {@code null} without calling the deserializer. If it is desired to handle {@code null} values, - * a {@link TypeAdapter} should be used instead. + *

{@link JsonSerializer} and {@link JsonDeserializer} are made "{@code null}-safe". This means + * when trying to serialize {@code null}, Gson will write a JSON {@code null} and the serializer + * is not called. Similarly when deserializing a JSON {@code null}, Gson will emit {@code null} + * without calling the deserializer. If it is desired to handle {@code null} values, a {@link + * TypeAdapter} should be used instead. * * @param type the type definition for the type adapter being registered - * @param typeAdapter This object must implement at least one of the {@link TypeAdapter}, - * {@link InstanceCreator}, {@link JsonSerializer}, and a {@link JsonDeserializer} interfaces. + * @param typeAdapter This object must implement at least one of the {@link TypeAdapter}, {@link + * InstanceCreator}, {@link JsonSerializer}, and a {@link JsonDeserializer} interfaces. * @return a reference to this {@code GsonBuilder} object to fulfill the "Builder" pattern - * @throws IllegalArgumentException if the type adapter being registered is for {@code Object} class or {@link JsonElement} or any of its subclasses + * @throws IllegalArgumentException if the type adapter being registered is for {@code Object} + * class or {@link JsonElement} or any of its subclasses */ @CanIgnoreReturnValue public GsonBuilder registerTypeAdapter(Type type, Object typeAdapter) { Objects.requireNonNull(type); - $Gson$Preconditions.checkArgument(typeAdapter instanceof JsonSerializer - || typeAdapter instanceof JsonDeserializer - || typeAdapter instanceof InstanceCreator - || typeAdapter instanceof TypeAdapter); + $Gson$Preconditions.checkArgument( + typeAdapter instanceof JsonSerializer + || typeAdapter instanceof JsonDeserializer + || typeAdapter instanceof InstanceCreator + || typeAdapter instanceof TypeAdapter); if (isTypeObjectOrJsonElement(type)) { throw new IllegalArgumentException("Cannot override built-in adapter for " + type); @@ -696,7 +701,8 @@ public GsonBuilder registerTypeAdapter(Type type, Object typeAdapter) { } if (typeAdapter instanceof TypeAdapter) { @SuppressWarnings({"unchecked", "rawtypes"}) - TypeAdapterFactory factory = TypeAdapters.newFactory(TypeToken.get(type), (TypeAdapter)typeAdapter); + TypeAdapterFactory factory = + TypeAdapters.newFactory(TypeToken.get(type), (TypeAdapter) typeAdapter); factories.add(factory); } return this; @@ -704,19 +710,18 @@ public GsonBuilder registerTypeAdapter(Type type, Object typeAdapter) { private boolean isTypeObjectOrJsonElement(Type type) { return type instanceof Class - && (type == Object.class - || JsonElement.class.isAssignableFrom((Class) type)); + && (type == Object.class || JsonElement.class.isAssignableFrom((Class) type)); } /** - * Register a factory for type adapters. Registering a factory is useful when the type - * adapter needs to be configured based on the type of the field being processed. Gson - * is designed to handle a large number of factories, so you should consider registering - * them to be at par with registering an individual type adapter. + * Register a factory for type adapters. Registering a factory is useful when the type adapter + * needs to be configured based on the type of the field being processed. Gson is designed to + * handle a large number of factories, so you should consider registering them to be at par with + * registering an individual type adapter. * - *

The created Gson instance might only use the factory once to create an adapter for - * a specific type and cache the result. It is not guaranteed that the factory will be used - * again every time the type is serialized or deserialized. + *

The created Gson instance might only use the factory once to create an adapter for a + * specific type and cache the result. It is not guaranteed that the factory will be used again + * every time the type is serialized or deserialized. * * @since 2.1 */ @@ -729,25 +734,27 @@ public GsonBuilder registerTypeAdapterFactory(TypeAdapterFactory factory) { /** * Configures Gson for custom serialization or deserialization for an inheritance type hierarchy. - * This method combines the registration of a {@link TypeAdapter}, {@link JsonSerializer} and - * a {@link JsonDeserializer}. If a type adapter was previously registered for the specified - * type hierarchy, it is overridden. If a type adapter is registered for a specific type in - * the type hierarchy, it will be invoked instead of the one registered for the type hierarchy. + * This method combines the registration of a {@link TypeAdapter}, {@link JsonSerializer} and a + * {@link JsonDeserializer}. If a type adapter was previously registered for the specified type + * hierarchy, it is overridden. If a type adapter is registered for a specific type in the type + * hierarchy, it will be invoked instead of the one registered for the type hierarchy. * * @param baseType the class definition for the type adapter being registered for the base class - * or interface - * @param typeAdapter This object must implement at least one of {@link TypeAdapter}, - * {@link JsonSerializer} or {@link JsonDeserializer} interfaces. + * or interface + * @param typeAdapter This object must implement at least one of {@link TypeAdapter}, {@link + * JsonSerializer} or {@link JsonDeserializer} interfaces. * @return a reference to this {@code GsonBuilder} object to fulfill the "Builder" pattern - * @throws IllegalArgumentException if the type adapter being registered is for {@link JsonElement} or any of its subclasses + * @throws IllegalArgumentException if the type adapter being registered is for {@link + * JsonElement} or any of its subclasses * @since 1.7 */ @CanIgnoreReturnValue public GsonBuilder registerTypeHierarchyAdapter(Class baseType, Object typeAdapter) { Objects.requireNonNull(baseType); - $Gson$Preconditions.checkArgument(typeAdapter instanceof JsonSerializer - || typeAdapter instanceof JsonDeserializer - || typeAdapter instanceof TypeAdapter); + $Gson$Preconditions.checkArgument( + typeAdapter instanceof JsonSerializer + || typeAdapter instanceof JsonDeserializer + || typeAdapter instanceof TypeAdapter); if (JsonElement.class.isAssignableFrom(baseType)) { throw new IllegalArgumentException("Cannot override built-in adapter for " + baseType); @@ -758,7 +765,8 @@ public GsonBuilder registerTypeHierarchyAdapter(Class baseType, Object typeAd } if (typeAdapter instanceof TypeAdapter) { @SuppressWarnings({"unchecked", "rawtypes"}) - TypeAdapterFactory factory = TypeAdapters.newTypeHierarchyFactory(baseType, (TypeAdapter)typeAdapter); + TypeAdapterFactory factory = + TypeAdapters.newTypeHierarchyFactory(baseType, (TypeAdapter) typeAdapter); factories.add(factory); } return this; @@ -766,20 +774,19 @@ public GsonBuilder registerTypeHierarchyAdapter(Class baseType, Object typeAd /** * Section 6 of JSON specification disallows - * special double values (NaN, Infinity, -Infinity). However, - * Javascript + * special double values (NaN, Infinity, -Infinity). However, Javascript * specification (see section 4.3.20, 4.3.22, 4.3.23) allows these values as valid Javascript * values. Moreover, most JavaScript engines will accept these special values in JSON without * problem. So, at a practical level, it makes sense to accept these values as valid JSON even * though JSON specification disallows them. * *

Gson always accepts these special values during deserialization. However, it outputs - * strictly compliant JSON. Hence, if it encounters a float value {@link Float#NaN}, - * {@link Float#POSITIVE_INFINITY}, {@link Float#NEGATIVE_INFINITY}, or a double value - * {@link Double#NaN}, {@link Double#POSITIVE_INFINITY}, {@link Double#NEGATIVE_INFINITY}, it - * will throw an {@link IllegalArgumentException}. This method provides a way to override the - * default behavior when you know that the JSON receiver will be able to handle these special - * values. + * strictly compliant JSON. Hence, if it encounters a float value {@link Float#NaN}, {@link + * Float#POSITIVE_INFINITY}, {@link Float#NEGATIVE_INFINITY}, or a double value {@link + * Double#NaN}, {@link Double#POSITIVE_INFINITY}, {@link Double#NEGATIVE_INFINITY}, it will throw + * an {@link IllegalArgumentException}. This method provides a way to override the default + * behavior when you know that the JSON receiver will be able to handle these special values. * * @return a reference to this {@code GsonBuilder} object to fulfill the "Builder" pattern * @since 1.3 @@ -793,15 +800,14 @@ public GsonBuilder serializeSpecialFloatingPointValues() { /** * Disables usage of JDK's {@code sun.misc.Unsafe}. * - *

By default Gson uses {@code Unsafe} to create instances of classes which don't have - * a no-args constructor. However, {@code Unsafe} might not be available for all Java - * runtimes. For example Android does not provide {@code Unsafe}, or only with limited - * functionality. Additionally {@code Unsafe} creates instances without executing any - * constructor or initializer block, or performing initialization of field values. This can - * lead to surprising and difficult to debug errors. - * Therefore, to get reliable behavior regardless of which runtime is used, and to detect - * classes which cannot be deserialized in an early stage of development, this method allows - * disabling usage of {@code Unsafe}. + *

By default Gson uses {@code Unsafe} to create instances of classes which don't have a + * no-args constructor. However, {@code Unsafe} might not be available for all Java runtimes. For + * example Android does not provide {@code Unsafe}, or only with limited functionality. + * Additionally {@code Unsafe} creates instances without executing any constructor or initializer + * block, or performing initialization of field values. This can lead to surprising and difficult + * to debug errors. Therefore, to get reliable behavior regardless of which runtime is used, and + * to detect classes which cannot be deserialized in an early stage of development, this method + * allows disabling usage of {@code Unsafe}. * * @return a reference to this {@code GsonBuilder} object to fulfill the "Builder" pattern * @since 2.9.0 @@ -813,20 +819,20 @@ public GsonBuilder disableJdkUnsafe() { } /** - * Adds a reflection access filter. A reflection access filter prevents Gson from using - * reflection for the serialization and deserialization of certain classes. The logic in - * the filter specifies which classes those are. + * Adds a reflection access filter. A reflection access filter prevents Gson from using reflection + * for the serialization and deserialization of certain classes. The logic in the filter specifies + * which classes those are. * - *

Filters will be invoked in reverse registration order, that is, the most recently - * added filter will be invoked first. + *

Filters will be invoked in reverse registration order, that is, the most recently added + * filter will be invoked first. * - *

By default Gson has no filters configured and will try to use reflection for - * all classes for which no {@link TypeAdapter} has been registered, and for which no - * built-in Gson {@code TypeAdapter} exists. + *

By default Gson has no filters configured and will try to use reflection for all classes for + * which no {@link TypeAdapter} has been registered, and for which no built-in Gson {@code + * TypeAdapter} exists. * - *

The created Gson instance might only use an access filter once for a class or its - * members and cache the result. It is not guaranteed that the filter will be used again - * every time a class or its members are accessed during serialization or deserialization. + *

The created Gson instance might only use an access filter once for a class or its members + * and cache the result. It is not guaranteed that the filter will be used again every time a + * class or its members are accessed during serialization or deserialization. * * @param filter filter to add * @return a reference to this {@code GsonBuilder} object to fulfill the "Builder" pattern @@ -846,7 +852,8 @@ public GsonBuilder addReflectionAccessFilter(ReflectionAccessFilter filter) { * @return an instance of Gson configured with the options currently set in this builder */ public Gson create() { - List factories = new ArrayList<>(this.factories.size() + this.hierarchyFactories.size() + 3); + List factories = + new ArrayList<>(this.factories.size() + this.hierarchyFactories.size() + 3); factories.addAll(this.factories); Collections.reverse(factories); @@ -856,17 +863,32 @@ public Gson create() { addTypeAdaptersForDate(datePattern, dateStyle, timeStyle, factories); - return new Gson(excluder, fieldNamingPolicy, new HashMap<>(instanceCreators), - serializeNulls, complexMapKeySerialization, - generateNonExecutableJson, escapeHtmlChars, formattingStyle, strictness, - serializeSpecialFloatingPointValues, useJdkUnsafe, longSerializationPolicy, - datePattern, dateStyle, timeStyle, new ArrayList<>(this.factories), - new ArrayList<>(this.hierarchyFactories), factories, - objectToNumberStrategy, numberToNumberStrategy, new ArrayList<>(reflectionFilters)); - } - - private void addTypeAdaptersForDate(String datePattern, int dateStyle, int timeStyle, - List factories) { + return new Gson( + excluder, + fieldNamingPolicy, + new HashMap<>(instanceCreators), + serializeNulls, + complexMapKeySerialization, + generateNonExecutableJson, + escapeHtmlChars, + formattingStyle, + strictness, + serializeSpecialFloatingPointValues, + useJdkUnsafe, + longSerializationPolicy, + datePattern, + dateStyle, + timeStyle, + new ArrayList<>(this.factories), + new ArrayList<>(this.hierarchyFactories), + factories, + objectToNumberStrategy, + numberToNumberStrategy, + new ArrayList<>(reflectionFilters)); + } + + private void addTypeAdaptersForDate( + String datePattern, int dateStyle, int timeStyle, List factories) { TypeAdapterFactory dateAdapterFactory; boolean sqlTypesSupported = SqlTypesSupport.SUPPORTS_SQL_TYPES; TypeAdapterFactory sqlTimestampAdapterFactory = null; @@ -876,15 +898,19 @@ private void addTypeAdaptersForDate(String datePattern, int dateStyle, int timeS dateAdapterFactory = DefaultDateTypeAdapter.DateType.DATE.createAdapterFactory(datePattern); if (sqlTypesSupported) { - sqlTimestampAdapterFactory = SqlTypesSupport.TIMESTAMP_DATE_TYPE.createAdapterFactory(datePattern); + sqlTimestampAdapterFactory = + SqlTypesSupport.TIMESTAMP_DATE_TYPE.createAdapterFactory(datePattern); sqlDateAdapterFactory = SqlTypesSupport.DATE_DATE_TYPE.createAdapterFactory(datePattern); } } else if (dateStyle != DateFormat.DEFAULT && timeStyle != DateFormat.DEFAULT) { - dateAdapterFactory = DefaultDateTypeAdapter.DateType.DATE.createAdapterFactory(dateStyle, timeStyle); + dateAdapterFactory = + DefaultDateTypeAdapter.DateType.DATE.createAdapterFactory(dateStyle, timeStyle); if (sqlTypesSupported) { - sqlTimestampAdapterFactory = SqlTypesSupport.TIMESTAMP_DATE_TYPE.createAdapterFactory(dateStyle, timeStyle); - sqlDateAdapterFactory = SqlTypesSupport.DATE_DATE_TYPE.createAdapterFactory(dateStyle, timeStyle); + sqlTimestampAdapterFactory = + SqlTypesSupport.TIMESTAMP_DATE_TYPE.createAdapterFactory(dateStyle, timeStyle); + sqlDateAdapterFactory = + SqlTypesSupport.DATE_DATE_TYPE.createAdapterFactory(dateStyle, timeStyle); } } else { return; diff --git a/gson/src/main/java/com/google/gson/InstanceCreator.java b/gson/src/main/java/com/google/gson/InstanceCreator.java index 486f250482..48cf6a4854 100644 --- a/gson/src/main/java/com/google/gson/InstanceCreator.java +++ b/gson/src/main/java/com/google/gson/InstanceCreator.java @@ -20,14 +20,15 @@ /** * This interface is implemented to create instances of a class that does not define a no-args - * constructor. If you can modify the class, you should instead add a private, or public - * no-args constructor. However, that is not possible for library classes, such as JDK classes, or - * a third-party library that you do not have source-code of. In such cases, you should define an + * constructor. If you can modify the class, you should instead add a private, or public no-args + * constructor. However, that is not possible for library classes, such as JDK classes, or a + * third-party library that you do not have source-code of. In such cases, you should define an * instance creator for the class. Implementations of this interface should be registered with * {@link GsonBuilder#registerTypeAdapter(Type, Object)} method before Gson will be able to use * them. - *

Let us look at an example where defining an InstanceCreator might be useful. The - * {@code Id} class defined below does not have a default no-args constructor.

+ * + *

Let us look at an example where defining an InstanceCreator might be useful. The {@code Id} + * class defined below does not have a default no-args constructor. * *

  * public class Id<T> {
@@ -42,7 +43,7 @@
  *
  * 

If Gson encounters an object of type {@code Id} during deserialization, it will throw an * exception. The easiest way to solve this problem will be to add a (public or private) no-args - * constructor as follows:

+ * constructor as follows: * *
  * private Id() {
@@ -51,8 +52,8 @@
  * 
* *

However, let us assume that the developer does not have access to the source-code of the - * {@code Id} class, or does not want to define a no-args constructor for it. The developer - * can solve this problem by defining an {@code InstanceCreator} for {@code Id}:

+ * {@code Id} class, or does not want to define a no-args constructor for it. The developer can + * solve this problem by defining an {@code InstanceCreator} for {@code Id}: * *
  * class IdInstanceCreator implements InstanceCreator<Id> {
@@ -64,17 +65,15 @@
  *
  * 

Note that it does not matter what the fields of the created instance contain since Gson will * overwrite them with the deserialized values specified in JSON. You should also ensure that a - * new object is returned, not a common object since its fields will be overwritten. - * The developer will need to register {@code IdInstanceCreator} with Gson as follows:

+ * new object is returned, not a common object since its fields will be overwritten. The + * developer will need to register {@code IdInstanceCreator} with Gson as follows: * *
  * Gson gson = new GsonBuilder().registerTypeAdapter(Id.class, new IdInstanceCreator()).create();
  * 
* * @param the type of object that will be created by this implementation. - * * @see GsonBuilder#registerTypeAdapter(Type, Object) - * * @author Inderjeet Singh * @author Joel Leitch */ @@ -82,10 +81,10 @@ public interface InstanceCreator { /** * Gson invokes this call-back method during deserialization to create an instance of the - * specified type. The fields of the returned instance are overwritten with the data present - * in the JSON. Since the prior contents of the object are destroyed and overwritten, do not - * return an instance that is useful elsewhere. In particular, do not return a common instance, - * always use {@code new} to create a new instance. + * specified type. The fields of the returned instance are overwritten with the data present in + * the JSON. Since the prior contents of the object are destroyed and overwritten, do not return + * an instance that is useful elsewhere. In particular, do not return a common instance, always + * use {@code new} to create a new instance. * * @param type the parameterized T represented as a {@link Type}. * @return a default object instance of type T. diff --git a/gson/src/main/java/com/google/gson/JsonArray.java b/gson/src/main/java/com/google/gson/JsonArray.java index e8768ef592..d260780d4c 100644 --- a/gson/src/main/java/com/google/gson/JsonArray.java +++ b/gson/src/main/java/com/google/gson/JsonArray.java @@ -39,9 +39,7 @@ public final class JsonArray extends JsonElement implements Iterable { private final ArrayList elements; - /** - * Creates an empty JsonArray. - */ + /** Creates an empty JsonArray. */ @SuppressWarnings("deprecation") // superclass constructor public JsonArray() { elements = new ArrayList<>(); @@ -51,8 +49,7 @@ public JsonArray() { * Creates an empty JsonArray with the desired initial capacity. * * @param capacity initial capacity. - * @throws IllegalArgumentException if the {@code capacity} is - * negative + * @throws IllegalArgumentException if the {@code capacity} is negative * @since 2.8.1 */ @SuppressWarnings("deprecation") // superclass constructor @@ -152,8 +149,8 @@ public JsonElement set(int index, JsonElement element) { } /** - * Removes the first occurrence of the specified element from this array, if it is present. - * If the array does not contain the element, it is unchanged. + * Removes the first occurrence of the specified element from this array, if it is present. If the + * array does not contain the element, it is unchanged. * * @param element element to be removed from this array, if present * @return true if this array contained the specified element, false otherwise @@ -165,9 +162,8 @@ public boolean remove(JsonElement element) { } /** - * Removes the element at the specified position in this array. Shifts any subsequent elements - * to the left (subtracts one from their indices). Returns the element that was removed from - * the array. + * Removes the element at the specified position in this array. Shifts any subsequent elements to + * the left (subtracts one from their indices). Returns the element removed from the array. * * @param index index the index of the element to be removed * @return the element previously at the specified position @@ -226,7 +222,7 @@ public Iterator iterator() { * @param i the index of the element that is being sought. * @return the element present at the i-th index. * @throws IndexOutOfBoundsException if {@code i} is negative or greater than or equal to the - * {@link #size()} of the array. + * {@link #size()} of the array. */ public JsonElement get(int i) { return elements.get(i); @@ -241,9 +237,9 @@ private JsonElement getAsSingleElement() { } /** - * Convenience method to get this array as a {@link Number} if it contains a single element. - * This method calls {@link JsonElement#getAsNumber()} on the element, therefore any - * of the exceptions declared by that method can occur. + * Convenience method to get this array as a {@link Number} if it contains a single element. This + * method calls {@link JsonElement#getAsNumber()} on the element, therefore any of the exceptions + * declared by that method can occur. * * @return this element as a number if it is single element array. * @throws IllegalStateException if the array is empty or has more than one element. @@ -254,9 +250,9 @@ public Number getAsNumber() { } /** - * Convenience method to get this array as a {@link String} if it contains a single element. - * This method calls {@link JsonElement#getAsString()} on the element, therefore any - * of the exceptions declared by that method can occur. + * Convenience method to get this array as a {@link String} if it contains a single element. This + * method calls {@link JsonElement#getAsString()} on the element, therefore any of the exceptions + * declared by that method can occur. * * @return this element as a String if it is single element array. * @throws IllegalStateException if the array is empty or has more than one element. @@ -267,9 +263,9 @@ public String getAsString() { } /** - * Convenience method to get this array as a double if it contains a single element. - * This method calls {@link JsonElement#getAsDouble()} on the element, therefore any - * of the exceptions declared by that method can occur. + * Convenience method to get this array as a double if it contains a single element. This method + * calls {@link JsonElement#getAsDouble()} on the element, therefore any of the exceptions + * declared by that method can occur. * * @return this element as a double if it is single element array. * @throws IllegalStateException if the array is empty or has more than one element. @@ -281,8 +277,8 @@ public double getAsDouble() { /** * Convenience method to get this array as a {@link BigDecimal} if it contains a single element. - * This method calls {@link JsonElement#getAsBigDecimal()} on the element, therefore any - * of the exceptions declared by that method can occur. + * This method calls {@link JsonElement#getAsBigDecimal()} on the element, therefore any of the + * exceptions declared by that method can occur. * * @return this element as a {@link BigDecimal} if it is single element array. * @throws IllegalStateException if the array is empty or has more than one element. @@ -295,8 +291,8 @@ public BigDecimal getAsBigDecimal() { /** * Convenience method to get this array as a {@link BigInteger} if it contains a single element. - * This method calls {@link JsonElement#getAsBigInteger()} on the element, therefore any - * of the exceptions declared by that method can occur. + * This method calls {@link JsonElement#getAsBigInteger()} on the element, therefore any of the + * exceptions declared by that method can occur. * * @return this element as a {@link BigInteger} if it is single element array. * @throws IllegalStateException if the array is empty or has more than one element. @@ -308,9 +304,9 @@ public BigInteger getAsBigInteger() { } /** - * Convenience method to get this array as a float if it contains a single element. - * This method calls {@link JsonElement#getAsFloat()} on the element, therefore any - * of the exceptions declared by that method can occur. + * Convenience method to get this array as a float if it contains a single element. This method + * calls {@link JsonElement#getAsFloat()} on the element, therefore any of the exceptions declared + * by that method can occur. * * @return this element as a float if it is single element array. * @throws IllegalStateException if the array is empty or has more than one element. @@ -321,9 +317,9 @@ public float getAsFloat() { } /** - * Convenience method to get this array as a long if it contains a single element. - * This method calls {@link JsonElement#getAsLong()} on the element, therefore any - * of the exceptions declared by that method can occur. + * Convenience method to get this array as a long if it contains a single element. This method + * calls {@link JsonElement#getAsLong()} on the element, therefore any of the exceptions declared + * by that method can occur. * * @return this element as a long if it is single element array. * @throws IllegalStateException if the array is empty or has more than one element. @@ -334,9 +330,9 @@ public long getAsLong() { } /** - * Convenience method to get this array as an integer if it contains a single element. - * This method calls {@link JsonElement#getAsInt()} on the element, therefore any - * of the exceptions declared by that method can occur. + * Convenience method to get this array as an integer if it contains a single element. This method + * calls {@link JsonElement#getAsInt()} on the element, therefore any of the exceptions declared + * by that method can occur. * * @return this element as an integer if it is single element array. * @throws IllegalStateException if the array is empty or has more than one element. @@ -347,9 +343,9 @@ public int getAsInt() { } /** - * Convenience method to get this array as a primitive byte if it contains a single element. - * This method calls {@link JsonElement#getAsByte()} on the element, therefore any - * of the exceptions declared by that method can occur. + * Convenience method to get this array as a primitive byte if it contains a single element. This + * method calls {@link JsonElement#getAsByte()} on the element, therefore any of the exceptions + * declared by that method can occur. * * @return this element as a primitive byte if it is single element array. * @throws IllegalStateException if the array is empty or has more than one element. @@ -360,14 +356,14 @@ public byte getAsByte() { } /** - * Convenience method to get this array as a character if it contains a single element. - * This method calls {@link JsonElement#getAsCharacter()} on the element, therefore any - * of the exceptions declared by that method can occur. + * Convenience method to get this array as a character if it contains a single element. This + * method calls {@link JsonElement#getAsCharacter()} on the element, therefore any of the + * exceptions declared by that method can occur. * * @return this element as a primitive short if it is single element array. * @throws IllegalStateException if the array is empty or has more than one element. * @deprecated This method is misleading, as it does not get this element as a char but rather as - * a string's first character. + * a string's first character. */ @Deprecated @Override @@ -376,9 +372,9 @@ public char getAsCharacter() { } /** - * Convenience method to get this array as a primitive short if it contains a single element. - * This method calls {@link JsonElement#getAsShort()} on the element, therefore any - * of the exceptions declared by that method can occur. + * Convenience method to get this array as a primitive short if it contains a single element. This + * method calls {@link JsonElement#getAsShort()} on the element, therefore any of the exceptions + * declared by that method can occur. * * @return this element as a primitive short if it is single element array. * @throws IllegalStateException if the array is empty or has more than one element. @@ -389,9 +385,9 @@ public short getAsShort() { } /** - * Convenience method to get this array as a boolean if it contains a single element. - * This method calls {@link JsonElement#getAsBoolean()} on the element, therefore any - * of the exceptions declared by that method can occur. + * Convenience method to get this array as a boolean if it contains a single element. This method + * calls {@link JsonElement#getAsBoolean()} on the element, therefore any of the exceptions + * declared by that method can occur. * * @return this element as a boolean if it is single element array. * @throws IllegalStateException if the array is empty or has more than one element. @@ -402,12 +398,12 @@ public boolean getAsBoolean() { } /** - * Returns a mutable {@link List} view of this {@code JsonArray}. Changes to the {@code List} - * are visible in this {@code JsonArray} and the other way around. + * Returns a mutable {@link List} view of this {@code JsonArray}. Changes to the {@code List} are + * visible in this {@code JsonArray} and the other way around. * - *

The {@code List} does not permit {@code null} elements. Unlike {@code JsonArray}'s - * {@code null} handling, a {@link NullPointerException} is thrown when trying to add {@code null}. - * Use {@link JsonNull} for JSON null values. + *

The {@code List} does not permit {@code null} elements. Unlike {@code JsonArray}'s {@code + * null} handling, a {@link NullPointerException} is thrown when trying to add {@code null}. Use + * {@link JsonNull} for JSON null values. * * @return mutable {@code List} view * @since 2.10 @@ -417,9 +413,8 @@ public List asList() { } /** - * Returns whether the other object is equal to this. This method only considers - * the other object to be equal if it is an instance of {@code JsonArray} and has - * equal elements in the same order. + * Returns whether the other object is equal to this. This method only considers the other object + * to be equal if it is an instance of {@code JsonArray} and has equal elements in the same order. */ @Override public boolean equals(Object o) { @@ -427,8 +422,8 @@ public boolean equals(Object o) { } /** - * Returns the hash code of this array. This method calculates the hash code based - * on the elements of this array. + * Returns the hash code of this array. This method calculates the hash code based on the elements + * of this array. */ @Override public int hashCode() { diff --git a/gson/src/main/java/com/google/gson/JsonDeserializationContext.java b/gson/src/main/java/com/google/gson/JsonDeserializationContext.java index 093653d90f..caf7fb7b8e 100644 --- a/gson/src/main/java/com/google/gson/JsonDeserializationContext.java +++ b/gson/src/main/java/com/google/gson/JsonDeserializationContext.java @@ -20,8 +20,7 @@ /** * Context for deserialization that is passed to a custom deserializer during invocation of its - * {@link JsonDeserializer#deserialize(JsonElement, Type, JsonDeserializationContext)} - * method. + * {@link JsonDeserializer#deserialize(JsonElement, Type, JsonDeserializationContext)} method. * * @author Inderjeet Singh * @author Joel Leitch @@ -29,10 +28,10 @@ public interface JsonDeserializationContext { /** - * Invokes default deserialization on the specified object. It should never be invoked on - * the element received as a parameter of the - * {@link JsonDeserializer#deserialize(JsonElement, Type, JsonDeserializationContext)} method. Doing - * so will result in an infinite loop since Gson will in-turn call the custom deserializer again. + * Invokes default deserialization on the specified object. It should never be invoked on the + * element received as a parameter of the {@link JsonDeserializer#deserialize(JsonElement, Type, + * JsonDeserializationContext)} method. Doing so will result in an infinite loop since Gson will + * in-turn call the custom deserializer again. * * @param json the parse tree. * @param typeOfT type of the expected return value. @@ -42,4 +41,4 @@ public interface JsonDeserializationContext { */ @SuppressWarnings("TypeParameterUnusedInFormals") public T deserialize(JsonElement json, Type typeOfT) throws JsonParseException; -} \ No newline at end of file +} diff --git a/gson/src/main/java/com/google/gson/JsonDeserializer.java b/gson/src/main/java/com/google/gson/JsonDeserializer.java index 94f871aeaa..7c83b63e0e 100644 --- a/gson/src/main/java/com/google/gson/JsonDeserializer.java +++ b/gson/src/main/java/com/google/gson/JsonDeserializer.java @@ -19,13 +19,12 @@ import java.lang.reflect.Type; /** - *

Interface representing a custom deserializer for JSON. You should write a custom - * deserializer, if you are not happy with the default deserialization done by Gson. You will - * also need to register this deserializer through - * {@link GsonBuilder#registerTypeAdapter(Type, Object)}.

+ * Interface representing a custom deserializer for JSON. You should write a custom deserializer, if + * you are not happy with the default deserialization done by Gson. You will also need to register + * this deserializer through {@link GsonBuilder#registerTypeAdapter(Type, Object)}. * *

Let us look at example where defining a deserializer will be useful. The {@code Id} class - * defined below has two fields: {@code clazz} and {@code value}.

+ * defined below has two fields: {@code clazz} and {@code value}. * *
  * public class Id<T> {
@@ -41,11 +40,11 @@
  * }
  * 
* - *

The default deserialization of {@code Id(com.foo.MyObject.class, 20L)} will require the - * JSON string to be {"clazz":"com.foo.MyObject","value":20}. Suppose, you already know - * the type of the field that the {@code Id} will be deserialized into, and hence just want to + *

The default deserialization of {@code Id(com.foo.MyObject.class, 20L)} will require the JSON + * string to be {"clazz":"com.foo.MyObject","value":20}. Suppose, you already know the + * type of the field that the {@code Id} will be deserialized into, and hence just want to * deserialize it from a JSON string {@code 20}. You can achieve that by writing a custom - * deserializer:

+ * deserializer: * *
  * class IdDeserializer implements JsonDeserializer<Id> {
@@ -57,34 +56,34 @@
  * }
  * 
* - *

You will also need to register {@code IdDeserializer} with Gson as follows:

+ *

You will also need to register {@code IdDeserializer} with Gson as follows: * *

  * Gson gson = new GsonBuilder().registerTypeAdapter(Id.class, new IdDeserializer()).create();
  * 
* - *

Deserializers should be stateless and thread-safe, otherwise the thread-safety - * guarantees of {@link Gson} might not apply. + *

Deserializers should be stateless and thread-safe, otherwise the thread-safety guarantees of + * {@link Gson} might not apply. * - *

New applications should prefer {@link TypeAdapter}, whose streaming API - * is more efficient than this interface's tree API. + *

New applications should prefer {@link TypeAdapter}, whose streaming API is more efficient than + * this interface's tree API. * * @author Inderjeet Singh * @author Joel Leitch - * * @param type for which the deserializer is being registered. It is possible that a - * deserializer may be asked to deserialize a specific generic type of the T. + * deserializer may be asked to deserialize a specific generic type of the T. */ public interface JsonDeserializer { /** * Gson invokes this call-back method during deserialization when it encounters a field of the * specified type. - *

In the implementation of this call-back method, you should consider invoking - * {@link JsonDeserializationContext#deserialize(JsonElement, Type)} method to create objects - * for any non-trivial field of the returned object. However, you should never invoke it on the - * same type passing {@code json} since that will cause an infinite loop (Gson will call your - * call-back method again). + * + *

In the implementation of this call-back method, you should consider invoking {@link + * JsonDeserializationContext#deserialize(JsonElement, Type)} method to create objects for any + * non-trivial field of the returned object. However, you should never invoke it on the same type + * passing {@code json} since that will cause an infinite loop (Gson will call your call-back + * method again). * * @param json The Json data being deserialized * @param typeOfT The type of the Object to deserialize to diff --git a/gson/src/main/java/com/google/gson/JsonElement.java b/gson/src/main/java/com/google/gson/JsonElement.java index 1b440d0532..fa3a7af5f6 100644 --- a/gson/src/main/java/com/google/gson/JsonElement.java +++ b/gson/src/main/java/com/google/gson/JsonElement.java @@ -25,25 +25,24 @@ import java.math.BigInteger; /** - * A class representing an element of JSON. It could either be a {@link JsonObject}, a - * {@link JsonArray}, a {@link JsonPrimitive} or a {@link JsonNull}. + * A class representing an element of JSON. It could either be a {@link JsonObject}, a {@link + * JsonArray}, a {@link JsonPrimitive} or a {@link JsonNull}. * * @author Inderjeet Singh * @author Joel Leitch */ public abstract class JsonElement { /** - * @deprecated Creating custom {@code JsonElement} subclasses is highly discouraged - * and can lead to undefined behavior.
- * This constructor is only kept for backward compatibility. + * @deprecated Creating custom {@code JsonElement} subclasses is highly discouraged and can lead + * to undefined behavior.
+ * This constructor is only kept for backward compatibility. */ @Deprecated - public JsonElement() { - } + public JsonElement() {} /** - * Returns a deep copy of this element. Immutable elements like primitives - * and nulls are not copied. + * Returns a deep copy of this element. Immutable elements like primitives and nulls are not + * copied. * * @since 2.8.2 */ @@ -103,10 +102,9 @@ public JsonObject getAsJsonObject() { } /** - * Convenience method to get this element as a {@link JsonArray}. If this element is of some - * other type, an {@link IllegalStateException} will result. Hence it is best to use this method - * after ensuring that this element is of the desired type by calling {@link #isJsonArray()} - * first. + * Convenience method to get this element as a {@link JsonArray}. If this element is of some other + * type, an {@link IllegalStateException} will result. Hence it is best to use this method after + * ensuring that this element is of the desired type by calling {@link #isJsonArray()} first. * * @return this element as a {@link JsonArray}. * @throws IllegalStateException if this element is of another type. @@ -135,10 +133,9 @@ public JsonPrimitive getAsJsonPrimitive() { } /** - * Convenience method to get this element as a {@link JsonNull}. If this element is of some - * other type, an {@link IllegalStateException} will result. Hence it is best to use this method - * after ensuring that this element is of the desired type by calling {@link #isJsonNull()} - * first. + * Convenience method to get this element as a {@link JsonNull}. If this element is of some other + * type, an {@link IllegalStateException} will result. Hence it is best to use this method after + * ensuring that this element is of the desired type by calling {@link #isJsonNull()} first. * * @return this element as a {@link JsonNull}. * @throws IllegalStateException if this element is of another type. @@ -156,9 +153,10 @@ public JsonNull getAsJsonNull() { * Convenience method to get this element as a boolean value. * * @return this element as a primitive boolean value. - * @throws UnsupportedOperationException if this element is not a {@link JsonPrimitive} or {@link JsonArray}. + * @throws UnsupportedOperationException if this element is not a {@link JsonPrimitive} or {@link + * JsonArray}. * @throws IllegalStateException if this element is of the type {@link JsonArray} but contains - * more than a single element. + * more than a single element. */ public boolean getAsBoolean() { throw new UnsupportedOperationException(getClass().getSimpleName()); @@ -168,10 +166,10 @@ public boolean getAsBoolean() { * Convenience method to get this element as a {@link Number}. * * @return this element as a {@link Number}. - * @throws UnsupportedOperationException if this element is not a {@link JsonPrimitive} or {@link JsonArray}, - * or cannot be converted to a number. + * @throws UnsupportedOperationException if this element is not a {@link JsonPrimitive} or {@link + * JsonArray}, or cannot be converted to a number. * @throws IllegalStateException if this element is of the type {@link JsonArray} but contains - * more than a single element. + * more than a single element. */ public Number getAsNumber() { throw new UnsupportedOperationException(getClass().getSimpleName()); @@ -181,9 +179,10 @@ public Number getAsNumber() { * Convenience method to get this element as a string value. * * @return this element as a string value. - * @throws UnsupportedOperationException if this element is not a {@link JsonPrimitive} or {@link JsonArray}. + * @throws UnsupportedOperationException if this element is not a {@link JsonPrimitive} or {@link + * JsonArray}. * @throws IllegalStateException if this element is of the type {@link JsonArray} but contains - * more than a single element. + * more than a single element. */ public String getAsString() { throw new UnsupportedOperationException(getClass().getSimpleName()); @@ -193,10 +192,11 @@ public String getAsString() { * Convenience method to get this element as a primitive double value. * * @return this element as a primitive double value. - * @throws UnsupportedOperationException if this element is not a {@link JsonPrimitive} or {@link JsonArray}. + * @throws UnsupportedOperationException if this element is not a {@link JsonPrimitive} or {@link + * JsonArray}. * @throws NumberFormatException if the value contained is not a valid double. * @throws IllegalStateException if this element is of the type {@link JsonArray} but contains - * more than a single element. + * more than a single element. */ public double getAsDouble() { throw new UnsupportedOperationException(getClass().getSimpleName()); @@ -206,10 +206,11 @@ public double getAsDouble() { * Convenience method to get this element as a primitive float value. * * @return this element as a primitive float value. - * @throws UnsupportedOperationException if this element is not a {@link JsonPrimitive} or {@link JsonArray}. + * @throws UnsupportedOperationException if this element is not a {@link JsonPrimitive} or {@link + * JsonArray}. * @throws NumberFormatException if the value contained is not a valid float. * @throws IllegalStateException if this element is of the type {@link JsonArray} but contains - * more than a single element. + * more than a single element. */ public float getAsFloat() { throw new UnsupportedOperationException(getClass().getSimpleName()); @@ -219,10 +220,11 @@ public float getAsFloat() { * Convenience method to get this element as a primitive long value. * * @return this element as a primitive long value. - * @throws UnsupportedOperationException if this element is not a {@link JsonPrimitive} or {@link JsonArray}. + * @throws UnsupportedOperationException if this element is not a {@link JsonPrimitive} or {@link + * JsonArray}. * @throws NumberFormatException if the value contained is not a valid long. * @throws IllegalStateException if this element is of the type {@link JsonArray} but contains - * more than a single element. + * more than a single element. */ public long getAsLong() { throw new UnsupportedOperationException(getClass().getSimpleName()); @@ -232,10 +234,11 @@ public long getAsLong() { * Convenience method to get this element as a primitive integer value. * * @return this element as a primitive integer value. - * @throws UnsupportedOperationException if this element is not a {@link JsonPrimitive} or {@link JsonArray}. + * @throws UnsupportedOperationException if this element is not a {@link JsonPrimitive} or {@link + * JsonArray}. * @throws NumberFormatException if the value contained is not a valid integer. * @throws IllegalStateException if this element is of the type {@link JsonArray} but contains - * more than a single element. + * more than a single element. */ public int getAsInt() { throw new UnsupportedOperationException(getClass().getSimpleName()); @@ -245,10 +248,11 @@ public int getAsInt() { * Convenience method to get this element as a primitive byte value. * * @return this element as a primitive byte value. - * @throws UnsupportedOperationException if this element is not a {@link JsonPrimitive} or {@link JsonArray}. + * @throws UnsupportedOperationException if this element is not a {@link JsonPrimitive} or {@link + * JsonArray}. * @throws NumberFormatException if the value contained is not a valid byte. * @throws IllegalStateException if this element is of the type {@link JsonArray} but contains - * more than a single element. + * more than a single element. * @since 1.3 */ public byte getAsByte() { @@ -259,13 +263,13 @@ public byte getAsByte() { * Convenience method to get the first character of the string value of this element. * * @return the first character of the string value. - * @throws UnsupportedOperationException if this element is not a {@link JsonPrimitive} or {@link JsonArray}, - * or if its string value is empty. + * @throws UnsupportedOperationException if this element is not a {@link JsonPrimitive} or {@link + * JsonArray}, or if its string value is empty. * @throws IllegalStateException if this element is of the type {@link JsonArray} but contains - * more than a single element. + * more than a single element. * @since 1.3 * @deprecated This method is misleading, as it does not get this element as a char but rather as - * a string's first character. + * a string's first character. */ @Deprecated public char getAsCharacter() { @@ -276,10 +280,11 @@ public char getAsCharacter() { * Convenience method to get this element as a {@link BigDecimal}. * * @return this element as a {@link BigDecimal}. - * @throws UnsupportedOperationException if this element is not a {@link JsonPrimitive} or {@link JsonArray}. + * @throws UnsupportedOperationException if this element is not a {@link JsonPrimitive} or {@link + * JsonArray}. * @throws NumberFormatException if this element is not a valid {@link BigDecimal}. * @throws IllegalStateException if this element is of the type {@link JsonArray} but contains - * more than a single element. + * more than a single element. * @since 1.2 */ public BigDecimal getAsBigDecimal() { @@ -290,10 +295,11 @@ public BigDecimal getAsBigDecimal() { * Convenience method to get this element as a {@link BigInteger}. * * @return this element as a {@link BigInteger}. - * @throws UnsupportedOperationException if this element is not a {@link JsonPrimitive} or {@link JsonArray}. + * @throws UnsupportedOperationException if this element is not a {@link JsonPrimitive} or {@link + * JsonArray}. * @throws NumberFormatException if this element is not a valid {@link BigInteger}. * @throws IllegalStateException if this element is of the type {@link JsonArray} but contains - * more than a single element. + * more than a single element. * @since 1.2 */ public BigInteger getAsBigInteger() { @@ -304,24 +310,24 @@ public BigInteger getAsBigInteger() { * Convenience method to get this element as a primitive short value. * * @return this element as a primitive short value. - * @throws UnsupportedOperationException if this element is not a {@link JsonPrimitive} or {@link JsonArray}. + * @throws UnsupportedOperationException if this element is not a {@link JsonPrimitive} or {@link + * JsonArray}. * @throws NumberFormatException if the value contained is not a valid short. * @throws IllegalStateException if this element is of the type {@link JsonArray} but contains - * more than a single element. + * more than a single element. */ public short getAsShort() { throw new UnsupportedOperationException(getClass().getSimpleName()); } - /** - * Returns a String representation of this element. - */ + /** Returns a String representation of this element. */ @Override public String toString() { try { StringWriter stringWriter = new StringWriter(); JsonWriter jsonWriter = new JsonWriter(stringWriter); - // Make writer lenient because toString() must not fail, even if for example JsonPrimitive contains NaN + // Make writer lenient because toString() must not fail, even if for example JsonPrimitive + // contains NaN jsonWriter.setStrictness(Strictness.LENIENT); Streams.write(this, jsonWriter); return stringWriter.toString(); diff --git a/gson/src/main/java/com/google/gson/JsonIOException.java b/gson/src/main/java/com/google/gson/JsonIOException.java index dfeccd8ede..45f453b288 100644 --- a/gson/src/main/java/com/google/gson/JsonIOException.java +++ b/gson/src/main/java/com/google/gson/JsonIOException.java @@ -16,9 +16,8 @@ package com.google.gson; /** - * This exception is raised when Gson was unable to read an input stream - * or write to one. - * + * This exception is raised when Gson was unable to read an input stream or write to one. + * * @author Inderjeet Singh * @author Joel Leitch */ @@ -34,8 +33,8 @@ public JsonIOException(String msg, Throwable cause) { } /** - * Creates exception with the specified cause. Consider using - * {@link #JsonIOException(String, Throwable)} instead if you can describe what happened. + * Creates exception with the specified cause. Consider using {@link #JsonIOException(String, + * Throwable)} instead if you can describe what happened. * * @param cause root exception that caused this exception to be thrown. */ diff --git a/gson/src/main/java/com/google/gson/JsonNull.java b/gson/src/main/java/com/google/gson/JsonNull.java index 1a4c136c05..7109fbfa7e 100644 --- a/gson/src/main/java/com/google/gson/JsonNull.java +++ b/gson/src/main/java/com/google/gson/JsonNull.java @@ -51,17 +51,13 @@ public JsonNull deepCopy() { return INSTANCE; } - /** - * All instances of {@code JsonNull} have the same hash code since they are indistinguishable. - */ + /** All instances of {@code JsonNull} have the same hash code since they are indistinguishable. */ @Override public int hashCode() { return JsonNull.class.hashCode(); } - /** - * All instances of {@code JsonNull} are considered equal. - */ + /** All instances of {@code JsonNull} are considered equal. */ @Override public boolean equals(Object other) { return other instanceof JsonNull; diff --git a/gson/src/main/java/com/google/gson/JsonObject.java b/gson/src/main/java/com/google/gson/JsonObject.java index a4b3c87644..d13be19dce 100644 --- a/gson/src/main/java/com/google/gson/JsonObject.java +++ b/gson/src/main/java/com/google/gson/JsonObject.java @@ -25,11 +25,11 @@ * A class representing an object type in Json. An object consists of name-value pairs where names * are strings, and values are any other type of {@link JsonElement}. This allows for a creating a * tree of JsonElements. The member elements of this object are maintained in order they were added. - * This class does not support {@code null} values. If {@code null} is provided as value argument - * to any of the methods, it is converted to a {@link JsonNull}. + * This class does not support {@code null} values. If {@code null} is provided as value argument to + * any of the methods, it is converted to a {@link JsonNull}. * - *

{@code JsonObject} does not implement the {@link Map} interface, but a {@code Map} view - * of it can be obtained with {@link #asMap()}. + *

{@code JsonObject} does not implement the {@link Map} interface, but a {@code Map} view of it + * can be obtained with {@link #asMap()}. * * @author Inderjeet Singh * @author Joel Leitch @@ -37,12 +37,9 @@ public final class JsonObject extends JsonElement { private final LinkedTreeMap members = new LinkedTreeMap<>(false); - /** - * Creates an empty JsonObject. - */ + /** Creates an empty JsonObject. */ @SuppressWarnings("deprecation") // superclass constructor - public JsonObject() { - } + public JsonObject() {} /** * Creates a deep copy of this element and all its children. @@ -60,8 +57,8 @@ public JsonObject deepCopy() { /** * Adds a member, which is a name-value pair, to self. The name must be a String, but the value - * can be an arbitrary {@link JsonElement}, thereby allowing you to build a full tree of JsonElements - * rooted at this node. + * can be an arbitrary {@link JsonElement}, thereby allowing you to build a full tree of + * JsonElements rooted at this node. * * @param property name of the member. * @param value the member object. @@ -74,8 +71,8 @@ public void add(String property, JsonElement value) { * Removes the {@code property} from this object. * * @param property name of the member that should be removed. - * @return the {@link JsonElement} object that is being removed, or {@code null} if no - * member with this name exists. + * @return the {@link JsonElement} object that is being removed, or {@code null} if no member with + * this name exists. * @since 1.3 */ @CanIgnoreReturnValue @@ -84,8 +81,8 @@ public JsonElement remove(String property) { } /** - * Convenience method to add a string member. The specified value is converted to a - * {@link JsonPrimitive} of String. + * Convenience method to add a string member. The specified value is converted to a {@link + * JsonPrimitive} of String. * * @param property name of the member. * @param value the string value associated with the member. @@ -95,8 +92,8 @@ public void addProperty(String property, String value) { } /** - * Convenience method to add a number member. The specified value is converted to a - * {@link JsonPrimitive} of Number. + * Convenience method to add a number member. The specified value is converted to a {@link + * JsonPrimitive} of Number. * * @param property name of the member. * @param value the number value associated with the member. @@ -106,8 +103,8 @@ public void addProperty(String property, Number value) { } /** - * Convenience method to add a boolean member. The specified value is converted to a - * {@link JsonPrimitive} of Boolean. + * Convenience method to add a boolean member. The specified value is converted to a {@link + * JsonPrimitive} of Boolean. * * @param property name of the member. * @param value the boolean value associated with the member. @@ -117,8 +114,8 @@ public void addProperty(String property, Boolean value) { } /** - * Convenience method to add a char member. The specified value is converted to a - * {@link JsonPrimitive} of Character. + * Convenience method to add a char member. The specified value is converted to a {@link + * JsonPrimitive} of Character. * * @param property name of the member. * @param value the char value associated with the member. @@ -192,7 +189,7 @@ public JsonElement get(String memberName) { * * @param memberName name of the member being requested. * @return the {@code JsonPrimitive} corresponding to the specified member, or {@code null} if no - * member with this name exists. + * member with this name exists. * @throws ClassCastException if the member is not of type {@code JsonPrimitive}. */ public JsonPrimitive getAsJsonPrimitive(String memberName) { @@ -204,7 +201,7 @@ public JsonPrimitive getAsJsonPrimitive(String memberName) { * * @param memberName name of the member being requested. * @return the {@code JsonArray} corresponding to the specified member, or {@code null} if no - * member with this name exists. + * member with this name exists. * @throws ClassCastException if the member is not of type {@code JsonArray}. */ public JsonArray getAsJsonArray(String memberName) { @@ -216,7 +213,7 @@ public JsonArray getAsJsonArray(String memberName) { * * @param memberName name of the member being requested. * @return the {@code JsonObject} corresponding to the specified member, or {@code null} if no - * member with this name exists. + * member with this name exists. * @throws ClassCastException if the member is not of type {@code JsonObject}. */ public JsonObject getAsJsonObject(String memberName) { @@ -224,12 +221,12 @@ public JsonObject getAsJsonObject(String memberName) { } /** - * Returns a mutable {@link Map} view of this {@code JsonObject}. Changes to the {@code Map} - * are visible in this {@code JsonObject} and the other way around. + * Returns a mutable {@link Map} view of this {@code JsonObject}. Changes to the {@code Map} are + * visible in this {@code JsonObject} and the other way around. * *

The {@code Map} does not permit {@code null} keys or values. Unlike {@code JsonObject}'s - * {@code null} handling, a {@link NullPointerException} is thrown when trying to add {@code null}. - * Use {@link JsonNull} for JSON null values. + * {@code null} handling, a {@link NullPointerException} is thrown when trying to add {@code + * null}. Use {@link JsonNull} for JSON null values. * * @return mutable {@code Map} view * @since 2.10 @@ -240,19 +237,17 @@ public Map asMap() { } /** - * Returns whether the other object is equal to this. This method only considers - * the other object to be equal if it is an instance of {@code JsonObject} and has - * equal members, ignoring order. + * Returns whether the other object is equal to this. This method only considers the other object + * to be equal if it is an instance of {@code JsonObject} and has equal members, ignoring order. */ @Override public boolean equals(Object o) { - return (o == this) || (o instanceof JsonObject - && ((JsonObject) o).members.equals(members)); + return (o == this) || (o instanceof JsonObject && ((JsonObject) o).members.equals(members)); } /** - * Returns the hash code of this object. This method calculates the hash code based - * on the members of this object, ignoring order. + * Returns the hash code of this object. This method calculates the hash code based on the members + * of this object, ignoring order. */ @Override public int hashCode() { diff --git a/gson/src/main/java/com/google/gson/JsonParseException.java b/gson/src/main/java/com/google/gson/JsonParseException.java index c1f264d76c..1e58f0bb72 100644 --- a/gson/src/main/java/com/google/gson/JsonParseException.java +++ b/gson/src/main/java/com/google/gson/JsonParseException.java @@ -17,14 +17,14 @@ package com.google.gson; /** - * This exception is raised if there is a serious issue that occurs during parsing of a Json - * string. One of the main usages for this class is for the Gson infrastructure. If the incoming - * Json is bad/malicious, an instance of this exception is raised. + * This exception is raised if there is a serious issue that occurs during parsing of a Json string. + * One of the main usages for this class is for the Gson infrastructure. If the incoming Json is + * bad/malicious, an instance of this exception is raised. * *

This exception is a {@link RuntimeException} because it is exposed to the client. Using a * {@link RuntimeException} avoids bad coding practices on the client side where they catch the * exception and do nothing. It is often the case that you want to blow up if there is a parsing - * error (i.e. often clients do not know how to recover from a {@link JsonParseException}.

+ * error (i.e. often clients do not know how to recover from a {@link JsonParseException}. * * @author Inderjeet Singh * @author Joel Leitch @@ -53,8 +53,8 @@ public JsonParseException(String msg, Throwable cause) { } /** - * Creates exception with the specified cause. Consider using - * {@link #JsonParseException(String, Throwable)} instead if you can describe what happened. + * Creates exception with the specified cause. Consider using {@link #JsonParseException(String, + * Throwable)} instead if you can describe what happened. * * @param cause root exception that caused this exception to be thrown. */ diff --git a/gson/src/main/java/com/google/gson/JsonParser.java b/gson/src/main/java/com/google/gson/JsonParser.java index 557d00c8e0..ee98777533 100644 --- a/gson/src/main/java/com/google/gson/JsonParser.java +++ b/gson/src/main/java/com/google/gson/JsonParser.java @@ -32,14 +32,15 @@ * @since 1.3 */ public final class JsonParser { - /** @deprecated No need to instantiate this class, use the static methods instead. */ + /** + * @deprecated No need to instantiate this class, use the static methods instead. + */ @Deprecated public JsonParser() {} /** - * Parses the specified JSON string into a parse tree. - * An exception is thrown if the JSON string has multiple top-level JSON elements, - * or if there is trailing data. + * Parses the specified JSON string into a parse tree. An exception is thrown if the JSON string + * has multiple top-level JSON elements, or if there is trailing data. * *

The JSON string is parsed in {@linkplain JsonReader#setStrictness(Strictness) lenient mode}. * @@ -53,16 +54,15 @@ public static JsonElement parseString(String json) throws JsonSyntaxException { } /** - * Parses the complete JSON string provided by the reader into a parse tree. - * An exception is thrown if the JSON string has multiple top-level JSON elements, - * or if there is trailing data. + * Parses the complete JSON string provided by the reader into a parse tree. An exception is + * thrown if the JSON string has multiple top-level JSON elements, or if there is trailing data. * *

The JSON data is parsed in {@linkplain JsonReader#setStrictness(Strictness) lenient mode}. * * @param reader JSON text * @return a parse tree of {@link JsonElement}s corresponding to the specified JSON - * @throws JsonParseException if there is an IOException or if the specified - * text is not valid JSON + * @throws JsonParseException if there is an IOException or if the specified text is not valid + * JSON * @since 2.8.6 */ public static JsonElement parseReader(Reader reader) throws JsonIOException, JsonSyntaxException { @@ -83,16 +83,16 @@ public static JsonElement parseReader(Reader reader) throws JsonIOException, Jso } /** - * Returns the next value from the JSON stream as a parse tree. - * Unlike the other {@code parse} methods, no exception is thrown if the JSON data has - * multiple top-level JSON elements, or if there is trailing data. + * Returns the next value from the JSON stream as a parse tree. Unlike the other {@code parse} + * methods, no exception is thrown if the JSON data has multiple top-level JSON elements, or if + * there is trailing data. * *

The JSON data is parsed in {@linkplain JsonReader#setStrictness(Strictness) lenient mode}, - * regardless of the strictness setting of the provided reader. The strictness setting - * of the reader is restored once this method returns. + * regardless of the strictness setting of the provided reader. The strictness setting of the + * reader is restored once this method returns. * - * @throws JsonParseException if there is an IOException or if the specified - * text is not valid JSON + * @throws JsonParseException if there is an IOException or if the specified text is not valid + * JSON * @since 2.8.6 */ public static JsonElement parseReader(JsonReader reader) @@ -110,21 +110,27 @@ public static JsonElement parseReader(JsonReader reader) } } - /** @deprecated Use {@link JsonParser#parseString} */ + /** + * @deprecated Use {@link JsonParser#parseString} + */ @Deprecated @InlineMe(replacement = "JsonParser.parseString(json)", imports = "com.google.gson.JsonParser") public JsonElement parse(String json) throws JsonSyntaxException { return parseString(json); } - /** @deprecated Use {@link JsonParser#parseReader(Reader)} */ + /** + * @deprecated Use {@link JsonParser#parseReader(Reader)} + */ @Deprecated @InlineMe(replacement = "JsonParser.parseReader(json)", imports = "com.google.gson.JsonParser") public JsonElement parse(Reader json) throws JsonIOException, JsonSyntaxException { return parseReader(json); } - /** @deprecated Use {@link JsonParser#parseReader(JsonReader)} */ + /** + * @deprecated Use {@link JsonParser#parseReader(JsonReader)} + */ @Deprecated @InlineMe(replacement = "JsonParser.parseReader(json)", imports = "com.google.gson.JsonParser") public JsonElement parse(JsonReader json) throws JsonIOException, JsonSyntaxException { diff --git a/gson/src/main/java/com/google/gson/JsonPrimitive.java b/gson/src/main/java/com/google/gson/JsonPrimitive.java index 190439617b..00530d5691 100644 --- a/gson/src/main/java/com/google/gson/JsonPrimitive.java +++ b/gson/src/main/java/com/google/gson/JsonPrimitive.java @@ -23,9 +23,8 @@ import java.util.Objects; /** - * A class representing a JSON primitive value. A primitive value - * is either a String, a Java primitive, or a Java primitive - * wrapper type. + * A class representing a JSON primitive value. A primitive value is either a String, a Java + * primitive, or a Java primitive wrapper type. * * @author Inderjeet Singh * @author Joel Leitch @@ -97,10 +96,10 @@ public boolean isBoolean() { } /** - * Convenience method to get this element as a boolean value. - * If this primitive {@linkplain #isBoolean() is not a boolean}, the string value - * is parsed using {@link Boolean#parseBoolean(String)}. This means {@code "true"} (ignoring - * case) is considered {@code true} and any other value is considered {@code false}. + * Convenience method to get this element as a boolean value. If this primitive {@linkplain + * #isBoolean() is not a boolean}, the string value is parsed using {@link + * Boolean#parseBoolean(String)}. This means {@code "true"} (ignoring case) is considered {@code + * true} and any other value is considered {@code false}. */ @Override public boolean getAsBoolean() { @@ -121,10 +120,9 @@ public boolean isNumber() { } /** - * Convenience method to get this element as a {@link Number}. - * If this primitive {@linkplain #isString() is a string}, a lazily parsed {@code Number} - * is constructed which parses the string when any of its methods are called (which can - * lead to a {@link NumberFormatException}). + * Convenience method to get this element as a {@link Number}. If this primitive {@linkplain + * #isString() is a string}, a lazily parsed {@code Number} is constructed which parses the string + * when any of its methods are called (which can lead to a {@link NumberFormatException}). * * @throws UnsupportedOperationException if this primitive is neither a number nor a string. */ @@ -173,7 +171,9 @@ public double getAsDouble() { */ @Override public BigDecimal getAsBigDecimal() { - return value instanceof BigDecimal ? (BigDecimal) value : NumberLimits.parseBigDecimal(getAsString()); + return value instanceof BigDecimal + ? (BigDecimal) value + : NumberLimits.parseBigDecimal(getAsString()); } /** @@ -215,9 +215,9 @@ public short getAsShort() { return isNumber() ? getAsNumber().shortValue() : Short.parseShort(getAsString()); } - /** - * @throws NumberFormatException {@inheritDoc} - */ + /** + * @throws NumberFormatException {@inheritDoc} + */ @Override public int getAsInt() { return isNumber() ? getAsNumber().intValue() : Integer.parseInt(getAsString()); @@ -232,10 +232,9 @@ public byte getAsByte() { } /** - * @throws UnsupportedOperationException if the string value of this - * primitive is empty. + * @throws UnsupportedOperationException if the string value of this primitive is empty. * @deprecated This method is misleading, as it does not get this element as a char but rather as - * a string's first character. + * a string's first character. */ @Deprecated @Override @@ -248,9 +247,7 @@ public char getAsCharacter() { } } - /** - * Returns the hash code of this object. - */ + /** Returns the hash code of this object. */ @Override public int hashCode() { if (value == null) { @@ -269,9 +266,8 @@ public int hashCode() { } /** - * Returns whether the other object is equal to this. This method only considers - * the other object to be equal if it is an instance of {@code JsonPrimitive} and - * has an equal value. + * Returns whether the other object is equal to this. This method only considers the other object + * to be equal if it is an instance of {@code JsonPrimitive} and has an equal value. */ @Override public boolean equals(Object obj) { @@ -281,7 +277,7 @@ public boolean equals(Object obj) { if (obj == null || getClass() != obj.getClass()) { return false; } - JsonPrimitive other = (JsonPrimitive)obj; + JsonPrimitive other = (JsonPrimitive) obj; if (value == null) { return other.value == null; } @@ -301,14 +297,17 @@ public boolean equals(Object obj) { } /** - * Returns true if the specified number is an integral type - * (Long, Integer, Short, Byte, BigInteger) + * Returns true if the specified number is an integral type (Long, Integer, Short, Byte, + * BigInteger) */ private static boolean isIntegral(JsonPrimitive primitive) { if (primitive.value instanceof Number) { Number number = (Number) primitive.value; - return number instanceof BigInteger || number instanceof Long || number instanceof Integer - || number instanceof Short || number instanceof Byte; + return number instanceof BigInteger + || number instanceof Long + || number instanceof Integer + || number instanceof Short + || number instanceof Byte; } return false; } diff --git a/gson/src/main/java/com/google/gson/JsonSerializationContext.java b/gson/src/main/java/com/google/gson/JsonSerializationContext.java index ca3ec4f901..b5c05b07b3 100644 --- a/gson/src/main/java/com/google/gson/JsonSerializationContext.java +++ b/gson/src/main/java/com/google/gson/JsonSerializationContext.java @@ -19,8 +19,8 @@ import java.lang.reflect.Type; /** - * Context for serialization that is passed to a custom serializer during invocation of its - * {@link JsonSerializer#serialize(Object, Type, JsonSerializationContext)} method. + * Context for serialization that is passed to a custom serializer during invocation of its {@link + * JsonSerializer#serialize(Object, Type, JsonSerializationContext)} method. * * @author Inderjeet Singh * @author Joel Leitch @@ -36,10 +36,10 @@ public interface JsonSerializationContext { public JsonElement serialize(Object src); /** - * Invokes default serialization on the specified object passing the specific type information. - * It should never be invoked on the element received as a parameter of the - * {@link JsonSerializer#serialize(Object, Type, JsonSerializationContext)} method. Doing - * so will result in an infinite loop since Gson will in-turn call the custom serializer again. + * Invokes default serialization on the specified object passing the specific type information. It + * should never be invoked on the element received as a parameter of the {@link + * JsonSerializer#serialize(Object, Type, JsonSerializationContext)} method. Doing so will result + * in an infinite loop since Gson will in-turn call the custom serializer again. * * @param src the object that needs to be serialized. * @param typeOfSrc the actual genericized type of src object. diff --git a/gson/src/main/java/com/google/gson/JsonSerializer.java b/gson/src/main/java/com/google/gson/JsonSerializer.java index 01d62723b8..550fd28505 100644 --- a/gson/src/main/java/com/google/gson/JsonSerializer.java +++ b/gson/src/main/java/com/google/gson/JsonSerializer.java @@ -19,12 +19,12 @@ import java.lang.reflect.Type; /** - * Interface representing a custom serializer for JSON. You should write a custom serializer, if - * you are not happy with the default serialization done by Gson. You will also need to register - * this serializer through {@link com.google.gson.GsonBuilder#registerTypeAdapter(Type, Object)}. + * Interface representing a custom serializer for JSON. You should write a custom serializer, if you + * are not happy with the default serialization done by Gson. You will also need to register this + * serializer through {@link com.google.gson.GsonBuilder#registerTypeAdapter(Type, Object)}. * *

Let us look at example where defining a serializer will be useful. The {@code Id} class - * defined below has two fields: {@code clazz} and {@code value}.

+ * defined below has two fields: {@code clazz} and {@code value}. * *
  * public class Id<T> {
@@ -42,10 +42,9 @@
  * }
  * 
* - *

The default serialization of {@code Id(com.foo.MyObject.class, 20L)} will be - * {"clazz":"com.foo.MyObject","value":20}. Suppose, you just want the output to be - * the value instead, which is {@code 20} in this case. You can achieve that by writing a custom - * serializer:

+ *

The default serialization of {@code Id(com.foo.MyObject.class, 20L)} will be + * {"clazz":"com.foo.MyObject","value":20}. Suppose, you just want the output to be the value + * instead, which is {@code 20} in this case. You can achieve that by writing a custom serializer: * *

  * class IdSerializer implements JsonSerializer<Id> {
@@ -55,22 +54,22 @@
  * }
  * 
* - *

You will also need to register {@code IdSerializer} with Gson as follows:

+ *

You will also need to register {@code IdSerializer} with Gson as follows: + * *

  * Gson gson = new GsonBuilder().registerTypeAdapter(Id.class, new IdSerializer()).create();
  * 
* - *

Serializers should be stateless and thread-safe, otherwise the thread-safety - * guarantees of {@link Gson} might not apply. + *

Serializers should be stateless and thread-safe, otherwise the thread-safety guarantees of + * {@link Gson} might not apply. * - *

New applications should prefer {@link TypeAdapter}, whose streaming API - * is more efficient than this interface's tree API. + *

New applications should prefer {@link TypeAdapter}, whose streaming API is more efficient than + * this interface's tree API. * * @author Inderjeet Singh * @author Joel Leitch - * * @param type for which the serializer is being registered. It is possible that a serializer - * may be asked to serialize a specific generic type of the T. + * may be asked to serialize a specific generic type of the T. */ public interface JsonSerializer { @@ -78,11 +77,11 @@ public interface JsonSerializer { * Gson invokes this call-back method during serialization when it encounters a field of the * specified type. * - *

In the implementation of this call-back method, you should consider invoking - * {@link JsonSerializationContext#serialize(Object, Type)} method to create JsonElements for any - * non-trivial field of the {@code src} object. However, you should never invoke it on the - * {@code src} object itself since that will cause an infinite loop (Gson will call your - * call-back method again).

+ *

In the implementation of this call-back method, you should consider invoking {@link + * JsonSerializationContext#serialize(Object, Type)} method to create JsonElements for any + * non-trivial field of the {@code src} object. However, you should never invoke it on the {@code + * src} object itself since that will cause an infinite loop (Gson will call your call-back method + * again). * * @param src the object that needs to be converted to Json. * @param typeOfSrc the actual type (fully genericized version) of the source object. diff --git a/gson/src/main/java/com/google/gson/JsonStreamParser.java b/gson/src/main/java/com/google/gson/JsonStreamParser.java index 7d2629368b..058d10da29 100644 --- a/gson/src/main/java/com/google/gson/JsonStreamParser.java +++ b/gson/src/main/java/com/google/gson/JsonStreamParser.java @@ -27,8 +27,8 @@ /** * A streaming parser that allows reading of multiple {@link JsonElement}s from the specified reader - * asynchronously. The JSON data is parsed in lenient mode, see also - * {@link JsonReader#setStrictness(Strictness)}. + * asynchronously. The JSON data is parsed in lenient mode, see also {@link + * JsonReader#setStrictness(Strictness)}. * *

This class is conditionally thread-safe (see Item 70, Effective Java second edition). To * properly use this class across multiple threads, you will need to add some external @@ -71,8 +71,8 @@ public JsonStreamParser(Reader reader) { } /** - * Returns the next available {@link JsonElement} on the reader. Throws a - * {@link NoSuchElementException} if no element is available. + * Returns the next available {@link JsonElement} on the reader. Throws a {@link + * NoSuchElementException} if no element is available. * * @return the next available {@code JsonElement} on the reader. * @throws JsonParseException if the incoming stream is malformed JSON. @@ -96,6 +96,7 @@ public JsonElement next() throws JsonParseException { /** * Returns true if a {@link JsonElement} is available on the input for consumption + * * @return true if a {@link JsonElement} is available on the input, false otherwise * @throws JsonParseException if the incoming stream is malformed JSON. * @since 1.4 @@ -116,6 +117,7 @@ public boolean hasNext() { /** * This optional {@link Iterator} method is not relevant for stream parsing and hence is not * implemented. + * * @since 1.4 */ @Override diff --git a/gson/src/main/java/com/google/gson/JsonSyntaxException.java b/gson/src/main/java/com/google/gson/JsonSyntaxException.java index 17c1d3d3a6..7bafe26676 100644 --- a/gson/src/main/java/com/google/gson/JsonSyntaxException.java +++ b/gson/src/main/java/com/google/gson/JsonSyntaxException.java @@ -16,8 +16,7 @@ package com.google.gson; /** - * This exception is raised when Gson attempts to read (or write) a malformed - * JSON element. + * This exception is raised when Gson attempts to read (or write) a malformed JSON element. * * @author Inderjeet Singh * @author Joel Leitch @@ -35,9 +34,8 @@ public JsonSyntaxException(String msg, Throwable cause) { } /** - * Creates exception with the specified cause. Consider using - * {@link #JsonSyntaxException(String, Throwable)} instead if you can - * describe what actually happened. + * Creates exception with the specified cause. Consider using {@link #JsonSyntaxException(String, + * Throwable)} instead if you can describe what actually happened. * * @param cause root exception that caused this exception to be thrown. */ diff --git a/gson/src/main/java/com/google/gson/LongSerializationPolicy.java b/gson/src/main/java/com/google/gson/LongSerializationPolicy.java index 3cc9c7241a..df7c8fa167 100644 --- a/gson/src/main/java/com/google/gson/LongSerializationPolicy.java +++ b/gson/src/main/java/com/google/gson/LongSerializationPolicy.java @@ -20,7 +20,6 @@ * Defines the expected format for a {@code long} or {@code Long} type when it is serialized. * * @since 1.3 - * * @author Inderjeet Singh * @author Joel Leitch */ @@ -28,36 +27,36 @@ public enum LongSerializationPolicy { /** * This is the "default" serialization policy that will output a {@code Long} object as a JSON * number. For example, assume an object has a long field named "f" then the serialized output - * would be: - * {@code {"f":123}} + * would be: {@code {"f":123}} * *

A {@code null} value is serialized as {@link JsonNull}. */ DEFAULT() { - @Override public JsonElement serialize(Long value) { + @Override + public JsonElement serialize(Long value) { if (value == null) { return JsonNull.INSTANCE; } return new JsonPrimitive(value); } }, - + /** - * Serializes a long value as a quoted string. For example, assume an object has a long field - * named "f" then the serialized output would be: - * {@code {"f":"123"}} + * Serializes a long value as a quoted string. For example, assume an object has a long field + * named "f" then the serialized output would be: {@code {"f":"123"}} * *

A {@code null} value is serialized as {@link JsonNull}. */ STRING() { - @Override public JsonElement serialize(Long value) { + @Override + public JsonElement serialize(Long value) { if (value == null) { return JsonNull.INSTANCE; } return new JsonPrimitive(value.toString()); } }; - + /** * Serialize this {@code value} using this serialization policy. * diff --git a/gson/src/main/java/com/google/gson/ReflectionAccessFilter.java b/gson/src/main/java/com/google/gson/ReflectionAccessFilter.java index 7736ec7aa0..44e3df09df 100644 --- a/gson/src/main/java/com/google/gson/ReflectionAccessFilter.java +++ b/gson/src/main/java/com/google/gson/ReflectionAccessFilter.java @@ -20,27 +20,24 @@ import java.lang.reflect.AccessibleObject; /** - * Filter for determining whether reflection based serialization and - * deserialization is allowed for a class. + * Filter for determining whether reflection based serialization and deserialization is allowed for + * a class. * - *

A filter can be useful in multiple scenarios, for example when - * upgrading to newer Java versions which use the Java Platform Module - * System (JPMS). A filter then allows to {@linkplain FilterResult#BLOCK_INACCESSIBLE - * prevent making inaccessible members accessible}, even if the used - * Java version might still allow illegal access (but logs a warning), - * or if {@code java} command line arguments are used to open the inaccessible - * packages to other parts of the application. This interface defines some - * convenience filters for this task, such as {@link #BLOCK_INACCESSIBLE_JAVA}. + *

A filter can be useful in multiple scenarios, for example when upgrading to newer Java + * versions which use the Java Platform Module System (JPMS). A filter then allows to {@linkplain + * FilterResult#BLOCK_INACCESSIBLE prevent making inaccessible members accessible}, even if the used + * Java version might still allow illegal access (but logs a warning), or if {@code java} command + * line arguments are used to open the inaccessible packages to other parts of the application. This + * interface defines some convenience filters for this task, such as {@link + * #BLOCK_INACCESSIBLE_JAVA}. * - *

A filter can also be useful to prevent mixing model classes of a - * project with other non-model classes; the filter could - * {@linkplain FilterResult#BLOCK_ALL block all reflective access} to + *

A filter can also be useful to prevent mixing model classes of a project with other non-model + * classes; the filter could {@linkplain FilterResult#BLOCK_ALL block all reflective access} to * non-model classes. * - *

A reflection access filter is similar to an {@link ExclusionStrategy} - * with the major difference that a filter will cause an exception to be - * thrown when access is disallowed while an exclusion strategy just skips - * fields and classes. + *

A reflection access filter is similar to an {@link ExclusionStrategy} with the major + * difference that a filter will cause an exception to be thrown when access is disallowed while an + * exclusion strategy just skips fields and classes. * * @see GsonBuilder#addReflectionAccessFilter(ReflectionAccessFilter) * @since 2.9.1 @@ -55,158 +52,157 @@ enum FilterResult { /** * Reflection access for the class is allowed. * - *

Note that this does not affect the Java access checks in any way, - * it only permits Gson to try using reflection for a class. The Java - * runtime might still deny such access. + *

Note that this does not affect the Java access checks in any way, it only permits Gson to + * try using reflection for a class. The Java runtime might still deny such access. */ ALLOW, /** - * The filter is indecisive whether reflection access should be allowed. - * The next registered filter will be consulted to get the result. If - * there is no next filter, this result acts like {@link #ALLOW}. + * The filter is indecisive whether reflection access should be allowed. The next registered + * filter will be consulted to get the result. If there is no next filter, this result acts like + * {@link #ALLOW}. */ INDECISIVE, /** - * Blocks reflection access if a member of the class is not accessible - * by default and would have to be made accessible. This is unaffected - * by any {@code java} command line arguments being used to make packages - * accessible, or by module declaration directives which open the - * complete module or certain packages for reflection and will consider - * such packages inaccessible. + * Blocks reflection access if a member of the class is not accessible by default and would have + * to be made accessible. This is unaffected by any {@code java} command line arguments being + * used to make packages accessible, or by module declaration directives which open the + * complete module or certain packages for reflection and will consider such packages + * inaccessible. * - *

Note that this only works for Java 9 and higher, for older - * Java versions its functionality will be limited and it might behave like - * {@link #ALLOW}. Access checks are only performed as defined by the Java - * Language Specification (JLS 11 §6.6), - * restrictions imposed by a {@link SecurityManager} are not considered. + *

Note that this only works for Java 9 and higher, for older Java versions its + * functionality will be limited and it might behave like {@link #ALLOW}. Access checks are only + * performed as defined by the Java Language Specification (JLS 11 + * §6.6), restrictions imposed by a {@link SecurityManager} are not considered. * - *

This result type is mainly intended to help enforce the access checks of - * the Java Platform Module System. It allows detecting illegal access, even if - * the used Java version would only log a warning, or is configured to open - * packages for reflection using command line arguments. + *

This result type is mainly intended to help enforce the access checks of the Java Platform + * Module System. It allows detecting illegal access, even if the used Java version would only + * log a warning, or is configured to open packages for reflection using command line arguments. * * @see AccessibleObject#canAccess(Object) */ BLOCK_INACCESSIBLE, /** - * Blocks all reflection access for the class. Other means for serializing - * and deserializing the class, such as a {@link TypeAdapter}, have to - * be used. + * Blocks all reflection access for the class. Other means for serializing and deserializing the + * class, such as a {@link TypeAdapter}, have to be used. */ BLOCK_ALL } /** - * Blocks all reflection access to members of standard Java classes which are - * not accessible by default. However, reflection access is still allowed for - * classes for which all fields are accessible and which have an accessible - * no-args constructor (or for which an {@link InstanceCreator} has been registered). + * Blocks all reflection access to members of standard Java classes which are not accessible by + * default. However, reflection access is still allowed for classes for which all fields are + * accessible and which have an accessible no-args constructor (or for which an {@link + * InstanceCreator} has been registered). * - *

If this filter encounters a class other than a standard Java class it - * returns {@link FilterResult#INDECISIVE}. + *

If this filter encounters a class other than a standard Java class it returns {@link + * FilterResult#INDECISIVE}. * - *

This filter is mainly intended to help enforcing the access checks of - * Java Platform Module System. It allows detecting illegal access, even if - * the used Java version would only log a warning, or is configured to open - * packages for reflection. However, this filter only works for Java 9 and - * higher, when using an older Java version its functionality will be - * limited. + *

This filter is mainly intended to help enforcing the access checks of Java Platform Module + * System. It allows detecting illegal access, even if the used Java version would only log a + * warning, or is configured to open packages for reflection. However, this filter only works + * for Java 9 and higher, when using an older Java version its functionality will be limited. * - *

Note that this filter might not cover all standard Java classes. Currently - * only classes in a {@code java.*} or {@code javax.*} package are considered. The - * set of detected classes might be expanded in the future without prior notice. + *

Note that this filter might not cover all standard Java classes. Currently only classes in a + * {@code java.*} or {@code javax.*} package are considered. The set of detected classes might be + * expanded in the future without prior notice. * * @see FilterResult#BLOCK_INACCESSIBLE */ - ReflectionAccessFilter BLOCK_INACCESSIBLE_JAVA = new ReflectionAccessFilter() { - @Override public FilterResult check(Class rawClass) { - return ReflectionAccessFilterHelper.isJavaType(rawClass) - ? FilterResult.BLOCK_INACCESSIBLE - : FilterResult.INDECISIVE; - } - }; + ReflectionAccessFilter BLOCK_INACCESSIBLE_JAVA = + new ReflectionAccessFilter() { + @Override + public FilterResult check(Class rawClass) { + return ReflectionAccessFilterHelper.isJavaType(rawClass) + ? FilterResult.BLOCK_INACCESSIBLE + : FilterResult.INDECISIVE; + } + }; /** * Blocks all reflection access to members of standard Java classes. * - *

If this filter encounters a class other than a standard Java class it - * returns {@link FilterResult#INDECISIVE}. + *

If this filter encounters a class other than a standard Java class it returns {@link + * FilterResult#INDECISIVE}. * - *

This filter is mainly intended to prevent depending on implementation - * details of the Java platform and to help applications prepare for upgrading - * to the Java Platform Module System. + *

This filter is mainly intended to prevent depending on implementation details of the Java + * platform and to help applications prepare for upgrading to the Java Platform Module System. * - *

Note that this filter might not cover all standard Java classes. Currently - * only classes in a {@code java.*} or {@code javax.*} package are considered. The - * set of detected classes might be expanded in the future without prior notice. + *

Note that this filter might not cover all standard Java classes. Currently only classes in a + * {@code java.*} or {@code javax.*} package are considered. The set of detected classes might be + * expanded in the future without prior notice. * * @see #BLOCK_INACCESSIBLE_JAVA * @see FilterResult#BLOCK_ALL */ - ReflectionAccessFilter BLOCK_ALL_JAVA = new ReflectionAccessFilter() { - @Override public FilterResult check(Class rawClass) { - return ReflectionAccessFilterHelper.isJavaType(rawClass) - ? FilterResult.BLOCK_ALL - : FilterResult.INDECISIVE; - } - }; + ReflectionAccessFilter BLOCK_ALL_JAVA = + new ReflectionAccessFilter() { + @Override + public FilterResult check(Class rawClass) { + return ReflectionAccessFilterHelper.isJavaType(rawClass) + ? FilterResult.BLOCK_ALL + : FilterResult.INDECISIVE; + } + }; /** * Blocks all reflection access to members of standard Android classes. * - *

If this filter encounters a class other than a standard Android class it - * returns {@link FilterResult#INDECISIVE}. + *

If this filter encounters a class other than a standard Android class it returns {@link + * FilterResult#INDECISIVE}. * - *

This filter is mainly intended to prevent depending on implementation - * details of the Android platform. + *

This filter is mainly intended to prevent depending on implementation details of the Android + * platform. * - *

Note that this filter might not cover all standard Android classes. Currently - * only classes in an {@code android.*} or {@code androidx.*} package, and standard - * Java classes in a {@code java.*} or {@code javax.*} package are considered. The - * set of detected classes might be expanded in the future without prior notice. + *

Note that this filter might not cover all standard Android classes. Currently only classes + * in an {@code android.*} or {@code androidx.*} package, and standard Java classes in a {@code + * java.*} or {@code javax.*} package are considered. The set of detected classes might be + * expanded in the future without prior notice. * * @see FilterResult#BLOCK_ALL */ - ReflectionAccessFilter BLOCK_ALL_ANDROID = new ReflectionAccessFilter() { - @Override public FilterResult check(Class rawClass) { - return ReflectionAccessFilterHelper.isAndroidType(rawClass) - ? FilterResult.BLOCK_ALL - : FilterResult.INDECISIVE; - } - }; + ReflectionAccessFilter BLOCK_ALL_ANDROID = + new ReflectionAccessFilter() { + @Override + public FilterResult check(Class rawClass) { + return ReflectionAccessFilterHelper.isAndroidType(rawClass) + ? FilterResult.BLOCK_ALL + : FilterResult.INDECISIVE; + } + }; /** - * Blocks all reflection access to members of classes belonging to programming - * language platforms, such as Java, Android, Kotlin or Scala. + * Blocks all reflection access to members of classes belonging to programming language platforms, + * such as Java, Android, Kotlin or Scala. * - *

If this filter encounters a class other than a standard platform class it - * returns {@link FilterResult#INDECISIVE}. + *

If this filter encounters a class other than a standard platform class it returns {@link + * FilterResult#INDECISIVE}. * - *

This filter is mainly intended to prevent depending on implementation - * details of the platform classes. + *

This filter is mainly intended to prevent depending on implementation details of the + * platform classes. * - *

Note that this filter might not cover all platform classes. Currently it - * combines the filters {@link #BLOCK_ALL_JAVA} and {@link #BLOCK_ALL_ANDROID}, - * and checks for other language-specific platform classes like {@code kotlin.*}. - * The set of detected classes might be expanded in the future without prior notice. + *

Note that this filter might not cover all platform classes. Currently it combines the + * filters {@link #BLOCK_ALL_JAVA} and {@link #BLOCK_ALL_ANDROID}, and checks for other + * language-specific platform classes like {@code kotlin.*}. The set of detected classes might be + * expanded in the future without prior notice. * * @see FilterResult#BLOCK_ALL */ - ReflectionAccessFilter BLOCK_ALL_PLATFORM = new ReflectionAccessFilter() { - @Override public FilterResult check(Class rawClass) { - return ReflectionAccessFilterHelper.isAnyPlatformType(rawClass) - ? FilterResult.BLOCK_ALL - : FilterResult.INDECISIVE; - } - }; + ReflectionAccessFilter BLOCK_ALL_PLATFORM = + new ReflectionAccessFilter() { + @Override + public FilterResult check(Class rawClass) { + return ReflectionAccessFilterHelper.isAnyPlatformType(rawClass) + ? FilterResult.BLOCK_ALL + : FilterResult.INDECISIVE; + } + }; /** * Checks if reflection access should be allowed for a class. * - * @param rawClass - * Class to check - * @return - * Result indicating whether reflection access is allowed + * @param rawClass Class to check + * @return Result indicating whether reflection access is allowed */ FilterResult check(Class rawClass); } diff --git a/gson/src/main/java/com/google/gson/Strictness.java b/gson/src/main/java/com/google/gson/Strictness.java index f3bd3fe08f..775214f36b 100644 --- a/gson/src/main/java/com/google/gson/Strictness.java +++ b/gson/src/main/java/com/google/gson/Strictness.java @@ -4,26 +4,25 @@ import com.google.gson.stream.JsonWriter; /** - * Modes that indicate how strictly a JSON {@linkplain JsonReader reader} or - * {@linkplain JsonWriter writer} follows the syntax laid out in the - * RFC 8259 JSON specification. + * Modes that indicate how strictly a JSON {@linkplain JsonReader reader} or {@linkplain JsonWriter + * writer} follows the syntax laid out in the RFC + * 8259 JSON specification. * - *

You can look at {@link JsonReader#setStrictness(Strictness)} to see how the strictness - * affects the {@link JsonReader} and you can look at - * {@link JsonWriter#setStrictness(Strictness)} to see how the strictness - * affects the {@link JsonWriter}.

+ *

You can look at {@link JsonReader#setStrictness(Strictness)} to see how the strictness affects + * the {@link JsonReader} and you can look at {@link JsonWriter#setStrictness(Strictness)} to see + * how the strictness affects the {@link JsonWriter}. * * @see JsonReader#setStrictness(Strictness) * @see JsonWriter#setStrictness(Strictness) * @since $next-version$ */ public enum Strictness { - /** Allow large deviations from the JSON specification. */ - LENIENT, + /** Allow large deviations from the JSON specification. */ + LENIENT, - /** Allow certain small deviations from the JSON specification for legacy reasons. */ - LEGACY_STRICT, + /** Allow certain small deviations from the JSON specification for legacy reasons. */ + LEGACY_STRICT, - /** Strict compliance with the JSON specification. */ - STRICT + /** Strict compliance with the JSON specification. */ + STRICT } diff --git a/gson/src/main/java/com/google/gson/ToNumberPolicy.java b/gson/src/main/java/com/google/gson/ToNumberPolicy.java index 34f4d4f9ea..6380e5d986 100644 --- a/gson/src/main/java/com/google/gson/ToNumberPolicy.java +++ b/gson/src/main/java/com/google/gson/ToNumberPolicy.java @@ -24,9 +24,9 @@ import java.math.BigDecimal; /** - * An enumeration that defines two standard number reading strategies and a couple of - * strategies to overcome some historical Gson limitations while deserializing numbers as - * {@link Object} and {@link Number}. + * An enumeration that defines two standard number reading strategies and a couple of strategies to + * overcome some historical Gson limitations while deserializing numbers as {@link Object} and + * {@link Number}. * * @see ToNumberStrategy * @since 2.8.9 @@ -34,37 +34,39 @@ public enum ToNumberPolicy implements ToNumberStrategy { /** - * Using this policy will ensure that numbers will be read as {@link Double} values. - * This is the default strategy used during deserialization of numbers as {@link Object}. + * Using this policy will ensure that numbers will be read as {@link Double} values. This is the + * default strategy used during deserialization of numbers as {@link Object}. */ DOUBLE { - @Override public Double readNumber(JsonReader in) throws IOException { + @Override + public Double readNumber(JsonReader in) throws IOException { return in.nextDouble(); } }, /** - * Using this policy will ensure that numbers will be read as a lazily parsed number backed - * by a string. This is the default strategy used during deserialization of numbers as - * {@link Number}. + * Using this policy will ensure that numbers will be read as a lazily parsed number backed by a + * string. This is the default strategy used during deserialization of numbers as {@link Number}. */ LAZILY_PARSED_NUMBER { - @Override public Number readNumber(JsonReader in) throws IOException { + @Override + public Number readNumber(JsonReader in) throws IOException { return new LazilyParsedNumber(in.nextString()); } }, /** * Using this policy will ensure that numbers will be read as {@link Long} or {@link Double} - * values depending on how JSON numbers are represented: {@code Long} if the JSON number can - * be parsed as a {@code Long} value, or otherwise {@code Double} if it can be parsed as a - * {@code Double} value. If the parsed double-precision number results in a positive or negative - * infinity ({@link Double#isInfinite()}) or a NaN ({@link Double#isNaN()}) value and the - * {@code JsonReader} is not {@link JsonReader#isLenient() lenient}, a {@link MalformedJsonException} - * is thrown. + * values depending on how JSON numbers are represented: {@code Long} if the JSON number can be + * parsed as a {@code Long} value, or otherwise {@code Double} if it can be parsed as a {@code + * Double} value. If the parsed double-precision number results in a positive or negative infinity + * ({@link Double#isInfinite()}) or a NaN ({@link Double#isNaN()}) value and the {@code + * JsonReader} is not {@link JsonReader#isLenient() lenient}, a {@link MalformedJsonException} is + * thrown. */ LONG_OR_DOUBLE { - @Override public Number readNumber(JsonReader in) throws IOException, JsonParseException { + @Override + public Number readNumber(JsonReader in) throws IOException, JsonParseException { String value = in.nextString(); try { return Long.parseLong(value); @@ -72,29 +74,32 @@ public enum ToNumberPolicy implements ToNumberStrategy { try { Double d = Double.valueOf(value); if ((d.isInfinite() || d.isNaN()) && !in.isLenient()) { - throw new MalformedJsonException("JSON forbids NaN and infinities: " + d + "; at path " + in.getPreviousPath()); + throw new MalformedJsonException( + "JSON forbids NaN and infinities: " + d + "; at path " + in.getPreviousPath()); } return d; } catch (NumberFormatException doubleE) { - throw new JsonParseException("Cannot parse " + value + "; at path " + in.getPreviousPath(), doubleE); + throw new JsonParseException( + "Cannot parse " + value + "; at path " + in.getPreviousPath(), doubleE); } } } }, /** - * Using this policy will ensure that numbers will be read as numbers of arbitrary length - * using {@link BigDecimal}. + * Using this policy will ensure that numbers will be read as numbers of arbitrary length using + * {@link BigDecimal}. */ BIG_DECIMAL { - @Override public BigDecimal readNumber(JsonReader in) throws IOException { + @Override + public BigDecimal readNumber(JsonReader in) throws IOException { String value = in.nextString(); try { return NumberLimits.parseBigDecimal(value); } catch (NumberFormatException e) { - throw new JsonParseException("Cannot parse " + value + "; at path " + in.getPreviousPath(), e); + throw new JsonParseException( + "Cannot parse " + value + "; at path " + in.getPreviousPath(), e); } } } - } diff --git a/gson/src/main/java/com/google/gson/ToNumberStrategy.java b/gson/src/main/java/com/google/gson/ToNumberStrategy.java index ae74221988..bbbd415aee 100644 --- a/gson/src/main/java/com/google/gson/ToNumberStrategy.java +++ b/gson/src/main/java/com/google/gson/ToNumberStrategy.java @@ -20,20 +20,20 @@ import java.io.IOException; /** - * A strategy that is used to control how numbers should be deserialized for {@link Object} and {@link Number} - * when a concrete type of the deserialized number is unknown in advance. By default, Gson uses the following - * deserialization strategies: + * A strategy that is used to control how numbers should be deserialized for {@link Object} and + * {@link Number} when a concrete type of the deserialized number is unknown in advance. By default, + * Gson uses the following deserialization strategies: * *

    - *
  • {@link Double} values are returned for JSON numbers if the deserialization type is declared as - * {@code Object}, see {@link ToNumberPolicy#DOUBLE};
  • - *
  • Lazily parsed number values are returned if the deserialization type is declared as {@code Number}, - * see {@link ToNumberPolicy#LAZILY_PARSED_NUMBER}.
  • + *
  • {@link Double} values are returned for JSON numbers if the deserialization type is declared + * as {@code Object}, see {@link ToNumberPolicy#DOUBLE}; + *
  • Lazily parsed number values are returned if the deserialization type is declared as {@code + * Number}, see {@link ToNumberPolicy#LAZILY_PARSED_NUMBER}. *
* *

For historical reasons, Gson does not support deserialization of arbitrary-length numbers for - * {@code Object} and {@code Number} by default, potentially causing precision loss. However, - * RFC 8259 permits this: + * {@code Object} and {@code Number} by default, potentially causing precision loss. However, RFC 8259 permits this: * *

  *   This specification allows implementations to set limits on the range
@@ -50,7 +50,7 @@
  * 
* *

To overcome the precision loss, use for example {@link ToNumberPolicy#LONG_OR_DOUBLE} or - * {@link ToNumberPolicy#BIG_DECIMAL}.

+ * {@link ToNumberPolicy#BIG_DECIMAL}. * * @see ToNumberPolicy * @see GsonBuilder#setObjectToNumberStrategy(ToNumberStrategy) @@ -60,8 +60,8 @@ public interface ToNumberStrategy { /** - * Reads a number from the given JSON reader. A strategy is supposed to read a single value from the - * reader, and the read value is guaranteed never to be {@code null}. + * Reads a number from the given JSON reader. A strategy is supposed to read a single value from + * the reader, and the read value is guaranteed never to be {@code null}. * * @param in JSON reader to read a number from * @return number read from the JSON reader. diff --git a/gson/src/main/java/com/google/gson/TypeAdapter.java b/gson/src/main/java/com/google/gson/TypeAdapter.java index 405fb12ac2..5e6f08766e 100644 --- a/gson/src/main/java/com/google/gson/TypeAdapter.java +++ b/gson/src/main/java/com/google/gson/TypeAdapter.java @@ -31,66 +31,69 @@ * Converts Java objects to and from JSON. * *

Defining a type's JSON form

- * By default Gson converts application classes to JSON using its built-in type - * adapters. If Gson's default JSON conversion isn't appropriate for a type, - * extend this class to customize the conversion. Here's an example of a type - * adapter for an (X,Y) coordinate point:
{@code
- *   public class PointAdapter extends TypeAdapter {
- *     public Point read(JsonReader reader) throws IOException {
- *       if (reader.peek() == JsonToken.NULL) {
- *         reader.nextNull();
- *         return null;
- *       }
- *       String xy = reader.nextString();
- *       String[] parts = xy.split(",");
- *       int x = Integer.parseInt(parts[0]);
- *       int y = Integer.parseInt(parts[1]);
- *       return new Point(x, y);
+ *
+ * By default Gson converts application classes to JSON using its built-in type adapters. If Gson's
+ * default JSON conversion isn't appropriate for a type, extend this class to customize the
+ * conversion. Here's an example of a type adapter for an (X,Y) coordinate point:
+ *
+ * 
{@code
+ * public class PointAdapter extends TypeAdapter {
+ *   public Point read(JsonReader reader) throws IOException {
+ *     if (reader.peek() == JsonToken.NULL) {
+ *       reader.nextNull();
+ *       return null;
  *     }
- *     public void write(JsonWriter writer, Point value) throws IOException {
- *       if (value == null) {
- *         writer.nullValue();
- *         return;
- *       }
- *       String xy = value.getX() + "," + value.getY();
- *       writer.value(xy);
+ *     String xy = reader.nextString();
+ *     String[] parts = xy.split(",");
+ *     int x = Integer.parseInt(parts[0]);
+ *     int y = Integer.parseInt(parts[1]);
+ *     return new Point(x, y);
+ *   }
+ *   public void write(JsonWriter writer, Point value) throws IOException {
+ *     if (value == null) {
+ *       writer.nullValue();
+ *       return;
  *     }
- *   }}
- * With this type adapter installed, Gson will convert {@code Points} to JSON as - * strings like {@code "5,8"} rather than objects like {@code {"x":5,"y":8}}. In - * this case the type adapter binds a rich Java class to a compact JSON value. + * String xy = value.getX() + "," + value.getY(); + * writer.value(xy); + * } + * } + * }
* - *

The {@link #read(JsonReader) read()} method must read exactly one value - * and {@link #write(JsonWriter,Object) write()} must write exactly one value. - * For primitive types this is means readers should make exactly one call to - * {@code nextBoolean()}, {@code nextDouble()}, {@code nextInt()}, {@code - * nextLong()}, {@code nextString()} or {@code nextNull()}. Writers should make - * exactly one call to one of value() or nullValue(). - * For arrays, type adapters should start with a call to {@code beginArray()}, - * convert all elements, and finish with a call to {@code endArray()}. For - * objects, they should start with {@code beginObject()}, convert the object, - * and finish with {@code endObject()}. Failing to convert a value or converting - * too many values may cause the application to crash. + * With this type adapter installed, Gson will convert {@code Points} to JSON as strings like {@code + * "5,8"} rather than objects like {@code {"x":5,"y":8}}. In this case the type adapter binds a rich + * Java class to a compact JSON value. * - *

Type adapters should be prepared to read null from the stream and write it - * to the stream. Alternatively, they should use {@link #nullSafe()} method while - * registering the type adapter with Gson. If your {@code Gson} instance - * has been configured to {@link GsonBuilder#serializeNulls()}, these nulls will be - * written to the final document. Otherwise the value (and the corresponding name - * when writing to a JSON object) will be omitted automatically. In either case - * your type adapter must handle null. + *

The {@link #read(JsonReader) read()} method must read exactly one value and {@link + * #write(JsonWriter,Object) write()} must write exactly one value. For primitive types this is + * means readers should make exactly one call to {@code nextBoolean()}, {@code nextDouble()}, {@code + * nextInt()}, {@code nextLong()}, {@code nextString()} or {@code nextNull()}. Writers should make + * exactly one call to one of value() or nullValue(). For arrays, type + * adapters should start with a call to {@code beginArray()}, convert all elements, and finish with + * a call to {@code endArray()}. For objects, they should start with {@code beginObject()}, convert + * the object, and finish with {@code endObject()}. Failing to convert a value or converting too + * many values may cause the application to crash. * - *

Type adapters should be stateless and thread-safe, otherwise the thread-safety - * guarantees of {@link Gson} might not apply. + *

Type adapters should be prepared to read null from the stream and write it to the stream. + * Alternatively, they should use {@link #nullSafe()} method while registering the type adapter with + * Gson. If your {@code Gson} instance has been configured to {@link GsonBuilder#serializeNulls()}, + * these nulls will be written to the final document. Otherwise the value (and the corresponding + * name when writing to a JSON object) will be omitted automatically. In either case your type + * adapter must handle null. * - *

To use a custom type adapter with Gson, you must register it with a - * {@link GsonBuilder}:

{@code
- *   GsonBuilder builder = new GsonBuilder();
- *   builder.registerTypeAdapter(Point.class, new PointAdapter());
- *   // if PointAdapter didn't check for nulls in its read/write methods, you should instead use
- *   // builder.registerTypeAdapter(Point.class, new PointAdapter().nullSafe());
- *   ...
- *   Gson gson = builder.create();
+ * 

Type adapters should be stateless and thread-safe, otherwise the thread-safety guarantees of + * {@link Gson} might not apply. + * + *

To use a custom type adapter with Gson, you must register it with a {@link + * GsonBuilder}: + * + *

{@code
+ * GsonBuilder builder = new GsonBuilder();
+ * builder.registerTypeAdapter(Point.class, new PointAdapter());
+ * // if PointAdapter didn't check for nulls in its read/write methods, you should instead use
+ * // builder.registerTypeAdapter(Point.class, new PointAdapter().nullSafe());
+ * ...
+ * Gson gson = builder.create();
  * }
* * @since 2.1 @@ -117,12 +120,10 @@ // public abstract class TypeAdapter { - public TypeAdapter() { - } + public TypeAdapter() {} /** - * Writes one JSON value (an array, object, string, number, boolean or null) - * for {@code value}. + * Writes one JSON value (an array, object, string, number, boolean or null) for {@code value}. * * @param value the Java object to write. May be null. */ @@ -131,9 +132,9 @@ public TypeAdapter() { /** * Converts {@code value} to a JSON document and writes it to {@code out}. * - *

A {@link JsonWriter} with default configuration is used for writing the - * JSON data. To customize this behavior, create a {@link JsonWriter}, configure - * it and then use {@link #write(JsonWriter, Object)} instead. + *

A {@link JsonWriter} with default configuration is used for writing the JSON data. To + * customize this behavior, create a {@link JsonWriter}, configure it and then use {@link + * #write(JsonWriter, Object)} instead. * * @param value the Java object to convert. May be {@code null}. * @since 2.2 @@ -144,9 +145,9 @@ public final void toJson(Writer out, T value) throws IOException { } /** - * This wrapper method is used to make a type adapter null tolerant. In general, a - * type adapter is required to handle nulls in write and read methods. Here is how this - * is typically done:
+ * This wrapper method is used to make a type adapter null tolerant. In general, a type adapter is + * required to handle nulls in write and read methods. Here is how this is typically done:
+ * *

{@code
    * Gson gson = new GsonBuilder().registerTypeAdapter(Foo.class,
    *   new TypeAdapter() {
@@ -166,8 +167,10 @@ public final void toJson(Writer out, T value) throws IOException {
    *     }
    *   }).create();
    * }
- * You can avoid this boilerplate handling of nulls by wrapping your type adapter with - * this method. Here is how we will rewrite the above example: + * + * You can avoid this boilerplate handling of nulls by wrapping your type adapter with this + * method. Here is how we will rewrite the above example: + * *
{@code
    * Gson gson = new GsonBuilder().registerTypeAdapter(Foo.class,
    *   new TypeAdapter() {
@@ -179,18 +182,22 @@ public final void toJson(Writer out, T value) throws IOException {
    *     }
    *   }.nullSafe()).create();
    * }
+ * * Note that we didn't need to check for nulls in our type adapter after we used nullSafe. */ public final TypeAdapter nullSafe() { return new TypeAdapter() { - @Override public void write(JsonWriter out, T value) throws IOException { + @Override + public void write(JsonWriter out, T value) throws IOException { if (value == null) { out.nullValue(); } else { TypeAdapter.this.write(out, value); } } - @Override public T read(JsonReader reader) throws IOException { + + @Override + public T read(JsonReader reader) throws IOException { if (reader.peek() == JsonToken.NULL) { reader.nextNull(); return null; @@ -203,11 +210,12 @@ public final TypeAdapter nullSafe() { /** * Converts {@code value} to a JSON document. * - *

A {@link JsonWriter} with default configuration is used for writing the - * JSON data. To customize this behavior, create a {@link JsonWriter}, configure - * it and then use {@link #write(JsonWriter, Object)} instead. + *

A {@link JsonWriter} with default configuration is used for writing the JSON data. To + * customize this behavior, create a {@link JsonWriter}, configure it and then use {@link + * #write(JsonWriter, Object)} instead. * - * @throws JsonIOException wrapping {@code IOException}s thrown by {@link #write(JsonWriter, Object)} + * @throws JsonIOException wrapping {@code IOException}s thrown by {@link #write(JsonWriter, + * Object)} * @param value the Java object to convert. May be {@code null}. * @since 2.2 */ @@ -226,7 +234,8 @@ public final String toJson(T value) { * * @param value the Java object to convert. May be {@code null}. * @return the converted JSON tree. May be {@link JsonNull}. - * @throws JsonIOException wrapping {@code IOException}s thrown by {@link #write(JsonWriter, Object)} + * @throws JsonIOException wrapping {@code IOException}s thrown by {@link #write(JsonWriter, + * Object)} * @since 2.2 */ public final JsonElement toJsonTree(T value) { @@ -240,8 +249,8 @@ public final JsonElement toJsonTree(T value) { } /** - * Reads one JSON value (an array, object, string, number, boolean or null) - * and converts it to a Java object. Returns the converted object. + * Reads one JSON value (an array, object, string, number, boolean or null) and converts it to a + * Java object. Returns the converted object. * * @return the converted Java object. May be {@code null}. */ @@ -250,13 +259,13 @@ public final JsonElement toJsonTree(T value) { /** * Converts the JSON document in {@code in} to a Java object. * - *

A {@link JsonReader} with default configuration (that is with - * {@link Strictness#LEGACY_STRICT} as strictness) is used for reading the JSON data. - * To customize this behavior, create a {@link JsonReader}, configure it and then - * use {@link #read(JsonReader)} instead. + *

A {@link JsonReader} with default configuration (that is with {@link + * Strictness#LEGACY_STRICT} as strictness) is used for reading the JSON data. To customize this + * behavior, create a {@link JsonReader}, configure it and then use {@link #read(JsonReader)} + * instead. * - *

No exception is thrown if the JSON data has multiple top-level JSON elements, - * or if there is trailing data. + *

No exception is thrown if the JSON data has multiple top-level JSON elements, or if there is + * trailing data. * * @return the converted Java object. May be {@code null}. * @since 2.2 @@ -269,13 +278,13 @@ public final T fromJson(Reader in) throws IOException { /** * Converts the JSON document in {@code json} to a Java object. * - *

A {@link JsonReader} with default configuration (that is with - * {@link Strictness#LEGACY_STRICT} as strictness) is used for reading the JSON data. - * To customize this behavior, create a {@link JsonReader}, configure it and then - * use {@link #read(JsonReader)} instead. + *

A {@link JsonReader} with default configuration (that is with {@link + * Strictness#LEGACY_STRICT} as strictness) is used for reading the JSON data. To customize this + * behavior, create a {@link JsonReader}, configure it and then use {@link #read(JsonReader)} + * instead. * - *

No exception is thrown if the JSON data has multiple top-level JSON elements, - * or if there is trailing data. + *

No exception is thrown if the JSON data has multiple top-level JSON elements, or if there is + * trailing data. * * @return the converted Java object. May be {@code null}. * @since 2.2 diff --git a/gson/src/main/java/com/google/gson/TypeAdapterFactory.java b/gson/src/main/java/com/google/gson/TypeAdapterFactory.java index 75fdddbfc4..0387340635 100644 --- a/gson/src/main/java/com/google/gson/TypeAdapterFactory.java +++ b/gson/src/main/java/com/google/gson/TypeAdapterFactory.java @@ -19,153 +19,152 @@ import com.google.gson.reflect.TypeToken; /** - * Creates type adapters for set of related types. Type adapter factories are - * most useful when several types share similar structure in their JSON form. + * Creates type adapters for set of related types. Type adapter factories are most useful when + * several types share similar structure in their JSON form. * *

Examples

+ * *

Example: Converting enums to lowercase

- * In this example, we implement a factory that creates type adapters for all - * enums. The type adapters will write enums in lowercase, despite the fact - * that they're defined in {@code CONSTANT_CASE} in the corresponding Java - * model:
   {@code
- *
- *   public class LowercaseEnumTypeAdapterFactory implements TypeAdapterFactory {
- *     public  TypeAdapter create(Gson gson, TypeToken type) {
- *       Class rawType = (Class) type.getRawType();
- *       if (!rawType.isEnum()) {
- *         return null;
- *       }
  *
- *       final Map lowercaseToConstant = new HashMap<>();
- *       for (T constant : rawType.getEnumConstants()) {
- *         lowercaseToConstant.put(toLowercase(constant), constant);
- *       }
+ * In this example, we implement a factory that creates type adapters for all enums. The type
+ * adapters will write enums in lowercase, despite the fact that they're defined in {@code
+ * CONSTANT_CASE} in the corresponding Java model:
+ *
+ * 
{@code
+ * public class LowercaseEnumTypeAdapterFactory implements TypeAdapterFactory {
+ *   public  TypeAdapter create(Gson gson, TypeToken type) {
+ *     Class rawType = (Class) type.getRawType();
+ *     if (!rawType.isEnum()) {
+ *       return null;
+ *     }
+ *
+ *     final Map lowercaseToConstant = new HashMap<>();
+ *     for (T constant : rawType.getEnumConstants()) {
+ *       lowercaseToConstant.put(toLowercase(constant), constant);
+ *     }
  *
- *       return new TypeAdapter() {
- *         public void write(JsonWriter out, T value) throws IOException {
- *           if (value == null) {
- *             out.nullValue();
- *           } else {
- *             out.value(toLowercase(value));
- *           }
+ *     return new TypeAdapter() {
+ *       public void write(JsonWriter out, T value) throws IOException {
+ *         if (value == null) {
+ *           out.nullValue();
+ *         } else {
+ *           out.value(toLowercase(value));
  *         }
+ *       }
  *
- *         public T read(JsonReader reader) throws IOException {
- *           if (reader.peek() == JsonToken.NULL) {
- *             reader.nextNull();
- *             return null;
- *           } else {
- *             return lowercaseToConstant.get(reader.nextString());
- *           }
+ *       public T read(JsonReader reader) throws IOException {
+ *         if (reader.peek() == JsonToken.NULL) {
+ *           reader.nextNull();
+ *           return null;
+ *         } else {
+ *           return lowercaseToConstant.get(reader.nextString());
  *         }
- *       };
- *     }
+ *       }
+ *     };
+ *   }
  *
- *     private String toLowercase(Object o) {
- *       return o.toString().toLowerCase(Locale.US);
- *     }
+ *   private String toLowercase(Object o) {
+ *     return o.toString().toLowerCase(Locale.US);
  *   }
+ * }
  * }
* - *

Type adapter factories select which types they provide type adapters - * for. If a factory cannot support a given type, it must return null when - * that type is passed to {@link #create}. Factories should expect {@code - * create()} to be called on them for many types and should return null for - * most of those types. In the above example the factory returns null for - * calls to {@code create()} where {@code type} is not an enum. - * - *

A factory is typically called once per type, but the returned type - * adapter may be used many times. It is most efficient to do expensive work - * like reflection in {@code create()} so that the type adapter's {@code - * read()} and {@code write()} methods can be very fast. In this example the + *

Type adapter factories select which types they provide type adapters for. If a factory cannot + * support a given type, it must return null when that type is passed to {@link #create}. Factories + * should expect {@code create()} to be called on them for many types and should return null for + * most of those types. In the above example the factory returns null for calls to {@code create()} + * where {@code type} is not an enum. + * + *

A factory is typically called once per type, but the returned type adapter may be used many + * times. It is most efficient to do expensive work like reflection in {@code create()} so that the + * type adapter's {@code read()} and {@code write()} methods can be very fast. In this example the * mapping from lowercase name to enum value is computed eagerly. * *

As with type adapters, factories must be registered with a {@link - * com.google.gson.GsonBuilder} for them to take effect:

   {@code
+ * com.google.gson.GsonBuilder} for them to take effect:
  *
- *  GsonBuilder builder = new GsonBuilder();
- *  builder.registerTypeAdapterFactory(new LowercaseEnumTypeAdapterFactory());
- *  ...
- *  Gson gson = builder.create();
+ * 
{@code
+ * GsonBuilder builder = new GsonBuilder();
+ * builder.registerTypeAdapterFactory(new LowercaseEnumTypeAdapterFactory());
+ * ...
+ * Gson gson = builder.create();
  * }
- * If multiple factories support the same type, the factory registered earlier - * takes precedence. + * + * If multiple factories support the same type, the factory registered earlier takes precedence. * *

Example: Composing other type adapters

- * In this example we implement a factory for Guava's {@code Multiset} - * collection type. The factory can be used to create type adapters for - * multisets of any element type: the type adapter for {@code - * Multiset} is different from the type adapter for {@code - * Multiset}. - * - *

The type adapter delegates to another type adapter for the - * multiset elements. It figures out the element type by reflecting on the - * multiset's type token. A {@code Gson} is passed in to {@code create} for - * just this purpose:

   {@code
- *
- *   public class MultisetTypeAdapterFactory implements TypeAdapterFactory {
- *     public  TypeAdapter create(Gson gson, TypeToken typeToken) {
- *       Type type = typeToken.getType();
- *       if (typeToken.getRawType() != Multiset.class
- *           || !(type instanceof ParameterizedType)) {
- *         return null;
- *       }
  *
- *       Type elementType = ((ParameterizedType) type).getActualTypeArguments()[0];
- *       TypeAdapter elementAdapter = gson.getAdapter(TypeToken.get(elementType));
- *       return (TypeAdapter) newMultisetAdapter(elementAdapter);
+ * In this example we implement a factory for Guava's {@code Multiset} collection type. The factory
+ * can be used to create type adapters for multisets of any element type: the type adapter for
+ * {@code Multiset} is different from the type adapter for {@code Multiset}.
+ *
+ * 

The type adapter delegates to another type adapter for the multiset elements. It + * figures out the element type by reflecting on the multiset's type token. A {@code Gson} is passed + * in to {@code create} for just this purpose: + * + *

{@code
+ * public class MultisetTypeAdapterFactory implements TypeAdapterFactory {
+ *   public  TypeAdapter create(Gson gson, TypeToken typeToken) {
+ *     Type type = typeToken.getType();
+ *     if (typeToken.getRawType() != Multiset.class
+ *         || !(type instanceof ParameterizedType)) {
+ *       return null;
  *     }
  *
- *     private  TypeAdapter> newMultisetAdapter(
- *         final TypeAdapter elementAdapter) {
- *       return new TypeAdapter>() {
- *         public void write(JsonWriter out, Multiset value) throws IOException {
- *           if (value == null) {
- *             out.nullValue();
- *             return;
- *           }
- *
- *           out.beginArray();
- *           for (Multiset.Entry entry : value.entrySet()) {
- *             out.value(entry.getCount());
- *             elementAdapter.write(out, entry.getElement());
- *           }
- *           out.endArray();
+ *     Type elementType = ((ParameterizedType) type).getActualTypeArguments()[0];
+ *     TypeAdapter elementAdapter = gson.getAdapter(TypeToken.get(elementType));
+ *     return (TypeAdapter) newMultisetAdapter(elementAdapter);
+ *   }
+ *
+ *   private  TypeAdapter> newMultisetAdapter(
+ *       final TypeAdapter elementAdapter) {
+ *     return new TypeAdapter>() {
+ *       public void write(JsonWriter out, Multiset value) throws IOException {
+ *         if (value == null) {
+ *           out.nullValue();
+ *           return;
  *         }
  *
- *         public Multiset read(JsonReader in) throws IOException {
- *           if (in.peek() == JsonToken.NULL) {
- *             in.nextNull();
- *             return null;
- *           }
- *
- *           Multiset result = LinkedHashMultiset.create();
- *           in.beginArray();
- *           while (in.hasNext()) {
- *             int count = in.nextInt();
- *             E element = elementAdapter.read(in);
- *             result.add(element, count);
- *           }
- *           in.endArray();
- *           return result;
+ *         out.beginArray();
+ *         for (Multiset.Entry entry : value.entrySet()) {
+ *           out.value(entry.getCount());
+ *           elementAdapter.write(out, entry.getElement());
  *         }
- *       };
- *     }
+ *         out.endArray();
+ *       }
+ *
+ *       public Multiset read(JsonReader in) throws IOException {
+ *         if (in.peek() == JsonToken.NULL) {
+ *           in.nextNull();
+ *           return null;
+ *         }
+ *
+ *         Multiset result = LinkedHashMultiset.create();
+ *         in.beginArray();
+ *         while (in.hasNext()) {
+ *           int count = in.nextInt();
+ *           E element = elementAdapter.read(in);
+ *           result.add(element, count);
+ *         }
+ *         in.endArray();
+ *         return result;
+ *       }
+ *     };
  *   }
+ * }
  * }
- * Delegating from one type adapter to another is extremely powerful; it's - * the foundation of how Gson converts Java objects and collections. Whenever - * possible your factory should retrieve its delegate type adapter in the - * {@code create()} method; this ensures potentially-expensive type adapter - * creation happens only once. + * + * Delegating from one type adapter to another is extremely powerful; it's the foundation of how + * Gson converts Java objects and collections. Whenever possible your factory should retrieve its + * delegate type adapter in the {@code create()} method; this ensures potentially-expensive type + * adapter creation happens only once. * * @since 2.1 */ public interface TypeAdapterFactory { /** - * Returns a type adapter for {@code type}, or null if this factory doesn't - * support {@code type}. + * Returns a type adapter for {@code type}, or null if this factory doesn't support {@code type}. */ TypeAdapter create(Gson gson, TypeToken type); } diff --git a/gson/src/main/java/com/google/gson/annotations/Expose.java b/gson/src/main/java/com/google/gson/annotations/Expose.java index 966460dbc6..9b66af4fb4 100644 --- a/gson/src/main/java/com/google/gson/annotations/Expose.java +++ b/gson/src/main/java/com/google/gson/annotations/Expose.java @@ -23,15 +23,15 @@ import java.lang.annotation.Target; /** - * An annotation that indicates this member should be exposed for JSON - * serialization or deserialization. + * An annotation that indicates this member should be exposed for JSON serialization or + * deserialization. * - *

This annotation has no effect unless you build {@link com.google.gson.Gson} - * with a {@link com.google.gson.GsonBuilder} and invoke - * {@link com.google.gson.GsonBuilder#excludeFieldsWithoutExposeAnnotation()} - * method.

+ *

This annotation has no effect unless you build {@link com.google.gson.Gson} with a {@link + * com.google.gson.GsonBuilder} and invoke {@link + * com.google.gson.GsonBuilder#excludeFieldsWithoutExposeAnnotation()} method. * *

Here is an example of how this annotation is meant to be used: + * *

  * public class User {
  *   @Expose private String firstName;
@@ -40,20 +40,21 @@
  *   private String password;
  * }
  * 
- * If you created Gson with {@code new Gson()}, the {@code toJson()} and {@code fromJson()} - * methods will use the {@code password} field along-with {@code firstName}, {@code lastName}, - * and {@code emailAddress} for serialization and deserialization. However, if you created Gson - * with {@code Gson gson = new GsonBuilder().excludeFieldsWithoutExposeAnnotation().create()} - * then the {@code toJson()} and {@code fromJson()} methods of Gson will exclude the - * {@code password} field. This is because the {@code password} field is not marked with the - * {@code @Expose} annotation. Gson will also exclude {@code lastName} and {@code emailAddress} - * from serialization since {@code serialize} is set to {@code false}. Similarly, Gson will - * exclude {@code emailAddress} from deserialization since {@code deserialize} is set to false. * - *

Note that another way to achieve the same effect would have been to just mark the - * {@code password} field as {@code transient}, and Gson would have excluded it even with default - * settings. The {@code @Expose} annotation is useful in a style of programming where you want to - * explicitly specify all fields that should get considered for serialization or deserialization. + * If you created Gson with {@code new Gson()}, the {@code toJson()} and {@code fromJson()} methods + * will use the {@code password} field along-with {@code firstName}, {@code lastName}, and {@code + * emailAddress} for serialization and deserialization. However, if you created Gson with {@code + * Gson gson = new GsonBuilder().excludeFieldsWithoutExposeAnnotation().create()} then the {@code + * toJson()} and {@code fromJson()} methods of Gson will exclude the {@code password} field. This is + * because the {@code password} field is not marked with the {@code @Expose} annotation. Gson will + * also exclude {@code lastName} and {@code emailAddress} from serialization since {@code serialize} + * is set to {@code false}. Similarly, Gson will exclude {@code emailAddress} from deserialization + * since {@code deserialize} is set to false. + * + *

Note that another way to achieve the same effect would have been to just mark the {@code + * password} field as {@code transient}, and Gson would have excluded it even with default settings. + * The {@code @Expose} annotation is useful in a style of programming where you want to explicitly + * specify all fields that should get considered for serialization or deserialization. * * @author Inderjeet Singh * @author Joel Leitch @@ -62,19 +63,21 @@ @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.FIELD) public @interface Expose { - + /** * If {@code true}, the field marked with this annotation is written out in the JSON while * serializing. If {@code false}, the field marked with this annotation is skipped from the * serialized output. Defaults to {@code true}. + * * @since 1.4 */ public boolean serialize() default true; /** - * If {@code true}, the field marked with this annotation is deserialized from the JSON. - * If {@code false}, the field marked with this annotation is skipped during deserialization. - * Defaults to {@code true}. + * If {@code true}, the field marked with this annotation is deserialized from the JSON. If {@code + * false}, the field marked with this annotation is skipped during deserialization. Defaults to + * {@code true}. + * * @since 1.4 */ public boolean deserialize() default true; diff --git a/gson/src/main/java/com/google/gson/annotations/JsonAdapter.java b/gson/src/main/java/com/google/gson/annotations/JsonAdapter.java index 5a5da72e13..6c8ef2fa71 100644 --- a/gson/src/main/java/com/google/gson/annotations/JsonAdapter.java +++ b/gson/src/main/java/com/google/gson/annotations/JsonAdapter.java @@ -29,10 +29,10 @@ import java.lang.annotation.Target; /** - * An annotation that indicates the Gson {@link TypeAdapter} to use with a class - * or field. + * An annotation that indicates the Gson {@link TypeAdapter} to use with a class or field. + * + *

Here is an example of how this annotation is used: * - *

Here is an example of how this annotation is used:

*
  * @JsonAdapter(UserJsonAdapter.class)
  * public class User {
@@ -68,6 +68,7 @@
  * annotation, it will automatically be invoked to serialize/deserialize {@code User} instances.
  *
  * 

Here is an example of how to apply this annotation to a field. + * *

  * private static final class Gadget {
  *   @JsonAdapter(UserJsonAdapter.class)
@@ -79,39 +80,34 @@
  * }
  * 
* - * It's possible to specify different type adapters on a field, that - * field's type, and in the {@link GsonBuilder}. Field annotations - * take precedence over {@code GsonBuilder}-registered type + * It's possible to specify different type adapters on a field, that field's type, and in the {@link + * GsonBuilder}. Field annotations take precedence over {@code GsonBuilder}-registered type * adapters, which in turn take precedence over annotated types. * - *

The class referenced by this annotation must be either a {@link - * TypeAdapter} or a {@link TypeAdapterFactory}, or must implement one - * or both of {@link JsonDeserializer} or {@link JsonSerializer}. - * Using {@link TypeAdapterFactory} makes it possible to delegate - * to the enclosing {@link Gson} instance. By default the specified - * adapter will not be called for {@code null} values; set {@link #nullSafe()} - * to {@code false} to let the adapter handle {@code null} values itself. + *

The class referenced by this annotation must be either a {@link TypeAdapter} or a {@link + * TypeAdapterFactory}, or must implement one or both of {@link JsonDeserializer} or {@link + * JsonSerializer}. Using {@link TypeAdapterFactory} makes it possible to delegate to the enclosing + * {@link Gson} instance. By default the specified adapter will not be called for {@code null} + * values; set {@link #nullSafe()} to {@code false} to let the adapter handle {@code null} values + * itself. + * + *

The type adapter is created in the same way Gson creates instances of custom classes during + * deserialization, that means: * - *

The type adapter is created in the same way Gson creates instances of - * custom classes during deserialization, that means: *

    - *
  1. If a custom {@link InstanceCreator} has been registered for the - * adapter class, it will be used to create the instance - *
  2. Otherwise, if the adapter class has a no-args constructor - * (regardless of which visibility), it will be invoked to create - * the instance - *
  3. Otherwise, JDK {@code Unsafe} will be used to create the instance; - * see {@link GsonBuilder#disableJdkUnsafe()} for the unexpected - * side-effects this might have + *
  4. If a custom {@link InstanceCreator} has been registered for the adapter class, it will be + * used to create the instance + *
  5. Otherwise, if the adapter class has a no-args constructor (regardless of which visibility), + * it will be invoked to create the instance + *
  6. Otherwise, JDK {@code Unsafe} will be used to create the instance; see {@link + * GsonBuilder#disableJdkUnsafe()} for the unexpected side-effects this might have *
* - *

{@code Gson} instances might cache the adapter they create for - * a {@code @JsonAdapter} annotation. It is not guaranteed that a new - * adapter is created every time the annotated class or field is serialized - * or deserialized. + *

{@code Gson} instances might cache the adapter they create for a {@code @JsonAdapter} + * annotation. It is not guaranteed that a new adapter is created every time the annotated class or + * field is serialized or deserialized. * * @since 2.3 - * * @author Inderjeet Singh * @author Joel Leitch * @author Jesse Wilson @@ -121,16 +117,19 @@ @Target({ElementType.TYPE, ElementType.FIELD}) public @interface JsonAdapter { - /** Either a {@link TypeAdapter} or {@link TypeAdapterFactory}, or one or both of {@link JsonDeserializer} or {@link JsonSerializer}. */ + /** + * Either a {@link TypeAdapter} or {@link TypeAdapterFactory}, or one or both of {@link + * JsonDeserializer} or {@link JsonSerializer}. + */ Class value(); /** - * Whether the adapter referenced by {@link #value()} should be made {@linkplain TypeAdapter#nullSafe() null-safe}. + * Whether the adapter referenced by {@link #value()} should be made {@linkplain + * TypeAdapter#nullSafe() null-safe}. * - *

If {@code true} (the default), it will be made null-safe and Gson will handle {@code null} Java objects - * on serialization and JSON {@code null} on deserialization without calling the adapter. If {@code false}, - * the adapter will have to handle the {@code null} values. + *

If {@code true} (the default), it will be made null-safe and Gson will handle {@code null} + * Java objects on serialization and JSON {@code null} on deserialization without calling the + * adapter. If {@code false}, the adapter will have to handle the {@code null} values. */ boolean nullSafe() default true; - } diff --git a/gson/src/main/java/com/google/gson/annotations/SerializedName.java b/gson/src/main/java/com/google/gson/annotations/SerializedName.java index 4ec3a4bbe2..91a61bc900 100644 --- a/gson/src/main/java/com/google/gson/annotations/SerializedName.java +++ b/gson/src/main/java/com/google/gson/annotations/SerializedName.java @@ -23,16 +23,17 @@ import java.lang.annotation.Target; /** - * An annotation that indicates this member should be serialized to JSON with - * the provided name value as its field name. + * An annotation that indicates this member should be serialized to JSON with the provided name + * value as its field name. * - *

This annotation will override any {@link com.google.gson.FieldNamingPolicy}, including - * the default field naming policy, that may have been set on the {@link com.google.gson.Gson} - * instance. A different naming policy can set using the {@code GsonBuilder} class. See - * {@link com.google.gson.GsonBuilder#setFieldNamingPolicy(com.google.gson.FieldNamingPolicy)} - * for more information.

+ *

This annotation will override any {@link com.google.gson.FieldNamingPolicy}, including the + * default field naming policy, that may have been set on the {@link com.google.gson.Gson} instance. + * A different naming policy can set using the {@code GsonBuilder} class. See {@link + * com.google.gson.GsonBuilder#setFieldNamingPolicy(com.google.gson.FieldNamingPolicy)} for more + * information. + * + *

Here is an example of how this annotation is meant to be used: * - *

Here is an example of how this annotation is meant to be used:

*
  * public class MyClass {
  *   @SerializedName("name") String a;
@@ -47,8 +48,9 @@
  * }
  * 
* - *

The following shows the output that is generated when serializing an instance of the - * above example class:

+ *

The following shows the output that is generated when serializing an instance of the above + * example class: + * *

  * MyClass target = new MyClass("v1", "v2", "v3");
  * Gson gson = new Gson();
@@ -59,9 +61,10 @@
  * {"name":"v1","name1":"v2","c":"v3"}
  * 
* - *

NOTE: The value you specify in this annotation must be a valid JSON field name.

- * While deserializing, all values specified in the annotation will be deserialized into the field. - * For example: + *

NOTE: The value you specify in this annotation must be a valid JSON field name. While + * deserializing, all values specified in the annotation will be deserialized into the field. For + * example: + * *

  *   MyClass target = gson.fromJson("{'name1':'v1'}", MyClass.class);
  *   assertEquals("v1", target.b);
@@ -70,10 +73,10 @@
  *   target = gson.fromJson("{'name3':'v3'}", MyClass.class);
  *   assertEquals("v3", target.b);
  * 
+ * * Note that MyClass.b is now deserialized from either name1, name2 or name3. * * @see com.google.gson.FieldNamingPolicy - * * @author Inderjeet Singh * @author Joel Leitch */ @@ -88,6 +91,7 @@ * @return the desired name of the field when it is serialized or deserialized */ String value(); + /** * The alternative names of the field when it is deserialized * diff --git a/gson/src/main/java/com/google/gson/annotations/Since.java b/gson/src/main/java/com/google/gson/annotations/Since.java index a7e51fc1c3..e363a58ba9 100644 --- a/gson/src/main/java/com/google/gson/annotations/Since.java +++ b/gson/src/main/java/com/google/gson/annotations/Since.java @@ -24,14 +24,14 @@ import java.lang.annotation.Target; /** - * An annotation that indicates the version number since a member or a type has been present. - * This annotation is useful to manage versioning of your JSON classes for a web-service. + * An annotation that indicates the version number since a member or a type has been present. This + * annotation is useful to manage versioning of your JSON classes for a web-service. * - *

- * This annotation has no effect unless you build {@link com.google.gson.Gson} with a - * {@code GsonBuilder} and invoke the {@link GsonBuilder#setVersion(double)} method. + *

This annotation has no effect unless you build {@link com.google.gson.Gson} with a {@code + * GsonBuilder} and invoke the {@link GsonBuilder#setVersion(double)} method. + * + *

Here is an example of how this annotation is meant to be used: * - *

Here is an example of how this annotation is meant to be used:

*
  * public class User {
  *   private String firstName;
@@ -44,9 +44,9 @@
  *
  * 

If you created Gson with {@code new Gson()}, the {@code toJson()} and {@code fromJson()} * methods will use all the fields for serialization and deserialization. However, if you created - * Gson with {@code Gson gson = new GsonBuilder().setVersion(1.0).create()} then the - * {@code toJson()} and {@code fromJson()} methods of Gson will exclude the {@code address} field - * since it's version number is set to {@code 1.1}.

+ * Gson with {@code Gson gson = new GsonBuilder().setVersion(1.0).create()} then the {@code + * toJson()} and {@code fromJson()} methods of Gson will exclude the {@code address} field since + * it's version number is set to {@code 1.1}. * * @author Inderjeet Singh * @author Joel Leitch @@ -58,8 +58,8 @@ @Target({ElementType.FIELD, ElementType.TYPE}) public @interface Since { /** - * The value indicating a version number since this member or type has been present. - * The number is inclusive; annotated elements will be included if {@code gsonVersion >= value}. + * The value indicating a version number since this member or type has been present. The number is + * inclusive; annotated elements will be included if {@code gsonVersion >= value}. */ double value(); } diff --git a/gson/src/main/java/com/google/gson/annotations/Until.java b/gson/src/main/java/com/google/gson/annotations/Until.java index 9fc28f40c0..9086f50ee7 100644 --- a/gson/src/main/java/com/google/gson/annotations/Until.java +++ b/gson/src/main/java/com/google/gson/annotations/Until.java @@ -25,15 +25,15 @@ /** * An annotation that indicates the version number until a member or a type should be present. - * Basically, if Gson is created with a version number that is equal to or exceeds the value - * stored in the {@code Until} annotation then the field will be ignored from the JSON output. - * This annotation is useful to manage versioning of your JSON classes for a web-service. + * Basically, if Gson is created with a version number that is equal to or exceeds the value stored + * in the {@code Until} annotation then the field will be ignored from the JSON output. This + * annotation is useful to manage versioning of your JSON classes for a web-service. * - *

- * This annotation has no effect unless you build {@link com.google.gson.Gson} with a - * {@code GsonBuilder} and invoke the {@link GsonBuilder#setVersion(double)} method. + *

This annotation has no effect unless you build {@link com.google.gson.Gson} with a {@code + * GsonBuilder} and invoke the {@link GsonBuilder#setVersion(double)} method. + * + *

Here is an example of how this annotation is meant to be used: * - *

Here is an example of how this annotation is meant to be used:

*
  * public class User {
  *   private String firstName;
@@ -45,11 +45,11 @@
  *
  * 

If you created Gson with {@code new Gson()}, the {@code toJson()} and {@code fromJson()} * methods will use all the fields for serialization and deserialization. However, if you created - * Gson with {@code Gson gson = new GsonBuilder().setVersion(1.2).create()} then the - * {@code toJson()} and {@code fromJson()} methods of Gson will exclude the {@code emailAddress} - * and {@code password} fields from the example above, because the version number passed to the - * GsonBuilder, {@code 1.2}, exceeds the version number set on the {@code Until} annotation, - * {@code 1.1}, for those fields. + * Gson with {@code Gson gson = new GsonBuilder().setVersion(1.2).create()} then the {@code + * toJson()} and {@code fromJson()} methods of Gson will exclude the {@code emailAddress} and {@code + * password} fields from the example above, because the version number passed to the GsonBuilder, + * {@code 1.2}, exceeds the version number set on the {@code Until} annotation, {@code 1.1}, for + * those fields. * * @author Inderjeet Singh * @author Joel Leitch @@ -63,8 +63,8 @@ public @interface Until { /** - * The value indicating a version number until this member or type should be included. - * The number is exclusive; annotated elements will be included if {@code gsonVersion < value}. + * The value indicating a version number until this member or type should be included. The number + * is exclusive; annotated elements will be included if {@code gsonVersion < value}. */ double value(); } diff --git a/gson/src/main/java/com/google/gson/annotations/package-info.java b/gson/src/main/java/com/google/gson/annotations/package-info.java index 8e272c0abc..4b380e6d33 100644 --- a/gson/src/main/java/com/google/gson/annotations/package-info.java +++ b/gson/src/main/java/com/google/gson/annotations/package-info.java @@ -16,7 +16,7 @@ /** * This package provides annotations that can be used with {@link com.google.gson.Gson}. - * + * * @author Inderjeet Singh, Joel Leitch */ package com.google.gson.annotations; diff --git a/gson/src/main/java/com/google/gson/internal/$Gson$Preconditions.java b/gson/src/main/java/com/google/gson/internal/$Gson$Preconditions.java index f1056efca9..ae39ba51d9 100644 --- a/gson/src/main/java/com/google/gson/internal/$Gson$Preconditions.java +++ b/gson/src/main/java/com/google/gson/internal/$Gson$Preconditions.java @@ -37,8 +37,8 @@ public final class $Gson$Preconditions { } /** - * @deprecated - * This is an internal Gson method. Use {@link Objects#requireNonNull(Object)} instead. + * @deprecated This is an internal Gson method. Use {@link Objects#requireNonNull(Object)} + * instead. */ // Only deprecated for now because external projects might be using this by accident @Deprecated diff --git a/gson/src/main/java/com/google/gson/internal/$Gson$Types.java b/gson/src/main/java/com/google/gson/internal/$Gson$Types.java index 94cd8b8b8c..8477be97c3 100644 --- a/gson/src/main/java/com/google/gson/internal/$Gson$Types.java +++ b/gson/src/main/java/com/google/gson/internal/$Gson$Types.java @@ -1,19 +1,16 @@ /** * Copyright (C) 2008 Google Inc. * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + *

Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + *

http://www.apache.org/licenses/LICENSE-2.0 * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and + *

Unless required by applicable law or agreed to in writing, software distributed under the + * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing permissions and * limitations under the License. */ - package com.google.gson.internal; import static com.google.gson.internal.$Gson$Preconditions.checkArgument; @@ -50,8 +47,8 @@ public final class $Gson$Types { } /** - * Returns a new parameterized type, applying {@code typeArguments} to - * {@code rawType} and enclosed by {@code ownerType}. + * Returns a new parameterized type, applying {@code typeArguments} to {@code rawType} and + * enclosed by {@code ownerType}. * * @return a {@link java.io.Serializable serializable} parameterized type. */ @@ -61,8 +58,7 @@ public static ParameterizedType newParameterizedTypeWithOwner( } /** - * Returns an array type whose elements are all instances of - * {@code componentType}. + * Returns an array type whose elements are all instances of {@code componentType}. * * @return a {@link java.io.Serializable serializable} generic array type. */ @@ -71,40 +67,38 @@ public static GenericArrayType arrayOf(Type componentType) { } /** - * Returns a type that represents an unknown type that extends {@code bound}. - * For example, if {@code bound} is {@code CharSequence.class}, this returns - * {@code ? extends CharSequence}. If {@code bound} is {@code Object.class}, - * this returns {@code ?}, which is shorthand for {@code ? extends Object}. + * Returns a type that represents an unknown type that extends {@code bound}. For example, if + * {@code bound} is {@code CharSequence.class}, this returns {@code ? extends CharSequence}. If + * {@code bound} is {@code Object.class}, this returns {@code ?}, which is shorthand for {@code ? + * extends Object}. */ public static WildcardType subtypeOf(Type bound) { Type[] upperBounds; if (bound instanceof WildcardType) { upperBounds = ((WildcardType) bound).getUpperBounds(); } else { - upperBounds = new Type[] { bound }; + upperBounds = new Type[] {bound}; } return new WildcardTypeImpl(upperBounds, EMPTY_TYPE_ARRAY); } /** - * Returns a type that represents an unknown supertype of {@code bound}. For - * example, if {@code bound} is {@code String.class}, this returns {@code ? - * super String}. + * Returns a type that represents an unknown supertype of {@code bound}. For example, if {@code + * bound} is {@code String.class}, this returns {@code ? super String}. */ public static WildcardType supertypeOf(Type bound) { Type[] lowerBounds; if (bound instanceof WildcardType) { lowerBounds = ((WildcardType) bound).getLowerBounds(); } else { - lowerBounds = new Type[] { bound }; + lowerBounds = new Type[] {bound}; } - return new WildcardTypeImpl(new Type[] { Object.class }, lowerBounds); + return new WildcardTypeImpl(new Type[] {Object.class}, lowerBounds); } /** - * Returns a type that is functionally equal but not necessarily equal - * according to {@link Object#equals(Object) Object.equals()}. The returned - * type is {@link java.io.Serializable}. + * Returns a type that is functionally equal but not necessarily equal according to {@link + * Object#equals(Object) Object.equals()}. The returned type is {@link java.io.Serializable}. */ public static Type canonicalize(Type type) { if (type instanceof Class) { @@ -113,8 +107,8 @@ public static Type canonicalize(Type type) { } else if (type instanceof ParameterizedType) { ParameterizedType p = (ParameterizedType) type; - return new ParameterizedTypeImpl(p.getOwnerType(), - p.getRawType(), p.getActualTypeArguments()); + return new ParameterizedTypeImpl( + p.getOwnerType(), p.getRawType(), p.getActualTypeArguments()); } else if (type instanceof GenericArrayType) { GenericArrayType g = (GenericArrayType) type; @@ -145,7 +139,7 @@ public static Class getRawType(Type type) { return (Class) rawType; } else if (type instanceof GenericArrayType) { - Type componentType = ((GenericArrayType)type).getGenericComponentType(); + Type componentType = ((GenericArrayType) type).getGenericComponentType(); return Array.newInstance(getRawType(componentType), 0).getClass(); } else if (type instanceof TypeVariable) { @@ -161,8 +155,12 @@ public static Class getRawType(Type type) { } else { String className = type == null ? "null" : type.getClass().getName(); - throw new IllegalArgumentException("Expected a Class, ParameterizedType, or " - + "GenericArrayType, but <" + type + "> is of type " + className); + throw new IllegalArgumentException( + "Expected a Class, ParameterizedType, or " + + "GenericArrayType, but <" + + type + + "> is of type " + + className); } } @@ -170,9 +168,7 @@ private static boolean equal(Object a, Object b) { return Objects.equals(a, b); } - /** - * Returns true if {@code a} and {@code b} are equal. - */ + /** Returns true if {@code a} and {@code b} are equal. */ public static boolean equals(Type a, Type b) { if (a == b) { // also handles (a == null && b == null) @@ -280,19 +276,23 @@ private static Type getGenericSupertype(Type context, Class rawType, Class */ private static Type getSupertype(Type context, Class contextRawType, Class supertype) { if (context instanceof WildcardType) { - // wildcards are useless for resolving supertypes. As the upper bound has the same raw type, use it instead - Type[] bounds = ((WildcardType)context).getUpperBounds(); + // Wildcards are useless for resolving supertypes. As the upper bound has the same raw type, + // use it instead + Type[] bounds = ((WildcardType) context).getUpperBounds(); // Currently the JLS only permits one bound for wildcards so using first bound is safe assert bounds.length == 1; context = bounds[0]; } checkArgument(supertype.isAssignableFrom(contextRawType)); - return resolve(context, contextRawType, + return resolve( + context, + contextRawType, $Gson$Types.getGenericSupertype(context, contextRawType, supertype)); } /** * Returns the component type of this array type. + * * @throws ClassCastException if this type is not an array. */ public static Type getArrayComponentType(Type array) { @@ -303,6 +303,7 @@ public static Type getArrayComponentType(Type array) { /** * Returns the element type of this collection type. + * * @throws IllegalArgumentException if this type is not a collection. */ public static Type getCollectionElementType(Type context, Class contextRawType) { @@ -315,8 +316,8 @@ public static Type getCollectionElementType(Type context, Class contextRawTyp } /** - * Returns a two element array containing this map's key and value types in - * positions 0 and 1 respectively. + * Returns a two element array containing this map's key and value types in positions 0 and 1 + * respectively. */ public static Type[] getMapKeyAndValueTypes(Type context, Class contextRawType) { /* @@ -325,7 +326,7 @@ public static Type[] getMapKeyAndValueTypes(Type context, Class contextRawTyp * extend Hashtable. */ if (context == Properties.class) { - return new Type[] { String.class, String.class }; // TODO: test subclasses of Properties! + return new Type[] {String.class, String.class}; // TODO: test subclasses of Properties! } Type mapType = getSupertype(context, contextRawType, Map.class); @@ -334,7 +335,7 @@ public static Type[] getMapKeyAndValueTypes(Type context, Class contextRawTyp ParameterizedType mapParameterizedType = (ParameterizedType) mapType; return mapParameterizedType.getActualTypeArguments(); } - return new Type[] { Object.class, Object.class }; + return new Type[] {Object.class, Object.class}; } public static Type resolve(Type context, Class contextRawType, Type toResolve) { @@ -342,8 +343,11 @@ public static Type resolve(Type context, Class contextRawType, Type toResolve return resolve(context, contextRawType, toResolve, new HashMap, Type>()); } - private static Type resolve(Type context, Class contextRawType, Type toResolve, - Map, Type> visitedTypeVariables) { + private static Type resolve( + Type context, + Class contextRawType, + Type toResolve, + Map, Type> visitedTypeVariables) { // this implementation is made a little more complicated in an attempt to avoid object-creation TypeVariable resolving = null; while (true) { @@ -369,19 +373,17 @@ private static Type resolve(Type context, Class contextRawType, Type toResolv } else if (toResolve instanceof Class && ((Class) toResolve).isArray()) { Class original = (Class) toResolve; Type componentType = original.getComponentType(); - Type newComponentType = resolve(context, contextRawType, componentType, visitedTypeVariables); - toResolve = equal(componentType, newComponentType) - ? original - : arrayOf(newComponentType); + Type newComponentType = + resolve(context, contextRawType, componentType, visitedTypeVariables); + toResolve = equal(componentType, newComponentType) ? original : arrayOf(newComponentType); break; } else if (toResolve instanceof GenericArrayType) { GenericArrayType original = (GenericArrayType) toResolve; Type componentType = original.getGenericComponentType(); - Type newComponentType = resolve(context, contextRawType, componentType, visitedTypeVariables); - toResolve = equal(componentType, newComponentType) - ? original - : arrayOf(newComponentType); + Type newComponentType = + resolve(context, contextRawType, componentType, visitedTypeVariables); + toResolve = equal(componentType, newComponentType) ? original : arrayOf(newComponentType); break; } else if (toResolve instanceof ParameterizedType) { @@ -392,7 +394,8 @@ private static Type resolve(Type context, Class contextRawType, Type toResolv Type[] args = original.getActualTypeArguments(); for (int t = 0, length = args.length; t < length; t++) { - Type resolvedTypeArgument = resolve(context, contextRawType, args[t], visitedTypeVariables); + Type resolvedTypeArgument = + resolve(context, contextRawType, args[t], visitedTypeVariables); if (!equal(resolvedTypeArgument, args[t])) { if (!changed) { args = args.clone(); @@ -402,9 +405,10 @@ private static Type resolve(Type context, Class contextRawType, Type toResolv } } - toResolve = changed - ? newParameterizedTypeWithOwner(newOwnerType, original.getRawType(), args) - : original; + toResolve = + changed + ? newParameterizedTypeWithOwner(newOwnerType, original.getRawType(), args) + : original; break; } else if (toResolve instanceof WildcardType) { @@ -413,13 +417,15 @@ private static Type resolve(Type context, Class contextRawType, Type toResolv Type[] originalUpperBound = original.getUpperBounds(); if (originalLowerBound.length == 1) { - Type lowerBound = resolve(context, contextRawType, originalLowerBound[0], visitedTypeVariables); + Type lowerBound = + resolve(context, contextRawType, originalLowerBound[0], visitedTypeVariables); if (lowerBound != originalLowerBound[0]) { toResolve = supertypeOf(lowerBound); break; } } else if (originalUpperBound.length == 1) { - Type upperBound = resolve(context, contextRawType, originalUpperBound[0], visitedTypeVariables); + Type upperBound = + resolve(context, contextRawType, originalUpperBound[0], visitedTypeVariables); if (upperBound != originalUpperBound[0]) { toResolve = subtypeOf(upperBound); break; @@ -439,7 +445,8 @@ private static Type resolve(Type context, Class contextRawType, Type toResolv return toResolve; } - private static Type resolveTypeVariable(Type context, Class contextRawType, TypeVariable unknown) { + private static Type resolveTypeVariable( + Type context, Class contextRawType, TypeVariable unknown) { Class declaredByRaw = declaringClassOf(unknown); // we can't reduce this further @@ -471,9 +478,7 @@ private static int indexOf(Object[] array, Object toFind) { */ private static Class declaringClassOf(TypeVariable typeVariable) { GenericDeclaration genericDeclaration = typeVariable.getGenericDeclaration(); - return genericDeclaration instanceof Class - ? (Class) genericDeclaration - : null; + return genericDeclaration instanceof Class ? (Class) genericDeclaration : null; } static void checkNotPrimitive(Type type) { @@ -503,8 +508,10 @@ public static boolean requiresOwnerType(Type rawType) { private static final class ParameterizedTypeImpl implements ParameterizedType, Serializable { @SuppressWarnings("serial") private final Type ownerType; + @SuppressWarnings("serial") private final Type rawType; + @SuppressWarnings("serial") private final Type[] typeArguments; @@ -526,19 +533,23 @@ public ParameterizedTypeImpl(Type ownerType, Type rawType, Type... typeArguments } } - @Override public Type[] getActualTypeArguments() { + @Override + public Type[] getActualTypeArguments() { return typeArguments.clone(); } - @Override public Type getRawType() { + @Override + public Type getRawType() { return rawType; } - @Override public Type getOwnerType() { + @Override + public Type getOwnerType() { return ownerType; } - @Override public boolean equals(Object other) { + @Override + public boolean equals(Object other) { return other instanceof ParameterizedType && $Gson$Types.equals(this, (ParameterizedType) other); } @@ -547,20 +558,23 @@ private static int hashCodeOrZero(Object o) { return o != null ? o.hashCode() : 0; } - @Override public int hashCode() { - return Arrays.hashCode(typeArguments) - ^ rawType.hashCode() - ^ hashCodeOrZero(ownerType); + @Override + public int hashCode() { + return Arrays.hashCode(typeArguments) ^ rawType.hashCode() ^ hashCodeOrZero(ownerType); } - @Override public String toString() { + @Override + public String toString() { int length = typeArguments.length; if (length == 0) { return typeToString(rawType); } StringBuilder stringBuilder = new StringBuilder(30 * (length + 1)); - stringBuilder.append(typeToString(rawType)).append("<").append(typeToString(typeArguments[0])); + stringBuilder + .append(typeToString(rawType)) + .append("<") + .append(typeToString(typeArguments[0])); for (int i = 1; i < length; i++) { stringBuilder.append(", ").append(typeToString(typeArguments[i])); } @@ -579,20 +593,23 @@ public GenericArrayTypeImpl(Type componentType) { this.componentType = canonicalize(componentType); } - @Override public Type getGenericComponentType() { + @Override + public Type getGenericComponentType() { return componentType; } - @Override public boolean equals(Object o) { - return o instanceof GenericArrayType - && $Gson$Types.equals(this, (GenericArrayType) o); + @Override + public boolean equals(Object o) { + return o instanceof GenericArrayType && $Gson$Types.equals(this, (GenericArrayType) o); } - @Override public int hashCode() { + @Override + public int hashCode() { return componentType.hashCode(); } - @Override public String toString() { + @Override + public String toString() { return typeToString(componentType) + "[]"; } @@ -600,14 +617,15 @@ public GenericArrayTypeImpl(Type componentType) { } /** - * The WildcardType interface supports multiple upper bounds and multiple - * lower bounds. We only support what the target Java version supports - at most one - * bound, see also https://bugs.openjdk.java.net/browse/JDK-8250660. If a lower bound - * is set, the upper bound must be Object.class. + * The WildcardType interface supports multiple upper bounds and multiple lower bounds. We only + * support what the target Java version supports - at most one bound, see also + * https://bugs.openjdk.java.net/browse/JDK-8250660. If a lower bound is set, the upper bound must + * be Object.class. */ private static final class WildcardTypeImpl implements WildcardType, Serializable { @SuppressWarnings("serial") private final Type upperBound; + @SuppressWarnings("serial") private final Type lowerBound; @@ -630,26 +648,29 @@ public WildcardTypeImpl(Type[] upperBounds, Type[] lowerBounds) { } } - @Override public Type[] getUpperBounds() { - return new Type[] { upperBound }; + @Override + public Type[] getUpperBounds() { + return new Type[] {upperBound}; } - @Override public Type[] getLowerBounds() { - return lowerBound != null ? new Type[] { lowerBound } : EMPTY_TYPE_ARRAY; + @Override + public Type[] getLowerBounds() { + return lowerBound != null ? new Type[] {lowerBound} : EMPTY_TYPE_ARRAY; } - @Override public boolean equals(Object other) { - return other instanceof WildcardType - && $Gson$Types.equals(this, (WildcardType) other); + @Override + public boolean equals(Object other) { + return other instanceof WildcardType && $Gson$Types.equals(this, (WildcardType) other); } - @Override public int hashCode() { + @Override + public int hashCode() { // this equals Arrays.hashCode(getLowerBounds()) ^ Arrays.hashCode(getUpperBounds()); - return (lowerBound != null ? 31 + lowerBound.hashCode() : 1) - ^ (31 + upperBound.hashCode()); + return (lowerBound != null ? 31 + lowerBound.hashCode() : 1) ^ (31 + upperBound.hashCode()); } - @Override public String toString() { + @Override + public String toString() { if (lowerBound != null) { return "? super " + typeToString(lowerBound); } else if (upperBound == Object.class) { diff --git a/gson/src/main/java/com/google/gson/internal/ConstructorConstructor.java b/gson/src/main/java/com/google/gson/internal/ConstructorConstructor.java index 0f488a9bb9..65ac857f56 100644 --- a/gson/src/main/java/com/google/gson/internal/ConstructorConstructor.java +++ b/gson/src/main/java/com/google/gson/internal/ConstructorConstructor.java @@ -47,23 +47,25 @@ import java.util.concurrent.ConcurrentNavigableMap; import java.util.concurrent.ConcurrentSkipListMap; -/** - * Returns a function that can construct an instance of a requested type. - */ +/** Returns a function that can construct an instance of a requested type. */ public final class ConstructorConstructor { private final Map> instanceCreators; private final boolean useJdkUnsafe; private final List reflectionFilters; - public ConstructorConstructor(Map> instanceCreators, boolean useJdkUnsafe, List reflectionFilters) { + public ConstructorConstructor( + Map> instanceCreators, + boolean useJdkUnsafe, + List reflectionFilters) { this.instanceCreators = instanceCreators; this.useJdkUnsafe = useJdkUnsafe; this.reflectionFilters = reflectionFilters; } /** - * Check if the class can be instantiated by Unsafe allocator. If the instance has interface or abstract modifiers - * return an exception message. + * Check if the class can be instantiated by Unsafe allocator. If the instance has interface or + * abstract modifiers return an exception message. + * * @param c instance of the class to be checked * @return if instantiable {@code null}, else a non-{@code null} exception message */ @@ -71,7 +73,8 @@ static String checkInstantiable(Class c) { int modifiers = c.getModifiers(); if (Modifier.isInterface(modifiers)) { return "Interfaces can't be instantiated! Register an InstanceCreator" - + " or a TypeAdapter for this type. Interface name: " + c.getName(); + + " or a TypeAdapter for this type. Interface name: " + + c.getName(); } if (Modifier.isAbstract(modifiers)) { // R8 performs aggressive optimizations where it removes the default constructor of a class @@ -83,8 +86,10 @@ static String checkInstantiable(Class c) { * still making the class abstract */ return "Abstract classes can't be instantiated! Adjust the R8 configuration or register" - + " an InstanceCreator or a TypeAdapter for this type. Class name: " + c.getName() - + "\nSee " + TroubleshootingGuide.createUrl("r8-abstract-class"); + + " an InstanceCreator or a TypeAdapter for this type. Class name: " + + c.getName() + + "\nSee " + + TroubleshootingGuide.createUrl("r8-abstract-class"); } return null; } @@ -99,7 +104,8 @@ public ObjectConstructor get(TypeToken typeToken) { final InstanceCreator typeCreator = (InstanceCreator) instanceCreators.get(type); if (typeCreator != null) { return new ObjectConstructor() { - @Override public T construct() { + @Override + public T construct() { return typeCreator.createInstance(type); } }; @@ -107,11 +113,11 @@ public ObjectConstructor get(TypeToken typeToken) { // Next try raw type match for instance creators @SuppressWarnings("unchecked") // types must agree - final InstanceCreator rawTypeCreator = - (InstanceCreator) instanceCreators.get(rawType); + final InstanceCreator rawTypeCreator = (InstanceCreator) instanceCreators.get(rawType); if (rawTypeCreator != null) { return new ObjectConstructor() { - @Override public T construct() { + @Override + public T construct() { return rawTypeCreator.createInstance(type); } }; @@ -125,7 +131,8 @@ public ObjectConstructor get(TypeToken typeToken) { return specialConstructor; } - FilterResult filterResult = ReflectionAccessFilterHelper.getFilterResult(reflectionFilters, rawType); + FilterResult filterResult = + ReflectionAccessFilterHelper.getFilterResult(reflectionFilters, rawType); ObjectConstructor defaultConstructor = newDefaultConstructor(rawType, filterResult); if (defaultConstructor != null) { return defaultConstructor; @@ -141,7 +148,8 @@ public ObjectConstructor get(TypeToken typeToken) { final String exceptionMessage = checkInstantiable(rawType); if (exceptionMessage != null) { return new ObjectConstructor() { - @Override public T construct() { + @Override + public T construct() { throw new JsonIOException(exceptionMessage); } }; @@ -153,11 +161,15 @@ public ObjectConstructor get(TypeToken typeToken) { // finally try unsafe return newUnsafeAllocator(rawType); } else { - final String message = "Unable to create instance of " + rawType + "; ReflectionAccessFilter" - + " does not permit using reflection or Unsafe. Register an InstanceCreator or a TypeAdapter" - + " for this type or adjust the access filter to allow using reflection."; + final String message = + "Unable to create instance of " + + rawType + + "; ReflectionAccessFilter does not permit using reflection or Unsafe. Register an" + + " InstanceCreator or a TypeAdapter for this type or adjust the access filter to" + + " allow using reflection."; return new ObjectConstructor() { - @Override public T construct() { + @Override + public T construct() { throw new JsonIOException(message); } }; @@ -165,17 +177,20 @@ public ObjectConstructor get(TypeToken typeToken) { } /** - * Creates constructors for special JDK collection types which do not have a public no-args constructor. + * Creates constructors for special JDK collection types which do not have a public no-args + * constructor. */ - private static ObjectConstructor newSpecialCollectionConstructor(final Type type, Class rawType) { + private static ObjectConstructor newSpecialCollectionConstructor( + final Type type, Class rawType) { if (EnumSet.class.isAssignableFrom(rawType)) { return new ObjectConstructor() { - @Override public T construct() { + @Override + public T construct() { if (type instanceof ParameterizedType) { Type elementType = ((ParameterizedType) type).getActualTypeArguments()[0]; if (elementType instanceof Class) { @SuppressWarnings({"unchecked", "rawtypes"}) - T set = (T) EnumSet.noneOf((Class)elementType); + T set = (T) EnumSet.noneOf((Class) elementType); return set; } else { throw new JsonIOException("Invalid EnumSet type: " + type.toString()); @@ -190,7 +205,8 @@ private static ObjectConstructor newSpecialCollectionConstructor(final Ty // and constructor parameter might have completely different meaning else if (rawType == EnumMap.class) { return new ObjectConstructor() { - @Override public T construct() { + @Override + public T construct() { if (type instanceof ParameterizedType) { Type elementType = ((ParameterizedType) type).getActualTypeArguments()[0]; if (elementType instanceof Class) { @@ -210,7 +226,8 @@ else if (rawType == EnumMap.class) { return null; } - private static ObjectConstructor newDefaultConstructor(Class rawType, FilterResult filterResult) { + private static ObjectConstructor newDefaultConstructor( + Class rawType, FilterResult filterResult) { // Cannot invoke constructor of abstract class if (Modifier.isAbstract(rawType.getModifiers())) { return null; @@ -223,17 +240,25 @@ private static ObjectConstructor newDefaultConstructor(Class r return null; } - boolean canAccess = filterResult == FilterResult.ALLOW || (ReflectionAccessFilterHelper.canAccess(constructor, null) - // Be a bit more lenient here for BLOCK_ALL; if constructor is accessible and public then allow calling it - && (filterResult != FilterResult.BLOCK_ALL || Modifier.isPublic(constructor.getModifiers()))); + boolean canAccess = + filterResult == FilterResult.ALLOW + || (ReflectionAccessFilterHelper.canAccess(constructor, null) + // Be a bit more lenient here for BLOCK_ALL; if constructor is accessible and public + // then allow calling it + && (filterResult != FilterResult.BLOCK_ALL + || Modifier.isPublic(constructor.getModifiers()))); if (!canAccess) { - final String message = "Unable to invoke no-args constructor of " + rawType + ";" - + " constructor is not accessible and ReflectionAccessFilter does not permit making" - + " it accessible. Register an InstanceCreator or a TypeAdapter for this type, change" - + " the visibility of the constructor or adjust the access filter."; + final String message = + "Unable to invoke no-args constructor of " + + rawType + + ";" + + " constructor is not accessible and ReflectionAccessFilter does not permit making" + + " it accessible. Register an InstanceCreator or a TypeAdapter for this type, change" + + " the visibility of the constructor or adjust the access filter."; return new ObjectConstructor() { - @Override public T construct() { + @Override + public T construct() { throw new JsonIOException(message); } }; @@ -265,22 +290,31 @@ public T construct() { } return new ObjectConstructor() { - @Override public T construct() { + @Override + public T construct() { try { @SuppressWarnings("unchecked") // T is the same raw type as is requested T newInstance = (T) constructor.newInstance(); return newInstance; } - // Note: InstantiationException should be impossible because check at start of method made sure - // that class is not abstract + // Note: InstantiationException should be impossible because check at start of method made + // sure that class is not abstract catch (InstantiationException e) { - throw new RuntimeException("Failed to invoke constructor '" + ReflectionHelper.constructorToString(constructor) + "'" - + " with no args", e); + throw new RuntimeException( + "Failed to invoke constructor '" + + ReflectionHelper.constructorToString(constructor) + + "'" + + " with no args", + e); } catch (InvocationTargetException e) { // TODO: don't wrap if cause is unchecked? // TODO: JsonParseException ? - throw new RuntimeException("Failed to invoke constructor '" + ReflectionHelper.constructorToString(constructor) + "'" - + " with no args", e.getCause()); + throw new RuntimeException( + "Failed to invoke constructor '" + + ReflectionHelper.constructorToString(constructor) + + "'" + + " with no args", + e.getCause()); } catch (IllegalAccessException e) { throw ReflectionHelper.createExceptionForUnexpectedIllegalAccess(e); } @@ -288,10 +322,7 @@ public T construct() { }; } - /** - * Constructors for common interface types like Map and List and their - * subtypes. - */ + /** Constructors for common interface types like Map and List and their subtypes. */ @SuppressWarnings("unchecked") // use runtime checks to guarantee that 'T' is what it is private static ObjectConstructor newDefaultImplementationConstructor( final Type type, Class rawType) { @@ -307,25 +338,29 @@ private static ObjectConstructor newDefaultImplementationConstructor( if (Collection.class.isAssignableFrom(rawType)) { if (SortedSet.class.isAssignableFrom(rawType)) { return new ObjectConstructor() { - @Override public T construct() { + @Override + public T construct() { return (T) new TreeSet<>(); } }; } else if (Set.class.isAssignableFrom(rawType)) { return new ObjectConstructor() { - @Override public T construct() { + @Override + public T construct() { return (T) new LinkedHashSet<>(); } }; } else if (Queue.class.isAssignableFrom(rawType)) { return new ObjectConstructor() { - @Override public T construct() { + @Override + public T construct() { return (T) new ArrayDeque<>(); } }; } else { return new ObjectConstructor() { - @Override public T construct() { + @Override + public T construct() { return (T) new ArrayList<>(); } }; @@ -335,32 +370,38 @@ private static ObjectConstructor newDefaultImplementationConstructor( if (Map.class.isAssignableFrom(rawType)) { if (ConcurrentNavigableMap.class.isAssignableFrom(rawType)) { return new ObjectConstructor() { - @Override public T construct() { + @Override + public T construct() { return (T) new ConcurrentSkipListMap<>(); } }; } else if (ConcurrentMap.class.isAssignableFrom(rawType)) { return new ObjectConstructor() { - @Override public T construct() { + @Override + public T construct() { return (T) new ConcurrentHashMap<>(); } }; } else if (SortedMap.class.isAssignableFrom(rawType)) { return new ObjectConstructor() { - @Override public T construct() { + @Override + public T construct() { return (T) new TreeMap<>(); } }; - } else if (type instanceof ParameterizedType && !String.class.isAssignableFrom( - TypeToken.get(((ParameterizedType) type).getActualTypeArguments()[0]).getRawType())) { + } else if (type instanceof ParameterizedType + && !String.class.isAssignableFrom( + TypeToken.get(((ParameterizedType) type).getActualTypeArguments()[0]).getRawType())) { return new ObjectConstructor() { - @Override public T construct() { + @Override + public T construct() { return (T) new LinkedHashMap<>(); } }; } else { return new ObjectConstructor() { - @Override public T construct() { + @Override + public T construct() { return (T) new LinkedTreeMap<>(); } }; @@ -373,41 +414,52 @@ private static ObjectConstructor newDefaultImplementationConstructor( private ObjectConstructor newUnsafeAllocator(final Class rawType) { if (useJdkUnsafe) { return new ObjectConstructor() { - @Override public T construct() { + @Override + public T construct() { try { @SuppressWarnings("unchecked") T newInstance = (T) UnsafeAllocator.INSTANCE.newInstance(rawType); return newInstance; } catch (Exception e) { - throw new RuntimeException(("Unable to create instance of " + rawType + "." - + " Registering an InstanceCreator or a TypeAdapter for this type, or adding a no-args" - + " constructor may fix this problem."), e); + throw new RuntimeException( + ("Unable to create instance of " + + rawType + + ". Registering an InstanceCreator or a TypeAdapter for this type, or adding a" + + " no-args constructor may fix this problem."), + e); } } }; } else { - String exceptionMessage = "Unable to create instance of " + rawType + "; usage of JDK Unsafe" - + " is disabled. Registering an InstanceCreator or a TypeAdapter for this type, adding a no-args" - + " constructor, or enabling usage of JDK Unsafe may fix this problem."; + String exceptionMessage = + "Unable to create instance of " + + rawType + + "; usage of JDK Unsafe is disabled. Registering an InstanceCreator or a TypeAdapter" + + " for this type, adding a no-args constructor, or enabling usage of JDK Unsafe may" + + " fix this problem."; // Check if R8 removed all constructors if (rawType.getDeclaredConstructors().length == 0) { - // R8 with Unsafe disabled might not be common enough to warrant a separate Troubleshooting Guide entry - exceptionMessage += " Or adjust your R8 configuration to keep the no-args constructor of the class."; + // R8 with Unsafe disabled might not be common enough to warrant a separate Troubleshooting + // Guide entry + exceptionMessage += + " Or adjust your R8 configuration to keep the no-args constructor of the class."; } // Explicit final variable to allow usage in the anonymous class below final String exceptionMessageF = exceptionMessage; return new ObjectConstructor() { - @Override public T construct() { + @Override + public T construct() { throw new JsonIOException(exceptionMessageF); } }; } } - @Override public String toString() { + @Override + public String toString() { return instanceCreators.toString(); } } diff --git a/gson/src/main/java/com/google/gson/internal/Excluder.java b/gson/src/main/java/com/google/gson/internal/Excluder.java index dd167b4838..dea3b851d7 100644 --- a/gson/src/main/java/com/google/gson/internal/Excluder.java +++ b/gson/src/main/java/com/google/gson/internal/Excluder.java @@ -35,14 +35,12 @@ import java.util.List; /** - * This class selects which fields and types to omit. It is configurable, - * supporting version attributes {@link Since} and {@link Until}, modifiers, - * synthetic fields, anonymous and local classes, inner classes, and fields with - * the {@link Expose} annotation. + * This class selects which fields and types to omit. It is configurable, supporting version + * attributes {@link Since} and {@link Until}, modifiers, synthetic fields, anonymous and local + * classes, inner classes, and fields with the {@link Expose} annotation. * - *

This class is a type adapter factory; types that are excluded will be - * adapted to null. It may delegate to another type adapter if only one - * direction is excluded. + *

This class is a type adapter factory; types that are excluded will be adapted to null. It may + * delegate to another type adapter if only one direction is excluded. * * @author Joel Leitch * @author Jesse Wilson @@ -58,7 +56,8 @@ public final class Excluder implements TypeAdapterFactory, Cloneable { private List serializationStrategies = Collections.emptyList(); private List deserializationStrategies = Collections.emptyList(); - @Override protected Excluder clone() { + @Override + protected Excluder clone() { try { return (Excluder) super.clone(); } catch (CloneNotSupportedException e) { @@ -93,8 +92,8 @@ public Excluder excludeFieldsWithoutExposeAnnotation() { return result; } - public Excluder withExclusionStrategy(ExclusionStrategy exclusionStrategy, - boolean serialization, boolean deserialization) { + public Excluder withExclusionStrategy( + ExclusionStrategy exclusionStrategy, boolean serialization, boolean deserialization) { Excluder result = clone(); if (serialization) { result.serializationStrategies = new ArrayList<>(serializationStrategies); @@ -107,12 +106,13 @@ public Excluder withExclusionStrategy(ExclusionStrategy exclusionStrategy, return result; } - @Override public TypeAdapter create(final Gson gson, final TypeToken type) { + @Override + public TypeAdapter create(final Gson gson, final TypeToken type) { Class rawType = type.getRawType(); boolean excludeClass = excludeClassChecks(rawType); final boolean skipSerialize = excludeClass || excludeClassInStrategy(rawType, true); - final boolean skipDeserialize = excludeClass || excludeClassInStrategy(rawType, false); + final boolean skipDeserialize = excludeClass || excludeClassInStrategy(rawType, false); if (!skipSerialize && !skipDeserialize) { return null; @@ -122,7 +122,8 @@ public Excluder withExclusionStrategy(ExclusionStrategy exclusionStrategy, /** The delegate is lazily created because it may not be needed, and creating it may fail. */ private TypeAdapter delegate; - @Override public T read(JsonReader in) throws IOException { + @Override + public T read(JsonReader in) throws IOException { if (skipDeserialize) { in.skipValue(); return null; @@ -130,7 +131,8 @@ public Excluder withExclusionStrategy(ExclusionStrategy exclusionStrategy, return delegate().read(in); } - @Override public void write(JsonWriter out, T value) throws IOException { + @Override + public void write(JsonWriter out, T value) throws IOException { if (skipSerialize) { out.nullValue(); return; @@ -140,9 +142,7 @@ public Excluder withExclusionStrategy(ExclusionStrategy exclusionStrategy, private TypeAdapter delegate() { TypeAdapter d = delegate; - return d != null - ? d - : (delegate = gson.getDelegateAdapter(Excluder.this, type)); + return d != null ? d : (delegate = gson.getDelegateAdapter(Excluder.this, type)); } }; } @@ -190,34 +190,35 @@ public boolean excludeField(Field field, boolean serialize) { } private boolean excludeClassChecks(Class clazz) { - if (version != Excluder.IGNORE_VERSIONS && !isValidVersion(clazz.getAnnotation(Since.class), clazz.getAnnotation(Until.class))) { - return true; - } + if (version != Excluder.IGNORE_VERSIONS + && !isValidVersion(clazz.getAnnotation(Since.class), clazz.getAnnotation(Until.class))) { + return true; + } - if (!serializeInnerClasses && isInnerClass(clazz)) { - return true; - } + if (!serializeInnerClasses && isInnerClass(clazz)) { + return true; + } - return isAnonymousOrNonStaticLocal(clazz); + return isAnonymousOrNonStaticLocal(clazz); } public boolean excludeClass(Class clazz, boolean serialize) { - return excludeClassChecks(clazz) || - excludeClassInStrategy(clazz, serialize); + return excludeClassChecks(clazz) || excludeClassInStrategy(clazz, serialize); } private boolean excludeClassInStrategy(Class clazz, boolean serialize) { - List list = serialize ? serializationStrategies : deserializationStrategies; - for (ExclusionStrategy exclusionStrategy : list) { - if (exclusionStrategy.shouldSkipClass(clazz)) { - return true; - } + List list = serialize ? serializationStrategies : deserializationStrategies; + for (ExclusionStrategy exclusionStrategy : list) { + if (exclusionStrategy.shouldSkipClass(clazz)) { + return true; } - return false; + } + return false; } private boolean isAnonymousOrNonStaticLocal(Class clazz) { - return !Enum.class.isAssignableFrom(clazz) && !isStatic(clazz) + return !Enum.class.isAssignableFrom(clazz) + && !isStatic(clazz) && (clazz.isAnonymousClass() || clazz.isLocalClass()); } diff --git a/gson/src/main/java/com/google/gson/internal/JavaVersion.java b/gson/src/main/java/com/google/gson/internal/JavaVersion.java index 21130ff9d0..d14ccee506 100644 --- a/gson/src/main/java/com/google/gson/internal/JavaVersion.java +++ b/gson/src/main/java/com/google/gson/internal/JavaVersion.java @@ -16,12 +16,12 @@ package com.google.gson.internal; -/** - * Utility to check the major Java version of the current JVM. - */ +/** Utility to check the major Java version of the current JVM. */ public final class JavaVersion { - // Oracle defines naming conventions at http://www.oracle.com/technetwork/java/javase/versioning-naming-139433.html - // However, many alternate implementations differ. For example, Debian used 9-debian as the version string + // Oracle defines naming conventions at + // http://www.oracle.com/technetwork/java/javase/versioning-naming-139433.html + // However, many alternate implementations differ. For example, Debian used 9-debian as the + // version string private static final int majorJavaVersion = determineMajorJavaVersion(); @@ -37,12 +37,12 @@ static int getMajorJavaVersion(String javaVersion) { version = extractBeginningInt(javaVersion); } if (version == -1) { - return 6; // Choose minimum supported JDK version as default + return 6; // Choose a minimum supported JDK version as default } return version; } - // Parses both legacy 1.8 style and newer 9.0.4 style + // Parses both legacy 1.8 style and newer 9.0.4 style private static int parseDotted(String javaVersion) { try { String[] parts = javaVersion.split("[._]", 3); @@ -86,11 +86,12 @@ public static int getMajorJavaVersion() { /** * Gets a boolean value depending if the application is running on Java 9 or later * - * @return {@code true} if the application is running on Java 9 or later; and {@code false} otherwise. + * @return {@code true} if the application is running on Java 9 or later; and {@code false} + * otherwise. */ public static boolean isJava9OrLater() { return majorJavaVersion >= 9; } - private JavaVersion() { } + private JavaVersion() {} } diff --git a/gson/src/main/java/com/google/gson/internal/JsonReaderInternalAccess.java b/gson/src/main/java/com/google/gson/internal/JsonReaderInternalAccess.java index bbd472040a..776b7639df 100644 --- a/gson/src/main/java/com/google/gson/internal/JsonReaderInternalAccess.java +++ b/gson/src/main/java/com/google/gson/internal/JsonReaderInternalAccess.java @@ -19,14 +19,10 @@ import com.google.gson.stream.JsonReader; import java.io.IOException; -/** - * Internal-only APIs of JsonReader available only to other classes in Gson. - */ +/** Internal-only APIs of JsonReader available only to other classes in Gson. */ public abstract class JsonReaderInternalAccess { public static JsonReaderInternalAccess INSTANCE; - /** - * Changes the type of the current property name token to a string value. - */ + /** Changes the type of the current property name token to a string value. */ public abstract void promoteNameToValue(JsonReader reader) throws IOException; } diff --git a/gson/src/main/java/com/google/gson/internal/LazilyParsedNumber.java b/gson/src/main/java/com/google/gson/internal/LazilyParsedNumber.java index 3358b485fe..dd9f9b9b57 100644 --- a/gson/src/main/java/com/google/gson/internal/LazilyParsedNumber.java +++ b/gson/src/main/java/com/google/gson/internal/LazilyParsedNumber.java @@ -30,7 +30,9 @@ public final class LazilyParsedNumber extends Number { private final String value; - /** @param value must not be null */ + /** + * @param value must not be null + */ public LazilyParsedNumber(String value) { this.value = value; } @@ -77,16 +79,16 @@ public String toString() { } /** - * If somebody is unlucky enough to have to serialize one of these, serialize - * it as a BigDecimal so that they won't need Gson on the other side to - * deserialize it. + * If somebody is unlucky enough to have to serialize one of these, serialize it as a BigDecimal + * so that they won't need Gson on the other side to deserialize it. */ private Object writeReplace() throws ObjectStreamException { return asBigDecimal(); } private void readObject(ObjectInputStream in) throws IOException { - // Don't permit directly deserializing this class; writeReplace() should have written a replacement + // Don't permit directly deserializing this class; writeReplace() should have written a + // replacement throw new InvalidObjectException("Deserialization is unsupported"); } diff --git a/gson/src/main/java/com/google/gson/internal/LinkedTreeMap.java b/gson/src/main/java/com/google/gson/internal/LinkedTreeMap.java index 6e004d770c..5058b3667b 100644 --- a/gson/src/main/java/com/google/gson/internal/LinkedTreeMap.java +++ b/gson/src/main/java/com/google/gson/internal/LinkedTreeMap.java @@ -30,24 +30,26 @@ import java.util.Iterator; import java.util.LinkedHashMap; import java.util.NoSuchElementException; -import java.util.Set; import java.util.Objects; +import java.util.Set; /** - * A map of comparable keys to values. Unlike {@code TreeMap}, this class uses - * insertion order for iteration order. Comparison order is only used as an - * optimization for efficient insertion and removal. + * A map of comparable keys to values. Unlike {@code TreeMap}, this class uses insertion order for + * iteration order. Comparison order is only used as an optimization for efficient insertion and + * removal. * *

This implementation was derived from Android 4.1's TreeMap class. */ @SuppressWarnings("serial") // ignore warning about missing serialVersionUID public final class LinkedTreeMap extends AbstractMap implements Serializable { - @SuppressWarnings({ "unchecked", "rawtypes" }) // to avoid Comparable>> - private static final Comparator NATURAL_ORDER = new Comparator() { - @Override public int compare(Comparable a, Comparable b) { - return a.compareTo(b); - } - }; + @SuppressWarnings({"unchecked", "rawtypes"}) // to avoid Comparable>> + private static final Comparator NATURAL_ORDER = + new Comparator() { + @Override + public int compare(Comparable a, Comparable b) { + return a.compareTo(b); + } + }; private final Comparator comparator; private final boolean allowNullValues; @@ -59,8 +61,8 @@ public final class LinkedTreeMap extends AbstractMap implements Seri final Node header; /** - * Create a natural order, empty tree map whose keys must be mutually - * comparable and non-null, and whose values can be {@code null}. + * Create a natural order, empty tree map whose keys must be mutually comparable and non-null, and + * whose values can be {@code null}. */ @SuppressWarnings("unchecked") // unsafe! this assumes K is comparable public LinkedTreeMap() { @@ -68,8 +70,7 @@ public LinkedTreeMap() { } /** - * Create a natural order, empty tree map whose keys must be mutually - * comparable and non-null. + * Create a natural order, empty tree map whose keys must be mutually comparable and non-null. * * @param allowNullValues whether {@code null} is allowed as entry value */ @@ -79,37 +80,40 @@ public LinkedTreeMap(boolean allowNullValues) { } /** - * Create a tree map ordered by {@code comparator}. This map's keys may only - * be null if {@code comparator} permits. + * Create a tree map ordered by {@code comparator}. This map's keys may only be null if {@code + * comparator} permits. * - * @param comparator the comparator to order elements with, or {@code null} to - * use the natural ordering. + * @param comparator the comparator to order elements with, or {@code null} to use the natural + * ordering. * @param allowNullValues whether {@code null} is allowed as entry value */ - @SuppressWarnings({ "unchecked", "rawtypes" }) // unsafe! if comparator is null, this assumes K is comparable + // unsafe! if comparator is null, this assumes K is comparable + @SuppressWarnings({"unchecked", "rawtypes"}) public LinkedTreeMap(Comparator comparator, boolean allowNullValues) { - this.comparator = comparator != null - ? comparator - : (Comparator) NATURAL_ORDER; + this.comparator = comparator != null ? comparator : (Comparator) NATURAL_ORDER; this.allowNullValues = allowNullValues; this.header = new Node<>(allowNullValues); } - @Override public int size() { + @Override + public int size() { return size; } - @Override public V get(Object key) { + @Override + public V get(Object key) { Node node = findByObject(key); return node != null ? node.value : null; } - @Override public boolean containsKey(Object key) { + @Override + public boolean containsKey(Object key) { return findByObject(key) != null; } @CanIgnoreReturnValue - @Override public V put(K key, V value) { + @Override + public V put(K key, V value) { if (key == null) { throw new NullPointerException("key == null"); } @@ -122,7 +126,8 @@ public LinkedTreeMap(Comparator comparator, boolean allowNullValues) return result; } - @Override public void clear() { + @Override + public void clear() { root = null; size = 0; modCount++; @@ -132,7 +137,8 @@ public LinkedTreeMap(Comparator comparator, boolean allowNullValues) header.next = header.prev = header; } - @Override public V remove(Object key) { + @Override + public V remove(Object key) { Node node = removeInternalByKey(key); return node != null ? node.value : null; } @@ -140,8 +146,7 @@ public LinkedTreeMap(Comparator comparator, boolean allowNullValues) /** * Returns the node at or adjacent to the given key, creating it if requested. * - * @throws ClassCastException if {@code key} and the tree's keys aren't - * mutually comparable. + * @throws ClassCastException if {@code key} and the tree's keys aren't mutually comparable. */ Node find(K key, boolean create) { Comparator comparator = this.comparator; @@ -151,14 +156,14 @@ Node find(K key, boolean create) { if (nearest != null) { // Micro-optimization: avoid polymorphic calls to Comparator.compare(). @SuppressWarnings("unchecked") // Throws a ClassCastException below if there's trouble. - Comparable comparableKey = (comparator == NATURAL_ORDER) - ? (Comparable) key - : null; + Comparable comparableKey = + (comparator == NATURAL_ORDER) ? (Comparable) key : null; while (true) { - comparison = (comparableKey != null) - ? comparableKey.compareTo(nearest.key) - : comparator.compare(key, nearest.key); + comparison = + (comparableKey != null) + ? comparableKey.compareTo(nearest.key) + : comparator.compare(key, nearest.key); // We found the requested key. if (comparison == 0) { @@ -215,13 +220,12 @@ Node findByObject(Object key) { } /** - * Returns this map's entry that has the same key and value as {@code - * entry}, or null if this map has no such entry. + * Returns this map's entry that has the same key and value as {@code entry}, or null if this map + * has no such entry. * - *

This method uses the comparator for key equality rather than {@code - * equals}. If this map's comparator isn't consistent with equals (such as - * {@code String.CASE_INSENSITIVE_ORDER}), then {@code remove()} and {@code - * contains()} will violate the collections API. + *

This method uses the comparator for key equality rather than {@code equals}. If this map's + * comparator isn't consistent with equals (such as {@code String.CASE_INSENSITIVE_ORDER}), then + * {@code remove()} and {@code contains()} will violate the collections API. */ Node findByEntry(Entry entry) { Node mine = findByObject(entry.getKey()); @@ -234,8 +238,7 @@ private boolean equal(Object a, Object b) { } /** - * Removes {@code node} from this tree, rearranging the tree's structure as - * necessary. + * Removes {@code node} from this tree, rearranging the tree's structure as necessary. * * @param unlink true to also unlink this node from the iteration linked list. */ @@ -327,11 +330,10 @@ private void replaceInParent(Node node, Node replacement) { } /** - * Rebalances the tree by making any AVL rotations necessary between the - * newly-unbalanced node and the tree's root. + * Rebalances the tree by making any AVL rotations necessary between the newly-unbalanced node and + * the tree's root. * - * @param insert true if the node was unbalanced by an insert; false if it - * was by a removal. + * @param insert true if the node was unbalanced by an insert; false if it was by a removal. */ private void rebalance(Node unbalanced, boolean insert) { for (Node node = unbalanced; node != null; node = node.parent) { @@ -393,9 +395,7 @@ private void rebalance(Node unbalanced, boolean insert) { } } - /** - * Rotates the subtree so that its root's right child is the new root. - */ + /** Rotates the subtree so that its root's right child is the new root. */ private void rotateLeft(Node root) { Node left = root.left; Node pivot = root.right; @@ -415,15 +415,12 @@ private void rotateLeft(Node root) { root.parent = pivot; // fix heights - root.height = Math.max(left != null ? left.height : 0, - pivotLeft != null ? pivotLeft.height : 0) + 1; - pivot.height = Math.max(root.height, - pivotRight != null ? pivotRight.height : 0) + 1; + root.height = + Math.max(left != null ? left.height : 0, pivotLeft != null ? pivotLeft.height : 0) + 1; + pivot.height = Math.max(root.height, pivotRight != null ? pivotRight.height : 0) + 1; } - /** - * Rotates the subtree so that its root's left child is the new root. - */ + /** Rotates the subtree so that its root's left child is the new root. */ private void rotateRight(Node root) { Node pivot = root.left; Node right = root.right; @@ -443,21 +440,22 @@ private void rotateRight(Node root) { root.parent = pivot; // fixup heights - root.height = Math.max(right != null ? right.height : 0, - pivotRight != null ? pivotRight.height : 0) + 1; - pivot.height = Math.max(root.height, - pivotLeft != null ? pivotLeft.height : 0) + 1; + root.height = + Math.max(right != null ? right.height : 0, pivotRight != null ? pivotRight.height : 0) + 1; + pivot.height = Math.max(root.height, pivotLeft != null ? pivotLeft.height : 0) + 1; } private EntrySet entrySet; private KeySet keySet; - @Override public Set> entrySet() { + @Override + public Set> entrySet() { EntrySet result = entrySet; return result != null ? result : (entrySet = new EntrySet()); } - @Override public Set keySet() { + @Override + public Set keySet() { KeySet result = keySet; return result != null ? result : (keySet = new KeySet()); } @@ -492,15 +490,18 @@ static final class Node implements Entry { next.prev = this; } - @Override public K getKey() { + @Override + public K getKey() { return key; } - @Override public V getValue() { + @Override + public V getValue() { return value; } - @Override public V setValue(V value) { + @Override + public V setValue(V value) { if (value == null && !allowNullValue) { throw new NullPointerException("value == null"); } @@ -509,7 +510,8 @@ static final class Node implements Entry { return oldValue; } - @Override public boolean equals(Object o) { + @Override + public boolean equals(Object o) { if (o instanceof Entry) { Entry other = (Entry) o; return (key == null ? other.getKey() == null : key.equals(other.getKey())) @@ -518,18 +520,17 @@ static final class Node implements Entry { return false; } - @Override public int hashCode() { - return (key == null ? 0 : key.hashCode()) - ^ (value == null ? 0 : value.hashCode()); + @Override + public int hashCode() { + return (key == null ? 0 : key.hashCode()) ^ (value == null ? 0 : value.hashCode()); } - @Override public String toString() { + @Override + public String toString() { return key + "=" + value; } - /** - * Returns the first node in this subtree. - */ + /** Returns the first node in this subtree. */ public Node first() { Node node = this; Node child = node.left; @@ -540,9 +541,7 @@ public Node first() { return node; } - /** - * Returns the last node in this subtree. - */ + /** Returns the last node in this subtree. */ public Node last() { Node node = this; Node child = node.right; @@ -559,8 +558,7 @@ private abstract class LinkedTreeMapIterator implements Iterator { Node lastReturned = null; int expectedModCount = modCount; - LinkedTreeMapIterator() { - } + LinkedTreeMapIterator() {} @Override @SuppressWarnings("ReferenceEquality") @@ -581,7 +579,8 @@ final Node nextNode() { return lastReturned = e; } - @Override public final void remove() { + @Override + public final void remove() { if (lastReturned == null) { throw new IllegalStateException(); } @@ -592,23 +591,28 @@ final Node nextNode() { } class EntrySet extends AbstractSet> { - @Override public int size() { + @Override + public int size() { return size; } - @Override public Iterator> iterator() { + @Override + public Iterator> iterator() { return new LinkedTreeMapIterator>() { - @Override public Entry next() { + @Override + public Entry next() { return nextNode(); } }; } - @Override public boolean contains(Object o) { + @Override + public boolean contains(Object o) { return o instanceof Entry && findByEntry((Entry) o) != null; } - @Override public boolean remove(Object o) { + @Override + public boolean remove(Object o) { if (!(o instanceof Entry)) { return false; } @@ -621,49 +625,56 @@ class EntrySet extends AbstractSet> { return true; } - @Override public void clear() { + @Override + public void clear() { LinkedTreeMap.this.clear(); } } final class KeySet extends AbstractSet { - @Override public int size() { + @Override + public int size() { return size; } - @Override public Iterator iterator() { + @Override + public Iterator iterator() { return new LinkedTreeMapIterator() { - @Override public K next() { + @Override + public K next() { return nextNode().key; } }; } - @Override public boolean contains(Object o) { + @Override + public boolean contains(Object o) { return containsKey(o); } - @Override public boolean remove(Object key) { + @Override + public boolean remove(Object key) { return removeInternalByKey(key) != null; } - @Override public void clear() { + @Override + public void clear() { LinkedTreeMap.this.clear(); } } /** - * If somebody is unlucky enough to have to serialize one of these, serialize - * it as a LinkedHashMap so that they won't need Gson on the other side to - * deserialize it. Using serialization defeats our DoS defence, so most apps - * shouldn't use it. + * If somebody is unlucky enough to have to serialize one of these, serialize it as a + * LinkedHashMap so that they won't need Gson on the other side to deserialize it. Using + * serialization defeats our DoS defence, so most apps shouldn't use it. */ private Object writeReplace() throws ObjectStreamException { return new LinkedHashMap<>(this); } private void readObject(ObjectInputStream in) throws IOException { - // Don't permit directly deserializing this class; writeReplace() should have written a replacement + // Don't permit directly deserializing this class; writeReplace() should have written a + // replacement throw new InvalidObjectException("Deserialization is unsupported"); } } diff --git a/gson/src/main/java/com/google/gson/internal/NonNullElementWrapperList.java b/gson/src/main/java/com/google/gson/internal/NonNullElementWrapperList.java index 57f2d96ed2..294bf0a9e5 100644 --- a/gson/src/main/java/com/google/gson/internal/NonNullElementWrapperList.java +++ b/gson/src/main/java/com/google/gson/internal/NonNullElementWrapperList.java @@ -24,10 +24,9 @@ import java.util.RandomAccess; /** - * {@link List} which wraps another {@code List} but prevents insertion of - * {@code null} elements. Methods which only perform checks with the element - * argument (e.g. {@link #contains(Object)}) do not throw exceptions for - * {@code null} arguments. + * {@link List} which wraps another {@code List} but prevents insertion of {@code null} elements. + * Methods which only perform checks with the element argument (e.g. {@link #contains(Object)}) do + * not throw exceptions for {@code null} arguments. */ public class NonNullElementWrapperList extends AbstractList implements RandomAccess { // Explicitly specify ArrayList as type to guarantee that delegate implements RandomAccess @@ -38,11 +37,13 @@ public NonNullElementWrapperList(ArrayList delegate) { this.delegate = Objects.requireNonNull(delegate); } - @Override public E get(int index) { + @Override + public E get(int index) { return delegate.get(index); } - @Override public int size() { + @Override + public int size() { return delegate.size(); } @@ -53,61 +54,75 @@ private E nonNull(E element) { return element; } - @Override public E set(int index, E element) { + @Override + public E set(int index, E element) { return delegate.set(index, nonNull(element)); } - @Override public void add(int index, E element) { + @Override + public void add(int index, E element) { delegate.add(index, nonNull(element)); } - @Override public E remove(int index) { + @Override + public E remove(int index) { return delegate.remove(index); } /* The following methods are overridden because their default implementation is inefficient */ - @Override public void clear() { + @Override + public void clear() { delegate.clear(); } - @Override public boolean remove(Object o) { + @Override + public boolean remove(Object o) { return delegate.remove(o); } - @Override public boolean removeAll(Collection c) { + @Override + public boolean removeAll(Collection c) { return delegate.removeAll(c); } - @Override public boolean retainAll(Collection c) { + @Override + public boolean retainAll(Collection c) { return delegate.retainAll(c); } - @Override public boolean contains(Object o) { + @Override + public boolean contains(Object o) { return delegate.contains(o); } - @Override public int indexOf(Object o) { + @Override + public int indexOf(Object o) { return delegate.indexOf(o); } - @Override public int lastIndexOf(Object o) { + @Override + public int lastIndexOf(Object o) { return delegate.lastIndexOf(o); } - @Override public Object[] toArray() { + @Override + public Object[] toArray() { return delegate.toArray(); } - @Override public T[] toArray(T[] a) { + @Override + public T[] toArray(T[] a) { return delegate.toArray(a); } - @Override public boolean equals(Object o) { + @Override + public boolean equals(Object o) { return delegate.equals(o); } - @Override public int hashCode() { + @Override + public int hashCode() { return delegate.hashCode(); } diff --git a/gson/src/main/java/com/google/gson/internal/NumberLimits.java b/gson/src/main/java/com/google/gson/internal/NumberLimits.java index 09c349c6d8..8d6c0981e9 100644 --- a/gson/src/main/java/com/google/gson/internal/NumberLimits.java +++ b/gson/src/main/java/com/google/gson/internal/NumberLimits.java @@ -4,12 +4,11 @@ import java.math.BigInteger; /** - * This class enforces limits on numbers parsed from JSON to avoid potential performance - * problems when extremely large numbers are used. + * This class enforces limits on numbers parsed from JSON to avoid potential performance problems + * when extremely large numbers are used. */ public class NumberLimits { - private NumberLimits() { - } + private NumberLimits() {} private static final int MAX_NUMBER_STRING_LENGTH = 10_000; diff --git a/gson/src/main/java/com/google/gson/internal/ObjectConstructor.java b/gson/src/main/java/com/google/gson/internal/ObjectConstructor.java index 6ef20607fe..30f3b1b778 100644 --- a/gson/src/main/java/com/google/gson/internal/ObjectConstructor.java +++ b/gson/src/main/java/com/google/gson/internal/ObjectConstructor.java @@ -17,17 +17,15 @@ package com.google.gson.internal; /** - * Defines a generic object construction factory. The purpose of this class - * is to construct a default instance of a class that can be used for object - * navigation while deserialization from its JSON representation. + * Defines a generic object construction factory. The purpose of this class is to construct a + * default instance of a class that can be used for object navigation while deserialization from its + * JSON representation. * * @author Inderjeet Singh * @author Joel Leitch */ public interface ObjectConstructor { - /** - * Returns a new instance. - */ + /** Returns a new instance. */ public T construct(); -} \ No newline at end of file +} diff --git a/gson/src/main/java/com/google/gson/internal/PreJava9DateFormatProvider.java b/gson/src/main/java/com/google/gson/internal/PreJava9DateFormatProvider.java index beb527c9ee..02367d9ee5 100644 --- a/gson/src/main/java/com/google/gson/internal/PreJava9DateFormatProvider.java +++ b/gson/src/main/java/com/google/gson/internal/PreJava9DateFormatProvider.java @@ -19,68 +19,68 @@ import java.text.SimpleDateFormat; import java.util.Locale; -/** - * Provides DateFormats for US locale with patterns which were the default ones before Java 9. - */ +/** Provides DateFormats for US locale with patterns which were the default ones before Java 9. */ public class PreJava9DateFormatProvider { /** - * Returns the same DateFormat as {@code DateFormat.getDateInstance(style, Locale.US)} in Java 8 or below. + * Returns the same DateFormat as {@code DateFormat.getDateInstance(style, Locale.US)} in Java 8 + * or below. */ public static DateFormat getUSDateFormat(int style) { return new SimpleDateFormat(getDateFormatPattern(style), Locale.US); } /** - * Returns the same DateFormat as {@code DateFormat.getDateTimeInstance(dateStyle, timeStyle, Locale.US)} - * in Java 8 or below. + * Returns the same DateFormat as {@code DateFormat.getDateTimeInstance(dateStyle, timeStyle, + * Locale.US)} in Java 8 or below. */ public static DateFormat getUSDateTimeFormat(int dateStyle, int timeStyle) { - String pattern = getDatePartOfDateTimePattern(dateStyle) + " " + getTimePartOfDateTimePattern(timeStyle); + String pattern = + getDatePartOfDateTimePattern(dateStyle) + " " + getTimePartOfDateTimePattern(timeStyle); return new SimpleDateFormat(pattern, Locale.US); } private static String getDateFormatPattern(int style) { switch (style) { - case DateFormat.SHORT: - return "M/d/yy"; - case DateFormat.MEDIUM: - return "MMM d, y"; - case DateFormat.LONG: - return "MMMM d, y"; - case DateFormat.FULL: - return "EEEE, MMMM d, y"; - default: - throw new IllegalArgumentException("Unknown DateFormat style: " + style); + case DateFormat.SHORT: + return "M/d/yy"; + case DateFormat.MEDIUM: + return "MMM d, y"; + case DateFormat.LONG: + return "MMMM d, y"; + case DateFormat.FULL: + return "EEEE, MMMM d, y"; + default: + throw new IllegalArgumentException("Unknown DateFormat style: " + style); } } private static String getDatePartOfDateTimePattern(int dateStyle) { switch (dateStyle) { - case DateFormat.SHORT: - return "M/d/yy"; - case DateFormat.MEDIUM: - return "MMM d, yyyy"; - case DateFormat.LONG: - return "MMMM d, yyyy"; - case DateFormat.FULL: - return "EEEE, MMMM d, yyyy"; - default: - throw new IllegalArgumentException("Unknown DateFormat style: " + dateStyle); + case DateFormat.SHORT: + return "M/d/yy"; + case DateFormat.MEDIUM: + return "MMM d, yyyy"; + case DateFormat.LONG: + return "MMMM d, yyyy"; + case DateFormat.FULL: + return "EEEE, MMMM d, yyyy"; + default: + throw new IllegalArgumentException("Unknown DateFormat style: " + dateStyle); } } private static String getTimePartOfDateTimePattern(int timeStyle) { switch (timeStyle) { - case DateFormat.SHORT: - return "h:mm a"; - case DateFormat.MEDIUM: - return "h:mm:ss a"; - case DateFormat.FULL: - case DateFormat.LONG: - return "h:mm:ss a z"; - default: - throw new IllegalArgumentException("Unknown DateFormat style: " + timeStyle); + case DateFormat.SHORT: + return "h:mm a"; + case DateFormat.MEDIUM: + return "h:mm:ss a"; + case DateFormat.FULL: + case DateFormat.LONG: + return "h:mm:ss a z"; + default: + throw new IllegalArgumentException("Unknown DateFormat style: " + timeStyle); } } } diff --git a/gson/src/main/java/com/google/gson/internal/Primitives.java b/gson/src/main/java/com/google/gson/internal/Primitives.java index 2506fd0a53..47b0e0eedf 100644 --- a/gson/src/main/java/com/google/gson/internal/Primitives.java +++ b/gson/src/main/java/com/google/gson/internal/Primitives.java @@ -19,24 +19,22 @@ import java.lang.reflect.Type; /** - * Contains static utility methods pertaining to primitive types and their - * corresponding wrapper types. + * Contains static utility methods pertaining to primitive types and their corresponding wrapper + * types. * * @author Kevin Bourrillion */ public final class Primitives { private Primitives() {} - /** - * Returns true if this type is a primitive. - */ + /** Returns true if this type is a primitive. */ public static boolean isPrimitive(Type type) { return type instanceof Class && ((Class) type).isPrimitive(); } /** - * Returns {@code true} if {@code type} is one of the nine - * primitive-wrapper types, such as {@link Integer}. + * Returns {@code true} if {@code type} is one of the nine primitive-wrapper types, such as {@link + * Integer}. * * @see Class#isPrimitive */ @@ -53,8 +51,9 @@ public static boolean isWrapperType(Type type) { } /** - * Returns the corresponding wrapper type of {@code type} if it is a primitive - * type; otherwise returns {@code type} itself. Idempotent. + * Returns the corresponding wrapper type of {@code type} if it is a primitive type; otherwise + * returns {@code type} itself. Idempotent. + * *

    *     wrap(int.class) == Integer.class
    *     wrap(Integer.class) == Integer.class
@@ -76,8 +75,9 @@ public static  Class wrap(Class type) {
   }
 
   /**
-   * Returns the corresponding primitive type of {@code type} if it is a
-   * wrapper type; otherwise returns {@code type} itself. Idempotent.
+   * Returns the corresponding primitive type of {@code type} if it is a wrapper type; otherwise
+   * returns {@code type} itself. Idempotent.
+   *
    * 
    *     unwrap(Integer.class) == int.class
    *     unwrap(int.class) == int.class
diff --git a/gson/src/main/java/com/google/gson/internal/ReflectionAccessFilterHelper.java b/gson/src/main/java/com/google/gson/internal/ReflectionAccessFilterHelper.java
index dc31b3ada7..4f66ee0799 100644
--- a/gson/src/main/java/com/google/gson/internal/ReflectionAccessFilterHelper.java
+++ b/gson/src/main/java/com/google/gson/internal/ReflectionAccessFilterHelper.java
@@ -16,21 +16,19 @@
 
 package com.google.gson.internal;
 
+import com.google.gson.ReflectionAccessFilter;
+import com.google.gson.ReflectionAccessFilter.FilterResult;
 import java.lang.reflect.AccessibleObject;
 import java.lang.reflect.Method;
 import java.util.List;
 
-import com.google.gson.ReflectionAccessFilter;
-import com.google.gson.ReflectionAccessFilter.FilterResult;
-
-/**
- * Internal helper class for {@link ReflectionAccessFilter}.
- */
+/** Internal helper class for {@link ReflectionAccessFilter}. */
 public class ReflectionAccessFilterHelper {
-  private ReflectionAccessFilterHelper() { }
+  private ReflectionAccessFilterHelper() {}
 
   // Platform type detection is based on Moshi's Util.isPlatformType(Class)
-  // See https://github.com/square/moshi/blob/3c108919ee1cce88a433ffda04eeeddc0341eae7/moshi/src/main/java/com/squareup/moshi/internal/Util.java#L141
+  // See
+  // https://github.com/square/moshi/blob/3c108919ee1cce88a433ffda04eeeddc0341eae7/moshi/src/main/java/com/squareup/moshi/internal/Util.java#L141
 
   public static boolean isJavaType(Class c) {
     return isJavaType(c.getName());
@@ -53,17 +51,18 @@ private static boolean isAndroidType(String className) {
   public static boolean isAnyPlatformType(Class c) {
     String className = c.getName();
     return isAndroidType(className) // Covers Android and Java
-      || className.startsWith("kotlin.")
-      || className.startsWith("kotlinx.")
-      || className.startsWith("scala.");
+        || className.startsWith("kotlin.")
+        || className.startsWith("kotlinx.")
+        || className.startsWith("scala.");
   }
 
   /**
-   * Gets the result of applying all filters until the first one returns a result
-   * other than {@link FilterResult#INDECISIVE}, or {@link FilterResult#ALLOW} if
-   * the list of filters is empty or all returned {@code INDECISIVE}.
+   * Gets the result of applying all filters until the first one returns a result other than {@link
+   * FilterResult#INDECISIVE}, or {@link FilterResult#ALLOW} if the list of filters is empty or all
+   * returned {@code INDECISIVE}.
    */
-  public static FilterResult getFilterResult(List reflectionFilters, Class c) {
+  public static FilterResult getFilterResult(
+      List reflectionFilters, Class c) {
     for (ReflectionAccessFilter filter : reflectionFilters) {
       FilterResult result = filter.check(c);
       if (result != FilterResult.INDECISIVE) {
@@ -73,42 +72,46 @@ public static FilterResult getFilterResult(List reflecti
     return FilterResult.ALLOW;
   }
 
-  /**
-   * See {@link AccessibleObject#canAccess(Object)} (Java >= 9)
-   */
+  /** See {@link AccessibleObject#canAccess(Object)} (Java >= 9) */
   public static boolean canAccess(AccessibleObject accessibleObject, Object object) {
     return AccessChecker.INSTANCE.canAccess(accessibleObject, object);
   }
 
-  private static abstract class AccessChecker {
+  private abstract static class AccessChecker {
     public static final AccessChecker INSTANCE;
+
     static {
       AccessChecker accessChecker = null;
       // TODO: Ideally should use Multi-Release JAR for this version specific code
       if (JavaVersion.isJava9OrLater()) {
         try {
-          final Method canAccessMethod = AccessibleObject.class.getDeclaredMethod("canAccess", Object.class);
-          accessChecker = new AccessChecker() {
-            @Override public boolean canAccess(AccessibleObject accessibleObject, Object object) {
-              try {
-                return (Boolean) canAccessMethod.invoke(accessibleObject, object);
-              } catch (Exception e) {
-                throw new RuntimeException("Failed invoking canAccess", e);
-              }
-            }
-          };
+          final Method canAccessMethod =
+              AccessibleObject.class.getDeclaredMethod("canAccess", Object.class);
+          accessChecker =
+              new AccessChecker() {
+                @Override
+                public boolean canAccess(AccessibleObject accessibleObject, Object object) {
+                  try {
+                    return (Boolean) canAccessMethod.invoke(accessibleObject, object);
+                  } catch (Exception e) {
+                    throw new RuntimeException("Failed invoking canAccess", e);
+                  }
+                }
+              };
         } catch (NoSuchMethodException ignored) {
           // OK: will assume everything is accessible
         }
       }
 
       if (accessChecker == null) {
-        accessChecker = new AccessChecker() {
-          @Override public boolean canAccess(AccessibleObject accessibleObject, Object object) {
-            // Cannot determine whether object can be accessed, so assume it can be accessed
-            return true;
-          }
-        };
+        accessChecker =
+            new AccessChecker() {
+              @Override
+              public boolean canAccess(AccessibleObject accessibleObject, Object object) {
+                // Cannot determine whether object can be accessed, so assume it can be accessed
+                return true;
+              }
+            };
       }
       INSTANCE = accessChecker;
     }
diff --git a/gson/src/main/java/com/google/gson/internal/Streams.java b/gson/src/main/java/com/google/gson/internal/Streams.java
index e08bbea387..2435522f85 100644
--- a/gson/src/main/java/com/google/gson/internal/Streams.java
+++ b/gson/src/main/java/com/google/gson/internal/Streams.java
@@ -31,17 +31,13 @@
 import java.io.Writer;
 import java.util.Objects;
 
-/**
- * Reads and writes GSON parse trees over streams.
- */
+/** Reads and writes GSON parse trees over streams. */
 public final class Streams {
   private Streams() {
     throw new UnsupportedOperationException();
   }
 
-  /**
-   * Takes a reader in any state and returns the next value as a JsonElement.
-   */
+  /** Takes a reader in any state and returns the next value as a JsonElement. */
   public static JsonElement parse(JsonReader reader) throws JsonParseException {
     boolean isEmpty = true;
     try {
@@ -67,9 +63,7 @@ public static JsonElement parse(JsonReader reader) throws JsonParseException {
     }
   }
 
-  /**
-   * Writes the JSON element to the writer, recursively.
-   */
+  /** Writes the JSON element to the writer, recursively. */
   public static void write(JsonElement element, JsonWriter writer) throws IOException {
     TypeAdapters.JSON_ELEMENT.write(writer, element);
   }
@@ -78,10 +72,7 @@ public static Writer writerForAppendable(Appendable appendable) {
     return appendable instanceof Writer ? (Writer) appendable : new AppendableWriter(appendable);
   }
 
-  /**
-   * Adapts an {@link Appendable} so it can be passed anywhere a {@link Writer}
-   * is used.
-   */
+  /** Adapts an {@link Appendable} so it can be passed anywhere a {@link Writer} is used. */
   private static final class AppendableWriter extends Writer {
     private final Appendable appendable;
     private final CurrentWrite currentWrite = new CurrentWrite();
@@ -90,40 +81,46 @@ private static final class AppendableWriter extends Writer {
       this.appendable = appendable;
     }
 
-    @Override public void write(char[] chars, int offset, int length) throws IOException {
+    @Override
+    public void write(char[] chars, int offset, int length) throws IOException {
       currentWrite.setChars(chars);
       appendable.append(currentWrite, offset, offset + length);
     }
 
-    @Override public void flush() {}
-    @Override public void close() {}
+    @Override
+    public void flush() {}
+
+    @Override
+    public void close() {}
 
     // Override these methods for better performance
     // They would otherwise unnecessarily create Strings or char arrays
 
-    @Override public void write(int i) throws IOException {
+    @Override
+    public void write(int i) throws IOException {
       appendable.append((char) i);
     }
 
-    @Override public void write(String str, int off, int len) throws IOException {
+    @Override
+    public void write(String str, int off, int len) throws IOException {
       // Appendable.append turns null -> "null", which is not desired here
       Objects.requireNonNull(str);
       appendable.append(str, off, off + len);
     }
 
-    @Override public Writer append(CharSequence csq) throws IOException {
+    @Override
+    public Writer append(CharSequence csq) throws IOException {
       appendable.append(csq);
       return this;
     }
 
-    @Override public Writer append(CharSequence csq, int start, int end) throws IOException {
+    @Override
+    public Writer append(CharSequence csq, int start, int end) throws IOException {
       appendable.append(csq, start, end);
       return this;
     }
 
-    /**
-     * A mutable char sequence pointing at a single char[].
-     */
+    /** A mutable char sequence pointing at a single char[]. */
     private static class CurrentWrite implements CharSequence {
       private char[] chars;
       private String cachedString;
@@ -133,18 +130,24 @@ void setChars(char[] chars) {
         this.cachedString = null;
       }
 
-      @Override public int length() {
+      @Override
+      public int length() {
         return chars.length;
       }
-      @Override public char charAt(int i) {
+
+      @Override
+      public char charAt(int i) {
         return chars[i];
       }
-      @Override public CharSequence subSequence(int start, int end) {
+
+      @Override
+      public CharSequence subSequence(int start, int end) {
         return new String(chars, start, end - start);
       }
 
       // Must return string representation to satisfy toString() contract
-      @Override public String toString() {
+      @Override
+      public String toString() {
         if (cachedString == null) {
           cachedString = new String(chars);
         }
diff --git a/gson/src/main/java/com/google/gson/internal/TroubleshootingGuide.java b/gson/src/main/java/com/google/gson/internal/TroubleshootingGuide.java
index d0fbc1a45b..0782693b6a 100644
--- a/gson/src/main/java/com/google/gson/internal/TroubleshootingGuide.java
+++ b/gson/src/main/java/com/google/gson/internal/TroubleshootingGuide.java
@@ -3,9 +3,7 @@
 public class TroubleshootingGuide {
   private TroubleshootingGuide() {}
 
-  /**
-   * Creates a URL referring to the specified troubleshooting section.
-   */
+  /** Creates a URL referring to the specified troubleshooting section. */
   public static String createUrl(String id) {
     return "https://github.com/google/gson/blob/main/Troubleshooting.md#" + id;
   }
diff --git a/gson/src/main/java/com/google/gson/internal/UnsafeAllocator.java b/gson/src/main/java/com/google/gson/internal/UnsafeAllocator.java
index 9099051356..8e18e92f77 100644
--- a/gson/src/main/java/com/google/gson/internal/UnsafeAllocator.java
+++ b/gson/src/main/java/com/google/gson/internal/UnsafeAllocator.java
@@ -31,14 +31,15 @@ public abstract class UnsafeAllocator {
   public abstract  T newInstance(Class c) throws Exception;
 
   /**
-   * Asserts that the class is instantiable. This check should have already occurred
-   * in {@link ConstructorConstructor}; this check here acts as safeguard since trying
-   * to use Unsafe for non-instantiable classes might crash the JVM on some devices.
+   * Asserts that the class is instantiable. This check should have already occurred in {@link
+   * ConstructorConstructor}; this check here acts as safeguard since trying to use Unsafe for
+   * non-instantiable classes might crash the JVM on some devices.
    */
   private static void assertInstantiable(Class c) {
     String exceptionMessage = ConstructorConstructor.checkInstantiable(c);
     if (exceptionMessage != null) {
-      throw new AssertionError("UnsafeAllocator is used for non-instantiable type: " + exceptionMessage);
+      throw new AssertionError(
+          "UnsafeAllocator is used for non-instantiable type: " + exceptionMessage);
     }
   }
 
@@ -73,12 +74,12 @@ public  T newInstance(Class c) throws Exception {
     //   private static native Object newInstance(Class instantiationClass, int methodId);
     // }
     try {
-      Method getConstructorId = ObjectStreamClass.class
-          .getDeclaredMethod("getConstructorId", Class.class);
+      Method getConstructorId =
+          ObjectStreamClass.class.getDeclaredMethod("getConstructorId", Class.class);
       getConstructorId.setAccessible(true);
       final int constructorId = (Integer) getConstructorId.invoke(null, Object.class);
-      final Method newInstance = ObjectStreamClass.class
-          .getDeclaredMethod("newInstance", Class.class, int.class);
+      final Method newInstance =
+          ObjectStreamClass.class.getDeclaredMethod("newInstance", Class.class, int.class);
       newInstance.setAccessible(true);
       return new UnsafeAllocator() {
         @Override
@@ -98,8 +99,8 @@ public  T newInstance(Class c) throws Exception {
     //     Class instantiationClass, Class constructorClass);
     // }
     try {
-      final Method newInstance = ObjectInputStream.class
-          .getDeclaredMethod("newInstance", Class.class, Class.class);
+      final Method newInstance =
+          ObjectInputStream.class.getDeclaredMethod("newInstance", Class.class, Class.class);
       newInstance.setAccessible(true);
       return new UnsafeAllocator() {
         @Override
@@ -117,8 +118,11 @@ public  T newInstance(Class c) throws Exception {
     return new UnsafeAllocator() {
       @Override
       public  T newInstance(Class c) {
-        throw new UnsupportedOperationException("Cannot allocate " + c + ". Usage of JDK sun.misc.Unsafe is enabled, "
-            + "but it could not be used. Make sure your runtime is configured correctly.");
+        throw new UnsupportedOperationException(
+            "Cannot allocate "
+                + c
+                + ". Usage of JDK sun.misc.Unsafe is enabled, "
+                + "but it could not be used. Make sure your runtime is configured correctly.");
       }
     };
   }
diff --git a/gson/src/main/java/com/google/gson/internal/bind/ArrayTypeAdapter.java b/gson/src/main/java/com/google/gson/internal/bind/ArrayTypeAdapter.java
index fca3dafc34..d4224d52cd 100644
--- a/gson/src/main/java/com/google/gson/internal/bind/ArrayTypeAdapter.java
+++ b/gson/src/main/java/com/google/gson/internal/bind/ArrayTypeAdapter.java
@@ -30,37 +30,41 @@
 import java.lang.reflect.Type;
 import java.util.ArrayList;
 
-/**
- * Adapt an array of objects.
- */
+/** Adapt an array of objects. */
 public final class ArrayTypeAdapter extends TypeAdapter {
-  public static final TypeAdapterFactory FACTORY = new TypeAdapterFactory() {
-    @Override public  TypeAdapter create(Gson gson, TypeToken typeToken) {
-      Type type = typeToken.getType();
-      if (!(type instanceof GenericArrayType || (type instanceof Class && ((Class) type).isArray()))) {
-        return null;
-      }
+  public static final TypeAdapterFactory FACTORY =
+      new TypeAdapterFactory() {
+        @Override
+        public  TypeAdapter create(Gson gson, TypeToken typeToken) {
+          Type type = typeToken.getType();
+          if (!(type instanceof GenericArrayType
+              || (type instanceof Class && ((Class) type).isArray()))) {
+            return null;
+          }
 
-      Type componentType = $Gson$Types.getArrayComponentType(type);
-      TypeAdapter componentTypeAdapter = gson.getAdapter(TypeToken.get(componentType));
+          Type componentType = $Gson$Types.getArrayComponentType(type);
+          TypeAdapter componentTypeAdapter = gson.getAdapter(TypeToken.get(componentType));
 
-      @SuppressWarnings({"unchecked", "rawtypes"})
-      TypeAdapter arrayAdapter = new ArrayTypeAdapter(
-              gson, componentTypeAdapter, $Gson$Types.getRawType(componentType));
-      return arrayAdapter;
-    }
-  };
+          @SuppressWarnings({"unchecked", "rawtypes"})
+          TypeAdapter arrayAdapter =
+              new ArrayTypeAdapter(
+                  gson, componentTypeAdapter, $Gson$Types.getRawType(componentType));
+          return arrayAdapter;
+        }
+      };
 
   private final Class componentType;
   private final TypeAdapter componentTypeAdapter;
 
-  public ArrayTypeAdapter(Gson context, TypeAdapter componentTypeAdapter, Class componentType) {
+  public ArrayTypeAdapter(
+      Gson context, TypeAdapter componentTypeAdapter, Class componentType) {
     this.componentTypeAdapter =
-      new TypeAdapterRuntimeTypeWrapper<>(context, componentTypeAdapter, componentType);
+        new TypeAdapterRuntimeTypeWrapper<>(context, componentTypeAdapter, componentType);
     this.componentType = componentType;
   }
 
-  @Override public Object read(JsonReader in) throws IOException {
+  @Override
+  public Object read(JsonReader in) throws IOException {
     if (in.peek() == JsonToken.NULL) {
       in.nextNull();
       return null;
@@ -91,7 +95,8 @@ public ArrayTypeAdapter(Gson context, TypeAdapter componentTypeAdapter, Class
     }
   }
 
-  @Override public void write(JsonWriter out, Object array) throws IOException {
+  @Override
+  public void write(JsonWriter out, Object array) throws IOException {
     if (array == null) {
       out.nullValue();
       return;
diff --git a/gson/src/main/java/com/google/gson/internal/bind/CollectionTypeAdapterFactory.java b/gson/src/main/java/com/google/gson/internal/bind/CollectionTypeAdapterFactory.java
index 8c65491a8a..1286d9beea 100644
--- a/gson/src/main/java/com/google/gson/internal/bind/CollectionTypeAdapterFactory.java
+++ b/gson/src/main/java/com/google/gson/internal/bind/CollectionTypeAdapterFactory.java
@@ -30,9 +30,7 @@
 import java.lang.reflect.Type;
 import java.util.Collection;
 
-/**
- * Adapt a homogeneous collection of objects.
- */
+/** Adapt a homogeneous collection of objects. */
 public final class CollectionTypeAdapterFactory implements TypeAdapterFactory {
   private final ConstructorConstructor constructorConstructor;
 
@@ -62,7 +60,9 @@ private static final class Adapter extends TypeAdapter> {
     private final TypeAdapter elementTypeAdapter;
     private final ObjectConstructor> constructor;
 
-    public Adapter(Gson context, Type elementType,
+    public Adapter(
+        Gson context,
+        Type elementType,
         TypeAdapter elementTypeAdapter,
         ObjectConstructor> constructor) {
       this.elementTypeAdapter =
@@ -70,7 +70,8 @@ public Adapter(Gson context, Type elementType,
       this.constructor = constructor;
     }
 
-    @Override public Collection read(JsonReader in) throws IOException {
+    @Override
+    public Collection read(JsonReader in) throws IOException {
       if (in.peek() == JsonToken.NULL) {
         in.nextNull();
         return null;
@@ -86,7 +87,8 @@ public Adapter(Gson context, Type elementType,
       return collection;
     }
 
-    @Override public void write(JsonWriter out, Collection collection) throws IOException {
+    @Override
+    public void write(JsonWriter out, Collection collection) throws IOException {
       if (collection == null) {
         out.nullValue();
         return;
diff --git a/gson/src/main/java/com/google/gson/internal/bind/DateTypeAdapter.java b/gson/src/main/java/com/google/gson/internal/bind/DateTypeAdapter.java
index 36b5f55e06..e12a100763 100644
--- a/gson/src/main/java/com/google/gson/internal/bind/DateTypeAdapter.java
+++ b/gson/src/main/java/com/google/gson/internal/bind/DateTypeAdapter.java
@@ -27,7 +27,6 @@
 import com.google.gson.stream.JsonReader;
 import com.google.gson.stream.JsonToken;
 import com.google.gson.stream.JsonWriter;
-
 import java.io.IOException;
 import java.text.DateFormat;
 import java.text.ParseException;
@@ -38,36 +37,42 @@
 import java.util.Locale;
 
 /**
- * Adapter for Date. Although this class appears stateless, it is not.
- * DateFormat captures its time zone and locale when it is created, which gives
- * this class state. DateFormat isn't thread safe either, so this class has
- * to synchronize its read and write methods.
+ * Adapter for Date. Although this class appears stateless, it is not. DateFormat captures its time
+ * zone and locale when it is created, which gives this class state. DateFormat isn't thread safe
+ * either, so this class has to synchronize its read and write methods.
  */
 public final class DateTypeAdapter extends TypeAdapter {
-  public static final TypeAdapterFactory FACTORY = new TypeAdapterFactory() {
-    @SuppressWarnings("unchecked") // we use a runtime check to make sure the 'T's equal
-    @Override public  TypeAdapter create(Gson gson, TypeToken typeToken) {
-      return typeToken.getRawType() == Date.class ? (TypeAdapter) new DateTypeAdapter() : null;
-    }
-  };
+  public static final TypeAdapterFactory FACTORY =
+      new TypeAdapterFactory() {
+        @SuppressWarnings("unchecked") // we use a runtime check to make sure the 'T's equal
+        @Override
+        public  TypeAdapter create(Gson gson, TypeToken typeToken) {
+          return typeToken.getRawType() == Date.class
+              ? (TypeAdapter) new DateTypeAdapter()
+              : null;
+        }
+      };
 
   /**
-   * List of 1 or more different date formats used for de-serialization attempts.
-   * The first of them (default US format) is used for serialization as well.
+   * List of 1 or more different date formats used for de-serialization attempts. The first of them
+   * (default US format) is used for serialization as well.
    */
   private final List dateFormats = new ArrayList<>();
 
   public DateTypeAdapter() {
-    dateFormats.add(DateFormat.getDateTimeInstance(DateFormat.DEFAULT, DateFormat.DEFAULT, Locale.US));
+    dateFormats.add(
+        DateFormat.getDateTimeInstance(DateFormat.DEFAULT, DateFormat.DEFAULT, Locale.US));
     if (!Locale.getDefault().equals(Locale.US)) {
       dateFormats.add(DateFormat.getDateTimeInstance(DateFormat.DEFAULT, DateFormat.DEFAULT));
     }
     if (JavaVersion.isJava9OrLater()) {
-      dateFormats.add(PreJava9DateFormatProvider.getUSDateTimeFormat(DateFormat.DEFAULT, DateFormat.DEFAULT));
+      dateFormats.add(
+          PreJava9DateFormatProvider.getUSDateTimeFormat(DateFormat.DEFAULT, DateFormat.DEFAULT));
     }
   }
 
-  @Override public Date read(JsonReader in) throws IOException {
+  @Override
+  public Date read(JsonReader in) throws IOException {
     if (in.peek() == JsonToken.NULL) {
       in.nextNull();
       return null;
@@ -89,11 +94,13 @@ private Date deserializeToDate(JsonReader in) throws IOException {
     try {
       return ISO8601Utils.parse(s, new ParsePosition(0));
     } catch (ParseException e) {
-      throw new JsonSyntaxException("Failed parsing '" + s + "' as Date; at path " + in.getPreviousPath(), e);
+      throw new JsonSyntaxException(
+          "Failed parsing '" + s + "' as Date; at path " + in.getPreviousPath(), e);
     }
   }
 
-  @Override public void write(JsonWriter out, Date value) throws IOException {
+  @Override
+  public void write(JsonWriter out, Date value) throws IOException {
     if (value == null) {
       out.nullValue();
       return;
diff --git a/gson/src/main/java/com/google/gson/internal/bind/DefaultDateTypeAdapter.java b/gson/src/main/java/com/google/gson/internal/bind/DefaultDateTypeAdapter.java
index 6a23f3301a..d4dd07e67d 100644
--- a/gson/src/main/java/com/google/gson/internal/bind/DefaultDateTypeAdapter.java
+++ b/gson/src/main/java/com/google/gson/internal/bind/DefaultDateTypeAdapter.java
@@ -37,9 +37,8 @@
 import java.util.Objects;
 
 /**
- * This type adapter supports subclasses of date by defining a
- * {@link DefaultDateTypeAdapter.DateType} and then using its {@code createAdapterFactory}
- * methods.
+ * This type adapter supports subclasses of date by defining a {@link
+ * DefaultDateTypeAdapter.DateType} and then using its {@code createAdapterFactory} methods.
  *
  * @author Inderjeet Singh
  * @author Joel Leitch
@@ -47,12 +46,14 @@
 public final class DefaultDateTypeAdapter extends TypeAdapter {
   private static final String SIMPLE_NAME = "DefaultDateTypeAdapter";
 
-  public static abstract class DateType {
-    public static final DateType DATE = new DateType(Date.class) {
-      @Override protected Date deserialize(Date date) {
-        return date;
-      }
-    };
+  public abstract static class DateType {
+    public static final DateType DATE =
+        new DateType(Date.class) {
+          @Override
+          protected Date deserialize(Date date) {
+            return date;
+          }
+        };
 
     private final Class dateClass;
 
@@ -79,15 +80,16 @@ public final TypeAdapterFactory createAdapterFactory(int dateStyle, int timeStyl
     }
 
     public final TypeAdapterFactory createDefaultsAdapterFactory() {
-      return createFactory(new DefaultDateTypeAdapter<>(this, DateFormat.DEFAULT, DateFormat.DEFAULT));
+      return createFactory(
+          new DefaultDateTypeAdapter<>(this, DateFormat.DEFAULT, DateFormat.DEFAULT));
     }
   }
 
   private final DateType dateType;
 
   /**
-   * List of 1 or more different date formats used for de-serialization attempts.
-   * The first of them is used for serialization as well.
+   * List of 1 or more different date formats used for de-serialization attempts. The first of them
+   * is used for serialization as well.
    */
   private final List dateFormats = new ArrayList<>();
 
@@ -163,7 +165,8 @@ private Date deserializeToDate(JsonReader in) throws IOException {
     try {
       return ISO8601Utils.parse(s, new ParsePosition(0));
     } catch (ParseException e) {
-      throw new JsonSyntaxException("Failed parsing '" + s + "' as Date; at path " + in.getPreviousPath(), e);
+      throw new JsonSyntaxException(
+          "Failed parsing '" + s + "' as Date; at path " + in.getPreviousPath(), e);
     }
   }
 
diff --git a/gson/src/main/java/com/google/gson/internal/bind/JsonAdapterAnnotationTypeAdapterFactory.java b/gson/src/main/java/com/google/gson/internal/bind/JsonAdapterAnnotationTypeAdapterFactory.java
index 8b528b7ff4..2cb9773c35 100644
--- a/gson/src/main/java/com/google/gson/internal/bind/JsonAdapterAnnotationTypeAdapterFactory.java
+++ b/gson/src/main/java/com/google/gson/internal/bind/JsonAdapterAnnotationTypeAdapterFactory.java
@@ -36,29 +36,26 @@
  */
 public final class JsonAdapterAnnotationTypeAdapterFactory implements TypeAdapterFactory {
   private static class DummyTypeAdapterFactory implements TypeAdapterFactory {
-    @Override public  TypeAdapter create(Gson gson, TypeToken type) {
+    @Override
+    public  TypeAdapter create(Gson gson, TypeToken type) {
       throw new AssertionError("Factory should not be used");
     }
   }
 
-  /**
-   * Factory used for {@link TreeTypeAdapter}s created for {@code @JsonAdapter}
-   * on a class.
-   */
-  private static final TypeAdapterFactory TREE_TYPE_CLASS_DUMMY_FACTORY = new DummyTypeAdapterFactory();
+  /** Factory used for {@link TreeTypeAdapter}s created for {@code @JsonAdapter} on a class. */
+  private static final TypeAdapterFactory TREE_TYPE_CLASS_DUMMY_FACTORY =
+      new DummyTypeAdapterFactory();
 
-  /**
-   * Factory used for {@link TreeTypeAdapter}s created for {@code @JsonAdapter}
-   * on a field.
-   */
-  private static final TypeAdapterFactory TREE_TYPE_FIELD_DUMMY_FACTORY = new DummyTypeAdapterFactory();
+  /** Factory used for {@link TreeTypeAdapter}s created for {@code @JsonAdapter} on a field. */
+  private static final TypeAdapterFactory TREE_TYPE_FIELD_DUMMY_FACTORY =
+      new DummyTypeAdapterFactory();
 
   private final ConstructorConstructor constructorConstructor;
 
   /**
-   * For a class, if it is annotated with {@code @JsonAdapter} and refers to a {@link TypeAdapterFactory},
-   * stores the factory instance in case it has been requested already.
-   * Has to be a {@link ConcurrentMap} because {@link Gson} guarantees to be thread-safe.
+   * For a class, if it is annotated with {@code @JsonAdapter} and refers to a {@link
+   * TypeAdapterFactory}, stores the factory instance in case it has been requested already. Has to
+   * be a {@link ConcurrentMap} because {@link Gson} guarantees to be thread-safe.
    */
   // Note: In case these strong reference to TypeAdapterFactory instances are considered
   // a memory leak in the future, could consider switching to WeakReference
@@ -74,7 +71,8 @@ private JsonAdapter getAnnotation(Class rawType) {
     return rawType.getAnnotation(JsonAdapter.class);
   }
 
-  @SuppressWarnings("unchecked") // this is not safe; requires that user has specified correct adapter class for @JsonAdapter
+  // this is not safe; requires that user has specified correct adapter class for  @JsonAdapter
+  @SuppressWarnings("unchecked")
   @Override
   public  TypeAdapter create(Gson gson, TypeToken targetType) {
     Class rawType = targetType.getRawType();
@@ -82,13 +80,16 @@ public  TypeAdapter create(Gson gson, TypeToken targetType) {
     if (annotation == null) {
       return null;
     }
-    return (TypeAdapter) getTypeAdapter(constructorConstructor, gson, targetType, annotation, true);
+    return (TypeAdapter)
+        getTypeAdapter(constructorConstructor, gson, targetType, annotation, true);
   }
 
   // Separate helper method to make sure callers create adapter in a consistent way
-  private static Object createAdapter(ConstructorConstructor constructorConstructor, Class adapterClass) {
-    // TODO: The exception messages created by ConstructorConstructor are currently written in the context of
-    // deserialization and for example suggest usage of TypeAdapter, which would not work for @JsonAdapter usage
+  private static Object createAdapter(
+      ConstructorConstructor constructorConstructor, Class adapterClass) {
+    // TODO: The exception messages created by ConstructorConstructor are currently written in the
+    // context of deserialization and for example suggest usage of TypeAdapter, which would not work
+    // for @JsonAdapter usage
     return constructorConstructor.get(TypeToken.get(adapterClass)).construct();
   }
 
@@ -98,8 +99,12 @@ private TypeAdapterFactory putFactoryAndGetCurrent(Class rawType, TypeAdapter
     return existingFactory != null ? existingFactory : factory;
   }
 
-  TypeAdapter getTypeAdapter(ConstructorConstructor constructorConstructor, Gson gson,
-      TypeToken type, JsonAdapter annotation, boolean isClassAnnotation) {
+  TypeAdapter getTypeAdapter(
+      ConstructorConstructor constructorConstructor,
+      Gson gson,
+      TypeToken type,
+      JsonAdapter annotation,
+      boolean isClassAnnotation) {
     Object instance = createAdapter(constructorConstructor, annotation.value());
 
     TypeAdapter typeAdapter;
@@ -115,32 +120,35 @@ TypeAdapter getTypeAdapter(ConstructorConstructor constructorConstructor, Gso
 
       typeAdapter = factory.create(gson, type);
     } else if (instance instanceof JsonSerializer || instance instanceof JsonDeserializer) {
-      JsonSerializer serializer = instance instanceof JsonSerializer
-          ? (JsonSerializer) instance
-          : null;
-      JsonDeserializer deserializer = instance instanceof JsonDeserializer
-          ? (JsonDeserializer) instance
-          : null;
-
-      // Uses dummy factory instances because TreeTypeAdapter needs a 'skipPast' factory for `Gson.getDelegateAdapter`
-      // call and has to differentiate there whether TreeTypeAdapter was created for @JsonAdapter on class or field
+      JsonSerializer serializer =
+          instance instanceof JsonSerializer ? (JsonSerializer) instance : null;
+      JsonDeserializer deserializer =
+          instance instanceof JsonDeserializer ? (JsonDeserializer) instance : null;
+
+      // Uses dummy factory instances because TreeTypeAdapter needs a 'skipPast' factory for
+      // `Gson.getDelegateAdapter` call and has to differentiate there whether TreeTypeAdapter was
+      // created for @JsonAdapter on class or field
       TypeAdapterFactory skipPast;
       if (isClassAnnotation) {
         skipPast = TREE_TYPE_CLASS_DUMMY_FACTORY;
       } else {
         skipPast = TREE_TYPE_FIELD_DUMMY_FACTORY;
       }
-      @SuppressWarnings({ "unchecked", "rawtypes" })
-      TypeAdapter tempAdapter = new TreeTypeAdapter(serializer, deserializer, gson, type, skipPast, nullSafe);
+      @SuppressWarnings({"unchecked", "rawtypes"})
+      TypeAdapter tempAdapter =
+          new TreeTypeAdapter(serializer, deserializer, gson, type, skipPast, nullSafe);
       typeAdapter = tempAdapter;
 
       // TreeTypeAdapter handles nullSafe; don't additionally call `nullSafe()`
       nullSafe = false;
     } else {
-      throw new IllegalArgumentException("Invalid attempt to bind an instance of "
-          + instance.getClass().getName() + " as a @JsonAdapter for " + type.toString()
-          + ". @JsonAdapter value must be a TypeAdapter, TypeAdapterFactory,"
-          + " JsonSerializer or JsonDeserializer.");
+      throw new IllegalArgumentException(
+          "Invalid attempt to bind an instance of "
+              + instance.getClass().getName()
+              + " as a @JsonAdapter for "
+              + type.toString()
+              + ". @JsonAdapter value must be a TypeAdapter, TypeAdapterFactory,"
+              + " JsonSerializer or JsonDeserializer.");
     }
 
     if (typeAdapter != null && nullSafe) {
@@ -173,8 +181,8 @@ public boolean isClassJsonAdapterFactory(TypeToken type, TypeAdapterFactory f
 
     // If no factory has been created for the type yet check manually for a @JsonAdapter annotation
     // which specifies a TypeAdapterFactory
-    // Otherwise behavior would not be consistent, depending on whether or not adapter had been requested
-    // before call to `isClassJsonAdapterFactory` was made
+    // Otherwise behavior would not be consistent, depending on whether or not adapter had been
+    // requested before call to `isClassJsonAdapterFactory` was made
     JsonAdapter annotation = getAnnotation(rawType);
     if (annotation == null) {
       return false;
diff --git a/gson/src/main/java/com/google/gson/internal/bind/JsonTreeReader.java b/gson/src/main/java/com/google/gson/internal/bind/JsonTreeReader.java
index ec8dcea302..d8e1ebe759 100644
--- a/gson/src/main/java/com/google/gson/internal/bind/JsonTreeReader.java
+++ b/gson/src/main/java/com/google/gson/internal/bind/JsonTreeReader.java
@@ -32,20 +32,23 @@
 import java.util.Map;
 
 /**
- * This reader walks the elements of a JsonElement as if it was coming from a
- * character stream.
+ * This reader walks the elements of a JsonElement as if it was coming from a character stream.
  *
  * @author Jesse Wilson
  */
 public final class JsonTreeReader extends JsonReader {
-  private static final Reader UNREADABLE_READER = new Reader() {
-    @Override public int read(char[] buffer, int offset, int count) {
-      throw new AssertionError();
-    }
-    @Override public void close() {
-      throw new AssertionError();
-    }
-  };
+  private static final Reader UNREADABLE_READER =
+      new Reader() {
+        @Override
+        public int read(char[] buffer, int offset, int count) {
+          throw new AssertionError();
+        }
+
+        @Override
+        public void close() {
+          throw new AssertionError();
+        }
+      };
   private static final Object SENTINEL_CLOSED = new Object();
 
   /*
@@ -70,14 +73,16 @@ public JsonTreeReader(JsonElement element) {
     push(element);
   }
 
-  @Override public void beginArray() throws IOException {
+  @Override
+  public void beginArray() throws IOException {
     expect(JsonToken.BEGIN_ARRAY);
     JsonArray array = (JsonArray) peekStack();
     push(array.iterator());
     pathIndices[stackSize - 1] = 0;
   }
 
-  @Override public void endArray() throws IOException {
+  @Override
+  public void endArray() throws IOException {
     expect(JsonToken.END_ARRAY);
     popStack(); // empty iterator
     popStack(); // array
@@ -86,13 +91,15 @@ public JsonTreeReader(JsonElement element) {
     }
   }
 
-  @Override public void beginObject() throws IOException {
+  @Override
+  public void beginObject() throws IOException {
     expect(JsonToken.BEGIN_OBJECT);
     JsonObject object = (JsonObject) peekStack();
     push(object.entrySet().iterator());
   }
 
-  @Override public void endObject() throws IOException {
+  @Override
+  public void endObject() throws IOException {
     expect(JsonToken.END_OBJECT);
     pathNames[stackSize - 1] = null; // Free the last path name so that it can be garbage collected
     popStack(); // empty iterator
@@ -102,12 +109,16 @@ public JsonTreeReader(JsonElement element) {
     }
   }
 
-  @Override public boolean hasNext() throws IOException {
+  @Override
+  public boolean hasNext() throws IOException {
     JsonToken token = peek();
-    return token != JsonToken.END_OBJECT && token != JsonToken.END_ARRAY && token != JsonToken.END_DOCUMENT;
+    return token != JsonToken.END_OBJECT
+        && token != JsonToken.END_ARRAY
+        && token != JsonToken.END_DOCUMENT;
   }
 
-  @Override public JsonToken peek() throws IOException {
+  @Override
+  public JsonToken peek() throws IOException {
     if (stackSize == 0) {
       return JsonToken.END_DOCUMENT;
     }
@@ -146,7 +157,8 @@ public JsonTreeReader(JsonElement element) {
     } else if (o == SENTINEL_CLOSED) {
       throw new IllegalStateException("JsonReader is closed");
     } else {
-      throw new MalformedJsonException("Custom JsonElement subclass " + o.getClass().getName() + " is not supported");
+      throw new MalformedJsonException(
+          "Custom JsonElement subclass " + o.getClass().getName() + " is not supported");
     }
   }
 
@@ -178,11 +190,13 @@ private String nextName(boolean skipName) throws IOException {
     return result;
   }
 
-  @Override public String nextName() throws IOException {
+  @Override
+  public String nextName() throws IOException {
     return nextName(false);
   }
 
-  @Override public String nextString() throws IOException {
+  @Override
+  public String nextString() throws IOException {
     JsonToken token = peek();
     if (token != JsonToken.STRING && token != JsonToken.NUMBER) {
       throw new IllegalStateException(
@@ -195,7 +209,8 @@ private String nextName(boolean skipName) throws IOException {
     return result;
   }
 
-  @Override public boolean nextBoolean() throws IOException {
+  @Override
+  public boolean nextBoolean() throws IOException {
     expect(JsonToken.BOOLEAN);
     boolean result = ((JsonPrimitive) popStack()).getAsBoolean();
     if (stackSize > 0) {
@@ -204,7 +219,8 @@ private String nextName(boolean skipName) throws IOException {
     return result;
   }
 
-  @Override public void nextNull() throws IOException {
+  @Override
+  public void nextNull() throws IOException {
     expect(JsonToken.NULL);
     popStack();
     if (stackSize > 0) {
@@ -212,7 +228,8 @@ private String nextName(boolean skipName) throws IOException {
     }
   }
 
-  @Override public double nextDouble() throws IOException {
+  @Override
+  public double nextDouble() throws IOException {
     JsonToken token = peek();
     if (token != JsonToken.NUMBER && token != JsonToken.STRING) {
       throw new IllegalStateException(
@@ -229,7 +246,8 @@ private String nextName(boolean skipName) throws IOException {
     return result;
   }
 
-  @Override public long nextLong() throws IOException {
+  @Override
+  public long nextLong() throws IOException {
     JsonToken token = peek();
     if (token != JsonToken.NUMBER && token != JsonToken.STRING) {
       throw new IllegalStateException(
@@ -243,7 +261,8 @@ private String nextName(boolean skipName) throws IOException {
     return result;
   }
 
-  @Override public int nextInt() throws IOException {
+  @Override
+  public int nextInt() throws IOException {
     JsonToken token = peek();
     if (token != JsonToken.NUMBER && token != JsonToken.STRING) {
       throw new IllegalStateException(
@@ -270,12 +289,14 @@ JsonElement nextJsonElement() throws IOException {
     return element;
   }
 
-  @Override public void close() throws IOException {
-    stack = new Object[] { SENTINEL_CLOSED };
+  @Override
+  public void close() throws IOException {
+    stack = new Object[] {SENTINEL_CLOSED};
     stackSize = 1;
   }
 
-  @Override public void skipValue() throws IOException {
+  @Override
+  public void skipValue() throws IOException {
     JsonToken peeked = peek();
     switch (peeked) {
       case NAME:
@@ -300,7 +321,8 @@ JsonElement nextJsonElement() throws IOException {
     }
   }
 
-  @Override public String toString() {
+  @Override
+  public String toString() {
     return getClass().getSimpleName() + locationString();
   }
 
@@ -348,11 +370,13 @@ private String getPath(boolean usePreviousPath) {
     return result.toString();
   }
 
-  @Override public String getPreviousPath() {
+  @Override
+  public String getPreviousPath() {
     return getPath(true);
   }
 
-  @Override public String getPath() {
+  @Override
+  public String getPath() {
     return getPath(false);
   }
 
diff --git a/gson/src/main/java/com/google/gson/internal/bind/JsonTreeWriter.java b/gson/src/main/java/com/google/gson/internal/bind/JsonTreeWriter.java
index f82257287d..3aa8b22d46 100644
--- a/gson/src/main/java/com/google/gson/internal/bind/JsonTreeWriter.java
+++ b/gson/src/main/java/com/google/gson/internal/bind/JsonTreeWriter.java
@@ -29,21 +29,26 @@
 import java.util.List;
 import java.util.Objects;
 
-/**
- * This writer creates a JsonElement.
- */
+/** This writer creates a JsonElement. */
 public final class JsonTreeWriter extends JsonWriter {
-  private static final Writer UNWRITABLE_WRITER = new Writer() {
-    @Override public void write(char[] buffer, int offset, int counter) {
-      throw new AssertionError();
-    }
-    @Override public void flush() {
-      throw new AssertionError();
-    }
-    @Override public void close() {
-      throw new AssertionError();
-    }
-  };
+  private static final Writer UNWRITABLE_WRITER =
+      new Writer() {
+        @Override
+        public void write(char[] buffer, int offset, int counter) {
+          throw new AssertionError();
+        }
+
+        @Override
+        public void flush() {
+          throw new AssertionError();
+        }
+
+        @Override
+        public void close() {
+          throw new AssertionError();
+        }
+      };
+
   /** Added to the top of the stack when this writer is closed to cause following ops to fail. */
   private static final JsonPrimitive SENTINEL_CLOSED = new JsonPrimitive("closed");
 
@@ -60,9 +65,7 @@ public JsonTreeWriter() {
     super(UNWRITABLE_WRITER);
   }
 
-  /**
-   * Returns the top level object produced by this writer.
-   */
+  /** Returns the top level object produced by this writer. */
   public JsonElement get() {
     if (!stack.isEmpty()) {
       throw new IllegalStateException("Expected one JSON element but was " + stack);
@@ -94,7 +97,8 @@ private void put(JsonElement value) {
   }
 
   @CanIgnoreReturnValue
-  @Override public JsonWriter beginArray() throws IOException {
+  @Override
+  public JsonWriter beginArray() throws IOException {
     JsonArray array = new JsonArray();
     put(array);
     stack.add(array);
@@ -102,7 +106,8 @@ private void put(JsonElement value) {
   }
 
   @CanIgnoreReturnValue
-  @Override public JsonWriter endArray() throws IOException {
+  @Override
+  public JsonWriter endArray() throws IOException {
     if (stack.isEmpty() || pendingName != null) {
       throw new IllegalStateException();
     }
@@ -115,7 +120,8 @@ private void put(JsonElement value) {
   }
 
   @CanIgnoreReturnValue
-  @Override public JsonWriter beginObject() throws IOException {
+  @Override
+  public JsonWriter beginObject() throws IOException {
     JsonObject object = new JsonObject();
     put(object);
     stack.add(object);
@@ -123,7 +129,8 @@ private void put(JsonElement value) {
   }
 
   @CanIgnoreReturnValue
-  @Override public JsonWriter endObject() throws IOException {
+  @Override
+  public JsonWriter endObject() throws IOException {
     if (stack.isEmpty() || pendingName != null) {
       throw new IllegalStateException();
     }
@@ -136,7 +143,8 @@ private void put(JsonElement value) {
   }
 
   @CanIgnoreReturnValue
-  @Override public JsonWriter name(String name) throws IOException {
+  @Override
+  public JsonWriter name(String name) throws IOException {
     Objects.requireNonNull(name, "name == null");
     if (stack.isEmpty() || pendingName != null) {
       throw new IllegalStateException("Did not expect a name");
@@ -150,7 +158,8 @@ private void put(JsonElement value) {
   }
 
   @CanIgnoreReturnValue
-  @Override public JsonWriter value(String value) throws IOException {
+  @Override
+  public JsonWriter value(String value) throws IOException {
     if (value == null) {
       return nullValue();
     }
@@ -158,24 +167,28 @@ private void put(JsonElement value) {
     return this;
   }
 
-  @Override public JsonWriter jsonValue(String value) throws IOException {
+  @Override
+  public JsonWriter jsonValue(String value) throws IOException {
     throw new UnsupportedOperationException();
   }
 
   @CanIgnoreReturnValue
-  @Override public JsonWriter nullValue() throws IOException {
+  @Override
+  public JsonWriter nullValue() throws IOException {
     put(JsonNull.INSTANCE);
     return this;
   }
 
   @CanIgnoreReturnValue
-  @Override public JsonWriter value(boolean value) throws IOException {
+  @Override
+  public JsonWriter value(boolean value) throws IOException {
     put(new JsonPrimitive(value));
     return this;
   }
 
   @CanIgnoreReturnValue
-  @Override public JsonWriter value(Boolean value) throws IOException {
+  @Override
+  public JsonWriter value(Boolean value) throws IOException {
     if (value == null) {
       return nullValue();
     }
@@ -184,7 +197,8 @@ private void put(JsonElement value) {
   }
 
   @CanIgnoreReturnValue
-  @Override public JsonWriter value(float value) throws IOException {
+  @Override
+  public JsonWriter value(float value) throws IOException {
     if (!isLenient() && (Float.isNaN(value) || Float.isInfinite(value))) {
       throw new IllegalArgumentException("JSON forbids NaN and infinities: " + value);
     }
@@ -193,7 +207,8 @@ private void put(JsonElement value) {
   }
 
   @CanIgnoreReturnValue
-  @Override public JsonWriter value(double value) throws IOException {
+  @Override
+  public JsonWriter value(double value) throws IOException {
     if (!isLenient() && (Double.isNaN(value) || Double.isInfinite(value))) {
       throw new IllegalArgumentException("JSON forbids NaN and infinities: " + value);
     }
@@ -202,13 +217,15 @@ private void put(JsonElement value) {
   }
 
   @CanIgnoreReturnValue
-  @Override public JsonWriter value(long value) throws IOException {
+  @Override
+  public JsonWriter value(long value) throws IOException {
     put(new JsonPrimitive(value));
     return this;
   }
 
   @CanIgnoreReturnValue
-  @Override public JsonWriter value(Number value) throws IOException {
+  @Override
+  public JsonWriter value(Number value) throws IOException {
     if (value == null) {
       return nullValue();
     }
@@ -224,10 +241,11 @@ private void put(JsonElement value) {
     return this;
   }
 
-  @Override public void flush() throws IOException {
-  }
+  @Override
+  public void flush() throws IOException {}
 
-  @Override public void close() throws IOException {
+  @Override
+  public void close() throws IOException {
     if (!stack.isEmpty()) {
       throw new IOException("Incomplete document");
     }
diff --git a/gson/src/main/java/com/google/gson/internal/bind/MapTypeAdapterFactory.java b/gson/src/main/java/com/google/gson/internal/bind/MapTypeAdapterFactory.java
index 68ecffb931..56de0ea3a8 100644
--- a/gson/src/main/java/com/google/gson/internal/bind/MapTypeAdapterFactory.java
+++ b/gson/src/main/java/com/google/gson/internal/bind/MapTypeAdapterFactory.java
@@ -41,78 +41,89 @@
  * Adapts maps to either JSON objects or JSON arrays.
  *
  * 

Maps as JSON objects

- * For primitive keys or when complex map key serialization is not enabled, this - * converts Java {@link Map Maps} to JSON Objects. This requires that map keys - * can be serialized as strings; this is insufficient for some key types. For - * example, consider a map whose keys are points on a grid. The default JSON - * form encodes reasonably:
   {@code
- *   Map original = new LinkedHashMap<>();
- *   original.put(new Point(5, 6), "a");
- *   original.put(new Point(8, 8), "b");
- *   System.out.println(gson.toJson(original, type));
+ *
+ * For primitive keys or when complex map key serialization is not enabled, this converts Java
+ * {@link Map Maps} to JSON Objects. This requires that map keys can be serialized as strings; this
+ * is insufficient for some key types. For example, consider a map whose keys are points on a grid.
+ * The default JSON form encodes reasonably:
+ *
+ * 
{@code
+ * Map original = new LinkedHashMap<>();
+ * original.put(new Point(5, 6), "a");
+ * original.put(new Point(8, 8), "b");
+ * System.out.println(gson.toJson(original, type));
  * }
- * The above code prints this JSON object:
   {@code
- *   {
- *     "(5,6)": "a",
- *     "(8,8)": "b"
- *   }
+ *
+ * The above code prints this JSON object:
+ *
+ * 
{@code
+ * {
+ *   "(5,6)": "a",
+ *   "(8,8)": "b"
+ * }
  * }
- * But GSON is unable to deserialize this value because the JSON string name is - * just the {@link Object#toString() toString()} of the map key. Attempting to - * convert the above JSON to an object fails with a parse exception: + * + * But GSON is unable to deserialize this value because the JSON string name is just the {@link + * Object#toString() toString()} of the map key. Attempting to convert the above JSON to an object + * fails with a parse exception: + * *
com.google.gson.JsonParseException: Expecting object found: "(5,6)"
  *   at com.google.gson.JsonObjectDeserializationVisitor.visitFieldUsingCustomHandler
  *   at com.google.gson.ObjectNavigator.navigateClassFields
  *   ...
* *

Maps as JSON arrays

- * An alternative approach taken by this type adapter when it is required and - * complex map key serialization is enabled is to encode maps as arrays of map - * entries. Each map entry is a two element array containing a key and a value. - * This approach is more flexible because any type can be used as the map's key; - * not just strings. But it's also less portable because the receiver of such - * JSON must be aware of the map entry convention. + * + * An alternative approach taken by this type adapter when it is required and complex map key + * serialization is enabled is to encode maps as arrays of map entries. Each map entry is a two + * element array containing a key and a value. This approach is more flexible because any type can + * be used as the map's key; not just strings. But it's also less portable because the receiver of + * such JSON must be aware of the map entry convention. * *

Register this adapter when you are creating your GSON instance. - *

   {@code
- *   Gson gson = new GsonBuilder()
- *     .registerTypeAdapter(Map.class, new MapAsArrayTypeAdapter())
- *     .create();
+ *
+ * 
{@code
+ * Gson gson = new GsonBuilder()
+ *   .registerTypeAdapter(Map.class, new MapAsArrayTypeAdapter())
+ *   .create();
  * }
- * This will change the structure of the JSON emitted by the code above. Now we - * get an array. In this case the arrays elements are map entries: - *
   {@code
+ *
+ * This will change the structure of the JSON emitted by the code above. Now we get an array. In
+ * this case the arrays elements are map entries:
+ *
+ * 
{@code
+ * [
+ *   [
+ *     {
+ *       "x": 5,
+ *       "y": 6
+ *     },
+ *     "a",
+ *   ],
  *   [
- *     [
- *       {
- *         "x": 5,
- *         "y": 6
- *       },
- *       "a",
- *     ],
- *     [
- *       {
- *         "x": 8,
- *         "y": 8
- *       },
- *       "b"
- *     ]
+ *     {
+ *       "x": 8,
+ *       "y": 8
+ *     },
+ *     "b"
  *   ]
+ * ]
  * }
- * This format will serialize and deserialize just fine as long as this adapter - * is registered. + * + * This format will serialize and deserialize just fine as long as this adapter is registered. */ public final class MapTypeAdapterFactory implements TypeAdapterFactory { private final ConstructorConstructor constructorConstructor; final boolean complexMapKeySerialization; - public MapTypeAdapterFactory(ConstructorConstructor constructorConstructor, - boolean complexMapKeySerialization) { + public MapTypeAdapterFactory( + ConstructorConstructor constructorConstructor, boolean complexMapKeySerialization) { this.constructorConstructor = constructorConstructor; this.complexMapKeySerialization = complexMapKeySerialization; } - @Override public TypeAdapter create(Gson gson, TypeToken typeToken) { + @Override + public TypeAdapter create(Gson gson, TypeToken typeToken) { Type type = typeToken.getType(); Class rawType = typeToken.getRawType(); @@ -127,14 +138,13 @@ public MapTypeAdapterFactory(ConstructorConstructor constructorConstructor, @SuppressWarnings({"unchecked", "rawtypes"}) // we don't define a type parameter for the key or value types - TypeAdapter result = new Adapter(gson, keyAndValueTypes[0], keyAdapter, - keyAndValueTypes[1], valueAdapter, constructor); + TypeAdapter result = + new Adapter( + gson, keyAndValueTypes[0], keyAdapter, keyAndValueTypes[1], valueAdapter, constructor); return result; } - /** - * Returns a type adapter that writes the value as a string. - */ + /** Returns a type adapter that writes the value as a string. */ private TypeAdapter getKeyAdapter(Gson context, Type keyType) { return (keyType == boolean.class || keyType == Boolean.class) ? TypeAdapters.BOOLEAN_AS_STRING @@ -146,17 +156,21 @@ private final class Adapter extends TypeAdapter> { private final TypeAdapter valueTypeAdapter; private final ObjectConstructor> constructor; - public Adapter(Gson context, Type keyType, TypeAdapter keyTypeAdapter, - Type valueType, TypeAdapter valueTypeAdapter, + public Adapter( + Gson context, + Type keyType, + TypeAdapter keyTypeAdapter, + Type valueType, + TypeAdapter valueTypeAdapter, ObjectConstructor> constructor) { - this.keyTypeAdapter = - new TypeAdapterRuntimeTypeWrapper<>(context, keyTypeAdapter, keyType); + this.keyTypeAdapter = new TypeAdapterRuntimeTypeWrapper<>(context, keyTypeAdapter, keyType); this.valueTypeAdapter = - new TypeAdapterRuntimeTypeWrapper<>(context, valueTypeAdapter, valueType); + new TypeAdapterRuntimeTypeWrapper<>(context, valueTypeAdapter, valueType); this.constructor = constructor; } - @Override public Map read(JsonReader in) throws IOException { + @Override + public Map read(JsonReader in) throws IOException { JsonToken peek = in.peek(); if (peek == JsonToken.NULL) { in.nextNull(); @@ -194,7 +208,8 @@ public Adapter(Gson context, Type keyType, TypeAdapter keyTypeAdapter, return map; } - @Override public void write(JsonWriter out, Map map) throws IOException { + @Override + public void write(JsonWriter out, Map map) throws IOException { if (map == null) { out.nullValue(); return; diff --git a/gson/src/main/java/com/google/gson/internal/bind/NumberTypeAdapter.java b/gson/src/main/java/com/google/gson/internal/bind/NumberTypeAdapter.java index 5aaeae3261..853f2a5fc0 100644 --- a/gson/src/main/java/com/google/gson/internal/bind/NumberTypeAdapter.java +++ b/gson/src/main/java/com/google/gson/internal/bind/NumberTypeAdapter.java @@ -18,25 +18,21 @@ import com.google.gson.Gson; import com.google.gson.JsonSyntaxException; -import com.google.gson.ToNumberStrategy; import com.google.gson.ToNumberPolicy; +import com.google.gson.ToNumberStrategy; import com.google.gson.TypeAdapter; import com.google.gson.TypeAdapterFactory; import com.google.gson.reflect.TypeToken; import com.google.gson.stream.JsonReader; import com.google.gson.stream.JsonToken; import com.google.gson.stream.JsonWriter; - import java.io.IOException; -/** - * Type adapter for {@link Number}. - */ +/** Type adapter for {@link Number}. */ public final class NumberTypeAdapter extends TypeAdapter { - /** - * Gson default factory using {@link ToNumberPolicy#LAZILY_PARSED_NUMBER}. - */ - private static final TypeAdapterFactory LAZILY_PARSED_NUMBER_FACTORY = newFactory(ToNumberPolicy.LAZILY_PARSED_NUMBER); + /** Gson default factory using {@link ToNumberPolicy#LAZILY_PARSED_NUMBER}. */ + private static final TypeAdapterFactory LAZILY_PARSED_NUMBER_FACTORY = + newFactory(ToNumberPolicy.LAZILY_PARSED_NUMBER); private final ToNumberStrategy toNumberStrategy; @@ -48,7 +44,8 @@ private static TypeAdapterFactory newFactory(ToNumberStrategy toNumberStrategy) final NumberTypeAdapter adapter = new NumberTypeAdapter(toNumberStrategy); return new TypeAdapterFactory() { @SuppressWarnings("unchecked") - @Override public TypeAdapter create(Gson gson, TypeToken type) { + @Override + public TypeAdapter create(Gson gson, TypeToken type) { return type.getRawType() == Number.class ? (TypeAdapter) adapter : null; } }; @@ -62,21 +59,24 @@ public static TypeAdapterFactory getFactory(ToNumberStrategy toNumberStrategy) { } } - @Override public Number read(JsonReader in) throws IOException { + @Override + public Number read(JsonReader in) throws IOException { JsonToken jsonToken = in.peek(); switch (jsonToken) { - case NULL: - in.nextNull(); - return null; - case NUMBER: - case STRING: - return toNumberStrategy.readNumber(in); - default: - throw new JsonSyntaxException("Expecting number, got: " + jsonToken + "; at path " + in.getPath()); + case NULL: + in.nextNull(); + return null; + case NUMBER: + case STRING: + return toNumberStrategy.readNumber(in); + default: + throw new JsonSyntaxException( + "Expecting number, got: " + jsonToken + "; at path " + in.getPath()); } } - @Override public void write(JsonWriter out, Number value) throws IOException { + @Override + public void write(JsonWriter out, Number value) throws IOException { out.value(value); } } diff --git a/gson/src/main/java/com/google/gson/internal/bind/ObjectTypeAdapter.java b/gson/src/main/java/com/google/gson/internal/bind/ObjectTypeAdapter.java index 4b4094454c..20d1606291 100644 --- a/gson/src/main/java/com/google/gson/internal/bind/ObjectTypeAdapter.java +++ b/gson/src/main/java/com/google/gson/internal/bind/ObjectTypeAdapter.java @@ -34,13 +34,11 @@ import java.util.Map; /** - * Adapts types whose static type is only 'Object'. Uses getClass() on - * serialization and a primitive/Map/List on deserialization. + * Adapts types whose static type is only 'Object'. Uses getClass() on serialization and a + * primitive/Map/List on deserialization. */ public final class ObjectTypeAdapter extends TypeAdapter { - /** - * Gson default factory using {@link ToNumberPolicy#DOUBLE}. - */ + /** Gson default factory using {@link ToNumberPolicy#DOUBLE}. */ private static final TypeAdapterFactory DOUBLE_FACTORY = newFactory(ToNumberPolicy.DOUBLE); private final Gson gson; @@ -54,7 +52,8 @@ private ObjectTypeAdapter(Gson gson, ToNumberStrategy toNumberStrategy) { private static TypeAdapterFactory newFactory(final ToNumberStrategy toNumberStrategy) { return new TypeAdapterFactory() { @SuppressWarnings("unchecked") - @Override public TypeAdapter create(Gson gson, TypeToken type) { + @Override + public TypeAdapter create(Gson gson, TypeToken type) { if (type.getRawType() == Object.class) { return (TypeAdapter) new ObjectTypeAdapter(gson, toNumberStrategy); } @@ -72,8 +71,8 @@ public static TypeAdapterFactory getFactory(ToNumberStrategy toNumberStrategy) { } /** - * Tries to begin reading a JSON array or JSON object, returning {@code null} if - * the next element is neither of those. + * Tries to begin reading a JSON array or JSON object, returning {@code null} if the next element + * is neither of those. */ private Object tryBeginNesting(JsonReader in, JsonToken peeked) throws IOException { switch (peeked) { @@ -106,7 +105,8 @@ private Object readTerminal(JsonReader in, JsonToken peeked) throws IOException } } - @Override public Object read(JsonReader in) throws IOException { + @Override + public Object read(JsonReader in) throws IOException { // Either List or Map Object current; JsonToken peeked = in.peek(); @@ -166,7 +166,8 @@ private Object readTerminal(JsonReader in, JsonToken peeked) throws IOException } } - @Override public void write(JsonWriter out, Object value) throws IOException { + @Override + public void write(JsonWriter out, Object value) throws IOException { if (value == null) { out.nullValue(); return; diff --git a/gson/src/main/java/com/google/gson/internal/bind/ReflectiveTypeAdapterFactory.java b/gson/src/main/java/com/google/gson/internal/bind/ReflectiveTypeAdapterFactory.java index 6e0ed85b5f..663d3a66d7 100644 --- a/gson/src/main/java/com/google/gson/internal/bind/ReflectiveTypeAdapterFactory.java +++ b/gson/src/main/java/com/google/gson/internal/bind/ReflectiveTypeAdapterFactory.java @@ -56,9 +56,7 @@ import java.util.List; import java.util.Map; -/** - * Type adapter that reflects over the fields and methods of a class. - */ +/** Type adapter that reflects over the fields and methods of a class. */ public final class ReflectiveTypeAdapterFactory implements TypeAdapterFactory { private final ConstructorConstructor constructorConstructor; private final FieldNamingStrategy fieldNamingPolicy; @@ -66,8 +64,10 @@ public final class ReflectiveTypeAdapterFactory implements TypeAdapterFactory { private final JsonAdapterAnnotationTypeAdapterFactory jsonAdapterFactory; private final List reflectionFilters; - public ReflectiveTypeAdapterFactory(ConstructorConstructor constructorConstructor, - FieldNamingStrategy fieldNamingPolicy, Excluder excluder, + public ReflectiveTypeAdapterFactory( + ConstructorConstructor constructorConstructor, + FieldNamingStrategy fieldNamingPolicy, + Excluder excluder, JsonAdapterAnnotationTypeAdapterFactory jsonAdapterFactory, List reflectionFilters) { this.constructorConstructor = constructorConstructor; @@ -114,36 +114,49 @@ public TypeAdapter create(Gson gson, final TypeToken type) { ReflectionAccessFilterHelper.getFilterResult(reflectionFilters, raw); if (filterResult == FilterResult.BLOCK_ALL) { throw new JsonIOException( - "ReflectionAccessFilter does not permit using reflection for " + raw - + ". Register a TypeAdapter for this type or adjust the access filter."); + "ReflectionAccessFilter does not permit using reflection for " + + raw + + ". Register a TypeAdapter for this type or adjust the access filter."); } boolean blockInaccessible = filterResult == FilterResult.BLOCK_INACCESSIBLE; - // If the type is actually a Java Record, we need to use the RecordAdapter instead. This will always be false - // on JVMs that do not support records. + // If the type is actually a Java Record, we need to use the RecordAdapter instead. This will + // always be false on JVMs that do not support records. if (ReflectionHelper.isRecord(raw)) { @SuppressWarnings("unchecked") - TypeAdapter adapter = (TypeAdapter) new RecordAdapter<>(raw, - getBoundFields(gson, type, raw, blockInaccessible, true), blockInaccessible); + TypeAdapter adapter = + (TypeAdapter) + new RecordAdapter<>( + raw, getBoundFields(gson, type, raw, blockInaccessible, true), blockInaccessible); return adapter; } ObjectConstructor constructor = constructorConstructor.get(type); - return new FieldReflectionAdapter<>(constructor, getBoundFields(gson, type, raw, blockInaccessible, false)); + return new FieldReflectionAdapter<>( + constructor, getBoundFields(gson, type, raw, blockInaccessible, false)); } - private static void checkAccessible(Object object, M member) { - if (!ReflectionAccessFilterHelper.canAccess(member, Modifier.isStatic(member.getModifiers()) ? null : object)) { + private static void checkAccessible( + Object object, M member) { + if (!ReflectionAccessFilterHelper.canAccess( + member, Modifier.isStatic(member.getModifiers()) ? null : object)) { String memberDescription = ReflectionHelper.getAccessibleObjectDescription(member, true); - throw new JsonIOException(memberDescription + " is not accessible and ReflectionAccessFilter does not" - + " permit making it accessible. Register a TypeAdapter for the declaring type, adjust the" - + " access filter or increase the visibility of the element and its declaring type."); + throw new JsonIOException( + memberDescription + + " is not accessible and ReflectionAccessFilter does not permit making it" + + " accessible. Register a TypeAdapter for the declaring type, adjust the access" + + " filter or increase the visibility of the element and its declaring type."); } } private BoundField createBoundField( - final Gson context, final Field field, final Method accessor, final String serializedName, - final TypeToken fieldType, final boolean serialize, final boolean blockInaccessible) { + final Gson context, + final Field field, + final Method accessor, + final String serializedName, + final TypeToken fieldType, + final boolean serialize, + final boolean blockInaccessible) { final boolean isPrimitive = Primitives.isPrimitive(fieldType.getRawType()); @@ -154,8 +167,9 @@ private BoundField createBoundField( TypeAdapter mapped = null; if (annotation != null) { // This is not safe; requires that user has specified correct adapter class for @JsonAdapter - mapped = jsonAdapterFactory.getTypeAdapter( - constructorConstructor, context, fieldType, annotation, false); + mapped = + jsonAdapterFactory.getTypeAdapter( + constructorConstructor, context, fieldType, annotation, false); } final boolean jsonAdapterPresent = mapped != null; if (mapped == null) mapped = context.getAdapter(fieldType); @@ -164,15 +178,17 @@ private BoundField createBoundField( final TypeAdapter typeAdapter = (TypeAdapter) mapped; final TypeAdapter writeTypeAdapter; if (serialize) { - writeTypeAdapter = jsonAdapterPresent ? typeAdapter - : new TypeAdapterRuntimeTypeWrapper<>(context, typeAdapter, fieldType.getType()); + writeTypeAdapter = + jsonAdapterPresent + ? typeAdapter + : new TypeAdapterRuntimeTypeWrapper<>(context, typeAdapter, fieldType.getType()); } else { // Will never actually be used, but we set it to avoid confusing nullness-analysis tools writeTypeAdapter = typeAdapter; } return new BoundField(serializedName, field) { - @Override void write(JsonWriter writer, Object source) - throws IOException, IllegalAccessException { + @Override + void write(JsonWriter writer, Object source) throws IOException, IllegalAccessException { if (blockInaccessible) { if (accessor == null) { checkAccessible(source, field); @@ -188,8 +204,10 @@ private BoundField createBoundField( try { fieldValue = accessor.invoke(source); } catch (InvocationTargetException e) { - String accessorDescription = ReflectionHelper.getAccessibleObjectDescription(accessor, false); - throw new JsonIOException("Accessor " + accessorDescription + " threw exception", e.getCause()); + String accessorDescription = + ReflectionHelper.getAccessibleObjectDescription(accessor, false); + throw new JsonIOException( + "Accessor " + accessorDescription + " threw exception", e.getCause()); } } else { fieldValue = field.get(source); @@ -203,11 +221,16 @@ private BoundField createBoundField( } @Override - void readIntoArray(JsonReader reader, int index, Object[] target) throws IOException, JsonParseException { + void readIntoArray(JsonReader reader, int index, Object[] target) + throws IOException, JsonParseException { Object fieldValue = typeAdapter.read(reader); if (fieldValue == null && isPrimitive) { - throw new JsonParseException("null is not allowed as value for record component '" + fieldName + "'" - + " of primitive type; at path " + reader.getPath()); + throw new JsonParseException( + "null is not allowed as value for record component '" + + fieldName + + "'" + + " of primitive type; at path " + + reader.getPath()); } target[index] = fieldValue; } @@ -220,8 +243,9 @@ void readIntoField(JsonReader reader, Object target) if (blockInaccessible) { checkAccessible(target, field); } else if (isStaticFinalField) { - // Reflection does not permit setting value of `static final` field, even after calling `setAccessible` - // Handle this here to avoid causing IllegalAccessException when calling `Field.set` + // Reflection does not permit setting value of `static final` field, even after calling + // `setAccessible` Handle this here to avoid causing IllegalAccessException when calling + // `Field.set` String fieldDescription = ReflectionHelper.getAccessibleObjectDescription(field, false); throw new JsonIOException("Cannot set value of 'static final' " + fieldDescription); } @@ -232,32 +256,47 @@ void readIntoField(JsonReader reader, Object target) } private static class FieldsData { - public static final FieldsData EMPTY = new FieldsData(Collections.emptyMap(), Collections.emptyList()); + public static final FieldsData EMPTY = + new FieldsData( + Collections.emptyMap(), Collections.emptyList()); /** Maps from JSON member name to field */ public final Map deserializedFields; + public final List serializedFields; - public FieldsData(Map deserializedFields, List serializedFields) { + public FieldsData( + Map deserializedFields, List serializedFields) { this.deserializedFields = deserializedFields; this.serializedFields = serializedFields; } } - private static IllegalArgumentException createDuplicateFieldException(Class declaringType, String duplicateName, Field field1, Field field2) { - throw new IllegalArgumentException("Class " + declaringType.getName() - + " declares multiple JSON fields named '" + duplicateName + "'; conflict is caused" - + " by fields " + ReflectionHelper.fieldToString(field1) + " and " + ReflectionHelper.fieldToString(field2) - + "\nSee " + TroubleshootingGuide.createUrl("duplicate-fields")); + private static IllegalArgumentException createDuplicateFieldException( + Class declaringType, String duplicateName, Field field1, Field field2) { + throw new IllegalArgumentException( + "Class " + + declaringType.getName() + + " declares multiple JSON fields named '" + + duplicateName + + "'; conflict is caused" + + " by fields " + + ReflectionHelper.fieldToString(field1) + + " and " + + ReflectionHelper.fieldToString(field2) + + "\nSee " + + TroubleshootingGuide.createUrl("duplicate-fields")); } - private FieldsData getBoundFields(Gson context, TypeToken type, Class raw, boolean blockInaccessible, boolean isRecord) { + private FieldsData getBoundFields( + Gson context, TypeToken type, Class raw, boolean blockInaccessible, boolean isRecord) { if (raw.isInterface()) { return FieldsData.EMPTY; } Map deserializedFields = new LinkedHashMap<>(); - // For serialized fields use a Map to track duplicate field names; otherwise this could be a List instead + // For serialized fields use a Map to track duplicate field names; otherwise this could be a + // List instead Map serializedFields = new LinkedHashMap<>(); Class originalRaw = raw; @@ -266,11 +305,16 @@ private FieldsData getBoundFields(Gson context, TypeToken type, Class raw, // For inherited fields, check if access to their declaring class is allowed if (raw != originalRaw && fields.length > 0) { - FilterResult filterResult = ReflectionAccessFilterHelper.getFilterResult(reflectionFilters, raw); + FilterResult filterResult = + ReflectionAccessFilterHelper.getFilterResult(reflectionFilters, raw); if (filterResult == FilterResult.BLOCK_ALL) { - throw new JsonIOException("ReflectionAccessFilter does not permit using reflection for " + raw - + " (supertype of " + originalRaw + "). Register a TypeAdapter for this type" - + " or adjust the access filter."); + throw new JsonIOException( + "ReflectionAccessFilter does not permit using reflection for " + + raw + + " (supertype of " + + originalRaw + + "). Register a TypeAdapter for this type" + + " or adjust the access filter."); } blockInaccessible = filterResult == FilterResult.BLOCK_INACCESSIBLE; } @@ -281,13 +325,16 @@ private FieldsData getBoundFields(Gson context, TypeToken type, Class raw, if (!serialize && !deserialize) { continue; } - // The accessor method is only used for records. If the type is a record, we will read out values - // via its accessor method instead of via reflection. This way we will bypass the accessible restrictions + // The accessor method is only used for records. If the type is a record, we will read out + // values via its accessor method instead of via reflection. This way we will bypass the + // accessible restrictions Method accessor = null; if (isRecord) { - // If there is a static field on a record, there will not be an accessor. Instead we will use the default - // field serialization logic, but for deserialization the field is excluded for simplicity. Note that Gson - // ignores static fields by default, but GsonBuilder.excludeFieldsWithModifiers can overwrite this. + // If there is a static field on a record, there will not be an accessor. Instead we will + // use the default field serialization logic, but for deserialization the field is + // excluded for simplicity. + // Note that Gson ignores static fields by default, but + // GsonBuilder.excludeFieldsWithModifiers can overwrite this. if (Modifier.isStatic(field.getModifiers())) { deserialize = false; } else { @@ -298,12 +345,15 @@ private FieldsData getBoundFields(Gson context, TypeToken type, Class raw, } // @SerializedName can be placed on accessor method, but it is not supported there - // If field and method have annotation it is not easily possible to determine if accessor method - // is implicit and has inherited annotation, or if it is explicitly declared with custom annotation + // If field and method have annotation it is not easily possible to determine if + // accessor method is implicit and has inherited annotation, or if it is explicitly + // declared with custom annotation if (accessor.getAnnotation(SerializedName.class) != null && field.getAnnotation(SerializedName.class) == null) { - String methodDescription = ReflectionHelper.getAccessibleObjectDescription(accessor, false); - throw new JsonIOException("@SerializedName on " + methodDescription + " is not supported"); + String methodDescription = + ReflectionHelper.getAccessibleObjectDescription(accessor, false); + throw new JsonIOException( + "@SerializedName on " + methodDescription + " is not supported"); } } } @@ -317,8 +367,15 @@ private FieldsData getBoundFields(Gson context, TypeToken type, Class raw, Type fieldType = $Gson$Types.resolve(type.getType(), raw, field.getGenericType()); List fieldNames = getFieldNames(field); String serializedName = fieldNames.get(0); - BoundField boundField = createBoundField(context, field, accessor, serializedName, - TypeToken.get(fieldType), serialize, blockInaccessible); + BoundField boundField = + createBoundField( + context, + field, + accessor, + serializedName, + TypeToken.get(fieldType), + serialize, + blockInaccessible); if (deserialize) { for (String name : fieldNames) { @@ -343,10 +400,12 @@ private FieldsData getBoundFields(Gson context, TypeToken type, Class raw, return new FieldsData(deserializedFields, new ArrayList<>(serializedFields.values())); } - static abstract class BoundField { + abstract static class BoundField { /** Name used for serialization (but not for deserialization) */ final String serializedName; + final Field field; + /** Name of the underlying field */ final String fieldName; @@ -357,13 +416,19 @@ protected BoundField(String serializedName, Field field) { } /** Read this field value from the source, and append its JSON value to the writer */ - abstract void write(JsonWriter writer, Object source) throws IOException, IllegalAccessException; + abstract void write(JsonWriter writer, Object source) + throws IOException, IllegalAccessException; /** Read the value into the target array, used to provide constructor arguments for records */ - abstract void readIntoArray(JsonReader reader, int index, Object[] target) throws IOException, JsonParseException; + abstract void readIntoArray(JsonReader reader, int index, Object[] target) + throws IOException, JsonParseException; - /** Read the value from the reader, and set it on the corresponding field on target via reflection */ - abstract void readIntoField(JsonReader reader, Object target) throws IOException, IllegalAccessException; + /** + * Read the value from the reader, and set it on the corresponding field on target via + * reflection + */ + abstract void readIntoField(JsonReader reader, Object target) + throws IOException, IllegalAccessException; } /** @@ -372,15 +437,16 @@ protected BoundField(String serializedName, Field field) { *

The {@link RecordAdapter} is a special case to handle records for JVMs that support it, for * all other types we use the {@link FieldReflectionAdapter}. This class encapsulates the common * logic for serialization and deserialization. During deserialization, we construct an - * accumulator A, which we use to accumulate values from the source JSON. After the object has been read in - * full, the {@link #finalize(Object)} method is used to convert the accumulator to an instance - * of T. + * accumulator A, which we use to accumulate values from the source JSON. After the object has + * been read in full, the {@link #finalize(Object)} method is used to convert the accumulator to + * an instance of T. * * @param type of objects that this Adapter creates. * @param type of accumulator used to build the deserialization result. */ - // This class is public because external projects check for this class with `instanceof` (even though it is internal) - public static abstract class Adapter extends TypeAdapter { + // This class is public because external projects check for this class with `instanceof` (even + // though it is internal) + public abstract static class Adapter extends TypeAdapter { private final FieldsData fieldsData; Adapter(FieldsData fieldsData) { @@ -437,12 +503,14 @@ public T read(JsonReader in) throws IOException { /** Create the Object that will be used to collect each field value */ abstract A createAccumulator(); + /** - * Read a single BoundField into the accumulator. The JsonReader will be pointed at the - * start of the value for the BoundField to read from. + * Read a single BoundField into the accumulator. The JsonReader will be pointed at the start of + * the value for the BoundField to read from. */ abstract void readField(A accumulator, JsonReader in, BoundField field) throws IllegalAccessException, IOException; + /** Convert the accumulator to a final instance of T. */ abstract T finalize(A accumulator); } @@ -499,8 +567,9 @@ private static final class RecordAdapter extends Adapter { } Class[] parameterTypes = constructor.getParameterTypes(); - // We need to ensure that we are passing non-null values to primitive fields in the constructor. To do this, - // we create an Object[] where all primitives are initialized to non-null values. + // We need to ensure that we are passing non-null values to primitive fields in the + // constructor. To do this, we create an Object[] where all primitives are initialized to + // non-null values. constructorArgsDefaults = new Object[parameterTypes.length]; for (int i = 0; i < parameterTypes.length; i++) { // This will correctly be null for non-primitive types: @@ -532,12 +601,16 @@ void readField(Object[] accumulator, JsonReader in, BoundField field) throws IOE Integer componentIndex = componentIndices.get(field.fieldName); if (componentIndex == null) { throw new IllegalStateException( - "Could not find the index in the constructor '" + ReflectionHelper.constructorToString(constructor) + "'" - + " for field with name '" + field.fieldName + "'," - + " unable to determine which argument in the constructor the field corresponds" + "Could not find the index in the constructor '" + + ReflectionHelper.constructorToString(constructor) + + "'" + + " for field with name '" + + field.fieldName + + "', unable to determine which argument in the constructor the field corresponds" + " to. This is unexpected behavior, as we expect the RecordComponents to have the" + " same names as the fields in the Java class, and that the order of the" - + " RecordComponents is the same as the order of the canonical constructor parameters."); + + " RecordComponents is the same as the order of the canonical constructor" + + " parameters."); } field.readIntoArray(in, componentIndex, accumulator); } @@ -550,17 +623,25 @@ T finalize(Object[] accumulator) { throw ReflectionHelper.createExceptionForUnexpectedIllegalAccess(e); } // Note: InstantiationException should be impossible because record class is not abstract; - // IllegalArgumentException should not be possible unless a bad adapter returns objects of the wrong type + // IllegalArgumentException should not be possible unless a bad adapter returns objects of + // the wrong type catch (InstantiationException | IllegalArgumentException e) { throw new RuntimeException( - "Failed to invoke constructor '" + ReflectionHelper.constructorToString(constructor) + "'" - + " with args " + Arrays.toString(accumulator), e); - } - catch (InvocationTargetException e) { + "Failed to invoke constructor '" + + ReflectionHelper.constructorToString(constructor) + + "'" + + " with args " + + Arrays.toString(accumulator), + e); + } catch (InvocationTargetException e) { // TODO: JsonParseException ? throw new RuntimeException( - "Failed to invoke constructor '" + ReflectionHelper.constructorToString(constructor) + "'" - + " with args " + Arrays.toString(accumulator), e.getCause()); + "Failed to invoke constructor '" + + ReflectionHelper.constructorToString(constructor) + + "'" + + " with args " + + Arrays.toString(accumulator), + e.getCause()); } } } diff --git a/gson/src/main/java/com/google/gson/internal/bind/SerializationDelegatingTypeAdapter.java b/gson/src/main/java/com/google/gson/internal/bind/SerializationDelegatingTypeAdapter.java index 25dd3b8504..3f0a7ba1fb 100644 --- a/gson/src/main/java/com/google/gson/internal/bind/SerializationDelegatingTypeAdapter.java +++ b/gson/src/main/java/com/google/gson/internal/bind/SerializationDelegatingTypeAdapter.java @@ -18,13 +18,11 @@ import com.google.gson.TypeAdapter; -/** - * Type adapter which might delegate serialization to another adapter. - */ +/** Type adapter which might delegate serialization to another adapter. */ public abstract class SerializationDelegatingTypeAdapter extends TypeAdapter { /** - * Returns the adapter used for serialization, might be {@code this} or another adapter. - * That other adapter might itself also be a {@code SerializationDelegatingTypeAdapter}. + * Returns the adapter used for serialization, might be {@code this} or another adapter. That + * other adapter might itself also be a {@code SerializationDelegatingTypeAdapter}. */ public abstract TypeAdapter getSerializationDelegate(); } diff --git a/gson/src/main/java/com/google/gson/internal/bind/TreeTypeAdapter.java b/gson/src/main/java/com/google/gson/internal/bind/TreeTypeAdapter.java index 0c72d7b73c..1ef1e40b9f 100644 --- a/gson/src/main/java/com/google/gson/internal/bind/TreeTypeAdapter.java +++ b/gson/src/main/java/com/google/gson/internal/bind/TreeTypeAdapter.java @@ -34,31 +34,38 @@ import java.lang.reflect.Type; /** - * Adapts a Gson 1.x tree-style adapter as a streaming TypeAdapter. Since the - * tree adapter may be serialization-only or deserialization-only, this class - * has a facility to look up a delegate type adapter on demand. + * Adapts a Gson 1.x tree-style adapter as a streaming TypeAdapter. Since the tree adapter may be + * serialization-only or deserialization-only, this class has a facility to look up a delegate type + * adapter on demand. */ public final class TreeTypeAdapter extends SerializationDelegatingTypeAdapter { private final JsonSerializer serializer; private final JsonDeserializer deserializer; final Gson gson; private final TypeToken typeToken; + /** - * Only intended as {@code skipPast} for {@link Gson#getDelegateAdapter(TypeAdapterFactory, TypeToken)}, - * must not be used in any other way. + * Only intended as {@code skipPast} for {@link Gson#getDelegateAdapter(TypeAdapterFactory, + * TypeToken)}, must not be used in any other way. */ private final TypeAdapterFactory skipPastForGetDelegateAdapter; + private final GsonContextImpl context = new GsonContextImpl(); private final boolean nullSafe; /** - * The delegate is lazily created because it may not be needed, and creating it may fail. - * Field has to be {@code volatile} because {@link Gson} guarantees to be thread-safe. + * The delegate is lazily created because it may not be needed, and creating it may fail. Field + * has to be {@code volatile} because {@link Gson} guarantees to be thread-safe. */ private volatile TypeAdapter delegate; - public TreeTypeAdapter(JsonSerializer serializer, JsonDeserializer deserializer, - Gson gson, TypeToken typeToken, TypeAdapterFactory skipPast, boolean nullSafe) { + public TreeTypeAdapter( + JsonSerializer serializer, + JsonDeserializer deserializer, + Gson gson, + TypeToken typeToken, + TypeAdapterFactory skipPast, + boolean nullSafe) { this.serializer = serializer; this.deserializer = deserializer; this.gson = gson; @@ -67,12 +74,17 @@ public TreeTypeAdapter(JsonSerializer serializer, JsonDeserializer deseria this.nullSafe = nullSafe; } - public TreeTypeAdapter(JsonSerializer serializer, JsonDeserializer deserializer, - Gson gson, TypeToken typeToken, TypeAdapterFactory skipPast) { + public TreeTypeAdapter( + JsonSerializer serializer, + JsonDeserializer deserializer, + Gson gson, + TypeToken typeToken, + TypeAdapterFactory skipPast) { this(serializer, deserializer, gson, typeToken, skipPast, true); } - @Override public T read(JsonReader in) throws IOException { + @Override + public T read(JsonReader in) throws IOException { if (deserializer == null) { return delegate().read(in); } @@ -83,7 +95,8 @@ public TreeTypeAdapter(JsonSerializer serializer, JsonDeserializer deseria return deserializer.deserialize(value, typeToken.getType(), context); } - @Override public void write(JsonWriter out, T value) throws IOException { + @Override + public void write(JsonWriter out, T value) throws IOException { if (serializer == null) { delegate().write(out, value); return; @@ -97,7 +110,8 @@ public TreeTypeAdapter(JsonSerializer serializer, JsonDeserializer deseria } private TypeAdapter delegate() { - // A race might lead to `delegate` being assigned by multiple threads but the last assignment will stick + // A race might lead to `delegate` being assigned by multiple threads but the last assignment + // will stick TypeAdapter d = delegate; return d != null ? d @@ -105,25 +119,20 @@ private TypeAdapter delegate() { } /** - * Returns the type adapter which is used for serialization. Returns {@code this} - * if this {@code TreeTypeAdapter} has a {@link #serializer}; otherwise returns - * the delegate. + * Returns the type adapter which is used for serialization. Returns {@code this} if this {@code + * TreeTypeAdapter} has a {@link #serializer}; otherwise returns the delegate. */ - @Override public TypeAdapter getSerializationDelegate() { + @Override + public TypeAdapter getSerializationDelegate() { return serializer != null ? this : delegate(); } - /** - * Returns a new factory that will match each type against {@code exactType}. - */ + /** Returns a new factory that will match each type against {@code exactType}. */ public static TypeAdapterFactory newFactory(TypeToken exactType, Object typeAdapter) { return new SingleTypeFactory(typeAdapter, exactType, false, null); } - /** - * Returns a new factory that will match each type and its raw type against - * {@code exactType}. - */ + /** Returns a new factory that will match each type and its raw type against {@code exactType}. */ public static TypeAdapterFactory newFactoryWithMatchRawType( TypeToken exactType, Object typeAdapter) { // only bother matching raw types if exact type is a raw type @@ -132,8 +141,8 @@ public static TypeAdapterFactory newFactoryWithMatchRawType( } /** - * Returns a new factory that will match each type's raw type for assignability - * to {@code hierarchyType}. + * Returns a new factory that will match each type's raw type for assignability to {@code + * hierarchyType}. */ public static TypeAdapterFactory newTypeHierarchyFactory( Class hierarchyType, Object typeAdapter) { @@ -147,14 +156,11 @@ private static final class SingleTypeFactory implements TypeAdapterFactory { private final JsonSerializer serializer; private final JsonDeserializer deserializer; - SingleTypeFactory(Object typeAdapter, TypeToken exactType, boolean matchRawType, - Class hierarchyType) { - serializer = typeAdapter instanceof JsonSerializer - ? (JsonSerializer) typeAdapter - : null; - deserializer = typeAdapter instanceof JsonDeserializer - ? (JsonDeserializer) typeAdapter - : null; + SingleTypeFactory( + Object typeAdapter, TypeToken exactType, boolean matchRawType, Class hierarchyType) { + serializer = typeAdapter instanceof JsonSerializer ? (JsonSerializer) typeAdapter : null; + deserializer = + typeAdapter instanceof JsonDeserializer ? (JsonDeserializer) typeAdapter : null; $Gson$Preconditions.checkArgument(serializer != null || deserializer != null); this.exactType = exactType; this.matchRawType = matchRawType; @@ -164,23 +170,29 @@ private static final class SingleTypeFactory implements TypeAdapterFactory { @SuppressWarnings("unchecked") // guarded by typeToken.equals() call @Override public TypeAdapter create(Gson gson, TypeToken type) { - boolean matches = exactType != null - ? exactType.equals(type) || (matchRawType && exactType.getType() == type.getRawType()) - : hierarchyType.isAssignableFrom(type.getRawType()); + boolean matches = + exactType != null + ? exactType.equals(type) || (matchRawType && exactType.getType() == type.getRawType()) + : hierarchyType.isAssignableFrom(type.getRawType()); return matches - ? new TreeTypeAdapter<>((JsonSerializer) serializer, - (JsonDeserializer) deserializer, gson, type, this) + ? new TreeTypeAdapter<>( + (JsonSerializer) serializer, (JsonDeserializer) deserializer, gson, type, this) : null; } } - private final class GsonContextImpl implements JsonSerializationContext, JsonDeserializationContext { - @Override public JsonElement serialize(Object src) { + private final class GsonContextImpl + implements JsonSerializationContext, JsonDeserializationContext { + @Override + public JsonElement serialize(Object src) { return gson.toJsonTree(src); } - @Override public JsonElement serialize(Object src, Type typeOfSrc) { + + @Override + public JsonElement serialize(Object src, Type typeOfSrc) { return gson.toJsonTree(src, typeOfSrc); } + @Override @SuppressWarnings({"unchecked", "TypeParameterUnusedInFormals"}) public R deserialize(JsonElement json, Type typeOfT) throws JsonParseException { diff --git a/gson/src/main/java/com/google/gson/internal/bind/TypeAdapterRuntimeTypeWrapper.java b/gson/src/main/java/com/google/gson/internal/bind/TypeAdapterRuntimeTypeWrapper.java index 01cb26bda1..049d81141e 100644 --- a/gson/src/main/java/com/google/gson/internal/bind/TypeAdapterRuntimeTypeWrapper.java +++ b/gson/src/main/java/com/google/gson/internal/bind/TypeAdapterRuntimeTypeWrapper.java @@ -45,16 +45,19 @@ public void write(JsonWriter out, T value) throws IOException { // Order of preference for choosing type adapters // First preference: a type adapter registered for the runtime type // Second preference: a type adapter registered for the declared type - // Third preference: reflective type adapter for the runtime type (if it is a subclass of the declared type) + // Third preference: reflective type adapter for the runtime type (if it is a subclass of the + // declared type) // Fourth preference: reflective type adapter for the declared type TypeAdapter chosen = delegate; Type runtimeType = getRuntimeTypeIfMoreSpecific(type, value); if (runtimeType != type) { @SuppressWarnings("unchecked") - TypeAdapter runtimeTypeAdapter = (TypeAdapter) context.getAdapter(TypeToken.get(runtimeType)); - // For backward compatibility only check ReflectiveTypeAdapterFactory.Adapter here but not any other - // wrapping adapters, see https://github.com/google/gson/pull/1787#issuecomment-1222175189 + TypeAdapter runtimeTypeAdapter = + (TypeAdapter) context.getAdapter(TypeToken.get(runtimeType)); + // For backward compatibility only check ReflectiveTypeAdapterFactory.Adapter here but not any + // other wrapping adapters, see + // https://github.com/google/gson/pull/1787#issuecomment-1222175189 if (!(runtimeTypeAdapter instanceof ReflectiveTypeAdapterFactory.Adapter)) { // The user registered a type adapter for the runtime type, so we will use that chosen = runtimeTypeAdapter; @@ -78,7 +81,8 @@ public void write(JsonWriter out, T value) throws IOException { private static boolean isReflective(TypeAdapter typeAdapter) { // Run this in loop in case multiple delegating adapters are nested while (typeAdapter instanceof SerializationDelegatingTypeAdapter) { - TypeAdapter delegate = ((SerializationDelegatingTypeAdapter) typeAdapter).getSerializationDelegate(); + TypeAdapter delegate = + ((SerializationDelegatingTypeAdapter) typeAdapter).getSerializationDelegate(); // Break if adapter does not delegate serialization if (delegate == typeAdapter) { break; @@ -89,9 +93,7 @@ private static boolean isReflective(TypeAdapter typeAdapter) { return typeAdapter instanceof ReflectiveTypeAdapterFactory.Adapter; } - /** - * Finds a compatible runtime type if it is more specific - */ + /** Finds a compatible runtime type if it is more specific */ private static Type getRuntimeTypeIfMoreSpecific(Type type, Object value) { if (value != null && (type instanceof Class || type instanceof TypeVariable)) { type = value.getClass(); diff --git a/gson/src/main/java/com/google/gson/internal/bind/TypeAdapters.java b/gson/src/main/java/com/google/gson/internal/bind/TypeAdapters.java index ec46db1d1d..75f64dc7d2 100644 --- a/gson/src/main/java/com/google/gson/internal/bind/TypeAdapters.java +++ b/gson/src/main/java/com/google/gson/internal/bind/TypeAdapters.java @@ -62,807 +62,887 @@ import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicIntegerArray; -/** - * Type adapters for basic types. - */ +/** Type adapters for basic types. */ public final class TypeAdapters { private TypeAdapters() { throw new UnsupportedOperationException(); } @SuppressWarnings("rawtypes") - public static final TypeAdapter CLASS = new TypeAdapter() { - @Override - public void write(JsonWriter out, Class value) throws IOException { - throw new UnsupportedOperationException("Attempted to serialize java.lang.Class: " - + value.getName() + ". Forgot to register a type adapter?" - + "\nSee " + TroubleshootingGuide.createUrl("java-lang-class-unsupported")); - } - @Override - public Class read(JsonReader in) throws IOException { - throw new UnsupportedOperationException( - "Attempted to deserialize a java.lang.Class. Forgot to register a type adapter?" - + "\nSee " + TroubleshootingGuide.createUrl("java-lang-class-unsupported")); - } - }.nullSafe(); + public static final TypeAdapter CLASS = + new TypeAdapter() { + @Override + public void write(JsonWriter out, Class value) throws IOException { + throw new UnsupportedOperationException( + "Attempted to serialize java.lang.Class: " + + value.getName() + + ". Forgot to register a type adapter?" + + "\nSee " + + TroubleshootingGuide.createUrl("java-lang-class-unsupported")); + } + + @Override + public Class read(JsonReader in) throws IOException { + throw new UnsupportedOperationException( + "Attempted to deserialize a java.lang.Class. Forgot to register a type adapter?" + + "\nSee " + + TroubleshootingGuide.createUrl("java-lang-class-unsupported")); + } + }.nullSafe(); public static final TypeAdapterFactory CLASS_FACTORY = newFactory(Class.class, CLASS); - public static final TypeAdapter BIT_SET = new TypeAdapter() { - @Override public BitSet read(JsonReader in) throws IOException { - BitSet bitset = new BitSet(); - in.beginArray(); - int i = 0; - JsonToken tokenType = in.peek(); - while (tokenType != JsonToken.END_ARRAY) { - boolean set; - switch (tokenType) { - case NUMBER: - case STRING: - int intValue = in.nextInt(); - if (intValue == 0) { - set = false; - } else if (intValue == 1) { - set = true; - } else { - throw new JsonSyntaxException("Invalid bitset value " + intValue + ", expected 0 or 1; at path " + in.getPreviousPath()); + public static final TypeAdapter BIT_SET = + new TypeAdapter() { + @Override + public BitSet read(JsonReader in) throws IOException { + BitSet bitset = new BitSet(); + in.beginArray(); + int i = 0; + JsonToken tokenType = in.peek(); + while (tokenType != JsonToken.END_ARRAY) { + boolean set; + switch (tokenType) { + case NUMBER: + case STRING: + int intValue = in.nextInt(); + if (intValue == 0) { + set = false; + } else if (intValue == 1) { + set = true; + } else { + throw new JsonSyntaxException( + "Invalid bitset value " + + intValue + + ", expected 0 or 1; at path " + + in.getPreviousPath()); + } + break; + case BOOLEAN: + set = in.nextBoolean(); + break; + default: + throw new JsonSyntaxException( + "Invalid bitset value type: " + tokenType + "; at path " + in.getPath()); + } + if (set) { + bitset.set(i); + } + ++i; + tokenType = in.peek(); } - break; - case BOOLEAN: - set = in.nextBoolean(); - break; - default: - throw new JsonSyntaxException("Invalid bitset value type: " + tokenType + "; at path " + in.getPath()); - } - if (set) { - bitset.set(i); + in.endArray(); + return bitset; } - ++i; - tokenType = in.peek(); - } - in.endArray(); - return bitset; - } - @Override public void write(JsonWriter out, BitSet src) throws IOException { - out.beginArray(); - for (int i = 0, length = src.length(); i < length; i++) { - int value = src.get(i) ? 1 : 0; - out.value(value); - } - out.endArray(); - } - }.nullSafe(); + @Override + public void write(JsonWriter out, BitSet src) throws IOException { + out.beginArray(); + for (int i = 0, length = src.length(); i < length; i++) { + int value = src.get(i) ? 1 : 0; + out.value(value); + } + out.endArray(); + } + }.nullSafe(); public static final TypeAdapterFactory BIT_SET_FACTORY = newFactory(BitSet.class, BIT_SET); - public static final TypeAdapter BOOLEAN = new TypeAdapter() { - @Override - public Boolean read(JsonReader in) throws IOException { - JsonToken peek = in.peek(); - if (peek == JsonToken.NULL) { - in.nextNull(); - return null; - } else if (peek == JsonToken.STRING) { - // support strings for compatibility with GSON 1.7 - return Boolean.parseBoolean(in.nextString()); - } - return in.nextBoolean(); - } - @Override - public void write(JsonWriter out, Boolean value) throws IOException { - out.value(value); - } - }; + public static final TypeAdapter BOOLEAN = + new TypeAdapter() { + @Override + public Boolean read(JsonReader in) throws IOException { + JsonToken peek = in.peek(); + if (peek == JsonToken.NULL) { + in.nextNull(); + return null; + } else if (peek == JsonToken.STRING) { + // support strings for compatibility with GSON 1.7 + return Boolean.parseBoolean(in.nextString()); + } + return in.nextBoolean(); + } + + @Override + public void write(JsonWriter out, Boolean value) throws IOException { + out.value(value); + } + }; /** - * Writes a boolean as a string. Useful for map keys, where booleans aren't - * otherwise permitted. + * Writes a boolean as a string. Useful for map keys, where booleans aren't otherwise permitted. */ - public static final TypeAdapter BOOLEAN_AS_STRING = new TypeAdapter() { - @Override public Boolean read(JsonReader in) throws IOException { - if (in.peek() == JsonToken.NULL) { - in.nextNull(); - return null; - } - return Boolean.valueOf(in.nextString()); - } - - @Override public void write(JsonWriter out, Boolean value) throws IOException { - out.value(value == null ? "null" : value.toString()); - } - }; + public static final TypeAdapter BOOLEAN_AS_STRING = + new TypeAdapter() { + @Override + public Boolean read(JsonReader in) throws IOException { + if (in.peek() == JsonToken.NULL) { + in.nextNull(); + return null; + } + return Boolean.valueOf(in.nextString()); + } - public static final TypeAdapterFactory BOOLEAN_FACTORY - = newFactory(boolean.class, Boolean.class, BOOLEAN); + @Override + public void write(JsonWriter out, Boolean value) throws IOException { + out.value(value == null ? "null" : value.toString()); + } + }; + + public static final TypeAdapterFactory BOOLEAN_FACTORY = + newFactory(boolean.class, Boolean.class, BOOLEAN); + + public static final TypeAdapter BYTE = + new TypeAdapter() { + @Override + public Number read(JsonReader in) throws IOException { + if (in.peek() == JsonToken.NULL) { + in.nextNull(); + return null; + } - public static final TypeAdapter BYTE = new TypeAdapter() { - @Override - public Number read(JsonReader in) throws IOException { - if (in.peek() == JsonToken.NULL) { - in.nextNull(); - return null; - } + int intValue; + try { + intValue = in.nextInt(); + } catch (NumberFormatException e) { + throw new JsonSyntaxException(e); + } + // Allow up to 255 to support unsigned values + if (intValue > 255 || intValue < Byte.MIN_VALUE) { + throw new JsonSyntaxException( + "Lossy conversion from " + intValue + " to byte; at path " + in.getPreviousPath()); + } + return (byte) intValue; + } - int intValue; - try { - intValue = in.nextInt(); - } catch (NumberFormatException e) { - throw new JsonSyntaxException(e); - } - // Allow up to 255 to support unsigned values - if (intValue > 255 || intValue < Byte.MIN_VALUE) { - throw new JsonSyntaxException("Lossy conversion from " + intValue + " to byte; at path " + in.getPreviousPath()); - } - return (byte) intValue; - } - @Override - public void write(JsonWriter out, Number value) throws IOException { - if (value == null) { - out.nullValue(); - } else { - out.value(value.byteValue()); - } - } - }; + @Override + public void write(JsonWriter out, Number value) throws IOException { + if (value == null) { + out.nullValue(); + } else { + out.value(value.byteValue()); + } + } + }; - public static final TypeAdapterFactory BYTE_FACTORY - = newFactory(byte.class, Byte.class, BYTE); + public static final TypeAdapterFactory BYTE_FACTORY = newFactory(byte.class, Byte.class, BYTE); - public static final TypeAdapter SHORT = new TypeAdapter() { - @Override - public Number read(JsonReader in) throws IOException { - if (in.peek() == JsonToken.NULL) { - in.nextNull(); - return null; - } + public static final TypeAdapter SHORT = + new TypeAdapter() { + @Override + public Number read(JsonReader in) throws IOException { + if (in.peek() == JsonToken.NULL) { + in.nextNull(); + return null; + } - int intValue; - try { - intValue = in.nextInt(); - } catch (NumberFormatException e) { - throw new JsonSyntaxException(e); - } - // Allow up to 65535 to support unsigned values - if (intValue > 65535 || intValue < Short.MIN_VALUE) { - throw new JsonSyntaxException("Lossy conversion from " + intValue + " to short; at path " + in.getPreviousPath()); - } - return (short) intValue; - } - @Override - public void write(JsonWriter out, Number value) throws IOException { - if (value == null) { - out.nullValue(); - } else { - out.value(value.shortValue()); - } - } - }; + int intValue; + try { + intValue = in.nextInt(); + } catch (NumberFormatException e) { + throw new JsonSyntaxException(e); + } + // Allow up to 65535 to support unsigned values + if (intValue > 65535 || intValue < Short.MIN_VALUE) { + throw new JsonSyntaxException( + "Lossy conversion from " + intValue + " to short; at path " + in.getPreviousPath()); + } + return (short) intValue; + } - public static final TypeAdapterFactory SHORT_FACTORY - = newFactory(short.class, Short.class, SHORT); + @Override + public void write(JsonWriter out, Number value) throws IOException { + if (value == null) { + out.nullValue(); + } else { + out.value(value.shortValue()); + } + } + }; + + public static final TypeAdapterFactory SHORT_FACTORY = + newFactory(short.class, Short.class, SHORT); + + public static final TypeAdapter INTEGER = + new TypeAdapter() { + @Override + public Number read(JsonReader in) throws IOException { + if (in.peek() == JsonToken.NULL) { + in.nextNull(); + return null; + } + try { + return in.nextInt(); + } catch (NumberFormatException e) { + throw new JsonSyntaxException(e); + } + } - public static final TypeAdapter INTEGER = new TypeAdapter() { - @Override - public Number read(JsonReader in) throws IOException { - if (in.peek() == JsonToken.NULL) { - in.nextNull(); - return null; - } - try { - return in.nextInt(); - } catch (NumberFormatException e) { - throw new JsonSyntaxException(e); - } - } - @Override - public void write(JsonWriter out, Number value) throws IOException { - if (value == null) { - out.nullValue(); - } else { - out.value(value.intValue()); - } - } - }; - public static final TypeAdapterFactory INTEGER_FACTORY - = newFactory(int.class, Integer.class, INTEGER); + @Override + public void write(JsonWriter out, Number value) throws IOException { + if (value == null) { + out.nullValue(); + } else { + out.value(value.intValue()); + } + } + }; + public static final TypeAdapterFactory INTEGER_FACTORY = + newFactory(int.class, Integer.class, INTEGER); + + public static final TypeAdapter ATOMIC_INTEGER = + new TypeAdapter() { + @Override + public AtomicInteger read(JsonReader in) throws IOException { + try { + return new AtomicInteger(in.nextInt()); + } catch (NumberFormatException e) { + throw new JsonSyntaxException(e); + } + } - public static final TypeAdapter ATOMIC_INTEGER = new TypeAdapter() { - @Override public AtomicInteger read(JsonReader in) throws IOException { - try { - return new AtomicInteger(in.nextInt()); - } catch (NumberFormatException e) { - throw new JsonSyntaxException(e); - } - } - @Override public void write(JsonWriter out, AtomicInteger value) throws IOException { - out.value(value.get()); - } - }.nullSafe(); + @Override + public void write(JsonWriter out, AtomicInteger value) throws IOException { + out.value(value.get()); + } + }.nullSafe(); public static final TypeAdapterFactory ATOMIC_INTEGER_FACTORY = newFactory(AtomicInteger.class, TypeAdapters.ATOMIC_INTEGER); - public static final TypeAdapter ATOMIC_BOOLEAN = new TypeAdapter() { - @Override public AtomicBoolean read(JsonReader in) throws IOException { - return new AtomicBoolean(in.nextBoolean()); - } - @Override public void write(JsonWriter out, AtomicBoolean value) throws IOException { - out.value(value.get()); - } - }.nullSafe(); + public static final TypeAdapter ATOMIC_BOOLEAN = + new TypeAdapter() { + @Override + public AtomicBoolean read(JsonReader in) throws IOException { + return new AtomicBoolean(in.nextBoolean()); + } + + @Override + public void write(JsonWriter out, AtomicBoolean value) throws IOException { + out.value(value.get()); + } + }.nullSafe(); public static final TypeAdapterFactory ATOMIC_BOOLEAN_FACTORY = newFactory(AtomicBoolean.class, TypeAdapters.ATOMIC_BOOLEAN); - public static final TypeAdapter ATOMIC_INTEGER_ARRAY = new TypeAdapter() { - @Override public AtomicIntegerArray read(JsonReader in) throws IOException { - List list = new ArrayList<>(); - in.beginArray(); - while (in.hasNext()) { - try { - int integer = in.nextInt(); - list.add(integer); - } catch (NumberFormatException e) { - throw new JsonSyntaxException(e); + public static final TypeAdapter ATOMIC_INTEGER_ARRAY = + new TypeAdapter() { + @Override + public AtomicIntegerArray read(JsonReader in) throws IOException { + List list = new ArrayList<>(); + in.beginArray(); + while (in.hasNext()) { + try { + int integer = in.nextInt(); + list.add(integer); + } catch (NumberFormatException e) { + throw new JsonSyntaxException(e); + } + } + in.endArray(); + int length = list.size(); + AtomicIntegerArray array = new AtomicIntegerArray(length); + for (int i = 0; i < length; ++i) { + array.set(i, list.get(i)); } + return array; } - in.endArray(); - int length = list.size(); - AtomicIntegerArray array = new AtomicIntegerArray(length); - for (int i = 0; i < length; ++i) { - array.set(i, list.get(i)); + + @Override + public void write(JsonWriter out, AtomicIntegerArray value) throws IOException { + out.beginArray(); + for (int i = 0, length = value.length(); i < length; i++) { + out.value(value.get(i)); + } + out.endArray(); } - return array; - } - @Override public void write(JsonWriter out, AtomicIntegerArray value) throws IOException { - out.beginArray(); - for (int i = 0, length = value.length(); i < length; i++) { - out.value(value.get(i)); - } - out.endArray(); - } - }.nullSafe(); + }.nullSafe(); public static final TypeAdapterFactory ATOMIC_INTEGER_ARRAY_FACTORY = newFactory(AtomicIntegerArray.class, TypeAdapters.ATOMIC_INTEGER_ARRAY); - public static final TypeAdapter LONG = new TypeAdapter() { - @Override - public Number read(JsonReader in) throws IOException { - if (in.peek() == JsonToken.NULL) { - in.nextNull(); - return null; - } - try { - return in.nextLong(); - } catch (NumberFormatException e) { - throw new JsonSyntaxException(e); - } - } - @Override - public void write(JsonWriter out, Number value) throws IOException { - if (value == null) { - out.nullValue(); - } else { - out.value(value.longValue()); - } - } - }; - - public static final TypeAdapter FLOAT = new TypeAdapter() { - @Override - public Number read(JsonReader in) throws IOException { - if (in.peek() == JsonToken.NULL) { - in.nextNull(); - return null; - } - return (float) in.nextDouble(); - } - @Override - public void write(JsonWriter out, Number value) throws IOException { - if (value == null) { - out.nullValue(); - } else { - // For backward compatibility don't call `JsonWriter.value(float)` because that method has - // been newly added and not all custom JsonWriter implementations might override it yet - Number floatNumber = value instanceof Float ? value : value.floatValue(); - out.value(floatNumber); - } - } - }; - - public static final TypeAdapter DOUBLE = new TypeAdapter() { - @Override - public Number read(JsonReader in) throws IOException { - if (in.peek() == JsonToken.NULL) { - in.nextNull(); - return null; - } - return in.nextDouble(); - } - @Override - public void write(JsonWriter out, Number value) throws IOException { - if (value == null) { - out.nullValue(); - } else { - out.value(value.doubleValue()); - } - } - }; - - public static final TypeAdapter CHARACTER = new TypeAdapter() { - @Override - public Character read(JsonReader in) throws IOException { - if (in.peek() == JsonToken.NULL) { - in.nextNull(); - return null; - } - String str = in.nextString(); - if (str.length() != 1) { - throw new JsonSyntaxException("Expecting character, got: " + str + "; at " + in.getPreviousPath()); - } - return str.charAt(0); - } - @Override - public void write(JsonWriter out, Character value) throws IOException { - out.value(value == null ? null : String.valueOf(value)); - } - }; + public static final TypeAdapter LONG = + new TypeAdapter() { + @Override + public Number read(JsonReader in) throws IOException { + if (in.peek() == JsonToken.NULL) { + in.nextNull(); + return null; + } + try { + return in.nextLong(); + } catch (NumberFormatException e) { + throw new JsonSyntaxException(e); + } + } - public static final TypeAdapterFactory CHARACTER_FACTORY - = newFactory(char.class, Character.class, CHARACTER); + @Override + public void write(JsonWriter out, Number value) throws IOException { + if (value == null) { + out.nullValue(); + } else { + out.value(value.longValue()); + } + } + }; + + public static final TypeAdapter FLOAT = + new TypeAdapter() { + @Override + public Number read(JsonReader in) throws IOException { + if (in.peek() == JsonToken.NULL) { + in.nextNull(); + return null; + } + return (float) in.nextDouble(); + } - public static final TypeAdapter STRING = new TypeAdapter() { - @Override - public String read(JsonReader in) throws IOException { - JsonToken peek = in.peek(); - if (peek == JsonToken.NULL) { - in.nextNull(); - return null; - } - /* coerce booleans to strings for backwards compatibility */ - if (peek == JsonToken.BOOLEAN) { - return Boolean.toString(in.nextBoolean()); - } - return in.nextString(); - } - @Override - public void write(JsonWriter out, String value) throws IOException { - out.value(value); - } - }; + @Override + public void write(JsonWriter out, Number value) throws IOException { + if (value == null) { + out.nullValue(); + } else { + // For backward compatibility don't call `JsonWriter.value(float)` because that method + // has + // been newly added and not all custom JsonWriter implementations might override it yet + Number floatNumber = value instanceof Float ? value : value.floatValue(); + out.value(floatNumber); + } + } + }; + + public static final TypeAdapter DOUBLE = + new TypeAdapter() { + @Override + public Number read(JsonReader in) throws IOException { + if (in.peek() == JsonToken.NULL) { + in.nextNull(); + return null; + } + return in.nextDouble(); + } - public static final TypeAdapter BIG_DECIMAL = new TypeAdapter() { - @Override public BigDecimal read(JsonReader in) throws IOException { - if (in.peek() == JsonToken.NULL) { - in.nextNull(); - return null; - } - String s = in.nextString(); - try { - return NumberLimits.parseBigDecimal(s); - } catch (NumberFormatException e) { - throw new JsonSyntaxException("Failed parsing '" + s + "' as BigDecimal; at path " + in.getPreviousPath(), e); - } - } + @Override + public void write(JsonWriter out, Number value) throws IOException { + if (value == null) { + out.nullValue(); + } else { + out.value(value.doubleValue()); + } + } + }; + + public static final TypeAdapter CHARACTER = + new TypeAdapter() { + @Override + public Character read(JsonReader in) throws IOException { + if (in.peek() == JsonToken.NULL) { + in.nextNull(); + return null; + } + String str = in.nextString(); + if (str.length() != 1) { + throw new JsonSyntaxException( + "Expecting character, got: " + str + "; at " + in.getPreviousPath()); + } + return str.charAt(0); + } - @Override public void write(JsonWriter out, BigDecimal value) throws IOException { - out.value(value); - } - }; + @Override + public void write(JsonWriter out, Character value) throws IOException { + out.value(value == null ? null : String.valueOf(value)); + } + }; + + public static final TypeAdapterFactory CHARACTER_FACTORY = + newFactory(char.class, Character.class, CHARACTER); + + public static final TypeAdapter STRING = + new TypeAdapter() { + @Override + public String read(JsonReader in) throws IOException { + JsonToken peek = in.peek(); + if (peek == JsonToken.NULL) { + in.nextNull(); + return null; + } + /* coerce booleans to strings for backwards compatibility */ + if (peek == JsonToken.BOOLEAN) { + return Boolean.toString(in.nextBoolean()); + } + return in.nextString(); + } - public static final TypeAdapter BIG_INTEGER = new TypeAdapter() { - @Override public BigInteger read(JsonReader in) throws IOException { - if (in.peek() == JsonToken.NULL) { - in.nextNull(); - return null; - } - String s = in.nextString(); - try { - return NumberLimits.parseBigInteger(s); - } catch (NumberFormatException e) { - throw new JsonSyntaxException("Failed parsing '" + s + "' as BigInteger; at path " + in.getPreviousPath(), e); - } - } + @Override + public void write(JsonWriter out, String value) throws IOException { + out.value(value); + } + }; + + public static final TypeAdapter BIG_DECIMAL = + new TypeAdapter() { + @Override + public BigDecimal read(JsonReader in) throws IOException { + if (in.peek() == JsonToken.NULL) { + in.nextNull(); + return null; + } + String s = in.nextString(); + try { + return NumberLimits.parseBigDecimal(s); + } catch (NumberFormatException e) { + throw new JsonSyntaxException( + "Failed parsing '" + s + "' as BigDecimal; at path " + in.getPreviousPath(), e); + } + } - @Override public void write(JsonWriter out, BigInteger value) throws IOException { - out.value(value); - } - }; + @Override + public void write(JsonWriter out, BigDecimal value) throws IOException { + out.value(value); + } + }; + + public static final TypeAdapter BIG_INTEGER = + new TypeAdapter() { + @Override + public BigInteger read(JsonReader in) throws IOException { + if (in.peek() == JsonToken.NULL) { + in.nextNull(); + return null; + } + String s = in.nextString(); + try { + return NumberLimits.parseBigInteger(s); + } catch (NumberFormatException e) { + throw new JsonSyntaxException( + "Failed parsing '" + s + "' as BigInteger; at path " + in.getPreviousPath(), e); + } + } - public static final TypeAdapter LAZILY_PARSED_NUMBER = new TypeAdapter() { - // Normally users should not be able to access and deserialize LazilyParsedNumber because - // it is an internal type, but implement this nonetheless in case there are legit corner - // cases where this is possible - @Override public LazilyParsedNumber read(JsonReader in) throws IOException { - if (in.peek() == JsonToken.NULL) { - in.nextNull(); - return null; - } - return new LazilyParsedNumber(in.nextString()); - } + @Override + public void write(JsonWriter out, BigInteger value) throws IOException { + out.value(value); + } + }; + + public static final TypeAdapter LAZILY_PARSED_NUMBER = + new TypeAdapter() { + // Normally users should not be able to access and deserialize LazilyParsedNumber because + // it is an internal type, but implement this nonetheless in case there are legit corner + // cases where this is possible + @Override + public LazilyParsedNumber read(JsonReader in) throws IOException { + if (in.peek() == JsonToken.NULL) { + in.nextNull(); + return null; + } + return new LazilyParsedNumber(in.nextString()); + } - @Override public void write(JsonWriter out, LazilyParsedNumber value) throws IOException { - out.value(value); - } - }; + @Override + public void write(JsonWriter out, LazilyParsedNumber value) throws IOException { + out.value(value); + } + }; public static final TypeAdapterFactory STRING_FACTORY = newFactory(String.class, STRING); - public static final TypeAdapter STRING_BUILDER = new TypeAdapter() { - @Override - public StringBuilder read(JsonReader in) throws IOException { - if (in.peek() == JsonToken.NULL) { - in.nextNull(); - return null; - } - return new StringBuilder(in.nextString()); - } - @Override - public void write(JsonWriter out, StringBuilder value) throws IOException { - out.value(value == null ? null : value.toString()); - } - }; + public static final TypeAdapter STRING_BUILDER = + new TypeAdapter() { + @Override + public StringBuilder read(JsonReader in) throws IOException { + if (in.peek() == JsonToken.NULL) { + in.nextNull(); + return null; + } + return new StringBuilder(in.nextString()); + } + + @Override + public void write(JsonWriter out, StringBuilder value) throws IOException { + out.value(value == null ? null : value.toString()); + } + }; public static final TypeAdapterFactory STRING_BUILDER_FACTORY = - newFactory(StringBuilder.class, STRING_BUILDER); + newFactory(StringBuilder.class, STRING_BUILDER); + + public static final TypeAdapter STRING_BUFFER = + new TypeAdapter() { + @Override + public StringBuffer read(JsonReader in) throws IOException { + if (in.peek() == JsonToken.NULL) { + in.nextNull(); + return null; + } + return new StringBuffer(in.nextString()); + } - public static final TypeAdapter STRING_BUFFER = new TypeAdapter() { - @Override - public StringBuffer read(JsonReader in) throws IOException { - if (in.peek() == JsonToken.NULL) { - in.nextNull(); - return null; - } - return new StringBuffer(in.nextString()); - } - @Override - public void write(JsonWriter out, StringBuffer value) throws IOException { - out.value(value == null ? null : value.toString()); - } - }; + @Override + public void write(JsonWriter out, StringBuffer value) throws IOException { + out.value(value == null ? null : value.toString()); + } + }; public static final TypeAdapterFactory STRING_BUFFER_FACTORY = - newFactory(StringBuffer.class, STRING_BUFFER); + newFactory(StringBuffer.class, STRING_BUFFER); + + public static final TypeAdapter URL = + new TypeAdapter() { + @Override + public URL read(JsonReader in) throws IOException { + if (in.peek() == JsonToken.NULL) { + in.nextNull(); + return null; + } + String nextString = in.nextString(); + return "null".equals(nextString) ? null : new URL(nextString); + } - public static final TypeAdapter URL = new TypeAdapter() { - @Override - public URL read(JsonReader in) throws IOException { - if (in.peek() == JsonToken.NULL) { - in.nextNull(); - return null; - } - String nextString = in.nextString(); - return "null".equals(nextString) ? null : new URL(nextString); - } - @Override - public void write(JsonWriter out, URL value) throws IOException { - out.value(value == null ? null : value.toExternalForm()); - } - }; + @Override + public void write(JsonWriter out, URL value) throws IOException { + out.value(value == null ? null : value.toExternalForm()); + } + }; public static final TypeAdapterFactory URL_FACTORY = newFactory(URL.class, URL); - public static final TypeAdapter URI = new TypeAdapter() { - @Override - public URI read(JsonReader in) throws IOException { - if (in.peek() == JsonToken.NULL) { - in.nextNull(); - return null; - } - try { - String nextString = in.nextString(); - return "null".equals(nextString) ? null : new URI(nextString); - } catch (URISyntaxException e) { - throw new JsonIOException(e); - } - } - @Override - public void write(JsonWriter out, URI value) throws IOException { - out.value(value == null ? null : value.toASCIIString()); - } - }; + public static final TypeAdapter URI = + new TypeAdapter() { + @Override + public URI read(JsonReader in) throws IOException { + if (in.peek() == JsonToken.NULL) { + in.nextNull(); + return null; + } + try { + String nextString = in.nextString(); + return "null".equals(nextString) ? null : new URI(nextString); + } catch (URISyntaxException e) { + throw new JsonIOException(e); + } + } + + @Override + public void write(JsonWriter out, URI value) throws IOException { + out.value(value == null ? null : value.toASCIIString()); + } + }; public static final TypeAdapterFactory URI_FACTORY = newFactory(URI.class, URI); - public static final TypeAdapter INET_ADDRESS = new TypeAdapter() { - @Override - public InetAddress read(JsonReader in) throws IOException { - if (in.peek() == JsonToken.NULL) { - in.nextNull(); - return null; - } - // regrettably, this should have included both the host name and the host address - // For compatibility, we use InetAddress.getByName rather than the possibly-better - // .getAllByName - @SuppressWarnings("AddressSelection") - InetAddress addr = InetAddress.getByName(in.nextString()); - return addr; - } - @Override - public void write(JsonWriter out, InetAddress value) throws IOException { - out.value(value == null ? null : value.getHostAddress()); - } - }; + public static final TypeAdapter INET_ADDRESS = + new TypeAdapter() { + @Override + public InetAddress read(JsonReader in) throws IOException { + if (in.peek() == JsonToken.NULL) { + in.nextNull(); + return null; + } + // regrettably, this should have included both the host name and the host address + // For compatibility, we use InetAddress.getByName rather than the possibly-better + // .getAllByName + @SuppressWarnings("AddressSelection") + InetAddress addr = InetAddress.getByName(in.nextString()); + return addr; + } + + @Override + public void write(JsonWriter out, InetAddress value) throws IOException { + out.value(value == null ? null : value.getHostAddress()); + } + }; public static final TypeAdapterFactory INET_ADDRESS_FACTORY = - newTypeHierarchyFactory(InetAddress.class, INET_ADDRESS); + newTypeHierarchyFactory(InetAddress.class, INET_ADDRESS); + + public static final TypeAdapter UUID = + new TypeAdapter() { + @Override + public UUID read(JsonReader in) throws IOException { + if (in.peek() == JsonToken.NULL) { + in.nextNull(); + return null; + } + String s = in.nextString(); + try { + return java.util.UUID.fromString(s); + } catch (IllegalArgumentException e) { + throw new JsonSyntaxException( + "Failed parsing '" + s + "' as UUID; at path " + in.getPreviousPath(), e); + } + } - public static final TypeAdapter UUID = new TypeAdapter() { - @Override - public UUID read(JsonReader in) throws IOException { - if (in.peek() == JsonToken.NULL) { - in.nextNull(); - return null; - } - String s = in.nextString(); - try { - return java.util.UUID.fromString(s); - } catch (IllegalArgumentException e) { - throw new JsonSyntaxException("Failed parsing '" + s + "' as UUID; at path " + in.getPreviousPath(), e); - } - } - @Override - public void write(JsonWriter out, UUID value) throws IOException { - out.value(value == null ? null : value.toString()); - } - }; + @Override + public void write(JsonWriter out, UUID value) throws IOException { + out.value(value == null ? null : value.toString()); + } + }; public static final TypeAdapterFactory UUID_FACTORY = newFactory(UUID.class, UUID); - public static final TypeAdapter CURRENCY = new TypeAdapter() { - @Override - public Currency read(JsonReader in) throws IOException { - String s = in.nextString(); - try { - return Currency.getInstance(s); - } catch (IllegalArgumentException e) { - throw new JsonSyntaxException("Failed parsing '" + s + "' as Currency; at path " + in.getPreviousPath(), e); - } - } - @Override - public void write(JsonWriter out, Currency value) throws IOException { - out.value(value.getCurrencyCode()); - } - }.nullSafe(); - public static final TypeAdapterFactory CURRENCY_FACTORY = newFactory(Currency.class, CURRENCY); + public static final TypeAdapter CURRENCY = + new TypeAdapter() { + @Override + public Currency read(JsonReader in) throws IOException { + String s = in.nextString(); + try { + return Currency.getInstance(s); + } catch (IllegalArgumentException e) { + throw new JsonSyntaxException( + "Failed parsing '" + s + "' as Currency; at path " + in.getPreviousPath(), e); + } + } - public static final TypeAdapter CALENDAR = new TypeAdapter() { - private static final String YEAR = "year"; - private static final String MONTH = "month"; - private static final String DAY_OF_MONTH = "dayOfMonth"; - private static final String HOUR_OF_DAY = "hourOfDay"; - private static final String MINUTE = "minute"; - private static final String SECOND = "second"; + @Override + public void write(JsonWriter out, Currency value) throws IOException { + out.value(value.getCurrencyCode()); + } + }.nullSafe(); + public static final TypeAdapterFactory CURRENCY_FACTORY = newFactory(Currency.class, CURRENCY); - @Override - public Calendar read(JsonReader in) throws IOException { - if (in.peek() == JsonToken.NULL) { - in.nextNull(); - return null; - } - in.beginObject(); - int year = 0; - int month = 0; - int dayOfMonth = 0; - int hourOfDay = 0; - int minute = 0; - int second = 0; - while (in.peek() != JsonToken.END_OBJECT) { - String name = in.nextName(); - int value = in.nextInt(); - if (YEAR.equals(name)) { - year = value; - } else if (MONTH.equals(name)) { - month = value; - } else if (DAY_OF_MONTH.equals(name)) { - dayOfMonth = value; - } else if (HOUR_OF_DAY.equals(name)) { - hourOfDay = value; - } else if (MINUTE.equals(name)) { - minute = value; - } else if (SECOND.equals(name)) { - second = value; + public static final TypeAdapter CALENDAR = + new TypeAdapter() { + private static final String YEAR = "year"; + private static final String MONTH = "month"; + private static final String DAY_OF_MONTH = "dayOfMonth"; + private static final String HOUR_OF_DAY = "hourOfDay"; + private static final String MINUTE = "minute"; + private static final String SECOND = "second"; + + @Override + public Calendar read(JsonReader in) throws IOException { + if (in.peek() == JsonToken.NULL) { + in.nextNull(); + return null; + } + in.beginObject(); + int year = 0; + int month = 0; + int dayOfMonth = 0; + int hourOfDay = 0; + int minute = 0; + int second = 0; + while (in.peek() != JsonToken.END_OBJECT) { + String name = in.nextName(); + int value = in.nextInt(); + if (YEAR.equals(name)) { + year = value; + } else if (MONTH.equals(name)) { + month = value; + } else if (DAY_OF_MONTH.equals(name)) { + dayOfMonth = value; + } else if (HOUR_OF_DAY.equals(name)) { + hourOfDay = value; + } else if (MINUTE.equals(name)) { + minute = value; + } else if (SECOND.equals(name)) { + second = value; + } + } + in.endObject(); + return new GregorianCalendar(year, month, dayOfMonth, hourOfDay, minute, second); } - } - in.endObject(); - return new GregorianCalendar(year, month, dayOfMonth, hourOfDay, minute, second); - } - @Override - public void write(JsonWriter out, Calendar value) throws IOException { - if (value == null) { - out.nullValue(); - return; - } - out.beginObject(); - out.name(YEAR); - out.value(value.get(Calendar.YEAR)); - out.name(MONTH); - out.value(value.get(Calendar.MONTH)); - out.name(DAY_OF_MONTH); - out.value(value.get(Calendar.DAY_OF_MONTH)); - out.name(HOUR_OF_DAY); - out.value(value.get(Calendar.HOUR_OF_DAY)); - out.name(MINUTE); - out.value(value.get(Calendar.MINUTE)); - out.name(SECOND); - out.value(value.get(Calendar.SECOND)); - out.endObject(); - } - }; + @Override + public void write(JsonWriter out, Calendar value) throws IOException { + if (value == null) { + out.nullValue(); + return; + } + out.beginObject(); + out.name(YEAR); + out.value(value.get(Calendar.YEAR)); + out.name(MONTH); + out.value(value.get(Calendar.MONTH)); + out.name(DAY_OF_MONTH); + out.value(value.get(Calendar.DAY_OF_MONTH)); + out.name(HOUR_OF_DAY); + out.value(value.get(Calendar.HOUR_OF_DAY)); + out.name(MINUTE); + out.value(value.get(Calendar.MINUTE)); + out.name(SECOND); + out.value(value.get(Calendar.SECOND)); + out.endObject(); + } + }; public static final TypeAdapterFactory CALENDAR_FACTORY = - newFactoryForMultipleTypes(Calendar.class, GregorianCalendar.class, CALENDAR); + newFactoryForMultipleTypes(Calendar.class, GregorianCalendar.class, CALENDAR); + + public static final TypeAdapter LOCALE = + new TypeAdapter() { + @Override + public Locale read(JsonReader in) throws IOException { + if (in.peek() == JsonToken.NULL) { + in.nextNull(); + return null; + } + String locale = in.nextString(); + StringTokenizer tokenizer = new StringTokenizer(locale, "_"); + String language = null; + String country = null; + String variant = null; + if (tokenizer.hasMoreElements()) { + language = tokenizer.nextToken(); + } + if (tokenizer.hasMoreElements()) { + country = tokenizer.nextToken(); + } + if (tokenizer.hasMoreElements()) { + variant = tokenizer.nextToken(); + } + if (country == null && variant == null) { + return new Locale(language); + } else if (variant == null) { + return new Locale(language, country); + } else { + return new Locale(language, country, variant); + } + } - public static final TypeAdapter LOCALE = new TypeAdapter() { - @Override - public Locale read(JsonReader in) throws IOException { - if (in.peek() == JsonToken.NULL) { - in.nextNull(); - return null; - } - String locale = in.nextString(); - StringTokenizer tokenizer = new StringTokenizer(locale, "_"); - String language = null; - String country = null; - String variant = null; - if (tokenizer.hasMoreElements()) { - language = tokenizer.nextToken(); - } - if (tokenizer.hasMoreElements()) { - country = tokenizer.nextToken(); - } - if (tokenizer.hasMoreElements()) { - variant = tokenizer.nextToken(); - } - if (country == null && variant == null) { - return new Locale(language); - } else if (variant == null) { - return new Locale(language, country); - } else { - return new Locale(language, country, variant); - } - } - @Override - public void write(JsonWriter out, Locale value) throws IOException { - out.value(value == null ? null : value.toString()); - } - }; + @Override + public void write(JsonWriter out, Locale value) throws IOException { + out.value(value == null ? null : value.toString()); + } + }; public static final TypeAdapterFactory LOCALE_FACTORY = newFactory(Locale.class, LOCALE); - public static final TypeAdapter JSON_ELEMENT = new TypeAdapter() { - /** - * Tries to begin reading a JSON array or JSON object, returning {@code null} if - * the next element is neither of those. - */ - private JsonElement tryBeginNesting(JsonReader in, JsonToken peeked) throws IOException { - switch (peeked) { - case BEGIN_ARRAY: - in.beginArray(); - return new JsonArray(); - case BEGIN_OBJECT: - in.beginObject(); - return new JsonObject(); - default: - return null; - } - } + public static final TypeAdapter JSON_ELEMENT = + new TypeAdapter() { + /** + * Tries to begin reading a JSON array or JSON object, returning {@code null} if the next + * element is neither of those. + */ + private JsonElement tryBeginNesting(JsonReader in, JsonToken peeked) throws IOException { + switch (peeked) { + case BEGIN_ARRAY: + in.beginArray(); + return new JsonArray(); + case BEGIN_OBJECT: + in.beginObject(); + return new JsonObject(); + default: + return null; + } + } - /** Reads a {@link JsonElement} which cannot have any nested elements */ - private JsonElement readTerminal(JsonReader in, JsonToken peeked) throws IOException { - switch (peeked) { - case STRING: - return new JsonPrimitive(in.nextString()); - case NUMBER: - String number = in.nextString(); - return new JsonPrimitive(new LazilyParsedNumber(number)); - case BOOLEAN: - return new JsonPrimitive(in.nextBoolean()); - case NULL: - in.nextNull(); - return JsonNull.INSTANCE; - default: - // When read(JsonReader) is called with JsonReader in invalid state - throw new IllegalStateException("Unexpected token: " + peeked); - } - } + /** Reads a {@link JsonElement} which cannot have any nested elements */ + private JsonElement readTerminal(JsonReader in, JsonToken peeked) throws IOException { + switch (peeked) { + case STRING: + return new JsonPrimitive(in.nextString()); + case NUMBER: + String number = in.nextString(); + return new JsonPrimitive(new LazilyParsedNumber(number)); + case BOOLEAN: + return new JsonPrimitive(in.nextBoolean()); + case NULL: + in.nextNull(); + return JsonNull.INSTANCE; + default: + // When read(JsonReader) is called with JsonReader in invalid state + throw new IllegalStateException("Unexpected token: " + peeked); + } + } - @Override public JsonElement read(JsonReader in) throws IOException { - if (in instanceof JsonTreeReader) { - return ((JsonTreeReader) in).nextJsonElement(); - } + @Override + public JsonElement read(JsonReader in) throws IOException { + if (in instanceof JsonTreeReader) { + return ((JsonTreeReader) in).nextJsonElement(); + } - // Either JsonArray or JsonObject - JsonElement current; - JsonToken peeked = in.peek(); + // Either JsonArray or JsonObject + JsonElement current; + JsonToken peeked = in.peek(); - current = tryBeginNesting(in, peeked); - if (current == null) { - return readTerminal(in, peeked); - } + current = tryBeginNesting(in, peeked); + if (current == null) { + return readTerminal(in, peeked); + } - Deque stack = new ArrayDeque<>(); + Deque stack = new ArrayDeque<>(); - while (true) { - while (in.hasNext()) { - String name = null; - // Name is only used for JSON object members - if (current instanceof JsonObject) { - name = in.nextName(); - } + while (true) { + while (in.hasNext()) { + String name = null; + // Name is only used for JSON object members + if (current instanceof JsonObject) { + name = in.nextName(); + } - peeked = in.peek(); - JsonElement value = tryBeginNesting(in, peeked); - boolean isNesting = value != null; + peeked = in.peek(); + JsonElement value = tryBeginNesting(in, peeked); + boolean isNesting = value != null; - if (value == null) { - value = readTerminal(in, peeked); - } + if (value == null) { + value = readTerminal(in, peeked); + } - if (current instanceof JsonArray) { - ((JsonArray) current).add(value); - } else { - ((JsonObject) current).add(name, value); - } + if (current instanceof JsonArray) { + ((JsonArray) current).add(value); + } else { + ((JsonObject) current).add(name, value); + } - if (isNesting) { - stack.addLast(current); - current = value; - } - } + if (isNesting) { + stack.addLast(current); + current = value; + } + } - // End current element - if (current instanceof JsonArray) { - in.endArray(); - } else { - in.endObject(); - } + // End current element + if (current instanceof JsonArray) { + in.endArray(); + } else { + in.endObject(); + } - if (stack.isEmpty()) { - return current; - } else { - // Continue with enclosing element - current = stack.removeLast(); + if (stack.isEmpty()) { + return current; + } else { + // Continue with enclosing element + current = stack.removeLast(); + } + } } - } - } - @Override public void write(JsonWriter out, JsonElement value) throws IOException { - if (value == null || value.isJsonNull()) { - out.nullValue(); - } else if (value.isJsonPrimitive()) { - JsonPrimitive primitive = value.getAsJsonPrimitive(); - if (primitive.isNumber()) { - out.value(primitive.getAsNumber()); - } else if (primitive.isBoolean()) { - out.value(primitive.getAsBoolean()); - } else { - out.value(primitive.getAsString()); - } + @Override + public void write(JsonWriter out, JsonElement value) throws IOException { + if (value == null || value.isJsonNull()) { + out.nullValue(); + } else if (value.isJsonPrimitive()) { + JsonPrimitive primitive = value.getAsJsonPrimitive(); + if (primitive.isNumber()) { + out.value(primitive.getAsNumber()); + } else if (primitive.isBoolean()) { + out.value(primitive.getAsBoolean()); + } else { + out.value(primitive.getAsString()); + } - } else if (value.isJsonArray()) { - out.beginArray(); - for (JsonElement e : value.getAsJsonArray()) { - write(out, e); - } - out.endArray(); + } else if (value.isJsonArray()) { + out.beginArray(); + for (JsonElement e : value.getAsJsonArray()) { + write(out, e); + } + out.endArray(); - } else if (value.isJsonObject()) { - out.beginObject(); - for (Map.Entry e : value.getAsJsonObject().entrySet()) { - out.name(e.getKey()); - write(out, e.getValue()); - } - out.endObject(); + } else if (value.isJsonObject()) { + out.beginObject(); + for (Map.Entry e : value.getAsJsonObject().entrySet()) { + out.name(e.getKey()); + write(out, e.getValue()); + } + out.endObject(); - } else { - throw new IllegalArgumentException("Couldn't write " + value.getClass()); - } - } - }; + } else { + throw new IllegalArgumentException("Couldn't write " + value.getClass()); + } + } + }; - public static final TypeAdapterFactory JSON_ELEMENT_FACTORY - = newTypeHierarchyFactory(JsonElement.class, JSON_ELEMENT); + public static final TypeAdapterFactory JSON_ELEMENT_FACTORY = + newTypeHierarchyFactory(JsonElement.class, JSON_ELEMENT); private static final class EnumTypeAdapter> extends TypeAdapter { private final Map nameToConstant = new HashMap<>(); @@ -871,24 +951,28 @@ private static final class EnumTypeAdapter> extends TypeAdapte public EnumTypeAdapter(final Class classOfT) { try { - // Uses reflection to find enum constants to work around name mismatches for obfuscated classes - // Reflection access might throw SecurityException, therefore run this in privileged context; - // should be acceptable because this only retrieves enum constants, but does not expose anything else - Field[] constantFields = AccessController.doPrivileged(new PrivilegedAction() { - @Override public Field[] run() { - Field[] fields = classOfT.getDeclaredFields(); - ArrayList constantFieldsList = new ArrayList<>(fields.length); - for (Field f : fields) { - if (f.isEnumConstant()) { - constantFieldsList.add(f); - } - } - - Field[] constantFields = constantFieldsList.toArray(new Field[0]); - AccessibleObject.setAccessible(constantFields, true); - return constantFields; - } - }); + // Uses reflection to find enum constants to work around name mismatches for obfuscated + // classes Reflection access might throw SecurityException, therefore run this in privileged + // context; should be acceptable because this only retrieves enum constants, but does not + // expose anything else + Field[] constantFields = + AccessController.doPrivileged( + new PrivilegedAction() { + @Override + public Field[] run() { + Field[] fields = classOfT.getDeclaredFields(); + ArrayList constantFieldsList = new ArrayList<>(fields.length); + for (Field f : fields) { + if (f.isEnumConstant()) { + constantFieldsList.add(f); + } + } + + Field[] constantFields = constantFieldsList.toArray(new Field[0]); + AccessibleObject.setAccessible(constantFields, true); + return constantFields; + } + }); for (Field constantField : constantFields) { @SuppressWarnings("unchecked") T constant = (T) constantField.get(null); @@ -910,7 +994,9 @@ public EnumTypeAdapter(final Class classOfT) { throw new AssertionError(e); } } - @Override public T read(JsonReader in) throws IOException { + + @Override + public T read(JsonReader in) throws IOException { if (in.peek() == JsonToken.NULL) { in.nextNull(); return null; @@ -920,31 +1006,35 @@ public EnumTypeAdapter(final Class classOfT) { return (constant == null) ? stringToConstant.get(key) : constant; } - @Override public void write(JsonWriter out, T value) throws IOException { + @Override + public void write(JsonWriter out, T value) throws IOException { out.value(value == null ? null : constantToName.get(value)); } } - public static final TypeAdapterFactory ENUM_FACTORY = new TypeAdapterFactory() { - @Override public TypeAdapter create(Gson gson, TypeToken typeToken) { - Class rawType = typeToken.getRawType(); - if (!Enum.class.isAssignableFrom(rawType) || rawType == Enum.class) { - return null; - } - if (!rawType.isEnum()) { - rawType = rawType.getSuperclass(); // handle anonymous subclasses - } - @SuppressWarnings({"rawtypes", "unchecked"}) - TypeAdapter adapter = (TypeAdapter) new EnumTypeAdapter(rawType); - return adapter; - } - }; + public static final TypeAdapterFactory ENUM_FACTORY = + new TypeAdapterFactory() { + @Override + public TypeAdapter create(Gson gson, TypeToken typeToken) { + Class rawType = typeToken.getRawType(); + if (!Enum.class.isAssignableFrom(rawType) || rawType == Enum.class) { + return null; + } + if (!rawType.isEnum()) { + rawType = rawType.getSuperclass(); // handle anonymous subclasses + } + @SuppressWarnings({"rawtypes", "unchecked"}) + TypeAdapter adapter = (TypeAdapter) new EnumTypeAdapter(rawType); + return adapter; + } + }; public static TypeAdapterFactory newFactory( final TypeToken type, final TypeAdapter typeAdapter) { return new TypeAdapterFactory() { @SuppressWarnings("unchecked") // we use a runtime check to make sure the 'T's equal - @Override public TypeAdapter create(Gson gson, TypeToken typeToken) { + @Override + public TypeAdapter create(Gson gson, TypeToken typeToken) { return typeToken.equals(type) ? (TypeAdapter) typeAdapter : null; } }; @@ -954,10 +1044,13 @@ public static TypeAdapterFactory newFactory( final Class type, final TypeAdapter typeAdapter) { return new TypeAdapterFactory() { @SuppressWarnings("unchecked") // we use a runtime check to make sure the 'T's equal - @Override public TypeAdapter create(Gson gson, TypeToken typeToken) { + @Override + public TypeAdapter create(Gson gson, TypeToken typeToken) { return typeToken.getRawType() == type ? (TypeAdapter) typeAdapter : null; } - @Override public String toString() { + + @Override + public String toString() { return "Factory[type=" + type.getName() + ",adapter=" + typeAdapter + "]"; } }; @@ -967,28 +1060,46 @@ public static TypeAdapterFactory newFactory( final Class unboxed, final Class boxed, final TypeAdapter typeAdapter) { return new TypeAdapterFactory() { @SuppressWarnings("unchecked") // we use a runtime check to make sure the 'T's equal - @Override public TypeAdapter create(Gson gson, TypeToken typeToken) { + @Override + public TypeAdapter create(Gson gson, TypeToken typeToken) { Class rawType = typeToken.getRawType(); return (rawType == unboxed || rawType == boxed) ? (TypeAdapter) typeAdapter : null; } - @Override public String toString() { - return "Factory[type=" + boxed.getName() - + "+" + unboxed.getName() + ",adapter=" + typeAdapter + "]"; + + @Override + public String toString() { + return "Factory[type=" + + boxed.getName() + + "+" + + unboxed.getName() + + ",adapter=" + + typeAdapter + + "]"; } }; } - public static TypeAdapterFactory newFactoryForMultipleTypes(final Class base, - final Class sub, final TypeAdapter typeAdapter) { + public static TypeAdapterFactory newFactoryForMultipleTypes( + final Class base, + final Class sub, + final TypeAdapter typeAdapter) { return new TypeAdapterFactory() { @SuppressWarnings("unchecked") // we use a runtime check to make sure the 'T's equal - @Override public TypeAdapter create(Gson gson, TypeToken typeToken) { + @Override + public TypeAdapter create(Gson gson, TypeToken typeToken) { Class rawType = typeToken.getRawType(); return (rawType == base || rawType == sub) ? (TypeAdapter) typeAdapter : null; } - @Override public String toString() { - return "Factory[type=" + base.getName() - + "+" + sub.getName() + ",adapter=" + typeAdapter + "]"; + + @Override + public String toString() { + return "Factory[type=" + + base.getName() + + "+" + + sub.getName() + + ",adapter=" + + typeAdapter + + "]"; } }; } @@ -1001,27 +1112,38 @@ public static TypeAdapterFactory newTypeHierarchyFactory( final Class clazz, final TypeAdapter typeAdapter) { return new TypeAdapterFactory() { @SuppressWarnings("unchecked") - @Override public TypeAdapter create(Gson gson, TypeToken typeToken) { + @Override + public TypeAdapter create(Gson gson, TypeToken typeToken) { final Class requestedType = typeToken.getRawType(); if (!clazz.isAssignableFrom(requestedType)) { return null; } - return (TypeAdapter) new TypeAdapter() { - @Override public void write(JsonWriter out, T1 value) throws IOException { - typeAdapter.write(out, value); - } + return (TypeAdapter) + new TypeAdapter() { + @Override + public void write(JsonWriter out, T1 value) throws IOException { + typeAdapter.write(out, value); + } - @Override public T1 read(JsonReader in) throws IOException { - T1 result = typeAdapter.read(in); - if (result != null && !requestedType.isInstance(result)) { - throw new JsonSyntaxException("Expected a " + requestedType.getName() - + " but was " + result.getClass().getName() + "; at path " + in.getPreviousPath()); - } - return result; - } - }; + @Override + public T1 read(JsonReader in) throws IOException { + T1 result = typeAdapter.read(in); + if (result != null && !requestedType.isInstance(result)) { + throw new JsonSyntaxException( + "Expected a " + + requestedType.getName() + + " but was " + + result.getClass().getName() + + "; at path " + + in.getPreviousPath()); + } + return result; + } + }; } - @Override public String toString() { + + @Override + public String toString() { return "Factory[typeHierarchy=" + clazz.getName() + ",adapter=" + typeAdapter + "]"; } }; diff --git a/gson/src/main/java/com/google/gson/internal/bind/util/ISO8601Utils.java b/gson/src/main/java/com/google/gson/internal/bind/util/ISO8601Utils.java index 2586674868..7c20e8888a 100644 --- a/gson/src/main/java/com/google/gson/internal/bind/util/ISO8601Utils.java +++ b/gson/src/main/java/com/google/gson/internal/bind/util/ISO8601Utils.java @@ -25,121 +25,122 @@ import java.util.TimeZone; /** - * Utilities methods for manipulating dates in iso8601 format. This is much faster and GC friendly than using SimpleDateFormat so - * highly suitable if you (un)serialize lots of date objects. - * - * Supported parse format: [yyyy-MM-dd|yyyyMMdd][T(hh:mm[:ss[.sss]]|hhmm[ss[.sss]])]?[Z|[+-]hh[:]mm]] - * + * Utilities methods for manipulating dates in iso8601 format. This is much faster and GC friendly + * than using SimpleDateFormat so highly suitable if you (un)serialize lots of date objects. + * + *

Supported parse format: + * [yyyy-MM-dd|yyyyMMdd][T(hh:mm[:ss[.sss]]|hhmm[ss[.sss]])]?[Z|[+-]hh[:]mm]] + * * @see this specification */ // Date parsing code from Jackson databind ISO8601Utils.java // https://github.com/FasterXML/jackson-databind/blob/2.8/src/main/java/com/fasterxml/jackson/databind/util/ISO8601Utils.java -public class ISO8601Utils -{ - /** - * ID to represent the 'UTC' string, default timezone since Jackson 2.7 - * - * @since 2.7 - */ - private static final String UTC_ID = "UTC"; - /** - * The UTC timezone, prefetched to avoid more lookups. - * - * @since 2.7 - */ - private static final TimeZone TIMEZONE_UTC = TimeZone.getTimeZone(UTC_ID); - - /* - /********************************************************** - /* Formatting - /********************************************************** - */ - - /** - * Format a date into 'yyyy-MM-ddThh:mm:ssZ' (default timezone, no milliseconds precision) - * - * @param date the date to format - * @return the date formatted as 'yyyy-MM-ddThh:mm:ssZ' - */ - public static String format(Date date) { - return format(date, false, TIMEZONE_UTC); +public class ISO8601Utils { + /** + * ID to represent the 'UTC' string, default timezone since Jackson 2.7 + * + * @since 2.7 + */ + private static final String UTC_ID = "UTC"; + + /** + * The UTC timezone, prefetched to avoid more lookups. + * + * @since 2.7 + */ + private static final TimeZone TIMEZONE_UTC = TimeZone.getTimeZone(UTC_ID); + + /* + /********************************************************** + /* Formatting + /********************************************************** + */ + + /** + * Format a date into 'yyyy-MM-ddThh:mm:ssZ' (default timezone, no milliseconds precision) + * + * @param date the date to format + * @return the date formatted as 'yyyy-MM-ddThh:mm:ssZ' + */ + public static String format(Date date) { + return format(date, false, TIMEZONE_UTC); + } + + /** + * Format a date into 'yyyy-MM-ddThh:mm:ss[.sss]Z' (GMT timezone) + * + * @param date the date to format + * @param millis true to include millis precision otherwise false + * @return the date formatted as 'yyyy-MM-ddThh:mm:ss[.sss]Z' + */ + public static String format(Date date, boolean millis) { + return format(date, millis, TIMEZONE_UTC); + } + + /** + * Format date into yyyy-MM-ddThh:mm:ss[.sss][Z|[+-]hh:mm] + * + * @param date the date to format + * @param millis true to include millis precision otherwise false + * @param tz timezone to use for the formatting (UTC will produce 'Z') + * @return the date formatted as yyyy-MM-ddThh:mm:ss[.sss][Z|[+-]hh:mm] + */ + public static String format(Date date, boolean millis, TimeZone tz) { + Calendar calendar = new GregorianCalendar(tz, Locale.US); + calendar.setTime(date); + + // estimate capacity of buffer as close as we can (yeah, that's pedantic ;) + int capacity = "yyyy-MM-ddThh:mm:ss".length(); + capacity += millis ? ".sss".length() : 0; + capacity += tz.getRawOffset() == 0 ? "Z".length() : "+hh:mm".length(); + StringBuilder formatted = new StringBuilder(capacity); + + padInt(formatted, calendar.get(Calendar.YEAR), "yyyy".length()); + formatted.append('-'); + padInt(formatted, calendar.get(Calendar.MONTH) + 1, "MM".length()); + formatted.append('-'); + padInt(formatted, calendar.get(Calendar.DAY_OF_MONTH), "dd".length()); + formatted.append('T'); + padInt(formatted, calendar.get(Calendar.HOUR_OF_DAY), "hh".length()); + formatted.append(':'); + padInt(formatted, calendar.get(Calendar.MINUTE), "mm".length()); + formatted.append(':'); + padInt(formatted, calendar.get(Calendar.SECOND), "ss".length()); + if (millis) { + formatted.append('.'); + padInt(formatted, calendar.get(Calendar.MILLISECOND), "sss".length()); } - /** - * Format a date into 'yyyy-MM-ddThh:mm:ss[.sss]Z' (GMT timezone) - * - * @param date the date to format - * @param millis true to include millis precision otherwise false - * @return the date formatted as 'yyyy-MM-ddThh:mm:ss[.sss]Z' - */ - public static String format(Date date, boolean millis) { - return format(date, millis, TIMEZONE_UTC); - } - - /** - * Format date into yyyy-MM-ddThh:mm:ss[.sss][Z|[+-]hh:mm] - * - * @param date the date to format - * @param millis true to include millis precision otherwise false - * @param tz timezone to use for the formatting (UTC will produce 'Z') - * @return the date formatted as yyyy-MM-ddThh:mm:ss[.sss][Z|[+-]hh:mm] - */ - public static String format(Date date, boolean millis, TimeZone tz) { - Calendar calendar = new GregorianCalendar(tz, Locale.US); - calendar.setTime(date); - - // estimate capacity of buffer as close as we can (yeah, that's pedantic ;) - int capacity = "yyyy-MM-ddThh:mm:ss".length(); - capacity += millis ? ".sss".length() : 0; - capacity += tz.getRawOffset() == 0 ? "Z".length() : "+hh:mm".length(); - StringBuilder formatted = new StringBuilder(capacity); - - padInt(formatted, calendar.get(Calendar.YEAR), "yyyy".length()); - formatted.append('-'); - padInt(formatted, calendar.get(Calendar.MONTH) + 1, "MM".length()); - formatted.append('-'); - padInt(formatted, calendar.get(Calendar.DAY_OF_MONTH), "dd".length()); - formatted.append('T'); - padInt(formatted, calendar.get(Calendar.HOUR_OF_DAY), "hh".length()); - formatted.append(':'); - padInt(formatted, calendar.get(Calendar.MINUTE), "mm".length()); - formatted.append(':'); - padInt(formatted, calendar.get(Calendar.SECOND), "ss".length()); - if (millis) { - formatted.append('.'); - padInt(formatted, calendar.get(Calendar.MILLISECOND), "sss".length()); - } - - int offset = tz.getOffset(calendar.getTimeInMillis()); - if (offset != 0) { - int hours = Math.abs((offset / (60 * 1000)) / 60); - int minutes = Math.abs((offset / (60 * 1000)) % 60); - formatted.append(offset < 0 ? '-' : '+'); - padInt(formatted, hours, "hh".length()); - formatted.append(':'); - padInt(formatted, minutes, "mm".length()); - } else { - formatted.append('Z'); - } - - return formatted.toString(); + int offset = tz.getOffset(calendar.getTimeInMillis()); + if (offset != 0) { + int hours = Math.abs((offset / (60 * 1000)) / 60); + int minutes = Math.abs((offset / (60 * 1000)) % 60); + formatted.append(offset < 0 ? '-' : '+'); + padInt(formatted, hours, "hh".length()); + formatted.append(':'); + padInt(formatted, minutes, "mm".length()); + } else { + formatted.append('Z'); } - /* - /********************************************************** - /* Parsing - /********************************************************** - */ - - /** - * Parse a date from ISO-8601 formatted string. It expects a format - * [yyyy-MM-dd|yyyyMMdd][T(hh:mm[:ss[.sss]]|hhmm[ss[.sss]])]?[Z|[+-]hh[:mm]]] - * - * @param date ISO string to parse in the appropriate format. - * @param pos The position to start parsing from, updated to where parsing stopped. - * @return the parsed date - * @throws ParseException if the date is not in the appropriate format - */ + return formatted.toString(); + } + + /* + /********************************************************** + /* Parsing + /********************************************************** + */ + + /** + * Parse a date from ISO-8601 formatted string. It expects a format + * [yyyy-MM-dd|yyyyMMdd][T(hh:mm[:ss[.sss]]|hhmm[ss[.sss]])]?[Z|[+-]hh[:mm]]] + * + * @param date ISO string to parse in the appropriate format. + * @param pos The position to start parsing from, updated to where parsing stopped. + * @return the parsed date + * @throws ParseException if the date is not in the appropriate format + */ public static Date parse(String date, ParsePosition pos) throws ParseException { Exception fail = null; try { @@ -293,77 +294,77 @@ public static Date parse(String date, ParsePosition pos) throws ParseException { throw ex; } - /** - * Check if the expected character exist at the given offset in the value. - * - * @param value the string to check at the specified offset - * @param offset the offset to look for the expected character - * @param expected the expected character - * @return true if the expected character exist at the given offset - */ - private static boolean checkOffset(String value, int offset, char expected) { - return (offset < value.length()) && (value.charAt(offset) == expected); + /** + * Check if the expected character exist at the given offset in the value. + * + * @param value the string to check at the specified offset + * @param offset the offset to look for the expected character + * @param expected the expected character + * @return true if the expected character exist at the given offset + */ + private static boolean checkOffset(String value, int offset, char expected) { + return (offset < value.length()) && (value.charAt(offset) == expected); + } + + /** + * Parse an integer located between 2 given offsets in a string + * + * @param value the string to parse + * @param beginIndex the start index for the integer in the string + * @param endIndex the end index for the integer in the string + * @return the int + * @throws NumberFormatException if the value is not a number + */ + private static int parseInt(String value, int beginIndex, int endIndex) + throws NumberFormatException { + if (beginIndex < 0 || endIndex > value.length() || beginIndex > endIndex) { + throw new NumberFormatException(value); } - - /** - * Parse an integer located between 2 given offsets in a string - * - * @param value the string to parse - * @param beginIndex the start index for the integer in the string - * @param endIndex the end index for the integer in the string - * @return the int - * @throws NumberFormatException if the value is not a number - */ - private static int parseInt(String value, int beginIndex, int endIndex) throws NumberFormatException { - if (beginIndex < 0 || endIndex > value.length() || beginIndex > endIndex) { - throw new NumberFormatException(value); - } - // use same logic as in Integer.parseInt() but less generic we're not supporting negative values - int i = beginIndex; - int result = 0; - int digit; - if (i < endIndex) { - digit = Character.digit(value.charAt(i++), 10); - if (digit < 0) { - throw new NumberFormatException("Invalid number: " + value.substring(beginIndex, endIndex)); - } - result = -digit; - } - while (i < endIndex) { - digit = Character.digit(value.charAt(i++), 10); - if (digit < 0) { - throw new NumberFormatException("Invalid number: " + value.substring(beginIndex, endIndex)); - } - result *= 10; - result -= digit; - } - return -result; + // use same logic as in Integer.parseInt() but less generic we're not supporting negative values + int i = beginIndex; + int result = 0; + int digit; + if (i < endIndex) { + digit = Character.digit(value.charAt(i++), 10); + if (digit < 0) { + throw new NumberFormatException("Invalid number: " + value.substring(beginIndex, endIndex)); + } + result = -digit; } - - /** - * Zero pad a number to a specified length - * - * @param buffer buffer to use for padding - * @param value the integer value to pad if necessary. - * @param length the length of the string we should zero pad - */ - private static void padInt(StringBuilder buffer, int value, int length) { - String strValue = Integer.toString(value); - for (int i = length - strValue.length(); i > 0; i--) { - buffer.append('0'); - } - buffer.append(strValue); + while (i < endIndex) { + digit = Character.digit(value.charAt(i++), 10); + if (digit < 0) { + throw new NumberFormatException("Invalid number: " + value.substring(beginIndex, endIndex)); + } + result *= 10; + result -= digit; } - - /** - * Returns the index of the first character in the string that is not a digit, starting at offset. - */ - private static int indexOfNonDigit(String string, int offset) { - for (int i = offset; i < string.length(); i++) { - char c = string.charAt(i); - if (c < '0' || c > '9') return i; - } - return string.length(); + return -result; + } + + /** + * Zero pad a number to a specified length + * + * @param buffer buffer to use for padding + * @param value the integer value to pad if necessary. + * @param length the length of the string we should zero pad + */ + private static void padInt(StringBuilder buffer, int value, int length) { + String strValue = Integer.toString(value); + for (int i = length - strValue.length(); i > 0; i--) { + buffer.append('0'); } - + buffer.append(strValue); + } + + /** + * Returns the index of the first character in the string that is not a digit, starting at offset. + */ + private static int indexOfNonDigit(String string, int offset) { + for (int i = offset; i < string.length(); i++) { + char c = string.charAt(i); + if (c < '0' || c > '9') return i; + } + return string.length(); + } } diff --git a/gson/src/main/java/com/google/gson/internal/package-info.java b/gson/src/main/java/com/google/gson/internal/package-info.java index e4387cb0fc..20b54fb816 100644 --- a/gson/src/main/java/com/google/gson/internal/package-info.java +++ b/gson/src/main/java/com/google/gson/internal/package-info.java @@ -15,8 +15,8 @@ */ /** - * Do NOT use any class in this package as they are meant for internal use in Gson. - * These classes will very likely change incompatibly in future versions. You have been warned. + * Do NOT use any class in this package as they are meant for internal use in Gson. These classes + * will very likely change incompatibly in future versions. You have been warned. * * @author Inderjeet Singh, Joel Leitch, Jesse Wilson */ diff --git a/gson/src/main/java/com/google/gson/internal/reflect/ReflectionHelper.java b/gson/src/main/java/com/google/gson/internal/reflect/ReflectionHelper.java index 0345292523..1bfdb83b6b 100644 --- a/gson/src/main/java/com/google/gson/internal/reflect/ReflectionHelper.java +++ b/gson/src/main/java/com/google/gson/internal/reflect/ReflectionHelper.java @@ -31,7 +31,8 @@ public class ReflectionHelper { static { RecordHelper instance; try { - // Try to construct the RecordSupportedHelper, if this fails, records are not supported on this JVM. + // Try to construct the RecordSupportedHelper, if this fails, records are not supported on + // this JVM. instance = new RecordSupportedHelper(); } catch (ReflectiveOperationException e) { instance = new RecordNotSupportedHelper(); @@ -45,8 +46,10 @@ private static String getInaccessibleTroubleshootingSuffix(Exception e) { // Class was added in Java 9, therefore cannot use instanceof if (e.getClass().getName().equals("java.lang.reflect.InaccessibleObjectException")) { String message = e.getMessage(); - String troubleshootingId = message != null && message.contains("to module com.google.gson") - ? "reflection-inaccessible-to-module-gson" : "reflection-inaccessible"; + String troubleshootingId = + message != null && message.contains("to module com.google.gson") + ? "reflection-inaccessible-to-module-gson" + : "reflection-inaccessible"; return "\nSee " + TroubleshootingGuide.createUrl(troubleshootingId); } return ""; @@ -55,7 +58,8 @@ private static String getInaccessibleTroubleshootingSuffix(Exception e) { /** * Internal implementation of making an {@link AccessibleObject} accessible. * - * @param object the object that {@link AccessibleObject#setAccessible(boolean)} should be called on. + * @param object the object that {@link AccessibleObject#setAccessible(boolean)} should be called + * on. * @throws JsonIOException if making the object accessible fails */ public static void makeAccessible(AccessibleObject object) throws JsonIOException { @@ -63,22 +67,26 @@ public static void makeAccessible(AccessibleObject object) throws JsonIOExceptio object.setAccessible(true); } catch (Exception exception) { String description = getAccessibleObjectDescription(object, false); - throw new JsonIOException("Failed making " + description + " accessible; either increase its visibility" - + " or write a custom TypeAdapter for its declaring type." + getInaccessibleTroubleshootingSuffix(exception), + throw new JsonIOException( + "Failed making " + + description + + " accessible; either increase its visibility" + + " or write a custom TypeAdapter for its declaring type." + + getInaccessibleTroubleshootingSuffix(exception), exception); } } /** - * Returns a short string describing the {@link AccessibleObject} in a human-readable way. - * The result is normally shorter than {@link AccessibleObject#toString()} because it omits - * modifiers (e.g. {@code final}) and uses simple names for constructor and method parameter - * types. + * Returns a short string describing the {@link AccessibleObject} in a human-readable way. The + * result is normally shorter than {@link AccessibleObject#toString()} because it omits modifiers + * (e.g. {@code final}) and uses simple names for constructor and method parameter types. * * @param object object to describe * @param uppercaseFirstLetter whether the first letter of the description should be uppercased */ - public static String getAccessibleObjectDescription(AccessibleObject object, boolean uppercaseFirstLetter) { + public static String getAccessibleObjectDescription( + AccessibleObject object, boolean uppercaseFirstLetter) { String description; if (object instanceof Field) { @@ -103,17 +111,14 @@ public static String getAccessibleObjectDescription(AccessibleObject object, boo return description; } - /** - * Creates a string representation for a field, omitting modifiers and - * the field type. - */ + /** Creates a string representation for a field, omitting modifiers and the field type. */ public static String fieldToString(Field field) { return field.getDeclaringClass().getName() + "#" + field.getName(); } /** - * Creates a string representation for a constructor. - * E.g.: {@code java.lang.String(char[], int, int)} + * Creates a string representation for a constructor. E.g.: {@code java.lang.String(char[], int, + * int)} */ public static String constructorToString(Constructor constructor) { StringBuilder stringBuilder = new StringBuilder(constructor.getDeclaringClass().getName()); @@ -122,13 +127,15 @@ public static String constructorToString(Constructor constructor) { return stringBuilder.toString(); } - // Note: Ideally parameter type would be java.lang.reflect.Executable, but that was added in Java 8 - private static void appendExecutableParameters(AccessibleObject executable, StringBuilder stringBuilder) { + // Ideally parameter type would be java.lang.reflect.Executable, but that was added in Java 8 + private static void appendExecutableParameters( + AccessibleObject executable, StringBuilder stringBuilder) { stringBuilder.append('('); - Class[] parameters = (executable instanceof Method) - ? ((Method) executable).getParameterTypes() - : ((Constructor) executable).getParameterTypes(); + Class[] parameters = + (executable instanceof Method) + ? ((Method) executable).getParameterTypes() + : ((Constructor) executable).getParameterTypes(); for (int i = 0; i < parameters.length; i++) { if (i > 0) { stringBuilder.append(", "); @@ -140,22 +147,24 @@ private static void appendExecutableParameters(AccessibleObject executable, Stri } /** - * Tries making the constructor accessible, returning an exception message - * if this fails. + * Tries making the constructor accessible, returning an exception message if this fails. * * @param constructor constructor to make accessible - * @return exception message; {@code null} if successful, non-{@code null} if - * unsuccessful + * @return exception message; {@code null} if successful, non-{@code null} if unsuccessful */ public static String tryMakeAccessible(Constructor constructor) { try { constructor.setAccessible(true); return null; } catch (Exception exception) { - return "Failed making constructor '" + constructorToString(constructor) + "' accessible;" + return "Failed making constructor '" + + constructorToString(constructor) + + "' accessible;" + " either increase its visibility or write a custom InstanceCreator or TypeAdapter for" // Include the message since it might contain more detailed information - + " its declaring type: " + exception.getMessage() + getInaccessibleTroubleshootingSuffix(exception); + + " its declaring type: " + + exception.getMessage() + + getInaccessibleTroubleshootingSuffix(exception); } } @@ -179,26 +188,28 @@ public static Constructor getCanonicalRecordConstructor(Class raw) { public static RuntimeException createExceptionForUnexpectedIllegalAccess( IllegalAccessException exception) { - throw new RuntimeException("Unexpected IllegalAccessException occurred (Gson " + GsonBuildConfig.VERSION + ")." - + " Certain ReflectionAccessFilter features require Java >= 9 to work correctly. If you are not using" - + " ReflectionAccessFilter, report this to the Gson maintainers.", + throw new RuntimeException( + "Unexpected IllegalAccessException occurred (Gson " + + GsonBuildConfig.VERSION + + "). Certain ReflectionAccessFilter features require Java >= 9 to work correctly. If" + + " you are not using ReflectionAccessFilter, report this to the Gson maintainers.", exception); } - private static RuntimeException createExceptionForRecordReflectionException( - ReflectiveOperationException exception) { - throw new RuntimeException("Unexpected ReflectiveOperationException occurred" - + " (Gson " + GsonBuildConfig.VERSION + ")." + ReflectiveOperationException exception) { + throw new RuntimeException( + "Unexpected ReflectiveOperationException occurred" + + " (Gson " + + GsonBuildConfig.VERSION + + ")." + " To support Java records, reflection is utilized to read out information" + " about records. All these invocations happens after it is established" + " that records exist in the JVM. This exception is unexpected behavior.", - exception); + exception); } - /** - * Internal abstraction over reflection when Records are supported. - */ + /** Internal abstraction over reflection when Records are supported. */ private abstract static class RecordHelper { abstract boolean isRecord(Class clazz); @@ -254,8 +265,8 @@ public Constructor getCanonicalRecordConstructor(Class raw) { for (int i = 0; i < recordComponents.length; i++) { recordComponentTypes[i] = (Class) getType.invoke(recordComponents[i]); } - // Uses getDeclaredConstructor because implicit constructor has same visibility as record and might - // therefore not be public + // Uses getDeclaredConstructor because implicit constructor has same visibility as record + // and might therefore not be public return raw.getDeclaredConstructor(recordComponentTypes); } catch (ReflectiveOperationException e) { throw createExceptionForRecordReflectionException(e); @@ -265,8 +276,9 @@ public Constructor getCanonicalRecordConstructor(Class raw) { @Override public Method getAccessor(Class raw, Field field) { try { - // Records consists of record components, each with a unique name, a corresponding field and accessor method - // with the same name. Ref.: https://docs.oracle.com/javase/specs/jls/se17/html/jls-8.html#jls-8.10.3 + // Records consists of record components, each with a unique name, a corresponding field and + // accessor method with the same name. Ref.: + // https://docs.oracle.com/javase/specs/jls/se17/html/jls-8.html#jls-8.10.3 return raw.getMethod(field.getName()); } catch (ReflectiveOperationException e) { throw createExceptionForRecordReflectionException(e); @@ -274,9 +286,7 @@ public Method getAccessor(Class raw, Field field) { } } - /** - * Instance used when records are not supported - */ + /** Instance used when records are not supported */ private static class RecordNotSupportedHelper extends RecordHelper { @Override @@ -287,19 +297,19 @@ boolean isRecord(Class clazz) { @Override String[] getRecordComponentNames(Class clazz) { throw new UnsupportedOperationException( - "Records are not supported on this JVM, this method should not be called"); + "Records are not supported on this JVM, this method should not be called"); } @Override Constructor getCanonicalRecordConstructor(Class raw) { throw new UnsupportedOperationException( - "Records are not supported on this JVM, this method should not be called"); + "Records are not supported on this JVM, this method should not be called"); } @Override public Method getAccessor(Class raw, Field field) { throw new UnsupportedOperationException( - "Records are not supported on this JVM, this method should not be called"); + "Records are not supported on this JVM, this method should not be called"); } } } diff --git a/gson/src/main/java/com/google/gson/internal/sql/SqlDateTypeAdapter.java b/gson/src/main/java/com/google/gson/internal/sql/SqlDateTypeAdapter.java index 271f23c21c..bebd6ca777 100644 --- a/gson/src/main/java/com/google/gson/internal/sql/SqlDateTypeAdapter.java +++ b/gson/src/main/java/com/google/gson/internal/sql/SqlDateTypeAdapter.java @@ -31,25 +31,26 @@ import java.util.Date; /** - * Adapter for java.sql.Date. Although this class appears stateless, it is not. - * DateFormat captures its time zone and locale when it is created, which gives - * this class state. DateFormat isn't thread safe either, so this class has - * to synchronize its read and write methods. + * Adapter for java.sql.Date. Although this class appears stateless, it is not. DateFormat captures + * its time zone and locale when it is created, which gives this class state. DateFormat isn't + * thread safe either, so this class has to synchronize its read and write methods. */ @SuppressWarnings("JavaUtilDate") final class SqlDateTypeAdapter extends TypeAdapter { - static final TypeAdapterFactory FACTORY = new TypeAdapterFactory() { - @SuppressWarnings("unchecked") // we use a runtime check to make sure the 'T's equal - @Override public TypeAdapter create(Gson gson, TypeToken typeToken) { - return typeToken.getRawType() == java.sql.Date.class - ? (TypeAdapter) new SqlDateTypeAdapter() : null; - } - }; + static final TypeAdapterFactory FACTORY = + new TypeAdapterFactory() { + @SuppressWarnings("unchecked") // we use a runtime check to make sure the 'T's equal + @Override + public TypeAdapter create(Gson gson, TypeToken typeToken) { + return typeToken.getRawType() == java.sql.Date.class + ? (TypeAdapter) new SqlDateTypeAdapter() + : null; + } + }; private final DateFormat format = new SimpleDateFormat("MMM d, yyyy"); - private SqlDateTypeAdapter() { - } + private SqlDateTypeAdapter() {} @Override public java.sql.Date read(JsonReader in) throws IOException { @@ -65,7 +66,8 @@ public java.sql.Date read(JsonReader in) throws IOException { } return new java.sql.Date(utilDate.getTime()); } catch (ParseException e) { - throw new JsonSyntaxException("Failed parsing '" + s + "' as SQL Date; at path " + in.getPreviousPath(), e); + throw new JsonSyntaxException( + "Failed parsing '" + s + "' as SQL Date; at path " + in.getPreviousPath(), e); } } diff --git a/gson/src/main/java/com/google/gson/internal/sql/SqlTimeTypeAdapter.java b/gson/src/main/java/com/google/gson/internal/sql/SqlTimeTypeAdapter.java index ac327ff16f..25a6fda95a 100644 --- a/gson/src/main/java/com/google/gson/internal/sql/SqlTimeTypeAdapter.java +++ b/gson/src/main/java/com/google/gson/internal/sql/SqlTimeTypeAdapter.java @@ -32,26 +32,29 @@ import java.util.Date; /** - * Adapter for java.sql.Time. Although this class appears stateless, it is not. - * DateFormat captures its time zone and locale when it is created, which gives - * this class state. DateFormat isn't thread safe either, so this class has - * to synchronize its read and write methods. + * Adapter for java.sql.Time. Although this class appears stateless, it is not. DateFormat captures + * its time zone and locale when it is created, which gives this class state. DateFormat isn't + * thread safe either, so this class has to synchronize its read and write methods. */ @SuppressWarnings("JavaUtilDate") final class SqlTimeTypeAdapter extends TypeAdapter

If {@link #SUPPORTS_SQL_TYPES} is {@code true}, all other - * constants of this class will be non-{@code null}. However, if - * it is {@code false} all other constants will be {@code null} and + *

If {@link #SUPPORTS_SQL_TYPES} is {@code true}, all other constants of this class will be + * non-{@code null}. However, if it is {@code false} all other constants will be {@code null} and * there will be no support for {@code java.sql} types. */ @SuppressWarnings("JavaUtilDate") public final class SqlTypesSupport { - /** - * {@code true} if {@code java.sql} types are supported, - * {@code false} otherwise - */ + /** {@code true} if {@code java.sql} types are supported, {@code false} otherwise */ public static final boolean SUPPORTS_SQL_TYPES; public static final DateType DATE_DATE_TYPE; @@ -59,16 +53,20 @@ public final class SqlTypesSupport { SUPPORTS_SQL_TYPES = sqlTypesSupport; if (SUPPORTS_SQL_TYPES) { - DATE_DATE_TYPE = new DateType(java.sql.Date.class) { - @Override protected java.sql.Date deserialize(Date date) { - return new java.sql.Date(date.getTime()); - } - }; - TIMESTAMP_DATE_TYPE = new DateType(Timestamp.class) { - @Override protected Timestamp deserialize(Date date) { - return new Timestamp(date.getTime()); - } - }; + DATE_DATE_TYPE = + new DateType(java.sql.Date.class) { + @Override + protected java.sql.Date deserialize(Date date) { + return new java.sql.Date(date.getTime()); + } + }; + TIMESTAMP_DATE_TYPE = + new DateType(Timestamp.class) { + @Override + protected Timestamp deserialize(Date date) { + return new Timestamp(date.getTime()); + } + }; DATE_FACTORY = SqlDateTypeAdapter.FACTORY; TIME_FACTORY = SqlTimeTypeAdapter.FACTORY; @@ -83,6 +81,5 @@ public final class SqlTypesSupport { } } - private SqlTypesSupport() { - } + private SqlTypesSupport() {} } diff --git a/gson/src/main/java/com/google/gson/package-info.java b/gson/src/main/java/com/google/gson/package-info.java index 4278370350..b7d3b9abe2 100644 --- a/gson/src/main/java/com/google/gson/package-info.java +++ b/gson/src/main/java/com/google/gson/package-info.java @@ -18,9 +18,9 @@ * This package provides the {@link com.google.gson.Gson} class to convert Json to Java and * vice-versa. * - *

The primary class to use is {@link com.google.gson.Gson} which can be constructed with - * {@code new Gson()} (using default settings) or by using {@link com.google.gson.GsonBuilder} - * (to configure various options such as using versioning and so on).

+ *

The primary class to use is {@link com.google.gson.Gson} which can be constructed with {@code + * new Gson()} (using default settings) or by using {@link com.google.gson.GsonBuilder} (to + * configure various options such as using versioning and so on). * * @author Inderjeet Singh, Joel Leitch */ diff --git a/gson/src/main/java/com/google/gson/reflect/TypeToken.java b/gson/src/main/java/com/google/gson/reflect/TypeToken.java index e91a6d731b..cd633ebc73 100644 --- a/gson/src/main/java/com/google/gson/reflect/TypeToken.java +++ b/gson/src/main/java/com/google/gson/reflect/TypeToken.java @@ -28,28 +28,24 @@ import java.util.Objects; /** - * Represents a generic type {@code T}. Java doesn't yet provide a way to - * represent generic types, so this class does. Forces clients to create a - * subclass of this class which enables retrieval the type information even at - * runtime. + * Represents a generic type {@code T}. Java doesn't yet provide a way to represent generic types, + * so this class does. Forces clients to create a subclass of this class which enables retrieval the + * type information even at runtime. * - *

For example, to create a type literal for {@code List}, you can - * create an empty anonymous class: + *

For example, to create a type literal for {@code List}, you can create an empty + * anonymous class: * - *

- * {@code TypeToken> list = new TypeToken>() {};} + *

{@code TypeToken> list = new TypeToken>() {};} * - *

Capturing a type variable as type argument of an anonymous {@code TypeToken} - * subclass is not allowed, for example {@code TypeToken>}. - * Due to type erasure the runtime type of a type variable is not available - * to Gson and therefore it cannot provide the functionality one might expect. - * This would give a false sense of type-safety at compile time and could - * lead to an unexpected {@code ClassCastException} at runtime. + *

Capturing a type variable as type argument of an anonymous {@code TypeToken} subclass is not + * allowed, for example {@code TypeToken>}. Due to type erasure the runtime type of a type + * variable is not available to Gson and therefore it cannot provide the functionality one might + * expect. This would give a false sense of type-safety at compile time and could lead to an + * unexpected {@code ClassCastException} at runtime. * - *

If the type arguments of the parameterized type are only available at - * runtime, for example when you want to create a {@code List} based on - * a {@code Class} representing the element type, the method - * {@link #getParameterized(Type, Type...)} can be used. + *

If the type arguments of the parameterized type are only available at runtime, for example + * when you want to create a {@code List} based on a {@code Class} representing the element + * type, the method {@link #getParameterized(Type, Type...)} can be used. * * @author Bob Lee * @author Sven Mawson @@ -61,19 +57,17 @@ public class TypeToken { private final int hashCode; /** - * Constructs a new type literal. Derives represented class from type - * parameter. + * Constructs a new type literal. Derives represented class from type parameter. * - *

Clients create an empty anonymous subclass. Doing so embeds the type - * parameter in the anonymous class's type hierarchy so we can reconstitute it - * at runtime despite erasure, for example: - *

- * {@code new TypeToken>() {}} + *

Clients create an empty anonymous subclass. Doing so embeds the type parameter in the + * anonymous class's type hierarchy so we can reconstitute it at runtime despite erasure, for + * example: * - * @throws IllegalArgumentException - * If the anonymous {@code TypeToken} subclass captures a type variable, - * for example {@code TypeToken>}. See the {@code TypeToken} - * class documentation for more details. + *

{@code new TypeToken>() {}} + * + * @throws IllegalArgumentException If the anonymous {@code TypeToken} subclass captures a type + * variable, for example {@code TypeToken>}. See the {@code TypeToken} class + * documentation for more details. */ @SuppressWarnings("unchecked") protected TypeToken() { @@ -82,9 +76,7 @@ protected TypeToken() { this.hashCode = type.hashCode(); } - /** - * Unsafe. Constructs a type literal manually. - */ + /** Unsafe. Constructs a type literal manually. */ @SuppressWarnings("unchecked") private TypeToken(Type type) { this.type = $Gson$Types.canonicalize(Objects.requireNonNull(type)); @@ -97,9 +89,8 @@ private static boolean isCapturingTypeVariablesForbidden() { } /** - * Verifies that {@code this} is an instance of a direct subclass of TypeToken and - * returns the type argument for {@code T} in {@link $Gson$Types#canonicalize - * canonical form}. + * Verifies that {@code this} is an instance of a direct subclass of TypeToken and returns the + * type argument for {@code T} in {@link $Gson$Types#canonicalize canonical form}. */ private Type getTypeTokenTypeArgument() { Type superclass = getClass().getGenericSuperclass(); @@ -116,10 +107,11 @@ private Type getTypeTokenTypeArgument() { } // Check for raw TypeToken as superclass else if (superclass == TypeToken.class) { - throw new IllegalStateException("TypeToken must be created with a type argument: new TypeToken<...>() {};" - + " When using code shrinkers (ProGuard, R8, ...) make sure that generic signatures are preserved." - + "\nSee " + TroubleshootingGuide.createUrl("type-token-raw") - ); + throw new IllegalStateException( + "TypeToken must be created with a type argument: new TypeToken<...>() {}; When using code" + + " shrinkers (ProGuard, R8, ...) make sure that generic signatures are preserved.\n" + + "See " + + TroubleshootingGuide.createUrl("type-token-raw")); } // User created subclass of subclass of TypeToken @@ -129,9 +121,13 @@ else if (superclass == TypeToken.class) { private static void verifyNoTypeVariable(Type type) { if (type instanceof TypeVariable) { TypeVariable typeVariable = (TypeVariable) type; - throw new IllegalArgumentException("TypeToken type argument must not contain a type variable; captured type variable " - + typeVariable.getName() + " declared by " + typeVariable.getGenericDeclaration() - + "\nSee " + TroubleshootingGuide.createUrl("typetoken-type-variable")); + throw new IllegalArgumentException( + "TypeToken type argument must not contain a type variable; captured type variable " + + typeVariable.getName() + + " declared by " + + typeVariable.getGenericDeclaration() + + "\nSee " + + TroubleshootingGuide.createUrl("typetoken-type-variable")); } else if (type instanceof GenericArrayType) { verifyNoTypeVariable(((GenericArrayType) type).getGenericComponentType()); } else if (type instanceof ParameterizedType) { @@ -153,22 +149,20 @@ private static void verifyNoTypeVariable(Type type) { verifyNoTypeVariable(bound); } } else if (type == null) { - // Occurs in Eclipse IDE and certain Java versions (e.g. Java 11.0.18) when capturing type variable - // declared by method of local class, see https://github.com/eclipse-jdt/eclipse.jdt.core/issues/975 - throw new IllegalArgumentException("TypeToken captured `null` as type argument; probably a compiler / runtime bug"); + // Occurs in Eclipse IDE and certain Java versions (e.g. Java 11.0.18) when capturing type + // variable declared by method of local class, see + // https://github.com/eclipse-jdt/eclipse.jdt.core/issues/975 + throw new IllegalArgumentException( + "TypeToken captured `null` as type argument; probably a compiler / runtime bug"); } } - /** - * Returns the raw (non-generic) type for this type. - */ + /** Returns the raw (non-generic) type for this type. */ public final Class getRawType() { return rawType; } - /** - * Gets underlying {@code Type} instance. - */ + /** Gets underlying {@code Type} instance. */ public final Type getType() { return type; } @@ -176,8 +170,7 @@ public final Type getType() { /** * Check if this type is assignable from the given class object. * - * @deprecated this implementation may be inconsistent with javac for types - * with wildcards. + * @deprecated this implementation may be inconsistent with javac for types with wildcards. */ @Deprecated public boolean isAssignableFrom(Class cls) { @@ -187,8 +180,7 @@ public boolean isAssignableFrom(Class cls) { /** * Check if this type is assignable from the given Type. * - * @deprecated this implementation may be inconsistent with javac for types - * with wildcards. + * @deprecated this implementation may be inconsistent with javac for types with wildcards. */ @Deprecated public boolean isAssignableFrom(Type from) { @@ -203,8 +195,7 @@ public boolean isAssignableFrom(Type from) { if (type instanceof Class) { return rawType.isAssignableFrom($Gson$Types.getRawType(from)); } else if (type instanceof ParameterizedType) { - return isAssignableFrom(from, (ParameterizedType) type, - new HashMap()); + return isAssignableFrom(from, (ParameterizedType) type, new HashMap()); } else if (type instanceof GenericArrayType) { return rawType.isAssignableFrom($Gson$Types.getRawType(from)) && isAssignableFrom(from, (GenericArrayType) type); @@ -217,8 +208,7 @@ public boolean isAssignableFrom(Type from) { /** * Check if this type is assignable from the given type token. * - * @deprecated this implementation may be inconsistent with javac for types - * with wildcards. + * @deprecated this implementation may be inconsistent with javac for types with wildcards. */ @Deprecated public boolean isAssignableFrom(TypeToken token) { @@ -226,8 +216,8 @@ public boolean isAssignableFrom(TypeToken token) { } /** - * Private helper function that performs some assignability checks for - * the provided GenericArrayType. + * Private helper function that performs some assignability checks for the provided + * GenericArrayType. */ private static boolean isAssignableFrom(Type from, GenericArrayType to) { Type toGenericComponentType = to.getGenericComponentType(); @@ -242,20 +232,17 @@ private static boolean isAssignableFrom(Type from, GenericArrayType to) { } t = classType; } - return isAssignableFrom(t, (ParameterizedType) toGenericComponentType, - new HashMap()); + return isAssignableFrom( + t, (ParameterizedType) toGenericComponentType, new HashMap()); } // No generic defined on "to"; therefore, return true and let other // checks determine assignability return true; } - /** - * Private recursive helper function to actually do the type-safe checking - * of assignability. - */ - private static boolean isAssignableFrom(Type from, ParameterizedType to, - Map typeVarMap) { + /** Private recursive helper function to actually do the type-safe checking of assignability. */ + private static boolean isAssignableFrom( + Type from, ParameterizedType to, Map typeVarMap) { if (from == null) { return false; @@ -304,11 +291,11 @@ private static boolean isAssignableFrom(Type from, ParameterizedType to, } /** - * Checks if two parameterized types are exactly equal, under the variable - * replacement described in the typeVarMap. + * Checks if two parameterized types are exactly equal, under the variable replacement described + * in the typeVarMap. */ - private static boolean typeEquals(ParameterizedType from, - ParameterizedType to, Map typeVarMap) { + private static boolean typeEquals( + ParameterizedType from, ParameterizedType to, Map typeVarMap) { if (from.getRawType().equals(to.getRawType())) { Type[] fromArgs = from.getActualTypeArguments(); Type[] toArgs = to.getActualTypeArguments(); @@ -322,55 +309,54 @@ private static boolean typeEquals(ParameterizedType from, return false; } - private static AssertionError buildUnexpectedTypeError( - Type token, Class... expected) { + private static AssertionError buildUnexpectedTypeError(Type token, Class... expected) { // Build exception message - StringBuilder exceptionMessage = - new StringBuilder("Unexpected type. Expected one of: "); + StringBuilder exceptionMessage = new StringBuilder("Unexpected type. Expected one of: "); for (Class clazz : expected) { exceptionMessage.append(clazz.getName()).append(", "); } - exceptionMessage.append("but got: ").append(token.getClass().getName()) - .append(", for type token: ").append(token.toString()).append('.'); + exceptionMessage + .append("but got: ") + .append(token.getClass().getName()) + .append(", for type token: ") + .append(token.toString()) + .append('.'); return new AssertionError(exceptionMessage.toString()); } /** - * Checks if two types are the same or are equivalent under a variable mapping - * given in the type map that was provided. + * Checks if two types are the same or are equivalent under a variable mapping given in the type + * map that was provided. */ private static boolean matches(Type from, Type to, Map typeMap) { return to.equals(from) || (from instanceof TypeVariable - && to.equals(typeMap.get(((TypeVariable) from).getName()))); - + && to.equals(typeMap.get(((TypeVariable) from).getName()))); } - @Override public final int hashCode() { + @Override + public final int hashCode() { return this.hashCode; } - @Override public final boolean equals(Object o) { - return o instanceof TypeToken - && $Gson$Types.equals(type, ((TypeToken) o).type); + @Override + public final boolean equals(Object o) { + return o instanceof TypeToken && $Gson$Types.equals(type, ((TypeToken) o).type); } - @Override public final String toString() { + @Override + public final String toString() { return $Gson$Types.typeToString(type); } - /** - * Gets type literal for the given {@code Type} instance. - */ + /** Gets type literal for the given {@code Type} instance. */ public static TypeToken get(Type type) { return new TypeToken<>(type); } - /** - * Gets type literal for the given {@code Class} instance. - */ + /** Gets type literal for the given {@code Class} instance. */ public static TypeToken get(Class type) { return new TypeToken<>(type); } @@ -380,20 +366,21 @@ public static TypeToken get(Class type) { * {@code rawType}. This is mainly intended for situations where the type arguments are not * available at compile time. The following example shows how a type token for {@code Map} * can be created: + * *

{@code
    * Class keyClass = ...;
    * Class valueClass = ...;
    * TypeToken mapTypeToken = TypeToken.getParameterized(Map.class, keyClass, valueClass);
    * }
+ * * As seen here the result is a {@code TypeToken}; this method cannot provide any type-safety, * and care must be taken to pass in the correct number of type arguments. * *

If {@code rawType} is a non-generic class and no type arguments are provided, this method * simply delegates to {@link #get(Class)} and creates a {@code TypeToken(Class)}. * - * @throws IllegalArgumentException - * If {@code rawType} is not of type {@code Class}, or if the type arguments are invalid for - * the raw type + * @throws IllegalArgumentException If {@code rawType} is not of type {@code Class}, or if the + * type arguments are invalid for the raw type */ public static TypeToken getParameterized(Type rawType, Type... typeArguments) { Objects.requireNonNull(rawType); @@ -411,8 +398,12 @@ public static TypeToken getParameterized(Type rawType, Type... typeArguments) int expectedArgsCount = typeVariables.length; int actualArgsCount = typeArguments.length; if (actualArgsCount != expectedArgsCount) { - throw new IllegalArgumentException(rawClass.getName() + " requires " + expectedArgsCount + - " type arguments, but got " + actualArgsCount); + throw new IllegalArgumentException( + rawClass.getName() + + " requires " + + expectedArgsCount + + " type arguments, but got " + + actualArgsCount); } // For legacy reasons create a TypeToken(Class) if the type is not generic @@ -422,12 +413,16 @@ public static TypeToken getParameterized(Type rawType, Type... typeArguments) // Check for this here to avoid misleading exception thrown by ParameterizedTypeImpl if ($Gson$Types.requiresOwnerType(rawType)) { - throw new IllegalArgumentException("Raw type " + rawClass.getName() + " is not supported because" - + " it requires specifying an owner type"); + throw new IllegalArgumentException( + "Raw type " + + rawClass.getName() + + " is not supported because" + + " it requires specifying an owner type"); } for (int i = 0; i < expectedArgsCount; i++) { - Type typeArgument = Objects.requireNonNull(typeArguments[i], "Type argument must not be null"); + Type typeArgument = + Objects.requireNonNull(typeArguments[i], "Type argument must not be null"); Class rawTypeArgument = $Gson$Types.getRawType(typeArgument); TypeVariable typeVariable = typeVariables[i]; @@ -435,8 +430,14 @@ public static TypeToken getParameterized(Type rawType, Type... typeArguments) Class rawBound = $Gson$Types.getRawType(bound); if (!rawBound.isAssignableFrom(rawTypeArgument)) { - throw new IllegalArgumentException("Type argument " + typeArgument + " does not satisfy bounds" - + " for type variable " + typeVariable + " declared by " + rawType); + throw new IllegalArgumentException( + "Type argument " + + typeArgument + + " does not satisfy bounds" + + " for type variable " + + typeVariable + + " declared by " + + rawType); } } } diff --git a/gson/src/main/java/com/google/gson/stream/JsonReader.java b/gson/src/main/java/com/google/gson/stream/JsonReader.java index 534e7a4f10..ee7f49ccbe 100644 --- a/gson/src/main/java/com/google/gson/stream/JsonReader.java +++ b/gson/src/main/java/com/google/gson/stream/JsonReader.java @@ -30,54 +30,56 @@ import java.util.Objects; /** - * Reads a JSON (RFC 8259) - * encoded value as a stream of tokens. This stream includes both literal - * values (strings, numbers, booleans, and nulls) as well as the begin and - * end delimiters of objects and arrays. The tokens are traversed in - * depth-first order, the same order that they appear in the JSON document. - * Within JSON objects, name/value pairs are represented by a single token. + * Reads a JSON (RFC 8259) encoded value as a + * stream of tokens. This stream includes both literal values (strings, numbers, booleans, and + * nulls) as well as the begin and end delimiters of objects and arrays. The tokens are traversed in + * depth-first order, the same order that they appear in the JSON document. Within JSON objects, + * name/value pairs are represented by a single token. * *

Parsing JSON

- * To create a recursive descent parser for your own JSON streams, first create - * an entry point method that creates a {@code JsonReader}. * - *

Next, create handler methods for each structure in your JSON text. You'll - * need a method for each object type and for each array type. + * To create a recursive descent parser for your own JSON streams, first create an entry point + * method that creates a {@code JsonReader}. + * + *

Next, create handler methods for each structure in your JSON text. You'll need a method for + * each object type and for each array type. + * *

    - *
  • Within array handling methods, first call {@link - * #beginArray} to consume the array's opening bracket. Then create a - * while loop that accumulates values, terminating when {@link #hasNext} - * is false. Finally, read the array's closing bracket by calling {@link + *
  • Within array handling methods, first call {@link #beginArray} to consume + * the array's opening bracket. Then create a while loop that accumulates values, terminating + * when {@link #hasNext} is false. Finally, read the array's closing bracket by calling {@link * #endArray}. - *
  • Within object handling methods, first call {@link - * #beginObject} to consume the object's opening brace. Then create a - * while loop that assigns values to local variables based on their name. - * This loop should terminate when {@link #hasNext} is false. Finally, + *
  • Within object handling methods, first call {@link #beginObject} to consume + * the object's opening brace. Then create a while loop that assigns values to local variables + * based on their name. This loop should terminate when {@link #hasNext} is false. Finally, * read the object's closing brace by calling {@link #endObject}. *
- *

When a nested object or array is encountered, delegate to the - * corresponding handler method. * - *

When an unknown name is encountered, strict parsers should fail with an - * exception. Lenient parsers should call {@link #skipValue()} to recursively - * skip the value's nested tokens, which may otherwise conflict. + *

When a nested object or array is encountered, delegate to the corresponding handler method. + * + *

When an unknown name is encountered, strict parsers should fail with an exception. Lenient + * parsers should call {@link #skipValue()} to recursively skip the value's nested tokens, which may + * otherwise conflict. * - *

If a value may be null, you should first check using {@link #peek()}. - * Null literals can be consumed using either {@link #nextNull()} or {@link - * #skipValue()}. + *

If a value may be null, you should first check using {@link #peek()}. Null literals can be + * consumed using either {@link #nextNull()} or {@link #skipValue()}. * *

Configuration

+ * * The behavior of this reader can be customized with the following methods: + * *
    *
  • {@link #setStrictness(Strictness)}, the default is {@link Strictness#LEGACY_STRICT} *
* - * The default configuration of {@code JsonReader} instances used internally by - * the {@link Gson} class differs, and can be adjusted with the various - * {@link GsonBuilder} methods. + * The default configuration of {@code JsonReader} instances used internally by the {@link Gson} + * class differs, and can be adjusted with the various {@link GsonBuilder} methods. * *

Example

- * Suppose we'd like to parse a stream of messages such as the following:
 {@code
+ *
+ * Suppose we'd like to parse a stream of messages such as the following:
+ *
+ * 
{@code
  * [
  *   {
  *     "id": 912345678901,
@@ -97,108 +99,111 @@
  *       "followers_count": 2
  *     }
  *   }
- * ]}
- * This code implements the parser for the above structure:
   {@code
+ * ]
+ * }
* - * public List readJsonStream(InputStream in) throws IOException { - * JsonReader reader = new JsonReader(new InputStreamReader(in, "UTF-8")); - * try { - * return readMessagesArray(reader); - * } finally { - * reader.close(); - * } + * This code implements the parser for the above structure: + * + *
{@code
+ * public List readJsonStream(InputStream in) throws IOException {
+ *   JsonReader reader = new JsonReader(new InputStreamReader(in, "UTF-8"));
+ *   try {
+ *     return readMessagesArray(reader);
+ *   } finally {
+ *     reader.close();
  *   }
+ * }
  *
- *   public List readMessagesArray(JsonReader reader) throws IOException {
- *     List messages = new ArrayList<>();
+ * public List readMessagesArray(JsonReader reader) throws IOException {
+ *   List messages = new ArrayList<>();
  *
- *     reader.beginArray();
- *     while (reader.hasNext()) {
- *       messages.add(readMessage(reader));
- *     }
- *     reader.endArray();
- *     return messages;
+ *   reader.beginArray();
+ *   while (reader.hasNext()) {
+ *     messages.add(readMessage(reader));
  *   }
+ *   reader.endArray();
+ *   return messages;
+ * }
  *
- *   public Message readMessage(JsonReader reader) throws IOException {
- *     long id = -1;
- *     String text = null;
- *     User user = null;
- *     List geo = null;
+ * public Message readMessage(JsonReader reader) throws IOException {
+ *   long id = -1;
+ *   String text = null;
+ *   User user = null;
+ *   List geo = null;
  *
- *     reader.beginObject();
- *     while (reader.hasNext()) {
- *       String name = reader.nextName();
- *       if (name.equals("id")) {
- *         id = reader.nextLong();
- *       } else if (name.equals("text")) {
- *         text = reader.nextString();
- *       } else if (name.equals("geo") && reader.peek() != JsonToken.NULL) {
- *         geo = readDoublesArray(reader);
- *       } else if (name.equals("user")) {
- *         user = readUser(reader);
- *       } else {
- *         reader.skipValue();
- *       }
+ *   reader.beginObject();
+ *   while (reader.hasNext()) {
+ *     String name = reader.nextName();
+ *     if (name.equals("id")) {
+ *       id = reader.nextLong();
+ *     } else if (name.equals("text")) {
+ *       text = reader.nextString();
+ *     } else if (name.equals("geo") && reader.peek() != JsonToken.NULL) {
+ *       geo = readDoublesArray(reader);
+ *     } else if (name.equals("user")) {
+ *       user = readUser(reader);
+ *     } else {
+ *       reader.skipValue();
  *     }
- *     reader.endObject();
- *     return new Message(id, text, user, geo);
  *   }
+ *   reader.endObject();
+ *   return new Message(id, text, user, geo);
+ * }
  *
- *   public List readDoublesArray(JsonReader reader) throws IOException {
- *     List doubles = new ArrayList<>();
+ * public List readDoublesArray(JsonReader reader) throws IOException {
+ *   List doubles = new ArrayList<>();
  *
- *     reader.beginArray();
- *     while (reader.hasNext()) {
- *       doubles.add(reader.nextDouble());
- *     }
- *     reader.endArray();
- *     return doubles;
+ *   reader.beginArray();
+ *   while (reader.hasNext()) {
+ *     doubles.add(reader.nextDouble());
  *   }
+ *   reader.endArray();
+ *   return doubles;
+ * }
  *
- *   public User readUser(JsonReader reader) throws IOException {
- *     String username = null;
- *     int followersCount = -1;
+ * public User readUser(JsonReader reader) throws IOException {
+ *   String username = null;
+ *   int followersCount = -1;
  *
- *     reader.beginObject();
- *     while (reader.hasNext()) {
- *       String name = reader.nextName();
- *       if (name.equals("name")) {
- *         username = reader.nextString();
- *       } else if (name.equals("followers_count")) {
- *         followersCount = reader.nextInt();
- *       } else {
- *         reader.skipValue();
- *       }
+ *   reader.beginObject();
+ *   while (reader.hasNext()) {
+ *     String name = reader.nextName();
+ *     if (name.equals("name")) {
+ *       username = reader.nextString();
+ *     } else if (name.equals("followers_count")) {
+ *       followersCount = reader.nextInt();
+ *     } else {
+ *       reader.skipValue();
  *     }
- *     reader.endObject();
- *     return new User(username, followersCount);
- *   }}
+ * } + * reader.endObject(); + * return new User(username, followersCount); + * } + * }
* *

Number Handling

- * This reader permits numeric values to be read as strings and string values to - * be read as numbers. For example, both elements of the JSON array {@code - * [1, "1"]} may be read using either {@link #nextInt} or {@link #nextString}. - * This behavior is intended to prevent lossy numeric conversions: double is - * JavaScript's only numeric type and very large values like {@code - * 9007199254740993} cannot be represented exactly on that platform. To minimize - * precision loss, extremely large values should be written and read as strings - * in JSON. + * + * This reader permits numeric values to be read as strings and string values to be read as numbers. + * For example, both elements of the JSON array {@code [1, "1"]} may be read using either {@link + * #nextInt} or {@link #nextString}. This behavior is intended to prevent lossy numeric conversions: + * double is JavaScript's only numeric type and very large values like {@code 9007199254740993} + * cannot be represented exactly on that platform. To minimize precision loss, extremely large + * values should be written and read as strings in JSON. * *

Non-Execute Prefix

+ * * Web servers that serve private data using JSON may be vulnerable to Cross-site - * request forgery attacks. In such an attack, a malicious site gains access - * to a private JSON file by executing it with an HTML {@code