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.Iterator; 030import java.util.LinkedHashSet; 031import java.util.List; 032import java.util.Locale; 033import java.util.Map; 034import java.util.Set; 035import java.util.StringTokenizer; 036import java.util.regex.Matcher; 037import java.util.regex.Pattern; 038 039import org.apache.commons.lang.SystemUtils; 040import org.apache.commons.lang.time.FastDateFormat; 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.base.Preconditions; 047import com.google.common.net.InetAddresses; 048 049/** 050 * General string utils 051 */ 052@InterfaceAudience.Private 053@InterfaceStability.Unstable 054public 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 /** 292 * 293 * Given the time in long milliseconds, returns a String in the sortable 294 * format Xhrs, Ymins, Zsec. X, Y, and Z are always two-digit. If the time is 295 * more than 100 hours ,it is displayed as 99hrs, 59mins, 59sec. 296 * 297 * @param timeDiff The time difference to format 298 */ 299 public static String formatTimeSortable(long timeDiff) { 300 StringBuilder buf = new StringBuilder(); 301 long hours = timeDiff / (60 * 60 * 1000); 302 long rem = (timeDiff % (60 * 60 * 1000)); 303 long minutes = rem / (60 * 1000); 304 rem = rem % (60 * 1000); 305 long seconds = rem / 1000; 306 307 // if hours is more than 99 hours, it will be set a max value format 308 if (hours > 99) { 309 hours = 99; 310 minutes = 59; 311 seconds = 59; 312 } 313 314 buf.append(String.format("%02d", hours)); 315 buf.append("hrs, "); 316 317 buf.append(String.format("%02d", minutes)); 318 buf.append("mins, "); 319 320 buf.append(String.format("%02d", seconds)); 321 buf.append("sec"); 322 return buf.toString(); 323 } 324 325 /** 326 * @param dateFormat date format to use 327 * @param finishTime finish time 328 * @param startTime start time 329 * @return formatted value. 330 * Formats time in ms and appends difference (finishTime - startTime) 331 * as returned by formatTimeDiff(). 332 * If finish time is 0, empty string is returned, if start time is 0 333 * then difference is not appended to return value. 334 * @deprecated Use 335 * {@link StringUtils#getFormattedTimeWithDiff(FastDateFormat, long, long)} or 336 * {@link StringUtils#getFormattedTimeWithDiff(String, long, long)} instead. 337 */ 338 @Deprecated 339 public static String getFormattedTimeWithDiff(DateFormat dateFormat, 340 long finishTime, long startTime){ 341 String formattedFinishTime = dateFormat.format(finishTime); 342 return getFormattedTimeWithDiff(formattedFinishTime, finishTime, startTime); 343 } 344 345 /** 346 * Formats time in ms and appends difference (finishTime - startTime) 347 * as returned by formatTimeDiff(). 348 * If finish time is 0, empty string is returned, if start time is 0 349 * then difference is not appended to return value. 350 * 351 * @param dateFormat date format to use 352 * @param finishTime finish time 353 * @param startTime start time 354 * @return formatted value. 355 */ 356 public static String getFormattedTimeWithDiff(FastDateFormat dateFormat, 357 long finishTime, long startTime) { 358 String formattedFinishTime = dateFormat.format(finishTime); 359 return getFormattedTimeWithDiff(formattedFinishTime, finishTime, startTime); 360 } 361 /** 362 * Formats time in ms and appends difference (finishTime - startTime) 363 * as returned by formatTimeDiff(). 364 * If finish time is 0, empty string is returned, if start time is 0 365 * then difference is not appended to return value. 366 * @param formattedFinishTime formattedFinishTime to use 367 * @param finishTime finish time 368 * @param startTime start time 369 * @return formatted value. 370 */ 371 public static String getFormattedTimeWithDiff(String formattedFinishTime, 372 long finishTime, long startTime){ 373 StringBuilder buf = new StringBuilder(); 374 if (0 != finishTime) { 375 buf.append(formattedFinishTime); 376 if (0 != startTime){ 377 buf.append(" (" + formatTimeDiff(finishTime , startTime) + ")"); 378 } 379 } 380 return buf.toString(); 381 } 382 383 /** 384 * Returns an arraylist of strings. 385 * @param str the comma seperated string values 386 * @return the arraylist of the comma seperated string values 387 */ 388 public static String[] getStrings(String str){ 389 String delim = ","; 390 return getStrings(str, delim); 391 } 392 393 /** 394 * Returns an arraylist of strings. 395 * @param str the string values 396 * @param delim delimiter to separate the values 397 * @return the arraylist of the seperated string values 398 */ 399 public static String[] getStrings(String str, String delim){ 400 Collection<String> values = getStringCollection(str, delim); 401 if(values.size() == 0) { 402 return null; 403 } 404 return values.toArray(new String[values.size()]); 405 } 406 407 /** 408 * Returns a collection of strings. 409 * @param str comma seperated string values 410 * @return an <code>ArrayList</code> of string values 411 */ 412 public static Collection<String> getStringCollection(String str){ 413 String delim = ","; 414 return getStringCollection(str, delim); 415 } 416 417 /** 418 * Returns a collection of strings. 419 * 420 * @param str 421 * String to parse 422 * @param delim 423 * delimiter to separate the values 424 * @return Collection of parsed elements. 425 */ 426 public static Collection<String> getStringCollection(String str, String delim) { 427 List<String> values = new ArrayList<String>(); 428 if (str == null) 429 return values; 430 StringTokenizer tokenizer = new StringTokenizer(str, delim); 431 while (tokenizer.hasMoreTokens()) { 432 values.add(tokenizer.nextToken()); 433 } 434 return values; 435 } 436 437 /** 438 * Splits a comma separated value <code>String</code>, trimming leading and 439 * trailing whitespace on each value. Duplicate and empty values are removed. 440 * 441 * @param str a comma separated <String> with values, may be null 442 * @return a <code>Collection</code> of <code>String</code> values, empty 443 * Collection if null String input 444 */ 445 public static Collection<String> getTrimmedStringCollection(String str){ 446 Set<String> set = new LinkedHashSet<String>( 447 Arrays.asList(getTrimmedStrings(str))); 448 set.remove(""); 449 return set; 450 } 451 452 /** 453 * Splits a comma separated value <code>String</code>, trimming leading and 454 * trailing whitespace on each value. 455 * 456 * @param str a comma separated <code>String</code> with values, may be null 457 * @return an array of <code>String</code> values, empty array if null String 458 * input 459 */ 460 public static String[] getTrimmedStrings(String str){ 461 if (null == str || str.trim().isEmpty()) { 462 return emptyStringArray; 463 } 464 465 return str.trim().split("\\s*,\\s*"); 466 } 467 468 final public static String[] emptyStringArray = {}; 469 final public static char COMMA = ','; 470 final public static String COMMA_STR = ","; 471 final public static char ESCAPE_CHAR = '\\'; 472 473 /** 474 * Split a string using the default separator 475 * @param str a string that may have escaped separator 476 * @return an array of strings 477 */ 478 public static String[] split(String str) { 479 return split(str, ESCAPE_CHAR, COMMA); 480 } 481 482 /** 483 * Split a string using the given separator 484 * @param str a string that may have escaped separator 485 * @param escapeChar a char that be used to escape the separator 486 * @param separator a separator char 487 * @return an array of strings 488 */ 489 public static String[] split( 490 String str, char escapeChar, char separator) { 491 if (str==null) { 492 return null; 493 } 494 ArrayList<String> strList = new ArrayList<String>(); 495 StringBuilder split = new StringBuilder(); 496 int index = 0; 497 while ((index = findNext(str, separator, escapeChar, index, split)) >= 0) { 498 ++index; // move over the separator for next search 499 strList.add(split.toString()); 500 split.setLength(0); // reset the buffer 501 } 502 strList.add(split.toString()); 503 // remove trailing empty split(s) 504 int last = strList.size(); // last split 505 while (--last>=0 && "".equals(strList.get(last))) { 506 strList.remove(last); 507 } 508 return strList.toArray(new String[strList.size()]); 509 } 510 511 /** 512 * Split a string using the given separator, with no escaping performed. 513 * @param str a string to be split. Note that this may not be null. 514 * @param separator a separator char 515 * @return an array of strings 516 */ 517 public static String[] split( 518 String str, char separator) { 519 // String.split returns a single empty result for splitting the empty 520 // string. 521 if (str.isEmpty()) { 522 return new String[]{""}; 523 } 524 ArrayList<String> strList = new ArrayList<String>(); 525 int startIndex = 0; 526 int nextIndex = 0; 527 while ((nextIndex = str.indexOf(separator, startIndex)) != -1) { 528 strList.add(str.substring(startIndex, nextIndex)); 529 startIndex = nextIndex + 1; 530 } 531 strList.add(str.substring(startIndex)); 532 // remove trailing empty split(s) 533 int last = strList.size(); // last split 534 while (--last>=0 && "".equals(strList.get(last))) { 535 strList.remove(last); 536 } 537 return strList.toArray(new String[strList.size()]); 538 } 539 540 /** 541 * Finds the first occurrence of the separator character ignoring the escaped 542 * separators starting from the index. Note the substring between the index 543 * and the position of the separator is passed. 544 * @param str the source string 545 * @param separator the character to find 546 * @param escapeChar character used to escape 547 * @param start from where to search 548 * @param split used to pass back the extracted string 549 */ 550 public static int findNext(String str, char separator, char escapeChar, 551 int start, StringBuilder split) { 552 int numPreEscapes = 0; 553 for (int i = start; i < str.length(); i++) { 554 char curChar = str.charAt(i); 555 if (numPreEscapes == 0 && curChar == separator) { // separator 556 return i; 557 } else { 558 split.append(curChar); 559 numPreEscapes = (curChar == escapeChar) 560 ? (++numPreEscapes) % 2 561 : 0; 562 } 563 } 564 return -1; 565 } 566 567 /** 568 * Escape commas in the string using the default escape char 569 * @param str a string 570 * @return an escaped string 571 */ 572 public static String escapeString(String str) { 573 return escapeString(str, ESCAPE_CHAR, COMMA); 574 } 575 576 /** 577 * Escape <code>charToEscape</code> in the string 578 * with the escape char <code>escapeChar</code> 579 * 580 * @param str string 581 * @param escapeChar escape char 582 * @param charToEscape the char to be escaped 583 * @return an escaped string 584 */ 585 public static String escapeString( 586 String str, char escapeChar, char charToEscape) { 587 return escapeString(str, escapeChar, new char[] {charToEscape}); 588 } 589 590 // check if the character array has the character 591 private static boolean hasChar(char[] chars, char character) { 592 for (char target : chars) { 593 if (character == target) { 594 return true; 595 } 596 } 597 return false; 598 } 599 600 /** 601 * @param charsToEscape array of characters to be escaped 602 */ 603 public static String escapeString(String str, char escapeChar, 604 char[] charsToEscape) { 605 if (str == null) { 606 return null; 607 } 608 StringBuilder result = new StringBuilder(); 609 for (int i=0; i<str.length(); i++) { 610 char curChar = str.charAt(i); 611 if (curChar == escapeChar || hasChar(charsToEscape, curChar)) { 612 // special char 613 result.append(escapeChar); 614 } 615 result.append(curChar); 616 } 617 return result.toString(); 618 } 619 620 /** 621 * Unescape commas in the string using the default escape char 622 * @param str a string 623 * @return an unescaped string 624 */ 625 public static String unEscapeString(String str) { 626 return unEscapeString(str, ESCAPE_CHAR, COMMA); 627 } 628 629 /** 630 * Unescape <code>charToEscape</code> in the string 631 * with the escape char <code>escapeChar</code> 632 * 633 * @param str string 634 * @param escapeChar escape char 635 * @param charToEscape the escaped char 636 * @return an unescaped string 637 */ 638 public static String unEscapeString( 639 String str, char escapeChar, char charToEscape) { 640 return unEscapeString(str, escapeChar, new char[] {charToEscape}); 641 } 642 643 /** 644 * @param charsToEscape array of characters to unescape 645 */ 646 public static String unEscapeString(String str, char escapeChar, 647 char[] charsToEscape) { 648 if (str == null) { 649 return null; 650 } 651 StringBuilder result = new StringBuilder(str.length()); 652 boolean hasPreEscape = false; 653 for (int i=0; i<str.length(); i++) { 654 char curChar = str.charAt(i); 655 if (hasPreEscape) { 656 if (curChar != escapeChar && !hasChar(charsToEscape, curChar)) { 657 // no special char 658 throw new IllegalArgumentException("Illegal escaped string " + str + 659 " unescaped " + escapeChar + " at " + (i-1)); 660 } 661 // otherwise discard the escape char 662 result.append(curChar); 663 hasPreEscape = false; 664 } else { 665 if (hasChar(charsToEscape, curChar)) { 666 throw new IllegalArgumentException("Illegal escaped string " + str + 667 " unescaped " + curChar + " at " + i); 668 } else if (curChar == escapeChar) { 669 hasPreEscape = true; 670 } else { 671 result.append(curChar); 672 } 673 } 674 } 675 if (hasPreEscape ) { 676 throw new IllegalArgumentException("Illegal escaped string " + str + 677 ", not expecting " + escapeChar + " in the end." ); 678 } 679 return result.toString(); 680 } 681 682 /** 683 * Return a message for logging. 684 * @param prefix prefix keyword for the message 685 * @param msg content of the message 686 * @return a message for logging 687 */ 688 private static String toStartupShutdownString(String prefix, String [] msg) { 689 StringBuilder b = new StringBuilder(prefix); 690 b.append("\n/************************************************************"); 691 for(String s : msg) 692 b.append("\n" + prefix + s); 693 b.append("\n************************************************************/"); 694 return b.toString(); 695 } 696 697 /** 698 * Print a log message for starting up and shutting down 699 * @param clazz the class of the server 700 * @param args arguments 701 * @param LOG the target log object 702 */ 703 public static void startupShutdownMessage(Class<?> clazz, String[] args, 704 final org.apache.commons.logging.Log LOG) { 705 startupShutdownMessage(clazz, args, LogAdapter.create(LOG)); 706 } 707 708 /** 709 * Print a log message for starting up and shutting down 710 * @param clazz the class of the server 711 * @param args arguments 712 * @param LOG the target log object 713 */ 714 public static void startupShutdownMessage(Class<?> clazz, String[] args, 715 final org.slf4j.Logger LOG) { 716 startupShutdownMessage(clazz, args, LogAdapter.create(LOG)); 717 } 718 719 static void startupShutdownMessage(Class<?> clazz, String[] args, 720 final LogAdapter LOG) { 721 final String hostname = NetUtils.getHostname(); 722 final String classname = clazz.getSimpleName(); 723 LOG.info( 724 toStartupShutdownString("STARTUP_MSG: ", new String[] { 725 "Starting " + classname, 726 " user = " + System.getProperty("user.name"), 727 " host = " + hostname, 728 " args = " + Arrays.asList(args), 729 " version = " + VersionInfo.getVersion(), 730 " classpath = " + System.getProperty("java.class.path"), 731 " build = " + VersionInfo.getUrl() + " -r " 732 + VersionInfo.getRevision() 733 + "; compiled by '" + VersionInfo.getUser() 734 + "' on " + VersionInfo.getDate(), 735 " java = " + System.getProperty("java.version") } 736 ) 737 ); 738 739 if (SystemUtils.IS_OS_UNIX) { 740 try { 741 SignalLogger.INSTANCE.register(LOG); 742 } catch (Throwable t) { 743 LOG.warn("failed to register any UNIX signal loggers: ", t); 744 } 745 } 746 ShutdownHookManager.get().addShutdownHook( 747 new Runnable() { 748 @Override 749 public void run() { 750 LOG.info(toStartupShutdownString("SHUTDOWN_MSG: ", new String[]{ 751 "Shutting down " + classname + " at " + hostname})); 752 } 753 }, SHUTDOWN_HOOK_PRIORITY); 754 755 } 756 757 /** 758 * The traditional binary prefixes, kilo, mega, ..., exa, 759 * which can be represented by a 64-bit integer. 760 * TraditionalBinaryPrefix symbol are case insensitive. 761 */ 762 public static enum TraditionalBinaryPrefix { 763 KILO(10), 764 MEGA(KILO.bitShift + 10), 765 GIGA(MEGA.bitShift + 10), 766 TERA(GIGA.bitShift + 10), 767 PETA(TERA.bitShift + 10), 768 EXA (PETA.bitShift + 10); 769 770 public final long value; 771 public final char symbol; 772 public final int bitShift; 773 public final long bitMask; 774 775 private TraditionalBinaryPrefix(int bitShift) { 776 this.bitShift = bitShift; 777 this.value = 1L << bitShift; 778 this.bitMask = this.value - 1L; 779 this.symbol = toString().charAt(0); 780 } 781 782 /** 783 * @return The TraditionalBinaryPrefix object corresponding to the symbol. 784 */ 785 public static TraditionalBinaryPrefix valueOf(char symbol) { 786 symbol = Character.toUpperCase(symbol); 787 for(TraditionalBinaryPrefix prefix : TraditionalBinaryPrefix.values()) { 788 if (symbol == prefix.symbol) { 789 return prefix; 790 } 791 } 792 throw new IllegalArgumentException("Unknown symbol '" + symbol + "'"); 793 } 794 795 /** 796 * Convert a string to long. 797 * The input string is first be trimmed 798 * and then it is parsed with traditional binary prefix. 799 * 800 * For example, 801 * "-1230k" will be converted to -1230 * 1024 = -1259520; 802 * "891g" will be converted to 891 * 1024^3 = 956703965184; 803 * 804 * @param s input string 805 * @return a long value represented by the input string. 806 */ 807 public static long string2long(String s) { 808 s = s.trim(); 809 final int lastpos = s.length() - 1; 810 final char lastchar = s.charAt(lastpos); 811 if (Character.isDigit(lastchar)) 812 return Long.parseLong(s); 813 else { 814 long prefix; 815 try { 816 prefix = TraditionalBinaryPrefix.valueOf(lastchar).value; 817 } catch (IllegalArgumentException e) { 818 throw new IllegalArgumentException("Invalid size prefix '" + lastchar 819 + "' in '" + s 820 + "'. Allowed prefixes are k, m, g, t, p, e(case insensitive)"); 821 } 822 long num = Long.parseLong(s.substring(0, lastpos)); 823 if (num > (Long.MAX_VALUE/prefix) || num < (Long.MIN_VALUE/prefix)) { 824 throw new IllegalArgumentException(s + " does not fit in a Long"); 825 } 826 return num * prefix; 827 } 828 } 829 830 /** 831 * Convert a long integer to a string with traditional binary prefix. 832 * 833 * @param n the value to be converted 834 * @param unit The unit, e.g. "B" for bytes. 835 * @param decimalPlaces The number of decimal places. 836 * @return a string with traditional binary prefix. 837 */ 838 public static String long2String(long n, String unit, int decimalPlaces) { 839 if (unit == null) { 840 unit = ""; 841 } 842 //take care a special case 843 if (n == Long.MIN_VALUE) { 844 return "-8 " + EXA.symbol + unit; 845 } 846 847 final StringBuilder b = new StringBuilder(); 848 //take care negative numbers 849 if (n < 0) { 850 b.append('-'); 851 n = -n; 852 } 853 if (n < KILO.value) { 854 //no prefix 855 b.append(n); 856 return (unit.isEmpty()? b: b.append(" ").append(unit)).toString(); 857 } else { 858 //find traditional binary prefix 859 int i = 0; 860 for(; i < values().length && n >= values()[i].value; i++); 861 TraditionalBinaryPrefix prefix = values()[i - 1]; 862 863 if ((n & prefix.bitMask) == 0) { 864 //exact division 865 b.append(n >> prefix.bitShift); 866 } else { 867 final String format = "%." + decimalPlaces + "f"; 868 String s = format(format, n/(double)prefix.value); 869 //check a special rounding up case 870 if (s.startsWith("1024")) { 871 prefix = values()[i]; 872 s = format(format, n/(double)prefix.value); 873 } 874 b.append(s); 875 } 876 return b.append(' ').append(prefix.symbol).append(unit).toString(); 877 } 878 } 879 } 880 881 /** 882 * Escapes HTML Special characters present in the string. 883 * @param string 884 * @return HTML Escaped String representation 885 */ 886 public static String escapeHTML(String string) { 887 if(string == null) { 888 return null; 889 } 890 StringBuilder sb = new StringBuilder(); 891 boolean lastCharacterWasSpace = false; 892 char[] chars = string.toCharArray(); 893 for(char c : chars) { 894 if(c == ' ') { 895 if(lastCharacterWasSpace){ 896 lastCharacterWasSpace = false; 897 sb.append(" "); 898 }else { 899 lastCharacterWasSpace=true; 900 sb.append(" "); 901 } 902 }else { 903 lastCharacterWasSpace = false; 904 switch(c) { 905 case '<': sb.append("<"); break; 906 case '>': sb.append(">"); break; 907 case '&': sb.append("&"); break; 908 case '"': sb.append("""); break; 909 default : sb.append(c);break; 910 } 911 } 912 } 913 914 return sb.toString(); 915 } 916 917 /** 918 * @return a byte description of the given long interger value. 919 */ 920 public static String byteDesc(long len) { 921 return TraditionalBinaryPrefix.long2String(len, "B", 2); 922 } 923 924 /** @deprecated use StringUtils.format("%.2f", d). */ 925 @Deprecated 926 public static String limitDecimalTo2(double d) { 927 return format("%.2f", d); 928 } 929 930 /** 931 * Concatenates strings, using a separator. 932 * 933 * @param separator Separator to join with. 934 * @param strings Strings to join. 935 */ 936 public static String join(CharSequence separator, Iterable<?> strings) { 937 Iterator<?> i = strings.iterator(); 938 if (!i.hasNext()) { 939 return ""; 940 } 941 StringBuilder sb = new StringBuilder(i.next().toString()); 942 while (i.hasNext()) { 943 sb.append(separator); 944 sb.append(i.next().toString()); 945 } 946 return sb.toString(); 947 } 948 949 public static String join(char separator, Iterable<?> strings) { 950 return join(separator + "", strings); 951 } 952 953 /** 954 * Concatenates strings, using a separator. 955 * 956 * @param separator to join with 957 * @param strings to join 958 * @return the joined string 959 */ 960 public static String join(CharSequence separator, String[] strings) { 961 // Ideally we don't have to duplicate the code here if array is iterable. 962 StringBuilder sb = new StringBuilder(); 963 boolean first = true; 964 for (String s : strings) { 965 if (first) { 966 first = false; 967 } else { 968 sb.append(separator); 969 } 970 sb.append(s); 971 } 972 return sb.toString(); 973 } 974 975 public static String join(char separator, String[] strings) { 976 return join(separator + "", strings); 977 } 978 979 /** 980 * Convert SOME_STUFF to SomeStuff 981 * 982 * @param s input string 983 * @return camelized string 984 */ 985 public static String camelize(String s) { 986 StringBuilder sb = new StringBuilder(); 987 String[] words = split(StringUtils.toLowerCase(s), ESCAPE_CHAR, '_'); 988 989 for (String word : words) 990 sb.append(org.apache.commons.lang.StringUtils.capitalize(word)); 991 992 return sb.toString(); 993 } 994 995 /** 996 * Matches a template string against a pattern, replaces matched tokens with 997 * the supplied replacements, and returns the result. The regular expression 998 * must use a capturing group. The value of the first capturing group is used 999 * to look up the replacement. If no replacement is found for the token, then 1000 * it is replaced with the empty string. 1001 * 1002 * For example, assume template is "%foo%_%bar%_%baz%", pattern is "%(.*?)%", 1003 * and replacements contains 2 entries, mapping "foo" to "zoo" and "baz" to 1004 * "zaz". The result returned would be "zoo__zaz". 1005 * 1006 * @param template String template to receive replacements 1007 * @param pattern Pattern to match for identifying tokens, must use a capturing 1008 * group 1009 * @param replacements Map<String, String> mapping tokens identified by the 1010 * capturing group to their replacement values 1011 * @return String template with replacements 1012 */ 1013 public static String replaceTokens(String template, Pattern pattern, 1014 Map<String, String> replacements) { 1015 StringBuffer sb = new StringBuffer(); 1016 Matcher matcher = pattern.matcher(template); 1017 while (matcher.find()) { 1018 String replacement = replacements.get(matcher.group(1)); 1019 if (replacement == null) { 1020 replacement = ""; 1021 } 1022 matcher.appendReplacement(sb, Matcher.quoteReplacement(replacement)); 1023 } 1024 matcher.appendTail(sb); 1025 return sb.toString(); 1026 } 1027 1028 /** 1029 * Get stack trace for a given thread. 1030 */ 1031 public static String getStackTrace(Thread t) { 1032 final StackTraceElement[] stackTrace = t.getStackTrace(); 1033 StringBuilder str = new StringBuilder(); 1034 for (StackTraceElement e : stackTrace) { 1035 str.append(e.toString() + "\n"); 1036 } 1037 return str.toString(); 1038 } 1039 1040 /** 1041 * From a list of command-line arguments, remove both an option and the 1042 * next argument. 1043 * 1044 * @param name Name of the option to remove. Example: -foo. 1045 * @param args List of arguments. 1046 * @return null if the option was not found; the value of the 1047 * option otherwise. 1048 * @throws IllegalArgumentException if the option's argument is not present 1049 */ 1050 public static String popOptionWithArgument(String name, List<String> args) 1051 throws IllegalArgumentException { 1052 String val = null; 1053 for (Iterator<String> iter = args.iterator(); iter.hasNext(); ) { 1054 String cur = iter.next(); 1055 if (cur.equals("--")) { 1056 // stop parsing arguments when you see -- 1057 break; 1058 } else if (cur.equals(name)) { 1059 iter.remove(); 1060 if (!iter.hasNext()) { 1061 throw new IllegalArgumentException("option " + name + " requires 1 " + 1062 "argument."); 1063 } 1064 val = iter.next(); 1065 iter.remove(); 1066 break; 1067 } 1068 } 1069 return val; 1070 } 1071 1072 /** 1073 * From a list of command-line arguments, remove an option. 1074 * 1075 * @param name Name of the option to remove. Example: -foo. 1076 * @param args List of arguments. 1077 * @return true if the option was found and removed; false otherwise. 1078 */ 1079 public static boolean popOption(String name, List<String> args) { 1080 for (Iterator<String> iter = args.iterator(); iter.hasNext(); ) { 1081 String cur = iter.next(); 1082 if (cur.equals("--")) { 1083 // stop parsing arguments when you see -- 1084 break; 1085 } else if (cur.equals(name)) { 1086 iter.remove(); 1087 return true; 1088 } 1089 } 1090 return false; 1091 } 1092 1093 /** 1094 * From a list of command-line arguments, return the first non-option 1095 * argument. Non-option arguments are those which either come after 1096 * a double dash (--) or do not start with a dash. 1097 * 1098 * @param args List of arguments. 1099 * @return The first non-option argument, or null if there were none. 1100 */ 1101 public static String popFirstNonOption(List<String> args) { 1102 for (Iterator<String> iter = args.iterator(); iter.hasNext(); ) { 1103 String cur = iter.next(); 1104 if (cur.equals("--")) { 1105 if (!iter.hasNext()) { 1106 return null; 1107 } 1108 cur = iter.next(); 1109 iter.remove(); 1110 return cur; 1111 } else if (!cur.startsWith("-")) { 1112 iter.remove(); 1113 return cur; 1114 } 1115 } 1116 return null; 1117 } 1118 1119 /** 1120 * Converts all of the characters in this String to lower case with 1121 * Locale.ENGLISH. 1122 * 1123 * @param str string to be converted 1124 * @return the str, converted to lowercase. 1125 */ 1126 public static String toLowerCase(String str) { 1127 return str.toLowerCase(Locale.ENGLISH); 1128 } 1129 1130 /** 1131 * Converts all of the characters in this String to upper case with 1132 * Locale.ENGLISH. 1133 * 1134 * @param str string to be converted 1135 * @return the str, converted to uppercase. 1136 */ 1137 public static String toUpperCase(String str) { 1138 return str.toUpperCase(Locale.ENGLISH); 1139 } 1140 1141 /** 1142 * Compare strings locale-freely by using String#equalsIgnoreCase. 1143 * 1144 * @param s1 Non-null string to be converted 1145 * @param s2 string to be converted 1146 * @return the str, converted to uppercase. 1147 */ 1148 public static boolean equalsIgnoreCase(String s1, String s2) { 1149 Preconditions.checkNotNull(s1); 1150 // don't check non-null against s2 to make the semantics same as 1151 // s1.equals(s2) 1152 return s1.equalsIgnoreCase(s2); 1153 } 1154 1155}