Commit 35d2f017 authored by kenton@google.com's avatar kenton@google.com

In Java's TextFormat, correcty concatenate adjacent string literals, as C++…

In Java's TextFormat, correcty concatenate adjacent string literals, as C++ does.  Also fix a bug in handling of single-quoted strings.
parent 6e8b9e4a
......@@ -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[]
......
......@@ -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());
}
......
......@@ -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());
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment