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