@@ -38,6 +38,8 @@ private TextFormat() {}
38
38
39
39
private static final Logger logger = Logger .getLogger (TextFormat .class .getName ());
40
40
41
+ private static final String DEBUG_STRING_SILENT_MARKER = " \t " ;
42
+
41
43
private static final String REDACTED_MARKER = "[REDACTED]" ;
42
44
43
45
/**
@@ -998,6 +1000,15 @@ private static final class Tokenizer {
998
1000
private int previousLine = 0 ;
999
1001
private int previousColumn = 0 ;
1000
1002
1003
+ /**
1004
+ * {@link containsSilentMarkerAfterCurrentToken} indicates if there is a silent marker after the
1005
+ * current token. This value is moved to {@link containsSilentMarkerAfterPrevToken} every time
1006
+ * the next token is parsed.
1007
+ */
1008
+ private boolean containsSilentMarkerAfterCurrentToken = false ;
1009
+
1010
+ private boolean containsSilentMarkerAfterPrevToken = false ;
1011
+
1001
1012
/** Construct a tokenizer that parses tokens from the given text. */
1002
1013
private Tokenizer (final CharSequence text ) {
1003
1014
this .text = text ;
@@ -1021,6 +1032,14 @@ int getColumn() {
1021
1032
return column ;
1022
1033
}
1023
1034
1035
+ boolean getContainsSilentMarkerAfterCurrentToken () {
1036
+ return containsSilentMarkerAfterCurrentToken ;
1037
+ }
1038
+
1039
+ boolean getContainsSilentMarkerAfterPrevToken () {
1040
+ return containsSilentMarkerAfterPrevToken ;
1041
+ }
1042
+
1024
1043
/** Are we at the end of the input? */
1025
1044
boolean atEnd () {
1026
1045
return currentToken .length () == 0 ;
@@ -1709,6 +1728,19 @@ public static <T extends Message> T parse(
1709
1728
* control the parser behavior.
1710
1729
*/
1711
1730
public static class Parser {
1731
+
1732
+ /**
1733
+ * A valid silent marker appears between a field name and its value. If there is a ":" in
1734
+ * between, the silent marker will only appear after the colon. This is called after a field
1735
+ * name is parsed, and before the ":" if it exists. If the current token is ":", then
1736
+ * containsSilentMarkerAfterCurrentToken indicates if there is a valid silent marker. Otherwise,
1737
+ * the current token is part of the field value, so the silent marker is indicated by
1738
+ * containsSilentMarkerAfterPrevToken.
1739
+ */
1740
+ private void detectSilentMarker (
1741
+ Tokenizer tokenizer , Descriptor immediateMessageType , String fieldName ) {
1742
+ }
1743
+
1712
1744
/**
1713
1745
* Determines if repeated values for non-repeated fields and oneofs are permitted. For example,
1714
1746
* given required/optional field "foo" and a oneof containing "baz" and "moo":
@@ -2081,12 +2113,14 @@ private void mergeField(
2081
2113
2082
2114
// Skips unknown fields.
2083
2115
if (field == null ) {
2116
+ detectSilentMarker (tokenizer , type , name );
2084
2117
guessFieldTypeAndSkip (tokenizer , type , recursionLimit );
2085
2118
return ;
2086
2119
}
2087
2120
2088
2121
// Handle potential ':'.
2089
2122
if (field .getJavaType () == FieldDescriptor .JavaType .MESSAGE ) {
2123
+ detectSilentMarker (tokenizer , type , field .getFullName ());
2090
2124
tokenizer .tryConsume (":" ); // optional
2091
2125
if (parseTreeBuilder != null ) {
2092
2126
TextFormatParseInfoTree .Builder childParseTreeBuilder =
@@ -2112,6 +2146,7 @@ private void mergeField(
2112
2146
recursionLimit );
2113
2147
}
2114
2148
} else {
2149
+ detectSilentMarker (tokenizer , type , field .getFullName ());
2115
2150
tokenizer .consume (":" ); // required
2116
2151
consumeFieldValues (
2117
2152
tokenizer ,
@@ -2135,26 +2170,26 @@ private void mergeField(
2135
2170
}
2136
2171
}
2137
2172
2138
- private void consumeFullTypeName (Tokenizer tokenizer ) throws ParseException {
2173
+ private String consumeFullTypeName (Tokenizer tokenizer ) throws ParseException {
2139
2174
// If there is not a leading `[`, this is just a type name.
2140
2175
if (!tokenizer .tryConsume ("[" )) {
2141
- tokenizer .consumeIdentifier ();
2142
- return ;
2176
+ return tokenizer .consumeIdentifier ();
2143
2177
}
2144
2178
2145
2179
// Otherwise, this is an extension or google.protobuf.Any type URL: we consume proto path
2146
2180
// elements until we've addressed the type.
2147
- tokenizer .consumeIdentifier ();
2181
+ String name = tokenizer .consumeIdentifier ();
2148
2182
while (tokenizer .tryConsume ("." )) {
2149
- tokenizer .consumeIdentifier ();
2183
+ name += "." + tokenizer .consumeIdentifier ();
2150
2184
}
2151
2185
if (tokenizer .tryConsume ("/" )) {
2152
- tokenizer .consumeIdentifier ();
2186
+ name += "/" + tokenizer .consumeIdentifier ();
2153
2187
while (tokenizer .tryConsume ("." )) {
2154
- tokenizer .consumeIdentifier ();
2188
+ name += "." + tokenizer .consumeIdentifier ();
2155
2189
}
2156
2190
}
2157
2191
tokenizer .consume ("]" );
2192
+ return name ;
2158
2193
}
2159
2194
2160
2195
/**
@@ -2400,6 +2435,7 @@ private void mergeAnyFieldValue(
2400
2435
throw tokenizer .parseExceptionPreviousToken ("Expected a valid type URL." );
2401
2436
}
2402
2437
}
2438
+ detectSilentMarker (tokenizer , anyDescriptor , typeUrlBuilder .toString ());
2403
2439
tokenizer .tryConsume (":" );
2404
2440
final String anyEndToken ;
2405
2441
if (tokenizer .tryConsume ("<" )) {
@@ -2444,7 +2480,8 @@ private void mergeAnyFieldValue(
2444
2480
/** Skips the next field including the field's name and value. */
2445
2481
private void skipField (Tokenizer tokenizer , Descriptor type , int recursionLimit )
2446
2482
throws ParseException {
2447
- consumeFullTypeName (tokenizer );
2483
+ String name = consumeFullTypeName (tokenizer );
2484
+ detectSilentMarker (tokenizer , type , name );
2448
2485
guessFieldTypeAndSkip (tokenizer , type , recursionLimit );
2449
2486
2450
2487
// For historical reasons, fields may optionally be separated by commas or
0 commit comments