aboutsummaryrefslogtreecommitdiffhomepage
path: root/javanano
diff options
context:
space:
mode:
authorGravatar Brian Duff <bduff@google.com>2014-10-01 13:33:40 -0700
committerGravatar Brian Duff <bduff@google.com>2015-04-28 12:42:55 -0700
commitd099a88685bf4b2df1689eb4cc56e75065cb87b1 (patch)
tree8b50aaed239f2f70e26710dd3965942e7145141b /javanano
parentfb96026b8deb79aa023c9f5c460582e8fea8f331 (diff)
Add clone() method support for nano.
Upstreamed from Another Place (cr/57247854). Change-Id: I2aaf59544c0f5ae21a51891d8a5eeda1dc722c90
Diffstat (limited to 'javanano')
-rw-r--r--javanano/pom.xml6
-rw-r--r--javanano/src/main/java/com/google/protobuf/nano/ExtendableMessageNano.java7
-rw-r--r--javanano/src/main/java/com/google/protobuf/nano/FieldArray.java17
-rw-r--r--javanano/src/main/java/com/google/protobuf/nano/FieldData.java52
-rw-r--r--javanano/src/main/java/com/google/protobuf/nano/InternalNano.java8
-rw-r--r--javanano/src/main/java/com/google/protobuf/nano/MessageNano.java8
-rw-r--r--javanano/src/main/java/com/google/protobuf/nano/UnknownFieldData.java4
-rw-r--r--javanano/src/test/java/com/google/protobuf/nano/NanoTest.java20
-rw-r--r--javanano/src/test/java/com/google/protobuf/nano/unittest_extension_nano.proto1
9 files changed, 118 insertions, 5 deletions
diff --git a/javanano/pom.xml b/javanano/pom.xml
index 64ed4372..3d8cfb9f 100644
--- a/javanano/pom.xml
+++ b/javanano/pom.xml
@@ -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>
diff --git a/javanano/src/main/java/com/google/protobuf/nano/ExtendableMessageNano.java b/javanano/src/main/java/com/google/protobuf/nano/ExtendableMessageNano.java
index b979390d..aeacbbf3 100644
--- a/javanano/src/main/java/com/google/protobuf/nano/ExtendableMessageNano.java
+++ b/javanano/src/main/java/com/google/protobuf/nano/ExtendableMessageNano.java
@@ -184,4 +184,11 @@ public abstract class ExtendableMessageNano<M extends ExtendableMessageNano<M>>
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;
+ }
}
diff --git a/javanano/src/main/java/com/google/protobuf/nano/FieldArray.java b/javanano/src/main/java/com/google/protobuf/nano/FieldArray.java
index cdb66da2..c2044f6a 100644
--- a/javanano/src/main/java/com/google/protobuf/nano/FieldArray.java
+++ b/javanano/src/main/java/com/google/protobuf/nano/FieldArray.java
@@ -37,7 +37,7 @@ package com.google.protobuf.nano;
*
* Based on {@link android.support.v4.util.SpareArrayCompat}.
*/
-class FieldArray {
+class FieldArray implements Cloneable {
private static final FieldData DELETED = new FieldData();
private boolean mGarbage = false;
@@ -270,4 +270,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;
+ }
}
diff --git a/javanano/src/main/java/com/google/protobuf/nano/FieldData.java b/javanano/src/main/java/com/google/protobuf/nano/FieldData.java
index 21ead88b..ebebabc8 100644
--- a/javanano/src/main/java/com/google/protobuf/nano/FieldData.java
+++ b/javanano/src/main/java/com/google/protobuf/nano/FieldData.java
@@ -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);
+ }
+ }
}
diff --git a/javanano/src/main/java/com/google/protobuf/nano/InternalNano.java b/javanano/src/main/java/com/google/protobuf/nano/InternalNano.java
index e7ba8d12..f1263df5 100644
--- a/javanano/src/main/java/com/google/protobuf/nano/InternalNano.java
+++ b/javanano/src/main/java/com/google/protobuf/nano/InternalNano.java
@@ -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();
+ }
+ }
}
diff --git a/javanano/src/main/java/com/google/protobuf/nano/MessageNano.java b/javanano/src/main/java/com/google/protobuf/nano/MessageNano.java
index 81e58571..23475027 100644
--- a/javanano/src/main/java/com/google/protobuf/nano/MessageNano.java
+++ b/javanano/src/main/java/com/google/protobuf/nano/MessageNano.java
@@ -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();
+ }
}
diff --git a/javanano/src/main/java/com/google/protobuf/nano/UnknownFieldData.java b/javanano/src/main/java/com/google/protobuf/nano/UnknownFieldData.java
index a17fccf3..b1678d1b 100644
--- a/javanano/src/main/java/com/google/protobuf/nano/UnknownFieldData.java
+++ b/javanano/src/main/java/com/google/protobuf/nano/UnknownFieldData.java
@@ -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) {
diff --git a/javanano/src/test/java/com/google/protobuf/nano/NanoTest.java b/javanano/src/test/java/com/google/protobuf/nano/NanoTest.java
index c81846e5..91cc385f 100644
--- a/javanano/src/test/java/com/google/protobuf/nano/NanoTest.java
+++ b/javanano/src/test/java/com/google/protobuf/nano/NanoTest.java
@@ -3023,6 +3023,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 {
@@ -4406,6 +4410,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;
diff --git a/javanano/src/test/java/com/google/protobuf/nano/unittest_extension_nano.proto b/javanano/src/test/java/com/google/protobuf/nano/unittest_extension_nano.proto
index d1c5766d..ca56b3dd 100644
--- a/javanano/src/test/java/com/google/protobuf/nano/unittest_extension_nano.proto
+++ b/javanano/src/test/java/com/google/protobuf/nano/unittest_extension_nano.proto
@@ -16,6 +16,7 @@ enum AnEnum {
message AnotherMessage {
optional string string = 1;
optional bool value = 2;
+ repeated int32 integers = 3;
}
message ContainerMessage {