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 ...@@ -145,6 +145,7 @@ optional_field_style -> default or accessors
enum_style -> c or java enum_style -> c or java
ignore_services -> true or false ignore_services -> true or false
parcelable_messages -> true or false parcelable_messages -> true or false
generate_intdefs -> true or false
``` ```
**java_package=\<file-name\>|\<package-name\>** (no default) **java_package=\<file-name\>|\<package-name\>** (no default)
...@@ -302,6 +303,28 @@ parcelable_messages -> true or false ...@@ -302,6 +303,28 @@ parcelable_messages -> true or false
Android-specific option to generate Parcelable messages. 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: To use nano protobufs within the Android repo:
---------------------------------------------- ----------------------------------------------
......
...@@ -97,19 +97,19 @@ ...@@ -97,19 +97,19 @@
<arg value="src/test/java/com/google/protobuf/nano/map_test.proto" /> <arg value="src/test/java/com/google/protobuf/nano/map_test.proto" />
</exec> </exec>
<exec executable="../src/protoc"> <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="--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_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_singular_nano.proto" />
<arg value="src/test/java/com/google/protobuf/nano/unittest_extension_repeated_nano.proto" /> <arg value="src/test/java/com/google/protobuf/nano/unittest_extension_repeated_nano.proto" />
</exec> </exec>
<exec executable="../src/protoc"> <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="--proto_path=src/test/java/com" />
<arg value="src/test/java/com/google/protobuf/nano/unittest_extension_packed_nano.proto" /> <arg value="src/test/java/com/google/protobuf/nano/unittest_extension_packed_nano.proto" />
</exec> </exec>
<exec executable="../src/protoc"> <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="--proto_path=src/test/java/com" />
<arg value="src/test/java/com/google/protobuf/nano/unittest_has_nano.proto" /> <arg value="src/test/java/com/google/protobuf/nano/unittest_has_nano.proto" />
</exec> </exec>
...@@ -139,6 +139,15 @@ ...@@ -139,6 +139,15 @@
<arg value="--proto_path=src/test/java/com" /> <arg value="--proto_path=src/test/java/com" />
<arg value="src/test/java/com/google/protobuf/nano/unittest_reference_types_nano.proto" /> <arg value="src/test/java/com/google/protobuf/nano/unittest_reference_types_nano.proto" />
</exec> </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> </tasks>
<testSourceRoot>target/generated-test-sources</testSourceRoot> <testSourceRoot>target/generated-test-sources</testSourceRoot>
</configuration> </configuration>
......
...@@ -160,28 +160,10 @@ public abstract class ExtendableMessageNano<M extends ExtendableMessageNano<M>> ...@@ -160,28 +160,10 @@ public abstract class ExtendableMessageNano<M extends ExtendableMessageNano<M>>
return true; return true;
} }
/** @Override
* Returns whether the stored unknown field data in this message is equivalent to that in the public M clone() throws CloneNotSupportedException {
* other message. M cloned = (M) super.clone();
* InternalNano.cloneUnknownFieldData(this, cloned);
* @param other the other message. return cloned;
* @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());
} }
} }
...@@ -79,12 +79,30 @@ public class Extension<M extends ExtendableMessageNano<M>, T> { ...@@ -79,12 +79,30 @@ public class Extension<M extends ExtendableMessageNano<M>, T> {
* Should be used by the generated code only. * Should be used by the generated code only.
* *
* @param type {@link #TYPE_MESSAGE} or {@link #TYPE_GROUP} * @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> public static <M extends ExtendableMessageNano<M>, T extends MessageNano>
Extension<M, T> createMessageTyped(int type, Class<T> clazz, int tag) { Extension<M, T> createMessageTyped(int type, Class<T> clazz, int tag) {
return new Extension<M, T>(type, clazz, tag, false); 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. * Creates a repeated {@code Extension} of the given message type and tag number.
* Should be used by the generated code only. * Should be used by the generated code only.
...@@ -92,8 +110,8 @@ public class Extension<M extends ExtendableMessageNano<M>, T> { ...@@ -92,8 +110,8 @@ public class Extension<M extends ExtendableMessageNano<M>, T> {
* @param type {@link #TYPE_MESSAGE} or {@link #TYPE_GROUP} * @param type {@link #TYPE_MESSAGE} or {@link #TYPE_GROUP}
*/ */
public static <M extends ExtendableMessageNano<M>, T extends MessageNano> public static <M extends ExtendableMessageNano<M>, T extends MessageNano>
Extension<M, T[]> createRepeatedMessageTyped(int type, Class<T[]> clazz, int tag) { Extension<M, T[]> createRepeatedMessageTyped(int type, Class<T[]> clazz, long tag) {
return new Extension<M, T[]>(type, clazz, tag, true); return new Extension<M, T[]>(type, clazz, (int) tag, true);
} }
/** /**
...@@ -104,8 +122,8 @@ public class Extension<M extends ExtendableMessageNano<M>, T> { ...@@ -104,8 +122,8 @@ public class Extension<M extends ExtendableMessageNano<M>, T> {
* @param clazz the boxed Java type of this extension * @param clazz the boxed Java type of this extension
*/ */
public static <M extends ExtendableMessageNano<M>, T> public static <M extends ExtendableMessageNano<M>, T>
Extension<M, T> createPrimitiveTyped(int type, Class<T> clazz, int tag) { Extension<M, T> createPrimitiveTyped(int type, Class<T> clazz, long tag) {
return new PrimitiveExtension<M, T>(type, clazz, tag, false, 0, 0); return new PrimitiveExtension<M, T>(type, clazz, (int) tag, false, 0, 0);
} }
/** /**
...@@ -117,8 +135,9 @@ public class Extension<M extends ExtendableMessageNano<M>, T> { ...@@ -117,8 +135,9 @@ public class Extension<M extends ExtendableMessageNano<M>, T> {
*/ */
public static <M extends ExtendableMessageNano<M>, T> public static <M extends ExtendableMessageNano<M>, T>
Extension<M, T> createRepeatedPrimitiveTyped( Extension<M, T> createRepeatedPrimitiveTyped(
int type, Class<T> clazz, int tag, int nonPackedTag, int packedTag) { int type, Class<T> clazz, long tag, long nonPackedTag, long packedTag) {
return new PrimitiveExtension<M, T>(type, clazz, tag, true, nonPackedTag, 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> { ...@@ -136,7 +155,7 @@ public class Extension<M extends ExtendableMessageNano<M>, T> {
protected final Class<T> clazz; 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; public final int tag;
......
...@@ -35,9 +35,12 @@ package com.google.protobuf.nano; ...@@ -35,9 +35,12 @@ package com.google.protobuf.nano;
* A custom version of {@link android.util.SparseArray} with the minimal API * A custom version of {@link android.util.SparseArray} with the minimal API
* for storing {@link FieldData} objects. * 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}. * Based on {@link android.support.v4.util.SpareArrayCompat}.
*/ */
class FieldArray { public final class FieldArray implements Cloneable {
private static final FieldData DELETED = new FieldData(); private static final FieldData DELETED = new FieldData();
private boolean mGarbage = false; private boolean mGarbage = false;
...@@ -48,7 +51,7 @@ class FieldArray { ...@@ -48,7 +51,7 @@ class FieldArray {
/** /**
* Creates a new FieldArray containing no fields. * Creates a new FieldArray containing no fields.
*/ */
public FieldArray() { FieldArray() {
this(10); this(10);
} }
...@@ -57,7 +60,7 @@ class FieldArray { ...@@ -57,7 +60,7 @@ class FieldArray {
* require any additional memory allocation to store the specified * require any additional memory allocation to store the specified
* number of mappings. * number of mappings.
*/ */
public FieldArray(int initialCapacity) { FieldArray(int initialCapacity) {
initialCapacity = idealIntArraySize(initialCapacity); initialCapacity = idealIntArraySize(initialCapacity);
mFieldNumbers = new int[initialCapacity]; mFieldNumbers = new int[initialCapacity];
mData = new FieldData[initialCapacity]; mData = new FieldData[initialCapacity];
...@@ -68,7 +71,7 @@ class FieldArray { ...@@ -68,7 +71,7 @@ class FieldArray {
* Gets the FieldData mapped from the specified fieldNumber, or <code>null</code> * Gets the FieldData mapped from the specified fieldNumber, or <code>null</code>
* if no such mapping has been made. * if no such mapping has been made.
*/ */
public FieldData get(int fieldNumber) { FieldData get(int fieldNumber) {
int i = binarySearch(fieldNumber); int i = binarySearch(fieldNumber);
if (i < 0 || mData[i] == DELETED) { if (i < 0 || mData[i] == DELETED) {
...@@ -81,7 +84,7 @@ class FieldArray { ...@@ -81,7 +84,7 @@ class FieldArray {
/** /**
* Removes the data from the specified fieldNumber, if there was any. * Removes the data from the specified fieldNumber, if there was any.
*/ */
public void remove(int fieldNumber) { void remove(int fieldNumber) {
int i = binarySearch(fieldNumber); int i = binarySearch(fieldNumber);
if (i >= 0 && mData[i] != DELETED) { if (i >= 0 && mData[i] != DELETED) {
...@@ -118,7 +121,7 @@ class FieldArray { ...@@ -118,7 +121,7 @@ class FieldArray {
* Adds a mapping from the specified fieldNumber to the specified data, * Adds a mapping from the specified fieldNumber to the specified data,
* replacing the previous mapping if there was one. * 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); int i = binarySearch(fieldNumber);
if (i >= 0) { if (i >= 0) {
...@@ -167,7 +170,7 @@ class FieldArray { ...@@ -167,7 +170,7 @@ class FieldArray {
* Returns the number of key-value mappings that this FieldArray * Returns the number of key-value mappings that this FieldArray
* currently stores. * currently stores.
*/ */
public int size() { int size() {
if (mGarbage) { if (mGarbage) {
gc(); gc();
} }
...@@ -184,7 +187,7 @@ class FieldArray { ...@@ -184,7 +187,7 @@ class FieldArray {
* the value from the <code>index</code>th key-value mapping that this * the value from the <code>index</code>th key-value mapping that this
* FieldArray stores. * FieldArray stores.
*/ */
public FieldData dataAt(int index) { FieldData dataAt(int index) {
if (mGarbage) { if (mGarbage) {
gc(); gc();
} }
...@@ -270,4 +273,19 @@ class FieldArray { ...@@ -270,4 +273,19 @@ class FieldArray {
} }
return true; 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; ...@@ -39,7 +39,7 @@ import java.util.List;
* Stores unknown fields. These might be extensions or fields that the generated API doesn't * Stores unknown fields. These might be extensions or fields that the generated API doesn't
* know about yet. * know about yet.
*/ */
class FieldData { class FieldData implements Cloneable {
private Extension<?, ?> cachedExtension; private Extension<?, ?> cachedExtension;
private Object value; private Object value;
/** The serialised values for this object. Will be cleared if getValue is called */ /** The serialised values for this object. Will be cleared if getValue is called */
...@@ -187,4 +187,54 @@ class FieldData { ...@@ -187,4 +187,54 @@ class FieldData {
return result; 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 { ...@@ -536,4 +536,12 @@ public final class InternalNano {
} }
return o.hashCode(); 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 { ...@@ -187,4 +187,12 @@ public abstract class MessageNano {
public String toString() { public String toString() {
return MessageNanoPrinter.print(this); 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 { ...@@ -109,6 +109,10 @@ public final class MessageNanoPrinter {
for (Field field : clazz.getFields()) { for (Field field : clazz.getFields()) {
int modifiers = field.getModifiers(); int modifiers = field.getModifiers();
String fieldName = field.getName(); 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 if ((modifiers & Modifier.PUBLIC) == Modifier.PUBLIC
&& (modifiers & Modifier.STATIC) != Modifier.STATIC && (modifiers & Modifier.STATIC) != Modifier.STATIC
......
...@@ -42,6 +42,10 @@ import java.util.Arrays; ...@@ -42,6 +42,10 @@ import java.util.Arrays;
final class UnknownFieldData { final class UnknownFieldData {
final int tag; final int tag;
/**
* Important: this should be treated as immutable, even though it's possible
* to change the array values.
*/
final byte[] bytes; final byte[] bytes;
UnknownFieldData(int tag, byte[] bytes) { UnknownFieldData(int tag, byte[] bytes) {
......
...@@ -31,11 +31,13 @@ ...@@ -31,11 +31,13 @@
package com.google.protobuf.nano; package com.google.protobuf.nano;
import com.google.protobuf.nano.MapTestProto.TestMap; 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.MapTestProto.TestMap.MessageValue;
import com.google.protobuf.nano.NanoAccessorsOuterClass.TestNanoAccessors; import com.google.protobuf.nano.NanoAccessorsOuterClass.TestNanoAccessors;
import com.google.protobuf.nano.NanoHasOuterClass.TestAllTypesNanoHas; import com.google.protobuf.nano.NanoHasOuterClass.TestAllTypesNanoHas;
import com.google.protobuf.nano.NanoOuterClass.TestAllTypesNano; import com.google.protobuf.nano.NanoOuterClass.TestAllTypesNano;
import com.google.protobuf.nano.UnittestRecursiveNano.RecursiveMessageNano; 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.UnittestSimpleNano.SimpleMessageNano;
import com.google.protobuf.nano.UnittestSingleNano.SingleMessageNano; import com.google.protobuf.nano.UnittestSingleNano.SingleMessageNano;
import com.google.protobuf.nano.testext.Extensions; import com.google.protobuf.nano.testext.Extensions;
...@@ -2300,6 +2302,59 @@ public class NanoTest extends TestCase { ...@@ -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 { public void testNanoWithHasParseFrom() throws Exception {
TestAllTypesNanoHas msg = null; TestAllTypesNanoHas msg = null;
// Test false on creation, after clear and upon empty parse. // Test false on creation, after clear and upon empty parse.
...@@ -2986,6 +3041,10 @@ public class NanoTest extends TestCase { ...@@ -2986,6 +3041,10 @@ public class NanoTest extends TestCase {
assertTrue(Arrays.equals(floats, message.getExtension(RepeatedExtensions.repeatedFloat))); assertTrue(Arrays.equals(floats, message.getExtension(RepeatedExtensions.repeatedFloat)));
assertTrue(Arrays.equals(doubles, message.getExtension(RepeatedExtensions.repeatedDouble))); assertTrue(Arrays.equals(doubles, message.getExtension(RepeatedExtensions.repeatedDouble)));
assertTrue(Arrays.equals(enums, message.getExtension(RepeatedExtensions.repeatedEnum))); 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 { public void testNullExtensions() throws Exception {
...@@ -4345,6 +4404,11 @@ public class NanoTest extends TestCase { ...@@ -4345,6 +4404,11 @@ public class NanoTest extends TestCase {
assertMapSet(testMap.sfixed64ToSfixed64Field, int64Values, int64Values); assertMapSet(testMap.sfixed64ToSfixed64Field, int64Values, int64Values);
} }
public void testRepeatedFieldInitializedInReftypesCompatMode() {
NanoReferenceTypesCompat.TestAllTypesNano proto = new NanoReferenceTypesCompat.TestAllTypesNano();
assertNotNull(proto.repeatedString);
}
private void assertRepeatedPackablesEqual( private void assertRepeatedPackablesEqual(
NanoRepeatedPackables.NonPacked nonPacked, NanoRepeatedPackables.Packed packed) { NanoRepeatedPackables.NonPacked nonPacked, NanoRepeatedPackables.Packed packed) {
// Not using MessageNano.equals() -- that belongs to a separate test. // Not using MessageNano.equals() -- that belongs to a separate test.
...@@ -4364,6 +4428,22 @@ public class NanoTest extends TestCase { ...@@ -4364,6 +4428,22 @@ public class NanoTest extends TestCase {
assertTrue(Arrays.equals(nonPacked.enums, packed.enums)); 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) { private void assertHasWireData(MessageNano message, boolean expected) {
byte[] bytes = MessageNano.toByteArray(message); byte[] bytes = MessageNano.toByteArray(message);
int wireLength = bytes.length; int wireLength = bytes.length;
......
...@@ -16,11 +16,15 @@ enum AnEnum { ...@@ -16,11 +16,15 @@ enum AnEnum {
message AnotherMessage { message AnotherMessage {
optional string string = 1; optional string string = 1;
optional bool value = 2; optional bool value = 2;
repeated int32 integers = 3;
} }
message ContainerMessage { message ContainerMessage {
extend ExtendableMessage { extend ExtendableMessage {
optional bool another_thing = 100; 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) { ...@@ -73,13 +73,45 @@ void EnumGenerator::Generate(io::Printer* printer) {
"// enum $classname$\n", "// enum $classname$\n",
"classname", descriptor_->name()); "classname", descriptor_->name());
const string classname = RenameJavaKeywords(descriptor_->name());
// Start of container interface // 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(); bool use_shell_class = params_.java_enum_style();
if (use_shell_class) { if (use_intdef) {
printer->Print( // @IntDef annotation so tools can enforce correctness
"public interface $classname$ {\n", // Annotations will be discarded by the compiler
"classname", RenameJavaKeywords(descriptor_->name())); printer->Print("@java.lang.annotation.Retention("
"java.lang.annotation.RetentionPolicy.SOURCE)\n"
"@android.support.annotation.IntDef({\n");
printer->Indent(); 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 // Canonical values
......
...@@ -76,6 +76,10 @@ void SetEnumVariables(const Params& params, ...@@ -76,6 +76,10 @@ void SetEnumVariables(const Params& params,
internal::WireFormatLite::MakeTag(descriptor->number(), internal::WireFormatLite::MakeTag(descriptor->number(),
internal::WireFormat::WireTypeForFieldType(descriptor->type()))); internal::WireFormat::WireTypeForFieldType(descriptor->type())));
(*variables)["message_name"] = descriptor->containing_type()->name(); (*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, void LoadEnumValues(const Params& params,
...@@ -116,8 +120,10 @@ EnumFieldGenerator::~EnumFieldGenerator() {} ...@@ -116,8 +120,10 @@ EnumFieldGenerator::~EnumFieldGenerator() {}
void EnumFieldGenerator:: void EnumFieldGenerator::
GenerateMembers(io::Printer* printer, bool /* unused lazy_init */) const { GenerateMembers(io::Printer* printer, bool /* unused lazy_init */) const {
printer->Print(variables_, if (params_.generate_intdefs()) {
"public $type$ $name$;\n"); printer->Print(variables_, "$message_type_intdef$\n");
}
printer->Print(variables_, "public $type$ $name$;\n");
if (params_.generate_has()) { if (params_.generate_has()) {
printer->Print(variables_, printer->Print(variables_,
...@@ -256,12 +262,22 @@ AccessorEnumFieldGenerator::~AccessorEnumFieldGenerator() {} ...@@ -256,12 +262,22 @@ AccessorEnumFieldGenerator::~AccessorEnumFieldGenerator() {}
void AccessorEnumFieldGenerator:: void AccessorEnumFieldGenerator::
GenerateMembers(io::Printer* printer, bool /* unused lazy_init */) const { 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_, printer->Print(variables_,
"private int $name$_;\n"
"public int get$capitalized_name$() {\n" "public int get$capitalized_name$() {\n"
" return $name$_;\n" " return $name$_;\n"
"}\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" " $name$_ = value;\n"
" $set_has$;\n" " $set_has$;\n"
" return this;\n" " return this;\n"
...@@ -498,6 +514,14 @@ GenerateSerializedSizeCode(io::Printer* printer) const { ...@@ -498,6 +514,14 @@ GenerateSerializedSizeCode(io::Printer* printer) const {
"}\n"); "}\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:: void RepeatedEnumFieldGenerator::
GenerateEqualsCode(io::Printer* printer) const { GenerateEqualsCode(io::Printer* printer) const {
printer->Print(variables_, printer->Print(variables_,
......
...@@ -106,6 +106,7 @@ class RepeatedEnumFieldGenerator : public FieldGenerator { ...@@ -106,6 +106,7 @@ class RepeatedEnumFieldGenerator : public FieldGenerator {
void GenerateSerializedSizeCode(io::Printer* printer) const; void GenerateSerializedSizeCode(io::Printer* printer) const;
void GenerateEqualsCode(io::Printer* printer) const; void GenerateEqualsCode(io::Printer* printer) const;
void GenerateHashCodeCode(io::Printer* printer) const; void GenerateHashCodeCode(io::Printer* printer) const;
void GenerateFixClonedCode(io::Printer* printer) const;
private: private:
void GenerateRepeatedDataSizeCode(io::Printer* printer) const; void GenerateRepeatedDataSizeCode(io::Printer* printer) const;
......
...@@ -140,7 +140,7 @@ void ExtensionGenerator::Generate(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.create$repeated$$ext_type$(\n"
" com.google.protobuf.nano.Extension.$type$,\n" " com.google.protobuf.nano.Extension.$type$,\n"
" $class$.class,\n" " $class$.class,\n"
" $tag_params$);\n"); " $tag_params$L);\n");
} }
} // namespace javanano } // namespace javanano
......
...@@ -83,6 +83,7 @@ class FieldGenerator { ...@@ -83,6 +83,7 @@ class FieldGenerator {
virtual void GenerateSerializedSizeCode(io::Printer* printer) const = 0; virtual void GenerateSerializedSizeCode(io::Printer* printer) const = 0;
virtual void GenerateEqualsCode(io::Printer* printer) const = 0; virtual void GenerateEqualsCode(io::Printer* printer) const = 0;
virtual void GenerateHashCodeCode(io::Printer* printer) const = 0; virtual void GenerateHashCodeCode(io::Printer* printer) const = 0;
virtual void GenerateFixClonedCode(io::Printer* printer) const {}
protected: protected:
const Params& params_; const Params& params_;
......
...@@ -152,6 +152,12 @@ bool JavaNanoGenerator::Generate(const FileDescriptor* file, ...@@ -152,6 +152,12 @@ bool JavaNanoGenerator::Generate(const FileDescriptor* file,
params.set_ignore_services(option_value == "true"); params.set_ignore_services(option_value == "true");
} else if (option_name == "parcelable_messages") { } else if (option_name == "parcelable_messages") {
params.set_parcelable_messages(option_value == "true"); 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 { } else {
*error = "Ignore unknown javanano generator option: " + option_name; *error = "Ignore unknown javanano generator option: " + option_name;
} }
......
...@@ -136,21 +136,37 @@ void MessageGenerator::Generate(io::Printer* printer) { ...@@ -136,21 +136,37 @@ void MessageGenerator::Generate(io::Printer* printer) {
} }
if (params_.store_unknown_fields() && params_.parcelable_messages()) { if (params_.store_unknown_fields() && params_.parcelable_messages()) {
printer->Print( printer->Print(
" com.google.protobuf.nano.android.ParcelableExtendableMessageNano<$classname$> {\n", " com.google.protobuf.nano.android.ParcelableExtendableMessageNano<$classname$>",
"classname", descriptor_->name()); "classname", descriptor_->name());
} else if (params_.store_unknown_fields()) { } else if (params_.store_unknown_fields()) {
printer->Print( printer->Print(
" com.google.protobuf.nano.ExtendableMessageNano<$classname$> {\n", " com.google.protobuf.nano.ExtendableMessageNano<$classname$>",
"classname", descriptor_->name()); "classname", descriptor_->name());
} else if (params_.parcelable_messages()) { } else if (params_.parcelable_messages()) {
printer->Print( printer->Print(
" com.google.protobuf.nano.android.ParcelableMessageNano {\n"); " com.google.protobuf.nano.android.ParcelableMessageNano");
} else { } else {
printer->Print( 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(); 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 // Nested types and extensions
for (int i = 0; i < descriptor_->extension_count(); i++) { for (int i = 0; i < descriptor_->extension_count(); i++) {
ExtensionGenerator(descriptor_->extension(i), params_).Generate(printer); ExtensionGenerator(descriptor_->extension(i), params_).Generate(printer);
...@@ -288,20 +304,28 @@ void MessageGenerator::Generate(io::Printer* printer) { ...@@ -288,20 +304,28 @@ void MessageGenerator::Generate(io::Printer* printer) {
} }
printer->Print("}\n"); printer->Print("}\n");
} else { } else {
printer->Print(
"\n"
"public $classname$() {\n",
"classname", descriptor_->name());
if (params_.generate_clear()) { if (params_.generate_clear()) {
printer->Print( printer->Print(" clear();\n");
"\n" } else {
"public $classname$() {\n" printer->Indent();
" clear();\n" GenerateFieldInitializers(printer);
"}\n", printer->Outdent();
"classname", descriptor_->name());
} }
printer->Print("}\n");
} }
// Other methods in this class // Other methods in this class
GenerateClear(printer); GenerateClear(printer);
if (params_.generate_clone()) {
GenerateClone(printer);
}
if (params_.generate_equals()) { if (params_.generate_equals()) {
GenerateEquals(printer); GenerateEquals(printer);
GenerateHashCode(printer); GenerateHashCode(printer);
...@@ -495,6 +519,15 @@ void MessageGenerator::GenerateClear(io::Printer* printer) { ...@@ -495,6 +519,15 @@ void MessageGenerator::GenerateClear(io::Printer* printer) {
"classname", descriptor_->name()); "classname", descriptor_->name());
printer->Indent(); printer->Indent();
GenerateFieldInitializers(printer);
printer->Outdent();
printer->Print(
" return this;\n"
"}\n");
}
void MessageGenerator::GenerateFieldInitializers(io::Printer* printer) {
// Clear bit fields. // Clear bit fields.
int totalInts = (field_generators_.total_bits() + 31) / 32; int totalInts = (field_generators_.total_bits() + 31) / 32;
for (int i = 0; i < totalInts; i++) { for (int i = 0; i < totalInts; i++) {
...@@ -520,12 +553,34 @@ void MessageGenerator::GenerateClear(io::Printer* printer) { ...@@ -520,12 +553,34 @@ void MessageGenerator::GenerateClear(io::Printer* printer) {
if (params_.store_unknown_fields()) { if (params_.store_unknown_fields()) {
printer->Print("unknownFieldData = null;\n"); 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->Outdent();
printer->Print( printer->Print(
" cachedSize = -1;\n" " return cloned;\n"
" return this;\n" "}\n"
"}\n"); "\n");
} }
void MessageGenerator::GenerateEquals(io::Printer* printer) { void MessageGenerator::GenerateEquals(io::Printer* printer) {
...@@ -568,7 +623,11 @@ void MessageGenerator::GenerateEquals(io::Printer* printer) { ...@@ -568,7 +623,11 @@ void MessageGenerator::GenerateEquals(io::Printer* printer) {
if (params_.store_unknown_fields()) { if (params_.store_unknown_fields()) {
printer->Print( 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 { } else {
printer->Print( printer->Print(
"return true;\n"); "return true;\n");
...@@ -598,7 +657,9 @@ void MessageGenerator::GenerateHashCode(io::Printer* printer) { ...@@ -598,7 +657,9 @@ void MessageGenerator::GenerateHashCode(io::Printer* printer) {
if (params_.store_unknown_fields()) { if (params_.store_unknown_fields()) {
printer->Print( 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"); printer->Print("return result;\n");
......
...@@ -77,8 +77,10 @@ class MessageGenerator { ...@@ -77,8 +77,10 @@ class MessageGenerator {
const FieldDescriptor* field); const FieldDescriptor* field);
void GenerateClear(io::Printer* printer); void GenerateClear(io::Printer* printer);
void GenerateFieldInitializers(io::Printer* printer);
void GenerateEquals(io::Printer* printer); void GenerateEquals(io::Printer* printer);
void GenerateHashCode(io::Printer* printer); void GenerateHashCode(io::Printer* printer);
void GenerateClone(io::Printer* printer);
const Params& params_; const Params& params_;
const Descriptor* descriptor_; const Descriptor* descriptor_;
......
...@@ -126,6 +126,14 @@ GenerateSerializedSizeCode(io::Printer* printer) const { ...@@ -126,6 +126,14 @@ GenerateSerializedSizeCode(io::Printer* printer) const {
"}\n"); "}\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:: void MessageFieldGenerator::
GenerateEqualsCode(io::Printer* printer) const { GenerateEqualsCode(io::Printer* printer) const {
printer->Print(variables_, printer->Print(variables_,
...@@ -212,6 +220,14 @@ GenerateSerializedSizeCode(io::Printer* printer) const { ...@@ -212,6 +220,14 @@ GenerateSerializedSizeCode(io::Printer* printer) const {
"}\n"); "}\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:: void MessageOneofFieldGenerator::
GenerateEqualsCode(io::Printer* printer) const { GenerateEqualsCode(io::Printer* printer) const {
GenerateOneofFieldEquals(descriptor_, variables_, printer); GenerateOneofFieldEquals(descriptor_, variables_, printer);
...@@ -312,6 +328,19 @@ GenerateSerializedSizeCode(io::Printer* printer) const { ...@@ -312,6 +328,19 @@ GenerateSerializedSizeCode(io::Printer* printer) const {
"}\n"); "}\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:: void RepeatedMessageFieldGenerator::
GenerateEqualsCode(io::Printer* printer) const { GenerateEqualsCode(io::Printer* printer) const {
printer->Print(variables_, printer->Print(variables_,
......
...@@ -58,6 +58,7 @@ class MessageFieldGenerator : public FieldGenerator { ...@@ -58,6 +58,7 @@ class MessageFieldGenerator : public FieldGenerator {
void GenerateSerializedSizeCode(io::Printer* printer) const; void GenerateSerializedSizeCode(io::Printer* printer) const;
void GenerateEqualsCode(io::Printer* printer) const; void GenerateEqualsCode(io::Printer* printer) const;
void GenerateHashCodeCode(io::Printer* printer) const; void GenerateHashCodeCode(io::Printer* printer) const;
void GenerateFixClonedCode(io::Printer* printer) const;
private: private:
const FieldDescriptor* descriptor_; const FieldDescriptor* descriptor_;
...@@ -80,6 +81,7 @@ class MessageOneofFieldGenerator : public FieldGenerator { ...@@ -80,6 +81,7 @@ class MessageOneofFieldGenerator : public FieldGenerator {
void GenerateSerializedSizeCode(io::Printer* printer) const; void GenerateSerializedSizeCode(io::Printer* printer) const;
void GenerateEqualsCode(io::Printer* printer) const; void GenerateEqualsCode(io::Printer* printer) const;
void GenerateHashCodeCode(io::Printer* printer) const; void GenerateHashCodeCode(io::Printer* printer) const;
void GenerateFixClonedCode(io::Printer* printer) const;
private: private:
const FieldDescriptor* descriptor_; const FieldDescriptor* descriptor_;
...@@ -102,6 +104,7 @@ class RepeatedMessageFieldGenerator : public FieldGenerator { ...@@ -102,6 +104,7 @@ class RepeatedMessageFieldGenerator : public FieldGenerator {
void GenerateSerializedSizeCode(io::Printer* printer) const; void GenerateSerializedSizeCode(io::Printer* printer) const;
void GenerateEqualsCode(io::Printer* printer) const; void GenerateEqualsCode(io::Printer* printer) const;
void GenerateHashCodeCode(io::Printer* printer) const; void GenerateHashCodeCode(io::Printer* printer) const;
void GenerateFixClonedCode(io::Printer* printer) const;
private: private:
const FieldDescriptor* descriptor_; const FieldDescriptor* descriptor_;
......
...@@ -66,6 +66,8 @@ class Params { ...@@ -66,6 +66,8 @@ class Params {
bool parcelable_messages_; bool parcelable_messages_;
bool reftypes_primitive_enums_; bool reftypes_primitive_enums_;
bool generate_clear_; bool generate_clear_;
bool generate_clone_;
bool generate_intdefs_;
public: public:
Params(const string & base_name) : Params(const string & base_name) :
...@@ -81,7 +83,9 @@ class Params { ...@@ -81,7 +83,9 @@ class Params {
ignore_services_(false), ignore_services_(false),
parcelable_messages_(false), parcelable_messages_(false),
reftypes_primitive_enums_(false), reftypes_primitive_enums_(false),
generate_clear_(true) { generate_clear_(true),
generate_clone_(false),
generate_intdefs_(false) {
} }
const string& base_name() const { const string& base_name() const {
...@@ -231,6 +235,20 @@ class Params { ...@@ -231,6 +235,20 @@ class Params {
bool generate_clear() const { bool generate_clear() const {
return generate_clear_; 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 } // namespace javanano
......
...@@ -364,6 +364,14 @@ GenerateSerializedSizeCode(io::Printer* printer) const { ...@@ -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:: void PrimitiveFieldGenerator::
GenerateEqualsCode(io::Printer* printer) const { GenerateEqualsCode(io::Printer* printer) const {
// We define equality as serialized form equality. If generate_has(), // We define equality as serialized form equality. If generate_has(),
......
...@@ -131,6 +131,7 @@ class RepeatedPrimitiveFieldGenerator : public FieldGenerator { ...@@ -131,6 +131,7 @@ class RepeatedPrimitiveFieldGenerator : public FieldGenerator {
void GenerateSerializedSizeCode(io::Printer* printer) const; void GenerateSerializedSizeCode(io::Printer* printer) const;
void GenerateEqualsCode(io::Printer* printer) const; void GenerateEqualsCode(io::Printer* printer) const;
void GenerateHashCodeCode(io::Printer* printer) const; void GenerateHashCodeCode(io::Printer* printer) const;
void GenerateFixClonedCode(io::Printer* printer) const;
private: private:
void GenerateRepeatedDataSizeCode(io::Printer* printer) const; 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