001 /*
002 * Copyright 2007-2014 UnboundID Corp.
003 * All Rights Reserved.
004 */
005 /*
006 * Copyright (C) 2008-2014 UnboundID Corp.
007 *
008 * This program is free software; you can redistribute it and/or modify
009 * it under the terms of the GNU General Public License (GPLv2 only)
010 * or the terms of the GNU Lesser General Public License (LGPLv2.1 only)
011 * as published by the Free Software Foundation.
012 *
013 * This program is distributed in the hope that it will be useful,
014 * but WITHOUT ANY WARRANTY; without even the implied warranty of
015 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
016 * GNU General Public License for more details.
017 *
018 * You should have received a copy of the GNU General Public License
019 * along with this program; if not, see <http://www.gnu.org/licenses>.
020 */
021 package com.unboundid.util;
022
023
024
025 import java.text.DecimalFormat;
026 import java.text.ParseException;
027 import java.text.SimpleDateFormat;
028 import java.util.ArrayList;
029 import java.util.Arrays;
030 import java.util.Collections;
031 import java.util.Date;
032 import java.util.HashSet;
033 import java.util.Iterator;
034 import java.util.List;
035 import java.util.StringTokenizer;
036 import java.util.TimeZone;
037 import java.util.UUID;
038
039 import com.unboundid.ldap.sdk.Control;
040 import com.unboundid.ldap.sdk.Version;
041
042 import static com.unboundid.util.Debug.*;
043 import static com.unboundid.util.UtilityMessages.*;
044 import static com.unboundid.util.Validator.*;
045
046
047
048 /**
049 * This class provides a number of static utility functions.
050 */
051 public final class StaticUtils
052 {
053 /**
054 * A pre-allocated byte array containing zero bytes.
055 */
056 public static final byte[] NO_BYTES = new byte[0];
057
058
059
060 /**
061 * A pre-allocated empty control array.
062 */
063 public static final Control[] NO_CONTROLS = new Control[0];
064
065
066
067 /**
068 * A pre-allocated empty string array.
069 */
070 public static final String[] NO_STRINGS = new String[0];
071
072
073
074 /**
075 * The end-of-line marker for this platform.
076 */
077 public static final String EOL = System.getProperty("line.separator");
078
079
080
081 /**
082 * A byte array containing the end-of-line marker for this platform.
083 */
084 public static final byte[] EOL_BYTES = getBytes(EOL);
085
086
087
088 /**
089 * The thread-local date formatter used to encode generalized time values.
090 */
091 private static final ThreadLocal<SimpleDateFormat> dateFormatters =
092 new ThreadLocal<SimpleDateFormat>();
093
094
095
096 /**
097 * Prevent this class from being instantiated.
098 */
099 private StaticUtils()
100 {
101 // No implementation is required.
102 }
103
104
105
106 /**
107 * Retrieves a UTF-8 byte representation of the provided string.
108 *
109 * @param s The string for which to retrieve the UTF-8 byte representation.
110 *
111 * @return The UTF-8 byte representation for the provided string.
112 */
113 public static byte[] getBytes(final String s)
114 {
115 final int length;
116 if ((s == null) || ((length = s.length()) == 0))
117 {
118 return NO_BYTES;
119 }
120
121 final byte[] b = new byte[length];
122 for (int i=0; i < length; i++)
123 {
124 final char c = s.charAt(i);
125 if (c <= 0x7F)
126 {
127 b[i] = (byte) (c & 0x7F);
128 }
129 else
130 {
131 try
132 {
133 return s.getBytes("UTF-8");
134 }
135 catch (Exception e)
136 {
137 // This should never happen.
138 debugException(e);
139 return s.getBytes();
140 }
141 }
142 }
143
144 return b;
145 }
146
147
148
149 /**
150 * Indicates whether the contents of the provided byte array represent an
151 * ASCII string, which is also known in LDAP terminology as an IA5 string.
152 * An ASCII string is one that contains only bytes in which the most
153 * significant bit is zero.
154 *
155 * @param b The byte array for which to make the determination. It must
156 * not be {@code null}.
157 *
158 * @return {@code true} if the contents of the provided array represent an
159 * ASCII string, or {@code false} if not.
160 */
161 public static boolean isASCIIString(final byte[] b)
162 {
163 for (final byte by : b)
164 {
165 if ((by & 0x80) == 0x80)
166 {
167 return false;
168 }
169 }
170
171 return true;
172 }
173
174
175
176 /**
177 * Indicates whether the contents of the provided byte array represent a
178 * printable LDAP string, as per RFC 4517 section 3.2. The only characters
179 * allowed in a printable string are:
180 * <UL>
181 * <LI>All uppercase and lowercase ASCII alphabetic letters</LI>
182 * <LI>All ASCII numeric digits</LI>
183 * <LI>The following additional ASCII characters: single quote, left
184 * parenthesis, right parenthesis, plus, comma, hyphen, period, equals,
185 * forward slash, colon, question mark, space.</LI>
186 * </UL>
187 * If the provided array contains anything other than the above characters
188 * (i.e., if the byte array contains any non-ASCII characters, or any ASCII
189 * control characters, or if it contains excluded ASCII characters like
190 * the exclamation point, double quote, octothorpe, dollar sign, etc.), then
191 * it will not be considered printable.
192 *
193 * @param b The byte array for which to make the determination. It must
194 * not be {@code null}.
195 *
196 * @return {@code true} if the contents of the provided byte array represent
197 * a printable LDAP string, or {@code false} if not.
198 */
199 public static boolean isPrintableString(final byte[] b)
200 {
201 for (final byte by : b)
202 {
203 if ((by & 0x80) == 0x80)
204 {
205 return false;
206 }
207
208 if (((by >= 'a') && (by <= 'z')) ||
209 ((by >= 'A') && (by <= 'Z')) ||
210 ((by >= '0') && (by <= '9')))
211 {
212 continue;
213 }
214
215 switch (by)
216 {
217 case '\'':
218 case '(':
219 case ')':
220 case '+':
221 case ',':
222 case '-':
223 case '.':
224 case '=':
225 case '/':
226 case ':':
227 case '?':
228 case ' ':
229 continue;
230 default:
231 return false;
232 }
233 }
234
235 return true;
236 }
237
238
239
240 /**
241 * Retrieves a string generated from the provided byte array using the UTF-8
242 * encoding.
243 *
244 * @param b The byte array for which to return the associated string.
245 *
246 * @return The string generated from the provided byte array using the UTF-8
247 * encoding.
248 */
249 public static String toUTF8String(final byte[] b)
250 {
251 try
252 {
253 return new String(b, "UTF-8");
254 }
255 catch (Exception e)
256 {
257 // This should never happen.
258 debugException(e);
259 return new String(b);
260 }
261 }
262
263
264
265 /**
266 * Retrieves a string generated from the specified portion of the provided
267 * byte array using the UTF-8 encoding.
268 *
269 * @param b The byte array for which to return the associated string.
270 * @param offset The offset in the array at which the value begins.
271 * @param length The number of bytes in the value to convert to a string.
272 *
273 * @return The string generated from the specified portion of the provided
274 * byte array using the UTF-8 encoding.
275 */
276 public static String toUTF8String(final byte[] b, final int offset,
277 final int length)
278 {
279 try
280 {
281 return new String(b, offset, length, "UTF-8");
282 }
283 catch (Exception e)
284 {
285 // This should never happen.
286 debugException(e);
287 return new String(b, offset, length);
288 }
289 }
290
291
292
293 /**
294 * Retrieves a version of the provided string with the first character
295 * converted to lowercase but all other characters retaining their original
296 * capitalization.
297 *
298 * @param s The string to be processed.
299 *
300 * @return A version of the provided string with the first character
301 * converted to lowercase but all other characters retaining their
302 * original capitalization.
303 */
304 public static String toInitialLowerCase(final String s)
305 {
306 if ((s == null) || (s.length() == 0))
307 {
308 return s;
309 }
310 else if (s.length() == 1)
311 {
312 return toLowerCase(s);
313 }
314 else
315 {
316 final char c = s.charAt(0);
317 if (((c >= 'A') && (c <= 'Z')) || (c < ' ') || (c > '~'))
318 {
319 final StringBuilder b = new StringBuilder(s);
320 b.setCharAt(0, Character.toLowerCase(c));
321 return b.toString();
322 }
323 else
324 {
325 return s;
326 }
327 }
328 }
329
330
331
332 /**
333 * Retrieves an all-lowercase version of the provided string.
334 *
335 * @param s The string for which to retrieve the lowercase version.
336 *
337 * @return An all-lowercase version of the provided string.
338 */
339 public static String toLowerCase(final String s)
340 {
341 if (s == null)
342 {
343 return null;
344 }
345
346 final int length = s.length();
347 final char[] charArray = s.toCharArray();
348 for (int i=0; i < length; i++)
349 {
350 switch (charArray[i])
351 {
352 case 'A':
353 charArray[i] = 'a';
354 break;
355 case 'B':
356 charArray[i] = 'b';
357 break;
358 case 'C':
359 charArray[i] = 'c';
360 break;
361 case 'D':
362 charArray[i] = 'd';
363 break;
364 case 'E':
365 charArray[i] = 'e';
366 break;
367 case 'F':
368 charArray[i] = 'f';
369 break;
370 case 'G':
371 charArray[i] = 'g';
372 break;
373 case 'H':
374 charArray[i] = 'h';
375 break;
376 case 'I':
377 charArray[i] = 'i';
378 break;
379 case 'J':
380 charArray[i] = 'j';
381 break;
382 case 'K':
383 charArray[i] = 'k';
384 break;
385 case 'L':
386 charArray[i] = 'l';
387 break;
388 case 'M':
389 charArray[i] = 'm';
390 break;
391 case 'N':
392 charArray[i] = 'n';
393 break;
394 case 'O':
395 charArray[i] = 'o';
396 break;
397 case 'P':
398 charArray[i] = 'p';
399 break;
400 case 'Q':
401 charArray[i] = 'q';
402 break;
403 case 'R':
404 charArray[i] = 'r';
405 break;
406 case 'S':
407 charArray[i] = 's';
408 break;
409 case 'T':
410 charArray[i] = 't';
411 break;
412 case 'U':
413 charArray[i] = 'u';
414 break;
415 case 'V':
416 charArray[i] = 'v';
417 break;
418 case 'W':
419 charArray[i] = 'w';
420 break;
421 case 'X':
422 charArray[i] = 'x';
423 break;
424 case 'Y':
425 charArray[i] = 'y';
426 break;
427 case 'Z':
428 charArray[i] = 'z';
429 break;
430 default:
431 if (charArray[i] > 0x7F)
432 {
433 return s.toLowerCase();
434 }
435 break;
436 }
437 }
438
439 return new String(charArray);
440 }
441
442
443
444 /**
445 * Indicates whether the provided character is a valid hexadecimal digit.
446 *
447 * @param c The character for which to make the determination.
448 *
449 * @return {@code true} if the provided character does represent a valid
450 * hexadecimal digit, or {@code false} if not.
451 */
452 public static boolean isHex(final char c)
453 {
454 switch (c)
455 {
456 case '0':
457 case '1':
458 case '2':
459 case '3':
460 case '4':
461 case '5':
462 case '6':
463 case '7':
464 case '8':
465 case '9':
466 case 'a':
467 case 'A':
468 case 'b':
469 case 'B':
470 case 'c':
471 case 'C':
472 case 'd':
473 case 'D':
474 case 'e':
475 case 'E':
476 case 'f':
477 case 'F':
478 return true;
479
480 default:
481 return false;
482 }
483 }
484
485
486
487 /**
488 * Retrieves a hexadecimal representation of the provided byte.
489 *
490 * @param b The byte to encode as hexadecimal.
491 *
492 * @return A string containing the hexadecimal representation of the provided
493 * byte.
494 */
495 public static String toHex(final byte b)
496 {
497 final StringBuilder buffer = new StringBuilder(2);
498 toHex(b, buffer);
499 return buffer.toString();
500 }
501
502
503
504 /**
505 * Appends a hexadecimal representation of the provided byte to the given
506 * buffer.
507 *
508 * @param b The byte to encode as hexadecimal.
509 * @param buffer The buffer to which the hexadecimal representation is to be
510 * appended.
511 */
512 public static void toHex(final byte b, final StringBuilder buffer)
513 {
514 switch (b & 0xF0)
515 {
516 case 0x00:
517 buffer.append('0');
518 break;
519 case 0x10:
520 buffer.append('1');
521 break;
522 case 0x20:
523 buffer.append('2');
524 break;
525 case 0x30:
526 buffer.append('3');
527 break;
528 case 0x40:
529 buffer.append('4');
530 break;
531 case 0x50:
532 buffer.append('5');
533 break;
534 case 0x60:
535 buffer.append('6');
536 break;
537 case 0x70:
538 buffer.append('7');
539 break;
540 case 0x80:
541 buffer.append('8');
542 break;
543 case 0x90:
544 buffer.append('9');
545 break;
546 case 0xA0:
547 buffer.append('a');
548 break;
549 case 0xB0:
550 buffer.append('b');
551 break;
552 case 0xC0:
553 buffer.append('c');
554 break;
555 case 0xD0:
556 buffer.append('d');
557 break;
558 case 0xE0:
559 buffer.append('e');
560 break;
561 case 0xF0:
562 buffer.append('f');
563 break;
564 }
565
566 switch (b & 0x0F)
567 {
568 case 0x00:
569 buffer.append('0');
570 break;
571 case 0x01:
572 buffer.append('1');
573 break;
574 case 0x02:
575 buffer.append('2');
576 break;
577 case 0x03:
578 buffer.append('3');
579 break;
580 case 0x04:
581 buffer.append('4');
582 break;
583 case 0x05:
584 buffer.append('5');
585 break;
586 case 0x06:
587 buffer.append('6');
588 break;
589 case 0x07:
590 buffer.append('7');
591 break;
592 case 0x08:
593 buffer.append('8');
594 break;
595 case 0x09:
596 buffer.append('9');
597 break;
598 case 0x0A:
599 buffer.append('a');
600 break;
601 case 0x0B:
602 buffer.append('b');
603 break;
604 case 0x0C:
605 buffer.append('c');
606 break;
607 case 0x0D:
608 buffer.append('d');
609 break;
610 case 0x0E:
611 buffer.append('e');
612 break;
613 case 0x0F:
614 buffer.append('f');
615 break;
616 }
617 }
618
619
620
621 /**
622 * Retrieves a hexadecimal representation of the contents of the provided byte
623 * array. No delimiter character will be inserted between the hexadecimal
624 * digits for each byte.
625 *
626 * @param b The byte array to be represented as a hexadecimal string. It
627 * must not be {@code null}.
628 *
629 * @return A string containing a hexadecimal representation of the contents
630 * of the provided byte array.
631 */
632 public static String toHex(final byte[] b)
633 {
634 ensureNotNull(b);
635
636 final StringBuilder buffer = new StringBuilder(2 * b.length);
637 toHex(b, buffer);
638 return buffer.toString();
639 }
640
641
642
643 /**
644 * Retrieves a hexadecimal representation of the contents of the provided byte
645 * array. No delimiter character will be inserted between the hexadecimal
646 * digits for each byte.
647 *
648 * @param b The byte array to be represented as a hexadecimal string.
649 * It must not be {@code null}.
650 * @param buffer A buffer to which the hexadecimal representation of the
651 * contents of the provided byte array should be appended.
652 */
653 public static void toHex(final byte[] b, final StringBuilder buffer)
654 {
655 toHex(b, null, buffer);
656 }
657
658
659
660 /**
661 * Retrieves a hexadecimal representation of the contents of the provided byte
662 * array. No delimiter character will be inserted between the hexadecimal
663 * digits for each byte.
664 *
665 * @param b The byte array to be represented as a hexadecimal
666 * string. It must not be {@code null}.
667 * @param delimiter A delimiter to be inserted between bytes. It may be
668 * {@code null} if no delimiter should be used.
669 * @param buffer A buffer to which the hexadecimal representation of the
670 * contents of the provided byte array should be appended.
671 */
672 public static void toHex(final byte[] b, final String delimiter,
673 final StringBuilder buffer)
674 {
675 boolean first = true;
676 for (final byte bt : b)
677 {
678 if (first)
679 {
680 first = false;
681 }
682 else if (delimiter != null)
683 {
684 buffer.append(delimiter);
685 }
686
687 toHex(bt, buffer);
688 }
689 }
690
691
692
693 /**
694 * Retrieves a hex-encoded representation of the contents of the provided
695 * array, along with an ASCII representation of its contents next to it. The
696 * output will be split across multiple lines, with up to sixteen bytes per
697 * line. For each of those sixteen bytes, the two-digit hex representation
698 * will be appended followed by a space. Then, the ASCII representation of
699 * those sixteen bytes will follow that, with a space used in place of any
700 * byte that does not have an ASCII representation.
701 *
702 * @param array The array whose contents should be processed.
703 * @param indent The number of spaces to insert on each line prior to the
704 * first hex byte.
705 *
706 * @return A hex-encoded representation of the contents of the provided
707 * array, along with an ASCII representation of its contents next to
708 * it.
709 */
710 public static String toHexPlusASCII(final byte[] array, final int indent)
711 {
712 final StringBuilder buffer = new StringBuilder();
713 toHexPlusASCII(array, indent, buffer);
714 return buffer.toString();
715 }
716
717
718
719 /**
720 * Appends a hex-encoded representation of the contents of the provided array
721 * to the given buffer, along with an ASCII representation of its contents
722 * next to it. The output will be split across multiple lines, with up to
723 * sixteen bytes per line. For each of those sixteen bytes, the two-digit hex
724 * representation will be appended followed by a space. Then, the ASCII
725 * representation of those sixteen bytes will follow that, with a space used
726 * in place of any byte that does not have an ASCII representation.
727 *
728 * @param array The array whose contents should be processed.
729 * @param indent The number of spaces to insert on each line prior to the
730 * first hex byte.
731 * @param buffer The buffer to which the encoded data should be appended.
732 */
733 public static void toHexPlusASCII(final byte[] array, final int indent,
734 final StringBuilder buffer)
735 {
736 if ((array == null) || (array.length == 0))
737 {
738 return;
739 }
740
741 for (int i=0; i < indent; i++)
742 {
743 buffer.append(' ');
744 }
745
746 int pos = 0;
747 int startPos = 0;
748 while (pos < array.length)
749 {
750 toHex(array[pos++], buffer);
751 buffer.append(' ');
752
753 if ((pos % 16) == 0)
754 {
755 buffer.append(" ");
756 for (int i=startPos; i < pos; i++)
757 {
758 if ((array[i] < ' ') || (array[i] > '~'))
759 {
760 buffer.append(' ');
761 }
762 else
763 {
764 buffer.append((char) array[i]);
765 }
766 }
767 buffer.append(EOL);
768 startPos = pos;
769
770 if (pos < array.length)
771 {
772 for (int i=0; i < indent; i++)
773 {
774 buffer.append(' ');
775 }
776 }
777 }
778 }
779
780 // If the last line isn't complete yet, then finish it off.
781 if ((array.length % 16) != 0)
782 {
783 final int missingBytes = (16 - (array.length % 16));
784 if (missingBytes > 0)
785 {
786 for (int i=0; i < missingBytes; i++)
787 {
788 buffer.append(" ");
789 }
790 buffer.append(" ");
791 for (int i=startPos; i < array.length; i++)
792 {
793 if ((array[i] < ' ') || (array[i] > '~'))
794 {
795 buffer.append(' ');
796 }
797 else
798 {
799 buffer.append((char) array[i]);
800 }
801 }
802 buffer.append(EOL);
803 }
804 }
805 }
806
807
808
809 /**
810 * Appends a hex-encoded representation of the provided character to the given
811 * buffer. Each byte of the hex-encoded representation will be prefixed with
812 * a backslash.
813 *
814 * @param c The character to be encoded.
815 * @param buffer The buffer to which the hex-encoded representation should
816 * be appended.
817 */
818 public static void hexEncode(final char c, final StringBuilder buffer)
819 {
820 final byte[] charBytes;
821 if (c <= 0x7F)
822 {
823 charBytes = new byte[] { (byte) (c & 0x7F) };
824 }
825 else
826 {
827 charBytes = getBytes(String.valueOf(c));
828 }
829
830 for (final byte b : charBytes)
831 {
832 buffer.append('\\');
833 toHex(b, buffer);
834 }
835 }
836
837
838
839 /**
840 * Retrieves a single-line string representation of the stack trace for the
841 * provided {@code Throwable}. It will include the unqualified name of the
842 * {@code Throwable} class, a list of source files and line numbers (if
843 * available) for the stack trace, and will also include the stack trace for
844 * the cause (if present).
845 *
846 * @param t The {@code Throwable} for which to retrieve the stack trace.
847 *
848 * @return A single-line string representation of the stack trace for the
849 * provided {@code Throwable}.
850 */
851 public static String getStackTrace(final Throwable t)
852 {
853 final StringBuilder buffer = new StringBuilder();
854 getStackTrace(t, buffer);
855 return buffer.toString();
856 }
857
858
859
860 /**
861 * Appends a single-line string representation of the stack trace for the
862 * provided {@code Throwable} to the given buffer. It will include the
863 * unqualified name of the {@code Throwable} class, a list of source files and
864 * line numbers (if available) for the stack trace, and will also include the
865 * stack trace for the cause (if present).
866 *
867 * @param t The {@code Throwable} for which to retrieve the stack
868 * trace.
869 * @param buffer The buffer to which the information should be appended.
870 */
871 public static void getStackTrace(final Throwable t,
872 final StringBuilder buffer)
873 {
874 buffer.append(getUnqualifiedClassName(t.getClass()));
875 buffer.append('(');
876
877 final String message = t.getMessage();
878 if (message != null)
879 {
880 buffer.append("message='");
881 buffer.append(message);
882 buffer.append("', ");
883 }
884
885 buffer.append("trace='");
886 getStackTrace(t.getStackTrace(), buffer);
887 buffer.append('\'');
888
889 final Throwable cause = t.getCause();
890 if (cause != null)
891 {
892 buffer.append(", cause=");
893 getStackTrace(cause, buffer);
894 }
895 buffer.append(", revision=");
896 buffer.append(Version.REVISION_NUMBER);
897 buffer.append(')');
898 }
899
900
901
902 /**
903 * Returns a single-line string representation of the stack trace. It will
904 * include a list of source files and line numbers (if available) for the
905 * stack trace.
906 *
907 * @param elements The stack trace.
908 *
909 * @return A single-line string representation of the stack trace.
910 */
911 public static String getStackTrace(final StackTraceElement[] elements)
912 {
913 final StringBuilder buffer = new StringBuilder();
914 getStackTrace(elements, buffer);
915 return buffer.toString();
916 }
917
918
919
920 /**
921 * Appends a single-line string representation of the stack trace to the given
922 * buffer. It will include a list of source files and line numbers
923 * (if available) for the stack trace.
924 *
925 * @param elements The stack trace.
926 * @param buffer The buffer to which the information should be appended.
927 */
928 public static void getStackTrace(final StackTraceElement[] elements,
929 final StringBuilder buffer)
930 {
931 for (int i=0; i < elements.length; i++)
932 {
933 if (i > 0)
934 {
935 buffer.append(" / ");
936 }
937
938 buffer.append(elements[i].getMethodName());
939 buffer.append('(');
940 buffer.append(elements[i].getFileName());
941
942 final int lineNumber = elements[i].getLineNumber();
943 if (lineNumber > 0)
944 {
945 buffer.append(':');
946 buffer.append(lineNumber);
947 }
948 buffer.append(')');
949 }
950 }
951
952
953
954 /**
955 * Retrieves a string representation of the provided {@code Throwable} object
956 * suitable for use in a message. For runtime exceptions and errors, then a
957 * full stack trace for the exception will be provided. For exception types
958 * defined in the LDAP SDK, then its {@code getExceptionMessage} method will
959 * be used to get the string representation. For all other types of
960 * exceptions, then the standard string representation will be used.
961 * <BR><BR>
962 * For all types of exceptions, the message will also include the cause if one
963 * exists.
964 *
965 * @param t The {@code Throwable} for which to generate the exception
966 * message.
967 *
968 * @return A string representation of the provided {@code Throwable} object
969 * suitable for use in a message.
970 */
971 public static String getExceptionMessage(final Throwable t)
972 {
973 if (t == null)
974 {
975 return ERR_NO_EXCEPTION.get();
976 }
977
978 final StringBuilder buffer = new StringBuilder();
979 if (t instanceof LDAPSDKException)
980 {
981 buffer.append(((LDAPSDKException) t).getExceptionMessage());
982 }
983 else if (t instanceof LDAPSDKRuntimeException)
984 {
985 buffer.append(((LDAPSDKRuntimeException) t).getExceptionMessage());
986 }
987 if ((t instanceof RuntimeException) || (t instanceof Error))
988 {
989 return getStackTrace(t);
990 }
991 else
992 {
993 buffer.append(String.valueOf(t));
994 }
995
996 final Throwable cause = t.getCause();
997 if (cause != null)
998 {
999 buffer.append(" caused by ");
1000 buffer.append(getExceptionMessage(cause));
1001 }
1002
1003 return buffer.toString();
1004 }
1005
1006
1007
1008 /**
1009 * Retrieves the unqualified name (i.e., the name without package information)
1010 * for the provided class.
1011 *
1012 * @param c The class for which to retrieve the unqualified name.
1013 *
1014 * @return The unqualified name for the provided class.
1015 */
1016 public static String getUnqualifiedClassName(final Class<?> c)
1017 {
1018 final String className = c.getName();
1019 final int lastPeriodPos = className.lastIndexOf('.');
1020
1021 if (lastPeriodPos > 0)
1022 {
1023 return className.substring(lastPeriodPos+1);
1024 }
1025 else
1026 {
1027 return className;
1028 }
1029 }
1030
1031
1032
1033 /**
1034 * Encodes the provided date in generalized time format.
1035 *
1036 * @param d The date to be encoded in generalized time format.
1037 *
1038 * @return The generalized time representation of the provided date.
1039 */
1040 public static String encodeGeneralizedTime(final Date d)
1041 {
1042 SimpleDateFormat dateFormat = dateFormatters.get();
1043 if (dateFormat == null)
1044 {
1045 dateFormat = new SimpleDateFormat("yyyyMMddHHmmss.SSS'Z'");
1046 dateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
1047 dateFormatters.set(dateFormat);
1048 }
1049
1050 return dateFormat.format(d);
1051 }
1052
1053
1054
1055 /**
1056 * Decodes the provided string as a timestamp in generalized time format.
1057 *
1058 * @param t The timestamp to be decoded. It must not be {@code null}.
1059 *
1060 * @return The {@code Date} object decoded from the provided timestamp.
1061 *
1062 * @throws ParseException If the provided string could not be decoded as a
1063 * timestamp in generalized time format.
1064 */
1065 public static Date decodeGeneralizedTime(final String t)
1066 throws ParseException
1067 {
1068 ensureNotNull(t);
1069
1070 // Extract the time zone information from the end of the value.
1071 int tzPos;
1072 final TimeZone tz;
1073 if (t.endsWith("Z"))
1074 {
1075 tz = TimeZone.getTimeZone("UTC");
1076 tzPos = t.length() - 1;
1077 }
1078 else
1079 {
1080 tzPos = t.lastIndexOf('-');
1081 if (tzPos < 0)
1082 {
1083 tzPos = t.lastIndexOf('+');
1084 if (tzPos < 0)
1085 {
1086 throw new ParseException(ERR_GENTIME_DECODE_CANNOT_PARSE_TZ.get(t),
1087 0);
1088 }
1089 }
1090
1091 tz = TimeZone.getTimeZone("GMT" + t.substring(tzPos));
1092 if (tz.getRawOffset() == 0)
1093 {
1094 // This is the default time zone that will be returned if the value
1095 // cannot be parsed. If it's valid, then it will end in "+0000" or
1096 // "-0000". Otherwise, it's invalid and GMT was just a fallback.
1097 if (! (t.endsWith("+0000") || t.endsWith("-0000")))
1098 {
1099 throw new ParseException(ERR_GENTIME_DECODE_CANNOT_PARSE_TZ.get(t),
1100 tzPos);
1101 }
1102 }
1103 }
1104
1105
1106 // See if the timestamp has a sub-second portion. Note that if there is a
1107 // sub-second portion, then we may need to massage the value so that there
1108 // are exactly three sub-second characters so that it can be interpreted as
1109 // milliseconds.
1110 final String subSecFormatStr;
1111 final String trimmedTimestamp;
1112 int periodPos = t.lastIndexOf('.', tzPos);
1113 if (periodPos > 0)
1114 {
1115 final int subSecondLength = tzPos - periodPos - 1;
1116 switch (subSecondLength)
1117 {
1118 case 0:
1119 subSecFormatStr = "";
1120 trimmedTimestamp = t.substring(0, periodPos);
1121 break;
1122 case 1:
1123 subSecFormatStr = ".SSS";
1124 trimmedTimestamp = t.substring(0, (periodPos+2)) + "00";
1125 break;
1126 case 2:
1127 subSecFormatStr = ".SSS";
1128 trimmedTimestamp = t.substring(0, (periodPos+3)) + '0';
1129 break;
1130 default:
1131 subSecFormatStr = ".SSS";
1132 trimmedTimestamp = t.substring(0, periodPos+4);
1133 break;
1134 }
1135 }
1136 else
1137 {
1138 subSecFormatStr = "";
1139 periodPos = tzPos;
1140 trimmedTimestamp = t.substring(0, tzPos);
1141 }
1142
1143
1144 // Look at where the period is (or would be if it existed) to see how many
1145 // characters are in the integer portion. This will give us what we need
1146 // for the rest of the format string.
1147 final String formatStr;
1148 switch (periodPos)
1149 {
1150 case 10:
1151 formatStr = "yyyyMMddHH" + subSecFormatStr;
1152 break;
1153 case 12:
1154 formatStr = "yyyyMMddHHmm" + subSecFormatStr;
1155 break;
1156 case 14:
1157 formatStr = "yyyyMMddHHmmss" + subSecFormatStr;
1158 break;
1159 default:
1160 throw new ParseException(ERR_GENTIME_CANNOT_PARSE_INVALID_LENGTH.get(t),
1161 periodPos);
1162 }
1163
1164
1165 // We should finally be able to create an appropriate date format object
1166 // to parse the trimmed version of the timestamp.
1167 final SimpleDateFormat dateFormat = new SimpleDateFormat(formatStr);
1168 dateFormat.setTimeZone(tz);
1169 dateFormat.setLenient(false);
1170 return dateFormat.parse(trimmedTimestamp);
1171 }
1172
1173
1174
1175 /**
1176 * Trims only leading spaces from the provided string, leaving any trailing
1177 * spaces intact.
1178 *
1179 * @param s The string to be processed. It must not be {@code null}.
1180 *
1181 * @return The original string if no trimming was required, or a new string
1182 * without leading spaces if the provided string had one or more. It
1183 * may be an empty string if the provided string was an empty string
1184 * or contained only spaces.
1185 */
1186 public static String trimLeading(final String s)
1187 {
1188 ensureNotNull(s);
1189
1190 int nonSpacePos = 0;
1191 final int length = s.length();
1192 while ((nonSpacePos < length) && (s.charAt(nonSpacePos) == ' '))
1193 {
1194 nonSpacePos++;
1195 }
1196
1197 if (nonSpacePos == 0)
1198 {
1199 // There were no leading spaces.
1200 return s;
1201 }
1202 else if (nonSpacePos >= length)
1203 {
1204 // There were no non-space characters.
1205 return "";
1206 }
1207 else
1208 {
1209 // There were leading spaces, so return the string without them.
1210 return s.substring(nonSpacePos, length);
1211 }
1212 }
1213
1214
1215
1216 /**
1217 * Trims only trailing spaces from the provided string, leaving any leading
1218 * spaces intact.
1219 *
1220 * @param s The string to be processed. It must not be {@code null}.
1221 *
1222 * @return The original string if no trimming was required, or a new string
1223 * without trailing spaces if the provided string had one or more.
1224 * It may be an empty string if the provided string was an empty
1225 * string or contained only spaces.
1226 */
1227 public static String trimTrailing(final String s)
1228 {
1229 ensureNotNull(s);
1230
1231 final int lastPos = s.length() - 1;
1232 int nonSpacePos = lastPos;
1233 while ((nonSpacePos >= 0) && (s.charAt(nonSpacePos) == ' '))
1234 {
1235 nonSpacePos--;
1236 }
1237
1238 if (nonSpacePos < 0)
1239 {
1240 // There were no non-space characters.
1241 return "";
1242 }
1243 else if (nonSpacePos == lastPos)
1244 {
1245 // There were no trailing spaces.
1246 return s;
1247 }
1248 else
1249 {
1250 // There were trailing spaces, so return the string without them.
1251 return s.substring(0, (nonSpacePos+1));
1252 }
1253 }
1254
1255
1256
1257 /**
1258 * Wraps the contents of the specified line using the given width. It will
1259 * attempt to wrap at spaces to preserve words, but if that is not possible
1260 * (because a single "word" is longer than the maximum width), then it will
1261 * wrap in the middle of the word at the specified maximum width.
1262 *
1263 * @param line The line to be wrapped. It must not be {@code null}.
1264 * @param maxWidth The maximum width for lines in the resulting list. A
1265 * value less than or equal to zero will cause no wrapping
1266 * to be performed.
1267 *
1268 * @return A list of the wrapped lines. It may be empty if the provided line
1269 * contained only spaces.
1270 */
1271 public static List<String> wrapLine(final String line, final int maxWidth)
1272 {
1273 // See if the provided string already contains line breaks. If so, then
1274 // treat it as multiple lines rather than a single line.
1275 final int breakPos = line.indexOf('\n');
1276 if (breakPos >= 0)
1277 {
1278 final ArrayList<String> lineList = new ArrayList<String>(10);
1279 final StringTokenizer tokenizer = new StringTokenizer(line, "\r\n");
1280 while (tokenizer.hasMoreTokens())
1281 {
1282 lineList.addAll(wrapLine(tokenizer.nextToken(), maxWidth));
1283 }
1284
1285 return lineList;
1286 }
1287
1288 final int length = line.length();
1289 if ((maxWidth <= 0) || (length < maxWidth))
1290 {
1291 return Arrays.asList(line);
1292 }
1293
1294
1295 int wrapPos = maxWidth;
1296 int lastWrapPos = 0;
1297 final ArrayList<String> lineList = new ArrayList<String>(5);
1298 while (true)
1299 {
1300 final int spacePos = line.lastIndexOf(' ', wrapPos);
1301 if (spacePos > lastWrapPos)
1302 {
1303 // We found a space in an acceptable location, so use it after trimming
1304 // any trailing spaces.
1305 final String s = trimTrailing(line.substring(lastWrapPos, spacePos));
1306
1307 // Don't bother adding the line if it contained only spaces.
1308 if (s.length() > 0)
1309 {
1310 lineList.add(s);
1311 }
1312
1313 wrapPos = spacePos;
1314 }
1315 else
1316 {
1317 // We didn't find any spaces, so we'll have to insert a hard break at
1318 // the specified wrap column.
1319 lineList.add(line.substring(lastWrapPos, wrapPos));
1320 }
1321
1322 // Skip over any spaces before the next non-space character.
1323 while ((wrapPos < length) && (line.charAt(wrapPos) == ' '))
1324 {
1325 wrapPos++;
1326 }
1327
1328 lastWrapPos = wrapPos;
1329 wrapPos += maxWidth;
1330 if (wrapPos >= length)
1331 {
1332 // The last fragment can fit on the line, so we can handle that now and
1333 // break.
1334 if (lastWrapPos >= length)
1335 {
1336 break;
1337 }
1338 else
1339 {
1340 final String s = trimTrailing(line.substring(lastWrapPos));
1341 if (s.length() > 0)
1342 {
1343 lineList.add(s);
1344 }
1345 break;
1346 }
1347 }
1348 }
1349
1350 return lineList;
1351 }
1352
1353
1354
1355 /**
1356 * Retrieves a single string which is a concatenation of all of the provided
1357 * strings.
1358 *
1359 * @param a The array of strings to concatenate. It must not be
1360 * {@code null}.
1361 *
1362 * @return A string containing a concatenation of all of the strings in the
1363 * provided array.
1364 */
1365 public static String concatenateStrings(final String... a)
1366 {
1367 return concatenateStrings(null, null, " ", null, null, a);
1368 }
1369
1370
1371
1372 /**
1373 * Retrieves a single string which is a concatenation of all of the provided
1374 * strings.
1375 *
1376 * @param l The list of strings to concatenate. It must not be
1377 * {@code null}.
1378 *
1379 * @return A string containing a concatenation of all of the strings in the
1380 * provided list.
1381 */
1382 public static String concatenateStrings(final List<String> l)
1383 {
1384 return concatenateStrings(null, null, " ", null, null, l);
1385 }
1386
1387
1388
1389 /**
1390 * Retrieves a single string which is a concatenation of all of the provided
1391 * strings.
1392 *
1393 * @param beforeList A string that should be placed at the beginning of
1394 * the list. It may be {@code null} or empty if
1395 * nothing should be placed at the beginning of the
1396 * list.
1397 * @param beforeElement A string that should be placed before each element
1398 * in the list. It may be {@code null} or empty if
1399 * nothing should be placed before each element.
1400 * @param betweenElements The separator that should be placed between
1401 * elements in the list. It may be {@code null} or
1402 * empty if no separator should be placed between
1403 * elements.
1404 * @param afterElement A string that should be placed after each element
1405 * in the list. It may be {@code null} or empty if
1406 * nothing should be placed after each element.
1407 * @param afterList A string that should be placed at the end of the
1408 * list. It may be {@code null} or empty if nothing
1409 * should be placed at the end of the list.
1410 * @param a The array of strings to concatenate. It must not
1411 * be {@code null}.
1412 *
1413 * @return A string containing a concatenation of all of the strings in the
1414 * provided list.
1415 */
1416 public static String concatenateStrings(final String beforeList,
1417 final String beforeElement,
1418 final String betweenElements,
1419 final String afterElement,
1420 final String afterList,
1421 final String... a)
1422 {
1423 return concatenateStrings(beforeList, beforeElement, betweenElements,
1424 afterElement, afterList, Arrays.asList(a));
1425 }
1426
1427
1428
1429 /**
1430 * Retrieves a single string which is a concatenation of all of the provided
1431 * strings.
1432 *
1433 * @param beforeList A string that should be placed at the beginning of
1434 * the list. It may be {@code null} or empty if
1435 * nothing should be placed at the beginning of the
1436 * list.
1437 * @param beforeElement A string that should be placed before each element
1438 * in the list. It may be {@code null} or empty if
1439 * nothing should be placed before each element.
1440 * @param betweenElements The separator that should be placed between
1441 * elements in the list. It may be {@code null} or
1442 * empty if no separator should be placed between
1443 * elements.
1444 * @param afterElement A string that should be placed after each element
1445 * in the list. It may be {@code null} or empty if
1446 * nothing should be placed after each element.
1447 * @param afterList A string that should be placed at the end of the
1448 * list. It may be {@code null} or empty if nothing
1449 * should be placed at the end of the list.
1450 * @param l The list of strings to concatenate. It must not
1451 * be {@code null}.
1452 *
1453 * @return A string containing a concatenation of all of the strings in the
1454 * provided list.
1455 */
1456 public static String concatenateStrings(final String beforeList,
1457 final String beforeElement,
1458 final String betweenElements,
1459 final String afterElement,
1460 final String afterList,
1461 final List<String> l)
1462 {
1463 ensureNotNull(l);
1464
1465 final StringBuilder buffer = new StringBuilder();
1466
1467 if (beforeList != null)
1468 {
1469 buffer.append(beforeList);
1470 }
1471
1472 final Iterator<String> iterator = l.iterator();
1473 while (iterator.hasNext())
1474 {
1475 if (beforeElement != null)
1476 {
1477 buffer.append(beforeElement);
1478 }
1479
1480 buffer.append(iterator.next());
1481
1482 if (afterElement != null)
1483 {
1484 buffer.append(afterElement);
1485 }
1486
1487 if ((betweenElements != null) && iterator.hasNext())
1488 {
1489 buffer.append(betweenElements);
1490 }
1491 }
1492
1493 if (afterList != null)
1494 {
1495 buffer.append(afterList);
1496 }
1497
1498 return buffer.toString();
1499 }
1500
1501
1502
1503 /**
1504 * Converts a duration in seconds to a string with a human-readable duration
1505 * which may include days, hours, minutes, and seconds, to the extent that
1506 * they are needed.
1507 *
1508 * @param s The number of seconds to be represented.
1509 *
1510 * @return A string containing a human-readable representation of the
1511 * provided time.
1512 */
1513 public static String secondsToHumanReadableDuration(final long s)
1514 {
1515 return millisToHumanReadableDuration(s * 1000L);
1516 }
1517
1518
1519
1520 /**
1521 * Converts a duration in seconds to a string with a human-readable duration
1522 * which may include days, hours, minutes, and seconds, to the extent that
1523 * they are needed.
1524 *
1525 * @param m The number of milliseconds to be represented.
1526 *
1527 * @return A string containing a human-readable representation of the
1528 * provided time.
1529 */
1530 public static String millisToHumanReadableDuration(final long m)
1531 {
1532 final StringBuilder buffer = new StringBuilder();
1533 long numMillis = m;
1534
1535 final long numDays = numMillis / 86400000L;
1536 if (numDays > 0)
1537 {
1538 numMillis -= (numDays * 86400000L);
1539 if (numDays == 1)
1540 {
1541 buffer.append(INFO_NUM_DAYS_SINGULAR.get(numDays));
1542 }
1543 else
1544 {
1545 buffer.append(INFO_NUM_DAYS_PLURAL.get(numDays));
1546 }
1547 }
1548
1549 final long numHours = numMillis / 3600000L;
1550 if (numHours > 0)
1551 {
1552 numMillis -= (numHours * 3600000L);
1553 if (buffer.length() > 0)
1554 {
1555 buffer.append(", ");
1556 }
1557
1558 if (numHours == 1)
1559 {
1560 buffer.append(INFO_NUM_HOURS_SINGULAR.get(numHours));
1561 }
1562 else
1563 {
1564 buffer.append(INFO_NUM_HOURS_PLURAL.get(numHours));
1565 }
1566 }
1567
1568 final long numMinutes = numMillis / 60000L;
1569 if (numMinutes > 0)
1570 {
1571 numMillis -= (numMinutes * 60000L);
1572 if (buffer.length() > 0)
1573 {
1574 buffer.append(", ");
1575 }
1576
1577 if (numMinutes == 1)
1578 {
1579 buffer.append(INFO_NUM_MINUTES_SINGULAR.get(numMinutes));
1580 }
1581 else
1582 {
1583 buffer.append(INFO_NUM_MINUTES_PLURAL.get(numMinutes));
1584 }
1585 }
1586
1587 if (numMillis == 1000)
1588 {
1589 if (buffer.length() > 0)
1590 {
1591 buffer.append(", ");
1592 }
1593
1594 buffer.append(INFO_NUM_SECONDS_SINGULAR.get(1));
1595 }
1596 else if ((numMillis > 0) || (buffer.length() == 0))
1597 {
1598 if (buffer.length() > 0)
1599 {
1600 buffer.append(", ");
1601 }
1602
1603 final long numSeconds = numMillis / 1000L;
1604 numMillis -= (numSeconds * 1000L);
1605 if ((numMillis % 1000L) != 0L)
1606 {
1607 final double numSecondsDouble = numSeconds + (numMillis / 1000.0);
1608 final DecimalFormat decimalFormat = new DecimalFormat("0.000");
1609 buffer.append(INFO_NUM_SECONDS_WITH_DECIMAL.get(
1610 decimalFormat.format(numSecondsDouble)));
1611 }
1612 else
1613 {
1614 buffer.append(INFO_NUM_SECONDS_PLURAL.get(numSeconds));
1615 }
1616 }
1617
1618 return buffer.toString();
1619 }
1620
1621
1622
1623 /**
1624 * Converts the provided number of nanoseconds to milliseconds.
1625 *
1626 * @param nanos The number of nanoseconds to convert to milliseconds.
1627 *
1628 * @return The number of milliseconds that most closely corresponds to the
1629 * specified number of nanoseconds.
1630 */
1631 public static long nanosToMillis(final long nanos)
1632 {
1633 return Math.max(0L, Math.round(nanos / 1000000.0d));
1634 }
1635
1636
1637
1638 /**
1639 * Converts the provided number of milliseconds to nanoseconds.
1640 *
1641 * @param millis The number of milliseconds to convert to nanoseconds.
1642 *
1643 * @return The number of nanoseconds that most closely corresponds to the
1644 * specified number of milliseconds.
1645 */
1646 public static long millisToNanos(final long millis)
1647 {
1648 return Math.max(0L, (millis * 1000000L));
1649 }
1650
1651
1652
1653 /**
1654 * Indicates whether the provided string is a valid numeric OID. A numeric
1655 * OID must start and end with a digit, must have at least on period, must
1656 * contain only digits and periods, and must not have two consecutive periods.
1657 *
1658 * @param s The string to examine. It must not be {@code null}.
1659 *
1660 * @return {@code true} if the provided string is a valid numeric OID, or
1661 * {@code false} if not.
1662 */
1663 public static boolean isNumericOID(final String s)
1664 {
1665 boolean digitRequired = true;
1666 boolean periodFound = false;
1667 for (final char c : s.toCharArray())
1668 {
1669 switch (c)
1670 {
1671 case '0':
1672 case '1':
1673 case '2':
1674 case '3':
1675 case '4':
1676 case '5':
1677 case '6':
1678 case '7':
1679 case '8':
1680 case '9':
1681 digitRequired = false;
1682 break;
1683
1684 case '.':
1685 if (digitRequired)
1686 {
1687 return false;
1688 }
1689 else
1690 {
1691 digitRequired = true;
1692 }
1693 periodFound = true;
1694 break;
1695
1696 default:
1697 return false;
1698 }
1699
1700 }
1701
1702 return (periodFound && (! digitRequired));
1703 }
1704
1705
1706
1707 /**
1708 * Capitalizes the provided string. The first character will be converted to
1709 * uppercase, and the rest of the string will be left unaltered.
1710 *
1711 * @param s The string to be capitalized.
1712 *
1713 * @return A capitalized version of the provided string.
1714 */
1715 public static String capitalize(final String s)
1716 {
1717 if (s == null)
1718 {
1719 return null;
1720 }
1721
1722 switch (s.length())
1723 {
1724 case 0:
1725 return s;
1726
1727 case 1:
1728 return s.toUpperCase();
1729
1730 default:
1731 final char c = s.charAt(0);
1732 if (Character.isUpperCase(c))
1733 {
1734 return s;
1735 }
1736 else
1737 {
1738 return Character.toUpperCase(c) + s.substring(1);
1739 }
1740 }
1741 }
1742
1743
1744
1745 /**
1746 * Encodes the provided UUID to a byte array containing its 128-bit
1747 * representation.
1748 *
1749 * @param uuid The UUID to be encoded. It must not be {@code null}.
1750 *
1751 * @return The byte array containing the 128-bit encoded UUID.
1752 */
1753 public static byte[] encodeUUID(final UUID uuid)
1754 {
1755 final byte[] b = new byte[16];
1756
1757 final long mostSignificantBits = uuid.getMostSignificantBits();
1758 b[0] = (byte) ((mostSignificantBits >> 56) & 0xFF);
1759 b[1] = (byte) ((mostSignificantBits >> 48) & 0xFF);
1760 b[2] = (byte) ((mostSignificantBits >> 40) & 0xFF);
1761 b[3] = (byte) ((mostSignificantBits >> 32) & 0xFF);
1762 b[4] = (byte) ((mostSignificantBits >> 24) & 0xFF);
1763 b[5] = (byte) ((mostSignificantBits >> 16) & 0xFF);
1764 b[6] = (byte) ((mostSignificantBits >> 8) & 0xFF);
1765 b[7] = (byte) (mostSignificantBits & 0xFF);
1766
1767 final long leastSignificantBits = uuid.getLeastSignificantBits();
1768 b[8] = (byte) ((leastSignificantBits >> 56) & 0xFF);
1769 b[9] = (byte) ((leastSignificantBits >> 48) & 0xFF);
1770 b[10] = (byte) ((leastSignificantBits >> 40) & 0xFF);
1771 b[11] = (byte) ((leastSignificantBits >> 32) & 0xFF);
1772 b[12] = (byte) ((leastSignificantBits >> 24) & 0xFF);
1773 b[13] = (byte) ((leastSignificantBits >> 16) & 0xFF);
1774 b[14] = (byte) ((leastSignificantBits >> 8) & 0xFF);
1775 b[15] = (byte) (leastSignificantBits & 0xFF);
1776
1777 return b;
1778 }
1779
1780
1781
1782 /**
1783 * Decodes the value of the provided byte array as a Java UUID.
1784 *
1785 * @param b The byte array to be decoded as a UUID. It must not be
1786 * {@code null}.
1787 *
1788 * @return The decoded UUID.
1789 *
1790 * @throws ParseException If the provided byte array cannot be parsed as a
1791 * UUID.
1792 */
1793 public static UUID decodeUUID(final byte[] b)
1794 throws ParseException
1795 {
1796 if (b.length != 16)
1797 {
1798 throw new ParseException(ERR_DECODE_UUID_INVALID_LENGTH.get(toHex(b)), 0);
1799 }
1800
1801 long mostSignificantBits = 0L;
1802 for (int i=0; i < 8; i++)
1803 {
1804 mostSignificantBits = (mostSignificantBits << 8) | (b[i] & 0xFF);
1805 }
1806
1807 long leastSignificantBits = 0L;
1808 for (int i=8; i < 16; i++)
1809 {
1810 leastSignificantBits = (leastSignificantBits << 8) | (b[i] & 0xFF);
1811 }
1812
1813 return new UUID(mostSignificantBits, leastSignificantBits);
1814 }
1815
1816
1817
1818 /**
1819 * Returns {@code true} if and only if the current process is running on
1820 * a Windows-based operating system.
1821 *
1822 * @return {@code true} if the current process is running on a Windows-based
1823 * operating system and {@code false} otherwise.
1824 */
1825 public static boolean isWindows()
1826 {
1827 final String osName = toLowerCase(System.getProperty("os.name"));
1828 return ((osName != null) && osName.contains("windows"));
1829 }
1830
1831
1832
1833 /**
1834 * Attempts to parse the contents of the provided string to an argument list
1835 * (e.g., converts something like "--arg1 arg1value --arg2 --arg3 arg3value"
1836 * to a list of "--arg1", "arg1value", "--arg2", "--arg3", "arg3value").
1837 *
1838 * @param s The string to be converted to an argument list.
1839 *
1840 * @return The parsed argument list.
1841 *
1842 * @throws ParseException If a problem is encountered while attempting to
1843 * parse the given string to an argument list.
1844 */
1845 public static List<String> toArgumentList(final String s)
1846 throws ParseException
1847 {
1848 if ((s == null) || (s.length() == 0))
1849 {
1850 return Collections.emptyList();
1851 }
1852
1853 int quoteStartPos = -1;
1854 boolean inEscape = false;
1855 final ArrayList<String> argList = new ArrayList<String>();
1856 final StringBuilder currentArg = new StringBuilder();
1857 for (int i=0; i < s.length(); i++)
1858 {
1859 final char c = s.charAt(i);
1860 if (inEscape)
1861 {
1862 currentArg.append(c);
1863 inEscape = false;
1864 continue;
1865 }
1866
1867 if (c == '\\')
1868 {
1869 inEscape = true;
1870 }
1871 else if (c == '"')
1872 {
1873 if (quoteStartPos >= 0)
1874 {
1875 quoteStartPos = -1;
1876 }
1877 else
1878 {
1879 quoteStartPos = i;
1880 }
1881 }
1882 else if (c == ' ')
1883 {
1884 if (quoteStartPos >= 0)
1885 {
1886 currentArg.append(c);
1887 }
1888 else if (currentArg.length() > 0)
1889 {
1890 argList.add(currentArg.toString());
1891 currentArg.setLength(0);
1892 }
1893 }
1894 else
1895 {
1896 currentArg.append(c);
1897 }
1898 }
1899
1900 if (s.endsWith("\\") && (! s.endsWith("\\\\")))
1901 {
1902 throw new ParseException(ERR_ARG_STRING_DANGLING_BACKSLASH.get(),
1903 (s.length() - 1));
1904 }
1905
1906 if (quoteStartPos >= 0)
1907 {
1908 throw new ParseException(ERR_ARG_STRING_UNMATCHED_QUOTE.get(
1909 quoteStartPos), quoteStartPos);
1910 }
1911
1912 if (currentArg.length() > 0)
1913 {
1914 argList.add(currentArg.toString());
1915 }
1916
1917 return Collections.unmodifiableList(argList);
1918 }
1919
1920
1921
1922 /**
1923 * Creates a modifiable list with all of the items of the provided array in
1924 * the same order. This method behaves much like {@code Arrays.asList},
1925 * except that if the provided array is {@code null}, then it will return a
1926 * {@code null} list rather than throwing an exception.
1927 *
1928 * @param <T> The type of item contained in the provided array.
1929 *
1930 * @param array The array of items to include in the list.
1931 *
1932 * @return The list that was created, or {@code null} if the provided array
1933 * was {@code null}.
1934 */
1935 public static <T> List<T> toList(final T[] array)
1936 {
1937 if (array == null)
1938 {
1939 return null;
1940 }
1941
1942 final ArrayList<T> l = new ArrayList<T>(array.length);
1943 l.addAll(Arrays.asList(array));
1944 return l;
1945 }
1946
1947
1948
1949 /**
1950 * Creates a modifiable list with all of the items of the provided array in
1951 * the same order. This method behaves much like {@code Arrays.asList},
1952 * except that if the provided array is {@code null}, then it will return an
1953 * empty list rather than throwing an exception.
1954 *
1955 * @param <T> The type of item contained in the provided array.
1956 *
1957 * @param array The array of items to include in the list.
1958 *
1959 * @return The list that was created, or an empty list if the provided array
1960 * was {@code null}.
1961 */
1962 public static <T> List<T> toNonNullList(final T[] array)
1963 {
1964 if (array == null)
1965 {
1966 return new ArrayList<T>(0);
1967 }
1968
1969 final ArrayList<T> l = new ArrayList<T>(array.length);
1970 l.addAll(Arrays.asList(array));
1971 return l;
1972 }
1973
1974
1975
1976 /**
1977 * Indicates whether both of the provided objects are {@code null} or both
1978 * are logically equal (using the {@code equals} method).
1979 *
1980 * @param o1 The first object for which to make the determination.
1981 * @param o2 The second object for which to make the determination.
1982 *
1983 * @return {@code true} if both objects are {@code null} or both are
1984 * logically equal, or {@code false} if only one of the objects is
1985 * {@code null} or they are not logically equal.
1986 */
1987 public static boolean bothNullOrEqual(final Object o1, final Object o2)
1988 {
1989 if (o1 == null)
1990 {
1991 return (o2 == null);
1992 }
1993 else if (o2 == null)
1994 {
1995 return false;
1996 }
1997
1998 return o1.equals(o2);
1999 }
2000
2001
2002
2003 /**
2004 * Indicates whether both of the provided strings are {@code null} or both
2005 * are logically equal ignoring differences in capitalization (using the
2006 * {@code equalsIgnoreCase} method).
2007 *
2008 * @param s1 The first string for which to make the determination.
2009 * @param s2 The second string for which to make the determination.
2010 *
2011 * @return {@code true} if both strings are {@code null} or both are
2012 * logically equal ignoring differences in capitalization, or
2013 * {@code false} if only one of the objects is {@code null} or they
2014 * are not logically equal ignoring capitalization.
2015 */
2016 public static boolean bothNullOrEqualIgnoreCase(final String s1,
2017 final String s2)
2018 {
2019 if (s1 == null)
2020 {
2021 return (s2 == null);
2022 }
2023 else if (s2 == null)
2024 {
2025 return false;
2026 }
2027
2028 return s1.equalsIgnoreCase(s2);
2029 }
2030
2031
2032
2033 /**
2034 * Indicates whether the provided string arrays have the same elements,
2035 * ignoring the order in which they appear and differences in capitalization.
2036 * It is assumed that neither array contains {@code null} strings, and that
2037 * no string appears more than once in each array.
2038 *
2039 * @param a1 The first array for which to make the determination.
2040 * @param a2 The second array for which to make the determination.
2041 *
2042 * @return {@code true} if both arrays have the same set of strings, or
2043 * {@code false} if not.
2044 */
2045 public static boolean stringsEqualIgnoreCaseOrderIndependent(
2046 final String[] a1, final String[] a2)
2047 {
2048 if (a1 == null)
2049 {
2050 return (a2 == null);
2051 }
2052 else if (a2 == null)
2053 {
2054 return false;
2055 }
2056
2057 if (a1.length != a2.length)
2058 {
2059 return false;
2060 }
2061
2062 if (a1.length == 1)
2063 {
2064 return (a1[0].equalsIgnoreCase(a2[0]));
2065 }
2066
2067 final HashSet<String> s1 = new HashSet<String>(a1.length);
2068 for (final String s : a1)
2069 {
2070 s1.add(toLowerCase(s));
2071 }
2072
2073 final HashSet<String> s2 = new HashSet<String>(a2.length);
2074 for (final String s : a2)
2075 {
2076 s2.add(toLowerCase(s));
2077 }
2078
2079 return s1.equals(s2);
2080 }
2081
2082
2083
2084 /**
2085 * Indicates whether the provided arrays have the same elements, ignoring the
2086 * order in which they appear. It is assumed that neither array contains
2087 * {@code null} elements, and that no element appears more than once in each
2088 * array.
2089 *
2090 * @param <T> The type of element contained in the arrays.
2091 *
2092 * @param a1 The first array for which to make the determination.
2093 * @param a2 The second array for which to make the determination.
2094 *
2095 * @return {@code true} if both arrays have the same set of elements, or
2096 * {@code false} if not.
2097 */
2098 public static <T> boolean arraysEqualOrderIndependent(final T[] a1,
2099 final T[] a2)
2100 {
2101 if (a1 == null)
2102 {
2103 return (a2 == null);
2104 }
2105 else if (a2 == null)
2106 {
2107 return false;
2108 }
2109
2110 if (a1.length != a2.length)
2111 {
2112 return false;
2113 }
2114
2115 if (a1.length == 1)
2116 {
2117 return (a1[0].equals(a2[0]));
2118 }
2119
2120 final HashSet<T> s1 = new HashSet<T>(Arrays.asList(a1));
2121 final HashSet<T> s2 = new HashSet<T>(Arrays.asList(a2));
2122 return s1.equals(s2);
2123 }
2124
2125
2126
2127 /**
2128 * Determines the number of bytes in a UTF-8 character that starts with the
2129 * given byte.
2130 *
2131 * @param b The byte for which to make the determination.
2132 *
2133 * @return The number of bytes in a UTF-8 character that starts with the
2134 * given byte, or -1 if it does not appear to be a valid first byte
2135 * for a UTF-8 character.
2136 */
2137 public static int numBytesInUTF8CharacterWithFirstByte(final byte b)
2138 {
2139 if ((b & 0x7F) == b)
2140 {
2141 return 1;
2142 }
2143 else if ((b & 0xE0) == 0xC0)
2144 {
2145 return 2;
2146 }
2147 else if ((b & 0xF0) == 0xE0)
2148 {
2149 return 3;
2150 }
2151 else if ((b & 0xF8) == 0xF0)
2152 {
2153 return 4;
2154 }
2155 else
2156 {
2157 return -1;
2158 }
2159 }
2160 }