1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.eclipse.jetty.websocket.api.util;
20
21 import java.util.Arrays;
22 import java.util.Collection;
23 import java.util.Iterator;
24 import java.util.NoSuchElementException;
25
26
27
28
29
30
31
32
33
34
35
36
37
38 public class QuoteUtil
39 {
40 private static class DeQuotingStringIterator implements Iterator<String>
41 {
42 private enum State
43 {
44 START,
45 TOKEN,
46 QUOTE_SINGLE,
47 QUOTE_DOUBLE
48 }
49
50 private final String input;
51 private final String delims;
52 private StringBuilder token;
53 private boolean hasToken = false;
54 private int i = 0;
55
56 public DeQuotingStringIterator(String input, String delims)
57 {
58 this.input = input;
59 this.delims = delims;
60 int len = input.length();
61 token = new StringBuilder(len > 1024?512:len / 2);
62 }
63
64 private void appendToken(char c)
65 {
66 if (hasToken)
67 {
68 token.append(c);
69 }
70 else
71 {
72 if (Character.isWhitespace(c))
73 {
74 return;
75 }
76 else
77 {
78 token.append(c);
79 hasToken = true;
80 }
81 }
82 }
83
84 @Override
85 public boolean hasNext()
86 {
87
88 if (hasToken)
89 {
90 return true;
91 }
92
93 State state = State.START;
94 boolean escape = false;
95 int inputLen = input.length();
96
97 while (i < inputLen)
98 {
99 char c = input.charAt(i++);
100
101 switch (state)
102 {
103 case START:
104 {
105 if (c == '\'')
106 {
107 state = State.QUOTE_SINGLE;
108 appendToken(c);
109 }
110 else if (c == '\"')
111 {
112 state = State.QUOTE_DOUBLE;
113 appendToken(c);
114 }
115 else
116 {
117 appendToken(c);
118 state = State.TOKEN;
119 }
120 break;
121 }
122 case TOKEN:
123 {
124 if (delims.indexOf(c) >= 0)
125 {
126
127 return hasToken;
128 }
129 else if (c == '\'')
130 {
131 state = State.QUOTE_SINGLE;
132 }
133 else if (c == '\"')
134 {
135 state = State.QUOTE_DOUBLE;
136 }
137 appendToken(c);
138 break;
139 }
140 case QUOTE_SINGLE:
141 {
142 if (escape)
143 {
144 escape = false;
145 appendToken(c);
146 }
147 else if (c == '\'')
148 {
149 appendToken(c);
150 state = State.TOKEN;
151 }
152 else if (c == '\\')
153 {
154 escape = true;
155 }
156 else
157 {
158 appendToken(c);
159 }
160 break;
161 }
162 case QUOTE_DOUBLE:
163 {
164 if (escape)
165 {
166 escape = false;
167 appendToken(c);
168 }
169 else if (c == '\"')
170 {
171 appendToken(c);
172 state = State.TOKEN;
173 }
174 else if (c == '\\')
175 {
176 escape = true;
177 }
178 else
179 {
180 appendToken(c);
181 }
182 break;
183 }
184 }
185
186 }
187
188 return hasToken;
189 }
190
191 @Override
192 public String next()
193 {
194 if (!hasNext())
195 {
196 throw new NoSuchElementException();
197 }
198 String ret = token.toString();
199 token.setLength(0);
200 hasToken = false;
201 return QuoteUtil.dequote(ret.trim());
202 }
203
204 @Override
205 public void remove()
206 {
207 throw new UnsupportedOperationException("Remove not supported with this iterator");
208 }
209 }
210
211
212
213
214 public static final String ABNF_REQUIRED_QUOTING = "\"'\\\n\r\t\f\b%+ ;=";
215
216 private static final char UNICODE_TAG = 0xFFFF;
217 private static final char[] escapes = new char[32];
218
219 static
220 {
221 Arrays.fill(escapes,UNICODE_TAG);
222
223 escapes['\b'] = 'b';
224 escapes['\t'] = 't';
225 escapes['\n'] = 'n';
226 escapes['\f'] = 'f';
227 escapes['\r'] = 'r';
228 }
229
230 private static int dehex(byte b)
231 {
232 if ((b >= '0') && (b <= '9'))
233 {
234 return (byte)(b - '0');
235 }
236 if ((b >= 'a') && (b <= 'f'))
237 {
238 return (byte)((b - 'a') + 10);
239 }
240 if ((b >= 'A') && (b <= 'F'))
241 {
242 return (byte)((b - 'A') + 10);
243 }
244 throw new IllegalArgumentException("!hex:" + Integer.toHexString(0xff & b));
245 }
246
247
248
249
250
251
252
253
254 public static String dequote(String str)
255 {
256 char start = str.charAt(0);
257 if ((start == '\'') || (start == '\"'))
258 {
259
260 char end = str.charAt(str.length() - 1);
261 if (start == end)
262 {
263
264 return str.substring(1,str.length() - 1);
265 }
266 }
267 return str;
268 }
269
270 public static void escape(StringBuilder buf, String str)
271 {
272 for (char c : str.toCharArray())
273 {
274 if (c >= 32)
275 {
276
277 if ((c == '"') || (c == '\\'))
278 {
279 buf.append('\\');
280 }
281 buf.append(c);
282 }
283 else
284 {
285
286 char escaped = escapes[c];
287
288
289 if (escaped == UNICODE_TAG)
290 {
291 buf.append("\\u00");
292 if (c < 0x10)
293 {
294 buf.append('0');
295 }
296 buf.append(Integer.toString(c,16));
297 }
298 else
299 {
300
301 buf.append('\\').append(escaped);
302 }
303 }
304 }
305 }
306
307
308
309
310
311
312
313
314
315 public static void quote(StringBuilder buf, String str)
316 {
317 buf.append('"');
318 escape(buf,str);
319 buf.append('"');
320 }
321
322
323
324
325
326
327
328
329
330
331
332
333
334 public static void quoteIfNeeded(StringBuilder buf, String str, String delim)
335 {
336 if (str == null)
337 {
338 return;
339 }
340
341 int len = str.length();
342 if (len == 0)
343 {
344 return;
345 }
346 int ch;
347 for (int i = 0; i < len; i++)
348 {
349 ch = str.codePointAt(i);
350 if (delim.indexOf(ch) >= 0)
351 {
352
353 quote(buf,str);
354 return;
355 }
356 }
357
358
359 buf.append(str);
360 }
361
362
363
364
365
366
367
368
369
370
371
372 public static Iterator<String> splitAt(String str, String delims)
373 {
374 return new DeQuotingStringIterator(str.trim(),delims);
375 }
376
377 public static String unescape(String str)
378 {
379 if (str == null)
380 {
381
382 return null;
383 }
384
385 int len = str.length();
386 if (len <= 1)
387 {
388
389 return str;
390 }
391
392 StringBuilder ret = new StringBuilder(len - 2);
393 boolean escaped = false;
394 char c;
395 for (int i = 0; i < len; i++)
396 {
397 c = str.charAt(i);
398 if (escaped)
399 {
400 escaped = false;
401 switch (c)
402 {
403 case 'n':
404 ret.append('\n');
405 break;
406 case 'r':
407 ret.append('\r');
408 break;
409 case 't':
410 ret.append('\t');
411 break;
412 case 'f':
413 ret.append('\f');
414 break;
415 case 'b':
416 ret.append('\b');
417 break;
418 case '\\':
419 ret.append('\\');
420 break;
421 case '/':
422 ret.append('/');
423 break;
424 case '"':
425 ret.append('"');
426 break;
427 case 'u':
428 ret.append((char)((dehex((byte)str.charAt(i++)) << 24) + (dehex((byte)str.charAt(i++)) << 16) + (dehex((byte)str.charAt(i++)) << 8) + (dehex((byte)str
429 .charAt(i++)))));
430 break;
431 default:
432 ret.append(c);
433 }
434 }
435 else if (c == '\\')
436 {
437 escaped = true;
438 }
439 else
440 {
441 ret.append(c);
442 }
443 }
444 return ret.toString();
445 }
446
447 public static String join(Object[] objs, String delim)
448 {
449 if (objs == null)
450 {
451 return "";
452 }
453 StringBuilder ret = new StringBuilder();
454 int len = objs.length;
455 for (int i = 0; i < len; i++)
456 {
457 if (i > 0)
458 {
459 ret.append(delim);
460 }
461 if (objs[i] instanceof String)
462 {
463 ret.append('"').append(objs[i]).append('"');
464 }
465 else
466 {
467 ret.append(objs[i]);
468 }
469 }
470 return ret.toString();
471 }
472
473 public static String join(Collection<?> objs, String delim)
474 {
475 if (objs == null)
476 {
477 return "";
478 }
479 StringBuilder ret = new StringBuilder();
480 boolean needDelim = false;
481 for (Object obj : objs)
482 {
483 if (needDelim)
484 {
485 ret.append(delim);
486 }
487 if (obj instanceof String)
488 {
489 ret.append('"').append(obj).append('"');
490 }
491 else
492 {
493 ret.append(obj);
494 }
495 needDelim = true;
496 }
497 return ret.toString();
498 }
499 }