Commit 0e122ce8 authored by Jisi Liu's avatar Jisi Liu

Merge pull request #315 from brianduff/sync_from_aosp

Sync nanoproto from AOSP
parents 5a9863b5 9d546c85
......@@ -145,6 +145,7 @@ optional_field_style -> default or accessors
enum_style -> c or java
ignore_services -> true or false
parcelable_messages -> true or false
generate_intdefs -> true or false
```
**java_package=\<file-name\>|\<package-name\>** (no default)
......@@ -302,6 +303,28 @@ parcelable_messages -> true or false
Android-specific option to generate Parcelable messages.
**generate_intdefs={true,false}** (default: false)
Android-specific option to generate @IntDef annotations for enums.
If turned on, an '@IntDef' annotation (a public @interface) will be
generated for each enum, and every integer parameter and return
value in the generated code meant for this enum will be annotated
with it. This interface is generated with the same name and at the
same place as the enum members' container interfaces described
above under 'enum_style=java', regardless of the enum_style option
used. When this is combined with enum_style=java, the interface
will be both the '@IntDef' annotation and the container of the enum
members; otherwise the interface has an empty body.
Your app must declare a compile-time dependency on the
android-support-annotations library.
For more information on how these @IntDef annotations help with
compile-time type safety, see:
https://sites.google.com/a/android.com/tools/tech-docs/support-annotations
and
https://developer.android.com/reference/android/support/annotation/IntDef.html
To use nano protobufs within the Android repo:
----------------------------------------------
......
......@@ -97,19 +97,19 @@
<arg value="src/test/java/com/google/protobuf/nano/map_test.proto" />
</exec>
<exec executable="../src/protoc">
<arg value="--javanano_out=store_unknown_fields=true,generate_equals=true:target/generated-test-sources" />
<arg value="--javanano_out=store_unknown_fields=true,generate_equals=true,generate_clone=true:target/generated-test-sources" />
<arg value="--proto_path=src/test/java/com" />
<arg value="src/test/java/com/google/protobuf/nano/unittest_extension_nano.proto" />
<arg value="src/test/java/com/google/protobuf/nano/unittest_extension_singular_nano.proto" />
<arg value="src/test/java/com/google/protobuf/nano/unittest_extension_repeated_nano.proto" />
</exec>
<exec executable="../src/protoc">
<arg value="--javanano_out=store_unknown_fields=true:target/generated-test-sources" />
<arg value="--javanano_out=store_unknown_fields=true,generate_clone=true:target/generated-test-sources" />
<arg value="--proto_path=src/test/java/com" />
<arg value="src/test/java/com/google/protobuf/nano/unittest_extension_packed_nano.proto" />
</exec>
<exec executable="../src/protoc">
<arg value="--javanano_out=java_nano_generate_has=true,generate_equals=true:target/generated-test-sources" />
<arg value="--javanano_out=java_nano_generate_has=true,generate_equals=true,generate_clone=true:target/generated-test-sources" />
<arg value="--proto_path=src/test/java/com" />
<arg value="src/test/java/com/google/protobuf/nano/unittest_has_nano.proto" />
</exec>
......@@ -139,6 +139,15 @@
<arg value="--proto_path=src/test/java/com" />
<arg value="src/test/java/com/google/protobuf/nano/unittest_reference_types_nano.proto" />
</exec>
<exec executable="../src/protoc">
<arg value="--javanano_out=
optional_field_style=reftypes_compat_mode,
generate_equals=true,
java_outer_classname=google/protobuf/nano/unittest_reference_types_nano.proto|NanoReferenceTypesCompat
:target/generated-test-sources" />
<arg value="--proto_path=src/test/java/com" />
<arg value="src/test/java/com/google/protobuf/nano/unittest_reference_types_nano.proto" />
</exec>
</tasks>
<testSourceRoot>target/generated-test-sources</testSourceRoot>
</configuration>
......
......@@ -160,28 +160,10 @@ public abstract class ExtendableMessageNano<M extends ExtendableMessageNano<M>>
return true;
}
/**
* Returns whether the stored unknown field data in this message is equivalent to that in the
* other message.
*
* @param other the other message.
* @return whether the two sets of unknown field data are equal.
*/
protected final boolean unknownFieldDataEquals(M other) {
if (unknownFieldData == null || unknownFieldData.isEmpty()) {
return other.unknownFieldData == null || other.unknownFieldData.isEmpty();
} else {
return unknownFieldData.equals(other.unknownFieldData);
}
}
/**
* Computes the hashcode representing the unknown field data stored in this message.
*
* @return the hashcode for the unknown field data.
*/
protected final int unknownFieldDataHashCode() {
return (unknownFieldData == null || unknownFieldData.isEmpty()
? 0 : unknownFieldData.hashCode());
@Override
public M clone() throws CloneNotSupportedException {
M cloned = (M) super.clone();
InternalNano.cloneUnknownFieldData(this, cloned);
return cloned;
}
}
......@@ -79,12 +79,30 @@ public class Extension<M extends ExtendableMessageNano<M>, T> {
* Should be used by the generated code only.
*
* @param type {@link #TYPE_MESSAGE} or {@link #TYPE_GROUP}
* @deprecated use {@link #createMessageTyped(int, Class, long)} instead.
*/
@Deprecated
public static <M extends ExtendableMessageNano<M>, T extends MessageNano>
Extension<M, T> createMessageTyped(int type, Class<T> clazz, int tag) {
return new Extension<M, T>(type, clazz, tag, false);
}
// Note: these create...() methods take a long for the tag parameter,
// because tags are represented as unsigned ints, and these values exist
// in generated code as long values. However, they can fit in 32-bits, so
// it's safe to cast them to int without loss of precision.
/**
* Creates an {@code Extension} of the given message type and tag number.
* Should be used by the generated code only.
*
* @param type {@link #TYPE_MESSAGE} or {@link #TYPE_GROUP}
*/
public static <M extends ExtendableMessageNano<M>, T extends MessageNano>
Extension<M, T> createMessageTyped(int type, Class<T> clazz, long tag) {
return new Extension<M, T>(type, clazz, (int) tag, false);
}
/**
* Creates a repeated {@code Extension} of the given message type and tag number.
* Should be used by the generated code only.
......@@ -92,8 +110,8 @@ public class Extension<M extends ExtendableMessageNano<M>, T> {
* @param type {@link #TYPE_MESSAGE} or {@link #TYPE_GROUP}
*/
public static <M extends ExtendableMessageNano<M>, T extends MessageNano>
Extension<M, T[]> createRepeatedMessageTyped(int type, Class<T[]> clazz, int tag) {
return new Extension<M, T[]>(type, clazz, tag, true);
Extension<M, T[]> createRepeatedMessageTyped(int type, Class<T[]> clazz, long tag) {
return new Extension<M, T[]>(type, clazz, (int) tag, true);
}
/**
......@@ -104,8 +122,8 @@ public class Extension<M extends ExtendableMessageNano<M>, T> {
* @param clazz the boxed Java type of this extension
*/
public static <M extends ExtendableMessageNano<M>, T>
Extension<M, T> createPrimitiveTyped(int type, Class<T> clazz, int tag) {
return new PrimitiveExtension<M, T>(type, clazz, tag, false, 0, 0);
Extension<M, T> createPrimitiveTyped(int type, Class<T> clazz, long tag) {
return new PrimitiveExtension<M, T>(type, clazz, (int) tag, false, 0, 0);
}
/**
......@@ -117,8 +135,9 @@ public class Extension<M extends ExtendableMessageNano<M>, T> {
*/
public static <M extends ExtendableMessageNano<M>, T>
Extension<M, T> createRepeatedPrimitiveTyped(
int type, Class<T> clazz, int tag, int nonPackedTag, int packedTag) {
return new PrimitiveExtension<M, T>(type, clazz, tag, true, nonPackedTag, packedTag);
int type, Class<T> clazz, long tag, long nonPackedTag, long packedTag) {
return new PrimitiveExtension<M, T>(type, clazz, (int) tag, true,
(int) nonPackedTag, (int) packedTag);
}
/**
......@@ -136,7 +155,7 @@ public class Extension<M extends ExtendableMessageNano<M>, T> {
protected final Class<T> clazz;
/**
* Tag number of this extension.
* Tag number of this extension. The data should be viewed as an unsigned 32-bit value.
*/
public final int tag;
......
......@@ -35,9 +35,12 @@ package com.google.protobuf.nano;
* A custom version of {@link android.util.SparseArray} with the minimal API
* for storing {@link FieldData} objects.
*
* <p>This class is an internal implementation detail of nano and should not
* be called directly by clients.
*
* Based on {@link android.support.v4.util.SpareArrayCompat}.
*/
class FieldArray {
public final class FieldArray implements Cloneable {
private static final FieldData DELETED = new FieldData();
private boolean mGarbage = false;
......@@ -48,7 +51,7 @@ class FieldArray {
/**
* Creates a new FieldArray containing no fields.
*/
public FieldArray() {
FieldArray() {
this(10);
}
......@@ -57,7 +60,7 @@ class FieldArray {
* require any additional memory allocation to store the specified
* number of mappings.
*/
public FieldArray(int initialCapacity) {
FieldArray(int initialCapacity) {
initialCapacity = idealIntArraySize(initialCapacity);
mFieldNumbers = new int[initialCapacity];
mData = new FieldData[initialCapacity];
......@@ -68,7 +71,7 @@ class FieldArray {
* Gets the FieldData mapped from the specified fieldNumber, or <code>null</code>
* if no such mapping has been made.
*/
public FieldData get(int fieldNumber) {
FieldData get(int fieldNumber) {
int i = binarySearch(fieldNumber);
if (i < 0 || mData[i] == DELETED) {
......@@ -81,7 +84,7 @@ class FieldArray {
/**
* Removes the data from the specified fieldNumber, if there was any.
*/
public void remove(int fieldNumber) {
void remove(int fieldNumber) {
int i = binarySearch(fieldNumber);
if (i >= 0 && mData[i] != DELETED) {
......@@ -118,7 +121,7 @@ class FieldArray {
* Adds a mapping from the specified fieldNumber to the specified data,
* replacing the previous mapping if there was one.
*/
public void put(int fieldNumber, FieldData data) {
void put(int fieldNumber, FieldData data) {
int i = binarySearch(fieldNumber);
if (i >= 0) {
......@@ -167,7 +170,7 @@ class FieldArray {
* Returns the number of key-value mappings that this FieldArray
* currently stores.
*/
public int size() {
int size() {
if (mGarbage) {
gc();
}
......@@ -184,7 +187,7 @@ class FieldArray {
* the value from the <code>index</code>th key-value mapping that this
* FieldArray stores.
*/
public FieldData dataAt(int index) {
FieldData dataAt(int index) {
if (mGarbage) {
gc();
}
......@@ -270,4 +273,19 @@ class FieldArray {
}
return true;
}
@Override
public final FieldArray clone() {
// Trigger GC so we compact and don't copy DELETED elements.
int size = size();
FieldArray clone = new FieldArray(size);
System.arraycopy(mFieldNumbers, 0, clone.mFieldNumbers, 0, size);
for (int i = 0; i < size; i++) {
if (mData[i] != null) {
clone.mData[i] = mData[i].clone();
}
}
clone.mSize = size;
return clone;
}
}
......@@ -39,7 +39,7 @@ import java.util.List;
* Stores unknown fields. These might be extensions or fields that the generated API doesn't
* know about yet.
*/
class FieldData {
class FieldData implements Cloneable {
private Extension<?, ?> cachedExtension;
private Object value;
/** The serialised values for this object. Will be cleared if getValue is called */
......@@ -187,4 +187,54 @@ class FieldData {
return result;
}
@Override
public final FieldData clone() {
FieldData clone = new FieldData();
try {
clone.cachedExtension = cachedExtension;
if (unknownFieldData == null) {
clone.unknownFieldData = null;
} else {
clone.unknownFieldData.addAll(unknownFieldData);
}
// Whether we need to deep clone value depends on its type. Primitive reference types
// (e.g. Integer, Long etc.) are ok, since they're immutable. We need to clone arrays
// and messages.
if (value == null) {
// No cloning required.
} else if (value instanceof MessageNano) {
clone.value = ((MessageNano) value).clone();
} else if (value instanceof byte[]) {
clone.value = ((byte[]) value).clone();
} else if (value instanceof byte[][]) {
byte[][] valueArray = (byte[][]) value;
byte[][] cloneArray = new byte[valueArray.length][];
clone.value = cloneArray;
for (int i = 0; i < valueArray.length; i++) {
cloneArray[i] = valueArray[i].clone();
}
} else if (value instanceof boolean[]) {
clone.value = ((boolean[]) value).clone();
} else if (value instanceof int[]) {
clone.value = ((int[]) value).clone();
} else if (value instanceof long[]) {
clone.value = ((long[]) value).clone();
} else if (value instanceof float[]) {
clone.value = ((float[]) value).clone();
} else if (value instanceof double[]) {
clone.value = ((double[]) value).clone();
} else if (value instanceof MessageNano[]) {
MessageNano[] valueArray = (MessageNano[]) value;
MessageNano[] cloneArray = new MessageNano[valueArray.length];
clone.value = cloneArray;
for (int i = 0; i < valueArray.length; i++) {
cloneArray[i] = valueArray[i].clone();
}
}
return clone;
} catch (CloneNotSupportedException e) {
throw new AssertionError(e);
}
}
}
......@@ -536,4 +536,12 @@ public final class InternalNano {
}
return o.hashCode();
}
// This avoids having to make FieldArray public.
public static void cloneUnknownFieldData(ExtendableMessageNano original,
ExtendableMessageNano cloned) {
if (original.unknownFieldData != null) {
cloned.unknownFieldData = (FieldArray) original.unknownFieldData.clone();
}
}
}
......@@ -187,4 +187,12 @@ public abstract class MessageNano {
public String toString() {
return MessageNanoPrinter.print(this);
}
/**
* Provides support for cloning. This only works if you specify the generate_clone method.
*/
@Override
public MessageNano clone() throws CloneNotSupportedException {
return (MessageNano) super.clone();
}
}
......@@ -109,6 +109,10 @@ public final class MessageNanoPrinter {
for (Field field : clazz.getFields()) {
int modifiers = field.getModifiers();
String fieldName = field.getName();
if ("cachedSize".equals(fieldName)) {
// TODO(bduff): perhaps cachedSize should have a more obscure name.
continue;
}
if ((modifiers & Modifier.PUBLIC) == Modifier.PUBLIC
&& (modifiers & Modifier.STATIC) != Modifier.STATIC
......
......@@ -42,6 +42,10 @@ import java.util.Arrays;
final class UnknownFieldData {
final int tag;
/**
* Important: this should be treated as immutable, even though it's possible
* to change the array values.
*/
final byte[] bytes;
UnknownFieldData(int tag, byte[] bytes) {
......
......@@ -31,11 +31,13 @@
package com.google.protobuf.nano;
import com.google.protobuf.nano.MapTestProto.TestMap;
import com.google.protobuf.nano.CodedOutputByteBufferNano;
import com.google.protobuf.nano.MapTestProto.TestMap.MessageValue;
import com.google.protobuf.nano.NanoAccessorsOuterClass.TestNanoAccessors;
import com.google.protobuf.nano.NanoHasOuterClass.TestAllTypesNanoHas;
import com.google.protobuf.nano.NanoOuterClass.TestAllTypesNano;
import com.google.protobuf.nano.UnittestRecursiveNano.RecursiveMessageNano;
import com.google.protobuf.nano.NanoReferenceTypesCompat;
import com.google.protobuf.nano.UnittestSimpleNano.SimpleMessageNano;
import com.google.protobuf.nano.UnittestSingleNano.SingleMessageNano;
import com.google.protobuf.nano.testext.Extensions;
......@@ -2300,6 +2302,59 @@ public class NanoTest extends TestCase {
}
}
public void testDifferentStringLengthsNano() throws Exception {
// Test string serialization roundtrip using strings of the following lengths,
// with ASCII and Unicode characters requiring different UTF-8 byte counts per
// char, hence causing the length delimiter varint to sometimes require more
// bytes for the Unicode strings than the ASCII string of the same length.
int[] lengths = new int[] {
0,
1,
(1 << 4) - 1, // 1 byte for ASCII and Unicode
(1 << 7) - 1, // 1 byte for ASCII, 2 bytes for Unicode
(1 << 11) - 1, // 2 bytes for ASCII and Unicode
(1 << 14) - 1, // 2 bytes for ASCII, 3 bytes for Unicode
(1 << 17) - 1, // 3 bytes for ASCII and Unicode
};
for (int i : lengths) {
testEncodingOfString('q', i); // 1 byte per char
testEncodingOfString('\u07FF', i); // 2 bytes per char
testEncodingOfString('\u0981', i); // 3 bytes per char
}
}
/** Regression test for https://github.com/google/protobuf/issues/292 */
public void testCorrectExceptionThrowWhenEncodingStringsWithoutEnoughSpace() throws Exception {
String testCase = "Foooooooo";
assertEquals(CodedOutputByteBufferNano.computeRawVarint32Size(testCase.length()),
CodedOutputByteBufferNano.computeRawVarint32Size(testCase.length() * 3));
assertEquals(11, CodedOutputByteBufferNano.computeStringSize(1, testCase));
// Tag is one byte, varint describing string length is 1 byte, string length is 9 bytes.
// An array of size 1 will cause a failure when trying to write the varint.
for (int i = 0; i < 11; i++) {
CodedOutputByteBufferNano bufferNano = CodedOutputByteBufferNano.newInstance(new byte[i]);
try {
bufferNano.writeString(1, testCase);
fail("Should have thrown an out of space exception");
} catch (CodedOutputByteBufferNano.OutOfSpaceException expected) {}
}
}
private void testEncodingOfString(char c, int length) throws InvalidProtocolBufferNanoException {
TestAllTypesNano testAllTypesNano = new TestAllTypesNano();
final String fullString = fullString(c, length);
testAllTypesNano.optionalString = fullString;
final TestAllTypesNano resultNano = new TestAllTypesNano();
MessageNano.mergeFrom(resultNano, MessageNano.toByteArray(testAllTypesNano));
assertEquals(fullString, resultNano.optionalString);
}
private String fullString(char c, int length) {
char[] result = new char[length];
Arrays.fill(result, c);
return new String(result);
}
public void testNanoWithHasParseFrom() throws Exception {
TestAllTypesNanoHas msg = null;
// Test false on creation, after clear and upon empty parse.
......@@ -2986,6 +3041,10 @@ public class NanoTest extends TestCase {
assertTrue(Arrays.equals(floats, message.getExtension(RepeatedExtensions.repeatedFloat)));
assertTrue(Arrays.equals(doubles, message.getExtension(RepeatedExtensions.repeatedDouble)));
assertTrue(Arrays.equals(enums, message.getExtension(RepeatedExtensions.repeatedEnum)));
// Clone the message and ensure it's still equal.
Extensions.ExtendableMessage clone = message.clone();
assertEquals(clone, message);
}
public void testNullExtensions() throws Exception {
......@@ -4345,6 +4404,11 @@ public class NanoTest extends TestCase {
assertMapSet(testMap.sfixed64ToSfixed64Field, int64Values, int64Values);
}
public void testRepeatedFieldInitializedInReftypesCompatMode() {
NanoReferenceTypesCompat.TestAllTypesNano proto = new NanoReferenceTypesCompat.TestAllTypesNano();
assertNotNull(proto.repeatedString);
}
private void assertRepeatedPackablesEqual(
NanoRepeatedPackables.NonPacked nonPacked, NanoRepeatedPackables.Packed packed) {
// Not using MessageNano.equals() -- that belongs to a separate test.
......@@ -4364,6 +4428,22 @@ public class NanoTest extends TestCase {
assertTrue(Arrays.equals(nonPacked.enums, packed.enums));
}
public void testClone() throws Exception {
// A simple message.
AnotherMessage anotherMessage = new AnotherMessage();
anotherMessage.string = "Hello";
anotherMessage.value = true;
anotherMessage.integers = new int[] { 1, 2, 3 };
AnotherMessage clone = anotherMessage.clone();
assertEquals(clone, anotherMessage);
// Verify it was a deep clone - changes to the clone shouldn't affect the
// original.
clone.integers[1] = 100;
assertFalse(clone.equals(anotherMessage));
}
private void assertHasWireData(MessageNano message, boolean expected) {
byte[] bytes = MessageNano.toByteArray(message);
int wireLength = bytes.length;
......
......@@ -16,11 +16,15 @@ enum AnEnum {
message AnotherMessage {
optional string string = 1;
optional bool value = 2;
repeated int32 integers = 3;
}
message ContainerMessage {
extend ExtendableMessage {
optional bool another_thing = 100;
// The largest permitted field number, per
// https://developers.google.com/protocol-buffers/docs/proto#simple
optional bool large_field_number = 536870911;
}
}
......
......@@ -73,13 +73,45 @@ void EnumGenerator::Generate(io::Printer* printer) {
"// enum $classname$\n",
"classname", descriptor_->name());
const string classname = RenameJavaKeywords(descriptor_->name());
// Start of container interface
// If generating intdefs, we use the container interface as the intdef if
// present. Otherwise, we just make an empty @interface parallel to the
// constants.
bool use_intdef = params_.generate_intdefs();
bool use_shell_class = params_.java_enum_style();
if (use_shell_class) {
printer->Print(
"public interface $classname$ {\n",
"classname", RenameJavaKeywords(descriptor_->name()));
if (use_intdef) {
// @IntDef annotation so tools can enforce correctness
// Annotations will be discarded by the compiler
printer->Print("@java.lang.annotation.Retention("
"java.lang.annotation.RetentionPolicy.SOURCE)\n"
"@android.support.annotation.IntDef({\n");
printer->Indent();
for (int i = 0; i < canonical_values_.size(); i++) {
const string constant_name =
RenameJavaKeywords(canonical_values_[i]->name());
if (use_shell_class) {
printer->Print("$classname$.$name$,\n",
"classname", classname,
"name", constant_name);
} else {
printer->Print("$name$,\n", "name", constant_name);
}
}
printer->Outdent();
printer->Print("})\n");
}
if (use_shell_class || use_intdef) {
printer->Print(
"public $at_for_intdef$interface $classname$ {\n",
"classname", classname,
"at_for_intdef", use_intdef ? "@" : "");
if (use_shell_class) {
printer->Indent();
} else {
printer->Print("}\n\n");
}
}
// Canonical values
......
......@@ -76,6 +76,10 @@ void SetEnumVariables(const Params& params,
internal::WireFormatLite::MakeTag(descriptor->number(),
internal::WireFormat::WireTypeForFieldType(descriptor->type())));
(*variables)["message_name"] = descriptor->containing_type()->name();
const EnumDescriptor* enum_type = descriptor->enum_type();
(*variables)["message_type_intdef"] = "@"
+ ToJavaName(params, enum_type->name(), true,
enum_type->containing_type(), enum_type->file());
}
void LoadEnumValues(const Params& params,
......@@ -116,8 +120,10 @@ EnumFieldGenerator::~EnumFieldGenerator() {}
void EnumFieldGenerator::
GenerateMembers(io::Printer* printer, bool /* unused lazy_init */) const {
printer->Print(variables_,
"public $type$ $name$;\n");
if (params_.generate_intdefs()) {
printer->Print(variables_, "$message_type_intdef$\n");
}
printer->Print(variables_, "public $type$ $name$;\n");
if (params_.generate_has()) {
printer->Print(variables_,
......@@ -256,12 +262,22 @@ AccessorEnumFieldGenerator::~AccessorEnumFieldGenerator() {}
void AccessorEnumFieldGenerator::
GenerateMembers(io::Printer* printer, bool /* unused lazy_init */) const {
printer->Print(variables_, "private int $name$_;\n");
if (params_.generate_intdefs()) {
printer->Print(variables_, "$message_type_intdef$\n");
}
printer->Print(variables_,
"private int $name$_;\n"
"public int get$capitalized_name$() {\n"
" return $name$_;\n"
"}\n"
"public $message_name$ set$capitalized_name$(int value) {\n"
"public $message_name$ set$capitalized_name$(");
if (params_.generate_intdefs()) {
printer->Print(variables_,
"\n"
" $message_type_intdef$ ");
}
printer->Print(variables_,
"int value) {\n"
" $name$_ = value;\n"
" $set_has$;\n"
" return this;\n"
......@@ -498,6 +514,14 @@ GenerateSerializedSizeCode(io::Printer* printer) const {
"}\n");
}
void RepeatedEnumFieldGenerator::
GenerateFixClonedCode(io::Printer* printer) const {
printer->Print(variables_,
"if (this.$name$ != null && this.$name$.length > 0) {\n"
" cloned.$name$ = this.$name$.clone();\n"
"}\n");
}
void RepeatedEnumFieldGenerator::
GenerateEqualsCode(io::Printer* printer) const {
printer->Print(variables_,
......
......@@ -106,6 +106,7 @@ class RepeatedEnumFieldGenerator : public FieldGenerator {
void GenerateSerializedSizeCode(io::Printer* printer) const;
void GenerateEqualsCode(io::Printer* printer) const;
void GenerateHashCodeCode(io::Printer* printer) const;
void GenerateFixClonedCode(io::Printer* printer) const;
private:
void GenerateRepeatedDataSizeCode(io::Printer* printer) const;
......
......@@ -140,7 +140,7 @@ void ExtensionGenerator::Generate(io::Printer* printer) const {
" com.google.protobuf.nano.Extension.create$repeated$$ext_type$(\n"
" com.google.protobuf.nano.Extension.$type$,\n"
" $class$.class,\n"
" $tag_params$);\n");
" $tag_params$L);\n");
}
} // namespace javanano
......
......@@ -83,6 +83,7 @@ class FieldGenerator {
virtual void GenerateSerializedSizeCode(io::Printer* printer) const = 0;
virtual void GenerateEqualsCode(io::Printer* printer) const = 0;
virtual void GenerateHashCodeCode(io::Printer* printer) const = 0;
virtual void GenerateFixClonedCode(io::Printer* printer) const {}
protected:
const Params& params_;
......
......@@ -152,6 +152,12 @@ bool JavaNanoGenerator::Generate(const FileDescriptor* file,
params.set_ignore_services(option_value == "true");
} else if (option_name == "parcelable_messages") {
params.set_parcelable_messages(option_value == "true");
} else if (option_name == "generate_clone") {
params.set_generate_clone(option_value == "true");
} else if (option_name == "generate_intdefs") {
params.set_generate_intdefs(option_value == "true");
} else if (option_name == "generate_clear") {
params.set_generate_clear(option_value == "true");
} else {
*error = "Ignore unknown javanano generator option: " + option_name;
}
......
......@@ -136,21 +136,37 @@ void MessageGenerator::Generate(io::Printer* printer) {
}
if (params_.store_unknown_fields() && params_.parcelable_messages()) {
printer->Print(
" com.google.protobuf.nano.android.ParcelableExtendableMessageNano<$classname$> {\n",
" com.google.protobuf.nano.android.ParcelableExtendableMessageNano<$classname$>",
"classname", descriptor_->name());
} else if (params_.store_unknown_fields()) {
printer->Print(
" com.google.protobuf.nano.ExtendableMessageNano<$classname$> {\n",
" com.google.protobuf.nano.ExtendableMessageNano<$classname$>",
"classname", descriptor_->name());
} else if (params_.parcelable_messages()) {
printer->Print(
" com.google.protobuf.nano.android.ParcelableMessageNano {\n");
" com.google.protobuf.nano.android.ParcelableMessageNano");
} else {
printer->Print(
" com.google.protobuf.nano.MessageNano {\n");
" com.google.protobuf.nano.MessageNano");
}
if (params_.generate_clone()) {
printer->Print(" implements java.lang.Cloneable {\n");
} else {
printer->Print(" {\n");
}
printer->Indent();
if (params_.parcelable_messages()) {
printer->Print(
"\n"
"// Used by Parcelable\n"
"@SuppressWarnings({\"unused\"})\n"
"public static final android.os.Parcelable.Creator<$classname$> CREATOR =\n"
" new com.google.protobuf.nano.android.ParcelableMessageNanoCreator<\n"
" $classname$>($classname$.class);\n",
"classname", descriptor_->name());
}
// Nested types and extensions
for (int i = 0; i < descriptor_->extension_count(); i++) {
ExtensionGenerator(descriptor_->extension(i), params_).Generate(printer);
......@@ -288,20 +304,28 @@ void MessageGenerator::Generate(io::Printer* printer) {
}
printer->Print("}\n");
} else {
printer->Print(
"\n"
"public $classname$() {\n",
"classname", descriptor_->name());
if (params_.generate_clear()) {
printer->Print(
"\n"
"public $classname$() {\n"
" clear();\n"
"}\n",
"classname", descriptor_->name());
printer->Print(" clear();\n");
} else {
printer->Indent();
GenerateFieldInitializers(printer);
printer->Outdent();
}
printer->Print("}\n");
}
// Other methods in this class
GenerateClear(printer);
if (params_.generate_clone()) {
GenerateClone(printer);
}
if (params_.generate_equals()) {
GenerateEquals(printer);
GenerateHashCode(printer);
......@@ -495,6 +519,15 @@ void MessageGenerator::GenerateClear(io::Printer* printer) {
"classname", descriptor_->name());
printer->Indent();
GenerateFieldInitializers(printer);
printer->Outdent();
printer->Print(
" return this;\n"
"}\n");
}
void MessageGenerator::GenerateFieldInitializers(io::Printer* printer) {
// Clear bit fields.
int totalInts = (field_generators_.total_bits() + 31) / 32;
for (int i = 0; i < totalInts; i++) {
......@@ -520,12 +553,34 @@ void MessageGenerator::GenerateClear(io::Printer* printer) {
if (params_.store_unknown_fields()) {
printer->Print("unknownFieldData = null;\n");
}
printer->Print("cachedSize = -1;\n");
}
void MessageGenerator::GenerateClone(io::Printer* printer) {
printer->Print(
"@Override\n"
"public $classname$ clone() {\n",
"classname", descriptor_->name());
printer->Indent();
printer->Print(
"$classname$ cloned;\n"
"try {\n"
" cloned = ($classname$) super.clone();\n"
"} catch (java.lang.CloneNotSupportedException e) {\n"
" throw new java.lang.AssertionError(e);\n"
"}\n",
"classname", descriptor_->name());
for (int i = 0; i < descriptor_->field_count(); i++) {
field_generators_.get(descriptor_->field(i)).GenerateFixClonedCode(printer);
}
printer->Outdent();
printer->Print(
" cachedSize = -1;\n"
" return this;\n"
"}\n");
" return cloned;\n"
"}\n"
"\n");
}
void MessageGenerator::GenerateEquals(io::Printer* printer) {
......@@ -568,7 +623,11 @@ void MessageGenerator::GenerateEquals(io::Printer* printer) {
if (params_.store_unknown_fields()) {
printer->Print(
"return unknownFieldDataEquals(other);\n");
"if (unknownFieldData == null || unknownFieldData.isEmpty()) {\n"
" return other.unknownFieldData == null || other.unknownFieldData.isEmpty();\n"
"} else {\n"
" return unknownFieldData.equals(other.unknownFieldData);\n"
"}");
} else {
printer->Print(
"return true;\n");
......@@ -598,7 +657,9 @@ void MessageGenerator::GenerateHashCode(io::Printer* printer) {
if (params_.store_unknown_fields()) {
printer->Print(
"result = 31 * result + unknownFieldDataHashCode();\n");
"result = 31 * result + \n"
" (unknownFieldData == null || unknownFieldData.isEmpty() ? 0 : \n"
" unknownFieldData.hashCode());\n");
}
printer->Print("return result;\n");
......
......@@ -77,8 +77,10 @@ class MessageGenerator {
const FieldDescriptor* field);
void GenerateClear(io::Printer* printer);
void GenerateFieldInitializers(io::Printer* printer);
void GenerateEquals(io::Printer* printer);
void GenerateHashCode(io::Printer* printer);
void GenerateClone(io::Printer* printer);
const Params& params_;
const Descriptor* descriptor_;
......
......@@ -126,6 +126,14 @@ GenerateSerializedSizeCode(io::Printer* printer) const {
"}\n");
}
void MessageFieldGenerator::
GenerateFixClonedCode(io::Printer* printer) const {
printer->Print(variables_,
"if (this.$name$ != null) {\n"
" cloned.$name$ = this.$name$.clone();\n"
"}\n");
}
void MessageFieldGenerator::
GenerateEqualsCode(io::Printer* printer) const {
printer->Print(variables_,
......@@ -212,6 +220,14 @@ GenerateSerializedSizeCode(io::Printer* printer) const {
"}\n");
}
void MessageOneofFieldGenerator::
GenerateFixClonedCode(io::Printer* printer) const {
printer->Print(variables_,
"if (this.$oneof_name$ != null) {\n"
" cloned.$oneof_name$ = this.$oneof_name$.clone();\n"
"}\n");
}
void MessageOneofFieldGenerator::
GenerateEqualsCode(io::Printer* printer) const {
GenerateOneofFieldEquals(descriptor_, variables_, printer);
......@@ -312,6 +328,19 @@ GenerateSerializedSizeCode(io::Printer* printer) const {
"}\n");
}
void RepeatedMessageFieldGenerator::
GenerateFixClonedCode(io::Printer* printer) const {
printer->Print(variables_,
"if (this.$name$ != null && this.$name$.length > 0) {\n"
" cloned.$name$ = new $type$[this.$name$.length];\n"
" for (int i = 0; i < this.$name$.length; i++) {\n"
" if (this.$name$[i] != null) {\n"
" cloned.$name$[i] = this.$name$[i].clone();\n"
" }\n"
" }\n"
"}\n");
}
void RepeatedMessageFieldGenerator::
GenerateEqualsCode(io::Printer* printer) const {
printer->Print(variables_,
......
......@@ -58,6 +58,7 @@ class MessageFieldGenerator : public FieldGenerator {
void GenerateSerializedSizeCode(io::Printer* printer) const;
void GenerateEqualsCode(io::Printer* printer) const;
void GenerateHashCodeCode(io::Printer* printer) const;
void GenerateFixClonedCode(io::Printer* printer) const;
private:
const FieldDescriptor* descriptor_;
......@@ -80,6 +81,7 @@ class MessageOneofFieldGenerator : public FieldGenerator {
void GenerateSerializedSizeCode(io::Printer* printer) const;
void GenerateEqualsCode(io::Printer* printer) const;
void GenerateHashCodeCode(io::Printer* printer) const;
void GenerateFixClonedCode(io::Printer* printer) const;
private:
const FieldDescriptor* descriptor_;
......@@ -102,6 +104,7 @@ class RepeatedMessageFieldGenerator : public FieldGenerator {
void GenerateSerializedSizeCode(io::Printer* printer) const;
void GenerateEqualsCode(io::Printer* printer) const;
void GenerateHashCodeCode(io::Printer* printer) const;
void GenerateFixClonedCode(io::Printer* printer) const;
private:
const FieldDescriptor* descriptor_;
......
......@@ -66,6 +66,8 @@ class Params {
bool parcelable_messages_;
bool reftypes_primitive_enums_;
bool generate_clear_;
bool generate_clone_;
bool generate_intdefs_;
public:
Params(const string & base_name) :
......@@ -81,7 +83,9 @@ class Params {
ignore_services_(false),
parcelable_messages_(false),
reftypes_primitive_enums_(false),
generate_clear_(true) {
generate_clear_(true),
generate_clone_(false),
generate_intdefs_(false) {
}
const string& base_name() const {
......@@ -231,6 +235,20 @@ class Params {
bool generate_clear() const {
return generate_clear_;
}
void set_generate_clone(bool value) {
generate_clone_ = value;
}
bool generate_clone() const {
return generate_clone_;
}
void set_generate_intdefs(bool value) {
generate_intdefs_ = value;
}
bool generate_intdefs() const {
return generate_intdefs_;
}
};
} // namespace javanano
......
......@@ -364,6 +364,14 @@ GenerateSerializedSizeCode(io::Printer* printer) const {
}
}
void RepeatedPrimitiveFieldGenerator::
GenerateFixClonedCode(io::Printer* printer) const {
printer->Print(variables_,
"if (this.$name$ != null && this.$name$.length > 0) {\n"
" cloned.$name$ = this.$name$.clone();\n"
"}\n");
}
void PrimitiveFieldGenerator::
GenerateEqualsCode(io::Printer* printer) const {
// We define equality as serialized form equality. If generate_has(),
......
......@@ -131,6 +131,7 @@ class RepeatedPrimitiveFieldGenerator : public FieldGenerator {
void GenerateSerializedSizeCode(io::Printer* printer) const;
void GenerateEqualsCode(io::Printer* printer) const;
void GenerateHashCodeCode(io::Printer* printer) const;
void GenerateFixClonedCode(io::Printer* printer) const;
private:
void GenerateRepeatedDataSizeCode(io::Printer* printer) const;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment