Add clone() method support for nano.
Upstreamed from Another Place (cr/57247854). Change-Id: I2aaf59544c0f5ae21a51891d8a5eeda1dc722c90
This commit is contained in:
parent
fb96026b8d
commit
d099a88685
20 changed files with 222 additions and 10 deletions
|
@ -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;
|
||||
|
|
Loading…
Add table
Reference in a new issue