diff --git a/gson/pom.xml b/gson/pom.xml index b7b6cd9e2..b3e5536bc 100644 --- a/gson/pom.xml +++ b/gson/pom.xml @@ -7,7 +7,7 @@ org.immutables 2.0-SNAPSHOT - gson + immutables-gson ${project.groupId}.${project.artifactId} Gson integration for Immutables. Also includes optional integration with Jackson to speed-up @@ -45,6 +45,13 @@ 2.5.0 true + + + com.fasterxml.jackson.core + jackson-databind + 2.5.0 + true + javax.ws.rs diff --git a/gson/src/org/immutables/gson/Gson.java b/gson/src/org/immutables/gson/Gson.java index ddf0d2897..529ffc627 100644 --- a/gson/src/org/immutables/gson/Gson.java +++ b/gson/src/org/immutables/gson/Gson.java @@ -1,5 +1,23 @@ +/* + Copyright 2015 Immutables Authors and Contributors + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ package org.immutables.gson; +import java.util.Map; +import com.google.gson.FieldNamingPolicy; +import com.google.gson.FieldNamingStrategy; import com.google.gson.GsonBuilder; import com.google.gson.annotations.SerializedName; import java.lang.annotation.Documented; @@ -7,36 +25,83 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; +import org.immutables.gson.adapter.ExpectedSubtypesAdapter; +import org.immutables.gson.adapter.FieldNamingTranslator; +/** + * The Interface Gson. + */ @Retention(RetentionPolicy.SOURCE) public @interface Gson { /** - * Use on a top level class to generate type adapted factory. + * Use on a top level class to generate type adapted factory supporting directly annotated and all + * nested immutable types. *

- * Type adapter factories are also registered statically as services - * {@code META-INF/services/com.google.gson.TypeAdapterFactory}. Easy way to configure - * {@link com.google.gson.Gson}. + * Type adapter factories are registered statically as services + * {@code META-INF/services/com.google.gson.TypeAdapterFactory}. The most easy way to register all + * such factories {@link com.google.gson.Gson}. *

- * Certain are gson options are supported for immutable objects in deliberate fashion: + * Certain Gson options are supported for immutable objects in deliberate fashion: *

*/ @Documented @Retention(RetentionPolicy.SOURCE) @Target({ElementType.TYPE}) - public @interface TypeAdapted {} + public @interface TypeAdapters { + /** + * When {@code namingStrategy=true}, somewhat more involved code is generated to apply naming + * strategies extracted from configured {@link com.google.gson.Gson} instance. + *

+ * This functionality uses runtime support class and requires that this Gson integration module + * jar will be available at runtime. Uses some Gson internals which could + * potentially break in later versions of Gson library. Uses oracle JVM internals. + * If you really want to discuss this functionality to be more portable, + * file a request at {@linkplain "https://github.com/immutables/immutables/issues"}. + * + * @see FieldNamingStrategy + * @see FieldNamingPolicy + * @see GsonBuilder#setFieldNamingPolicy(FieldNamingPolicy) + * @see FieldNamingTranslator + * @return {@code true} if enabled, by default is {@code false} + */ + boolean fieldNamingStrategy() default false; + + /** + * When {@code emptyAsNulls=true}, empty arrays and objects will be omitted from output as + * if they where {@code null}, both field name and value will be omited. + *

+ * Note that {@code null} skipping behaviour is controlled by + * {@link GsonBuilder#serializeNulls()}, which forces all nulls and empty arrays/objects to be + * serialized. + * @return {@code true} if enabled, by default is {@code false} + */ + boolean emptyAsNulls() default false; + } /** - * Expected subclasses for marshaling could be specified on attribute level or an abstract - * supertype directly, however the former declaration site has precedence. + * Expected subtypes for serialization could be specified on attribute level or an abstract + * supertype directly, however the former declaration site has precedence. It enables polymorphic + * marshaling by structure. Subtype that matches JSON value will be returned, for the details + * please see {@link ExpectedSubtypesAdapter}. + *

+ * Note: when this annotation is used with {@link Map} attribute, it refers to types of values, + * not keys. + *

+ * This functionality uses runtime support class and requires that this Gson integration module + * jar will be available at runtime. + * @see ExpectedSubtypesAdapter * @see #value() * @see Named */ @Retention(RetentionPolicy.SOURCE) @Target({ElementType.METHOD, ElementType.TYPE}) - public @interface Subtypes { + public @interface ExpectedSubtypes { /** * Specifies expected subclasses of an abstract type that is matched during parsing by diff --git a/gson/src/org/immutables/gson/stream/GsonMessageBodyProvider.java b/gson/src/org/immutables/gson/stream/GsonMessageBodyProvider.java index 5e835eacb..32799b0f6 100644 --- a/gson/src/org/immutables/gson/stream/GsonMessageBodyProvider.java +++ b/gson/src/org/immutables/gson/stream/GsonMessageBodyProvider.java @@ -15,14 +15,6 @@ */ package org.immutables.gson.stream; -import org.immutables.value.Value.Style.ImplementationVisibility; -import java.lang.reflect.Field; -import org.immutables.value.Value; -import java.util.List; -import javax.ws.rs.core.Response; -import com.google.common.base.Throwables; -import javax.ws.rs.Consumes; -import javax.ws.rs.Produces; import com.fasterxml.jackson.core.JsonFactory; import com.fasterxml.jackson.core.JsonGenerator; import com.fasterxml.jackson.core.JsonParser; @@ -39,20 +31,26 @@ import java.io.OutputStream; import java.io.OutputStreamWriter; import java.lang.annotation.Annotation; +import java.lang.reflect.Field; import java.lang.reflect.Type; -import java.util.Arrays; import java.util.Collections; import java.util.HashSet; +import java.util.List; import java.util.ServiceLoader; import java.util.Set; import javax.annotation.Nullable; +import javax.ws.rs.Consumes; +import javax.ws.rs.Produces; import javax.ws.rs.WebApplicationException; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.MultivaluedMap; +import javax.ws.rs.core.Response; import javax.ws.rs.ext.MessageBodyReader; import javax.ws.rs.ext.MessageBodyWriter; import javax.ws.rs.ext.Provider; import org.immutables.metainf.Metainf; +import org.immutables.value.Value; +import org.immutables.value.Value.Style.ImplementationVisibility; /** * Gson serialization provider for JAX-RS 1.0 and JAX-RS 2.0. diff --git a/gson/src/org/immutables/gson/stream/JsonGeneratorWriter.java b/gson/src/org/immutables/gson/stream/JsonGeneratorWriter.java index 90bcbe51e..c737521e0 100644 --- a/gson/src/org/immutables/gson/stream/JsonGeneratorWriter.java +++ b/gson/src/org/immutables/gson/stream/JsonGeneratorWriter.java @@ -15,11 +15,12 @@ */ package org.immutables.gson.stream; -import javax.annotation.concurrent.NotThreadSafe; import com.fasterxml.jackson.core.JsonGenerator; import com.google.gson.stream.JsonWriter; import java.io.IOException; import java.io.Writer; +import java.util.concurrent.Callable; +import javax.annotation.concurrent.NotThreadSafe; /** * {@link JsonWriter} impementation backed by Jackson's {@link JsonGenerator}. @@ -27,8 +28,7 @@ * Error reporting is might differ, however. */ @NotThreadSafe -public class JsonGeneratorWriter extends JsonWriter { - +public class JsonGeneratorWriter extends JsonWriter implements Callable { private static final Writer UNSUPPORTED_WRITER = new Writer() { @Override public void write(char[] cbuf, int off, int len) throws IOException { @@ -53,6 +53,10 @@ public JsonGeneratorWriter(JsonGenerator generator) { this.generator = generator; } + public JsonGenerator getGenerator() { + return generator; + } + @Override public JsonWriter beginArray() throws IOException { generator.writeStartArray(); @@ -145,4 +149,14 @@ public void close() throws IOException { public String toString() { return getClass().getSimpleName(); } + + /** + * Implements {@link Callable} mostly as a marker interface. + * Better use {@link #getGenerator()} to get generator. + * @return unwrapped {@link JsonGenerator} + */ + @Override + public JsonGenerator call() throws Exception { + return generator; + } } diff --git a/gson/src/org/immutables/gson/stream/JsonParserReader.java b/gson/src/org/immutables/gson/stream/JsonParserReader.java index cfdc4945d..b52e9e399 100644 --- a/gson/src/org/immutables/gson/stream/JsonParserReader.java +++ b/gson/src/org/immutables/gson/stream/JsonParserReader.java @@ -15,6 +15,7 @@ */ package org.immutables.gson.stream; +import java.util.concurrent.Callable; import com.fasterxml.jackson.core.JsonParser; import com.google.gson.stream.JsonReader; import com.google.gson.stream.JsonToken; @@ -27,10 +28,10 @@ /** * {@link JsonReader} impementation backed by Jackson's {@link JsonParser}. * Provides measurable JSON parsing improvements over Gson's native implementation. - * Error reporting is might differ, however. + * Error reporting might differ, however. */ @NotThreadSafe -public class JsonParserReader extends JsonReader { +public class JsonParserReader extends JsonReader implements Callable { private static final Reader UNSUPPORTED_READER = new Reader() { @Override @@ -51,6 +52,10 @@ public JsonParserReader(JsonParser parser) { this.parser = parser; } + public JsonParser getParser() { + return parser; + } + @Nullable private com.fasterxml.jackson.core.JsonToken peek; @@ -130,6 +135,7 @@ public String nextString() throws IOException { clearPeek(); return value; } + @Override public boolean nextBoolean() throws IOException { requirePeek(); @@ -216,7 +222,17 @@ private static JsonToken toGsonToken(com.fasterxml.jackson.core.JsonToken token) case VALUE_STRING: return JsonToken.STRING; default: // Not semantically equivalent - return JsonToken.END_DOCUMENT; + return JsonToken.NULL; } } + + /** + * Implements {@link Callable} mostly as a marker interface. + * Better use {@link #getParser()} to get parser. + * @return unwrapped {@link JsonParser} + */ + @Override + public JsonParser call() throws Exception { + return parser; + } } diff --git a/gson/test/org/immutables/gson/adapters/Adapt.java b/gson/test/org/immutables/gson/adapter/Adapt.java similarity index 92% rename from gson/test/org/immutables/gson/adapters/Adapt.java rename to gson/test/org/immutables/gson/adapter/Adapt.java index 601f8fc91..b7abaeb39 100644 --- a/gson/test/org/immutables/gson/adapters/Adapt.java +++ b/gson/test/org/immutables/gson/adapter/Adapt.java @@ -1,4 +1,4 @@ -package org.immutables.gson.adapters; +package org.immutables.gson.adapter; import com.google.common.collect.Multiset; import com.google.common.collect.SetMultimap; @@ -11,7 +11,7 @@ @Value.Immutable(builder = false) @Value.Nested -@Gson.TypeAdapted +@Gson.TypeAdapters public interface Adapt { @Value.Parameter diff --git a/gson/test/org/immutables/gson/adapters/AdaptReadWriteTest.java b/gson/test/org/immutables/gson/adapter/AdaptReadWriteTest.java similarity index 98% rename from gson/test/org/immutables/gson/adapters/AdaptReadWriteTest.java rename to gson/test/org/immutables/gson/adapter/AdaptReadWriteTest.java index 3e1132e9d..eb44efef3 100644 --- a/gson/test/org/immutables/gson/adapters/AdaptReadWriteTest.java +++ b/gson/test/org/immutables/gson/adapter/AdaptReadWriteTest.java @@ -1,4 +1,4 @@ -package org.immutables.gson.adapters; +package org.immutables.gson.adapter; import com.google.common.collect.ImmutableList; import com.google.gson.Gson; diff --git a/gson/test/org/immutables/gson/adapters/OtherType.java b/gson/test/org/immutables/gson/adapter/OtherType.java similarity index 93% rename from gson/test/org/immutables/gson/adapters/OtherType.java rename to gson/test/org/immutables/gson/adapter/OtherType.java index a239f5412..29b4a979f 100644 --- a/gson/test/org/immutables/gson/adapters/OtherType.java +++ b/gson/test/org/immutables/gson/adapter/OtherType.java @@ -1,4 +1,4 @@ -package org.immutables.gson.adapters; +package org.immutables.gson.adapter; import com.google.common.collect.ListMultimap; import com.google.common.collect.Multiset; @@ -11,7 +11,7 @@ @Value.Immutable(builder = false) @Value.Nested -@Gson.TypeAdapted +@Gson.TypeAdapters public interface OtherType { @Value.Parameter