147107914acbad70ff4db1664d3664ccc994315afAndrew Flynn// Protocol Buffers - Google's data interchange format 247107914acbad70ff4db1664d3664ccc994315afAndrew Flynn// Copyright 2013 Google Inc. All rights reserved. 347107914acbad70ff4db1664d3664ccc994315afAndrew Flynn// http://code.google.com/p/protobuf/ 447107914acbad70ff4db1664d3664ccc994315afAndrew Flynn// 547107914acbad70ff4db1664d3664ccc994315afAndrew Flynn// Redistribution and use in source and binary forms, with or without 647107914acbad70ff4db1664d3664ccc994315afAndrew Flynn// modification, are permitted provided that the following conditions are 747107914acbad70ff4db1664d3664ccc994315afAndrew Flynn// met: 847107914acbad70ff4db1664d3664ccc994315afAndrew Flynn// 947107914acbad70ff4db1664d3664ccc994315afAndrew Flynn// * Redistributions of source code must retain the above copyright 1047107914acbad70ff4db1664d3664ccc994315afAndrew Flynn// notice, this list of conditions and the following disclaimer. 1147107914acbad70ff4db1664d3664ccc994315afAndrew Flynn// * Redistributions in binary form must reproduce the above 1247107914acbad70ff4db1664d3664ccc994315afAndrew Flynn// copyright notice, this list of conditions and the following disclaimer 1347107914acbad70ff4db1664d3664ccc994315afAndrew Flynn// in the documentation and/or other materials provided with the 1447107914acbad70ff4db1664d3664ccc994315afAndrew Flynn// distribution. 1547107914acbad70ff4db1664d3664ccc994315afAndrew Flynn// * Neither the name of Google Inc. nor the names of its 1647107914acbad70ff4db1664d3664ccc994315afAndrew Flynn// contributors may be used to endorse or promote products derived from 1747107914acbad70ff4db1664d3664ccc994315afAndrew Flynn// this software without specific prior written permission. 1847107914acbad70ff4db1664d3664ccc994315afAndrew Flynn// 1947107914acbad70ff4db1664d3664ccc994315afAndrew Flynn// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 2047107914acbad70ff4db1664d3664ccc994315afAndrew Flynn// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 2147107914acbad70ff4db1664d3664ccc994315afAndrew Flynn// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 2247107914acbad70ff4db1664d3664ccc994315afAndrew Flynn// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 2347107914acbad70ff4db1664d3664ccc994315afAndrew Flynn// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 2447107914acbad70ff4db1664d3664ccc994315afAndrew Flynn// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 2547107914acbad70ff4db1664d3664ccc994315afAndrew Flynn// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 2647107914acbad70ff4db1664d3664ccc994315afAndrew Flynn// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 2747107914acbad70ff4db1664d3664ccc994315afAndrew Flynn// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 2847107914acbad70ff4db1664d3664ccc994315afAndrew Flynn// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 2947107914acbad70ff4db1664d3664ccc994315afAndrew Flynn// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 3047107914acbad70ff4db1664d3664ccc994315afAndrew Flynn 3147107914acbad70ff4db1664d3664ccc994315afAndrew Flynnpackage com.google.protobuf.nano; 3247107914acbad70ff4db1664d3664ccc994315afAndrew Flynn 3347107914acbad70ff4db1664d3664ccc994315afAndrew Flynnimport java.lang.reflect.Array; 3447107914acbad70ff4db1664d3664ccc994315afAndrew Flynnimport java.lang.reflect.Field; 356b594c228e7954eb98353ad8e242b83fb255a277Andrew Flynnimport java.lang.reflect.InvocationTargetException; 366b594c228e7954eb98353ad8e242b83fb255a277Andrew Flynnimport java.lang.reflect.Method; 3747107914acbad70ff4db1664d3664ccc994315afAndrew Flynnimport java.lang.reflect.Modifier; 3847107914acbad70ff4db1664d3664ccc994315afAndrew Flynn 3947107914acbad70ff4db1664d3664ccc994315afAndrew Flynn/** 4047107914acbad70ff4db1664d3664ccc994315afAndrew Flynn * Static helper methods for printing nano protos. 4147107914acbad70ff4db1664d3664ccc994315afAndrew Flynn * 4247107914acbad70ff4db1664d3664ccc994315afAndrew Flynn * @author flynn@google.com Andrew Flynn 4347107914acbad70ff4db1664d3664ccc994315afAndrew Flynn */ 4447107914acbad70ff4db1664d3664ccc994315afAndrew Flynnpublic final class MessageNanoPrinter { 4547107914acbad70ff4db1664d3664ccc994315afAndrew Flynn // Do not allow instantiation 4647107914acbad70ff4db1664d3664ccc994315afAndrew Flynn private MessageNanoPrinter() {} 4747107914acbad70ff4db1664d3664ccc994315afAndrew Flynn 4847107914acbad70ff4db1664d3664ccc994315afAndrew Flynn private static final String INDENT = " "; 4947107914acbad70ff4db1664d3664ccc994315afAndrew Flynn private static final int MAX_STRING_LEN = 200; 5047107914acbad70ff4db1664d3664ccc994315afAndrew Flynn 5147107914acbad70ff4db1664d3664ccc994315afAndrew Flynn /** 5262a22a732fb134e5f34dd3e01920933ca5b16346Nicholas Seckar * Returns an text representation of a MessageNano suitable for debugging. The returned string 5362a22a732fb134e5f34dd3e01920933ca5b16346Nicholas Seckar * is mostly compatible with Protocol Buffer's TextFormat (as provided by non-nano protocol 5462a22a732fb134e5f34dd3e01920933ca5b16346Nicholas Seckar * buffers) -- groups (which are deprecated) are output with an underscore name (e.g. foo_bar 5562a22a732fb134e5f34dd3e01920933ca5b16346Nicholas Seckar * instead of FooBar) and will thus not parse. 5647107914acbad70ff4db1664d3664ccc994315afAndrew Flynn * 5747107914acbad70ff4db1664d3664ccc994315afAndrew Flynn * <p>Employs Java reflection on the given object and recursively prints primitive fields, 5847107914acbad70ff4db1664d3664ccc994315afAndrew Flynn * groups, and messages.</p> 5947107914acbad70ff4db1664d3664ccc994315afAndrew Flynn */ 6047107914acbad70ff4db1664d3664ccc994315afAndrew Flynn public static <T extends MessageNano> String print(T message) { 6147107914acbad70ff4db1664d3664ccc994315afAndrew Flynn if (message == null) { 6262a22a732fb134e5f34dd3e01920933ca5b16346Nicholas Seckar return ""; 6347107914acbad70ff4db1664d3664ccc994315afAndrew Flynn } 6447107914acbad70ff4db1664d3664ccc994315afAndrew Flynn 6547107914acbad70ff4db1664d3664ccc994315afAndrew Flynn StringBuffer buf = new StringBuffer(); 6647107914acbad70ff4db1664d3664ccc994315afAndrew Flynn try { 675cc242074f189837b38e7768b57ccfb0bca258dfMax Cai print(null, message, new StringBuffer(), buf); 6847107914acbad70ff4db1664d3664ccc994315afAndrew Flynn } catch (IllegalAccessException e) { 6947107914acbad70ff4db1664d3664ccc994315afAndrew Flynn return "Error printing proto: " + e.getMessage(); 706b594c228e7954eb98353ad8e242b83fb255a277Andrew Flynn } catch (InvocationTargetException e) { 716b594c228e7954eb98353ad8e242b83fb255a277Andrew Flynn return "Error printing proto: " + e.getMessage(); 7247107914acbad70ff4db1664d3664ccc994315afAndrew Flynn } 7347107914acbad70ff4db1664d3664ccc994315afAndrew Flynn return buf.toString(); 7447107914acbad70ff4db1664d3664ccc994315afAndrew Flynn } 7547107914acbad70ff4db1664d3664ccc994315afAndrew Flynn 7647107914acbad70ff4db1664d3664ccc994315afAndrew Flynn /** 775cc242074f189837b38e7768b57ccfb0bca258dfMax Cai * Function that will print the given message/field into the StringBuffer. 7847107914acbad70ff4db1664d3664ccc994315afAndrew Flynn * Meant to be called recursively. 7962a22a732fb134e5f34dd3e01920933ca5b16346Nicholas Seckar * 8062a22a732fb134e5f34dd3e01920933ca5b16346Nicholas Seckar * @param identifier the identifier to use, or {@code null} if this is the root message to 8162a22a732fb134e5f34dd3e01920933ca5b16346Nicholas Seckar * print. 825cc242074f189837b38e7768b57ccfb0bca258dfMax Cai * @param object the value to print. May in fact be a primitive value or byte array and not a 8362a22a732fb134e5f34dd3e01920933ca5b16346Nicholas Seckar * message. 8462a22a732fb134e5f34dd3e01920933ca5b16346Nicholas Seckar * @param indentBuf the indentation each line should begin with. 8562a22a732fb134e5f34dd3e01920933ca5b16346Nicholas Seckar * @param buf the output buffer. 8647107914acbad70ff4db1664d3664ccc994315afAndrew Flynn */ 875cc242074f189837b38e7768b57ccfb0bca258dfMax Cai private static void print(String identifier, Object object, 886b594c228e7954eb98353ad8e242b83fb255a277Andrew Flynn StringBuffer indentBuf, StringBuffer buf) throws IllegalAccessException, 896b594c228e7954eb98353ad8e242b83fb255a277Andrew Flynn InvocationTargetException { 905cc242074f189837b38e7768b57ccfb0bca258dfMax Cai if (object == null) { 9162a22a732fb134e5f34dd3e01920933ca5b16346Nicholas Seckar // This can happen if... 9262a22a732fb134e5f34dd3e01920933ca5b16346Nicholas Seckar // - we're about to print a message, String, or byte[], but it not present; 9362a22a732fb134e5f34dd3e01920933ca5b16346Nicholas Seckar // - we're about to print a primitive, but "reftype" optional style is enabled, and 9462a22a732fb134e5f34dd3e01920933ca5b16346Nicholas Seckar // the field is unset. 9562a22a732fb134e5f34dd3e01920933ca5b16346Nicholas Seckar // In both cases the appropriate behavior is to output nothing. 965cc242074f189837b38e7768b57ccfb0bca258dfMax Cai } else if (object instanceof MessageNano) { // Nano proto message 9762a22a732fb134e5f34dd3e01920933ca5b16346Nicholas Seckar int origIndentBufLength = indentBuf.length(); 9862a22a732fb134e5f34dd3e01920933ca5b16346Nicholas Seckar if (identifier != null) { 9962a22a732fb134e5f34dd3e01920933ca5b16346Nicholas Seckar buf.append(indentBuf).append(deCamelCaseify(identifier)).append(" <\n"); 10062a22a732fb134e5f34dd3e01920933ca5b16346Nicholas Seckar indentBuf.append(INDENT); 10147107914acbad70ff4db1664d3664ccc994315afAndrew Flynn } 1026b594c228e7954eb98353ad8e242b83fb255a277Andrew Flynn Class<?> clazz = object.getClass(); 10347107914acbad70ff4db1664d3664ccc994315afAndrew Flynn 1046b594c228e7954eb98353ad8e242b83fb255a277Andrew Flynn // Proto fields follow one of two formats: 1056b594c228e7954eb98353ad8e242b83fb255a277Andrew Flynn // 1066b594c228e7954eb98353ad8e242b83fb255a277Andrew Flynn // 1) Public, non-static variables that do not begin or end with '_' 1076b594c228e7954eb98353ad8e242b83fb255a277Andrew Flynn // Find and print these using declared public fields 1086b594c228e7954eb98353ad8e242b83fb255a277Andrew Flynn for (Field field : clazz.getFields()) { 10947107914acbad70ff4db1664d3664ccc994315afAndrew Flynn int modifiers = field.getModifiers(); 11047107914acbad70ff4db1664d3664ccc994315afAndrew Flynn String fieldName = field.getName(); 111d270ce1431d40af4caebdcac447cf762036ade1cBrian Duff if ("cachedSize".equals(fieldName)) { 112d270ce1431d40af4caebdcac447cf762036ade1cBrian Duff // TODO(bduff): perhaps cachedSize should have a more obscure name. 113d270ce1431d40af4caebdcac447cf762036ade1cBrian Duff continue; 114d270ce1431d40af4caebdcac447cf762036ade1cBrian Duff } 11547107914acbad70ff4db1664d3664ccc994315afAndrew Flynn 1166b594c228e7954eb98353ad8e242b83fb255a277Andrew Flynn if ((modifiers & Modifier.PUBLIC) == Modifier.PUBLIC 1176b594c228e7954eb98353ad8e242b83fb255a277Andrew Flynn && (modifiers & Modifier.STATIC) != Modifier.STATIC 1186b594c228e7954eb98353ad8e242b83fb255a277Andrew Flynn && !fieldName.startsWith("_") 1196b594c228e7954eb98353ad8e242b83fb255a277Andrew Flynn && !fieldName.endsWith("_")) { 1206b594c228e7954eb98353ad8e242b83fb255a277Andrew Flynn Class<?> fieldType = field.getType(); 1216b594c228e7954eb98353ad8e242b83fb255a277Andrew Flynn Object value = field.get(object); 12247107914acbad70ff4db1664d3664ccc994315afAndrew Flynn 1236b594c228e7954eb98353ad8e242b83fb255a277Andrew Flynn if (fieldType.isArray()) { 1246b594c228e7954eb98353ad8e242b83fb255a277Andrew Flynn Class<?> arrayType = fieldType.getComponentType(); 12547107914acbad70ff4db1664d3664ccc994315afAndrew Flynn 1266b594c228e7954eb98353ad8e242b83fb255a277Andrew Flynn // bytes is special since it's not repeated, but is represented by an array 1276b594c228e7954eb98353ad8e242b83fb255a277Andrew Flynn if (arrayType == byte.class) { 1286b594c228e7954eb98353ad8e242b83fb255a277Andrew Flynn print(fieldName, value, indentBuf, buf); 1296b594c228e7954eb98353ad8e242b83fb255a277Andrew Flynn } else { 1306b594c228e7954eb98353ad8e242b83fb255a277Andrew Flynn int len = value == null ? 0 : Array.getLength(value); 1316b594c228e7954eb98353ad8e242b83fb255a277Andrew Flynn for (int i = 0; i < len; i++) { 1326b594c228e7954eb98353ad8e242b83fb255a277Andrew Flynn Object elem = Array.get(value, i); 1336b594c228e7954eb98353ad8e242b83fb255a277Andrew Flynn print(fieldName, elem, indentBuf, buf); 1346b594c228e7954eb98353ad8e242b83fb255a277Andrew Flynn } 13547107914acbad70ff4db1664d3664ccc994315afAndrew Flynn } 1366b594c228e7954eb98353ad8e242b83fb255a277Andrew Flynn } else { 1376b594c228e7954eb98353ad8e242b83fb255a277Andrew Flynn print(fieldName, value, indentBuf, buf); 13847107914acbad70ff4db1664d3664ccc994315afAndrew Flynn } 1396b594c228e7954eb98353ad8e242b83fb255a277Andrew Flynn } 1406b594c228e7954eb98353ad8e242b83fb255a277Andrew Flynn } 1416b594c228e7954eb98353ad8e242b83fb255a277Andrew Flynn 1426b594c228e7954eb98353ad8e242b83fb255a277Andrew Flynn // 2) Fields that are accessed via getter methods (when accessors 1436b594c228e7954eb98353ad8e242b83fb255a277Andrew Flynn // mode is turned on) 1446b594c228e7954eb98353ad8e242b83fb255a277Andrew Flynn // Find and print these using getter methods. 1456b594c228e7954eb98353ad8e242b83fb255a277Andrew Flynn for (Method method : clazz.getMethods()) { 1466b594c228e7954eb98353ad8e242b83fb255a277Andrew Flynn String name = method.getName(); 1476b594c228e7954eb98353ad8e242b83fb255a277Andrew Flynn // Check for the setter accessor method since getters and hazzers both have 1486b594c228e7954eb98353ad8e242b83fb255a277Andrew Flynn // non-proto-field name collisions (hashCode() and getSerializedSize()) 1496b594c228e7954eb98353ad8e242b83fb255a277Andrew Flynn if (name.startsWith("set")) { 1506b594c228e7954eb98353ad8e242b83fb255a277Andrew Flynn String subfieldName = name.substring(3); 1516b594c228e7954eb98353ad8e242b83fb255a277Andrew Flynn 1526b594c228e7954eb98353ad8e242b83fb255a277Andrew Flynn Method hazzer = null; 1536b594c228e7954eb98353ad8e242b83fb255a277Andrew Flynn try { 1546b594c228e7954eb98353ad8e242b83fb255a277Andrew Flynn hazzer = clazz.getMethod("has" + subfieldName); 1556b594c228e7954eb98353ad8e242b83fb255a277Andrew Flynn } catch (NoSuchMethodException e) { 1566b594c228e7954eb98353ad8e242b83fb255a277Andrew Flynn continue; 1576b594c228e7954eb98353ad8e242b83fb255a277Andrew Flynn } 1586b594c228e7954eb98353ad8e242b83fb255a277Andrew Flynn // If hazzer does't exist or returns false, no need to continue 1596b594c228e7954eb98353ad8e242b83fb255a277Andrew Flynn if (!(Boolean) hazzer.invoke(object)) { 1606b594c228e7954eb98353ad8e242b83fb255a277Andrew Flynn continue; 1616b594c228e7954eb98353ad8e242b83fb255a277Andrew Flynn } 1626b594c228e7954eb98353ad8e242b83fb255a277Andrew Flynn 1636b594c228e7954eb98353ad8e242b83fb255a277Andrew Flynn Method getter = null; 1646b594c228e7954eb98353ad8e242b83fb255a277Andrew Flynn try { 1656b594c228e7954eb98353ad8e242b83fb255a277Andrew Flynn getter = clazz.getMethod("get" + subfieldName); 1666b594c228e7954eb98353ad8e242b83fb255a277Andrew Flynn } catch (NoSuchMethodException e) { 1676b594c228e7954eb98353ad8e242b83fb255a277Andrew Flynn continue; 1686b594c228e7954eb98353ad8e242b83fb255a277Andrew Flynn } 1696b594c228e7954eb98353ad8e242b83fb255a277Andrew Flynn 1706b594c228e7954eb98353ad8e242b83fb255a277Andrew Flynn print(subfieldName, getter.invoke(object), indentBuf, buf); 17147107914acbad70ff4db1664d3664ccc994315afAndrew Flynn } 17247107914acbad70ff4db1664d3664ccc994315afAndrew Flynn } 17362a22a732fb134e5f34dd3e01920933ca5b16346Nicholas Seckar if (identifier != null) { 17462a22a732fb134e5f34dd3e01920933ca5b16346Nicholas Seckar indentBuf.setLength(origIndentBufLength); 17562a22a732fb134e5f34dd3e01920933ca5b16346Nicholas Seckar buf.append(indentBuf).append(">\n"); 17662a22a732fb134e5f34dd3e01920933ca5b16346Nicholas Seckar } 17747107914acbad70ff4db1664d3664ccc994315afAndrew Flynn } else { 17862a22a732fb134e5f34dd3e01920933ca5b16346Nicholas Seckar // Non-null primitive value 17947107914acbad70ff4db1664d3664ccc994315afAndrew Flynn identifier = deCamelCaseify(identifier); 18047107914acbad70ff4db1664d3664ccc994315afAndrew Flynn buf.append(indentBuf).append(identifier).append(": "); 1815cc242074f189837b38e7768b57ccfb0bca258dfMax Cai if (object instanceof String) { 1825cc242074f189837b38e7768b57ccfb0bca258dfMax Cai String stringMessage = sanitizeString((String) object); 18347107914acbad70ff4db1664d3664ccc994315afAndrew Flynn buf.append("\"").append(stringMessage).append("\""); 1845cc242074f189837b38e7768b57ccfb0bca258dfMax Cai } else if (object instanceof byte[]) { 1855cc242074f189837b38e7768b57ccfb0bca258dfMax Cai appendQuotedBytes((byte[]) object, buf); 18647107914acbad70ff4db1664d3664ccc994315afAndrew Flynn } else { 1875cc242074f189837b38e7768b57ccfb0bca258dfMax Cai buf.append(object); 18847107914acbad70ff4db1664d3664ccc994315afAndrew Flynn } 18947107914acbad70ff4db1664d3664ccc994315afAndrew Flynn buf.append("\n"); 19047107914acbad70ff4db1664d3664ccc994315afAndrew Flynn } 19147107914acbad70ff4db1664d3664ccc994315afAndrew Flynn } 19247107914acbad70ff4db1664d3664ccc994315afAndrew Flynn 19347107914acbad70ff4db1664d3664ccc994315afAndrew Flynn /** 19447107914acbad70ff4db1664d3664ccc994315afAndrew Flynn * Converts an identifier of the format "FieldName" into "field_name". 19547107914acbad70ff4db1664d3664ccc994315afAndrew Flynn */ 19647107914acbad70ff4db1664d3664ccc994315afAndrew Flynn private static String deCamelCaseify(String identifier) { 19747107914acbad70ff4db1664d3664ccc994315afAndrew Flynn StringBuffer out = new StringBuffer(); 19847107914acbad70ff4db1664d3664ccc994315afAndrew Flynn for (int i = 0; i < identifier.length(); i++) { 19947107914acbad70ff4db1664d3664ccc994315afAndrew Flynn char currentChar = identifier.charAt(i); 20047107914acbad70ff4db1664d3664ccc994315afAndrew Flynn if (i == 0) { 20147107914acbad70ff4db1664d3664ccc994315afAndrew Flynn out.append(Character.toLowerCase(currentChar)); 20247107914acbad70ff4db1664d3664ccc994315afAndrew Flynn } else if (Character.isUpperCase(currentChar)) { 20347107914acbad70ff4db1664d3664ccc994315afAndrew Flynn out.append('_').append(Character.toLowerCase(currentChar)); 20447107914acbad70ff4db1664d3664ccc994315afAndrew Flynn } else { 20547107914acbad70ff4db1664d3664ccc994315afAndrew Flynn out.append(currentChar); 20647107914acbad70ff4db1664d3664ccc994315afAndrew Flynn } 20747107914acbad70ff4db1664d3664ccc994315afAndrew Flynn } 20847107914acbad70ff4db1664d3664ccc994315afAndrew Flynn return out.toString(); 20947107914acbad70ff4db1664d3664ccc994315afAndrew Flynn } 21047107914acbad70ff4db1664d3664ccc994315afAndrew Flynn 21147107914acbad70ff4db1664d3664ccc994315afAndrew Flynn /** 21247107914acbad70ff4db1664d3664ccc994315afAndrew Flynn * Shortens and escapes the given string. 21347107914acbad70ff4db1664d3664ccc994315afAndrew Flynn */ 21447107914acbad70ff4db1664d3664ccc994315afAndrew Flynn private static String sanitizeString(String str) { 21547107914acbad70ff4db1664d3664ccc994315afAndrew Flynn if (!str.startsWith("http") && str.length() > MAX_STRING_LEN) { 21647107914acbad70ff4db1664d3664ccc994315afAndrew Flynn // Trim non-URL strings. 21747107914acbad70ff4db1664d3664ccc994315afAndrew Flynn str = str.substring(0, MAX_STRING_LEN) + "[...]"; 21847107914acbad70ff4db1664d3664ccc994315afAndrew Flynn } 21947107914acbad70ff4db1664d3664ccc994315afAndrew Flynn return escapeString(str); 22047107914acbad70ff4db1664d3664ccc994315afAndrew Flynn } 22147107914acbad70ff4db1664d3664ccc994315afAndrew Flynn 22247107914acbad70ff4db1664d3664ccc994315afAndrew Flynn /** 22347107914acbad70ff4db1664d3664ccc994315afAndrew Flynn * Escape everything except for low ASCII code points. 22447107914acbad70ff4db1664d3664ccc994315afAndrew Flynn */ 22547107914acbad70ff4db1664d3664ccc994315afAndrew Flynn private static String escapeString(String str) { 22647107914acbad70ff4db1664d3664ccc994315afAndrew Flynn int strLen = str.length(); 22747107914acbad70ff4db1664d3664ccc994315afAndrew Flynn StringBuilder b = new StringBuilder(strLen); 22847107914acbad70ff4db1664d3664ccc994315afAndrew Flynn for (int i = 0; i < strLen; i++) { 22947107914acbad70ff4db1664d3664ccc994315afAndrew Flynn char original = str.charAt(i); 23047107914acbad70ff4db1664d3664ccc994315afAndrew Flynn if (original >= ' ' && original <= '~' && original != '"' && original != '\'') { 23147107914acbad70ff4db1664d3664ccc994315afAndrew Flynn b.append(original); 23247107914acbad70ff4db1664d3664ccc994315afAndrew Flynn } else { 23347107914acbad70ff4db1664d3664ccc994315afAndrew Flynn b.append(String.format("\\u%04x", (int) original)); 23447107914acbad70ff4db1664d3664ccc994315afAndrew Flynn } 23547107914acbad70ff4db1664d3664ccc994315afAndrew Flynn } 23647107914acbad70ff4db1664d3664ccc994315afAndrew Flynn return b.toString(); 23747107914acbad70ff4db1664d3664ccc994315afAndrew Flynn } 23862a22a732fb134e5f34dd3e01920933ca5b16346Nicholas Seckar 23962a22a732fb134e5f34dd3e01920933ca5b16346Nicholas Seckar /** 24062a22a732fb134e5f34dd3e01920933ca5b16346Nicholas Seckar * Appends a quoted byte array to the provided {@code StringBuffer}. 24162a22a732fb134e5f34dd3e01920933ca5b16346Nicholas Seckar */ 24262a22a732fb134e5f34dd3e01920933ca5b16346Nicholas Seckar private static void appendQuotedBytes(byte[] bytes, StringBuffer builder) { 24362a22a732fb134e5f34dd3e01920933ca5b16346Nicholas Seckar if (bytes == null) { 24462a22a732fb134e5f34dd3e01920933ca5b16346Nicholas Seckar builder.append("\"\""); 24562a22a732fb134e5f34dd3e01920933ca5b16346Nicholas Seckar return; 24662a22a732fb134e5f34dd3e01920933ca5b16346Nicholas Seckar } 24762a22a732fb134e5f34dd3e01920933ca5b16346Nicholas Seckar 24862a22a732fb134e5f34dd3e01920933ca5b16346Nicholas Seckar builder.append('"'); 24962a22a732fb134e5f34dd3e01920933ca5b16346Nicholas Seckar for (int i = 0; i < bytes.length; ++i) { 250f56bd09ac980fcde8b6af4e3fbd5bf9c97fc9199Linus Tufvesson int ch = bytes[i] & 0xff; 25162a22a732fb134e5f34dd3e01920933ca5b16346Nicholas Seckar if (ch == '\\' || ch == '"') { 25262a22a732fb134e5f34dd3e01920933ca5b16346Nicholas Seckar builder.append('\\').append((char) ch); 25362a22a732fb134e5f34dd3e01920933ca5b16346Nicholas Seckar } else if (ch >= 32 && ch < 127) { 25462a22a732fb134e5f34dd3e01920933ca5b16346Nicholas Seckar builder.append((char) ch); 25562a22a732fb134e5f34dd3e01920933ca5b16346Nicholas Seckar } else { 256c9c2ffc3c005fbcaac2d0f91094bdb1928cb309eMax Cai builder.append(String.format("\\%03o", ch)); 25762a22a732fb134e5f34dd3e01920933ca5b16346Nicholas Seckar } 25862a22a732fb134e5f34dd3e01920933ca5b16346Nicholas Seckar } 25962a22a732fb134e5f34dd3e01920933ca5b16346Nicholas Seckar builder.append('"'); 26062a22a732fb134e5f34dd3e01920933ca5b16346Nicholas Seckar } 26147107914acbad70ff4db1664d3664ccc994315afAndrew Flynn} 262