1 // 2 // ======================================================================== 3 // Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd. 4 // ------------------------------------------------------------------------ 5 // All rights reserved. This program and the accompanying materials 6 // are made available under the terms of the Eclipse Public License v1.0 7 // and Apache License v2.0 which accompanies this distribution. 8 // 9 // The Eclipse Public License is available at 10 // http://www.eclipse.org/legal/epl-v10.html 11 // 12 // The Apache License v2.0 is available at 13 // http://www.opensource.org/licenses/apache2.0.php 14 // 15 // You may elect to redistribute this code under either of these licenses. 16 // ======================================================================== 17 // 18 19 package org.eclipse.jetty.util; 20 21 import java.io.UnsupportedEncodingException; 22 import java.nio.charset.Charset; 23 24 import org.eclipse.jetty.util.log.Log; 25 import org.eclipse.jetty.util.log.Logger; 26 27 /** Fast String Utilities. 28 * 29 * These string utilities provide both conveniance methods and 30 * performance improvements over most standard library versions. The 31 * main aim of the optimizations is to avoid object creation unless 32 * absolutely required. 33 * 34 * 35 */ 36 public class StringUtil 37 { 38 private static final Logger LOG = Log.getLogger(StringUtil.class); 39 40 public static final String ALL_INTERFACES="0.0.0.0"; 41 public static final String CRLF="\015\012"; 42 public static final String __LINE_SEPARATOR= 43 System.getProperty("line.separator","\n"); 44 45 public static final String __ISO_8859_1="ISO-8859-1"; 46 public final static String __UTF8="UTF-8"; 47 public final static String __UTF8Alt="UTF8"; 48 public final static String __UTF16="UTF-16"; 49 50 public final static Charset __UTF8_CHARSET; 51 public final static Charset __ISO_8859_1_CHARSET; 52 53 static 54 { 55 __UTF8_CHARSET=Charset.forName(__UTF8); 56 __ISO_8859_1_CHARSET=Charset.forName(__ISO_8859_1); 57 } 58 59 private static char[] lowercases = { 60 '\000','\001','\002','\003','\004','\005','\006','\007', 61 '\010','\011','\012','\013','\014','\015','\016','\017', 62 '\020','\021','\022','\023','\024','\025','\026','\027', 63 '\030','\031','\032','\033','\034','\035','\036','\037', 64 '\040','\041','\042','\043','\044','\045','\046','\047', 65 '\050','\051','\052','\053','\054','\055','\056','\057', 66 '\060','\061','\062','\063','\064','\065','\066','\067', 67 '\070','\071','\072','\073','\074','\075','\076','\077', 68 '\100','\141','\142','\143','\144','\145','\146','\147', 69 '\150','\151','\152','\153','\154','\155','\156','\157', 70 '\160','\161','\162','\163','\164','\165','\166','\167', 71 '\170','\171','\172','\133','\134','\135','\136','\137', 72 '\140','\141','\142','\143','\144','\145','\146','\147', 73 '\150','\151','\152','\153','\154','\155','\156','\157', 74 '\160','\161','\162','\163','\164','\165','\166','\167', 75 '\170','\171','\172','\173','\174','\175','\176','\177' }; 76 77 /* ------------------------------------------------------------ */ 78 /** 79 * fast lower case conversion. Only works on ascii (not unicode) 80 * @param s the string to convert 81 * @return a lower case version of s 82 */ asciiToLowerCase(String s)83 public static String asciiToLowerCase(String s) 84 { 85 char[] c = null; 86 int i=s.length(); 87 88 // look for first conversion 89 while (i-->0) 90 { 91 char c1=s.charAt(i); 92 if (c1<=127) 93 { 94 char c2=lowercases[c1]; 95 if (c1!=c2) 96 { 97 c=s.toCharArray(); 98 c[i]=c2; 99 break; 100 } 101 } 102 } 103 104 while (i-->0) 105 { 106 if(c[i]<=127) 107 c[i] = lowercases[c[i]]; 108 } 109 110 return c==null?s:new String(c); 111 } 112 113 114 /* ------------------------------------------------------------ */ startsWithIgnoreCase(String s,String w)115 public static boolean startsWithIgnoreCase(String s,String w) 116 { 117 if (w==null) 118 return true; 119 120 if (s==null || s.length()<w.length()) 121 return false; 122 123 for (int i=0;i<w.length();i++) 124 { 125 char c1=s.charAt(i); 126 char c2=w.charAt(i); 127 if (c1!=c2) 128 { 129 if (c1<=127) 130 c1=lowercases[c1]; 131 if (c2<=127) 132 c2=lowercases[c2]; 133 if (c1!=c2) 134 return false; 135 } 136 } 137 return true; 138 } 139 140 /* ------------------------------------------------------------ */ endsWithIgnoreCase(String s,String w)141 public static boolean endsWithIgnoreCase(String s,String w) 142 { 143 if (w==null) 144 return true; 145 146 if (s==null) 147 return false; 148 149 int sl=s.length(); 150 int wl=w.length(); 151 152 if (sl<wl) 153 return false; 154 155 for (int i=wl;i-->0;) 156 { 157 char c1=s.charAt(--sl); 158 char c2=w.charAt(i); 159 if (c1!=c2) 160 { 161 if (c1<=127) 162 c1=lowercases[c1]; 163 if (c2<=127) 164 c2=lowercases[c2]; 165 if (c1!=c2) 166 return false; 167 } 168 } 169 return true; 170 } 171 172 /* ------------------------------------------------------------ */ 173 /** 174 * returns the next index of a character from the chars string 175 */ indexFrom(String s,String chars)176 public static int indexFrom(String s,String chars) 177 { 178 for (int i=0;i<s.length();i++) 179 if (chars.indexOf(s.charAt(i))>=0) 180 return i; 181 return -1; 182 } 183 184 /* ------------------------------------------------------------ */ 185 /** 186 * replace substrings within string. 187 */ replace(String s, String sub, String with)188 public static String replace(String s, String sub, String with) 189 { 190 int c=0; 191 int i=s.indexOf(sub,c); 192 if (i == -1) 193 return s; 194 195 StringBuilder buf = new StringBuilder(s.length()+with.length()); 196 197 do 198 { 199 buf.append(s.substring(c,i)); 200 buf.append(with); 201 c=i+sub.length(); 202 } while ((i=s.indexOf(sub,c))!=-1); 203 204 if (c<s.length()) 205 buf.append(s.substring(c,s.length())); 206 207 return buf.toString(); 208 209 } 210 211 212 /* ------------------------------------------------------------ */ 213 /** Remove single or double quotes. 214 */ unquote(String s)215 public static String unquote(String s) 216 { 217 return QuotedStringTokenizer.unquote(s); 218 } 219 220 221 /* ------------------------------------------------------------ */ 222 /** Append substring to StringBuilder 223 * @param buf StringBuilder to append to 224 * @param s String to append from 225 * @param offset The offset of the substring 226 * @param length The length of the substring 227 */ append(StringBuilder buf, String s, int offset, int length)228 public static void append(StringBuilder buf, 229 String s, 230 int offset, 231 int length) 232 { 233 synchronized(buf) 234 { 235 int end=offset+length; 236 for (int i=offset; i<end;i++) 237 { 238 if (i>=s.length()) 239 break; 240 buf.append(s.charAt(i)); 241 } 242 } 243 } 244 245 246 /* ------------------------------------------------------------ */ 247 /** 248 * append hex digit 249 * 250 */ append(StringBuilder buf,byte b,int base)251 public static void append(StringBuilder buf,byte b,int base) 252 { 253 int bi=0xff&b; 254 int c='0'+(bi/base)%base; 255 if (c>'9') 256 c= 'a'+(c-'0'-10); 257 buf.append((char)c); 258 c='0'+bi%base; 259 if (c>'9') 260 c= 'a'+(c-'0'-10); 261 buf.append((char)c); 262 } 263 264 /* ------------------------------------------------------------ */ append2digits(StringBuffer buf,int i)265 public static void append2digits(StringBuffer buf,int i) 266 { 267 if (i<100) 268 { 269 buf.append((char)(i/10+'0')); 270 buf.append((char)(i%10+'0')); 271 } 272 } 273 274 /* ------------------------------------------------------------ */ append2digits(StringBuilder buf,int i)275 public static void append2digits(StringBuilder buf,int i) 276 { 277 if (i<100) 278 { 279 buf.append((char)(i/10+'0')); 280 buf.append((char)(i%10+'0')); 281 } 282 } 283 284 /* ------------------------------------------------------------ */ 285 /** Return a non null string. 286 * @param s String 287 * @return The string passed in or empty string if it is null. 288 */ nonNull(String s)289 public static String nonNull(String s) 290 { 291 if (s==null) 292 return ""; 293 return s; 294 } 295 296 /* ------------------------------------------------------------ */ equals(String s,char[] buf, int offset, int length)297 public static boolean equals(String s,char[] buf, int offset, int length) 298 { 299 if (s.length()!=length) 300 return false; 301 for (int i=0;i<length;i++) 302 if (buf[offset+i]!=s.charAt(i)) 303 return false; 304 return true; 305 } 306 307 /* ------------------------------------------------------------ */ toUTF8String(byte[] b,int offset,int length)308 public static String toUTF8String(byte[] b,int offset,int length) 309 { 310 try 311 { 312 return new String(b,offset,length,__UTF8); 313 } 314 catch (UnsupportedEncodingException e) 315 { 316 throw new IllegalArgumentException(e); 317 } 318 } 319 320 /* ------------------------------------------------------------ */ toString(byte[] b,int offset,int length,String charset)321 public static String toString(byte[] b,int offset,int length,String charset) 322 { 323 try 324 { 325 return new String(b,offset,length,charset); 326 } 327 catch (UnsupportedEncodingException e) 328 { 329 throw new IllegalArgumentException(e); 330 } 331 } 332 333 334 /* ------------------------------------------------------------ */ isUTF8(String charset)335 public static boolean isUTF8(String charset) 336 { 337 return __UTF8.equalsIgnoreCase(charset)||__UTF8Alt.equalsIgnoreCase(charset); 338 } 339 340 341 /* ------------------------------------------------------------ */ printable(String name)342 public static String printable(String name) 343 { 344 if (name==null) 345 return null; 346 StringBuilder buf = new StringBuilder(name.length()); 347 for (int i=0;i<name.length();i++) 348 { 349 char c=name.charAt(i); 350 if (!Character.isISOControl(c)) 351 buf.append(c); 352 } 353 return buf.toString(); 354 } 355 356 /* ------------------------------------------------------------ */ printable(byte[] b)357 public static String printable(byte[] b) 358 { 359 StringBuilder buf = new StringBuilder(); 360 for (int i=0;i<b.length;i++) 361 { 362 char c=(char)b[i]; 363 if (Character.isWhitespace(c)|| c>' ' && c<0x7f) 364 buf.append(c); 365 else 366 { 367 buf.append("0x"); 368 TypeUtil.toHex(b[i],buf); 369 } 370 } 371 return buf.toString(); 372 } 373 getBytes(String s)374 public static byte[] getBytes(String s) 375 { 376 try 377 { 378 return s.getBytes(__ISO_8859_1); 379 } 380 catch(Exception e) 381 { 382 LOG.warn(e); 383 return s.getBytes(); 384 } 385 } 386 getBytes(String s,String charset)387 public static byte[] getBytes(String s,String charset) 388 { 389 try 390 { 391 return s.getBytes(charset); 392 } 393 catch(Exception e) 394 { 395 LOG.warn(e); 396 return s.getBytes(); 397 } 398 } 399 400 401 402 /** 403 * Converts a binary SID to a string SID 404 * 405 * http://en.wikipedia.org/wiki/Security_Identifier 406 * 407 * S-1-IdentifierAuthority-SubAuthority1-SubAuthority2-...-SubAuthorityn 408 */ sidBytesToString(byte[] sidBytes)409 public static String sidBytesToString(byte[] sidBytes) 410 { 411 StringBuilder sidString = new StringBuilder(); 412 413 // Identify this as a SID 414 sidString.append("S-"); 415 416 // Add SID revision level (expect 1 but may change someday) 417 sidString.append(Byte.toString(sidBytes[0])).append('-'); 418 419 StringBuilder tmpBuilder = new StringBuilder(); 420 421 // crunch the six bytes of issuing authority value 422 for (int i = 2; i <= 7; ++i) 423 { 424 tmpBuilder.append(Integer.toHexString(sidBytes[i] & 0xFF)); 425 } 426 427 sidString.append(Long.parseLong(tmpBuilder.toString(), 16)); // '-' is in the subauth loop 428 429 // the number of subAuthorities we need to attach 430 int subAuthorityCount = sidBytes[1]; 431 432 // attach each of the subAuthorities 433 for (int i = 0; i < subAuthorityCount; ++i) 434 { 435 int offset = i * 4; 436 tmpBuilder.setLength(0); 437 // these need to be zero padded hex and little endian 438 tmpBuilder.append(String.format("%02X%02X%02X%02X", 439 (sidBytes[11 + offset] & 0xFF), 440 (sidBytes[10 + offset] & 0xFF), 441 (sidBytes[9 + offset] & 0xFF), 442 (sidBytes[8 + offset] & 0xFF))); 443 sidString.append('-').append(Long.parseLong(tmpBuilder.toString(), 16)); 444 } 445 446 return sidString.toString(); 447 } 448 449 /** 450 * Converts a string SID to a binary SID 451 * 452 * http://en.wikipedia.org/wiki/Security_Identifier 453 * 454 * S-1-IdentifierAuthority-SubAuthority1-SubAuthority2-...-SubAuthorityn 455 */ sidStringToBytes( String sidString )456 public static byte[] sidStringToBytes( String sidString ) 457 { 458 String[] sidTokens = sidString.split("-"); 459 460 int subAuthorityCount = sidTokens.length - 3; // S-Rev-IdAuth- 461 462 int byteCount = 0; 463 byte[] sidBytes = new byte[1 + 1 + 6 + (4 * subAuthorityCount)]; 464 465 // the revision byte 466 sidBytes[byteCount++] = (byte)Integer.parseInt(sidTokens[1]); 467 468 // the # of sub authorities byte 469 sidBytes[byteCount++] = (byte)subAuthorityCount; 470 471 // the certAuthority 472 String hexStr = Long.toHexString(Long.parseLong(sidTokens[2])); 473 474 while( hexStr.length() < 12) // pad to 12 characters 475 { 476 hexStr = "0" + hexStr; 477 } 478 479 // place the certAuthority 6 bytes 480 for ( int i = 0 ; i < hexStr.length(); i = i + 2) 481 { 482 sidBytes[byteCount++] = (byte)Integer.parseInt(hexStr.substring(i, i + 2),16); 483 } 484 485 486 for ( int i = 3; i < sidTokens.length ; ++i) 487 { 488 hexStr = Long.toHexString(Long.parseLong(sidTokens[i])); 489 490 while( hexStr.length() < 8) // pad to 8 characters 491 { 492 hexStr = "0" + hexStr; 493 } 494 495 // place the inverted sub authorities, 4 bytes each 496 for ( int j = hexStr.length(); j > 0; j = j - 2) 497 { 498 sidBytes[byteCount++] = (byte)Integer.parseInt(hexStr.substring(j-2, j),16); 499 } 500 } 501 502 return sidBytes; 503 } 504 } 505