|
|
|
@ -47,20 +47,22 @@ public final class MessageNanoPrinter { |
|
|
|
|
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, |
|
|
|
|
* groups, and messages.</p> |
|
|
|
|
*/ |
|
|
|
|
public static <T extends MessageNano> String print(T message) { |
|
|
|
|
if (message == null) { |
|
|
|
|
return "null"; |
|
|
|
|
return ""; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
StringBuffer buf = new StringBuffer(); |
|
|
|
|
try { |
|
|
|
|
print(message.getClass().getSimpleName(), message.getClass(), message, |
|
|
|
|
new StringBuffer(), buf); |
|
|
|
|
print(null, message.getClass(), message, new StringBuffer(), buf); |
|
|
|
|
} catch (IllegalAccessException e) { |
|
|
|
|
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. |
|
|
|
|
* 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, |
|
|
|
|
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()) { |
|
|
|
|
// Proto fields are public, non-static variables that do not begin or end with '_'
|
|
|
|
|
int modifiers = field.getModifiers(); |
|
|
|
@ -115,15 +126,19 @@ public final class MessageNanoPrinter { |
|
|
|
|
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 { |
|
|
|
|
// Primitive value
|
|
|
|
|
// Non-null primitive value
|
|
|
|
|
identifier = deCamelCaseify(identifier); |
|
|
|
|
buf.append(indentBuf).append(identifier).append(": "); |
|
|
|
|
if (message instanceof String) { |
|
|
|
|
String stringMessage = sanitizeString((String) message); |
|
|
|
|
buf.append("\"").append(stringMessage).append("\""); |
|
|
|
|
} else if (message instanceof byte[]) { |
|
|
|
|
appendQuotedBytes((byte[]) message, buf); |
|
|
|
|
} else { |
|
|
|
|
buf.append(message); |
|
|
|
|
} |
|
|
|
@ -176,4 +191,27 @@ public final class MessageNanoPrinter { |
|
|
|
|
} |
|
|
|
|
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('"'); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|