@@ -53,11 +53,23 @@ class JsonUrlGrammarAQF extends AbstractGrammar {
53
53
private static final char ESCAPE = '!' ;
54
54
55
55
/**
56
- * Buffer for decoded literal text.
56
+ * Buffer for decoded/unescaped literal text.
57
57
*/
58
58
@ SuppressWarnings ("PMD.AvoidStringBufferField" ) // reused
59
59
private final StringBuilder decodedTextBuffer = new StringBuilder (512 );
60
60
61
+ /**
62
+ * Buffer for non-decoded/escaped literal text.
63
+ */
64
+ @ SuppressWarnings ("PMD.AvoidStringBufferField" ) // reused
65
+ private final StringBuilder numTextBuffer = new StringBuilder (512 );
66
+
67
+ /**
68
+ * Reference to either {@link #decodedTextBuffer} or
69
+ * {@link #numTextBuffer}.
70
+ */
71
+ private CharSequence literalText ;
72
+
61
73
/**
62
74
* Construct a new JsonUrlGrammar.
63
75
* @param text input text
@@ -77,7 +89,7 @@ public JsonUrlGrammarAQF(
77
89
*/
78
90
@ SuppressWarnings ("PMD.CyclomaticComplexity" )
79
91
private void decodeBang (StringBuilder decodedText , boolean isFirst ) {
80
- int cur = nextCodePoint ();
92
+ int cur = nextCodePoint (false );
81
93
82
94
switch (cur ) {
83
95
case '0' :
@@ -91,6 +103,7 @@ private void decodeBang(StringBuilder decodedText, boolean isFirst) {
91
103
case '8' :
92
104
case '9' :
93
105
case '-' :
106
+ case '+' :
94
107
case ESCAPE :
95
108
case 't' :
96
109
case 'f' :
@@ -103,7 +116,7 @@ private void decodeBang(StringBuilder decodedText, boolean isFirst) {
103
116
break ;
104
117
105
118
case 'e' :
106
- if (isFirst && decodedText . length () == 0 ) {
119
+ if (isFirst ) {
107
120
break ;
108
121
}
109
122
// fall through
@@ -118,45 +131,62 @@ protected boolean readAndBufferLiteral() {
118
131
final StringBuilder decodedText = this .decodedTextBuffer ;
119
132
decodedText .setLength (0 );
120
133
134
+ final StringBuilder numText = this .numTextBuffer ;
135
+ numText .setLength (0 );
136
+
121
137
//
122
138
// return true if this has an escape sequence and therefore
123
139
// must be parsed as a string value; otherwise, return false.
124
140
//
125
141
boolean ret = false ;
126
142
127
- for (;;) {
143
+ for (boolean isFirst = true ;; isFirst = false ) {
144
+ final char rawPlus ;
145
+
128
146
//
129
147
// The browser address bar *does* recognize a difference between
130
- // percent encoded vs. literal ampersand and equals , unlike other
131
- // sub-delims. So I have to check for those specifically, here ,
132
- // because the call to nextCodePoint() will decode them and I can't
133
- // tell the difference at that point.
148
+ // percent encoded vs. literal ampersand, equals, and plus , unlike
149
+ // other sub-delims. So I have to check for those specifically,
150
+ // here, because the call to nextCodePoint() will decode them and
151
+ // I can't tell the difference at that point.
134
152
//
135
153
switch (peekAscii ()) { // NOPMD - no default
136
154
case EOF :
137
155
case WFU_VALUE_SEPARATOR :
138
156
case WFU_NAME_SEPARATOR :
139
157
return ret ;
158
+ case WFU_SPACE :
159
+ rawPlus = '+' ;
160
+ break ;
161
+ default :
162
+ rawPlus = ' ' ;
163
+ break ;
140
164
}
141
165
142
- int ucp = nextCodePoint ();
166
+ final int ucp = nextCodePoint ();
143
167
144
168
if (ucp >= CHARBITS_LENGTH ) {
145
169
decodedText .appendCodePoint (ucp );
170
+ numText .appendCodePoint (ucp );
146
171
continue ;
147
172
}
148
173
149
174
switch (CHARBITS [ucp ] & (IS_SPACE
150
175
| IS_BANG | IS_LITCHAR | IS_STRUCTCHAR | IS_CGICHAR )) {
151
176
152
177
case IS_BANG | IS_LITCHAR :
153
- decodeBang (decodedText , !ret );
178
+ decodeBang (decodedText , isFirst );
179
+ numText .appendCodePoint (ucp );
154
180
ret = true ;
155
181
continue ;
156
- case IS_LITCHAR :
157
182
case IS_SPACE :
183
+ decodedText .appendCodePoint (ucp );
184
+ numText .append (rawPlus );
185
+ break ;
186
+ case IS_LITCHAR :
158
187
case IS_STRUCTCHAR | IS_CGICHAR :
159
188
decodedText .appendCodePoint (ucp );
189
+ numText .appendCodePoint (ucp );
160
190
continue ;
161
191
case IS_STRUCTCHAR :
162
192
text .pushbackChar (ucp );
@@ -170,9 +200,12 @@ protected boolean readAndBufferLiteral() {
170
200
171
201
@ Override
172
202
protected JsonUrlEvent readBufferedLiteral (
173
- boolean wasEscapedString ,
203
+ boolean isEscaped ,
174
204
boolean isKey ) {
175
205
206
+ final StringBuilder decodedText = this .decodedTextBuffer ;
207
+ literalText = decodedText ;
208
+
176
209
if (optionImpliedStringLiterals (options ())) {
177
210
//
178
211
// VALUE_STRING (rather than VALUE_EMPTY_LITERAL) should be
@@ -181,9 +214,7 @@ protected JsonUrlEvent readBufferedLiteral(
181
214
return keyEvent (isKey , JsonUrlEvent .VALUE_STRING );
182
215
}
183
216
184
- final StringBuilder decodedText = this .decodedTextBuffer ;
185
-
186
- if (wasEscapedString ) {
217
+ if (isEscaped ) {
187
218
if (decodedText .length () == 0 ) {
188
219
return keyEvent (isKey , JsonUrlEvent .VALUE_EMPTY_LITERAL );
189
220
}
@@ -208,7 +239,10 @@ && optionCoerceNullToEmptyString(options())) {
208
239
return keyEvent (isKey , ret );
209
240
}
210
241
211
- if (numberBuilder .reset ().parse (decodedText )) {
242
+ final StringBuilder numText = this .numTextBuffer ;
243
+
244
+ if (numberBuilder .reset ().parse (numText , options ())) {
245
+ literalText = numText ;
212
246
return keyEvent (isKey , JsonUrlEvent .VALUE_NUMBER );
213
247
}
214
248
@@ -222,7 +256,7 @@ protected JsonUrlEvent readLiteral(boolean isKey) {
222
256
223
257
@ Override
224
258
public String getString () {
225
- return decodedTextBuffer .toString ();
259
+ return literalText .toString ();
226
260
}
227
261
228
262
@ Override
@@ -236,13 +270,20 @@ protected int peekChar() {
236
270
text .pushbackChar (codePoint );
237
271
return codePoint ;
238
272
}
239
-
273
+
240
274
/**
241
275
* Read and decode the next codepoint.
242
276
*/
243
277
private int nextCodePoint () {
278
+ return nextCodePoint (true );
279
+ }
280
+
281
+ /**
282
+ * Read and decode the next codepoint.
283
+ */
284
+ private int nextCodePoint (boolean decodePlus ) {
244
285
try {
245
- return PercentCodec .decode (text );
286
+ return PercentCodec .decode (text , decodePlus );
246
287
247
288
} catch (IOException e ) {
248
289
SyntaxException tex = newSyntaxException (MSG_BAD_CHAR );
0 commit comments