Merge "Add an option to inspect "has" state upon parse."
This commit is contained in:
commit
064b60c659
@ -301,6 +301,22 @@ message's constructor or clear() function is called, the default value
|
||||
penalty. This is not a problem if the field has no default or is an
|
||||
empty default.
|
||||
|
||||
Nano Generator options
|
||||
|
||||
java_nano_generate_has:
|
||||
If true, generates a public boolean variable has<fieldname>
|
||||
accompanying the optional or required field (not present for
|
||||
repeated fields, groups or messages). It is set to false initially
|
||||
and upon clear(). If parseFrom(...) reads the field from the wire,
|
||||
it is set to true. This is a way for clients to inspect the "has"
|
||||
value upon parse. If it is set to true, writeTo(...) will ALWAYS
|
||||
output that field (even if field value is equal to its
|
||||
default).
|
||||
|
||||
IMPORTANT: This option costs an extra 4 bytes per primitive field in
|
||||
the message. Think carefully about whether you really need this. In
|
||||
many cases reading the default works and determining whether the
|
||||
field was received over the wire is irrelevant.
|
||||
|
||||
To use nano protobufs:
|
||||
|
||||
|
@ -37,6 +37,7 @@ import com.google.protobuf.nano.InternalNano;
|
||||
import com.google.protobuf.nano.MessageNano;
|
||||
import com.google.protobuf.nano.MultipleImportingNonMultipleNano1;
|
||||
import com.google.protobuf.nano.MultipleImportingNonMultipleNano2;
|
||||
import com.google.protobuf.nano.NanoHasOuterClass.TestAllTypesNanoHas;
|
||||
import com.google.protobuf.nano.NanoOuterClass;
|
||||
import com.google.protobuf.nano.NanoOuterClass.TestAllTypesNano;
|
||||
import com.google.protobuf.nano.RecursiveMessageNano;
|
||||
@ -2095,6 +2096,93 @@ public class NanoTest extends TestCase {
|
||||
}
|
||||
}
|
||||
|
||||
public void testNanoWithHasParseFrom() throws Exception {
|
||||
TestAllTypesNanoHas msg = null;
|
||||
// Test false on creation, after clear and upon empty parse.
|
||||
for (int i = 0; i < 3; i++) {
|
||||
if (i == 0) {
|
||||
msg = new TestAllTypesNanoHas();
|
||||
} else if (i == 1) {
|
||||
msg.clear();
|
||||
} else if (i == 2) {
|
||||
msg = TestAllTypesNanoHas.parseFrom(new byte[0]);
|
||||
}
|
||||
assertFalse(msg.hasOptionalInt32);
|
||||
assertFalse(msg.hasOptionalString);
|
||||
assertFalse(msg.hasOptionalBytes);
|
||||
assertFalse(msg.hasOptionalNestedEnum);
|
||||
assertFalse(msg.hasDefaultInt32);
|
||||
assertFalse(msg.hasDefaultString);
|
||||
assertFalse(msg.hasDefaultBytes);
|
||||
assertFalse(msg.hasDefaultFloatNan);
|
||||
assertFalse(msg.hasDefaultNestedEnum);
|
||||
assertFalse(msg.hasId);
|
||||
msg.optionalInt32 = 123;
|
||||
msg.optionalNestedMessage = new TestAllTypesNanoHas.NestedMessage();
|
||||
msg.optionalNestedMessage.bb = 2;
|
||||
msg.optionalNestedEnum = TestAllTypesNano.BAZ;
|
||||
}
|
||||
|
||||
byte [] result = MessageNano.toByteArray(msg);
|
||||
int msgSerializedSize = msg.getSerializedSize();
|
||||
//System.out.printf("mss=%d result.length=%d\n", msgSerializedSize, result.length);
|
||||
assertTrue(msgSerializedSize == 13);
|
||||
assertEquals(result.length, msgSerializedSize);
|
||||
|
||||
// Has fields true upon parse.
|
||||
TestAllTypesNanoHas newMsg = TestAllTypesNanoHas.parseFrom(result);
|
||||
assertEquals(123, newMsg.optionalInt32);
|
||||
assertTrue(newMsg.hasOptionalInt32);
|
||||
assertEquals(2, newMsg.optionalNestedMessage.bb);
|
||||
assertTrue(newMsg.optionalNestedMessage.hasBb);
|
||||
assertEquals(TestAllTypesNanoHas.BAZ, newMsg.optionalNestedEnum);
|
||||
assertTrue(newMsg.hasOptionalNestedEnum);
|
||||
}
|
||||
|
||||
public void testNanoWithHasSerialize() throws Exception {
|
||||
TestAllTypesNanoHas msg = new TestAllTypesNanoHas();
|
||||
msg.hasOptionalInt32 = true;
|
||||
msg.hasOptionalString = true;
|
||||
msg.hasOptionalBytes = true;
|
||||
msg.optionalNestedMessage = new TestAllTypesNanoHas.NestedMessage();
|
||||
msg.optionalNestedMessage.hasBb = true;
|
||||
msg.hasOptionalNestedEnum = true;
|
||||
msg.hasDefaultInt32 = true;
|
||||
msg.hasDefaultString = true;
|
||||
msg.hasDefaultBytes = true;
|
||||
msg.hasDefaultFloatNan = true;
|
||||
msg.hasDefaultNestedEnum = true;
|
||||
|
||||
byte [] result = MessageNano.toByteArray(msg);
|
||||
int msgSerializedSize = msg.getSerializedSize();
|
||||
assertEquals(result.length, msgSerializedSize);
|
||||
|
||||
// Now deserialize and find that all fields are set and equal to their defaults.
|
||||
TestAllTypesNanoHas newMsg = TestAllTypesNanoHas.parseFrom(result);
|
||||
assertTrue(newMsg.hasOptionalInt32);
|
||||
assertTrue(newMsg.hasOptionalString);
|
||||
assertTrue(newMsg.hasOptionalBytes);
|
||||
assertTrue(newMsg.optionalNestedMessage.hasBb);
|
||||
assertTrue(newMsg.hasOptionalNestedEnum);
|
||||
assertTrue(newMsg.hasDefaultInt32);
|
||||
assertTrue(newMsg.hasDefaultString);
|
||||
assertTrue(newMsg.hasDefaultBytes);
|
||||
assertTrue(newMsg.hasDefaultFloatNan);
|
||||
assertTrue(newMsg.hasDefaultNestedEnum);
|
||||
assertTrue(newMsg.hasId);
|
||||
assertEquals(0, newMsg.optionalInt32);
|
||||
assertEquals(0, newMsg.optionalString.length());
|
||||
assertEquals(0, newMsg.optionalBytes.length);
|
||||
assertEquals(0, newMsg.optionalNestedMessage.bb);
|
||||
assertEquals(TestAllTypesNanoHas.FOO, newMsg.optionalNestedEnum);
|
||||
assertEquals(41, newMsg.defaultInt32);
|
||||
assertEquals("hello", newMsg.defaultString);
|
||||
assertEquals("world", new String(newMsg.defaultBytes, "UTF-8"));
|
||||
assertEquals(TestAllTypesNanoHas.BAR, newMsg.defaultNestedEnum);
|
||||
assertEquals(Float.NaN, newMsg.defaultFloatNan);
|
||||
assertEquals(0, newMsg.id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that fields with a default value of NaN are not serialized when
|
||||
* set to NaN. This is a special case as NaN != NaN, so normal equality
|
||||
|
@ -82,12 +82,22 @@ void EnumFieldGenerator::
|
||||
GenerateMembers(io::Printer* printer) const {
|
||||
printer->Print(variables_,
|
||||
"public int $name$ = $default$;\n");
|
||||
|
||||
if (params_.generate_has()) {
|
||||
printer->Print(variables_,
|
||||
"public boolean has$capitalized_name$ = false;\n");
|
||||
}
|
||||
}
|
||||
|
||||
void EnumFieldGenerator::
|
||||
GenerateParsingCode(io::Printer* printer) const {
|
||||
printer->Print(variables_,
|
||||
" this.$name$ = input.readInt32();\n");
|
||||
|
||||
if (params_.generate_has()) {
|
||||
printer->Print(variables_,
|
||||
" has$capitalized_name$ = true;\n");
|
||||
}
|
||||
}
|
||||
|
||||
void EnumFieldGenerator::
|
||||
@ -96,8 +106,14 @@ GenerateSerializationCode(io::Printer* printer) const {
|
||||
printer->Print(variables_,
|
||||
"output.writeInt32($number$, this.$name$);\n");
|
||||
} else {
|
||||
if (params_.generate_has()) {
|
||||
printer->Print(variables_,
|
||||
"if (this.$name$ != $default$ || has$capitalized_name$) {\n");
|
||||
} else {
|
||||
printer->Print(variables_,
|
||||
"if (this.$name$ != $default$) {\n");
|
||||
}
|
||||
printer->Print(variables_,
|
||||
"if (this.$name$ != $default$) {\n"
|
||||
" output.writeInt32($number$, this.$name$);\n"
|
||||
"}\n");
|
||||
}
|
||||
@ -110,8 +126,14 @@ GenerateSerializedSizeCode(io::Printer* printer) const {
|
||||
"size += com.google.protobuf.nano.CodedOutputByteBufferNano\n"
|
||||
" .computeInt32Size($number$, this.$name$);\n");
|
||||
} else {
|
||||
if (params_.generate_has()) {
|
||||
printer->Print(variables_,
|
||||
"if (this.$name$ != $default$ || has$capitalized_name$) {\n");
|
||||
} else {
|
||||
printer->Print(variables_,
|
||||
"if (this.$name$ != $default$) {\n");
|
||||
}
|
||||
printer->Print(variables_,
|
||||
"if (this.$name$ != $default$) {\n"
|
||||
" size += com.google.protobuf.nano.CodedOutputByteBufferNano\n"
|
||||
" .computeInt32Size($number$, this.$name$);\n"
|
||||
"}\n");
|
||||
|
@ -118,6 +118,8 @@ bool JavaNanoGenerator::Generate(const FileDescriptor* file,
|
||||
params.set_store_unknown_fields(options[i].second == "true");
|
||||
} else if (options[i].first == "java_multiple_files") {
|
||||
params.set_override_java_multiple_files(options[i].second == "true");
|
||||
} else if (options[i].first == "java_nano_generate_has") {
|
||||
params.set_generate_has(options[i].second == "true");
|
||||
} else {
|
||||
*error = "Ignore unknown javanano generator option: " + options[i].first;
|
||||
}
|
||||
|
@ -406,6 +406,15 @@ void MessageGenerator::GenerateClear(io::Printer* printer) {
|
||||
"name", RenameJavaKeywords(UnderscoresToCamelCase(field)),
|
||||
"default", DefaultValue(params_, field));
|
||||
}
|
||||
|
||||
if (params_.generate_has() &&
|
||||
field->label() != FieldDescriptor::LABEL_REPEATED &&
|
||||
field->type() != FieldDescriptor::TYPE_GROUP &&
|
||||
field->type() != FieldDescriptor::TYPE_MESSAGE) {
|
||||
printer->Print(
|
||||
"has$capitalized_name$ = false;\n",
|
||||
"capitalized_name", UnderscoresToCapitalizedCamelCase(field));
|
||||
}
|
||||
}
|
||||
|
||||
// Clear unknown fields.
|
||||
|
@ -57,13 +57,15 @@ class Params {
|
||||
NameMap java_packages_;
|
||||
NameMap java_outer_classnames_;
|
||||
NameSet java_multiple_files_;
|
||||
bool generate_has_;
|
||||
|
||||
public:
|
||||
Params(const string & base_name) :
|
||||
empty_(""),
|
||||
base_name_(base_name),
|
||||
override_java_multiple_files_(JAVANANO_MUL_UNSET),
|
||||
store_unknown_fields_(false) {
|
||||
store_unknown_fields_(false),
|
||||
generate_has_(false) {
|
||||
}
|
||||
|
||||
const string& base_name() const {
|
||||
@ -151,6 +153,13 @@ class Params {
|
||||
return store_unknown_fields_;
|
||||
}
|
||||
|
||||
void set_generate_has(bool value) {
|
||||
generate_has_ = value;
|
||||
}
|
||||
bool generate_has() const {
|
||||
return generate_has_;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
} // namespace javanano
|
||||
|
@ -321,12 +321,46 @@ GenerateMembers(io::Printer* printer) const {
|
||||
printer->Print(variables_,
|
||||
"public $type$ $name$ = $default$;\n");
|
||||
}
|
||||
|
||||
if (params_.generate_has()) {
|
||||
printer->Print(variables_,
|
||||
"public boolean has$capitalized_name$ = false;\n");
|
||||
}
|
||||
}
|
||||
|
||||
void PrimitiveFieldGenerator::
|
||||
GenerateParsingCode(io::Printer* printer) const {
|
||||
printer->Print(variables_,
|
||||
"this.$name$ = input.read$capitalized_type$();\n");
|
||||
|
||||
if (params_.generate_has()) {
|
||||
printer->Print(variables_,
|
||||
"has$capitalized_name$ = true;\n");
|
||||
}
|
||||
}
|
||||
|
||||
void PrimitiveFieldGenerator::
|
||||
GenerateSerializationConditional(io::Printer* printer) const {
|
||||
if (params_.generate_has()) {
|
||||
printer->Print(variables_,
|
||||
"if (has$capitalized_name$ || ");
|
||||
} else {
|
||||
printer->Print(variables_,
|
||||
"if (");
|
||||
}
|
||||
if (IsArrayType(GetJavaType(descriptor_))) {
|
||||
printer->Print(variables_,
|
||||
"!java.util.Arrays.equals(this.$name$, $default$)) {\n");
|
||||
} else if (IsReferenceType(GetJavaType(descriptor_))) {
|
||||
printer->Print(variables_,
|
||||
"!this.$name$.equals($default$)) {\n");
|
||||
} else if (IsDefaultNaN(descriptor_)) {
|
||||
printer->Print(variables_,
|
||||
"!$capitalized_type$.isNaN(this.$name$)) {\n");
|
||||
} else {
|
||||
printer->Print(variables_,
|
||||
"this.$name$ != $default$) {\n");
|
||||
}
|
||||
}
|
||||
|
||||
void PrimitiveFieldGenerator::
|
||||
@ -335,20 +369,7 @@ GenerateSerializationCode(io::Printer* printer) const {
|
||||
printer->Print(variables_,
|
||||
"output.write$capitalized_type$($number$, this.$name$);\n");
|
||||
} else {
|
||||
if (IsArrayType(GetJavaType(descriptor_))) {
|
||||
printer->Print(variables_,
|
||||
"if (!java.util.Arrays.equals(this.$name$, $default$)) {\n");
|
||||
} else if (IsReferenceType(GetJavaType(descriptor_))) {
|
||||
printer->Print(variables_,
|
||||
"if (!this.$name$.equals($default$)) {\n");
|
||||
} else if (IsDefaultNaN(descriptor_)) {
|
||||
printer->Print(variables_,
|
||||
"if (!$capitalized_type$.isNaN(this.$name$)) {\n");
|
||||
} else {
|
||||
printer->Print(variables_,
|
||||
"if (this.$name$ != $default$) {\n");
|
||||
}
|
||||
|
||||
GenerateSerializationConditional(printer);
|
||||
printer->Print(variables_,
|
||||
" output.write$capitalized_type$($number$, this.$name$);\n"
|
||||
"}\n");
|
||||
@ -362,20 +383,7 @@ GenerateSerializedSizeCode(io::Printer* printer) const {
|
||||
"size += com.google.protobuf.nano.CodedOutputByteBufferNano\n"
|
||||
" .compute$capitalized_type$Size($number$, this.$name$);\n");
|
||||
} else {
|
||||
if (IsArrayType(GetJavaType(descriptor_))) {
|
||||
printer->Print(variables_,
|
||||
"if (!java.util.Arrays.equals(this.$name$, $default$)) {\n");
|
||||
} else if (IsReferenceType(GetJavaType(descriptor_))) {
|
||||
printer->Print(variables_,
|
||||
"if (!this.$name$.equals($default$)) {\n");
|
||||
} else if (IsDefaultNaN(descriptor_)) {
|
||||
printer->Print(variables_,
|
||||
"if (!$capitalized_type$.isNaN(this.$name$)) {\n");
|
||||
} else {
|
||||
printer->Print(variables_,
|
||||
"if (this.$name$ != $default$) {\n");
|
||||
}
|
||||
|
||||
GenerateSerializationConditional(printer);
|
||||
printer->Print(variables_,
|
||||
" size += com.google.protobuf.nano.CodedOutputByteBufferNano\n"
|
||||
" .compute$capitalized_type$Size($number$, this.$name$);\n"
|
||||
|
@ -58,6 +58,8 @@ class PrimitiveFieldGenerator : public FieldGenerator {
|
||||
string GetBoxedType() const;
|
||||
|
||||
private:
|
||||
void GenerateSerializationConditional(io::Printer* printer) const;
|
||||
|
||||
const FieldDescriptor* descriptor_;
|
||||
map<string, string> variables_;
|
||||
|
||||
|
78
src/google/protobuf/unittest_has_nano.proto
Normal file
78
src/google/protobuf/unittest_has_nano.proto
Normal file
@ -0,0 +1,78 @@
|
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 Google Inc. All rights reserved.
|
||||
// http://code.google.com/p/protobuf/
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
// Author: ulas@google.com (Ulas Kirazci)
|
||||
|
||||
package protobuf_unittest;
|
||||
|
||||
option java_package = "com.google.protobuf.nano";
|
||||
option java_outer_classname = "NanoHasOuterClass";
|
||||
|
||||
message TestAllTypesNanoHas {
|
||||
|
||||
message NestedMessage {
|
||||
optional int32 bb = 1;
|
||||
}
|
||||
|
||||
enum NestedEnum {
|
||||
FOO = 1;
|
||||
BAR = 2;
|
||||
BAZ = 3;
|
||||
}
|
||||
|
||||
// Singular
|
||||
optional int32 optional_int32 = 1;
|
||||
optional string optional_string = 14;
|
||||
optional bytes optional_bytes = 15;
|
||||
|
||||
optional NestedMessage optional_nested_message = 18;
|
||||
|
||||
optional NestedEnum optional_nested_enum = 21;
|
||||
|
||||
// Repeated
|
||||
repeated int32 repeated_int32 = 31;
|
||||
repeated string repeated_string = 44;
|
||||
repeated bytes repeated_bytes = 45;
|
||||
|
||||
repeated NestedMessage repeated_nested_message = 48;
|
||||
|
||||
repeated NestedEnum repeated_nested_enum = 51;
|
||||
|
||||
// Singular with defaults
|
||||
optional int32 default_int32 = 61 [default = 41 ];
|
||||
optional string default_string = 74 [default = "hello"];
|
||||
optional bytes default_bytes = 75 [default = "world"];
|
||||
|
||||
optional float default_float_nan = 99 [default = nan];
|
||||
|
||||
optional NestedEnum default_nested_enum = 81 [default = BAR];
|
||||
|
||||
required int32 id = 86;
|
||||
}
|
Loading…
Reference in New Issue
Block a user