diff --git a/retrofit-converters/guava/README.md b/retrofit-converters/guava/README.md new file mode 100644 index 0000000000000000000000000000000000000000..03eae96f560a1c9631e4f55734509b935e2767c7 --- /dev/null +++ b/retrofit-converters/guava/README.md @@ -0,0 +1,30 @@ +Guava Converter +=============== + +A `Converter` which supports Guava's `Optional` by delegating to other converters for `T` +and then wrapping it into `Optional`. + + +Download +-------- + +Download [the latest JAR][1] or grab via [Maven][2]: +```xml + + com.squareup.retrofit2 + converter-guava + latest.version + +``` +or [Gradle][2]: +```groovy +compile 'com.squareup.retrofit2:converter-guava:latest.version' +``` + +Snapshots of the development version are available in [Sonatype's `snapshots` repository][snap]. + + + + [1]: https://search.maven.org/remote_content?g=com.squareup.retrofit2&a=converter-guava&v=LATEST + [2]: http://search.maven.org/#search%7Cga%7C1%7Cg%3A%22com.squareup.retrofit2%22%20a%3A%22converter-guava%22 + [snap]: https://oss.sonatype.org/content/repositories/snapshots/ diff --git a/retrofit-converters/guava/pom.xml b/retrofit-converters/guava/pom.xml new file mode 100644 index 0000000000000000000000000000000000000000..442194cdb516cd79698ada3eb31a25b5c26c2d43 --- /dev/null +++ b/retrofit-converters/guava/pom.xml @@ -0,0 +1,48 @@ + + + + 4.0.0 + + + com.squareup.retrofit2 + retrofit-converters + 2.3.0-SNAPSHOT + ../pom.xml + + + converter-guava + Converter: Guava + + + + ${project.groupId} + retrofit + ${project.version} + + + com.google.guava + guava + + + com.google.code.findbugs + jsr305 + provided + + + + junit + junit + test + + + com.squareup.okhttp3 + mockwebserver + test + + + org.assertj + assertj-core + test + + + diff --git a/retrofit-converters/guava/src/main/java/retrofit/converter/guava/GuavaOptionalConverterFactory.java b/retrofit-converters/guava/src/main/java/retrofit/converter/guava/GuavaOptionalConverterFactory.java new file mode 100644 index 0000000000000000000000000000000000000000..bfa033614f0f664c40421a08107ac4f68ab9d412 --- /dev/null +++ b/retrofit-converters/guava/src/main/java/retrofit/converter/guava/GuavaOptionalConverterFactory.java @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2017 Square, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package retrofit.converter.guava; + +import com.google.common.base.Optional; +import java.lang.annotation.Annotation; +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; +import javax.annotation.Nullable; +import okhttp3.ResponseBody; +import retrofit2.Converter; +import retrofit2.Retrofit; + +/** + * A {@linkplain Converter.Factory converter} for {@code Optional} which delegates to another + * converter to deserialize {@code T} and then wraps it into {@link Optional}. + */ +public final class GuavaOptionalConverterFactory extends Converter.Factory { + public static GuavaOptionalConverterFactory create() { + return new GuavaOptionalConverterFactory(); + } + + private GuavaOptionalConverterFactory() { + } + + @Nullable @Override + public Converter responseBodyConverter(Type type, Annotation[] annotations, + Retrofit retrofit) { + if (getRawType(type) != Optional.class) { + return null; + } + + Type innerType = getParameterUpperBound(0, (ParameterizedType) type); + Converter delegate = + retrofit.nextResponseBodyConverter(this, innerType, annotations); + return new OptionalConverter<>(delegate); + } +} diff --git a/retrofit-converters/guava/src/main/java/retrofit/converter/guava/OptionalConverter.java b/retrofit-converters/guava/src/main/java/retrofit/converter/guava/OptionalConverter.java new file mode 100644 index 0000000000000000000000000000000000000000..72f2184dbd2affd8865e9143dc433a7a3577d459 --- /dev/null +++ b/retrofit-converters/guava/src/main/java/retrofit/converter/guava/OptionalConverter.java @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2017 Square, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package retrofit.converter.guava; + +import com.google.common.base.Optional; +import java.io.IOException; +import okhttp3.ResponseBody; +import retrofit2.Converter; + +final class OptionalConverter implements Converter> { + private final Converter delegate; + + OptionalConverter(Converter delegate) { + this.delegate = delegate; + } + + @Override public Optional convert(ResponseBody value) throws IOException { + return Optional.fromNullable(delegate.convert(value)); + } +} diff --git a/retrofit-converters/guava/src/main/java/retrofit/converter/guava/package-info.java b/retrofit-converters/guava/src/main/java/retrofit/converter/guava/package-info.java new file mode 100644 index 0000000000000000000000000000000000000000..bd25d1fe4c07533ca770eba3d9e60bfe8ccb202b --- /dev/null +++ b/retrofit-converters/guava/src/main/java/retrofit/converter/guava/package-info.java @@ -0,0 +1,4 @@ +@ParametersAreNonnullByDefault +package retrofit.converter.guava; + +import javax.annotation.ParametersAreNonnullByDefault; diff --git a/retrofit-converters/guava/src/test/java/retrofit/converter/guava/AlwaysNullConverterFactory.java b/retrofit-converters/guava/src/test/java/retrofit/converter/guava/AlwaysNullConverterFactory.java new file mode 100644 index 0000000000000000000000000000000000000000..92325230143fef59c21080ed686ceabb1e315a1c --- /dev/null +++ b/retrofit-converters/guava/src/test/java/retrofit/converter/guava/AlwaysNullConverterFactory.java @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2017 Square, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package retrofit.converter.guava; + +import java.io.IOException; +import java.lang.annotation.Annotation; +import java.lang.reflect.Type; +import okhttp3.ResponseBody; +import retrofit2.Converter; +import retrofit2.Retrofit; + +final class AlwaysNullConverterFactory extends Converter.Factory { + @Override + public Converter responseBodyConverter(Type type, Annotation[] annotations, + Retrofit retrofit) { + return new Converter() { + @Override public Object convert(ResponseBody value) throws IOException { + return null; + } + }; + } +} diff --git a/retrofit-converters/guava/src/test/java/retrofit/converter/guava/GuavaOptionalConverterFactoryTest.java b/retrofit-converters/guava/src/test/java/retrofit/converter/guava/GuavaOptionalConverterFactoryTest.java new file mode 100644 index 0000000000000000000000000000000000000000..27680150e75b246bb19e7157060177415fbe959e --- /dev/null +++ b/retrofit-converters/guava/src/test/java/retrofit/converter/guava/GuavaOptionalConverterFactoryTest.java @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2017 Square, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package retrofit.converter.guava; + +import com.google.common.base.Optional; +import java.io.IOException; +import okhttp3.mockwebserver.MockResponse; +import okhttp3.mockwebserver.MockWebServer; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import retrofit2.Call; +import retrofit2.Retrofit; +import retrofit2.http.GET; + +import static org.assertj.core.api.Assertions.assertThat; + +public final class GuavaOptionalConverterFactoryTest { + interface Service { + @GET("/") Call> optional(); + @GET("/") Call object(); + } + + @Rule public final MockWebServer server = new MockWebServer(); + + private Service service; + + @Before public void setUp() { + Retrofit retrofit = new Retrofit.Builder() + .baseUrl(server.url("/")) + .addConverterFactory(GuavaOptionalConverterFactory.create()) + .addConverterFactory(new AlwaysNullConverterFactory()) + .build(); + service = retrofit.create(Service.class); + } + + @Test public void optional() throws IOException { + server.enqueue(new MockResponse()); + + Optional optional = service.optional().execute().body(); + assertThat(optional).isNotNull(); + assertThat(optional.isPresent()).isFalse(); + } + + @Test public void onlyMatchesOptional() throws IOException { + server.enqueue(new MockResponse()); + + Object body = service.object().execute().body(); + assertThat(body).isNull(); + } +} diff --git a/retrofit-converters/java8/README.md b/retrofit-converters/java8/README.md new file mode 100644 index 0000000000000000000000000000000000000000..696ac23c6dcad91f6cf6fc4dbaceecbf13e64d9c --- /dev/null +++ b/retrofit-converters/java8/README.md @@ -0,0 +1,30 @@ +Java 8 Converter +================ + +A `Converter` which supports Java 8's `Optional` by delegating to other converters for `T` +and then wrapping it into `Optional`. + + +Download +-------- + +Download [the latest JAR][1] or grab via [Maven][2]: +```xml + + com.squareup.retrofit2 + converter-java8 + latest.version + +``` +or [Gradle][2]: +```groovy +compile 'com.squareup.retrofit2:converter-java8:latest.version' +``` + +Snapshots of the development version are available in [Sonatype's `snapshots` repository][snap]. + + + + [1]: https://search.maven.org/remote_content?g=com.squareup.retrofit2&a=converter-java8&v=LATEST + [2]: http://search.maven.org/#search%7Cga%7C1%7Cg%3A%22com.squareup.retrofit2%22%20a%3A%22converter-java8%22 + [snap]: https://oss.sonatype.org/content/repositories/snapshots/ diff --git a/retrofit-converters/java8/pom.xml b/retrofit-converters/java8/pom.xml new file mode 100644 index 0000000000000000000000000000000000000000..f561897e4f0c76316dbec18a25dfbcd4764f4fe1 --- /dev/null +++ b/retrofit-converters/java8/pom.xml @@ -0,0 +1,65 @@ + + + + 4.0.0 + + + com.squareup.retrofit2 + retrofit-converters + 2.3.0-SNAPSHOT + ../pom.xml + + + converter-java8 + Converter: Java 8 + + + 1.8 + + + + + ${project.groupId} + retrofit + ${project.version} + + + com.google.code.findbugs + jsr305 + provided + + + + junit + junit + test + + + com.squareup.okhttp3 + mockwebserver + test + + + org.assertj + assertj-core + test + + + + + + + org.codehaus.mojo + animal-sniffer-maven-plugin + ${animal.sniffer.version} + + + org.kaazing.mojo.signature + java18 + 1.0 + + + + + + diff --git a/retrofit-converters/java8/src/main/java/retrofit/converter/java8/Java8OptionalConverterFactory.java b/retrofit-converters/java8/src/main/java/retrofit/converter/java8/Java8OptionalConverterFactory.java new file mode 100644 index 0000000000000000000000000000000000000000..7b55ca9653519e637e8ed10f745d51396f15e94b --- /dev/null +++ b/retrofit-converters/java8/src/main/java/retrofit/converter/java8/Java8OptionalConverterFactory.java @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2017 Square, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package retrofit.converter.java8; + +import java.lang.annotation.Annotation; +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; +import java.util.Optional; +import javax.annotation.Nullable; +import okhttp3.ResponseBody; +import retrofit2.Converter; +import retrofit2.Retrofit; + +/** + * A {@linkplain Converter.Factory converter} for {@code Optional} which delegates to another + * converter to deserialize {@code T} and then wraps it into {@link Optional}. + */ +public final class Java8OptionalConverterFactory extends Converter.Factory { + public static Java8OptionalConverterFactory create() { + return new Java8OptionalConverterFactory(); + } + + private Java8OptionalConverterFactory() { + } + + @Nullable @Override + public Converter responseBodyConverter(Type type, Annotation[] annotations, + Retrofit retrofit) { + if (getRawType(type) != Optional.class) { + return null; + } + + Type innerType = getParameterUpperBound(0, (ParameterizedType) type); + Converter delegate = + retrofit.nextResponseBodyConverter(this, innerType, annotations); + return new OptionalConverter<>(delegate); + } +} diff --git a/retrofit-converters/java8/src/main/java/retrofit/converter/java8/OptionalConverter.java b/retrofit-converters/java8/src/main/java/retrofit/converter/java8/OptionalConverter.java new file mode 100644 index 0000000000000000000000000000000000000000..4384432565cdac0ad9f41fad541230477104776b --- /dev/null +++ b/retrofit-converters/java8/src/main/java/retrofit/converter/java8/OptionalConverter.java @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2017 Square, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package retrofit.converter.java8; + +import java.io.IOException; +import java.util.Optional; +import okhttp3.ResponseBody; +import retrofit2.Converter; + +final class OptionalConverter implements Converter> { + private final Converter delegate; + + OptionalConverter(Converter delegate) { + this.delegate = delegate; + } + + @Override public Optional convert(ResponseBody value) throws IOException { + return Optional.ofNullable(delegate.convert(value)); + } +} diff --git a/retrofit-converters/java8/src/main/java/retrofit/converter/java8/package-info.java b/retrofit-converters/java8/src/main/java/retrofit/converter/java8/package-info.java new file mode 100644 index 0000000000000000000000000000000000000000..e883be0323625c564fd50e0292b5ad86a6b9806f --- /dev/null +++ b/retrofit-converters/java8/src/main/java/retrofit/converter/java8/package-info.java @@ -0,0 +1,4 @@ +@ParametersAreNonnullByDefault +package retrofit.converter.java8; + +import javax.annotation.ParametersAreNonnullByDefault; diff --git a/retrofit-converters/java8/src/test/java/retrofit/converter/java8/AlwaysNullConverterFactory.java b/retrofit-converters/java8/src/test/java/retrofit/converter/java8/AlwaysNullConverterFactory.java new file mode 100644 index 0000000000000000000000000000000000000000..006b9062489b8d6385ab1512550e4c26021dcb97 --- /dev/null +++ b/retrofit-converters/java8/src/test/java/retrofit/converter/java8/AlwaysNullConverterFactory.java @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2017 Square, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package retrofit.converter.java8; + +import java.io.IOException; +import java.lang.annotation.Annotation; +import java.lang.reflect.Type; +import okhttp3.ResponseBody; +import retrofit2.Converter; +import retrofit2.Retrofit; + +final class AlwaysNullConverterFactory extends Converter.Factory { + @Override + public Converter responseBodyConverter(Type type, Annotation[] annotations, + Retrofit retrofit) { + return new Converter() { + @Override public Object convert(ResponseBody value) throws IOException { + return null; + } + }; + } +} diff --git a/retrofit-converters/java8/src/test/java/retrofit/converter/java8/Java8OptionalConverterFactoryTest.java b/retrofit-converters/java8/src/test/java/retrofit/converter/java8/Java8OptionalConverterFactoryTest.java new file mode 100644 index 0000000000000000000000000000000000000000..73027907b9ae59875e93e4b7366b52e4c0c11a7f --- /dev/null +++ b/retrofit-converters/java8/src/test/java/retrofit/converter/java8/Java8OptionalConverterFactoryTest.java @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2017 Square, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package retrofit.converter.java8; + +import java.io.IOException; +import java.util.Optional; +import okhttp3.mockwebserver.MockResponse; +import okhttp3.mockwebserver.MockWebServer; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import retrofit2.Call; +import retrofit2.Retrofit; +import retrofit2.http.GET; + +import static org.assertj.core.api.Assertions.assertThat; + +public final class Java8OptionalConverterFactoryTest { + interface Service { + @GET("/") Call> optional(); + @GET("/") Call object(); + } + + @Rule public final MockWebServer server = new MockWebServer(); + + private Service service; + + @Before public void setUp() { + Retrofit retrofit = new Retrofit.Builder() + .baseUrl(server.url("/")) + .addConverterFactory(Java8OptionalConverterFactory.create()) + .addConverterFactory(new AlwaysNullConverterFactory()) + .build(); + service = retrofit.create(Service.class); + } + + @Test public void optional() throws IOException { + server.enqueue(new MockResponse()); + + Optional optional = service.optional().execute().body(); + assertThat(optional).isNotNull(); + assertThat(optional.isPresent()).isFalse(); + } + + @Test public void onlyMatchesOptional() throws IOException { + server.enqueue(new MockResponse()); + + Object body = service.object().execute().body(); + assertThat(body).isNull(); + } +} diff --git a/retrofit-converters/pom.xml b/retrofit-converters/pom.xml index 4c49a4a8831d02ce9ec89d17390da8bf6153c5ff..e0d0292266d9b5c5e4c49869c551990ebc171f0b 100644 --- a/retrofit-converters/pom.xml +++ b/retrofit-converters/pom.xml @@ -16,8 +16,10 @@ gson + guava protobuf jackson + java8 wire simplexml scalars diff --git a/retrofit/src/main/java/retrofit2/Converter.java b/retrofit/src/main/java/retrofit2/Converter.java index 4835d4594b172dd5554d7239750355baba39421d..37a64f8d183975af704eba2beed77a9eb8b8e447 100644 --- a/retrofit/src/main/java/retrofit2/Converter.java +++ b/retrofit/src/main/java/retrofit2/Converter.java @@ -17,6 +17,7 @@ package retrofit2; import java.io.IOException; import java.lang.annotation.Annotation; +import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import javax.annotation.Nullable; import okhttp3.RequestBody; @@ -75,5 +76,21 @@ public interface Converter { Retrofit retrofit) { return null; } + + /** + * Extract the upper bound of the generic parameter at {@code index} from {@code type}. For + * example, index 1 of {@code Map} returns {@code Runnable}. + */ + protected static Type getParameterUpperBound(int index, ParameterizedType type) { + return Utils.getParameterUpperBound(index, type); + } + + /** + * Extract the raw class type from {@code type}. For example, the type representing + * {@code List} returns {@code List.class}. + */ + protected static Class getRawType(Type type) { + return Utils.getRawType(type); + } } }