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