Commit d099a886 authored by Brian Duff's avatar Brian Duff

Add clone() method support for nano.

Upstreamed from Another Place (cr/57247854).

Change-Id: I2aaf59544c0f5ae21a51891d8a5eeda1dc722c90
parent fb96026b
......@@ -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>
......
......@@ -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;
}
}
......@@ -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;
}
}
......@@ -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();
}
}
......@@ -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) {
......
......@@ -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;
......
......@@ -16,6 +16,7 @@ enum AnEnum {
message AnotherMessage {
optional string string = 1;
optional bool value = 2;
repeated int32 integers = 3;
}
message ContainerMessage {
......
......@@ -498,6 +498,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;
......
......@@ -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,8 @@ 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 {
*error = "Ignore unknown javanano generator option: " + option_name;
}
......
......@@ -136,18 +136,23 @@ 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();
......@@ -306,6 +311,10 @@ void MessageGenerator::Generate(io::Printer* printer) {
GenerateClear(printer);
if (params_.generate_clone()) {
GenerateClone(printer);
}
if (params_.generate_equals()) {
GenerateEquals(printer);
GenerateHashCode(printer);
......@@ -536,6 +545,33 @@ void MessageGenerator::GenerateFieldInitializers(io::Printer* printer) {
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(
" return cloned;\n"
"}\n"
"\n");
}
void MessageGenerator::GenerateEquals(io::Printer* printer) {
// Don't override if there are no fields. We could generate an
// equals method that compares types, but often empty messages
......
......@@ -80,6 +80,7 @@ class MessageGenerator {
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,7 @@ class Params {
bool parcelable_messages_;
bool reftypes_primitive_enums_;
bool generate_clear_;
bool generate_clone_;
public:
Params(const string & base_name) :
......@@ -81,7 +82,8 @@ class Params {
ignore_services_(false),
parcelable_messages_(false),
reftypes_primitive_enums_(false),
generate_clear_(true) {
generate_clear_(true),
generate_clone_(false) {
}
const string& base_name() const {
......@@ -231,6 +233,13 @@ 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_;
}
};
} // 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