am a896c1d8: am e7741c06: Merge "Fix MessageNanoPrinter for accessors"

* commit 'a896c1d8699a3a14aae94b7067339ebc5cccc794':
  Fix MessageNanoPrinter for accessors
pull/91/head
Andrew Flynn 11 years ago committed by Android Git Automerger
commit 76252d76c0
  1. 3
      java/src/main/java/com/google/protobuf/nano/MessageNano.java
  2. 85
      java/src/main/java/com/google/protobuf/nano/MessageNanoPrinter.java
  3. 39
      java/src/test/java/com/google/protobuf/NanoTest.java

@ -142,7 +142,8 @@ public abstract class MessageNano {
* Returns a string that is (mostly) compatible with ProtoBuffer's TextFormat. Note that groups
* (which are deprecated) are not serialized with the correct field name.
*
* <p>This is implemented using reflection, so it is not especially fast.
* <p>This is implemented using reflection, so it is not especially fast nor is it guaranteed
* to find all fields if you have method removal turned on for proguard.
*/
@Override
public String toString() {

@ -32,6 +32,8 @@ package com.google.protobuf.nano;
import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
/**
@ -65,6 +67,8 @@ public final class MessageNanoPrinter {
print(null, message, new StringBuffer(), buf);
} catch (IllegalAccessException e) {
return "Error printing proto: " + e.getMessage();
} catch (InvocationTargetException e) {
return "Error printing proto: " + e.getMessage();
}
return buf.toString();
}
@ -81,7 +85,8 @@ public final class MessageNanoPrinter {
* @param buf the output buffer.
*/
private static void print(String identifier, Object object,
StringBuffer indentBuf, StringBuffer buf) throws IllegalAccessException {
StringBuffer indentBuf, StringBuffer buf) throws IllegalAccessException,
InvocationTargetException {
if (object == null) {
// This can happen if...
// - we're about to print a message, String, or byte[], but it not present;
@ -94,35 +99,71 @@ public final class MessageNanoPrinter {
buf.append(indentBuf).append(deCamelCaseify(identifier)).append(" <\n");
indentBuf.append(INDENT);
}
Class<?> clazz = object.getClass();
for (Field field : object.getClass().getFields()) {
// Proto fields are public, non-static variables that do not begin or end with '_'
// Proto fields follow one of two formats:
//
// 1) Public, non-static variables that do not begin or end with '_'
// Find and print these using declared public fields
for (Field field : clazz.getFields()) {
int modifiers = field.getModifiers();
String fieldName = field.getName();
if ((modifiers & Modifier.PUBLIC) != Modifier.PUBLIC
|| (modifiers & Modifier.STATIC) == Modifier.STATIC
|| fieldName.startsWith("_") || fieldName.endsWith("_")) {
continue;
}
Class<?> fieldType = field.getType();
Object value = field.get(object);
if ((modifiers & Modifier.PUBLIC) == Modifier.PUBLIC
&& (modifiers & Modifier.STATIC) != Modifier.STATIC
&& !fieldName.startsWith("_")
&& !fieldName.endsWith("_")) {
Class<?> fieldType = field.getType();
Object value = field.get(object);
if (fieldType.isArray()) {
Class<?> arrayType = fieldType.getComponentType();
if (fieldType.isArray()) {
Class<?> arrayType = fieldType.getComponentType();
// bytes is special since it's not repeated, but is represented by an array
if (arrayType == byte.class) {
print(fieldName, value, indentBuf, buf);
} else {
int len = value == null ? 0 : Array.getLength(value);
for (int i = 0; i < len; i++) {
Object elem = Array.get(value, i);
print(fieldName, elem, indentBuf, buf);
// bytes is special since it's not repeated, but is represented by an array
if (arrayType == byte.class) {
print(fieldName, value, indentBuf, buf);
} else {
int len = value == null ? 0 : Array.getLength(value);
for (int i = 0; i < len; i++) {
Object elem = Array.get(value, i);
print(fieldName, elem, indentBuf, buf);
}
}
} else {
print(fieldName, value, indentBuf, buf);
}
} else {
print(fieldName, value, indentBuf, buf);
}
}
// 2) Fields that are accessed via getter methods (when accessors
// mode is turned on)
// Find and print these using getter methods.
for (Method method : clazz.getMethods()) {
String name = method.getName();
// Check for the setter accessor method since getters and hazzers both have
// non-proto-field name collisions (hashCode() and getSerializedSize())
if (name.startsWith("set")) {
String subfieldName = name.substring(3);
Method hazzer = null;
try {
hazzer = clazz.getMethod("has" + subfieldName);
} catch (NoSuchMethodException e) {
continue;
}
// If hazzer does't exist or returns false, no need to continue
if (!(Boolean) hazzer.invoke(object)) {
continue;
}
Method getter = null;
try {
getter = clazz.getMethod("get" + subfieldName);
} catch (NoSuchMethodException e) {
continue;
}
print(subfieldName, getter.invoke(object), indentBuf, buf);
}
}
if (identifier != null) {

@ -2553,8 +2553,7 @@ public class NanoTest extends TestCase {
assertTrue(protoPrint.contains("optional_bytes: \"\\\"\\000\\001\\010\""));
assertTrue(protoPrint.contains("optional_group <\n a: 15\n>"));
assertTrue(protoPrint.contains("repeated_int64: 1"));
assertTrue(protoPrint.contains("repeated_int64: -1"));
assertTrue(protoPrint.contains("repeated_int64: 1\nrepeated_int64: -1"));
assertFalse(protoPrint.contains("repeated_bytes: \"\"")); // null should be dropped
assertTrue(protoPrint.contains("repeated_bytes: \"hello\""));
assertTrue(protoPrint.contains("repeated_group <\n a: -27\n>\n"
@ -2570,6 +2569,42 @@ public class NanoTest extends TestCase {
assertTrue(protoPrint.contains("repeated_string_piece: \"world\""));
}
public void testMessageNanoPrinterAccessors() throws Exception {
TestNanoAccessors msg = new TestNanoAccessors();
msg.setOptionalInt32(13);
msg.setOptionalString("foo");
msg.setOptionalBytes(new byte[] {'"', '\0', 1, 8});
msg.optionalNestedMessage = new TestNanoAccessors.NestedMessage();
msg.optionalNestedMessage.setBb(7);
msg.setOptionalNestedEnum(TestNanoAccessors.BAZ);
msg.repeatedInt32 = new int[] { 1, -1 };
msg.repeatedString = new String[] { "Hello", "world" };
msg.repeatedBytes = new byte[2][];
msg.repeatedBytes[1] = new byte[] {'h', 'e', 'l', 'l', 'o'};
msg.repeatedNestedMessage = new TestNanoAccessors.NestedMessage[2];
msg.repeatedNestedMessage[0] = new TestNanoAccessors.NestedMessage();
msg.repeatedNestedMessage[0].setBb(5);
msg.repeatedNestedMessage[1] = new TestNanoAccessors.NestedMessage();
msg.repeatedNestedMessage[1].setBb(6);
msg.repeatedNestedEnum = new int[] { TestNanoAccessors.FOO, TestNanoAccessors.BAR };
msg.id = 33;
String protoPrint = msg.toString();
assertTrue(protoPrint.contains("optional_int32: 13"));
assertTrue(protoPrint.contains("optional_string: \"foo\""));
assertTrue(protoPrint.contains("optional_bytes: \"\\\"\\000\\001\\010\""));
assertTrue(protoPrint.contains("optional_nested_message <\n bb: 7\n>"));
assertTrue(protoPrint.contains("optional_nested_enum: 3"));
assertTrue(protoPrint.contains("repeated_int32: 1\nrepeated_int32: -1"));
assertTrue(protoPrint.contains("repeated_string: \"Hello\"\nrepeated_string: \"world\""));
assertFalse(protoPrint.contains("repeated_bytes: \"\"")); // null should be dropped
assertTrue(protoPrint.contains("repeated_bytes: \"hello\""));
assertTrue(protoPrint.contains("repeated_nested_message <\n bb: 5\n>\n"
+ "repeated_nested_message <\n bb: 6\n>"));
assertTrue(protoPrint.contains("repeated_nested_enum: 1\nrepeated_nested_enum: 2"));
assertTrue(protoPrint.contains("id: 33"));
}
public void testExtensions() throws Exception {
Extensions.ExtendableMessage message = new Extensions.ExtendableMessage();
message.field = 5;

Loading…
Cancel
Save