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