001: // ========================================================================
002: // Copyright 2004-2005 Mort Bay Consulting Pty. Ltd.
003: // ------------------------------------------------------------------------
004: // Licensed under the Apache License, Version 2.0 (the "License");
005: // you may not use this file except in compliance with the License.
006: // You may obtain a copy of the License at
007: // http://www.apache.org/licenses/LICENSE-2.0
008: // Unless required by applicable law or agreed to in writing, software
009: // distributed under the License is distributed on an "AS IS" BASIS,
010: // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
011: // See the License for the specific language governing permissions and
012: // limitations under the License.
013: // ========================================================================
014:
015: package org.mortbay.util;
016:
017: import java.util.NoSuchElementException;
018: import java.util.StringTokenizer;
019:
020: /* ------------------------------------------------------------ */
021: /** StringTokenizer with Quoting support.
022: *
023: * This class is a copy of the java.util.StringTokenizer API and
024: * the behaviour is the same, except that single and doulbe quoted
025: * string values are recognized.
026: * Delimiters within quotes are not considered delimiters.
027: * Quotes can be escaped with '\'.
028: *
029: * @see java.util.StringTokenizer
030: * @author Greg Wilkins (gregw)
031: */
032: public class QuotedStringTokenizer extends StringTokenizer {
033: private final static String __delim = "\t\n\r";
034: private String _string;
035: private String _delim = __delim;
036: private boolean _returnQuotes = false;
037: private boolean _returnDelimiters = false;
038: private StringBuffer _token;
039: private boolean _hasToken = false;
040: private int _i = 0;
041: private int _lastStart = 0;
042: private boolean _double = true;
043: private boolean _single = true;
044:
045: /* ------------------------------------------------------------ */
046: public QuotedStringTokenizer(String str, String delim,
047: boolean returnDelimiters, boolean returnQuotes) {
048: super ("");
049: _string = str;
050: if (delim != null)
051: _delim = delim;
052: _returnDelimiters = returnDelimiters;
053: _returnQuotes = returnQuotes;
054:
055: if (_delim.indexOf('\'') >= 0 || _delim.indexOf('"') >= 0)
056: throw new Error("Can't use quotes as delimiters: " + _delim);
057:
058: _token = new StringBuffer(_string.length() > 1024 ? 512
059: : _string.length() / 2);
060: }
061:
062: /* ------------------------------------------------------------ */
063: public QuotedStringTokenizer(String str, String delim,
064: boolean returnDelimiters) {
065: this (str, delim, returnDelimiters, false);
066: }
067:
068: /* ------------------------------------------------------------ */
069: public QuotedStringTokenizer(String str, String delim) {
070: this (str, delim, false, false);
071: }
072:
073: /* ------------------------------------------------------------ */
074: public QuotedStringTokenizer(String str) {
075: this (str, null, false, false);
076: }
077:
078: /* ------------------------------------------------------------ */
079: public boolean hasMoreTokens() {
080: // Already found a token
081: if (_hasToken)
082: return true;
083:
084: _lastStart = _i;
085:
086: int state = 0;
087: boolean escape = false;
088: while (_i < _string.length()) {
089: char c = _string.charAt(_i++);
090:
091: switch (state) {
092: case 0: // Start
093: if (_delim.indexOf(c) >= 0) {
094: if (_returnDelimiters) {
095: _token.append(c);
096: return _hasToken = true;
097: }
098: } else if (c == '\'' && _single) {
099: if (_returnQuotes)
100: _token.append(c);
101: state = 2;
102: } else if (c == '\"' && _double) {
103: if (_returnQuotes)
104: _token.append(c);
105: state = 3;
106: } else {
107: _token.append(c);
108: _hasToken = true;
109: state = 1;
110: }
111: continue;
112:
113: case 1: // Token
114: _hasToken = true;
115: if (_delim.indexOf(c) >= 0) {
116: if (_returnDelimiters)
117: _i--;
118: return _hasToken;
119: } else if (c == '\'' && _single) {
120: if (_returnQuotes)
121: _token.append(c);
122: state = 2;
123: } else if (c == '\"' && _double) {
124: if (_returnQuotes)
125: _token.append(c);
126: state = 3;
127: } else
128: _token.append(c);
129: continue;
130:
131: case 2: // Single Quote
132: _hasToken = true;
133: if (escape) {
134: escape = false;
135: _token.append(c);
136: } else if (c == '\'') {
137: if (_returnQuotes)
138: _token.append(c);
139: state = 1;
140: } else if (c == '\\') {
141: if (_returnQuotes)
142: _token.append(c);
143: escape = true;
144: } else
145: _token.append(c);
146: continue;
147:
148: case 3: // Double Quote
149: _hasToken = true;
150: if (escape) {
151: escape = false;
152: _token.append(c);
153: } else if (c == '\"') {
154: if (_returnQuotes)
155: _token.append(c);
156: state = 1;
157: } else if (c == '\\') {
158: if (_returnQuotes)
159: _token.append(c);
160: escape = true;
161: } else
162: _token.append(c);
163: continue;
164: }
165: }
166:
167: return _hasToken;
168: }
169:
170: /* ------------------------------------------------------------ */
171: public String nextToken() throws NoSuchElementException {
172: if (!hasMoreTokens() || _token == null)
173: throw new NoSuchElementException();
174: String t = _token.toString();
175: _token.setLength(0);
176: _hasToken = false;
177: return t;
178: }
179:
180: /* ------------------------------------------------------------ */
181: public String nextToken(String delim) throws NoSuchElementException {
182: _delim = delim;
183: _i = _lastStart;
184: _token.setLength(0);
185: _hasToken = false;
186: return nextToken();
187: }
188:
189: /* ------------------------------------------------------------ */
190: public boolean hasMoreElements() {
191: return hasMoreTokens();
192: }
193:
194: /* ------------------------------------------------------------ */
195: public Object nextElement() throws NoSuchElementException {
196: return nextToken();
197: }
198:
199: /* ------------------------------------------------------------ */
200: /** Not implemented.
201: */
202: public int countTokens() {
203: return -1;
204: }
205:
206: /* ------------------------------------------------------------ */
207: /** Quote a string.
208: * The string is quoted only if quoting is required due to
209: * embeded delimiters, quote characters or the
210: * empty string.
211: * @param s The string to quote.
212: * @return quoted string
213: */
214: public static String quote(String s, String delim) {
215: if (s == null)
216: return null;
217: if (s.length() == 0)
218: return "\"\"";
219:
220: for (int i = 0; i < s.length(); i++) {
221: char c = s.charAt(i);
222: if (c == '\\' || c == '"' || c == '\''
223: || Character.isWhitespace(c)
224: || delim.indexOf(c) >= 0) {
225: StringBuffer b = new StringBuffer(s.length() + 8);
226: quote(b, s);
227: return b.toString();
228: }
229: }
230:
231: return s;
232: }
233:
234: /* ------------------------------------------------------------ */
235: /** Quote a string.
236: * The string is quoted only if quoting is required due to
237: * embeded delimiters, quote characters or the
238: * empty string.
239: * @param s The string to quote.
240: * @return quoted string
241: */
242: public static String quote(String s) {
243: if (s == null)
244: return null;
245: if (s.length() == 0)
246: return "\"\"";
247:
248: StringBuffer b = new StringBuffer(s.length() + 8);
249: quote(b, s);
250: return b.toString();
251:
252: }
253:
254: /* ------------------------------------------------------------ */
255: /** Quote a string into a StringBuffer.
256: * The characters ", \, \n, \r, \t, \f and \b are escaped
257: * @param buf The StringBuffer
258: * @param s The String to quote.
259: */
260: public static void quote(StringBuffer buf, String s) {
261: synchronized (buf) {
262: buf.append('"');
263: for (int i = 0; i < s.length(); i++) {
264: char c = s.charAt(i);
265: switch (c) {
266: case '"':
267: buf.append("\\\"");
268: continue;
269: case '\\':
270: buf.append("\\\\");
271: continue;
272: case '\n':
273: buf.append("\\n");
274: continue;
275: case '\r':
276: buf.append("\\r");
277: continue;
278: case '\t':
279: buf.append("\\t");
280: continue;
281: case '\f':
282: buf.append("\\f");
283: continue;
284: case '\b':
285: buf.append("\\b");
286: continue;
287:
288: default:
289: buf.append(c);
290: continue;
291: }
292: }
293: buf.append('"');
294: }
295: }
296:
297: /* ------------------------------------------------------------ */
298: /** Unquote a string.
299: * @param s The string to unquote.
300: * @return quoted string
301: */
302: public static String unquote(String s) {
303: if (s == null)
304: return null;
305: if (s.length() < 2)
306: return s;
307:
308: char first = s.charAt(0);
309: char last = s.charAt(s.length() - 1);
310: if (first != last || (first != '"' && first != '\''))
311: return s;
312:
313: StringBuffer b = new StringBuffer(s.length() - 2);
314: synchronized (b) {
315: boolean escape = false;
316: for (int i = 1; i < s.length() - 1; i++) {
317: char c = s.charAt(i);
318:
319: if (escape) {
320: escape = false;
321: switch (c) {
322: case 'n':
323: b.append('\n');
324: break;
325: case 'r':
326: b.append('\r');
327: break;
328: case 't':
329: b.append('\t');
330: break;
331: case 'f':
332: b.append('\f');
333: break;
334: case 'b':
335: b.append('\b');
336: break;
337: case 'u':
338: b
339: .append((char) ((TypeUtil
340: .convertHexDigit((byte) s
341: .charAt(i++)) << 24)
342: + (TypeUtil
343: .convertHexDigit((byte) s
344: .charAt(i++)) << 16)
345: + (TypeUtil
346: .convertHexDigit((byte) s
347: .charAt(i++)) << 8) + (TypeUtil
348: .convertHexDigit((byte) s
349: .charAt(i++)))));
350: break;
351: default:
352: b.append(c);
353: }
354: } else if (c == '\\') {
355: escape = true;
356: continue;
357: } else
358: b.append(c);
359: }
360:
361: return b.toString();
362: }
363: }
364:
365: /* ------------------------------------------------------------ */
366: /**
367: * @return handle double quotes if true
368: */
369: public boolean getDouble() {
370: return _double;
371: }
372:
373: /* ------------------------------------------------------------ */
374: /**
375: * @param d handle double quotes if true
376: */
377: public void setDouble(boolean d) {
378: _double = d;
379: }
380:
381: /* ------------------------------------------------------------ */
382: /**
383: * @return handle single quotes if true
384: */
385: public boolean getSingle() {
386: return _single;
387: }
388:
389: /* ------------------------------------------------------------ */
390: /**
391: * @param single handle single quotes if true
392: */
393: public void setSingle(boolean single) {
394: _single = single;
395: }
396: }
|