diff options
3 files changed, 52 insertions, 7 deletions
diff --git a/java/src/main/java/com/google/protobuf/ByteString.java b/java/src/main/java/com/google/protobuf/ByteString.java index c043df8b..5fade03a 100644 --- a/java/src/main/java/com/google/protobuf/ByteString.java +++ b/java/src/main/java/com/google/protobuf/ByteString.java @@ -36,6 +36,7 @@ import java.io.ByteArrayOutputStream; import java.io.FilterOutputStream; import java.io.UnsupportedEncodingException; import java.nio.ByteBuffer; +import java.util.List; /** * Immutable array of bytes. @@ -137,6 +138,34 @@ public final class ByteString { } } + /** + * Concatenates all byte strings in the list and returns the result. + * + * <p>The returned {@code ByteString} is not necessarily a unique object. + * If the list is empty, the returned object is the singleton empty + * {@code ByteString}. If the list has only one element, that + * {@code ByteString} will be returned without copying. + */ + public static ByteString copyFrom(List<ByteString> list) { + if (list.size() == 0) { + return EMPTY; + } else if (list.size() == 1) { + return list.get(0); + } + + int size = 0; + for (ByteString str : list) { + size += str.size(); + } + byte[] bytes = new byte[size]; + int pos = 0; + for (ByteString str : list) { + System.arraycopy(str.bytes, 0, bytes, pos, str.size()); + pos += str.size(); + } + return new ByteString(bytes); + } + // ================================================================= // ByteString -> byte[] diff --git a/java/src/main/java/com/google/protobuf/TextFormat.java b/java/src/main/java/com/google/protobuf/TextFormat.java index 698992f0..ed26722d 100644 --- a/java/src/main/java/com/google/protobuf/TextFormat.java +++ b/java/src/main/java/com/google/protobuf/TextFormat.java @@ -38,6 +38,7 @@ import com.google.protobuf.Descriptors.EnumValueDescriptor; import java.io.IOException; import java.nio.CharBuffer; import java.math.BigInteger; +import java.util.ArrayList; import java.util.List; import java.util.Locale; import java.util.Map; @@ -115,7 +116,7 @@ public final class TextFormat { } printUnknownFields(message.getUnknownFields(), generator); } - + public static void printField(final FieldDescriptor field, final Object value, final Appendable output) @@ -133,10 +134,10 @@ public final class TextFormat { } catch (IOException e) { throw new RuntimeException( "Writing to a StringBuilder threw an IOException (should never " + - "happen).", e); + "happen).", e); } } - + private static void printField(final FieldDescriptor field, final Object value, final TextGenerator generator) @@ -428,7 +429,7 @@ public final class TextFormat { "[a-zA-Z_][0-9a-zA-Z_+-]*+|" + // an identifier "[.]?[0-9+-][0-9a-zA-Z_.+-]*+|" + // a number "\"([^\"\n\\\\]|\\\\.)*+(\"|\\\\?$)|" + // a double-quoted string - "\'([^\"\n\\\\]|\\\\.)*+(\'|\\\\?$)", // a single-quoted string + "\'([^\'\n\\\\]|\\\\.)*+(\'|\\\\?$)", // a single-quoted string Pattern.MULTILINE); private static final Pattern DOUBLE_INFINITY = Pattern.compile( @@ -695,6 +696,15 @@ public final class TextFormat { * {@link ParseException}. */ public ByteString consumeByteString() throws ParseException { + List<ByteString> list = new ArrayList<ByteString>(); + consumeByteString(list); + while (currentToken.startsWith("'") || currentToken.startsWith("\"")) { + consumeByteString(list); + } + return ByteString.copyFrom(list); + } + + public void consumeByteString(List<ByteString> list) throws ParseException { final char quote = currentToken.length() > 0 ? currentToken.charAt(0) : '\0'; if (quote != '\"' && quote != '\'') { @@ -711,7 +721,7 @@ public final class TextFormat { currentToken.substring(1, currentToken.length() - 1); final ByteString result = unescapeBytes(escaped); nextToken(); - return result; + list.add(result); } catch (InvalidEscapeSequenceException e) { throw parseException(e.getMessage()); } diff --git a/java/src/test/java/com/google/protobuf/TextFormatTest.java b/java/src/test/java/com/google/protobuf/TextFormatTest.java index 3ea7b2cf..60bd800e 100644 --- a/java/src/test/java/com/google/protobuf/TextFormatTest.java +++ b/java/src/test/java/com/google/protobuf/TextFormatTest.java @@ -196,12 +196,12 @@ public class TextFormatTest extends TestCase { final FieldDescriptor optionalField = TestAllTypes.getDescriptor().findFieldByName("optional_nested_message"); final Object value = NestedMessage.newBuilder().setBb(42).build(); - + assertEquals( "optional_nested_message {\n bb: 42\n}\n", TextFormat.printFieldToString(optionalField, value)); } - + /** * Helper to construct a ByteString from a String containing only 8-bit * characters. The characters are converted directly to bytes, *not* @@ -649,4 +649,10 @@ public class TextFormatTest extends TestCase { TextFormat.merge("optional_string: \"" + longText + "\"", builder); assertEquals(longText, builder.getOptionalString()); } + + public void testParseAdjacentStringLiterals() throws Exception { + TestAllTypes.Builder builder = TestAllTypes.newBuilder(); + TextFormat.merge("optional_string: \"foo\" 'corge' \"grault\"", builder); + assertEquals("foocorgegrault", builder.getOptionalString()); + } } |