| // |
| // ======================================================================== |
| // Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd. |
| // ------------------------------------------------------------------------ |
| // All rights reserved. This program and the accompanying materials |
| // are made available under the terms of the Eclipse Public License v1.0 |
| // and Apache License v2.0 which accompanies this distribution. |
| // |
| // The Eclipse Public License is available at |
| // http://www.eclipse.org/legal/epl-v10.html |
| // |
| // The Apache License v2.0 is available at |
| // http://www.opensource.org/licenses/apache2.0.php |
| // |
| // You may elect to redistribute this code under either of these licenses. |
| // ======================================================================== |
| // |
| |
| package org.eclipse.jetty.util; |
| |
| import java.io.IOException; |
| import java.util.Arrays; |
| import java.util.NoSuchElementException; |
| import java.util.StringTokenizer; |
| |
| /* ------------------------------------------------------------ */ |
| /** StringTokenizer with Quoting support. |
| * |
| * This class is a copy of the java.util.StringTokenizer API and |
| * the behaviour is the same, except that single and double quoted |
| * string values are recognised. |
| * Delimiters within quotes are not considered delimiters. |
| * Quotes can be escaped with '\'. |
| * |
| * @see java.util.StringTokenizer |
| * |
| */ |
| public class QuotedStringTokenizer |
| extends StringTokenizer |
| { |
| private final static String __delim="\t\n\r"; |
| private String _string; |
| private String _delim = __delim; |
| private boolean _returnQuotes=false; |
| private boolean _returnDelimiters=false; |
| private StringBuffer _token; |
| private boolean _hasToken=false; |
| private int _i=0; |
| private int _lastStart=0; |
| private boolean _double=true; |
| private boolean _single=true; |
| |
| /* ------------------------------------------------------------ */ |
| public QuotedStringTokenizer(String str, |
| String delim, |
| boolean returnDelimiters, |
| boolean returnQuotes) |
| { |
| super(""); |
| _string=str; |
| if (delim!=null) |
| _delim=delim; |
| _returnDelimiters=returnDelimiters; |
| _returnQuotes=returnQuotes; |
| |
| if (_delim.indexOf('\'')>=0 || |
| _delim.indexOf('"')>=0) |
| throw new Error("Can't use quotes as delimiters: "+_delim); |
| |
| _token=new StringBuffer(_string.length()>1024?512:_string.length()/2); |
| } |
| |
| /* ------------------------------------------------------------ */ |
| public QuotedStringTokenizer(String str, |
| String delim, |
| boolean returnDelimiters) |
| { |
| this(str,delim,returnDelimiters,false); |
| } |
| |
| /* ------------------------------------------------------------ */ |
| public QuotedStringTokenizer(String str, |
| String delim) |
| { |
| this(str,delim,false,false); |
| } |
| |
| /* ------------------------------------------------------------ */ |
| public QuotedStringTokenizer(String str) |
| { |
| this(str,null,false,false); |
| } |
| |
| /* ------------------------------------------------------------ */ |
| @Override |
| public boolean hasMoreTokens() |
| { |
| // Already found a token |
| if (_hasToken) |
| return true; |
| |
| _lastStart=_i; |
| |
| int state=0; |
| boolean escape=false; |
| while (_i<_string.length()) |
| { |
| char c=_string.charAt(_i++); |
| |
| switch (state) |
| { |
| case 0: // Start |
| if(_delim.indexOf(c)>=0) |
| { |
| if (_returnDelimiters) |
| { |
| _token.append(c); |
| return _hasToken=true; |
| } |
| } |
| else if (c=='\'' && _single) |
| { |
| if (_returnQuotes) |
| _token.append(c); |
| state=2; |
| } |
| else if (c=='\"' && _double) |
| { |
| if (_returnQuotes) |
| _token.append(c); |
| state=3; |
| } |
| else |
| { |
| _token.append(c); |
| _hasToken=true; |
| state=1; |
| } |
| break; |
| |
| case 1: // Token |
| _hasToken=true; |
| if(_delim.indexOf(c)>=0) |
| { |
| if (_returnDelimiters) |
| _i--; |
| return _hasToken; |
| } |
| else if (c=='\'' && _single) |
| { |
| if (_returnQuotes) |
| _token.append(c); |
| state=2; |
| } |
| else if (c=='\"' && _double) |
| { |
| if (_returnQuotes) |
| _token.append(c); |
| state=3; |
| } |
| else |
| { |
| _token.append(c); |
| } |
| break; |
| |
| case 2: // Single Quote |
| _hasToken=true; |
| if (escape) |
| { |
| escape=false; |
| _token.append(c); |
| } |
| else if (c=='\'') |
| { |
| if (_returnQuotes) |
| _token.append(c); |
| state=1; |
| } |
| else if (c=='\\') |
| { |
| if (_returnQuotes) |
| _token.append(c); |
| escape=true; |
| } |
| else |
| { |
| _token.append(c); |
| } |
| break; |
| |
| case 3: // Double Quote |
| _hasToken=true; |
| if (escape) |
| { |
| escape=false; |
| _token.append(c); |
| } |
| else if (c=='\"') |
| { |
| if (_returnQuotes) |
| _token.append(c); |
| state=1; |
| } |
| else if (c=='\\') |
| { |
| if (_returnQuotes) |
| _token.append(c); |
| escape=true; |
| } |
| else |
| { |
| _token.append(c); |
| } |
| break; |
| } |
| } |
| |
| return _hasToken; |
| } |
| |
| /* ------------------------------------------------------------ */ |
| @Override |
| public String nextToken() |
| throws NoSuchElementException |
| { |
| if (!hasMoreTokens() || _token==null) |
| throw new NoSuchElementException(); |
| String t=_token.toString(); |
| _token.setLength(0); |
| _hasToken=false; |
| return t; |
| } |
| |
| /* ------------------------------------------------------------ */ |
| @Override |
| public String nextToken(String delim) |
| throws NoSuchElementException |
| { |
| _delim=delim; |
| _i=_lastStart; |
| _token.setLength(0); |
| _hasToken=false; |
| return nextToken(); |
| } |
| |
| /* ------------------------------------------------------------ */ |
| @Override |
| public boolean hasMoreElements() |
| { |
| return hasMoreTokens(); |
| } |
| |
| /* ------------------------------------------------------------ */ |
| @Override |
| public Object nextElement() |
| throws NoSuchElementException |
| { |
| return nextToken(); |
| } |
| |
| /* ------------------------------------------------------------ */ |
| /** Not implemented. |
| */ |
| @Override |
| public int countTokens() |
| { |
| return -1; |
| } |
| |
| |
| /* ------------------------------------------------------------ */ |
| /** Quote a string. |
| * The string is quoted only if quoting is required due to |
| * embedded delimiters, quote characters or the |
| * empty string. |
| * @param s The string to quote. |
| * @param delim the delimiter to use to quote the string |
| * @return quoted string |
| */ |
| public static String quoteIfNeeded(String s, String delim) |
| { |
| if (s==null) |
| return null; |
| if (s.length()==0) |
| return "\"\""; |
| |
| |
| for (int i=0;i<s.length();i++) |
| { |
| char c = s.charAt(i); |
| if (c=='\\' || c=='"' || c=='\'' || Character.isWhitespace(c) || delim.indexOf(c)>=0) |
| { |
| StringBuffer b=new StringBuffer(s.length()+8); |
| quote(b,s); |
| return b.toString(); |
| } |
| } |
| |
| return s; |
| } |
| |
| /* ------------------------------------------------------------ */ |
| /** Quote a string. |
| * The string is quoted only if quoting is required due to |
| * embeded delimiters, quote characters or the |
| * empty string. |
| * @param s The string to quote. |
| * @return quoted string |
| */ |
| public static String quote(String s) |
| { |
| if (s==null) |
| return null; |
| if (s.length()==0) |
| return "\"\""; |
| |
| StringBuffer b=new StringBuffer(s.length()+8); |
| quote(b,s); |
| return b.toString(); |
| |
| } |
| |
| private static final char[] escapes = new char[32]; |
| static |
| { |
| Arrays.fill(escapes, (char)0xFFFF); |
| escapes['\b'] = 'b'; |
| escapes['\t'] = 't'; |
| escapes['\n'] = 'n'; |
| escapes['\f'] = 'f'; |
| escapes['\r'] = 'r'; |
| } |
| |
| /* ------------------------------------------------------------ */ |
| /** Quote a string into an Appendable. |
| * Only quotes and backslash are escaped. |
| * @param buffer The Appendable |
| * @param input The String to quote. |
| */ |
| public static void quoteOnly(Appendable buffer, String input) |
| { |
| if(input==null) |
| return; |
| |
| try |
| { |
| buffer.append('"'); |
| for (int i = 0; i < input.length(); ++i) |
| { |
| char c = input.charAt(i); |
| if (c == '"' || c == '\\') |
| buffer.append('\\'); |
| buffer.append(c); |
| } |
| buffer.append('"'); |
| } |
| catch (IOException x) |
| { |
| throw new RuntimeException(x); |
| } |
| } |
| |
| /* ------------------------------------------------------------ */ |
| /** Quote a string into an Appendable. |
| * The characters ", \, \n, \r, \t, \f and \b are escaped |
| * @param buffer The Appendable |
| * @param input The String to quote. |
| */ |
| public static void quote(Appendable buffer, String input) |
| { |
| if(input==null) |
| return; |
| |
| try |
| { |
| buffer.append('"'); |
| for (int i = 0; i < input.length(); ++i) |
| { |
| char c = input.charAt(i); |
| if (c >= 32) |
| { |
| if (c == '"' || c == '\\') |
| buffer.append('\\'); |
| buffer.append(c); |
| } |
| else |
| { |
| char escape = escapes[c]; |
| if (escape == 0xFFFF) |
| { |
| // Unicode escape |
| buffer.append('\\').append('u').append('0').append('0'); |
| if (c < 0x10) |
| buffer.append('0'); |
| buffer.append(Integer.toString(c, 16)); |
| } |
| else |
| { |
| buffer.append('\\').append(escape); |
| } |
| } |
| } |
| buffer.append('"'); |
| } |
| catch (IOException x) |
| { |
| throw new RuntimeException(x); |
| } |
| } |
| |
| |
| /* ------------------------------------------------------------ */ |
| public static String unquoteOnly(String s) |
| { |
| return unquoteOnly(s, false); |
| } |
| |
| |
| /* ------------------------------------------------------------ */ |
| /** Unquote a string, NOT converting unicode sequences |
| * @param s The string to unquote. |
| * @param lenient if true, will leave in backslashes that aren't valid escapes |
| * @return quoted string |
| */ |
| public static String unquoteOnly(String s, boolean lenient) |
| { |
| if (s==null) |
| return null; |
| if (s.length()<2) |
| return s; |
| |
| char first=s.charAt(0); |
| char last=s.charAt(s.length()-1); |
| if (first!=last || (first!='"' && first!='\'')) |
| return s; |
| |
| StringBuilder b = new StringBuilder(s.length() - 2); |
| boolean escape=false; |
| for (int i=1;i<s.length()-1;i++) |
| { |
| char c = s.charAt(i); |
| |
| if (escape) |
| { |
| escape=false; |
| if (lenient && !isValidEscaping(c)) |
| { |
| b.append('\\'); |
| } |
| b.append(c); |
| } |
| else if (c=='\\') |
| { |
| escape=true; |
| } |
| else |
| { |
| b.append(c); |
| } |
| } |
| |
| return b.toString(); |
| } |
| |
| /* ------------------------------------------------------------ */ |
| public static String unquote(String s) |
| { |
| return unquote(s,false); |
| } |
| |
| /* ------------------------------------------------------------ */ |
| /** Unquote a string. |
| * @param s The string to unquote. |
| * @return quoted string |
| */ |
| public static String unquote(String s, boolean lenient) |
| { |
| if (s==null) |
| return null; |
| if (s.length()<2) |
| return s; |
| |
| char first=s.charAt(0); |
| char last=s.charAt(s.length()-1); |
| if (first!=last || (first!='"' && first!='\'')) |
| return s; |
| |
| StringBuilder b = new StringBuilder(s.length() - 2); |
| boolean escape=false; |
| for (int i=1;i<s.length()-1;i++) |
| { |
| char c = s.charAt(i); |
| |
| if (escape) |
| { |
| escape=false; |
| switch (c) |
| { |
| case 'n': |
| b.append('\n'); |
| break; |
| case 'r': |
| b.append('\r'); |
| break; |
| case 't': |
| b.append('\t'); |
| break; |
| case 'f': |
| b.append('\f'); |
| break; |
| case 'b': |
| b.append('\b'); |
| break; |
| case '\\': |
| b.append('\\'); |
| break; |
| case '/': |
| b.append('/'); |
| break; |
| case '"': |
| b.append('"'); |
| break; |
| case 'u': |
| b.append((char)( |
| (TypeUtil.convertHexDigit((byte)s.charAt(i++))<<24)+ |
| (TypeUtil.convertHexDigit((byte)s.charAt(i++))<<16)+ |
| (TypeUtil.convertHexDigit((byte)s.charAt(i++))<<8)+ |
| (TypeUtil.convertHexDigit((byte)s.charAt(i++))) |
| ) |
| ); |
| break; |
| default: |
| if (lenient && !isValidEscaping(c)) |
| { |
| b.append('\\'); |
| } |
| b.append(c); |
| } |
| } |
| else if (c=='\\') |
| { |
| escape=true; |
| } |
| else |
| { |
| b.append(c); |
| } |
| } |
| |
| return b.toString(); |
| } |
| |
| |
| /* ------------------------------------------------------------ */ |
| /** Check that char c (which is preceded by a backslash) is a valid |
| * escape sequence. |
| * @param c |
| * @return |
| */ |
| private static boolean isValidEscaping(char c) |
| { |
| return ((c == 'n') || (c == 'r') || (c == 't') || |
| (c == 'f') || (c == 'b') || (c == '\\') || |
| (c == '/') || (c == '"') || (c == 'u')); |
| } |
| |
| /* ------------------------------------------------------------ */ |
| public static boolean isQuoted(String s) |
| { |
| return s!=null && s.length()>0 && s.charAt(0)=='"' && s.charAt(s.length()-1)=='"'; |
| } |
| |
| /* ------------------------------------------------------------ */ |
| /** |
| * @return handle double quotes if true |
| */ |
| public boolean getDouble() |
| { |
| return _double; |
| } |
| |
| /* ------------------------------------------------------------ */ |
| /** |
| * @param d handle double quotes if true |
| */ |
| public void setDouble(boolean d) |
| { |
| _double=d; |
| } |
| |
| /* ------------------------------------------------------------ */ |
| /** |
| * @return handle single quotes if true |
| */ |
| public boolean getSingle() |
| { |
| return _single; |
| } |
| |
| /* ------------------------------------------------------------ */ |
| /** |
| * @param single handle single quotes if true |
| */ |
| public void setSingle(boolean single) |
| { |
| _single=single; |
| } |
| } |