Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add a first-party converter for Wire. #311

Merged
merged 1 commit into from
Aug 24, 2013
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@
<!-- Converter Dependencies -->
<protobuf.version>2.5.0</protobuf.version>
<jackson.version>2.2.2</jackson.version>
<wire.version>1.0.0</wire.version>

<!-- Test Dependencies -->
<junit.version>4.10</junit.version>
Expand Down Expand Up @@ -113,7 +114,11 @@
<artifactId>jackson-databind</artifactId>
<version>${jackson.version}</version>
</dependency>

<dependency>
<groupId>com.squareup.wire</groupId>
<artifactId>wire-runtime</artifactId>
<version>${wire.version}</version>
</dependency>

<dependency>
<groupId>junit</groupId>
Expand Down
1 change: 1 addition & 0 deletions retrofit-converters/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -17,5 +17,6 @@
<modules>
<module>protobuf</module>
<module>jackson</module>
<module>wire</module>
</modules>
</project>
47 changes: 47 additions & 0 deletions retrofit-converters/wire/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
<?xml version="1.0" encoding="UTF-8"?>

<!--
~ Copyright 2013 Square, Inc.
-->

<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.retrofit</groupId>
<artifactId>retrofit-converters</artifactId>
<version>1.2.1-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

<artifactId>converter-wire</artifactId>
<name>Converter: Wire Protocol Buffers</name>

<dependencies>
<dependency>
<groupId>com.squareup.retrofit</groupId>
<artifactId>retrofit</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>com.squareup.wire</groupId>
<artifactId>wire-runtime</artifactId>
</dependency>

<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.easytesting</groupId>
<artifactId>fest-assert-core</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
// Copyright 2013 Square, Inc.
package retrofit.converter;

import com.squareup.wire.Message;
import com.squareup.wire.Wire;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.reflect.Type;
import retrofit.mime.TypedByteArray;
import retrofit.mime.TypedInput;
import retrofit.mime.TypedOutput;

/** A {@link Converter} that reads and writes protocol buffers using Wire. */
public class WireConverter implements Converter {
private static final String MIME_TYPE = "application/x-protobuf";

private final Wire wire;

public WireConverter(Wire wire) {
this.wire = wire;
}

@SuppressWarnings("unchecked") //
@Override public Object fromBody(TypedInput body, Type type) throws ConversionException {
if (!(type instanceof Class<?>)) {
throw new IllegalArgumentException("Expected a raw Class<?> but was " + type);
}
Class<?> c = (Class<?>) type;
if (!Message.class.isAssignableFrom(c)) {
throw new IllegalArgumentException("Expected a proto message but was " + c.getName());
}

if (!MIME_TYPE.equalsIgnoreCase(body.mimeType())) {
throw new IllegalArgumentException("Expected a proto but was: " + body.mimeType());
}

try {
byte[] data = consumeAsBytes(body.in());
return wire.parseFrom(data, (Class<Message>) c);
} catch (IOException e) {
throw new ConversionException(e);
}
}

@Override public TypedOutput toBody(Object object) {
if (!(object instanceof Message)) {
throw new IllegalArgumentException(
"Expected a proto message but was " + (object != null ? object.getClass().getName()
: "null"));
}
byte[] bytes = ((Message) object).toByteArray();
return new TypedByteArray(MIME_TYPE, bytes);
}

/** Reads a stream into a {@code byte} array. */
private byte[] consumeAsBytes(InputStream in) throws IOException {
ByteArrayOutputStream out = new ByteArrayOutputStream();
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@danrice-square Wire should have an API to read a Message off of an InputStream. This code is tedious and does an unnecessary copy

pipe(in, out);
return out.toByteArray();
}

/** Reads content from the given input and pipes it to the given output. */
private void pipe(InputStream in, OutputStream out) throws IOException {
byte[] buffer = new byte[4096];
int count;
while ((count = in.read(buffer)) != -1) {
out.write(buffer, 0, count);
}
}
}
210 changes: 210 additions & 0 deletions retrofit-converters/wire/src/test/java/retrofit/converter/Person.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,210 @@
// Copyright 2013 Square, Inc.

// Code generated by Wire protocol buffer compiler, do not edit.
// Source file: ../wire-runtime/src/test/proto/person.proto
package retrofit.converter;

