Merge "Add an option to inspect "has" state upon parse."

pull/91/head
Ulas Kirazci 11 years ago committed by Gerrit Code Review
commit 064b60c659
  1. 16
      java/README.txt
  2. 88
      java/src/test/java/com/google/protobuf/NanoTest.java
  3. 26
      src/google/protobuf/compiler/javanano/javanano_enum_field.cc
  4. 2
      src/google/protobuf/compiler/javanano/javanano_generator.cc
  5. 9
      src/google/protobuf/compiler/javanano/javanano_message.cc
  6. 11
      src/google/protobuf/compiler/javanano/javanano_params.h
  7. 64
      src/google/protobuf/compiler/javanano/javanano_primitive_field.cc
  8. 2
      src/google/protobuf/compiler/javanano/javanano_primitive_field.h
  9. 78
      src/google/protobuf/unittest_has_nano.proto

@ -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_;

@ -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…
Cancel
Save