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