|  | @@ -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('"');
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  |  }
 |