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