import com.squareup.wire.Message;
import com.squareup.wire.ProtoEnum;
import com.squareup.wire.ProtoField;
import java.util.Collections;
import java.util.List;

import static com.squareup.wire.Message.Datatype.ENUM;
import static com.squareup.wire.Message.Datatype.INT32;
import static com.squareup.wire.Message.Datatype.STRING;
import static com.squareup.wire.Message.Label.REPEATED;
import static com.squareup.wire.Message.Label.REQUIRED;

public final class Person extends Message {

public static final String DEFAULT_NAME = "";
public static final Integer DEFAULT_ID = 0;
public static final String DEFAULT_EMAIL = "";
public static final List<PhoneNumber> DEFAULT_PHONE = Collections.emptyList();

/**
* The customer's full name.
*/
@ProtoField(tag = 1, type = STRING, label = REQUIRED)
public final String name;

/**
* The customer's ID number.
*/
@ProtoField(tag = 2, type = INT32, label = REQUIRED)
public final Integer id;

/**
* Email address for the customer.
*/
@ProtoField(tag = 3, type = STRING)
public final String email;

/**
* A list of the user's phone numbers.
*/
@ProtoField(tag = 4, label = REPEATED)
public final List<PhoneNumber> phone;

private Person(Builder builder) {
super(builder);
this.name = builder.name;
this.id = builder.id;
this.email = builder.email;
this.phone = immutableCopyOf(builder.phone);
}

@Override
public boolean equals(Object other) {
if (!(other instanceof Person)) return false;
Person o = (Person) other;
return equals(name, o.name)
&& equals(id, o.id)
&& equals(email, o.email)
&& equals(phone, o.phone);
}

@Override
public int hashCode() {
int result = hashCode;
if (result == 0) {
result = name != null ? name.hashCode() : 0;
result = result * 37 + (id != null ? id.hashCode() : 0);
result = result * 37 + (email != null ? email.hashCode() : 0);
result = result * 37 + (phone != null ? phone.hashCode() : 0);
hashCode = result;
}
return result;
}

public static final class Builder extends Message.Builder<Person> {

public String name;
public Integer id;
public String email;
public List<PhoneNumber> phone;

public Builder() {
}

public Builder(Person message) {
super(message);
if (message == null) return;
this.name = message.name;
this.id = message.id;
this.email = message.email;
this.phone = copyOf(message.phone);
}

public Builder name(String name) {
this.name = name;
return this;
}

public Builder id(Integer id) {
this.id = id;
return this;
}

public Builder email(String email) {
this.email = email;
return this;
}

public Builder phone(List<PhoneNumber> phone) {
this.phone = phone;
return this;
}

@Override
public Person build() {
checkRequiredFields();
return new Person(this);
}
}

public enum PhoneType {
@ProtoEnum(0)
MOBILE,
@ProtoEnum(1)
HOME,
@ProtoEnum(2)
WORK,
}

public static final class PhoneNumber extends Message {

public static final String DEFAULT_NUMBER = "";
public static final PhoneType DEFAULT_TYPE = PhoneType.HOME;

/**
* The user's phone number.
*/
@ProtoField(tag = 1, type = STRING, label = REQUIRED)
public final String number;

/**
* The type of phone stored here.
*/
@ProtoField(tag = 2, type = ENUM)
public final PhoneType type;

private PhoneNumber(Builder builder) {
super(builder);
this.number = builder.number;
this.type = builder.type;
}

@Override
public boolean equals(Object other) {
if (!(other instanceof PhoneNumber)) return false;
PhoneNumber o = (PhoneNumber) other;
return equals(number, o.number)
&& equals(type, o.type);
}

@Override
public int hashCode() {
int result = hashCode;
if (result == 0) {
result = number != null ? number.hashCode() : 0;
result = result * 37 + (type != null ? type.hashCode() : 0);
hashCode = result;
}
return result;
}

public static final class Builder extends Message.Builder<PhoneNumber> {

public String number;
public PhoneType type;

public Builder() {
}

public Builder(PhoneNumber message) {
super(message);
if (message == null) return;
this.number = message.number;
this.type = message.type;
}

public Builder number(String number) {
this.number = number;
return this;
}

public Builder type(PhoneType type) {
this.type = type;
return this;
}

@Override
public PhoneNumber build() {
checkRequiredFields();
return new PhoneNumber(this);
}
}
}
}
Loading