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