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 }