// ********************************************************************** // // Copyright (c) 2003-2010 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) { IllegalArgumentException e = new IllegalArgumentException("unsupported encoding"); e.initCause(ex); throw e; } } // // 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 } }