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