|
@@ -47,20 +47,22 @@ public final class MessageNanoPrinter {
|
|
private static final int MAX_STRING_LEN = 200;
|
|
private static final int MAX_STRING_LEN = 200;
|
|
|
|
|
|
/**
|
|
/**
|
|
- * Returns an text representation of a MessageNano suitable for debugging.
|
|
|
|
|
|
+ * Returns an text representation of a MessageNano suitable for debugging. The returned string
|
|
|
|
+ * is mostly compatible with Protocol Buffer's TextFormat (as provided by non-nano protocol
|
|
|
|
+ * buffers) -- groups (which are deprecated) are output with an underscore name (e.g. foo_bar
|
|
|
|
+ * instead of FooBar) and will thus not parse.
|
|
*
|
|
*
|
|
* <p>Employs Java reflection on the given object and recursively prints primitive fields,
|
|
* <p>Employs Java reflection on the given object and recursively prints primitive fields,
|
|
* groups, and messages.</p>
|
|
* groups, and messages.</p>
|
|
*/
|
|
*/
|
|
public static <T extends MessageNano> String print(T message) {
|
|
public static <T extends MessageNano> String print(T message) {
|
|
if (message == null) {
|
|
if (message == null) {
|
|
- return "null";
|
|
|
|
|
|
+ return "";
|
|
}
|
|
}
|
|
|
|
|
|
StringBuffer buf = new StringBuffer();
|
|
StringBuffer buf = new StringBuffer();
|
|
try {
|
|
try {
|
|
- print(message.getClass().getSimpleName(), message.getClass(), message,
|
|
|
|
- new StringBuffer(), buf);
|
|
|
|
|
|
+ print(null, message.getClass(), message, new StringBuffer(), buf);
|
|
} catch (IllegalAccessException e) {
|
|
} catch (IllegalAccessException e) {
|
|
return "Error printing proto: " + e.getMessage();
|
|
return "Error printing proto: " + e.getMessage();
|
|
}
|
|
}
|
|
@@ -70,21 +72,30 @@ public final class MessageNanoPrinter {
|
|
/**
|
|
/**
|
|
* Function that will print the given message/class into the StringBuffer.
|
|
* Function that will print the given message/class into the StringBuffer.
|
|
* Meant to be called recursively.
|
|
* Meant to be called recursively.
|
|
|
|
+ *
|
|
|
|
+ * @param identifier the identifier to use, or {@code null} if this is the root message to
|
|
|
|
+ * print.
|
|
|
|
+ * @param clazz the class of {@code message}.
|
|
|
|
+ * @param message the value to print. May in fact be a primitive value or byte array and not a
|
|
|
|
+ * message.
|
|
|
|
+ * @param indentBuf the indentation each line should begin with.
|
|
|
|
+ * @param buf the output buffer.
|
|
*/
|
|
*/
|
|
private static void print(String identifier, Class<?> clazz, Object message,
|
|
private static void print(String identifier, Class<?> clazz, Object message,
|
|
StringBuffer indentBuf, StringBuffer buf) throws IllegalAccessException {
|
|
StringBuffer indentBuf, StringBuffer buf) throws IllegalAccessException {
|
|
- if (MessageNano.class.isAssignableFrom(clazz)) {
|
|
|
|
- // Nano proto message
|
|
|
|
- buf.append(indentBuf).append(identifier);
|
|
|
|
-
|
|
|
|
- // If null, just print it and return
|
|
|
|
- if (message == null) {
|
|
|
|
- buf.append(": ").append(message).append("\n");
|
|
|
|
- return;
|
|
|
|
|
|
+ if (message == null) {
|
|
|
|
+ // This can happen if...
|
|
|
|
+ // - we're about to print a message, String, or byte[], but it not present;
|
|
|
|
+ // - we're about to print a primitive, but "reftype" optional style is enabled, and
|
|
|
|
+ // the field is unset.
|
|
|
|
+ // In both cases the appropriate behavior is to output nothing.
|
|
|
|
+ } else if (MessageNano.class.isAssignableFrom(clazz)) { // Nano proto message
|
|
|
|
+ int origIndentBufLength = indentBuf.length();
|
|
|
|
+ if (identifier != null) {
|
|
|
|
+ buf.append(indentBuf).append(deCamelCaseify(identifier)).append(" <\n");
|
|
|
|
+ indentBuf.append(INDENT);
|
|
}
|
|
}
|
|
|
|
|
|
- indentBuf.append(INDENT);
|
|
|
|
- buf.append(" <\n");
|
|
|
|
for (Field field : clazz.getFields()) {
|
|
for (Field field : clazz.getFields()) {
|
|
// Proto fields are public, non-static variables that do not begin or end with '_'
|
|
// Proto fields are public, non-static variables that do not begin or end with '_'
|
|
int modifiers = field.getModifiers();
|
|
int modifiers = field.getModifiers();
|
|
@@ -115,15 +126,19 @@ public final class MessageNanoPrinter {
|
|
print(fieldName, fieldType, value, indentBuf, buf);
|
|
print(fieldName, fieldType, value, indentBuf, buf);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
- indentBuf.delete(indentBuf.length() - INDENT.length(), indentBuf.length());
|
|
|
|
- buf.append(indentBuf).append(">\n");
|
|
|
|
|
|
+ if (identifier != null) {
|
|
|
|
+ indentBuf.setLength(origIndentBufLength);
|
|
|
|
+ buf.append(indentBuf).append(">\n");
|
|
|
|
+ }
|
|
} else {
|
|
} else {
|
|
- // Primitive value
|
|
|
|
|
|
+ // Non-null primitive value
|
|
identifier = deCamelCaseify(identifier);
|
|
identifier = deCamelCaseify(identifier);
|
|
buf.append(indentBuf).append(identifier).append(": ");
|
|
buf.append(indentBuf).append(identifier).append(": ");
|
|
if (message instanceof String) {
|
|
if (message instanceof String) {
|
|
String stringMessage = sanitizeString((String) message);
|
|
String stringMessage = sanitizeString((String) message);
|
|
buf.append("\"").append(stringMessage).append("\"");
|
|
buf.append("\"").append(stringMessage).append("\"");
|
|
|
|
+ } else if (message instanceof byte[]) {
|
|
|
|
+ appendQuotedBytes((byte[]) message, buf);
|
|
} else {
|
|
} else {
|
|
buf.append(message);
|
|
buf.append(message);
|
|
}
|
|
}
|
|
@@ -176,4 +191,27 @@ public final class MessageNanoPrinter {
|
|
}
|
|
}
|
|
return b.toString();
|
|
return b.toString();
|
|
}
|
|
}
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * Appends a quoted byte array to the provided {@code StringBuffer}.
|
|
|
|
+ */
|
|
|
|
+ private static void appendQuotedBytes(byte[] bytes, StringBuffer builder) {
|
|
|
|
+ if (bytes == null) {
|
|
|
|
+ builder.append("\"\"");
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ builder.append('"');
|
|
|
|
+ for (int i = 0; i < bytes.length; ++i) {
|
|
|
|
+ int ch = bytes[i];
|
|
|
|
+ if (ch == '\\' || ch == '"') {
|
|
|
|
+ builder.append('\\').append((char) ch);
|
|
|
|
+ } else if (ch >= 32 && ch < 127) {
|
|
|
|
+ builder.append((char) ch);
|
|
|
|
+ } else {
|
|
|
|
+ builder.append(String.format("\\%03o", ch));
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ builder.append('"');
|
|
|
|
+ }
|
|
}
|
|
}
|