001/** 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, software 013 * distributed under the License is distributed on an "AS IS" BASIS, 014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 015 * See the License for the specific language governing permissions and 016 * limitations under the License. 017 */ 018 019package org.apache.hadoop.util; 020 021import java.io.PrintWriter; 022import java.io.StringWriter; 023import java.net.URI; 024import java.net.URISyntaxException; 025import java.text.DateFormat; 026import java.util.ArrayList; 027import java.util.Arrays; 028import java.util.Collection; 029import java.util.Date; 030import java.util.Iterator; 031import java.util.LinkedHashSet; 032import java.util.List; 033import java.util.Locale; 034import java.util.Map; 035import java.util.Set; 036import java.util.StringTokenizer; 037import java.util.regex.Matcher; 038import java.util.regex.Pattern; 039 040import org.apache.commons.lang.SystemUtils; 041import org.apache.hadoop.classification.InterfaceAudience; 042import org.apache.hadoop.classification.InterfaceStability; 043import org.apache.hadoop.fs.Path; 044import org.apache.hadoop.net.NetUtils; 045 046import com.google.common.net.InetAddresses; 047 048/** 049 * General string utils 050 */ 051@InterfaceAudience.Private 052@InterfaceStability.Unstable 053public class StringUtils { 054 055 /** 056 * Priority of the StringUtils shutdown hook. 057 */ 058 public static final int SHUTDOWN_HOOK_PRIORITY = 0; 059 060 /** 061 * Shell environment variables: $ followed by one letter or _ followed by 062 * multiple letters, numbers, or underscores. The group captures the 063 * environment variable name without the leading $. 064 */ 065 public static final Pattern SHELL_ENV_VAR_PATTERN = 066 Pattern.compile("\\$([A-Za-z_]{1}[A-Za-z0-9_]*)"); 067 068 /** 069 * Windows environment variables: surrounded by %. The group captures the 070 * environment variable name without the leading and trailing %. 071 */ 072 public static final Pattern WIN_ENV_VAR_PATTERN = Pattern.compile("%(.*?)%"); 073 074 /** 075 * Regular expression that matches and captures environment variable names 076 * according to platform-specific rules. 077 */ 078 public static final Pattern ENV_VAR_PATTERN = Shell.WINDOWS ? 079 WIN_ENV_VAR_PATTERN : SHELL_ENV_VAR_PATTERN; 080 081 /** 082 * Make a string representation of the exception. 083 * @param e The exception to stringify 084 * @return A string with exception name and call stack. 085 */ 086 public static String stringifyException(Throwable e) { 087 StringWriter stm = new StringWriter(); 088 PrintWriter wrt = new PrintWriter(stm); 089 e.printStackTrace(wrt); 090 wrt.close(); 091 return stm.toString(); 092 } 093 094 /** 095 * Given a full hostname, return the word upto the first dot. 096 * @param fullHostname the full hostname 097 * @return the hostname to the first dot 098 */ 099 public static String simpleHostname(String fullHostname) { 100 if (InetAddresses.isInetAddress(fullHostname)) { 101 return fullHostname; 102 } 103 int offset = fullHostname.indexOf('.'); 104 if (offset != -1) { 105 return fullHostname.substring(0, offset); 106 } 107 return fullHostname; 108 } 109 110 /** 111 * Given an integer, return a string that is in an approximate, but human 112 * readable format. 113 * @param number the number to format 114 * @return a human readable form of the integer 115 * 116 * @deprecated use {@link TraditionalBinaryPrefix#long2String(long, String, int)}. 117 */ 118 @Deprecated 119 public static String humanReadableInt(long number) { 120 return TraditionalBinaryPrefix.long2String(number, "", 1); 121 } 122 123 /** The same as String.format(Locale.ENGLISH, format, objects). */ 124 public static String format(final String format, final Object... objects) { 125 return String.format(Locale.ENGLISH, format, objects); 126 } 127 128 /** 129 * Format a percentage for presentation to the user. 130 * @param fraction the percentage as a fraction, e.g. 0.1 = 10% 131 * @param decimalPlaces the number of decimal places 132 * @return a string representation of the percentage 133 */ 134 public static String formatPercent(double fraction, int decimalPlaces) { 135 return format("%." + decimalPlaces + "f%%", fraction*100); 136 } 137 138 /** 139 * Given an array of strings, return a comma-separated list of its elements. 140 * @param strs Array of strings 141 * @return Empty string if strs.length is 0, comma separated list of strings 142 * otherwise 143 */ 144 145 public static String arrayToString(String[] strs) { 146 if (strs.length == 0) { return ""; } 147 StringBuilder sbuf = new StringBuilder(); 148 sbuf.append(strs[0]); 149 for (int idx = 1; idx < strs.length; idx++) { 150 sbuf.append(","); 151 sbuf.append(strs[idx]); 152 } 153 return sbuf.toString(); 154 } 155 156 /** 157 * Given an array of bytes it will convert the bytes to a hex string 158 * representation of the bytes 159 * @param bytes 160 * @param start start index, inclusively 161 * @param end end index, exclusively 162 * @return hex string representation of the byte array 163 */ 164 public static String byteToHexString(byte[] bytes, int start, int end) { 165 if (bytes == null) { 166 throw new IllegalArgumentException("bytes == null"); 167 } 168 StringBuilder s = new StringBuilder(); 169 for(int i = start; i < end; i++) { 170 s.append(format("%02x", bytes[i])); 171 } 172 return s.toString(); 173 } 174 175 /** Same as byteToHexString(bytes, 0, bytes.length). */ 176 public static String byteToHexString(byte bytes[]) { 177 return byteToHexString(bytes, 0, bytes.length); 178 } 179 180 /** 181 * Given a hexstring this will return the byte array corresponding to the 182 * string 183 * @param hex the hex String array 184 * @return a byte array that is a hex string representation of the given 185 * string. The size of the byte array is therefore hex.length/2 186 */ 187 public static byte[] hexStringToByte(String hex) { 188 byte[] bts = new byte[hex.length() / 2]; 189 for (int i = 0; i < bts.length; i++) { 190 bts[i] = (byte) Integer.parseInt(hex.substring(2 * i, 2 * i + 2), 16); 191 } 192 return bts; 193 } 194 /** 195 * 196 * @param uris 197 */ 198 public static String uriToString(URI[] uris){ 199 if (uris == null) { 200 return null; 201 } 202 StringBuilder ret = new StringBuilder(uris[0].toString()); 203 for(int i = 1; i < uris.length;i++){ 204 ret.append(","); 205 ret.append(uris[i].toString()); 206 } 207 return ret.toString(); 208 } 209 210 /** 211 * @param str 212 * The string array to be parsed into an URI array. 213 * @return <tt>null</tt> if str is <tt>null</tt>, else the URI array 214 * equivalent to str. 215 * @throws IllegalArgumentException 216 * If any string in str violates RFC 2396. 217 */ 218 public static URI[] stringToURI(String[] str){ 219 if (str == null) 220 return null; 221 URI[] uris = new URI[str.length]; 222 for (int i = 0; i < str.length;i++){ 223 try{ 224 uris[i] = new URI(str[i]); 225 }catch(URISyntaxException ur){ 226 throw new IllegalArgumentException( 227 "Failed to create uri for " + str[i], ur); 228 } 229 } 230 return uris; 231 } 232 233 /** 234 * 235 * @param str 236 */ 237 public static Path[] stringToPath(String[] str){ 238 if (str == null) { 239 return null; 240 } 241 Path[] p = new Path[str.length]; 242 for (int i = 0; i < str.length;i++){ 243 p[i] = new Path(str[i]); 244 } 245 return p; 246 } 247 /** 248 * 249 * Given a finish and start time in long milliseconds, returns a 250 * String in the format Xhrs, Ymins, Z sec, for the time difference between two times. 251 * If finish time comes before start time then negative valeus of X, Y and Z wil return. 252 * 253 * @param finishTime finish time 254 * @param startTime start time 255 */ 256 public static String formatTimeDiff(long finishTime, long startTime){ 257 long timeDiff = finishTime - startTime; 258 return formatTime(timeDiff); 259 } 260 261 /** 262 * 263 * Given the time in long milliseconds, returns a 264 * String in the format Xhrs, Ymins, Z sec. 265 * 266 * @param timeDiff The time difference to format 267 */ 268 public static String formatTime(long timeDiff){ 269 StringBuilder buf = new StringBuilder(); 270 long hours = timeDiff / (60*60*1000); 271 long rem = (timeDiff % (60*60*1000)); 272 long minutes = rem / (60*1000); 273 rem = rem % (60*1000); 274 long seconds = rem / 1000; 275 276 if (hours != 0){ 277 buf.append(hours); 278 buf.append("hrs, "); 279 } 280 if (minutes != 0){ 281 buf.append(minutes); 282 buf.append("mins, "); 283 } 284 // return "0sec if no difference 285 buf.append(seconds); 286 buf.append("sec"); 287 return buf.toString(); 288 } 289 /** 290 * Formats time in ms and appends difference (finishTime - startTime) 291 * as returned by formatTimeDiff(). 292 * If finish time is 0, empty string is returned, if start time is 0 293 * then difference is not appended to return value. 294 * @param dateFormat date format to use 295 * @param finishTime fnish time 296 * @param startTime start time 297 * @return formatted value. 298 */ 299 public static String getFormattedTimeWithDiff(DateFormat dateFormat, 300 long finishTime, long startTime){ 301 StringBuilder buf = new StringBuilder(); 302 if (0 != finishTime) { 303 buf.append(dateFormat.format(new Date(finishTime))); 304 if (0 != startTime){ 305 buf.append(" (" + formatTimeDiff(finishTime , startTime) + ")"); 306 } 307 } 308 return buf.toString(); 309 } 310 311 /** 312 * Returns an arraylist of strings. 313 * @param str the comma seperated string values 314 * @return the arraylist of the comma seperated string values 315 */ 316 public static String[] getStrings(String str){ 317 Collection<String> values = getStringCollection(str); 318 if(values.size() == 0) { 319 return null; 320 } 321 return values.toArray(new String[values.size()]); 322 } 323 324 /** 325 * Returns a collection of strings. 326 * @param str comma seperated string values 327 * @return an <code>ArrayList</code> of string values 328 */ 329 public static Collection<String> getStringCollection(String str){ 330 String delim = ","; 331 return getStringCollection(str, delim); 332 } 333 334 /** 335 * Returns a collection of strings. 336 * 337 * @param str 338 * String to parse 339 * @param delim 340 * delimiter to separate the values 341 * @return Collection of parsed elements. 342 */ 343 public static Collection<String> getStringCollection(String str, String delim) { 344 List<String> values = new ArrayList<String>(); 345 if (str == null) 346 return values; 347 StringTokenizer tokenizer = new StringTokenizer(str, delim); 348 while (tokenizer.hasMoreTokens()) { 349 values.add(tokenizer.nextToken()); 350 } 351 return values; 352 } 353 354 /** 355 * Splits a comma separated value <code>String</code>, trimming leading and trailing whitespace on each value. 356 * Duplicate and empty values are removed. 357 * @param str a comma separated <String> with values 358 * @return a <code>Collection</code> of <code>String</code> values 359 */ 360 public static Collection<String> getTrimmedStringCollection(String str){ 361 Set<String> set = new LinkedHashSet<String>( 362 Arrays.asList(getTrimmedStrings(str))); 363 set.remove(""); 364 return set; 365 } 366 367 /** 368 * Splits a comma separated value <code>String</code>, trimming leading and trailing whitespace on each value. 369 * @param str a comma separated <String> with values 370 * @return an array of <code>String</code> values 371 */ 372 public static String[] getTrimmedStrings(String str){ 373 if (null == str || str.trim().isEmpty()) { 374 return emptyStringArray; 375 } 376 377 return str.trim().split("\\s*,\\s*"); 378 } 379 380 final public static String[] emptyStringArray = {}; 381 final public static char COMMA = ','; 382 final public static String COMMA_STR = ","; 383 final public static char ESCAPE_CHAR = '\\'; 384 385 /** 386 * Split a string using the default separator 387 * @param str a string that may have escaped separator 388 * @return an array of strings 389 */ 390 public static String[] split(String str) { 391 return split(str, ESCAPE_CHAR, COMMA); 392 } 393 394 /** 395 * Split a string using the given separator 396 * @param str a string that may have escaped separator 397 * @param escapeChar a char that be used to escape the separator 398 * @param separator a separator char 399 * @return an array of strings 400 */ 401 public static String[] split( 402 String str, char escapeChar, char separator) { 403 if (str==null) { 404 return null; 405 } 406 ArrayList<String> strList = new ArrayList<String>(); 407 StringBuilder split = new StringBuilder(); 408 int index = 0; 409 while ((index = findNext(str, separator, escapeChar, index, split)) >= 0) { 410 ++index; // move over the separator for next search 411 strList.add(split.toString()); 412 split.setLength(0); // reset the buffer 413 } 414 strList.add(split.toString()); 415 // remove trailing empty split(s) 416 int last = strList.size(); // last split 417 while (--last>=0 && "".equals(strList.get(last))) { 418 strList.remove(last); 419 } 420 return strList.toArray(new String[strList.size()]); 421 } 422 423 /** 424 * Split a string using the given separator, with no escaping performed. 425 * @param str a string to be split. Note that this may not be null. 426 * @param separator a separator char 427 * @return an array of strings 428 */ 429 public static String[] split( 430 String str, char separator) { 431 // String.split returns a single empty result for splitting the empty 432 // string. 433 if (str.isEmpty()) { 434 return new String[]{""}; 435 } 436 ArrayList<String> strList = new ArrayList<String>(); 437 int startIndex = 0; 438 int nextIndex = 0; 439 while ((nextIndex = str.indexOf(separator, startIndex)) != -1) { 440 strList.add(str.substring(startIndex, nextIndex)); 441 startIndex = nextIndex + 1; 442 } 443 strList.add(str.substring(startIndex)); 444 // remove trailing empty split(s) 445 int last = strList.size(); // last split 446 while (--last>=0 && "".equals(strList.get(last))) { 447 strList.remove(last); 448 } 449 return strList.toArray(new String[strList.size()]); 450 } 451 452 /** 453 * Finds the first occurrence of the separator character ignoring the escaped 454 * separators starting from the index. Note the substring between the index 455 * and the position of the separator is passed. 456 * @param str the source string 457 * @param separator the character to find 458 * @param escapeChar character used to escape 459 * @param start from where to search 460 * @param split used to pass back the extracted string 461 */ 462 public static int findNext(String str, char separator, char escapeChar, 463 int start, StringBuilder split) { 464 int numPreEscapes = 0; 465 for (int i = start; i < str.length(); i++) { 466 char curChar = str.charAt(i); 467 if (numPreEscapes == 0 && curChar == separator) { // separator 468 return i; 469 } else { 470 split.append(curChar); 471 numPreEscapes = (curChar == escapeChar) 472 ? (++numPreEscapes) % 2 473 : 0; 474 } 475 } 476 return -1; 477 } 478 479 /** 480 * Escape commas in the string using the default escape char 481 * @param str a string 482 * @return an escaped string 483 */ 484 public static String escapeString(String str) { 485 return escapeString(str, ESCAPE_CHAR, COMMA); 486 } 487 488 /** 489 * Escape <code>charToEscape</code> in the string 490 * with the escape char <code>escapeChar</code> 491 * 492 * @param str string 493 * @param escapeChar escape char 494 * @param charToEscape the char to be escaped 495 * @return an escaped string 496 */ 497 public static String escapeString( 498 String str, char escapeChar, char charToEscape) { 499 return escapeString(str, escapeChar, new char[] {charToEscape}); 500 } 501 502 // check if the character array has the character 503 private static boolean hasChar(char[] chars, char character) { 504 for (char target : chars) { 505 if (character == target) { 506 return true; 507 } 508 } 509 return false; 510 } 511 512 /** 513 * @param charsToEscape array of characters to be escaped 514 */ 515 public static String escapeString(String str, char escapeChar, 516 char[] charsToEscape) { 517 if (str == null) { 518 return null; 519 } 520 StringBuilder result = new StringBuilder(); 521 for (int i=0; i<str.length(); i++) { 522 char curChar = str.charAt(i); 523 if (curChar == escapeChar || hasChar(charsToEscape, curChar)) { 524 // special char 525 result.append(escapeChar); 526 } 527 result.append(curChar); 528 } 529 return result.toString(); 530 } 531 532 /** 533 * Unescape commas in the string using the default escape char 534 * @param str a string 535 * @return an unescaped string 536 */ 537 public static String unEscapeString(String str) { 538 return unEscapeString(str, ESCAPE_CHAR, COMMA); 539 } 540 541 /** 542 * Unescape <code>charToEscape</code> in the string 543 * with the escape char <code>escapeChar</code> 544 * 545 * @param str string 546 * @param escapeChar escape char 547 * @param charToEscape the escaped char 548 * @return an unescaped string 549 */ 550 public static String unEscapeString( 551 String str, char escapeChar, char charToEscape) { 552 return unEscapeString(str, escapeChar, new char[] {charToEscape}); 553 } 554 555 /** 556 * @param charsToEscape array of characters to unescape 557 */ 558 public static String unEscapeString(String str, char escapeChar, 559 char[] charsToEscape) { 560 if (str == null) { 561 return null; 562 } 563 StringBuilder result = new StringBuilder(str.length()); 564 boolean hasPreEscape = false; 565 for (int i=0; i<str.length(); i++) { 566 char curChar = str.charAt(i); 567 if (hasPreEscape) { 568 if (curChar != escapeChar && !hasChar(charsToEscape, curChar)) { 569 // no special char 570 throw new IllegalArgumentException("Illegal escaped string " + str + 571 " unescaped " + escapeChar + " at " + (i-1)); 572 } 573 // otherwise discard the escape char 574 result.append(curChar); 575 hasPreEscape = false; 576 } else { 577 if (hasChar(charsToEscape, curChar)) { 578 throw new IllegalArgumentException("Illegal escaped string " + str + 579 " unescaped " + curChar + " at " + i); 580 } else if (curChar == escapeChar) { 581 hasPreEscape = true; 582 } else { 583 result.append(curChar); 584 } 585 } 586 } 587 if (hasPreEscape ) { 588 throw new IllegalArgumentException("Illegal escaped string " + str + 589 ", not expecting " + escapeChar + " in the end." ); 590 } 591 return result.toString(); 592 } 593 594 /** 595 * Return a message for logging. 596 * @param prefix prefix keyword for the message 597 * @param msg content of the message 598 * @return a message for logging 599 */ 600 private static String toStartupShutdownString(String prefix, String [] msg) { 601 StringBuilder b = new StringBuilder(prefix); 602 b.append("\n/************************************************************"); 603 for(String s : msg) 604 b.append("\n" + prefix + s); 605 b.append("\n************************************************************/"); 606 return b.toString(); 607 } 608 609 /** 610 * Print a log message for starting up and shutting down 611 * @param clazz the class of the server 612 * @param args arguments 613 * @param LOG the target log object 614 */ 615 public static void startupShutdownMessage(Class<?> clazz, String[] args, 616 final org.apache.commons.logging.Log LOG) { 617 final String hostname = NetUtils.getHostname(); 618 final String classname = clazz.getSimpleName(); 619 LOG.info( 620 toStartupShutdownString("STARTUP_MSG: ", new String[] { 621 "Starting " + classname, 622 " host = " + hostname, 623 " args = " + Arrays.asList(args), 624 " version = " + VersionInfo.getVersion(), 625 " classpath = " + System.getProperty("java.class.path"), 626 " build = " + VersionInfo.getUrl() + " -r " 627 + VersionInfo.getRevision() 628 + "; compiled by '" + VersionInfo.getUser() 629 + "' on " + VersionInfo.getDate(), 630 " java = " + System.getProperty("java.version") } 631 ) 632 ); 633 634 if (SystemUtils.IS_OS_UNIX) { 635 try { 636 SignalLogger.INSTANCE.register(LOG); 637 } catch (Throwable t) { 638 LOG.warn("failed to register any UNIX signal loggers: ", t); 639 } 640 } 641 ShutdownHookManager.get().addShutdownHook( 642 new Runnable() { 643 @Override 644 public void run() { 645 LOG.info(toStartupShutdownString("SHUTDOWN_MSG: ", new String[]{ 646 "Shutting down " + classname + " at " + hostname})); 647 } 648 }, SHUTDOWN_HOOK_PRIORITY); 649 650 } 651 652 /** 653 * The traditional binary prefixes, kilo, mega, ..., exa, 654 * which can be represented by a 64-bit integer. 655 * TraditionalBinaryPrefix symbol are case insensitive. 656 */ 657 public static enum TraditionalBinaryPrefix { 658 KILO(10), 659 MEGA(KILO.bitShift + 10), 660 GIGA(MEGA.bitShift + 10), 661 TERA(GIGA.bitShift + 10), 662 PETA(TERA.bitShift + 10), 663 EXA (PETA.bitShift + 10); 664 665 public final long value; 666 public final char symbol; 667 public final int bitShift; 668 public final long bitMask; 669 670 private TraditionalBinaryPrefix(int bitShift) { 671 this.bitShift = bitShift; 672 this.value = 1L << bitShift; 673 this.bitMask = this.value - 1L; 674 this.symbol = toString().charAt(0); 675 } 676 677 /** 678 * @return The TraditionalBinaryPrefix object corresponding to the symbol. 679 */ 680 public static TraditionalBinaryPrefix valueOf(char symbol) { 681 symbol = Character.toUpperCase(symbol); 682 for(TraditionalBinaryPrefix prefix : TraditionalBinaryPrefix.values()) { 683 if (symbol == prefix.symbol) { 684 return prefix; 685 } 686 } 687 throw new IllegalArgumentException("Unknown symbol '" + symbol + "'"); 688 } 689 690 /** 691 * Convert a string to long. 692 * The input string is first be trimmed 693 * and then it is parsed with traditional binary prefix. 694 * 695 * For example, 696 * "-1230k" will be converted to -1230 * 1024 = -1259520; 697 * "891g" will be converted to 891 * 1024^3 = 956703965184; 698 * 699 * @param s input string 700 * @return a long value represented by the input string. 701 */ 702 public static long string2long(String s) { 703 s = s.trim(); 704 final int lastpos = s.length() - 1; 705 final char lastchar = s.charAt(lastpos); 706 if (Character.isDigit(lastchar)) 707 return Long.parseLong(s); 708 else { 709 long prefix; 710 try { 711 prefix = TraditionalBinaryPrefix.valueOf(lastchar).value; 712 } catch (IllegalArgumentException e) { 713 throw new IllegalArgumentException("Invalid size prefix '" + lastchar 714 + "' in '" + s 715 + "'. Allowed prefixes are k, m, g, t, p, e(case insensitive)"); 716 } 717 long num = Long.parseLong(s.substring(0, lastpos)); 718 if (num > (Long.MAX_VALUE/prefix) || num < (Long.MIN_VALUE/prefix)) { 719 throw new IllegalArgumentException(s + " does not fit in a Long"); 720 } 721 return num * prefix; 722 } 723 } 724 725 /** 726 * Convert a long integer to a string with traditional binary prefix. 727 * 728 * @param n the value to be converted 729 * @param unit The unit, e.g. "B" for bytes. 730 * @param decimalPlaces The number of decimal places. 731 * @return a string with traditional binary prefix. 732 */ 733 public static String long2String(long n, String unit, int decimalPlaces) { 734 if (unit == null) { 735 unit = ""; 736 } 737 //take care a special case 738 if (n == Long.MIN_VALUE) { 739 return "-8 " + EXA.symbol + unit; 740 } 741 742 final StringBuilder b = new StringBuilder(); 743 //take care negative numbers 744 if (n < 0) { 745 b.append('-'); 746 n = -n; 747 } 748 if (n < KILO.value) { 749 //no prefix 750 b.append(n); 751 return (unit.isEmpty()? b: b.append(" ").append(unit)).toString(); 752 } else { 753 //find traditional binary prefix 754 int i = 0; 755 for(; i < values().length && n >= values()[i].value; i++); 756 TraditionalBinaryPrefix prefix = values()[i - 1]; 757 758 if ((n & prefix.bitMask) == 0) { 759 //exact division 760 b.append(n >> prefix.bitShift); 761 } else { 762 final String format = "%." + decimalPlaces + "f"; 763 String s = format(format, n/(double)prefix.value); 764 //check a special rounding up case 765 if (s.startsWith("1024")) { 766 prefix = values()[i]; 767 s = format(format, n/(double)prefix.value); 768 } 769 b.append(s); 770 } 771 return b.append(' ').append(prefix.symbol).append(unit).toString(); 772 } 773 } 774 } 775 776 /** 777 * Escapes HTML Special characters present in the string. 778 * @param string 779 * @return HTML Escaped String representation 780 */ 781 public static String escapeHTML(String string) { 782 if(string == null) { 783 return null; 784 } 785 StringBuilder sb = new StringBuilder(); 786 boolean lastCharacterWasSpace = false; 787 char[] chars = string.toCharArray(); 788 for(char c : chars) { 789 if(c == ' ') { 790 if(lastCharacterWasSpace){ 791 lastCharacterWasSpace = false; 792 sb.append(" "); 793 }else { 794 lastCharacterWasSpace=true; 795 sb.append(" "); 796 } 797 }else { 798 lastCharacterWasSpace = false; 799 switch(c) { 800 case '<': sb.append("<"); break; 801 case '>': sb.append(">"); break; 802 case '&': sb.append("&"); break; 803 case '"': sb.append("""); break; 804 default : sb.append(c);break; 805 } 806 } 807 } 808 809 return sb.toString(); 810 } 811 812 /** 813 * @return a byte description of the given long interger value. 814 */ 815 public static String byteDesc(long len) { 816 return TraditionalBinaryPrefix.long2String(len, "B", 2); 817 } 818 819 /** @deprecated use StringUtils.format("%.2f", d). */ 820 @Deprecated 821 public static String limitDecimalTo2(double d) { 822 return format("%.2f", d); 823 } 824 825 /** 826 * Concatenates strings, using a separator. 827 * 828 * @param separator Separator to join with. 829 * @param strings Strings to join. 830 */ 831 public static String join(CharSequence separator, Iterable<?> strings) { 832 Iterator<?> i = strings.iterator(); 833 if (!i.hasNext()) { 834 return ""; 835 } 836 StringBuilder sb = new StringBuilder(i.next().toString()); 837 while (i.hasNext()) { 838 sb.append(separator); 839 sb.append(i.next().toString()); 840 } 841 return sb.toString(); 842 } 843 844 /** 845 * Concatenates strings, using a separator. 846 * 847 * @param separator to join with 848 * @param strings to join 849 * @return the joined string 850 */ 851 public static String join(CharSequence separator, String[] strings) { 852 // Ideally we don't have to duplicate the code here if array is iterable. 853 StringBuilder sb = new StringBuilder(); 854 boolean first = true; 855 for (String s : strings) { 856 if (first) { 857 first = false; 858 } else { 859 sb.append(separator); 860 } 861 sb.append(s); 862 } 863 return sb.toString(); 864 } 865 866 /** 867 * Convert SOME_STUFF to SomeStuff 868 * 869 * @param s input string 870 * @return camelized string 871 */ 872 public static String camelize(String s) { 873 StringBuilder sb = new StringBuilder(); 874 String[] words = split(s.toLowerCase(Locale.US), ESCAPE_CHAR, '_'); 875 876 for (String word : words) 877 sb.append(org.apache.commons.lang.StringUtils.capitalize(word)); 878 879 return sb.toString(); 880 } 881 882 /** 883 * Matches a template string against a pattern, replaces matched tokens with 884 * the supplied replacements, and returns the result. The regular expression 885 * must use a capturing group. The value of the first capturing group is used 886 * to look up the replacement. If no replacement is found for the token, then 887 * it is replaced with the empty string. 888 * 889 * For example, assume template is "%foo%_%bar%_%baz%", pattern is "%(.*?)%", 890 * and replacements contains 2 entries, mapping "foo" to "zoo" and "baz" to 891 * "zaz". The result returned would be "zoo__zaz". 892 * 893 * @param template String template to receive replacements 894 * @param pattern Pattern to match for identifying tokens, must use a capturing 895 * group 896 * @param replacements Map<String, String> mapping tokens identified by the 897 * capturing group to their replacement values 898 * @return String template with replacements 899 */ 900 public static String replaceTokens(String template, Pattern pattern, 901 Map<String, String> replacements) { 902 StringBuffer sb = new StringBuffer(); 903 Matcher matcher = pattern.matcher(template); 904 while (matcher.find()) { 905 String replacement = replacements.get(matcher.group(1)); 906 if (replacement == null) { 907 replacement = ""; 908 } 909 matcher.appendReplacement(sb, Matcher.quoteReplacement(replacement)); 910 } 911 matcher.appendTail(sb); 912 return sb.toString(); 913 } 914 915 /** 916 * Get stack trace for a given thread. 917 */ 918 public static String getStackTrace(Thread t) { 919 final StackTraceElement[] stackTrace = t.getStackTrace(); 920 StringBuilder str = new StringBuilder(); 921 for (StackTraceElement e : stackTrace) { 922 str.append(e.toString() + "\n"); 923 } 924 return str.toString(); 925 } 926 927 /** 928 * From a list of command-line arguments, remove both an option and the 929 * next argument. 930 * 931 * @param name Name of the option to remove. Example: -foo. 932 * @param args List of arguments. 933 * @return null if the option was not found; the value of the 934 * option otherwise. 935 * @throws IllegalArgumentException if the option's argument is not present 936 */ 937 public static String popOptionWithArgument(String name, List<String> args) 938 throws IllegalArgumentException { 939 String val = null; 940 for (Iterator<String> iter = args.iterator(); iter.hasNext(); ) { 941 String cur = iter.next(); 942 if (cur.equals("--")) { 943 // stop parsing arguments when you see -- 944 break; 945 } else if (cur.equals(name)) { 946 iter.remove(); 947 if (!iter.hasNext()) { 948 throw new IllegalArgumentException("option " + name + " requires 1 " + 949 "argument."); 950 } 951 val = iter.next(); 952 iter.remove(); 953 break; 954 } 955 } 956 return val; 957 } 958 959 /** 960 * From a list of command-line arguments, remove an option. 961 * 962 * @param name Name of the option to remove. Example: -foo. 963 * @param args List of arguments. 964 * @return true if the option was found and removed; false otherwise. 965 */ 966 public static boolean popOption(String name, List<String> args) { 967 for (Iterator<String> iter = args.iterator(); iter.hasNext(); ) { 968 String cur = iter.next(); 969 if (cur.equals("--")) { 970 // stop parsing arguments when you see -- 971 break; 972 } else if (cur.equals(name)) { 973 iter.remove(); 974 return true; 975 } 976 } 977 return false; 978 } 979 980 /** 981 * From a list of command-line arguments, return the first non-option 982 * argument. Non-option arguments are those which either come after 983 * a double dash (--) or do not start with a dash. 984 * 985 * @param args List of arguments. 986 * @return The first non-option argument, or null if there were none. 987 */ 988 public static String popFirstNonOption(List<String> args) { 989 for (Iterator<String> iter = args.iterator(); iter.hasNext(); ) { 990 String cur = iter.next(); 991 if (cur.equals("--")) { 992 if (!iter.hasNext()) { 993 return null; 994 } 995 cur = iter.next(); 996 iter.remove(); 997 return cur; 998 } else if (!cur.startsWith("-")) { 999 iter.remove(); 1000 return cur; 1001 } 1002 } 1003 return null; 1004 } 1005}