提交 85593d29 编写于 作者: J Jesse Wilson 提交者: GitHub

Merge pull request #2324 from square/jw/2017-05-11/optional-converters

Add converters which facilitate the use of Optional.
Guava Converter
===============
A `Converter` which supports Guava's `Optional<T>` 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
<dependency>
<groupId>com.squareup.retrofit2</groupId>
<artifactId>converter-guava</artifactId>
<version>latest.version</version>
</dependency>
```
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/
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.squareup.retrofit2</groupId>
<artifactId>retrofit-converters</artifactId>
<version>2.3.0-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>converter-guava</artifactId>
<name>Converter: Guava</name>
<dependencies>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>retrofit</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
</dependency>
<dependency>
<groupId>com.google.code.findbugs</groupId>
<artifactId>jsr305</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>mockwebserver</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.assertj</groupId>
<artifactId>assertj-core</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>
/*
* 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<T>} 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<ResponseBody, ?> responseBodyConverter(Type type, Annotation[] annotations,
Retrofit retrofit) {
if (getRawType(type) != Optional.class) {
return null;
}
Type innerType = getParameterUpperBound(0, (ParameterizedType) type);
Converter<ResponseBody, Object> delegate =
retrofit.nextResponseBodyConverter(this, innerType, annotations);
return new OptionalConverter<>(delegate);
}
}
/*
* 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<T> implements Converter<ResponseBody, Optional<T>> {
private final Converter<ResponseBody, T> delegate;
OptionalConverter(Converter<ResponseBody, T> delegate) {
this.delegate = delegate;
}
@Override public Optional<T> convert(ResponseBody value) throws IOException {
return Optional.fromNullable(delegate.convert(value));
}
}
@ParametersAreNonnullByDefault
package retrofit.converter.guava;
import javax.annotation.ParametersAreNonnullByDefault;
/*
* 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<ResponseBody, ?> responseBodyConverter(Type type, Annotation[] annotations,
Retrofit retrofit) {
return new Converter<ResponseBody, Object>() {
@Override public Object convert(ResponseBody value) throws IOException {
return null;
}
};
}
}
/*
* 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<Object>> optional();
@GET("/") Call<Object> 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<Object> 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();
}
}
Java 8 Converter
================
A `Converter` which supports Java 8's `Optional<T>` 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
<dependency>
<groupId>com.squareup.retrofit2</groupId>
<artifactId>converter-java8</artifactId>
<version>latest.version</version>
</dependency>
```
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/
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.squareup.retrofit2</groupId>
<artifactId>retrofit-converters</artifactId>
<version>2.3.0-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>converter-java8</artifactId>
<name>Converter: Java 8</name>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>retrofit</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>com.google.code.findbugs</groupId>
<artifactId>jsr305</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>mockwebserver</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.assertj</groupId>
<artifactId>assertj-core</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>animal-sniffer-maven-plugin</artifactId>
<version>${animal.sniffer.version}</version>
<configuration>
<signature>
<groupId>org.kaazing.mojo.signature</groupId>
<artifactId>java18</artifactId>
<version>1.0</version>
</signature>
</configuration>
</plugin>
</plugins>
</build>
</project>
/*
* 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<T>} 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<ResponseBody, ?> responseBodyConverter(Type type, Annotation[] annotations,
Retrofit retrofit) {
if (getRawType(type) != Optional.class) {
return null;
}
Type innerType = getParameterUpperBound(0, (ParameterizedType) type);
Converter<ResponseBody, Object> delegate =
retrofit.nextResponseBodyConverter(this, innerType, annotations);
return new OptionalConverter<>(delegate);
}
}
/*
* 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<T> implements Converter<ResponseBody, Optional<T>> {
private final Converter<ResponseBody, T> delegate;
OptionalConverter(Converter<ResponseBody, T> delegate) {
this.delegate = delegate;
}
@Override public Optional<T> convert(ResponseBody value) throws IOException {
return Optional.ofNullable(delegate.convert(value));
}
}
@ParametersAreNonnullByDefault
package retrofit.converter.java8;
import javax.annotation.ParametersAreNonnullByDefault;
/*
* 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<ResponseBody, ?> responseBodyConverter(Type type, Annotation[] annotations,
Retrofit retrofit) {
return new Converter<ResponseBody, Object>() {
@Override public Object convert(ResponseBody value) throws IOException {
return null;
}
};
}
}
/*
* 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<Object>> optional();
@GET("/") Call<Object> 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<Object> 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();
}
}
......@@ -16,8 +16,10 @@
<modules>
<module>gson</module>
<module>guava</module>
<module>protobuf</module>
<module>jackson</module>
<module>java8</module>
<module>wire</module>
<module>simplexml</module>
<module>scalars</module>
......
......@@ -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<F, T> {
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<String, ? extends Runnable>} 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<? extends Runnable>} returns {@code List.class}.
*/
protected static Class<?> getRawType(Type type) {
return Utils.getRawType(type);
}
}
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册