diff options
Diffstat (limited to 'java/src/IceUtilInternal/StringUtil.java')
-rw-r--r-- | java/src/IceUtilInternal/StringUtil.java | 486 |
1 files changed, 486 insertions, 0 deletions
diff --git a/java/src/IceUtilInternal/StringUtil.java b/java/src/IceUtilInternal/StringUtil.java new file mode 100644 index 00000000000..7029a0fce87 --- /dev/null +++ b/java/src/IceUtilInternal/StringUtil.java @@ -0,0 +1,486 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2011 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +package IceUtilInternal; + +public final class StringUtil +{ + // + // Return the index of the first character in str to + // appear in match, starting from 0. Returns -1 if none is + // found. + // + public static int + findFirstOf(String str, String match) + { + return findFirstOf(str, match, 0); + } + + // + // Return the index of the first character in str to + // appear in match, starting from start. Returns -1 if none is + // found. + // + public static int + findFirstOf(String str, String match, int start) + { + final int len = str.length(); + for(int i = start; i < len; i++) + { + char ch = str.charAt(i); + if(match.indexOf(ch) != -1) + { + return i; + } + } + + return -1; + } + + // + // Return the index of the first character in str which does + // not appear in match, starting from 0. Returns -1 if none is + // found. + // + public static int + findFirstNotOf(String str, String match) + { + return findFirstNotOf(str, match, 0); + } + + // + // Return the index of the first character in str which does + // not appear in match, starting from start. Returns -1 if none is + // found. + // + public static int + findFirstNotOf(String str, String match, int start) + { + final int len = str.length(); + for(int i = start; i < len; i++) + { + char ch = str.charAt(i); + if(match.indexOf(ch) == -1) + { + return i; + } + } + + return -1; + } + + // + // Write the byte b as an escape sequence if it isn't a printable ASCII + // character and append the escape sequence to sb. Additional characters + // that should be escaped can be passed in special. If b is any of these + // characters, b is preceded by a backslash in sb. + // + private static void + encodeChar(byte b, StringBuilder sb, String special) + { + switch(b) + { + case (byte)'\\': + { + sb.append("\\\\"); + break; + } + case (byte)'\'': + { + sb.append("\\'"); + break; + } + case (byte)'"': + { + sb.append("\\\""); + break; + } + case (byte)'\b': + { + sb.append("\\b"); + break; + } + case (byte)'\f': + { + sb.append("\\f"); + break; + } + case (byte)'\n': + { + sb.append("\\n"); + break; + } + case (byte)'\r': + { + sb.append("\\r"); + break; + } + case (byte)'\t': + { + sb.append("\\t"); + break; + } + default: + { + if(!(b >= 32 && b <= 126)) + { + sb.append('\\'); + String octal = Integer.toOctalString(b < 0 ? b + 256 : b); + // + // Add leading zeroes so that we avoid problems during + // decoding. For example, consider the encoded string + // \0013 (i.e., a character with value 1 followed by + // the character '3'). If the leading zeroes were omitted, + // the result would be incorrectly interpreted by the + // decoder as a single character with value 11. + // + for(int j = octal.length(); j < 3; j++) + { + sb.append('0'); + } + sb.append(octal); + } + else if(special != null && special.indexOf((char)b) != -1) + { + sb.append('\\'); + sb.append((char)b); + } + else + { + sb.append((char)b); + } + } + } + } + + // + // Add escape sequences (such as "\n", or "\007") to make a string + // readable in ASCII. Any characters that appear in special are + // prefixed with a backlash in the returned string. + // + public static String + escapeString(String s, String special) + { + if(special != null) + { + for(int i = 0; i < special.length(); ++i) + { + if(special.charAt(i) < 32 || special.charAt(i) > 126) + { + throw new IllegalArgumentException("special characters must be in ASCII range 32-126"); + } + } + } + + byte[] bytes = null; + try + { + bytes = s.getBytes("UTF8"); + } + catch(java.io.UnsupportedEncodingException ex) + { + assert(false); + return null; + } + + StringBuilder result = new StringBuilder(bytes.length); + for(int i = 0; i < bytes.length; i++) + { + encodeChar(bytes[i], result, special); + } + + return result.toString(); + } + + private static char + checkChar(String s, int pos) + { + char c = s.charAt(pos); + if(!(c >= 32 && c <= 126)) + { + String msg; + if(pos > 0) + { + msg = "character after `" + s.substring(0, pos) + "'"; + } + else + { + msg = "first character"; + } + msg += " is not a printable ASCII character (ordinal " + (int)c + ")"; + throw new IllegalArgumentException(msg); + } + return c; + } + + // + // Decode the character or escape sequence starting at start and return it. + // newStart is set to the index of the first character following the decoded character + // or escape sequence. + // + private static char decodeChar(String s, int start, int end, Ice.IntHolder nextStart) + { + assert(start >= 0); + assert(start < end); + assert(end <= s.length()); + + char c; + + if(s.charAt(start) != '\\') + { + c = checkChar(s, start++); + } + else + { + if(start + 1 == end) + { + throw new IllegalArgumentException("trailing backslash"); + } + switch(s.charAt(++start)) + { + case '\\': + case '\'': + case '"': + { + c = s.charAt(start++); + break; + } + case 'b': + { + ++start; + c = '\b'; + break; + } + case 'f': + { + ++start; + c = '\f'; + break; + } + case 'n': + { + ++start; + c = '\n'; + break; + } + case 'r': + { + ++start; + c = '\r'; + break; + } + case 't': + { + ++start; + c = '\t'; + break; + } + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + { + int val = 0; + for(int j = 0; j < 3 && start < end; ++j) + { + int charVal = s.charAt(start++) - '0'; + if(charVal < 0 || charVal > 7) + { + --start; + break; + } + val = val * 8 + charVal; + } + if(val > 255) + { + String msg = "octal value \\" + Integer.toOctalString(val) + " (" + val + ") is out of range"; + throw new IllegalArgumentException(msg); + } + c = (char)val; + break; + } + default: + { + c = checkChar(s, start++); + break; + } + } + } + nextStart.value = start; + return c; + } + + // + // Remove escape sequences from s and append the result to sb. + // Return true if successful, false otherwise. + // + private static void + decodeString(String s, int start, int end, StringBuilder sb) + { + Ice.IntHolder nextStart = new Ice.IntHolder(); + while(start < end) + { + sb.append(decodeChar(s, start, end, nextStart)); + start = nextStart.value; + } + } + + // + // Remove escape sequences added by escapeString. Throws IllegalArgumentException + // for an invalid input string. + // + public static String + unescapeString(String s, int start, int end) + { + assert(start >= 0 && start <= end && end <= s.length()); + + StringBuilder sb = new StringBuilder(end - start); + decodeString(s, start, end, sb); + String decodedString = sb.toString(); + + byte[] arr = new byte[decodedString.length()]; + for(int i = 0; i < arr.length; ++i) + { + arr[i] = (byte)decodedString.charAt(i); + } + + try + { + return new String(arr, 0, arr.length, "UTF8"); + } + catch(java.io.UnsupportedEncodingException ex) + { + throw new IllegalArgumentException("unsupported encoding", ex); + } + } + + // + // Join a list of strings using the given delimiter. + // + public static String + joinString(java.util.List<String> values, String delimiter) + { + StringBuffer s = new StringBuffer(); + boolean first = true; + for(String v : values) + { + if(!first) + { + s.append(delimiter); + } + s.append(v); + first = false; + } + return s.toString(); + } + + // + // Split string helper; returns null for unmatched quotes + // + static public String[] + splitString(String str, String delim) + { + java.util.List<String> l = new java.util.ArrayList<String>(); + char[] arr = new char[str.length()]; + int pos = 0; + + int n = 0; + char quoteChar = '\0'; + while(pos < str.length()) + { + if(quoteChar == '\0' && (str.charAt(pos) == '"' || str.charAt(pos) == '\'')) + { + quoteChar = str.charAt(pos++); + continue; // Skip the quote. + } + else if(quoteChar == '\0' && str.charAt(pos) == '\\' && pos + 1 < str.length() && + (str.charAt(pos + 1) == '"' || str.charAt(pos + 1) == '\'')) + { + ++pos; // Skip the backslash + } + else if(quoteChar != '\0' && str.charAt(pos) == '\\' && pos + 1 < str.length() && + str.charAt(pos + 1) == quoteChar) + { + ++pos; // Skip the backslash + } + else if(quoteChar != '\0' && str.charAt(pos) == quoteChar) + { + ++pos; + quoteChar = '\0'; + continue; // Skip the quote. + } + else if(delim.indexOf(str.charAt(pos)) != -1) + { + if(quoteChar == '\0') + { + ++pos; + if(n > 0) + { + l.add(new String(arr, 0, n)); + n = 0; + } + continue; + } + } + + if(pos < str.length()) + { + arr[n++] = str.charAt(pos++); + } + } + + if(n > 0) + { + l.add(new String(arr, 0, n)); + } + if(quoteChar != '\0') + { + return null; // Unmatched quote. + } + return (String[])l.toArray(new String[0]); + } + + public static int + checkQuote(String s) + { + return checkQuote(s, 0); + } + + // + // If a single or double quotation mark is found at the start position, + // then the position of the matching closing quote is returned. If no + // quotation mark is found at the start position, then 0 is returned. + // If no matching closing quote is found, then -1 is returned. + // + public static int + checkQuote(String s, int start) + { + char quoteChar = s.charAt(start); + if(quoteChar == '"' || quoteChar == '\'') + { + start++; + final int len = s.length(); + int pos; + while(start < len && (pos = s.indexOf(quoteChar, start)) != -1) + { + if(s.charAt(pos - 1) != '\\') + { + return pos; + } + start = pos + 1; + } + return -1; // Unmatched quote + } + return 0; // Not quoted + } +} |