aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBrian Duff <bduff@google.com>2013-06-19 13:17:43 -0700
committerBrian Duff <bduff@google.com>2013-06-24 16:02:12 -0700
commit0e055f079f53b07de3705838a7b4742ce56839f8 (patch)
treed60a02039cea8773f7ba76efd0c7fd6f23768d70
parent9459b5c45b0ad33bea9376c2afb66cf93da610ac (diff)
downloadexternal_protobuf-0e055f079f53b07de3705838a7b4742ce56839f8.zip
external_protobuf-0e055f079f53b07de3705838a7b4742ce56839f8.tar.gz
external_protobuf-0e055f079f53b07de3705838a7b4742ce56839f8.tar.bz2
Nano support for extensions and unknown fields.
You can use the processor option store_unknown_fields to switch this support on: aprotoc --javanano_out=store_unknown_fields=true:/tmp/out A separate option for extensions isn't required. Support for unknown fields must be turned on to allow storing and retrieving extensions, because they are just stored as unknown fields. If unknown fields are switched on, extension related code will be generated when a proto message includes an extension range, or an extension is encountered. By default, store_unknown_fields is false. No additional code is generated, and the generator will error out if protos contain extension ranges or extensions. Change-Id: I1e034c9e8f3305612953f72438189a7da6ed2167
-rw-r--r--Android.mk1
-rw-r--r--java/pom.xml6
-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
-rw-r--r--src/google/protobuf/compiler/javanano/javanano_extension.cc96
-rw-r--r--src/google/protobuf/compiler/javanano/javanano_extension.h74
-rw-r--r--src/google/protobuf/compiler/javanano/javanano_file.cc11
-rw-r--r--src/google/protobuf/compiler/javanano/javanano_generator.cc2
-rw-r--r--src/google/protobuf/compiler/javanano/javanano_message.cc93
-rw-r--r--src/google/protobuf/compiler/javanano/javanano_params.h9
-rw-r--r--src/google/protobuf/unittest_extension_nano.proto44
15 files changed, 812 insertions, 25 deletions
diff --git a/Android.mk b/Android.mk
index dd48922..6a7c6c0 100644
--- a/Android.mk
+++ b/Android.mk
@@ -108,6 +108,7 @@ COMPILER_SRC_FILES := \
src/google/protobuf/compiler/javamicro/javamicro_primitive_field.cc \
src/google/protobuf/compiler/javanano/javanano_enum.cc \
src/google/protobuf/compiler/javanano/javanano_enum_field.cc \
+ src/google/protobuf/compiler/javanano/javanano_extension.cc \
src/google/protobuf/compiler/javanano/javanano_field.cc \
src/google/protobuf/compiler/javanano/javanano_file.cc \
src/google/protobuf/compiler/javanano/javanano_generator.cc \
diff --git a/java/pom.xml b/java/pom.xml
index 46fc764..0b9f6e4 100644
--- a/java/pom.xml
+++ b/java/pom.xml
@@ -138,6 +138,12 @@
<arg value="../src/google/protobuf/unittest_import_nano.proto" />
<arg value="../src/google/protobuf/unittest_enum_multiplejava_nano.proto" />
</exec>
+ <exec executable="../src/protoc">
+ <arg value="--javanano_out=store_unknown_fields=true:target/generated-test-sources" />
+ <arg value="--proto_path=../src" />
+ <arg value="--proto_path=src/test/java" />
+ <arg value="../src/google/protobuf/unittest_extension_nano.proto" />
+ </exec>
</tasks>
<testSourceRoot>target/generated-test-sources</testSourceRoot>
<!--testSourceRoot>target/generated-test-sources/opt-space</testSourceRoot-->
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 ed38788..c5fea5a 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 0000000..4512b01
--- /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 d6c1e9a..5c1eb2f 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 0000000..0db2a83
--- /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 8fa3636..c901e59 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 da17a9e..38fafb9 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;
+ }
}
diff --git a/src/google/protobuf/compiler/javanano/javanano_extension.cc b/src/google/protobuf/compiler/javanano/javanano_extension.cc
new file mode 100644
index 0000000..ea74af9
--- /dev/null
+++ b/src/google/protobuf/compiler/javanano/javanano_extension.cc
@@ -0,0 +1,96 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 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.
+
+// Author: bduff@google.com (Brian Duff)
+
+#include <google/protobuf/compiler/javanano/javanano_extension.h>
+#include <google/protobuf/compiler/javanano/javanano_helpers.h>
+#include <google/protobuf/io/printer.h>
+#include <google/protobuf/stubs/strutil.h>
+#include <google/protobuf/wire_format.h>
+
+namespace google {
+namespace protobuf {
+namespace compiler {
+namespace javanano {
+
+using internal::WireFormat;
+
+void SetVariables(const FieldDescriptor* descriptor, const Params params,
+ map<string, string>* variables) {
+ (*variables)["name"] = UnderscoresToCamelCase(descriptor);
+ (*variables)["number"] = SimpleItoa(descriptor->number());
+ (*variables)["extends"] = ClassName(params, descriptor->containing_type());
+
+ string type;
+ JavaType java_type = GetJavaType(descriptor->type());
+ switch (java_type) {
+ case JAVATYPE_ENUM:
+ type = "java.lang.Integer";
+ break;
+ case JAVATYPE_MESSAGE:
+ type = ClassName(params, descriptor->message_type());
+ break;
+ default:
+ type = BoxedPrimitiveTypeName(java_type);
+ break;
+ }
+ (*variables)["type"] = type;
+}
+
+ExtensionGenerator::
+ExtensionGenerator(const FieldDescriptor* descriptor, const Params& params)
+ : params_(params), descriptor_(descriptor) {
+ SetVariables(descriptor, params, &variables_);
+}
+
+ExtensionGenerator::~ExtensionGenerator() {}
+
+void ExtensionGenerator::Generate(io::Printer* printer) const {
+ if (descriptor_->is_repeated()) {
+ printer->Print(variables_,
+ "// Extends $extends$\n"
+ "public static final com.google.protobuf.nano.Extension<java.util.List<$type$>> $name$ = \n"
+ " com.google.protobuf.nano.Extension.createRepeated($number$,\n"
+ " new com.google.protobuf.nano.Extension.TypeLiteral<java.util.List<$type$>>(){});\n");
+ } else {
+ printer->Print(variables_,
+ "// Extends $extends$\n"
+ "public static final com.google.protobuf.nano.Extension<$type$> $name$ =\n"
+ " com.google.protobuf.nano.Extension.create($number$,\n"
+ " new com.google.protobuf.nano.Extension.TypeLiteral<$type$>(){});\n");
+ }
+}
+
+} // namespace javanano
+} // namespace compiler
+} // namespace protobuf
+} // namespace google
+
diff --git a/src/google/protobuf/compiler/javanano/javanano_extension.h b/src/google/protobuf/compiler/javanano/javanano_extension.h
new file mode 100644
index 0000000..c6543eb
--- /dev/null
+++ b/src/google/protobuf/compiler/javanano/javanano_extension.h
@@ -0,0 +1,74 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 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.
+
+// Author: bduff@google.com (Brian Duff)
+// Based on original Protocol Buffers design by
+// Sanjay Ghemawat, Jeff Dean, and others.
+
+#ifndef GOOGLE_PROTOBUF_COMPILER_JAVA_EXTENSION_H_
+#define GOOGLE_PROTOBUF_COMPILER_JAVA_EXTENSION_H_
+
+#include <google/protobuf/stubs/common.h>
+#include <google/protobuf/compiler/javanano/javanano_params.h>
+#include <google/protobuf/descriptor.pb.h>
+
+
+namespace google {
+namespace protobuf {
+ namespace io {
+ class Printer; // printer.h
+ }
+}
+
+namespace protobuf {
+namespace compiler {
+namespace javanano {
+
+class ExtensionGenerator {
+ public:
+ explicit ExtensionGenerator(const FieldDescriptor* descriptor, const Params& params);
+ ~ExtensionGenerator();
+
+ void Generate(io::Printer* printer) const;
+
+ private:
+ const Params& params_;
+ const FieldDescriptor* descriptor_;
+ map<string, string> variables_;
+
+ GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(ExtensionGenerator);
+};
+
+} // namespace javanano
+} // namespace compiler
+} // namespace protobuf
+} // namespace google
+
+#endif // GOOGLE_PROTOBUF_COMPILER_JAVA_EXTENSION_H_
diff --git a/src/google/protobuf/compiler/javanano/javanano_file.cc b/src/google/protobuf/compiler/javanano/javanano_file.cc
index 2f42fa0..6efa2bf 100644
--- a/src/google/protobuf/compiler/javanano/javanano_file.cc
+++ b/src/google/protobuf/compiler/javanano/javanano_file.cc
@@ -34,6 +34,7 @@
#include <google/protobuf/compiler/javanano/javanano_file.h>
#include <google/protobuf/compiler/javanano/javanano_enum.h>
+#include <google/protobuf/compiler/javanano/javanano_extension.h>
#include <google/protobuf/compiler/javanano/javanano_helpers.h>
#include <google/protobuf/compiler/javanano/javanano_message.h>
#include <google/protobuf/compiler/code_generator.h>
@@ -94,10 +95,11 @@ bool FileGenerator::Validate(string* error) {
// Check for extensions
FileDescriptorProto file_proto;
file_->CopyTo(&file_proto);
- if (UsesExtensions(file_proto)) {
+ if (UsesExtensions(file_proto) && !params_.store_unknown_fields()) {
error->assign(file_->name());
error->append(
- ": Java NANO_RUNTIME does not support extensions\"");
+ ": Java NANO_RUNTIME only supports extensions when the "
+ "'store_unknown_fields' generator option is 'true'.");
return false;
}
@@ -179,6 +181,11 @@ void FileGenerator::Generate(io::Printer* printer) {
// -----------------------------------------------------------------
+ // Extensions.
+ for (int i = 0; i < file_->extension_count(); i++) {
+ ExtensionGenerator(file_->extension(i), params_).Generate(printer);
+ }
+
if (!params_.java_multiple_files()) {
for (int i = 0; i < file_->enum_type_count(); i++) {
EnumGenerator(file_->enum_type(i), params_).Generate(printer);
diff --git a/src/google/protobuf/compiler/javanano/javanano_generator.cc b/src/google/protobuf/compiler/javanano/javanano_generator.cc
index 554f6d4..19a3dcc 100644
--- a/src/google/protobuf/compiler/javanano/javanano_generator.cc
+++ b/src/google/protobuf/compiler/javanano/javanano_generator.cc
@@ -115,6 +115,8 @@ bool JavaNanoGenerator::Generate(const FileDescriptor* file,
return false;
}
params.set_java_outer_classname(parts[0], parts[1]);
+ } else if (options[i].first == "store_unknown_fields") {
+ params.set_store_unknown_fields(options[i].second == "true");
} else if (options[i].first == "java_multiple_files") {
params.set_java_multiple_files(options[i].second == "true");
} else {
diff --git a/src/google/protobuf/compiler/javanano/javanano_message.cc b/src/google/protobuf/compiler/javanano/javanano_message.cc
index f8a4fe7..2740779 100644
--- a/src/google/protobuf/compiler/javanano/javanano_message.cc
+++ b/src/google/protobuf/compiler/javanano/javanano_message.cc
@@ -36,6 +36,7 @@
#include <google/protobuf/stubs/hash.h>
#include <google/protobuf/compiler/javanano/javanano_message.h>
#include <google/protobuf/compiler/javanano/javanano_enum.h>
+#include <google/protobuf/compiler/javanano/javanano_extension.h>
#include <google/protobuf/compiler/javanano/javanano_helpers.h>
#include <google/protobuf/stubs/strutil.h>
#include <google/protobuf/io/printer.h>
@@ -117,10 +118,6 @@ void MessageGenerator::GenerateStaticVariableInitializers(
MessageGenerator(descriptor_->nested_type(i), params_)
.GenerateStaticVariableInitializers(printer);
}
-
- if (descriptor_->extension_count() != 0) {
- GOOGLE_LOG(FATAL) << "Extensions not supported in NANO_RUNTIME\n";
- }
}
void MessageGenerator::Generate(io::Printer* printer) {
@@ -135,9 +132,10 @@ void MessageGenerator::Generate(io::Printer* printer) {
GOOGLE_LOG(INFO) << "has_java_outer_classname()=" << params_.has_java_outer_classname(file_->name());
#endif
- if ((descriptor_->extension_count() != 0)
- || (descriptor_->extension_range_count() != 0)) {
- GOOGLE_LOG(FATAL) << "Extensions not supported in NANO_RUNTIME\n";
+ if (!params_.store_unknown_fields() &&
+ (descriptor_->extension_count() != 0 || descriptor_->extension_range_count() != 0)) {
+ GOOGLE_LOG(FATAL) << "Extensions are only supported in NANO_RUNTIME if the "
+ "'store_unknown_fields' generator option is 'true'\n";
}
// Note: Fields (which will be emitted in the loop, below) may have the same names as fields in
@@ -156,7 +154,17 @@ void MessageGenerator::Generate(io::Printer* printer) {
"\n",
"classname", descriptor_->name());
+ if (params_.store_unknown_fields()) {
+ printer->Print(
+ "private java.util.List<com.google.protobuf.nano.UnknownFieldData>\n"
+ " unknownFieldData;\n");
+ }
+
// Nested types and extensions
+ for (int i = 0; i < descriptor_->extension_count(); i++) {
+ ExtensionGenerator(descriptor_->extension(i), params_).Generate(printer);
+ }
+
for (int i = 0; i < descriptor_->enum_type_count(); i++) {
EnumGenerator(descriptor_->enum_type(i), params_).Generate(printer);
}
@@ -173,6 +181,24 @@ void MessageGenerator::Generate(io::Printer* printer) {
}
GenerateClear(printer);
+
+ // If we have an extension range, generate accessors for extensions.
+ if (params_.store_unknown_fields()
+ && descriptor_->extension_range_count() > 0) {
+ printer->Print(
+ "public <T> T getExtension(com.google.protobuf.nano.Extension<T> extension) {\n"
+ " return com.google.protobuf.nano.WireFormatNano.getExtension(\n"
+ " extension, unknownFieldData);\n"
+ "}\n\n"
+ "public <T> void setExtension(com.google.protobuf.nano.Extension<T> extension, T value) {\n"
+ " if (unknownFieldData == null) {\n"
+ " unknownFieldData = \n"
+ " new java.util.ArrayList<com.google.protobuf.nano.UnknownFieldData>();\n"
+ " }\n"
+ " com.google.protobuf.nano.WireFormatNano.setExtension(\n"
+ " extension, value, unknownFieldData);\n"
+ "}\n\n");
+ }
GenerateMessageSerializationMethods(printer);
GenerateMergeFromMethods(printer);
GenerateParseFromMethods(printer);
@@ -188,12 +214,8 @@ GenerateMessageSerializationMethods(io::Printer* printer) {
scoped_array<const FieldDescriptor*> sorted_fields(
SortFieldsByNumber(descriptor_));
- if (descriptor_->extension_range_count() != 0) {
- GOOGLE_LOG(FATAL) << "Extensions not supported in NANO_RUNTIME\n";
- }
-
// writeTo only throws an exception if it contains one or more fields to write
- if (descriptor_->field_count() > 0) {
+ if (descriptor_->field_count() > 0 || params_.store_unknown_fields()) {
printer->Print(
"@Override\n"
"public void writeTo(com.google.protobuf.nano.CodedOutputByteBufferNano output)\n"
@@ -207,7 +229,14 @@ GenerateMessageSerializationMethods(io::Printer* printer) {
// Output the fields in sorted order
for (int i = 0; i < descriptor_->field_count(); i++) {
- GenerateSerializeOneField(printer, sorted_fields[i]);
+ GenerateSerializeOneField(printer, sorted_fields[i]);
+ }
+
+ // Write unknown fields.
+ if (params_.store_unknown_fields()) {
+ printer->Print(
+ "com.google.protobuf.nano.WireFormatNano.writeUnknownFields(\n"
+ " unknownFieldData, output);\n");
}
printer->Outdent();
@@ -233,6 +262,11 @@ GenerateMessageSerializationMethods(io::Printer* printer) {
field_generators_.get(sorted_fields[i]).GenerateSerializedSizeCode(printer);
}
+ if (params_.store_unknown_fields()) {
+ printer->Print(
+ "size += com.google.protobuf.nano.WireFormatNano.computeWireSize(unknownFieldData);\n");
+ }
+
printer->Outdent();
printer->Print(
" cachedSize = size;\n"
@@ -266,12 +300,28 @@ void MessageGenerator::GenerateMergeFromMethods(io::Printer* printer) {
printer->Print(
"case 0:\n" // zero signals EOF / limit reached
" return this;\n"
- "default: {\n"
- " if (!com.google.protobuf.nano.WireFormatNano.parseUnknownField(input, tag)) {\n"
- " return this;\n" // it's an endgroup tag
- " }\n"
- " break;\n"
- "}\n");
+ "default: {\n");
+
+ printer->Indent();
+ if (params_.store_unknown_fields()) {
+ printer->Print(
+ "if (unknownFieldData == null) {\n"
+ " unknownFieldData = \n"
+ " new java.util.ArrayList<com.google.protobuf.nano.UnknownFieldData>();\n"
+ "}\n"
+ "if (!com.google.protobuf.nano.WireFormatNano.storeUnknownField(unknownFieldData, \n"
+ " input, tag)) {\n"
+ " return this;\n"
+ "}\n");
+ } else {
+ printer->Print(
+ "if (!com.google.protobuf.nano.WireFormatNano.parseUnknownField(input, tag)) {\n"
+ " return this;\n" // it's an endgroup tag
+ "}\n");
+ }
+ printer->Print("break;\n");
+ printer->Outdent();
+ printer->Print("}\n");
for (int i = 0; i < descriptor_->field_count(); i++) {
const FieldDescriptor* field = sorted_fields[i];
@@ -356,6 +406,11 @@ void MessageGenerator::GenerateClear(io::Printer* printer) {
}
}
+ // Clear unknown fields.
+ if (params_.store_unknown_fields()) {
+ printer->Print("unknownFieldData = null;\n");
+ }
+
printer->Outdent();
printer->Print(
" cachedSize = -1;\n"
diff --git a/src/google/protobuf/compiler/javanano/javanano_params.h b/src/google/protobuf/compiler/javanano/javanano_params.h
index f6192ea..30eedff 100644
--- a/src/google/protobuf/compiler/javanano/javanano_params.h
+++ b/src/google/protobuf/compiler/javanano/javanano_params.h
@@ -49,6 +49,7 @@ class Params {
string empty_;
string base_name_;
bool java_multiple_files_;
+ bool store_unknown_fields_;
NameMap java_packages_;
NameMap java_outer_classnames_;
@@ -56,6 +57,7 @@ class Params {
Params(const string & base_name) :
empty_(""),
base_name_(base_name),
+ store_unknown_fields_(false),
java_multiple_files_(false) {
}
@@ -107,6 +109,13 @@ class Params {
return java_outer_classnames_;
}
+ void set_store_unknown_fields(bool value) {
+ store_unknown_fields_ = value;
+ }
+ bool store_unknown_fields() const {
+ return store_unknown_fields_;
+ }
+
void set_java_multiple_files(bool value) {
java_multiple_files_ = value;
}
diff --git a/src/google/protobuf/unittest_extension_nano.proto b/src/google/protobuf/unittest_extension_nano.proto
new file mode 100644
index 0000000..f2906f0
--- /dev/null
+++ b/src/google/protobuf/unittest_extension_nano.proto
@@ -0,0 +1,44 @@
+syntax = "proto2";
+
+option java_outer_classname = "Extensions";
+option java_package = "com.google.protobuf.nano";
+
+message ExtendableMessage {
+ optional int32 field = 1;
+ extensions 10 to max;
+}
+
+enum AnEnum {
+ FIRST_VALUE = 1;
+ SECOND_VALUE = 2;
+}
+
+message AnotherMessage {
+ optional string string = 1;
+ optional bool value = 2;
+}
+
+extend ExtendableMessage {
+ optional string some_string = 10;
+ optional int32 some_int = 11;
+ optional int64 some_long = 12;
+ optional float some_float = 13;
+ optional double some_double = 14;
+ optional bool some_bool = 15;
+ optional AnEnum some_enum = 16;
+ optional AnotherMessage some_message = 17;
+ repeated string some_repeated_string = 18;
+ repeated int32 some_repeated_int = 19;
+ repeated int64 some_repeated_long = 20;
+ repeated float some_repeated_float = 21;
+ repeated double some_repeated_double = 22;
+ repeated bool some_repeated_bool = 23;
+ repeated AnEnum some_repeated_enum = 24;
+ repeated AnotherMessage some_repeated_message = 25;
+}
+
+message ContainerMessage {
+ extend ExtendableMessage {
+ optional bool another_thing = 100;
+ }
+}