From ccc48faf20dbf3b3cddcffe78d198876d543529b Mon Sep 17 00:00:00 2001 From: Brian Duff Date: Tue, 15 Oct 2013 18:35:44 -0700 Subject: Implement hashCode() and equals() behind a generator option. The option is only called 'generate_equals' because: - equals() is the main thing; hashCode() is there only to complement equals(); - it's shorter; - toString() should not be included in this option because it's more for debugging and it's more likely to stop ProGuard from working well. Also shortened the "has bit" expression; was ((bitField & mask) == mask), now ((bitField & mask) != 0). Both the Java code and the bytecode are slightly shorter. Change-Id: Ic309a08a60883bf454eb6612679aa99611620e76 --- .../compiler/javanano/javanano_enum_field.cc | 73 +++++++- .../compiler/javanano/javanano_enum_field.h | 12 +- .../protobuf/compiler/javanano/javanano_field.h | 4 +- .../compiler/javanano/javanano_generator.cc | 2 + .../protobuf/compiler/javanano/javanano_helpers.cc | 13 +- .../protobuf/compiler/javanano/javanano_helpers.h | 31 ++-- .../protobuf/compiler/javanano/javanano_message.cc | 84 ++++++++- .../protobuf/compiler/javanano/javanano_message.h | 7 +- .../compiler/javanano/javanano_message_field.cc | 55 +++++- .../compiler/javanano/javanano_message_field.h | 12 +- .../protobuf/compiler/javanano/javanano_params.h | 11 +- .../compiler/javanano/javanano_primitive_field.cc | 197 ++++++++++++++++++++- .../compiler/javanano/javanano_primitive_field.h | 12 +- 13 files changed, 456 insertions(+), 57 deletions(-) (limited to 'src') diff --git a/src/google/protobuf/compiler/javanano/javanano_enum_field.cc b/src/google/protobuf/compiler/javanano/javanano_enum_field.cc index 3c4b978..b9f4fd0 100644 --- a/src/google/protobuf/compiler/javanano/javanano_enum_field.cc +++ b/src/google/protobuf/compiler/javanano/javanano_enum_field.cc @@ -159,8 +159,46 @@ GenerateSerializedSizeCode(io::Printer* printer) const { } } -string EnumFieldGenerator::GetBoxedType() const { - return ClassName(params_, descriptor_->enum_type()); +void EnumFieldGenerator::GenerateEqualsCode(io::Printer* printer) const { + if (params_.use_reference_types_for_primitives()) { + printer->Print(variables_, + "if (this.$name$ == null) {\n" + " if (other.$name$ != null) {\n" + " return false;\n" + " }\n" + "} else if (!this.$name$.equals(other.$name$)) {\n" + " return false;" + "}\n"); + } else { + // We define equality as serialized form equality. If generate_has(), + // then if the field value equals the default value in both messages, + // but one's 'has' field is set and the other's is not, the serialized + // forms are different and we should return false. + printer->Print(variables_, + "if (this.$name$ != other.$name$"); + if (params_.generate_has()) { + printer->Print(variables_, + "\n" + " || (this.$name$ == $default$\n" + " && this.has$capitalized_name$ != other.has$capitalized_name$)"); + } + printer->Print(") {\n" + " return false;\n" + "}\n"); + } +} + +void EnumFieldGenerator::GenerateHashCodeCode(io::Printer* printer) const { + printer->Print( + "result = 31 * result + "); + if (params_.use_reference_types_for_primitives()) { + printer->Print(variables_, + "(this.$name$ == null ? 0 : this.$name$)"); + } else { + printer->Print(variables_, + "this.$name$"); + } + printer->Print(";\n"); } // =================================================================== @@ -227,8 +265,19 @@ GenerateSerializedSizeCode(io::Printer* printer) const { "}\n"); } -string AccessorEnumFieldGenerator::GetBoxedType() const { - return ClassName(params_, descriptor_->enum_type()); +void AccessorEnumFieldGenerator:: +GenerateEqualsCode(io::Printer* printer) const { + printer->Print(variables_, + "if ($different_has$\n" + " || $name$_ != other.$name$_) {\n" + " return false;\n" + "}\n"); +} + +void AccessorEnumFieldGenerator:: +GenerateHashCodeCode(io::Printer* printer) const { + printer->Print(variables_, + "result = 31 * result + $name$_;\n"); } // =================================================================== @@ -366,8 +415,20 @@ GenerateSerializedSizeCode(io::Printer* printer) const { } } -string RepeatedEnumFieldGenerator::GetBoxedType() const { - return ClassName(params_, descriptor_->enum_type()); +void RepeatedEnumFieldGenerator:: +GenerateEqualsCode(io::Printer* printer) const { + printer->Print(variables_, + "if (!com.google.protobuf.nano.InternalNano.equals(\n" + " this.$name$, other.$name$)) {\n" + " return false;\n" + "}\n"); +} + +void RepeatedEnumFieldGenerator:: +GenerateHashCodeCode(io::Printer* printer) const { + printer->Print(variables_, + "result = 31 * result\n" + " + com.google.protobuf.nano.InternalNano.hashCode(this.$name$);\n"); } } // namespace javanano diff --git a/src/google/protobuf/compiler/javanano/javanano_enum_field.h b/src/google/protobuf/compiler/javanano/javanano_enum_field.h index 934aaaa..af317d8 100644 --- a/src/google/protobuf/compiler/javanano/javanano_enum_field.h +++ b/src/google/protobuf/compiler/javanano/javanano_enum_field.h @@ -55,8 +55,8 @@ class EnumFieldGenerator : public FieldGenerator { void GenerateMergingCode(io::Printer* printer) const; void GenerateSerializationCode(io::Printer* printer) const; void GenerateSerializedSizeCode(io::Printer* printer) const; - - string GetBoxedType() const; + void GenerateEqualsCode(io::Printer* printer) const; + void GenerateHashCodeCode(io::Printer* printer) const; private: const FieldDescriptor* descriptor_; @@ -77,8 +77,8 @@ class AccessorEnumFieldGenerator : public FieldGenerator { void GenerateMergingCode(io::Printer* printer) const; void GenerateSerializationCode(io::Printer* printer) const; void GenerateSerializedSizeCode(io::Printer* printer) const; - - string GetBoxedType() const; + void GenerateEqualsCode(io::Printer* printer) const; + void GenerateHashCodeCode(io::Printer* printer) const; private: const FieldDescriptor* descriptor_; @@ -98,8 +98,8 @@ class RepeatedEnumFieldGenerator : public FieldGenerator { void GenerateMergingCode(io::Printer* printer) const; void GenerateSerializationCode(io::Printer* printer) const; void GenerateSerializedSizeCode(io::Printer* printer) const; - - string GetBoxedType() const; + void GenerateEqualsCode(io::Printer* printer) const; + void GenerateHashCodeCode(io::Printer* printer) const; private: const FieldDescriptor* descriptor_; diff --git a/src/google/protobuf/compiler/javanano/javanano_field.h b/src/google/protobuf/compiler/javanano/javanano_field.h index dad2e7a..150ec19 100644 --- a/src/google/protobuf/compiler/javanano/javanano_field.h +++ b/src/google/protobuf/compiler/javanano/javanano_field.h @@ -62,8 +62,8 @@ class FieldGenerator { virtual void GenerateMergingCode(io::Printer* printer) const = 0; virtual void GenerateSerializationCode(io::Printer* printer) const = 0; virtual void GenerateSerializedSizeCode(io::Printer* printer) const = 0; - - virtual string GetBoxedType() const = 0; + virtual void GenerateEqualsCode(io::Printer* printer) const = 0; + virtual void GenerateHashCodeCode(io::Printer* printer) const = 0; protected: const Params& params_; diff --git a/src/google/protobuf/compiler/javanano/javanano_generator.cc b/src/google/protobuf/compiler/javanano/javanano_generator.cc index 48c3a21..5c8bcb6 100644 --- a/src/google/protobuf/compiler/javanano/javanano_generator.cc +++ b/src/google/protobuf/compiler/javanano/javanano_generator.cc @@ -125,6 +125,8 @@ bool JavaNanoGenerator::Generate(const FileDescriptor* file, } else if (options[i].first == "optional_field_style") { params.set_optional_field_accessors(options[i].second == "accessors"); params.set_use_reference_types_for_primitives(options[i].second == "reftypes"); + } else if (options[i].first == "generate_equals") { + params.set_generate_equals(options[i].second == "true"); } else { *error = "Ignore unknown javanano generator option: " + options[i].first; } diff --git a/src/google/protobuf/compiler/javanano/javanano_helpers.cc b/src/google/protobuf/compiler/javanano/javanano_helpers.cc index ed6dbd7..95ee670 100644 --- a/src/google/protobuf/compiler/javanano/javanano_helpers.cc +++ b/src/google/protobuf/compiler/javanano/javanano_helpers.cc @@ -482,7 +482,7 @@ string GenerateGetBit(int bit_index) { int bit_in_var_index = bit_index % 32; string mask = kBitMasks[bit_in_var_index]; - string result = "((" + var_name + " & " + mask + ") == " + mask + ")"; + string result = "((" + var_name + " & " + mask + ") != 0)"; return result; } @@ -504,11 +504,22 @@ string GenerateClearBit(int bit_index) { return result; } +string GenerateDifferentBit(int bit_index) { + string var_name = GetBitFieldNameForBit(bit_index); + int bit_in_var_index = bit_index % 32; + + string mask = kBitMasks[bit_in_var_index]; + string result = "((" + var_name + " & " + mask + + ") != (other." + var_name + " & " + mask + "))"; + return result; +} + void SetBitOperationVariables(const string name, int bitIndex, map* variables) { (*variables)["get_" + name] = GenerateGetBit(bitIndex); (*variables)["set_" + name] = GenerateSetBit(bitIndex); (*variables)["clear_" + name] = GenerateClearBit(bitIndex); + (*variables)["different_" + name] = GenerateDifferentBit(bitIndex); } } // namespace javanano diff --git a/src/google/protobuf/compiler/javanano/javanano_helpers.h b/src/google/protobuf/compiler/javanano/javanano_helpers.h index cddc077..753a4bd 100644 --- a/src/google/protobuf/compiler/javanano/javanano_helpers.h +++ b/src/google/protobuf/compiler/javanano/javanano_helpers.h @@ -141,30 +141,37 @@ string DefaultValue(const Params& params, const FieldDescriptor* field); // Methods for shared bitfields. -// Gets the name of the shared bitfield for the given index. +// Gets the name of the shared bitfield for the given field index. string GetBitFieldName(int index); // Gets the name of the shared bitfield for the given bit index. // Effectively, GetBitFieldName(bit_index / 32) string GetBitFieldNameForBit(int bit_index); -// Generates the java code for the expression that returns the boolean value -// of the bit of the shared bitfields for the given bit index. -// Example: "((bitField1_ & 0x04) == 0x04)" +// Generates the java code for the expression that returns whether the bit at +// the given bit index is set. +// Example: "((bitField1_ & 0x04000000) != 0)" string GenerateGetBit(int bit_index); -// Generates the java code for the expression that sets the bit of the shared -// bitfields for the given bit index. -// Example: "bitField1_ = (bitField1_ | 0x04)" +// Generates the java code for the expression that sets the bit at the given +// bit index. +// Example: "bitField1_ |= 0x04000000" string GenerateSetBit(int bit_index); -// Generates the java code for the expression that clears the bit of the shared -// bitfields for the given bit index. -// Example: "bitField1_ = (bitField1_ & ~0x04)" +// Generates the java code for the expression that clears the bit at the given +// bit index. +// Example: "bitField1_ = (bitField1_ & ~0x04000000)" string GenerateClearBit(int bit_index); -// Sets the 'get_*', 'set_*' and 'clear_*' variables, where * is the given bit -// field name, to the appropriate Java expressions for the given bit index. +// Generates the java code for the expression that returns whether the bit at +// the given bit index contains different values in the current object and +// another object accessible via the variable 'other'. +// Example: "((bitField1_ & 0x04000000) != (other.bitField1_ & 0x04000000))" +string GenerateDifferentBit(int bit_index); + +// Sets the 'get_*', 'set_*', 'clear_*' and 'different_*' variables, where * is +// the given name of the bit, to the appropriate Java expressions for the given +// bit index. void SetBitOperationVariables(const string name, int bitIndex, map* variables); diff --git a/src/google/protobuf/compiler/javanano/javanano_message.cc b/src/google/protobuf/compiler/javanano/javanano_message.cc index c55ca55..63f8955 100644 --- a/src/google/protobuf/compiler/javanano/javanano_message.cc +++ b/src/google/protobuf/compiler/javanano/javanano_message.cc @@ -198,6 +198,11 @@ void MessageGenerator::Generate(io::Printer* printer) { GenerateClear(printer); + if (params_.generate_equals()) { + GenerateEquals(printer); + GenerateHashCode(printer); + } + // If we have an extension range, generate accessors for extensions. if (params_.store_unknown_fields() && descriptor_->extension_range_count() > 0) { @@ -326,11 +331,11 @@ void MessageGenerator::GenerateMergeFromMethods(io::Printer* printer) { if (params_.store_unknown_fields()) { printer->Print( "if (unknownFieldData == null) {\n" - " unknownFieldData = \n" + " unknownFieldData =\n" " new java.util.ArrayList();\n" "}\n" - "if (!com.google.protobuf.nano.WireFormatNano.storeUnknownField(unknownFieldData, \n" - " input, tag)) {\n" + "if (!com.google.protobuf.nano.WireFormatNano.storeUnknownField(\n" + " unknownFieldData, input, tag)) {\n" " return this;\n" "}\n"); } else { @@ -427,6 +432,79 @@ void MessageGenerator::GenerateClear(io::Printer* printer) { "}\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 + // are used as namespaces. + if (descriptor_->field_count() == 0 && !params_.store_unknown_fields()) { + return; + } + + printer->Print( + "\n" + "@Override\n" + "public boolean equals(Object o) {\n"); + printer->Indent(); + printer->Print( + "if (o == this) {\n" + " return true;\n" + "}\n" + "if (!(o instanceof $classname$)) {\n" + " return false;\n" + "}\n" + "$classname$ other = ($classname$) o;\n", + "classname", descriptor_->name()); + + for (int i = 0; i < descriptor_->field_count(); i++) { + const FieldDescriptor* field = descriptor_->field(i); + field_generators_.get(field).GenerateEqualsCode(printer); + } + + if (params_.store_unknown_fields()) { + printer->Print( + "if (unknownFieldData == null || unknownFieldData.isEmpty()) {\n" + " return other.unknownFieldData == null || other.unknownFieldData.isEmpty();" + "} else {\n" + " return unknownFieldData.equals(other.unknownFieldData);\n" + "}\n"); + } else { + printer->Print( + "return true;\n"); + } + + printer->Outdent(); + printer->Print("}\n"); +} + +void MessageGenerator::GenerateHashCode(io::Printer* printer) { + if (descriptor_->field_count() == 0 && !params_.store_unknown_fields()) { + return; + } + + printer->Print( + "\n" + "@Override\n" + "public int hashCode() {\n"); + printer->Indent(); + + printer->Print("int result = 17;\n"); + for (int i = 0; i < descriptor_->field_count(); i++) { + const FieldDescriptor* field = descriptor_->field(i); + field_generators_.get(field).GenerateHashCodeCode(printer); + } + + if (params_.store_unknown_fields()) { + printer->Print( + "result = 31 * result + (unknownFieldData == null || unknownFieldData.isEmpty()\n" + " ? 0 : unknownFieldData.hashCode());\n"); + } + + printer->Print("return result;\n"); + + printer->Outdent(); + printer->Print("}\n"); +} + // =================================================================== } // namespace javanano diff --git a/src/google/protobuf/compiler/javanano/javanano_message.h b/src/google/protobuf/compiler/javanano/javanano_message.h index d59ec9f..f87f84f 100644 --- a/src/google/protobuf/compiler/javanano/javanano_message.h +++ b/src/google/protobuf/compiler/javanano/javanano_message.h @@ -36,9 +36,10 @@ #define GOOGLE_PROTOBUF_COMPILER_JAVA_MESSAGE_H__ #include -#include -#include +#include #include +#include +#include namespace google { namespace protobuf { @@ -76,6 +77,8 @@ class MessageGenerator { const FieldDescriptor* field); void GenerateClear(io::Printer* printer); + void GenerateEquals(io::Printer* printer); + void GenerateHashCode(io::Printer* printer); const Params& params_; const Descriptor* descriptor_; diff --git a/src/google/protobuf/compiler/javanano/javanano_message_field.cc b/src/google/protobuf/compiler/javanano/javanano_message_field.cc index 02253f9..9f8298c 100644 --- a/src/google/protobuf/compiler/javanano/javanano_message_field.cc +++ b/src/google/protobuf/compiler/javanano/javanano_message_field.cc @@ -126,8 +126,25 @@ GenerateSerializedSizeCode(io::Printer* printer) const { "}\n"); } -string MessageFieldGenerator::GetBoxedType() const { - return ClassName(params_, descriptor_->message_type()); +void MessageFieldGenerator:: +GenerateEqualsCode(io::Printer* printer) const { + printer->Print(variables_, + "if (this.$name$ == null) { \n" + " if (other.$name$ != null) {\n" + " return false;\n" + " }\n" + "} else {\n" + " if (!this.$name$.equals(other.$name$)) {\n" + " return false;\n" + " }\n" + "}\n"); +} + +void MessageFieldGenerator:: +GenerateHashCodeCode(io::Printer* printer) const { + printer->Print(variables_, + "result = 31 * result +\n" + " (this.$name$ == null ? 0 : this.$name$.hashCode());\n"); } // =================================================================== @@ -203,8 +220,22 @@ GenerateSerializedSizeCode(io::Printer* printer) const { "}\n"); } -string AccessorMessageFieldGenerator::GetBoxedType() const { - return ClassName(params_, descriptor_->message_type()); +void AccessorMessageFieldGenerator:: +GenerateEqualsCode(io::Printer* printer) const { + printer->Print(variables_, + "if ($name$_ == null) {\n" + " if (other.$name$_ != null) {\n" + " return false;\n" + " }\n" + "} else if (!$name$_.equals(other.$name$_)) {\n" + " return false;\n" + "}\n"); +} + +void AccessorMessageFieldGenerator:: +GenerateHashCodeCode(io::Printer* printer) const { + printer->Print(variables_, + "result = 31 * result + ($name$_ == null ? 0 : $name$_.hashCode());\n"); } // =================================================================== @@ -291,8 +322,20 @@ GenerateSerializedSizeCode(io::Printer* printer) const { "}\n"); } -string RepeatedMessageFieldGenerator::GetBoxedType() const { - return ClassName(params_, descriptor_->message_type()); +void RepeatedMessageFieldGenerator:: +GenerateEqualsCode(io::Printer* printer) const { + printer->Print(variables_, + "if (!com.google.protobuf.nano.InternalNano.equals(\n" + " this.$name$, other.$name$)) {\n" + " return false;\n" + "}\n"); +} + +void RepeatedMessageFieldGenerator:: +GenerateHashCodeCode(io::Printer* printer) const { + printer->Print(variables_, + "result = 31 * result\n" + " + com.google.protobuf.nano.InternalNano.hashCode(this.$name$);\n"); } } // namespace javanano diff --git a/src/google/protobuf/compiler/javanano/javanano_message_field.h b/src/google/protobuf/compiler/javanano/javanano_message_field.h index 9807513..b724351 100644 --- a/src/google/protobuf/compiler/javanano/javanano_message_field.h +++ b/src/google/protobuf/compiler/javanano/javanano_message_field.h @@ -55,8 +55,8 @@ class MessageFieldGenerator : public FieldGenerator { void GenerateMergingCode(io::Printer* printer) const; void GenerateSerializationCode(io::Printer* printer) const; void GenerateSerializedSizeCode(io::Printer* printer) const; - - string GetBoxedType() const; + void GenerateEqualsCode(io::Printer* printer) const; + void GenerateHashCodeCode(io::Printer* printer) const; private: const FieldDescriptor* descriptor_; @@ -77,8 +77,8 @@ class AccessorMessageFieldGenerator : public FieldGenerator { void GenerateMergingCode(io::Printer* printer) const; void GenerateSerializationCode(io::Printer* printer) const; void GenerateSerializedSizeCode(io::Printer* printer) const; - - string GetBoxedType() const; + void GenerateEqualsCode(io::Printer* printer) const; + void GenerateHashCodeCode(io::Printer* printer) const; private: const FieldDescriptor* descriptor_; @@ -99,8 +99,8 @@ class RepeatedMessageFieldGenerator : public FieldGenerator { void GenerateMergingCode(io::Printer* printer) const; void GenerateSerializationCode(io::Printer* printer) const; void GenerateSerializedSizeCode(io::Printer* printer) const; - - string GetBoxedType() const; + void GenerateEqualsCode(io::Printer* printer) const; + void GenerateHashCodeCode(io::Printer* printer) const; private: const FieldDescriptor* descriptor_; diff --git a/src/google/protobuf/compiler/javanano/javanano_params.h b/src/google/protobuf/compiler/javanano/javanano_params.h index 6e5379c..24aece3 100644 --- a/src/google/protobuf/compiler/javanano/javanano_params.h +++ b/src/google/protobuf/compiler/javanano/javanano_params.h @@ -61,6 +61,7 @@ class Params { bool java_enum_style_; bool optional_field_accessors_; bool use_reference_types_for_primitives_; + bool generate_equals_; public: Params(const string & base_name) : @@ -71,7 +72,8 @@ class Params { generate_has_(false), java_enum_style_(false), optional_field_accessors_(false), - use_reference_types_for_primitives_(false) { + use_reference_types_for_primitives_(false), + generate_equals_(false) { } const string& base_name() const { @@ -186,6 +188,13 @@ class Params { bool use_reference_types_for_primitives() const { return use_reference_types_for_primitives_; } + + void set_generate_equals(bool value) { + generate_equals_ = value; + } + bool generate_equals() const { + return generate_equals_; + } }; } // namespace javanano diff --git a/src/google/protobuf/compiler/javanano/javanano_primitive_field.cc b/src/google/protobuf/compiler/javanano/javanano_primitive_field.cc index 0f7512a..cc5fd45 100644 --- a/src/google/protobuf/compiler/javanano/javanano_primitive_field.cc +++ b/src/google/protobuf/compiler/javanano/javanano_primitive_field.cc @@ -404,8 +404,102 @@ GenerateSerializedSizeCode(io::Printer* printer) const { } } -string PrimitiveFieldGenerator::GetBoxedType() const { - return BoxedPrimitiveTypeName(GetJavaType(descriptor_)); +void PrimitiveFieldGenerator:: +GenerateEqualsCode(io::Printer* printer) const { + // We define equality as serialized form equality. If generate_has(), + // then if the field value equals the default value in both messages, + // but one's 'has' field is set and the other's is not, the serialized + // forms are different and we should return false. + JavaType java_type = GetJavaType(descriptor_); + if (java_type == JAVATYPE_BYTES) { + printer->Print(variables_, + "if (!java.util.Arrays.equals(this.$name$, other.$name$)"); + if (params_.generate_has()) { + printer->Print(variables_, + "\n" + " || (java.util.Arrays.equals(this.$name$, $default$)\n" + " && this.has$capitalized_name$ != other.has$capitalized_name$)"); + } + printer->Print(") {\n" + " return false;\n" + "}\n"); + } else if (java_type == JAVATYPE_STRING + || params_.use_reference_types_for_primitives()) { + printer->Print(variables_, + "if (this.$name$ == null) {\n" + " if (other.$name$ != null) {\n" + " return false;\n" + " }\n" + "} else if (!this.$name$.equals(other.$name$)"); + if (params_.generate_has()) { + printer->Print(variables_, + "\n" + " || (this.$name$.equals($default$)\n" + " && this.has$capitalized_name$ != other.has$capitalized_name$)"); + } + printer->Print(") {\n" + " return false;\n" + "}\n"); + } else { + printer->Print(variables_, + "if (this.$name$ != other.$name$"); + if (params_.generate_has()) { + printer->Print(variables_, + "\n" + " || (this.$name$ == $default$\n" + " && this.has$capitalized_name$ != other.has$capitalized_name$)"); + } + printer->Print(") {\n" + " return false;\n" + "}\n"); + } +} + +void PrimitiveFieldGenerator:: +GenerateHashCodeCode(io::Printer* printer) const { + JavaType java_type = GetJavaType(descriptor_); + if (java_type == JAVATYPE_BYTES) { + printer->Print(variables_, + "result = 31 * result + java.util.Arrays.hashCode(this.$name$);\n"); + } else if (java_type == JAVATYPE_STRING + || params_.use_reference_types_for_primitives()) { + printer->Print(variables_, + "result = 31 * result\n" + " + (this.$name$ == null ? 0 : this.$name$.hashCode());\n"); + } else { + switch (java_type) { + // For all Java primitive types below, the hash codes match the + // results of BoxedType.valueOf(primitiveValue).hashCode(). + case JAVATYPE_INT: + printer->Print(variables_, + "result = 31 * result + this.$name$;\n"); + break; + case JAVATYPE_LONG: + printer->Print(variables_, + "result = 31 * result\n" + " + (int) (this.$name$ ^ (this.$name$ >>> 32));\n"); + break; + case JAVATYPE_FLOAT: + printer->Print(variables_, + "result = 31 * result\n" + " + java.lang.Float.floatToIntBits(this.$name$);\n"); + break; + case JAVATYPE_DOUBLE: + printer->Print(variables_, + "{\n" + " long v = java.lang.Double.doubleToLongBits(this.$name$);\n" + " result = 31 * result + (int) (v ^ (v >>> 32));\n" + "}\n"); + break; + case JAVATYPE_BOOLEAN: + printer->Print(variables_, + "result = 31 * result + (this.$name$ ? 1231 : 1237);\n"); + break; + default: + GOOGLE_LOG(ERROR) << "unknown java type for primitive field"; + break; + } + } } // =================================================================== @@ -483,8 +577,87 @@ GenerateSerializedSizeCode(io::Printer* printer) const { "}\n"); } -string AccessorPrimitiveFieldGenerator::GetBoxedType() const { - return BoxedPrimitiveTypeName(GetJavaType(descriptor_)); +void AccessorPrimitiveFieldGenerator:: +GenerateEqualsCode(io::Printer* printer) const { + switch (GetJavaType(descriptor_)) { + // For all Java primitive types below, the hash codes match the + // results of BoxedType.valueOf(primitiveValue).hashCode(). + case JAVATYPE_INT: + case JAVATYPE_LONG: + case JAVATYPE_FLOAT: + case JAVATYPE_DOUBLE: + case JAVATYPE_BOOLEAN: + printer->Print(variables_, + "if ($different_has$\n" + " || $name$_ != other.$name$_) {\n" + " return false;\n" + "}\n"); + break; + case JAVATYPE_STRING: + // Accessor style would guarantee $name$_ non-null + printer->Print(variables_, + "if ($different_has$\n" + " || !$name$_.equals(other.$name$_)) {\n" + " return false;\n" + "}\n"); + break; + case JAVATYPE_BYTES: + // Accessor style would guarantee $name$_ non-null + printer->Print(variables_, + "if ($different_has$\n" + " || !java.util.Arrays.equals($name$_, other.$name$_)) {\n" + " return false;\n" + "}\n"); + break; + default: + GOOGLE_LOG(ERROR) << "unknown java type for primitive field"; + break; + } +} + +void AccessorPrimitiveFieldGenerator:: +GenerateHashCodeCode(io::Printer* printer) const { + switch (GetJavaType(descriptor_)) { + // For all Java primitive types below, the hash codes match the + // results of BoxedType.valueOf(primitiveValue).hashCode(). + case JAVATYPE_INT: + printer->Print(variables_, + "result = 31 * result + $name$_;\n"); + break; + case JAVATYPE_LONG: + printer->Print(variables_, + "result = 31 * result + (int) ($name$_ ^ ($name$_ >>> 32));\n"); + break; + case JAVATYPE_FLOAT: + printer->Print(variables_, + "result = 31 * result +\n" + " java.lang.Float.floatToIntBits($name$_);\n"); + break; + case JAVATYPE_DOUBLE: + printer->Print(variables_, + "{\n" + " long v = java.lang.Double.doubleToLongBits($name$_);\n" + " result = 31 * result + (int) (v ^ (v >>> 32));\n" + "}\n"); + break; + case JAVATYPE_BOOLEAN: + printer->Print(variables_, + "result = 31 * result + ($name$_ ? 1231 : 1237);\n"); + break; + case JAVATYPE_STRING: + // Accessor style would guarantee $name$_ non-null + printer->Print(variables_, + "result = 31 * result + $name$_.hashCode();\n"); + break; + case JAVATYPE_BYTES: + // Accessor style would guarantee $name$_ non-null + printer->Print(variables_, + "result = 31 * result + java.util.Arrays.hashCode($name$_);\n"); + break; + default: + GOOGLE_LOG(ERROR) << "unknown java type for primitive field"; + break; + } } // =================================================================== @@ -629,8 +802,20 @@ GenerateSerializedSizeCode(io::Printer* printer) const { "}\n"); } -string RepeatedPrimitiveFieldGenerator::GetBoxedType() const { - return BoxedPrimitiveTypeName(GetJavaType(descriptor_)); +void RepeatedPrimitiveFieldGenerator:: +GenerateEqualsCode(io::Printer* printer) const { + printer->Print(variables_, + "if (!com.google.protobuf.nano.InternalNano.equals(\n" + " this.$name$, other.$name$)) {\n" + " return false;\n" + "}\n"); +} + +void RepeatedPrimitiveFieldGenerator:: +GenerateHashCodeCode(io::Printer* printer) const { + printer->Print(variables_, + "result = 31 * result\n" + " + com.google.protobuf.nano.InternalNano.hashCode(this.$name$);\n"); } } // namespace javanano diff --git a/src/google/protobuf/compiler/javanano/javanano_primitive_field.h b/src/google/protobuf/compiler/javanano/javanano_primitive_field.h index b42e2f3..2d2b268 100644 --- a/src/google/protobuf/compiler/javanano/javanano_primitive_field.h +++ b/src/google/protobuf/compiler/javanano/javanano_primitive_field.h @@ -55,8 +55,8 @@ class PrimitiveFieldGenerator : public FieldGenerator { void GenerateMergingCode(io::Printer* printer) const; void GenerateSerializationCode(io::Printer* printer) const; void GenerateSerializedSizeCode(io::Printer* printer) const; - - string GetBoxedType() const; + void GenerateEqualsCode(io::Printer* printer) const; + void GenerateHashCodeCode(io::Printer* printer) const; private: void GenerateSerializationConditional(io::Printer* printer) const; @@ -79,8 +79,8 @@ class AccessorPrimitiveFieldGenerator : public FieldGenerator { void GenerateMergingCode(io::Printer* printer) const; void GenerateSerializationCode(io::Printer* printer) const; void GenerateSerializedSizeCode(io::Printer* printer) const; - - string GetBoxedType() const; + void GenerateEqualsCode(io::Printer* printer) const; + void GenerateHashCodeCode(io::Printer* printer) const; private: const FieldDescriptor* descriptor_; @@ -100,8 +100,8 @@ class RepeatedPrimitiveFieldGenerator : public FieldGenerator { void GenerateMergingCode(io::Printer* printer) const; void GenerateSerializationCode(io::Printer* printer) const; void GenerateSerializedSizeCode(io::Printer* printer) const; - - string GetBoxedType() const; + void GenerateEqualsCode(io::Printer* printer) const; + void GenerateHashCodeCode(io::Printer* printer) const; private: void GenerateRepeatedDataSizeCode(io::Printer* printer) const; -- cgit v1.1