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; ...@@ -36,6 +36,7 @@ import java.io.ByteArrayOutputStream;
import java.io.FilterOutputStream; import java.io.FilterOutputStream;
import java.io.UnsupportedEncodingException; import java.io.UnsupportedEncodingException;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.util.List;
/** /**
* Immutable array of bytes. * Immutable array of bytes.
...@@ -137,6 +138,34 @@ public final class ByteString { ...@@ -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[] // ByteString -> byte[]
......
...@@ -38,6 +38,7 @@ import com.google.protobuf.Descriptors.EnumValueDescriptor; ...@@ -38,6 +38,7 @@ import com.google.protobuf.Descriptors.EnumValueDescriptor;
import java.io.IOException; import java.io.IOException;
import java.nio.CharBuffer; import java.nio.CharBuffer;
import java.math.BigInteger; import java.math.BigInteger;
import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Locale; import java.util.Locale;
import java.util.Map; import java.util.Map;
...@@ -115,7 +116,7 @@ public final class TextFormat { ...@@ -115,7 +116,7 @@ public final class TextFormat {
} }
printUnknownFields(message.getUnknownFields(), generator); printUnknownFields(message.getUnknownFields(), generator);
} }
public static void printField(final FieldDescriptor field, public static void printField(final FieldDescriptor field,
final Object value, final Object value,
final Appendable output) final Appendable output)
...@@ -133,10 +134,10 @@ public final class TextFormat { ...@@ -133,10 +134,10 @@ public final class TextFormat {
} catch (IOException e) { } catch (IOException e) {
throw new RuntimeException( throw new RuntimeException(
"Writing to a StringBuilder threw an IOException (should never " + "Writing to a StringBuilder threw an IOException (should never " +
"happen).", e); "happen).", e);
} }
} }
private static void printField(final FieldDescriptor field, private static void printField(final FieldDescriptor field,
final Object value, final Object value,
final TextGenerator generator) final TextGenerator generator)
...@@ -428,7 +429,7 @@ public final class TextFormat { ...@@ -428,7 +429,7 @@ public final class TextFormat {
"[a-zA-Z_][0-9a-zA-Z_+-]*+|" + // an identifier "[a-zA-Z_][0-9a-zA-Z_+-]*+|" + // an identifier
"[.]?[0-9+-][0-9a-zA-Z_.+-]*+|" + // a number "[.]?[0-9+-][0-9a-zA-Z_.+-]*+|" + // a number
"\"([^\"\n\\\\]|\\\\.)*+(\"|\\\\?$)|" + // a double-quoted string "\"([^\"\n\\\\]|\\\\.)*+(\"|\\\\?$)|" + // a double-quoted string
"\'([^\"\n\\\\]|\\\\.)*+(\'|\\\\?$)", // a single-quoted string "\'([^\'\n\\\\]|\\\\.)*+(\'|\\\\?$)", // a single-quoted string
Pattern.MULTILINE); Pattern.MULTILINE);
private static final Pattern DOUBLE_INFINITY = Pattern.compile( private static final Pattern DOUBLE_INFINITY = Pattern.compile(
...@@ -695,6 +696,15 @@ public final class TextFormat { ...@@ -695,6 +696,15 @@ public final class TextFormat {
* {@link ParseException}. * {@link ParseException}.
*/ */
public ByteString consumeByteString() throws 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) final char quote = currentToken.length() > 0 ? currentToken.charAt(0)
: '\0'; : '\0';
if (quote != '\"' && quote != '\'') { if (quote != '\"' && quote != '\'') {
...@@ -711,7 +721,7 @@ public final class TextFormat { ...@@ -711,7 +721,7 @@ public final class TextFormat {
currentToken.substring(1, currentToken.length() - 1); currentToken.substring(1, currentToken.length() - 1);
final ByteString result = unescapeBytes(escaped); final ByteString result = unescapeBytes(escaped);
nextToken(); nextToken();
return result; list.add(result);
} catch (InvalidEscapeSequenceException e) { } catch (InvalidEscapeSequenceException e) {
throw parseException(e.getMessage()); throw parseException(e.getMessage());
} }
......
...@@ -196,12 +196,12 @@ public class TextFormatTest extends TestCase { ...@@ -196,12 +196,12 @@ public class TextFormatTest extends TestCase {
final FieldDescriptor optionalField = final FieldDescriptor optionalField =
TestAllTypes.getDescriptor().findFieldByName("optional_nested_message"); TestAllTypes.getDescriptor().findFieldByName("optional_nested_message");
final Object value = NestedMessage.newBuilder().setBb(42).build(); final Object value = NestedMessage.newBuilder().setBb(42).build();
assertEquals( assertEquals(
"optional_nested_message {\n bb: 42\n}\n", "optional_nested_message {\n bb: 42\n}\n",
TextFormat.printFieldToString(optionalField, value)); TextFormat.printFieldToString(optionalField, value));
} }
/** /**
* Helper to construct a ByteString from a String containing only 8-bit * Helper to construct a ByteString from a String containing only 8-bit
* characters. The characters are converted directly to bytes, *not* * characters. The characters are converted directly to bytes, *not*
...@@ -649,4 +649,10 @@ public class TextFormatTest extends TestCase { ...@@ -649,4 +649,10 @@ public class TextFormatTest extends TestCase {
TextFormat.merge("optional_string: \"" + longText + "\"", builder); TextFormat.merge("optional_string: \"" + longText + "\"", builder);
assertEquals(longText, builder.getOptionalString()); 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