From 10107cbc7a0159afce57795863c140e1622c9688 Mon Sep 17 00:00:00 2001 From: Brian Duff Date: Mon, 30 Sep 2013 20:49:13 -0700 Subject: Add reftypes field generator option. This option generates fields as reference types, and serializes based on nullness. Change-Id: Ic32e0eebff59d14016cc9a19e15a9bb08ae0bba5 Signed-off-by: Brian Duff --- java/README.txt | 71 ++++++++++++++++------ .../test/java/com/google/protobuf/NanoTest.java | 62 +++++++++++++++++++ 2 files changed, 114 insertions(+), 19 deletions(-) (limited to 'java') diff --git a/java/README.txt b/java/README.txt index e2e698e1..adc1972c 100644 --- a/java/README.txt +++ b/java/README.txt @@ -487,34 +487,67 @@ java_nano_generate_has={true,false} (default: false) many cases reading the default works and determining whether the field was received over the wire is irrelevant. -optional_field_style={default,accessors} (default: default) - Defines the style of the generated code for _optional_ fields only. +optional_field_style={default,accessors,reftypes} (default: default) + Defines the style of the generated code for fields. + + * default * + In the default style, optional fields translate into public mutable Java fields, and the serialization process is as discussed in the - "IMPORTANT" section above. When set to 'accessors', each optional - field is encapsulated behind 4 accessors, namely get(), - set(), has() and clear() methods, - with the standard semantics. The hazzer's return value determines - whether a field is serialized, so this style is useful when you need - to serialize a field with the default value, or check if a field has - been explicitly set to its default value from the wire. - - Required fields are still translated to one public mutable Java - field each, and repeated fields are still translated to arrays. No - accessors are generated for them. - - optional_field_style=accessors cannot be used together with - java_nano_generate_has=true. If you need the 'has' flag for any - required field (you have no reason to), you can only use - java_nano_generate_has=true. + "IMPORTANT" section above. - IMPORTANT: When using the 'accessor' style, ProGuard should always + * accessors * + + When set to 'accessors', each optional field is encapsulated behind + 4 accessors, namely get(), set(), has() + and clear() methods, with the standard semantics. The hazzer's + return value determines whether a field is serialized, so this style is + useful when you need to serialize a field with the default value, or check + if a field has been explicitly set to its default value from the wire. + + In the 'accessors' style, required fields are still translated to one + public mutable Java field each, and repeated fields are still translated + to arrays. No accessors are generated for them. + + IMPORTANT: When using the 'accessors' style, ProGuard should always be enabled with optimization (don't use -dontoptimize) and allowing access modification (use -allowaccessmodification). This removes the unused accessors and maybe inline the rest at the call sites, reducing the final code size. TODO(maxtroy): find ProGuard config that would work the best. + * reftypes * + + When set to 'reftypes', each proto field is generated as a public Java + field. For primitive types, these fields use the Java reference types + such as java.lang.Integer instead of primitive types such as int. + + In the 'reftypes' style, fields are initialized to null (or empty + arrays for repeated fields), and their default values are not available. + They are serialized over the wire based on equality to null. + + The 'reftypes' mode has some additional cost due to autoboxing and usage + of reference types. In practice, many boxed types are cached, and so don't + result in object creation. However, references do take slightly more memory + than primitives. + + The 'reftypes' mode is useful when you want to be able to serialize fields + with default values, or check if a field has been explicitly set to the + default over the wire without paying the extra method cost of the + 'accessors' mode. + + Note that if you attempt to write null to a required field in the reftypes + mode, serialization of the proto will cause a NullPointerException. This is + an intentional indicator that you must set required fields. + + + NOTE + optional_field_style=accessors or reftypes cannot be used together with + java_nano_generate_has=true. If you need the 'has' flag for any + required field (you have no reason to), you can only use + java_nano_generate_has=true. + + To use nano protobufs: - Link with the generated jar file diff --git a/java/src/test/java/com/google/protobuf/NanoTest.java b/java/src/test/java/com/google/protobuf/NanoTest.java index ca0bcda4..4f2ac3f8 100644 --- a/java/src/test/java/com/google/protobuf/NanoTest.java +++ b/java/src/test/java/com/google/protobuf/NanoTest.java @@ -48,6 +48,7 @@ import com.google.protobuf.nano.NanoAccessorsOuterClass.TestNanoAccessors; 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.NanoReferenceTypes; import com.google.protobuf.nano.UnittestImportNano; import com.google.protobuf.nano.UnittestMultipleNano; import com.google.protobuf.nano.UnittestRecursiveNano.RecursiveMessageNano; @@ -2624,6 +2625,67 @@ public class NanoTest extends TestCase { assertEquals(123, msg.synchronized_); } + public void testReferenceTypesForPrimitives() throws Exception { + NanoReferenceTypes.TestAllTypesNano message = new NanoReferenceTypes.TestAllTypesNano(); + + // Base check - when nothing is set, we serialize nothing. + assertHasWireData(message, false); + + message.defaultBool = true; + assertHasWireData(message, true); + + message.defaultBool = false; + assertHasWireData(message, true); + + message.defaultBool = null; + assertHasWireData(message, false); + + message.defaultInt32 = 5; + assertHasWireData(message, true); + + message.defaultInt32 = null; + assertHasWireData(message, false); + + message.defaultInt64 = 123456L; + assertHasWireData(message, true); + + message.defaultInt64 = null; + assertHasWireData(message, false); + + message.defaultFloat = 1f; + assertHasWireData(message, true); + + message.defaultFloat = null; + assertHasWireData(message, false); + + message.defaultDouble = 2.1; + assertHasWireData(message, true); + + message.defaultDouble = null; + assertHasWireData(message, false); + + message.defaultString = "hello"; + assertHasWireData(message, true); + + message.defaultString = null; + assertHasWireData(message, false); + + message.defaultBytes = new byte[] { 1, 2, 3 }; + assertHasWireData(message, true); + + message.defaultBytes = null; + assertHasWireData(message, false); + } + + private void assertHasWireData(MessageNano message, boolean expected) { + int wireLength = MessageNano.toByteArray(message).length; + if (expected) { + assertFalse(wireLength == 0); + } else { + assertEquals(0, wireLength); + } + } + private List list(T first, T... remaining) { List list = new ArrayList(); list.add(first); -- cgit v1.2.3