aboutsummaryrefslogtreecommitdiff
path: root/java
diff options
context:
space:
mode:
Diffstat (limited to 'java')
-rw-r--r--java/src/main/java/com/google/protobuf/nano/CodedInputByteBufferNano.java17
-rw-r--r--java/src/main/java/com/google/protobuf/nano/Extension.java114
-rw-r--r--java/src/main/java/com/google/protobuf/nano/MessageNano.java2
-rw-r--r--java/src/main/java/com/google/protobuf/nano/UnknownFieldData.java47
-rw-r--r--java/src/main/java/com/google/protobuf/nano/WireFormatNano.java225
-rw-r--r--java/src/test/java/com/google/protobuf/NanoTest.java96
6 files changed, 497 insertions, 4 deletions
diff --git a/java/src/main/java/com/google/protobuf/nano/CodedInputByteBufferNano.java b/java/src/main/java/com/google/protobuf/nano/CodedInputByteBufferNano.java
index ed387882..c5fea5ae 100644
--- a/java/src/main/java/com/google/protobuf/nano/CodedInputByteBufferNano.java
+++ b/java/src/main/java/com/google/protobuf/nano/CodedInputByteBufferNano.java
@@ -541,6 +541,23 @@ public final class CodedInputByteBufferNano {
}
/**
+ * Retrieves a subset of data in the buffer. The returned array is not backed by the original
+ * buffer array.
+ *
+ * @param offset the position (relative to the buffer start position) to start at.
+ * @param length the number of bytes to retrieve.
+ */
+ public byte[] getData(int offset, int length) {
+ if (length == 0) {
+ return WireFormatNano.EMPTY_BYTES;
+ }
+ byte[] copy = new byte[length];
+ int start = bufferStart + offset;
+ System.arraycopy(buffer, start, copy, 0, length);
+ return copy;
+ }
+
+ /**
* Rewind to previous position. Cannot go forward.
*/
public void rewindToPosition(int position) {
diff --git a/java/src/main/java/com/google/protobuf/nano/Extension.java b/java/src/main/java/com/google/protobuf/nano/Extension.java
new file mode 100644
index 00000000..4512b01a
--- /dev/null
+++ b/java/src/main/java/com/google/protobuf/nano/Extension.java
@@ -0,0 +1,114 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2013 Google Inc. All rights reserved.
+// http://code.google.com/p/protobuf/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+package com.google.protobuf.nano;
+
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Type;
+import java.util.List;
+
+/**
+ * Represents an extension.
+ *
+ * @author bduff@google.com (Brian Duff)
+ * @param <T> the type of the extension.
+ */
+public class Extension<T> {
+ public final int fieldNumber;
+ public boolean isRepeatedField;
+ public Class<T> fieldType;
+ public Class<T> listType;
+
+ private Extension(int fieldNumber, TypeLiteral<T> type) {
+ this.fieldNumber = fieldNumber;
+ isRepeatedField = type.isList();
+ fieldType = type.getTargetClass();
+ listType = isRepeatedField ? type.getListType() : null;
+ }
+
+ /**
+ * Creates a new instance of {@code Extension} for the specified {@code fieldNumber} and
+ * {@code type}.
+ */
+ public static <T> Extension<T> create(int fieldNumber, TypeLiteral<T> type) {
+ return new Extension<T>(fieldNumber, type);
+ }
+
+ /**
+ * Creates a new instance of {@code Extension} for the specified {@code fieldNumber} and
+ * {@code type}. This version is used for repeated fields.
+ */
+ public static <T> Extension<List<T>> createRepeated(int fieldNumber, TypeLiteral<List<T>> type) {
+ return new Extension<List<T>>(fieldNumber, type);
+ }
+
+ /**
+ * Represents a generic type literal. We can't typesafely reference a
+ * Class&lt;List&lt;Foo>>.class in Java, so we use this instead.
+ * See: http://gafter.blogspot.com/2006/12/super-type-tokens.html
+ *
+ * <p>Somewhat specialized because we only ever have a Foo or a List&lt;Foo>.
+ */
+ public static abstract class TypeLiteral<T> {
+ private final Type type;
+
+ protected TypeLiteral() {
+ Type superclass = getClass().getGenericSuperclass();
+ if (superclass instanceof Class) {
+ throw new RuntimeException("Missing type parameter");
+ }
+ this.type = ((ParameterizedType) superclass).getActualTypeArguments()[0];
+ }
+
+ /**
+ * If the generic type is a list, returns {@code true}.
+ */
+ private boolean isList() {
+ return type instanceof ParameterizedType;
+ }
+
+ @SuppressWarnings("unchecked")
+ private Class<T> getListType() {
+ return (Class<T>) ((ParameterizedType) type).getRawType();
+ }
+
+ /**
+ * If the generic type is a list, returns the type of element in the list. Otherwise,
+ * returns the actual type.
+ */
+ @SuppressWarnings("unchecked")
+ private Class<T> getTargetClass() {
+ if (isList()) {
+ return (Class<T>) ((ParameterizedType) type).getActualTypeArguments()[0];
+ }
+ return (Class<T>) type;
+ }
+ }
+}
diff --git a/java/src/main/java/com/google/protobuf/nano/MessageNano.java b/java/src/main/java/com/google/protobuf/nano/MessageNano.java
index d6c1e9a8..5c1eb2ff 100644
--- a/java/src/main/java/com/google/protobuf/nano/MessageNano.java
+++ b/java/src/main/java/com/google/protobuf/nano/MessageNano.java
@@ -93,7 +93,7 @@ public abstract class MessageNano {
output.checkNoSpaceLeft();
} catch (IOException e) {
throw new RuntimeException("Serializing to a byte array threw an IOException "
- + "(should never happen).");
+ + "(should never happen).", e);
}
}
diff --git a/java/src/main/java/com/google/protobuf/nano/UnknownFieldData.java b/java/src/main/java/com/google/protobuf/nano/UnknownFieldData.java
new file mode 100644
index 00000000..0db2a83c
--- /dev/null
+++ b/java/src/main/java/com/google/protobuf/nano/UnknownFieldData.java
@@ -0,0 +1,47 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2013 Google Inc. All rights reserved.
+// http://code.google.com/p/protobuf/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+package com.google.protobuf.nano;
+
+/**
+ * Stores unknown fields. These might be extensions or fields that the generated API doesn't
+ * know about yet.
+ *
+ * @author bduff@google.com (Brian Duff)
+ */
+public final class UnknownFieldData {
+ final int tag;
+ final byte[] bytes;
+
+ UnknownFieldData(int tag, byte[] bytes) {
+ this.tag = tag;
+ this.bytes = bytes;
+ }
+}
diff --git a/java/src/main/java/com/google/protobuf/nano/WireFormatNano.java b/java/src/main/java/com/google/protobuf/nano/WireFormatNano.java
index 8fa36364..c901e59c 100644
--- a/java/src/main/java/com/google/protobuf/nano/WireFormatNano.java
+++ b/java/src/main/java/com/google/protobuf/nano/WireFormatNano.java
@@ -31,6 +31,9 @@
package com.google.protobuf.nano;
import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
/**
* This class is used internally by the Protocol Buffer library and generated
@@ -97,8 +100,12 @@ public final class WireFormatNano {
public static final byte[] EMPTY_BYTES = {};
/**
- * Called by subclasses to parse an unknown field.
- * @return {@code true} unless the tag is an end-group tag.
+ * Parses an unknown field. This implementation skips the field.
+ *
+ * <p>Generated messages will call this for unknown fields if the store_unknown_fields
+ * option is off.
+ *
+ * @return {@literal true} unless the tag is an end-group tag.
*/
public static boolean parseUnknownField(
final CodedInputByteBufferNano input,
@@ -107,6 +114,30 @@ public final class WireFormatNano {
}
/**
+ * Stores the binary data of an unknown field.
+ *
+ * <p>Generated messages will call this for unknown fields if the store_unknown_fields
+ * option is on.
+ *
+ * @param data a Collection in which to store the data.
+ * @param input the input buffer.
+ * @param tag the tag of the field.
+
+ * @return {@literal true} unless the tag is an end-group tag.
+ */
+ public static boolean storeUnknownField(
+ final List<UnknownFieldData> data,
+ final CodedInputByteBufferNano input,
+ final int tag) throws IOException {
+ int startPos = input.getPosition();
+ boolean skip = input.skipField(tag);
+ int endPos = input.getPosition();
+ byte[] bytes = input.getData(startPos, endPos - startPos);
+ data.add(new UnknownFieldData(tag, bytes));
+ return skip;
+ }
+
+ /**
* Computes the array length of a repeated field. We assume that in the common case repeated
* fields are contiguously serialized but we still correctly handle interspersed values of a
* repeated field (but with extra allocations).
@@ -135,4 +166,194 @@ public final class WireFormatNano {
input.rewindToPosition(startPos);
return arrayLength;
}
+
+ /**
+ * Decodes the value of an extension.
+ */
+ public static <T> T getExtension(Extension<T> extension, List<UnknownFieldData> unknownFields) {
+ if (unknownFields == null) {
+ return null;
+ }
+ List<UnknownFieldData> dataForField = new ArrayList<UnknownFieldData>();
+ for (UnknownFieldData data : unknownFields) {
+ if (getTagFieldNumber(data.tag) == extension.fieldNumber) {
+ dataForField.add(data);
+ }
+ }
+ if (dataForField.isEmpty()) {
+ return null;
+ }
+
+ if (extension.isRepeatedField) {
+ List<Object> result = new ArrayList<Object>(dataForField.size());
+ for (UnknownFieldData data : dataForField) {
+ result.add(readData(extension.fieldType, data.bytes));
+ }
+ return extension.listType.cast(result);
+ }
+
+ // Normal fields. Note that the protobuf docs require us to handle multiple instances
+ // of the same field even for fields that are not repeated.
+ UnknownFieldData lastData = dataForField.get(dataForField.size() - 1);
+ return readData(extension.fieldType, lastData.bytes);
+ }
+
+ /**
+ * Reads (extension) data of the specified type from the specified byte array.
+ *
+ * @throws IllegalArgumentException if an error occurs while reading the data.
+ */
+ private static <T> T readData(Class<T> clazz, byte[] data) {
+ if (data.length == 0) {
+ return null;
+ }
+ CodedInputByteBufferNano buffer = CodedInputByteBufferNano.newInstance(data);
+ try {
+ if (clazz == String.class) {
+ return clazz.cast(buffer.readString());
+ } else if (clazz == Integer.class) {
+ return clazz.cast(buffer.readInt32());
+ } else if (clazz == Long.class) {
+ return clazz.cast(buffer.readInt64());
+ } else if (clazz == Boolean.class) {
+ return clazz.cast(buffer.readBool());
+ } else if (clazz == Float.class) {
+ return clazz.cast(buffer.readFloat());
+ } else if (clazz == Double.class) {
+ return clazz.cast(buffer.readDouble());
+ } else if (clazz == byte[].class) {
+ return clazz.cast(buffer.readBytes());
+ } else if (MessageNano.class.isAssignableFrom(clazz)) {
+ try {
+ MessageNano message = (MessageNano) clazz.newInstance();
+ buffer.readMessage(message);
+ return clazz.cast(message);
+ } catch (IllegalAccessException e) {
+ throw new IllegalArgumentException("Error creating instance of class " + clazz, e);
+ } catch (InstantiationException e) {
+ throw new IllegalArgumentException("Error creating instance of class " + clazz, e);
+ }
+ } else {
+ throw new IllegalArgumentException("Unhandled extension field type: " + clazz);
+ }
+ } catch (IOException e) {
+ throw new IllegalArgumentException("Error reading extension field", e);
+ }
+ }
+
+ public static <T> void setExtension(Extension<T> extension, T value,
+ List<UnknownFieldData> unknownFields) {
+ // First, remove all unknown fields with this tag.
+ for (Iterator<UnknownFieldData> i = unknownFields.iterator(); i.hasNext();) {
+ UnknownFieldData data = i.next();
+ if (extension.fieldNumber == getTagFieldNumber(data.tag)) {
+ i.remove();
+ }
+ }
+ if (value == null) {
+ return;
+ }
+ // Repeated field.
+ if (value instanceof List) {
+ for (Object item : (List<?>) value) {
+ unknownFields.add(write(extension.fieldNumber, item));
+ }
+ } else {
+ unknownFields.add(write(extension.fieldNumber, value));
+ }
+ }
+
+ /**
+ * Writes extension data and returns an {@link UnknownFieldData} containing
+ * bytes and a tag.
+ *
+ * @throws IllegalArgumentException if an error occurs while writing.
+ */
+ private static UnknownFieldData write(int fieldNumber, Object object) {
+ byte[] data;
+ int tag;
+ Class<?> clazz = object.getClass();
+ try {
+ if (clazz == String.class) {
+ String str = (String) object;
+ data = new byte[CodedOutputByteBufferNano.computeStringSizeNoTag(str)];
+ CodedOutputByteBufferNano.newInstance(data).writeStringNoTag(str);
+ tag = makeTag(fieldNumber, WIRETYPE_LENGTH_DELIMITED);
+ } else if (clazz == Integer.class) {
+ Integer integer = (Integer) object;
+ data = new byte[CodedOutputByteBufferNano.computeInt32SizeNoTag(integer)];
+ CodedOutputByteBufferNano.newInstance(data).writeInt32NoTag(integer);
+ tag = makeTag(fieldNumber, WIRETYPE_VARINT);
+ } else if (clazz == Long.class) {
+ Long longValue = (Long) object;
+ data = new byte[CodedOutputByteBufferNano.computeInt64SizeNoTag(longValue)];
+ CodedOutputByteBufferNano.newInstance(data).writeInt64NoTag(longValue);
+ tag = makeTag(fieldNumber, WIRETYPE_VARINT);
+ } else if (clazz == Boolean.class) {
+ Boolean boolValue = (Boolean) object;
+ data = new byte[CodedOutputByteBufferNano.computeBoolSizeNoTag(boolValue)];
+ CodedOutputByteBufferNano.newInstance(data).writeBoolNoTag(boolValue);
+ tag = makeTag(fieldNumber, WIRETYPE_VARINT);
+ } else if (clazz == Float.class) {
+ Float floatValue = (Float) object;
+ data = new byte[CodedOutputByteBufferNano.computeFloatSizeNoTag(floatValue)];
+ CodedOutputByteBufferNano.newInstance(data).writeFloatNoTag(floatValue);
+ tag = makeTag(fieldNumber, WIRETYPE_FIXED32);
+ } else if (clazz == Double.class) {
+ Double doubleValue = (Double) object;
+ data = new byte[CodedOutputByteBufferNano.computeDoubleSizeNoTag(doubleValue)];
+ CodedOutputByteBufferNano.newInstance(data).writeDoubleNoTag(doubleValue);
+ tag = makeTag(fieldNumber, WIRETYPE_FIXED64);
+ } else if (clazz == byte[].class) {
+ byte[] byteArrayValue = (byte[]) object;
+ data = new byte[CodedOutputByteBufferNano.computeByteArraySizeNoTag(byteArrayValue)];
+ CodedOutputByteBufferNano.newInstance(data).writeByteArrayNoTag(byteArrayValue);
+ tag = makeTag(fieldNumber, WIRETYPE_LENGTH_DELIMITED);
+ } else if (MessageNano.class.isAssignableFrom(clazz)) {
+ MessageNano messageValue = (MessageNano) object;
+
+ int messageSize = messageValue.getSerializedSize();
+ int delimiterSize = CodedOutputByteBufferNano.computeRawVarint32Size(messageSize);
+ data = new byte[messageSize + delimiterSize];
+ CodedOutputByteBufferNano buffer = CodedOutputByteBufferNano.newInstance(data);
+ buffer.writeRawVarint32(messageSize);
+ buffer.writeRawBytes(MessageNano.toByteArray(messageValue));
+ tag = makeTag(fieldNumber, WIRETYPE_LENGTH_DELIMITED);
+ } else {
+ throw new IllegalArgumentException("Unhandled extension field type: " + clazz);
+ }
+ } catch (IOException e) {
+ throw new IllegalArgumentException(e);
+ }
+ return new UnknownFieldData(tag, data);
+ }
+
+ /**
+ * Given a set of unknown field data, compute the wire size.
+ */
+ public static int computeWireSize(List<UnknownFieldData> unknownFields) {
+ if (unknownFields == null) {
+ return 0;
+ }
+ int size = 0;
+ for (UnknownFieldData unknownField : unknownFields) {
+ size += CodedOutputByteBufferNano.computeRawVarint32Size(unknownField.tag);
+ size += unknownField.bytes.length;
+ }
+ return size;
+ }
+
+ /**
+ * Write unknown fields.
+ */
+ public static void writeUnknownFields(List<UnknownFieldData> unknownFields,
+ CodedOutputByteBufferNano outBuffer) throws IOException {
+ if (unknownFields == null) {
+ return;
+ }
+ for (UnknownFieldData data : unknownFields) {
+ outBuffer.writeTag(getTagFieldNumber(data.tag), getTagWireType(data.tag));
+ outBuffer.writeRawBytes(data.bytes);
+ }
+ }
}
diff --git a/java/src/test/java/com/google/protobuf/NanoTest.java b/java/src/test/java/com/google/protobuf/NanoTest.java
index da17a9e1..38fafb9e 100644
--- a/java/src/test/java/com/google/protobuf/NanoTest.java
+++ b/java/src/test/java/com/google/protobuf/NanoTest.java
@@ -30,6 +30,9 @@
package com.google.protobuf;
+import com.google.protobuf.nano.CodedInputByteBufferNano;
+import com.google.protobuf.nano.Extensions;
+import com.google.protobuf.nano.Extensions.AnotherMessage;
import com.google.protobuf.nano.InternalNano;
import com.google.protobuf.nano.MessageNano;
import com.google.protobuf.nano.NanoOuterClass;
@@ -37,10 +40,12 @@ import com.google.protobuf.nano.NanoOuterClass.TestAllTypesNano;
import com.google.protobuf.nano.RecursiveMessageNano;
import com.google.protobuf.nano.SimpleMessageNano;
import com.google.protobuf.nano.UnittestImportNano;
-import com.google.protobuf.nano.CodedInputByteBufferNano;
import junit.framework.TestCase;
+import java.util.ArrayList;
+import java.util.List;
+
/**
* Test nano runtime.
*
@@ -2155,4 +2160,93 @@ public class NanoTest extends TestCase {
assertTrue(protoPrint.contains(" default_int32: 41"));
assertTrue(protoPrint.contains(" default_string: \"hello\""));
}
+
+ public void testExtensions() throws Exception {
+ Extensions.ExtendableMessage message = new Extensions.ExtendableMessage();
+ message.field = 5;
+ message.setExtension(Extensions.someString, "Hello World!");
+ message.setExtension(Extensions.someBool, true);
+ message.setExtension(Extensions.someInt, 42);
+ message.setExtension(Extensions.someLong, 124234234234L);
+ message.setExtension(Extensions.someFloat, 42.0f);
+ message.setExtension(Extensions.someDouble, 422222.0);
+ message.setExtension(Extensions.someEnum, Extensions.FIRST_VALUE);
+ AnotherMessage another = new AnotherMessage();
+ another.string = "Foo";
+ another.value = true;
+ message.setExtension(Extensions.someMessage, another);
+
+ message.setExtension(Extensions.someRepeatedString, list("a", "bee", "seeya"));
+ message.setExtension(Extensions.someRepeatedBool, list(true, false, true));
+ message.setExtension(Extensions.someRepeatedInt, list(4, 8, 15, 16, 23, 42));
+ message.setExtension(Extensions.someRepeatedLong, list(4L, 8L, 15L, 16L, 23L, 42L));
+ message.setExtension(Extensions.someRepeatedFloat, list(1.0f, 3.0f));
+ message.setExtension(Extensions.someRepeatedDouble, list(55.133, 3.14159));
+ message.setExtension(Extensions.someRepeatedEnum, list(Extensions.FIRST_VALUE,
+ Extensions.SECOND_VALUE));
+ AnotherMessage second = new AnotherMessage();
+ second.string = "Whee";
+ second.value = false;
+ message.setExtension(Extensions.someRepeatedMessage, list(another, second));
+
+ byte[] data = MessageNano.toByteArray(message);
+
+ Extensions.ExtendableMessage deserialized = Extensions.ExtendableMessage.parseFrom(data);
+ assertEquals(5, deserialized.field);
+ assertEquals("Hello World!", deserialized.getExtension(Extensions.someString));
+ assertEquals(Boolean.TRUE, deserialized.getExtension(Extensions.someBool));
+ assertEquals(Integer.valueOf(42), deserialized.getExtension(Extensions.someInt));
+ assertEquals(Long.valueOf(124234234234L), deserialized.getExtension(Extensions.someLong));
+ assertEquals(Float.valueOf(42.0f), deserialized.getExtension(Extensions.someFloat));
+ assertEquals(Double.valueOf(422222.0), deserialized.getExtension(Extensions.someDouble));
+ assertEquals(Integer.valueOf(Extensions.FIRST_VALUE),
+ deserialized.getExtension(Extensions.someEnum));
+ assertEquals(another.string, deserialized.getExtension(Extensions.someMessage).string);
+ assertEquals(another.value, deserialized.getExtension(Extensions.someMessage).value);
+ assertEquals(list("a", "bee", "seeya"), deserialized.getExtension(Extensions.someRepeatedString));
+ assertEquals(list(true, false, true), deserialized.getExtension(Extensions.someRepeatedBool));
+ assertEquals(list(4, 8, 15, 16, 23, 42), deserialized.getExtension(Extensions.someRepeatedInt));
+ assertEquals(list(4L, 8L, 15L, 16L, 23L, 42L), deserialized.getExtension(Extensions.someRepeatedLong));
+ assertEquals(list(1.0f, 3.0f), deserialized.getExtension(Extensions.someRepeatedFloat));
+ assertEquals(list(55.133, 3.14159), deserialized.getExtension(Extensions.someRepeatedDouble));
+ assertEquals(list(Extensions.FIRST_VALUE,
+ Extensions.SECOND_VALUE), deserialized.getExtension(Extensions.someRepeatedEnum));
+ assertEquals("Foo", deserialized.getExtension(Extensions.someRepeatedMessage).get(0).string);
+ assertEquals(true, deserialized.getExtension(Extensions.someRepeatedMessage).get(0).value);
+ assertEquals("Whee", deserialized.getExtension(Extensions.someRepeatedMessage).get(1).string);
+ assertEquals(false, deserialized.getExtension(Extensions.someRepeatedMessage).get(1).value);
+ }
+
+ public void testUnknownFields() throws Exception {
+ // Check that we roundtrip (serialize and deserialize) unrecognized fields.
+ AnotherMessage message = new AnotherMessage();
+ message.string = "Hello World";
+ message.value = false;
+
+ byte[] bytes = MessageNano.toByteArray(message);
+ int extraFieldSize = CodedOutputStream.computeStringSize(1001, "This is an unknown field");
+ byte[] newBytes = new byte[bytes.length + extraFieldSize];
+ System.arraycopy(bytes, 0, newBytes, 0, bytes.length);
+ CodedOutputStream.newInstance(newBytes, bytes.length, extraFieldSize).writeString(1001,
+ "This is an unknown field");
+
+ // Deserialize with an unknown field.
+ AnotherMessage deserialized = AnotherMessage.parseFrom(newBytes);
+ byte[] serialized = MessageNano.toByteArray(deserialized);
+
+ assertEquals(newBytes.length, serialized.length);
+
+ // Clear, and make sure it clears everything.
+ deserialized.clear();
+ assertEquals(0, MessageNano.toByteArray(deserialized).length);
+ }
+
+ private <T> List<T> list(T first, T... remaining) {
+ List<T> list = new ArrayList<T>();
+ list.add(first);
+ for (T item : remaining) {
+ list.add(item);
+ }
+ return list;
+ }
}