aboutsummaryrefslogtreecommitdiffhomepage
path: root/java/core/src/main/java/com/google/protobuf/MessageLiteToString.java
diff options
context:
space:
mode:
authorGravatar Jisi Liu <jisi.liu@gmail.com>2016-04-28 14:34:59 -0700
committerGravatar Jisi Liu <jisi.liu@gmail.com>2016-04-28 14:34:59 -0700
commitcf14183bcd5485b4a71541599ddce0b35eb71352 (patch)
tree12f6e5eb731d7a70cdac4cdafc8b3131629413e2 /java/core/src/main/java/com/google/protobuf/MessageLiteToString.java
parentf00300d7f04f1c38a7d70e271f9232b94dd0e326 (diff)
Down integrate from Google internal.
Diffstat (limited to 'java/core/src/main/java/com/google/protobuf/MessageLiteToString.java')
-rw-r--r--java/core/src/main/java/com/google/protobuf/MessageLiteToString.java151
1 files changed, 95 insertions, 56 deletions
diff --git a/java/core/src/main/java/com/google/protobuf/MessageLiteToString.java b/java/core/src/main/java/com/google/protobuf/MessageLiteToString.java
index 2a6e0e30..43847651 100644
--- a/java/core/src/main/java/com/google/protobuf/MessageLiteToString.java
+++ b/java/core/src/main/java/com/google/protobuf/MessageLiteToString.java
@@ -30,23 +30,24 @@
package com.google.protobuf;
-import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
+import java.util.Set;
+import java.util.TreeSet;
/**
* Helps generate {@link String} representations of {@link MessageLite} protos.
*/
+// TODO(dweis): Fix map fields.
final class MessageLiteToString {
- /**
- * Suffix for *_FIELD_NUMBER fields. This is used to reflectively detect proto fields that should
- * be toString()ed.
- */
- private static final String FIELD_NUMBER_NAME_SUFFIX = "_FIELD_NUMBER";
+ private static final String LIST_SUFFIX = "List";
+ private static final String BUILDER_LIST_SUFFIX = "OrBuilderList";
+ private static final String BYTES_SUFFIX = "Bytes";
+
/**
* Returns a {@link String} representation of the {@link MessageLite} object. The first line of
* the {@code String} representation representation includes a comment string to uniquely identify
@@ -73,52 +74,70 @@ final class MessageLiteToString {
// Build a map of method name to method. We're looking for methods like getFoo(), hasFoo(), and
// getFooList() which might be useful for building an object's string representation.
Map<String, Method> nameToNoArgMethod = new HashMap<String, Method>();
+ Map<String, Method> nameToMethod = new HashMap<String, Method>();
+ Set<String> getters = new TreeSet<String>();
for (Method method : messageLite.getClass().getDeclaredMethods()) {
+ nameToMethod.put(method.getName(), method);
if (method.getParameterTypes().length == 0) {
nameToNoArgMethod.put(method.getName(), method);
+
+ if (method.getName().startsWith("get")) {
+ getters.add(method.getName());
+ }
}
}
- for (Field field : messageLite.getClass().getDeclaredFields()) {
- String fieldName = field.getName();
- // Skip all fields that aren't in a format like "FOO_BAR_FIELD_NUMBER"
- if (!fieldName.endsWith(FIELD_NUMBER_NAME_SUFFIX)) {
- continue;
+ for (String getter : getters) {
+ String suffix = getter.replaceFirst("get", "");
+ if (suffix.endsWith(LIST_SUFFIX) && !suffix.endsWith(BUILDER_LIST_SUFFIX)) {
+ String camelCase = suffix.substring(0, 1).toLowerCase()
+ + suffix.substring(1, suffix.length() - LIST_SUFFIX.length());
+ // Try to reflectively get the value and toString() the field as if it were repeated. This
+ // only works if the method names have not be proguarded out or renamed.
+ Method listMethod = nameToNoArgMethod.get("get" + suffix);
+ if (listMethod != null) {
+ printField(
+ buffer,
+ indent,
+ camelCaseToSnakeCase(camelCase),
+ GeneratedMessageLite.invokeOrDie(listMethod, messageLite));
+ continue;
+ }
}
- // For "FOO_BAR_FIELD_NUMBER" his would be "FOO_BAR"
- String upperUnderscore =
- fieldName.substring(0, fieldName.length() - FIELD_NUMBER_NAME_SUFFIX.length());
-
- // For "FOO_BAR_FIELD_NUMBER" his would be "FooBar"
- String upperCamelCaseName = upperUnderscoreToUpperCamel(upperUnderscore);
+ Method setter = nameToMethod.get("set" + suffix);
+ if (setter == null) {
+ continue;
+ }
+ if (suffix.endsWith(BYTES_SUFFIX)
+ && nameToNoArgMethod.containsKey(
+ "get" + suffix.substring(0, suffix.length() - "Bytes".length()))) {
+ // Heuristic to skip bytes based accessors for string fields.
+ continue;
+ }
+
+ String camelCase = suffix.substring(0, 1).toLowerCase() + suffix.substring(1);
// Try to reflectively get the value and toString() the field as if it were optional. This
// only works if the method names have not be proguarded out or renamed.
- Method getMethod = nameToNoArgMethod.get("get" + upperCamelCaseName);
- Method hasMethod = nameToNoArgMethod.get("has" + upperCamelCaseName);
- if (getMethod != null && hasMethod != null) {
- if ((Boolean) GeneratedMessageLite.invokeOrDie(hasMethod, messageLite)) {
+ Method getMethod = nameToNoArgMethod.get("get" + suffix);
+ Method hasMethod = nameToNoArgMethod.get("has" + suffix);
+ // TODO(dweis): Fix proto3 semantics.
+ if (getMethod != null) {
+ Object value = GeneratedMessageLite.invokeOrDie(getMethod, messageLite);
+ final boolean hasValue = hasMethod == null
+ ? !isDefaultValue(value)
+ : (Boolean) GeneratedMessageLite.invokeOrDie(hasMethod, messageLite);
+ // TODO(dweis): This doesn't stop printing oneof case twice: value and enum style.
+ if (hasValue) {
printField(
buffer,
indent,
- upperUnderscore.toLowerCase(),
- GeneratedMessageLite.invokeOrDie(getMethod, messageLite));
+ camelCaseToSnakeCase(camelCase),
+ value);
}
continue;
}
-
- // Try to reflectively get the value and toString() the field as if it were repeated. This
- // only works if the method names have not be proguarded out or renamed.
- Method listMethod = nameToNoArgMethod.get("get" + upperCamelCaseName + "List");
- if (listMethod != null) {
- printField(
- buffer,
- indent,
- upperUnderscore.toLowerCase(),
- GeneratedMessageLite.invokeOrDie(listMethod, messageLite));
- continue;
- }
}
if (messageLite instanceof GeneratedMessageLite.ExtendableMessage) {
@@ -130,10 +149,39 @@ final class MessageLiteToString {
}
}
- if (((GeneratedMessageLite) messageLite).unknownFields != null) {
- ((GeneratedMessageLite) messageLite).unknownFields.printWithIndent(buffer, indent);
+ if (((GeneratedMessageLite<?, ?>) messageLite).unknownFields != null) {
+ ((GeneratedMessageLite<?, ?>) messageLite).unknownFields.printWithIndent(buffer, indent);
}
}
+
+ private static boolean isDefaultValue(Object o) {
+ if (o instanceof Boolean) {
+ return !((Boolean) o);
+ }
+ if (o instanceof Integer) {
+ return ((Integer) o) == 0;
+ }
+ if (o instanceof Float) {
+ return ((Float) o) == 0f;
+ }
+ if (o instanceof Double) {
+ return ((Double) o) == 0d;
+ }
+ if (o instanceof String) {
+ return o.equals("");
+ }
+ if (o instanceof ByteString) {
+ return o.equals(ByteString.EMPTY);
+ }
+ if (o instanceof MessageLite) { // Can happen in oneofs.
+ return o == ((MessageLite) o).getDefaultInstanceForType();
+ }
+ if (o instanceof java.lang.Enum<?>) { // Catches oneof enums.
+ return ((java.lang.Enum<?>) o).ordinal() == 0;
+ }
+
+ return false;
+ }
/**
* Formats a text proto field.
@@ -166,7 +214,7 @@ final class MessageLiteToString {
buffer.append(": \"").append(TextFormatEscaper.escapeBytes((ByteString) object)).append('"');
} else if (object instanceof GeneratedMessageLite) {
buffer.append(" {");
- reflectivePrintWithIndent((GeneratedMessageLite) object, buffer, indent + 2);
+ reflectivePrintWithIndent((GeneratedMessageLite<?, ?>) object, buffer, indent + 2);
buffer.append("\n");
for (int i = 0; i < indent; i++) {
buffer.append(' ');
@@ -176,25 +224,16 @@ final class MessageLiteToString {
buffer.append(": ").append(object.toString());
}
}
-
- /**
- * A Guava-less implementation of:
- * {@code CaseFormat.UPPER_UNDERSCORE.to(CaseFormat.UPPER_CAMEL, upperUnderscore)}
- */
- private static String upperUnderscoreToUpperCamel(String upperUnderscore) {
- String upperCamelCaseName = "";
- boolean nextCharacterShouldBeUpper = true;
- for (int i = 0; i < upperUnderscore.length(); i++) {
- char ch = upperUnderscore.charAt(i);
- if (ch == '_') {
- nextCharacterShouldBeUpper = true;
- } else if (nextCharacterShouldBeUpper){
- upperCamelCaseName += Character.toUpperCase(ch);
- nextCharacterShouldBeUpper = false;
- } else {
- upperCamelCaseName += Character.toLowerCase(ch);
+
+ private static final String camelCaseToSnakeCase(String camelCase) {
+ StringBuilder builder = new StringBuilder();
+ for (int i = 0; i < camelCase.length(); i++) {
+ char ch = camelCase.charAt(i);
+ if (Character.isUpperCase(ch)) {
+ builder.append("_");
}
+ builder.append(Character.toLowerCase(ch));
}
- return upperCamelCaseName;
+ return builder.toString();
}
}