001/* 002 * Copyright 2007-2018 Ping Identity Corporation 003 * All Rights Reserved. 004 */ 005/* 006 * Copyright (C) 2008-2018 Ping Identity Corporation 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.io.BufferedReader; 026import java.io.File; 027import java.io.IOException; 028import java.io.StringReader; 029import java.nio.charset.StandardCharsets; 030import java.text.DecimalFormat; 031import java.text.ParseException; 032import java.text.SimpleDateFormat; 033import java.util.ArrayList; 034import java.util.Arrays; 035import java.util.Collection; 036import java.util.Collections; 037import java.util.Date; 038import java.util.HashSet; 039import java.util.Iterator; 040import java.util.LinkedHashSet; 041import java.util.List; 042import java.util.Set; 043import java.util.StringTokenizer; 044import java.util.TimeZone; 045import java.util.UUID; 046 047import com.unboundid.ldap.sdk.Attribute; 048import com.unboundid.ldap.sdk.Control; 049import com.unboundid.ldap.sdk.Version; 050 051import static com.unboundid.util.UtilityMessages.*; 052 053 054 055/** 056 * This class provides a number of static utility functions. 057 */ 058@ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE) 059public final class StaticUtils 060{ 061 /** 062 * A pre-allocated byte array containing zero bytes. 063 */ 064 public static final byte[] NO_BYTES = new byte[0]; 065 066 067 068 /** 069 * A pre-allocated empty character array. 070 */ 071 public static final char[] NO_CHARS = new char[0]; 072 073 074 075 /** 076 * A pre-allocated empty control array. 077 */ 078 public static final Control[] NO_CONTROLS = new Control[0]; 079 080 081 082 /** 083 * A pre-allocated empty string array. 084 */ 085 public static final String[] NO_STRINGS = new String[0]; 086 087 088 089 /** 090 * The end-of-line marker for this platform. 091 */ 092 public static final String EOL = System.getProperty("line.separator"); 093 094 095 096 /** 097 * A byte array containing the end-of-line marker for this platform. 098 */ 099 public static final byte[] EOL_BYTES = getBytes(EOL); 100 101 102 103 /** 104 * Indicates whether the unit tests are currently running. 105 */ 106 private static final boolean IS_WITHIN_UNIT_TESTS = 107 Boolean.getBoolean("com.unboundid.ldap.sdk.RunningUnitTests") || 108 Boolean.getBoolean("com.unboundid.directory.server.RunningUnitTests"); 109 110 111 112 /** 113 * The width of the terminal window, in columns. 114 */ 115 public static final int TERMINAL_WIDTH_COLUMNS; 116 static 117 { 118 // Try to dynamically determine the size of the terminal window using the 119 // COLUMNS environment variable. 120 int terminalWidth = 80; 121 final String columnsEnvVar = System.getenv("COLUMNS"); 122 if (columnsEnvVar != null) 123 { 124 try 125 { 126 terminalWidth = Integer.parseInt(columnsEnvVar); 127 } 128 catch (final Exception e) 129 { 130 Debug.debugException(e); 131 } 132 } 133 134 TERMINAL_WIDTH_COLUMNS = terminalWidth; 135 } 136 137 138 139 /** 140 * The thread-local date formatter used to encode generalized time values. 141 */ 142 private static final ThreadLocal<SimpleDateFormat> DATE_FORMATTERS = 143 new ThreadLocal<>(); 144 145 146 147 /** 148 * The {@code TimeZone} object that represents the UTC (universal coordinated 149 * time) time zone. 150 */ 151 private static final TimeZone UTC_TIME_ZONE = TimeZone.getTimeZone("UTC"); 152 153 154 155 /** 156 * A set containing the names of attributes that will be considered sensitive 157 * by the {@code toCode} methods of various request and data structure types. 158 */ 159 private static volatile Set<String> TO_CODE_SENSITIVE_ATTRIBUTE_NAMES; 160 static 161 { 162 final LinkedHashSet<String> nameSet = new LinkedHashSet<>(4); 163 164 // Add userPassword by name and OID. 165 nameSet.add("userpassword"); 166 nameSet.add("2.5.4.35"); 167 168 // add authPassword by name and OID. 169 nameSet.add("authpassword"); 170 nameSet.add("1.3.6.1.4.1.4203.1.3.4"); 171 172 TO_CODE_SENSITIVE_ATTRIBUTE_NAMES = Collections.unmodifiableSet(nameSet); 173 } 174 175 176 177 /** 178 * Prevent this class from being instantiated. 179 */ 180 private StaticUtils() 181 { 182 // No implementation is required. 183 } 184 185 186 187 /** 188 * Retrieves a UTF-8 byte representation of the provided string. 189 * 190 * @param s The string for which to retrieve the UTF-8 byte representation. 191 * 192 * @return The UTF-8 byte representation for the provided string. 193 */ 194 public static byte[] getBytes(final String s) 195 { 196 final int length; 197 if ((s == null) || ((length = s.length()) == 0)) 198 { 199 return NO_BYTES; 200 } 201 202 final byte[] b = new byte[length]; 203 for (int i=0; i < length; i++) 204 { 205 final char c = s.charAt(i); 206 if (c <= 0x7F) 207 { 208 b[i] = (byte) (c & 0x7F); 209 } 210 else 211 { 212 return s.getBytes(StandardCharsets.UTF_8); 213 } 214 } 215 216 return b; 217 } 218 219 220 221 /** 222 * Indicates whether the contents of the provided byte array represent an 223 * ASCII string, which is also known in LDAP terminology as an IA5 string. 224 * An ASCII string is one that contains only bytes in which the most 225 * significant bit is zero. 226 * 227 * @param b The byte array for which to make the determination. It must 228 * not be {@code null}. 229 * 230 * @return {@code true} if the contents of the provided array represent an 231 * ASCII string, or {@code false} if not. 232 */ 233 public static boolean isASCIIString(final byte[] b) 234 { 235 for (final byte by : b) 236 { 237 if ((by & 0x80) == 0x80) 238 { 239 return false; 240 } 241 } 242 243 return true; 244 } 245 246 247 248 /** 249 * Indicates whether the provided character is a printable ASCII character, as 250 * per RFC 4517 section 3.2. The only printable characters are: 251 * <UL> 252 * <LI>All uppercase and lowercase ASCII alphabetic letters</LI> 253 * <LI>All ASCII numeric digits</LI> 254 * <LI>The following additional ASCII characters: single quote, left 255 * parenthesis, right parenthesis, plus, comma, hyphen, period, equals, 256 * forward slash, colon, question mark, space.</LI> 257 * </UL> 258 * 259 * @param c The character for which to make the determination. 260 * 261 * @return {@code true} if the provided character is a printable ASCII 262 * character, or {@code false} if not. 263 */ 264 public static boolean isPrintable(final char c) 265 { 266 if (((c >= 'a') && (c <= 'z')) || 267 ((c >= 'A') && (c <= 'Z')) || 268 ((c >= '0') && (c <= '9'))) 269 { 270 return true; 271 } 272 273 switch (c) 274 { 275 case '\'': 276 case '(': 277 case ')': 278 case '+': 279 case ',': 280 case '-': 281 case '.': 282 case '=': 283 case '/': 284 case ':': 285 case '?': 286 case ' ': 287 return true; 288 default: 289 return false; 290 } 291 } 292 293 294 295 /** 296 * Indicates whether the contents of the provided byte array represent a 297 * printable LDAP string, as per RFC 4517 section 3.2. The only characters 298 * allowed in a printable string are: 299 * <UL> 300 * <LI>All uppercase and lowercase ASCII alphabetic letters</LI> 301 * <LI>All ASCII numeric digits</LI> 302 * <LI>The following additional ASCII characters: single quote, left 303 * parenthesis, right parenthesis, plus, comma, hyphen, period, equals, 304 * forward slash, colon, question mark, space.</LI> 305 * </UL> 306 * If the provided array contains anything other than the above characters 307 * (i.e., if the byte array contains any non-ASCII characters, or any ASCII 308 * control characters, or if it contains excluded ASCII characters like 309 * the exclamation point, double quote, octothorpe, dollar sign, etc.), then 310 * it will not be considered printable. 311 * 312 * @param b The byte array for which to make the determination. It must 313 * not be {@code null}. 314 * 315 * @return {@code true} if the contents of the provided byte array represent 316 * a printable LDAP string, or {@code false} if not. 317 */ 318 public static boolean isPrintableString(final byte[] b) 319 { 320 for (final byte by : b) 321 { 322 if ((by & 0x80) == 0x80) 323 { 324 return false; 325 } 326 327 if (((by >= 'a') && (by <= 'z')) || 328 ((by >= 'A') && (by <= 'Z')) || 329 ((by >= '0') && (by <= '9'))) 330 { 331 continue; 332 } 333 334 switch (by) 335 { 336 case '\'': 337 case '(': 338 case ')': 339 case '+': 340 case ',': 341 case '-': 342 case '.': 343 case '=': 344 case '/': 345 case ':': 346 case '?': 347 case ' ': 348 continue; 349 default: 350 return false; 351 } 352 } 353 354 return true; 355 } 356 357 358 359 /** 360 * Indicates whether the contents of the provided array are valid UTF-8. 361 * 362 * @param b The byte array to examine. It must not be {@code null}. 363 * 364 * @return {@code true} if the byte array can be parsed as a valid UTF-8 365 * string, or {@code false} if not. 366 */ 367 public static boolean isValidUTF8(final byte[] b) 368 { 369 int i = 0; 370 while (i < b.length) 371 { 372 final byte currentByte = b[i++]; 373 374 // If the most significant bit is not set, then this represents a valid 375 // single-byte character. 376 if ((currentByte & 0b1000_0000) == 0b0000_0000) 377 { 378 continue; 379 } 380 381 // If the first byte starts with 0b110, then it must be followed by 382 // another byte that starts with 0b10. 383 if ((currentByte & 0b1110_0000) == 0b1100_0000) 384 { 385 if (! hasExpectedSubsequentUTF8Bytes(b, i, 1)) 386 { 387 return false; 388 } 389 390 i++; 391 continue; 392 } 393 394 // If the first byte starts with 0b1110, then it must be followed by two 395 // more bytes that start with 0b10. 396 if ((currentByte & 0b1111_0000) == 0b1110_0000) 397 { 398 if (! hasExpectedSubsequentUTF8Bytes(b, i, 2)) 399 { 400 return false; 401 } 402 403 i += 2; 404 continue; 405 } 406 407 // If the first byte starts with 0b11110, then it must be followed by 408 // three more bytes that start with 0b10. 409 if ((currentByte & 0b1111_1000) == 0b1111_0000) 410 { 411 if (! hasExpectedSubsequentUTF8Bytes(b, i, 3)) 412 { 413 return false; 414 } 415 416 i += 3; 417 continue; 418 } 419 420 // If the first byte starts with 0b111110, then it must be followed by 421 // four more bytes that start with 0b10. 422 if ((currentByte & 0b1111_1100) == 0b1111_1000) 423 { 424 if (! hasExpectedSubsequentUTF8Bytes(b, i, 4)) 425 { 426 return false; 427 } 428 429 i += 4; 430 continue; 431 } 432 433 // If the first byte starts with 0b1111110, then it must be followed by 434 // five more bytes that start with 0b10. 435 if ((currentByte & 0b1111_1110) == 0b1111_1100) 436 { 437 if (! hasExpectedSubsequentUTF8Bytes(b, i, 5)) 438 { 439 return false; 440 } 441 442 i += 5; 443 continue; 444 } 445 446 // This is not a valid first byte for a UTF-8 character. 447 return false; 448 } 449 450 451 // If we've gotten here, then the provided array represents a valid UTF-8 452 // string. 453 return true; 454 } 455 456 457 458 /** 459 * Ensures that the provided array has the expected number of bytes that start 460 * with 0b10 starting at the specified position in the array. 461 * 462 * @param b The byte array to examine. 463 * @param p The position in the byte array at which to start looking. 464 * @param n The number of bytes to examine. 465 * 466 * @return {@code true} if the provided byte array has the expected number of 467 * bytes that start with 0b10, or {@code false} if not. 468 */ 469 private static boolean hasExpectedSubsequentUTF8Bytes(final byte[] b, 470 final int p, 471 final int n) 472 { 473 if (b.length < (p + n)) 474 { 475 return false; 476 } 477 478 for (int i=0; i < n; i++) 479 { 480 if ((b[p+i] & 0b1100_0000) != 0b1000_0000) 481 { 482 return false; 483 } 484 } 485 486 return true; 487 } 488 489 490 491 /** 492 * Retrieves a string generated from the provided byte array using the UTF-8 493 * encoding. 494 * 495 * @param b The byte array for which to return the associated string. 496 * 497 * @return The string generated from the provided byte array using the UTF-8 498 * encoding. 499 */ 500 public static String toUTF8String(final byte[] b) 501 { 502 try 503 { 504 return new String(b, StandardCharsets.UTF_8); 505 } 506 catch (final Exception e) 507 { 508 // This should never happen. 509 Debug.debugException(e); 510 return new String(b); 511 } 512 } 513 514 515 516 /** 517 * Retrieves a string generated from the specified portion of the provided 518 * byte array using the UTF-8 encoding. 519 * 520 * @param b The byte array for which to return the associated string. 521 * @param offset The offset in the array at which the value begins. 522 * @param length The number of bytes in the value to convert to a string. 523 * 524 * @return The string generated from the specified portion of the provided 525 * byte array using the UTF-8 encoding. 526 */ 527 public static String toUTF8String(final byte[] b, final int offset, 528 final int length) 529 { 530 try 531 { 532 return new String(b, offset, length, StandardCharsets.UTF_8); 533 } 534 catch (final Exception e) 535 { 536 // This should never happen. 537 Debug.debugException(e); 538 return new String(b, offset, length); 539 } 540 } 541 542 543 544 /** 545 * Retrieves a version of the provided string with the first character 546 * converted to lowercase but all other characters retaining their original 547 * capitalization. 548 * 549 * @param s The string to be processed. 550 * 551 * @return A version of the provided string with the first character 552 * converted to lowercase but all other characters retaining their 553 * original capitalization. 554 */ 555 public static String toInitialLowerCase(final String s) 556 { 557 if ((s == null) || s.isEmpty()) 558 { 559 return s; 560 } 561 else if (s.length() == 1) 562 { 563 return toLowerCase(s); 564 } 565 else 566 { 567 final char c = s.charAt(0); 568 if (((c >= 'A') && (c <= 'Z')) || (c < ' ') || (c > '~')) 569 { 570 final StringBuilder b = new StringBuilder(s); 571 b.setCharAt(0, Character.toLowerCase(c)); 572 return b.toString(); 573 } 574 else 575 { 576 return s; 577 } 578 } 579 } 580 581 582 583 /** 584 * Retrieves an all-lowercase version of the provided string. 585 * 586 * @param s The string for which to retrieve the lowercase version. 587 * 588 * @return An all-lowercase version of the provided string. 589 */ 590 public static String toLowerCase(final String s) 591 { 592 if (s == null) 593 { 594 return null; 595 } 596 597 final int length = s.length(); 598 final char[] charArray = s.toCharArray(); 599 for (int i=0; i < length; i++) 600 { 601 switch (charArray[i]) 602 { 603 case 'A': 604 charArray[i] = 'a'; 605 break; 606 case 'B': 607 charArray[i] = 'b'; 608 break; 609 case 'C': 610 charArray[i] = 'c'; 611 break; 612 case 'D': 613 charArray[i] = 'd'; 614 break; 615 case 'E': 616 charArray[i] = 'e'; 617 break; 618 case 'F': 619 charArray[i] = 'f'; 620 break; 621 case 'G': 622 charArray[i] = 'g'; 623 break; 624 case 'H': 625 charArray[i] = 'h'; 626 break; 627 case 'I': 628 charArray[i] = 'i'; 629 break; 630 case 'J': 631 charArray[i] = 'j'; 632 break; 633 case 'K': 634 charArray[i] = 'k'; 635 break; 636 case 'L': 637 charArray[i] = 'l'; 638 break; 639 case 'M': 640 charArray[i] = 'm'; 641 break; 642 case 'N': 643 charArray[i] = 'n'; 644 break; 645 case 'O': 646 charArray[i] = 'o'; 647 break; 648 case 'P': 649 charArray[i] = 'p'; 650 break; 651 case 'Q': 652 charArray[i] = 'q'; 653 break; 654 case 'R': 655 charArray[i] = 'r'; 656 break; 657 case 'S': 658 charArray[i] = 's'; 659 break; 660 case 'T': 661 charArray[i] = 't'; 662 break; 663 case 'U': 664 charArray[i] = 'u'; 665 break; 666 case 'V': 667 charArray[i] = 'v'; 668 break; 669 case 'W': 670 charArray[i] = 'w'; 671 break; 672 case 'X': 673 charArray[i] = 'x'; 674 break; 675 case 'Y': 676 charArray[i] = 'y'; 677 break; 678 case 'Z': 679 charArray[i] = 'z'; 680 break; 681 default: 682 if (charArray[i] > 0x7F) 683 { 684 return s.toLowerCase(); 685 } 686 break; 687 } 688 } 689 690 return new String(charArray); 691 } 692 693 694 695 /** 696 * Retrieves an all-uppercase version of the provided string. 697 * 698 * @param s The string for which to retrieve the uppercase version. 699 * 700 * @return An all-uppercase version of the provided string. 701 */ 702 public static String toUpperCase(final String s) 703 { 704 if (s == null) 705 { 706 return null; 707 } 708 709 final int length = s.length(); 710 final char[] charArray = s.toCharArray(); 711 for (int i=0; i < length; i++) 712 { 713 switch (charArray[i]) 714 { 715 case 'a': 716 charArray[i] = 'A'; 717 break; 718 case 'b': 719 charArray[i] = 'B'; 720 break; 721 case 'c': 722 charArray[i] = 'C'; 723 break; 724 case 'd': 725 charArray[i] = 'D'; 726 break; 727 case 'e': 728 charArray[i] = 'E'; 729 break; 730 case 'f': 731 charArray[i] = 'F'; 732 break; 733 case 'g': 734 charArray[i] = 'G'; 735 break; 736 case 'h': 737 charArray[i] = 'H'; 738 break; 739 case 'i': 740 charArray[i] = 'I'; 741 break; 742 case 'j': 743 charArray[i] = 'J'; 744 break; 745 case 'k': 746 charArray[i] = 'K'; 747 break; 748 case 'l': 749 charArray[i] = 'L'; 750 break; 751 case 'm': 752 charArray[i] = 'M'; 753 break; 754 case 'n': 755 charArray[i] = 'N'; 756 break; 757 case 'o': 758 charArray[i] = 'O'; 759 break; 760 case 'p': 761 charArray[i] = 'P'; 762 break; 763 case 'q': 764 charArray[i] = 'Q'; 765 break; 766 case 'r': 767 charArray[i] = 'R'; 768 break; 769 case 's': 770 charArray[i] = 'S'; 771 break; 772 case 't': 773 charArray[i] = 'T'; 774 break; 775 case 'u': 776 charArray[i] = 'U'; 777 break; 778 case 'v': 779 charArray[i] = 'V'; 780 break; 781 case 'w': 782 charArray[i] = 'W'; 783 break; 784 case 'x': 785 charArray[i] = 'X'; 786 break; 787 case 'y': 788 charArray[i] = 'Y'; 789 break; 790 case 'z': 791 charArray[i] = 'Z'; 792 break; 793 default: 794 if (charArray[i] > 0x7F) 795 { 796 return s.toUpperCase(); 797 } 798 break; 799 } 800 } 801 802 return new String(charArray); 803 } 804 805 806 807 /** 808 * Indicates whether the provided character is a valid hexadecimal digit. 809 * 810 * @param c The character for which to make the determination. 811 * 812 * @return {@code true} if the provided character does represent a valid 813 * hexadecimal digit, or {@code false} if not. 814 */ 815 public static boolean isHex(final char c) 816 { 817 switch (c) 818 { 819 case '0': 820 case '1': 821 case '2': 822 case '3': 823 case '4': 824 case '5': 825 case '6': 826 case '7': 827 case '8': 828 case '9': 829 case 'a': 830 case 'A': 831 case 'b': 832 case 'B': 833 case 'c': 834 case 'C': 835 case 'd': 836 case 'D': 837 case 'e': 838 case 'E': 839 case 'f': 840 case 'F': 841 return true; 842 843 default: 844 return false; 845 } 846 } 847 848 849 850 /** 851 * Retrieves a hexadecimal representation of the provided byte. 852 * 853 * @param b The byte to encode as hexadecimal. 854 * 855 * @return A string containing the hexadecimal representation of the provided 856 * byte. 857 */ 858 public static String toHex(final byte b) 859 { 860 final StringBuilder buffer = new StringBuilder(2); 861 toHex(b, buffer); 862 return buffer.toString(); 863 } 864 865 866 867 /** 868 * Appends a hexadecimal representation of the provided byte to the given 869 * buffer. 870 * 871 * @param b The byte to encode as hexadecimal. 872 * @param buffer The buffer to which the hexadecimal representation is to be 873 * appended. 874 */ 875 public static void toHex(final byte b, final StringBuilder buffer) 876 { 877 switch (b & 0xF0) 878 { 879 case 0x00: 880 buffer.append('0'); 881 break; 882 case 0x10: 883 buffer.append('1'); 884 break; 885 case 0x20: 886 buffer.append('2'); 887 break; 888 case 0x30: 889 buffer.append('3'); 890 break; 891 case 0x40: 892 buffer.append('4'); 893 break; 894 case 0x50: 895 buffer.append('5'); 896 break; 897 case 0x60: 898 buffer.append('6'); 899 break; 900 case 0x70: 901 buffer.append('7'); 902 break; 903 case 0x80: 904 buffer.append('8'); 905 break; 906 case 0x90: 907 buffer.append('9'); 908 break; 909 case 0xA0: 910 buffer.append('a'); 911 break; 912 case 0xB0: 913 buffer.append('b'); 914 break; 915 case 0xC0: 916 buffer.append('c'); 917 break; 918 case 0xD0: 919 buffer.append('d'); 920 break; 921 case 0xE0: 922 buffer.append('e'); 923 break; 924 case 0xF0: 925 buffer.append('f'); 926 break; 927 } 928 929 switch (b & 0x0F) 930 { 931 case 0x00: 932 buffer.append('0'); 933 break; 934 case 0x01: 935 buffer.append('1'); 936 break; 937 case 0x02: 938 buffer.append('2'); 939 break; 940 case 0x03: 941 buffer.append('3'); 942 break; 943 case 0x04: 944 buffer.append('4'); 945 break; 946 case 0x05: 947 buffer.append('5'); 948 break; 949 case 0x06: 950 buffer.append('6'); 951 break; 952 case 0x07: 953 buffer.append('7'); 954 break; 955 case 0x08: 956 buffer.append('8'); 957 break; 958 case 0x09: 959 buffer.append('9'); 960 break; 961 case 0x0A: 962 buffer.append('a'); 963 break; 964 case 0x0B: 965 buffer.append('b'); 966 break; 967 case 0x0C: 968 buffer.append('c'); 969 break; 970 case 0x0D: 971 buffer.append('d'); 972 break; 973 case 0x0E: 974 buffer.append('e'); 975 break; 976 case 0x0F: 977 buffer.append('f'); 978 break; 979 } 980 } 981 982 983 984 /** 985 * Retrieves a hexadecimal representation of the contents of the provided byte 986 * array. No delimiter character will be inserted between the hexadecimal 987 * digits for each byte. 988 * 989 * @param b The byte array to be represented as a hexadecimal string. It 990 * must not be {@code null}. 991 * 992 * @return A string containing a hexadecimal representation of the contents 993 * of the provided byte array. 994 */ 995 public static String toHex(final byte[] b) 996 { 997 Validator.ensureNotNull(b); 998 999 final StringBuilder buffer = new StringBuilder(2 * b.length); 1000 toHex(b, buffer); 1001 return buffer.toString(); 1002 } 1003 1004 1005 1006 /** 1007 * Retrieves a hexadecimal representation of the contents of the provided byte 1008 * array. No delimiter character will be inserted between the hexadecimal 1009 * digits for each byte. 1010 * 1011 * @param b The byte array to be represented as a hexadecimal string. 1012 * It must not be {@code null}. 1013 * @param buffer A buffer to which the hexadecimal representation of the 1014 * contents of the provided byte array should be appended. 1015 */ 1016 public static void toHex(final byte[] b, final StringBuilder buffer) 1017 { 1018 toHex(b, null, buffer); 1019 } 1020 1021 1022 1023 /** 1024 * Retrieves a hexadecimal representation of the contents of the provided byte 1025 * array. No delimiter character will be inserted between the hexadecimal 1026 * digits for each byte. 1027 * 1028 * @param b The byte array to be represented as a hexadecimal 1029 * string. It must not be {@code null}. 1030 * @param delimiter A delimiter to be inserted between bytes. It may be 1031 * {@code null} if no delimiter should be used. 1032 * @param buffer A buffer to which the hexadecimal representation of the 1033 * contents of the provided byte array should be appended. 1034 */ 1035 public static void toHex(final byte[] b, final String delimiter, 1036 final StringBuilder buffer) 1037 { 1038 boolean first = true; 1039 for (final byte bt : b) 1040 { 1041 if (first) 1042 { 1043 first = false; 1044 } 1045 else if (delimiter != null) 1046 { 1047 buffer.append(delimiter); 1048 } 1049 1050 toHex(bt, buffer); 1051 } 1052 } 1053 1054 1055 1056 /** 1057 * Retrieves a hex-encoded representation of the contents of the provided 1058 * array, along with an ASCII representation of its contents next to it. The 1059 * output will be split across multiple lines, with up to sixteen bytes per 1060 * line. For each of those sixteen bytes, the two-digit hex representation 1061 * will be appended followed by a space. Then, the ASCII representation of 1062 * those sixteen bytes will follow that, with a space used in place of any 1063 * byte that does not have an ASCII representation. 1064 * 1065 * @param array The array whose contents should be processed. 1066 * @param indent The number of spaces to insert on each line prior to the 1067 * first hex byte. 1068 * 1069 * @return A hex-encoded representation of the contents of the provided 1070 * array, along with an ASCII representation of its contents next to 1071 * it. 1072 */ 1073 public static String toHexPlusASCII(final byte[] array, final int indent) 1074 { 1075 final StringBuilder buffer = new StringBuilder(); 1076 toHexPlusASCII(array, indent, buffer); 1077 return buffer.toString(); 1078 } 1079 1080 1081 1082 /** 1083 * Appends a hex-encoded representation of the contents of the provided array 1084 * to the given buffer, along with an ASCII representation of its contents 1085 * next to it. The output will be split across multiple lines, with up to 1086 * sixteen bytes per line. For each of those sixteen bytes, the two-digit hex 1087 * representation will be appended followed by a space. Then, the ASCII 1088 * representation of those sixteen bytes will follow that, with a space used 1089 * in place of any byte that does not have an ASCII representation. 1090 * 1091 * @param array The array whose contents should be processed. 1092 * @param indent The number of spaces to insert on each line prior to the 1093 * first hex byte. 1094 * @param buffer The buffer to which the encoded data should be appended. 1095 */ 1096 public static void toHexPlusASCII(final byte[] array, final int indent, 1097 final StringBuilder buffer) 1098 { 1099 if ((array == null) || (array.length == 0)) 1100 { 1101 return; 1102 } 1103 1104 for (int i=0; i < indent; i++) 1105 { 1106 buffer.append(' '); 1107 } 1108 1109 int pos = 0; 1110 int startPos = 0; 1111 while (pos < array.length) 1112 { 1113 toHex(array[pos++], buffer); 1114 buffer.append(' '); 1115 1116 if ((pos % 16) == 0) 1117 { 1118 buffer.append(" "); 1119 for (int i=startPos; i < pos; i++) 1120 { 1121 if ((array[i] < ' ') || (array[i] > '~')) 1122 { 1123 buffer.append(' '); 1124 } 1125 else 1126 { 1127 buffer.append((char) array[i]); 1128 } 1129 } 1130 buffer.append(EOL); 1131 startPos = pos; 1132 1133 if (pos < array.length) 1134 { 1135 for (int i=0; i < indent; i++) 1136 { 1137 buffer.append(' '); 1138 } 1139 } 1140 } 1141 } 1142 1143 // If the last line isn't complete yet, then finish it off. 1144 if ((array.length % 16) != 0) 1145 { 1146 final int missingBytes = (16 - (array.length % 16)); 1147 if (missingBytes > 0) 1148 { 1149 for (int i=0; i < missingBytes; i++) 1150 { 1151 buffer.append(" "); 1152 } 1153 buffer.append(" "); 1154 for (int i=startPos; i < array.length; i++) 1155 { 1156 if ((array[i] < ' ') || (array[i] > '~')) 1157 { 1158 buffer.append(' '); 1159 } 1160 else 1161 { 1162 buffer.append((char) array[i]); 1163 } 1164 } 1165 buffer.append(EOL); 1166 } 1167 } 1168 } 1169 1170 1171 1172 /** 1173 * Retrieves the bytes that correspond to the provided hexadecimal string. 1174 * 1175 * @param hexString The hexadecimal string for which to retrieve the bytes. 1176 * It must not be {@code null}, and there must not be any 1177 * delimiter between bytes. 1178 * 1179 * @return The bytes that correspond to the provided hexadecimal string. 1180 * 1181 * @throws ParseException If the provided string does not represent valid 1182 * hexadecimal data, or if the provided string does 1183 * not contain an even number of characters. 1184 */ 1185 public static byte[] fromHex(final String hexString) 1186 throws ParseException 1187 { 1188 if ((hexString.length() % 2) != 0) 1189 { 1190 throw new ParseException( 1191 ERR_FROM_HEX_ODD_NUMBER_OF_CHARACTERS.get(hexString.length()), 1192 hexString.length()); 1193 } 1194 1195 final byte[] decodedBytes = new byte[hexString.length() / 2]; 1196 for (int i=0, j=0; i < decodedBytes.length; i++, j+= 2) 1197 { 1198 switch (hexString.charAt(j)) 1199 { 1200 case '0': 1201 // No action is required. 1202 break; 1203 case '1': 1204 decodedBytes[i] = 0x10; 1205 break; 1206 case '2': 1207 decodedBytes[i] = 0x20; 1208 break; 1209 case '3': 1210 decodedBytes[i] = 0x30; 1211 break; 1212 case '4': 1213 decodedBytes[i] = 0x40; 1214 break; 1215 case '5': 1216 decodedBytes[i] = 0x50; 1217 break; 1218 case '6': 1219 decodedBytes[i] = 0x60; 1220 break; 1221 case '7': 1222 decodedBytes[i] = 0x70; 1223 break; 1224 case '8': 1225 decodedBytes[i] = (byte) 0x80; 1226 break; 1227 case '9': 1228 decodedBytes[i] = (byte) 0x90; 1229 break; 1230 case 'a': 1231 case 'A': 1232 decodedBytes[i] = (byte) 0xA0; 1233 break; 1234 case 'b': 1235 case 'B': 1236 decodedBytes[i] = (byte) 0xB0; 1237 break; 1238 case 'c': 1239 case 'C': 1240 decodedBytes[i] = (byte) 0xC0; 1241 break; 1242 case 'd': 1243 case 'D': 1244 decodedBytes[i] = (byte) 0xD0; 1245 break; 1246 case 'e': 1247 case 'E': 1248 decodedBytes[i] = (byte) 0xE0; 1249 break; 1250 case 'f': 1251 case 'F': 1252 decodedBytes[i] = (byte) 0xF0; 1253 break; 1254 default: 1255 throw new ParseException(ERR_FROM_HEX_NON_HEX_CHARACTER.get(j), j); 1256 } 1257 1258 switch (hexString.charAt(j+1)) 1259 { 1260 case '0': 1261 // No action is required. 1262 break; 1263 case '1': 1264 decodedBytes[i] |= 0x01; 1265 break; 1266 case '2': 1267 decodedBytes[i] |= 0x02; 1268 break; 1269 case '3': 1270 decodedBytes[i] |= 0x03; 1271 break; 1272 case '4': 1273 decodedBytes[i] |= 0x04; 1274 break; 1275 case '5': 1276 decodedBytes[i] |= 0x05; 1277 break; 1278 case '6': 1279 decodedBytes[i] |= 0x06; 1280 break; 1281 case '7': 1282 decodedBytes[i] |= 0x07; 1283 break; 1284 case '8': 1285 decodedBytes[i] |= 0x08; 1286 break; 1287 case '9': 1288 decodedBytes[i] |= 0x09; 1289 break; 1290 case 'a': 1291 case 'A': 1292 decodedBytes[i] |= 0x0A; 1293 break; 1294 case 'b': 1295 case 'B': 1296 decodedBytes[i] |= 0x0B; 1297 break; 1298 case 'c': 1299 case 'C': 1300 decodedBytes[i] |= 0x0C; 1301 break; 1302 case 'd': 1303 case 'D': 1304 decodedBytes[i] |= 0x0D; 1305 break; 1306 case 'e': 1307 case 'E': 1308 decodedBytes[i] |= 0x0E; 1309 break; 1310 case 'f': 1311 case 'F': 1312 decodedBytes[i] |= 0x0F; 1313 break; 1314 default: 1315 throw new ParseException(ERR_FROM_HEX_NON_HEX_CHARACTER.get(j+1), 1316 j+1); 1317 } 1318 } 1319 1320 return decodedBytes; 1321 } 1322 1323 1324 1325 /** 1326 * Appends a hex-encoded representation of the provided character to the given 1327 * buffer. Each byte of the hex-encoded representation will be prefixed with 1328 * a backslash. 1329 * 1330 * @param c The character to be encoded. 1331 * @param buffer The buffer to which the hex-encoded representation should 1332 * be appended. 1333 */ 1334 public static void hexEncode(final char c, final StringBuilder buffer) 1335 { 1336 final byte[] charBytes; 1337 if (c <= 0x7F) 1338 { 1339 charBytes = new byte[] { (byte) (c & 0x7F) }; 1340 } 1341 else 1342 { 1343 charBytes = getBytes(String.valueOf(c)); 1344 } 1345 1346 for (final byte b : charBytes) 1347 { 1348 buffer.append('\\'); 1349 toHex(b, buffer); 1350 } 1351 } 1352 1353 1354 1355 /** 1356 * Appends a hex-encoded representation of the provided code point to the 1357 * given buffer. Each byte of the hex-encoded representation will be prefixed 1358 * with a backslash. 1359 * 1360 * @param codePoint The code point to be encoded. 1361 * @param buffer The buffer to which the hex-encoded representation 1362 * should be appended. 1363 */ 1364 public static void hexEncode(final int codePoint, final StringBuilder buffer) 1365 { 1366 final byte[] charBytes = 1367 getBytes(new String(new int[] { codePoint }, 0, 1)); 1368 1369 for (final byte b : charBytes) 1370 { 1371 buffer.append('\\'); 1372 toHex(b, buffer); 1373 } 1374 } 1375 1376 1377 1378 /** 1379 * Appends the Java code that may be used to create the provided byte 1380 * array to the given buffer. 1381 * 1382 * @param array The byte array containing the data to represent. It must 1383 * not be {@code null}. 1384 * @param buffer The buffer to which the code should be appended. 1385 */ 1386 public static void byteArrayToCode(final byte[] array, 1387 final StringBuilder buffer) 1388 { 1389 buffer.append("new byte[] {"); 1390 for (int i=0; i < array.length; i++) 1391 { 1392 if (i > 0) 1393 { 1394 buffer.append(','); 1395 } 1396 1397 buffer.append(" (byte) 0x"); 1398 toHex(array[i], buffer); 1399 } 1400 buffer.append(" }"); 1401 } 1402 1403 1404 1405 /** 1406 * Retrieves a single-line string representation of the stack trace for the 1407 * provided {@code Throwable}. It will include the unqualified name of the 1408 * {@code Throwable} class, a list of source files and line numbers (if 1409 * available) for the stack trace, and will also include the stack trace for 1410 * the cause (if present). 1411 * 1412 * @param t The {@code Throwable} for which to retrieve the stack trace. 1413 * 1414 * @return A single-line string representation of the stack trace for the 1415 * provided {@code Throwable}. 1416 */ 1417 public static String getStackTrace(final Throwable t) 1418 { 1419 final StringBuilder buffer = new StringBuilder(); 1420 getStackTrace(t, buffer); 1421 return buffer.toString(); 1422 } 1423 1424 1425 1426 /** 1427 * Appends a single-line string representation of the stack trace for the 1428 * provided {@code Throwable} to the given buffer. It will include the 1429 * unqualified name of the {@code Throwable} class, a list of source files and 1430 * line numbers (if available) for the stack trace, and will also include the 1431 * stack trace for the cause (if present). 1432 * 1433 * @param t The {@code Throwable} for which to retrieve the stack 1434 * trace. 1435 * @param buffer The buffer to which the information should be appended. 1436 */ 1437 public static void getStackTrace(final Throwable t, 1438 final StringBuilder buffer) 1439 { 1440 buffer.append(getUnqualifiedClassName(t.getClass())); 1441 buffer.append('('); 1442 1443 final String message = t.getMessage(); 1444 if (message != null) 1445 { 1446 buffer.append("message='"); 1447 buffer.append(message); 1448 buffer.append("', "); 1449 } 1450 1451 buffer.append("trace='"); 1452 getStackTrace(t.getStackTrace(), buffer); 1453 buffer.append('\''); 1454 1455 final Throwable cause = t.getCause(); 1456 if (cause != null) 1457 { 1458 buffer.append(", cause="); 1459 getStackTrace(cause, buffer); 1460 } 1461 1462 final String ldapSDKVersionString = ", ldapSDKVersion=" + 1463 Version.NUMERIC_VERSION_STRING + ", revision=" + Version.REVISION_ID; 1464 if (buffer.indexOf(ldapSDKVersionString) < 0) 1465 { 1466 buffer.append(ldapSDKVersionString); 1467 } 1468 1469 buffer.append(')'); 1470 } 1471 1472 1473 1474 /** 1475 * Returns a single-line string representation of the stack trace. It will 1476 * include a list of source files and line numbers (if available) for the 1477 * stack trace. 1478 * 1479 * @param elements The stack trace. 1480 * 1481 * @return A single-line string representation of the stack trace. 1482 */ 1483 public static String getStackTrace(final StackTraceElement[] elements) 1484 { 1485 final StringBuilder buffer = new StringBuilder(); 1486 getStackTrace(elements, buffer); 1487 return buffer.toString(); 1488 } 1489 1490 1491 1492 /** 1493 * Appends a single-line string representation of the stack trace to the given 1494 * buffer. It will include a list of source files and line numbers 1495 * (if available) for the stack trace. 1496 * 1497 * @param elements The stack trace. 1498 * @param buffer The buffer to which the information should be appended. 1499 */ 1500 public static void getStackTrace(final StackTraceElement[] elements, 1501 final StringBuilder buffer) 1502 { 1503 for (int i=0; i < elements.length; i++) 1504 { 1505 if (i > 0) 1506 { 1507 buffer.append(" / "); 1508 } 1509 1510 buffer.append(elements[i].getMethodName()); 1511 buffer.append('('); 1512 buffer.append(elements[i].getFileName()); 1513 1514 final int lineNumber = elements[i].getLineNumber(); 1515 if (lineNumber > 0) 1516 { 1517 buffer.append(':'); 1518 buffer.append(lineNumber); 1519 } 1520 else if (elements[i].isNativeMethod()) 1521 { 1522 buffer.append(":native"); 1523 } 1524 else 1525 { 1526 buffer.append(":unknown"); 1527 } 1528 buffer.append(')'); 1529 } 1530 } 1531 1532 1533 1534 /** 1535 * Retrieves a string representation of the provided {@code Throwable} object 1536 * suitable for use in a message. For runtime exceptions and errors, then a 1537 * full stack trace for the exception will be provided. For exception types 1538 * defined in the LDAP SDK, then its {@code getExceptionMessage} method will 1539 * be used to get the string representation. For all other types of 1540 * exceptions, then the standard string representation will be used. 1541 * <BR><BR> 1542 * For all types of exceptions, the message will also include the cause if one 1543 * exists. 1544 * 1545 * @param t The {@code Throwable} for which to generate the exception 1546 * message. 1547 * 1548 * @return A string representation of the provided {@code Throwable} object 1549 * suitable for use in a message. 1550 */ 1551 public static String getExceptionMessage(final Throwable t) 1552 { 1553 final boolean includeCause = 1554 Boolean.getBoolean(Debug.PROPERTY_INCLUDE_CAUSE_IN_EXCEPTION_MESSAGES); 1555 final boolean includeStackTrace = Boolean.getBoolean( 1556 Debug.PROPERTY_INCLUDE_STACK_TRACE_IN_EXCEPTION_MESSAGES); 1557 1558 return getExceptionMessage(t, includeCause, includeStackTrace); 1559 } 1560 1561 1562 1563 /** 1564 * Retrieves a string representation of the provided {@code Throwable} object 1565 * suitable for use in a message. For runtime exceptions and errors, then a 1566 * full stack trace for the exception will be provided. For exception types 1567 * defined in the LDAP SDK, then its {@code getExceptionMessage} method will 1568 * be used to get the string representation. For all other types of 1569 * exceptions, then the standard string representation will be used. 1570 * <BR><BR> 1571 * For all types of exceptions, the message will also include the cause if one 1572 * exists. 1573 * 1574 * @param t The {@code Throwable} for which to generate the 1575 * exception message. 1576 * @param includeCause Indicates whether to include information about 1577 * the cause (if any) in the exception message. 1578 * @param includeStackTrace Indicates whether to include a condensed 1579 * representation of the stack trace in the 1580 * exception message. 1581 * 1582 * @return A string representation of the provided {@code Throwable} object 1583 * suitable for use in a message. 1584 */ 1585 public static String getExceptionMessage(final Throwable t, 1586 final boolean includeCause, 1587 final boolean includeStackTrace) 1588 { 1589 if (t == null) 1590 { 1591 return ERR_NO_EXCEPTION.get(); 1592 } 1593 1594 final StringBuilder buffer = new StringBuilder(); 1595 if (t instanceof LDAPSDKException) 1596 { 1597 buffer.append(((LDAPSDKException) t).getExceptionMessage()); 1598 } 1599 else if (t instanceof LDAPSDKRuntimeException) 1600 { 1601 buffer.append(((LDAPSDKRuntimeException) t).getExceptionMessage()); 1602 } 1603 else if (t instanceof NullPointerException) 1604 { 1605 buffer.append("NullPointerException("); 1606 1607 final StackTraceElement[] stackTraceElements = t.getStackTrace(); 1608 for (int i=0; i < stackTraceElements.length; i++) 1609 { 1610 final StackTraceElement e = stackTraceElements[i]; 1611 if (i > 0) 1612 { 1613 buffer.append(" / "); 1614 } 1615 1616 buffer.append(e.getFileName()); 1617 1618 final int lineNumber = e.getLineNumber(); 1619 if (lineNumber > 0) 1620 { 1621 buffer.append(':'); 1622 buffer.append(lineNumber); 1623 } 1624 else if (e.isNativeMethod()) 1625 { 1626 buffer.append(":native"); 1627 } 1628 else 1629 { 1630 buffer.append(":unknown"); 1631 } 1632 1633 if (e.getClassName().contains("unboundid")) 1634 { 1635 if (i < (stackTraceElements.length - 1)) 1636 { 1637 buffer.append(" ..."); 1638 } 1639 1640 break; 1641 } 1642 } 1643 1644 buffer.append(')'); 1645 } 1646 else if ((t.getMessage() == null) || t.getMessage().isEmpty() || 1647 t.getMessage().equalsIgnoreCase("null")) 1648 { 1649 getStackTrace(t, buffer); 1650 } 1651 else 1652 { 1653 buffer.append(t.getClass().getSimpleName()); 1654 buffer.append('('); 1655 buffer.append(t.getMessage()); 1656 buffer.append(')'); 1657 1658 if (includeStackTrace) 1659 { 1660 buffer.append(" trace="); 1661 getStackTrace(t, buffer); 1662 } 1663 else if (includeCause) 1664 { 1665 final Throwable cause = t.getCause(); 1666 if (cause != null) 1667 { 1668 buffer.append(" caused by "); 1669 buffer.append(getExceptionMessage(cause)); 1670 } 1671 } 1672 } 1673 1674 final String ldapSDKVersionString = ", ldapSDKVersion=" + 1675 Version.NUMERIC_VERSION_STRING + ", revision=" + Version.REVISION_ID; 1676 if (buffer.indexOf(ldapSDKVersionString) < 0) 1677 { 1678 buffer.append(ldapSDKVersionString); 1679 } 1680 1681 return buffer.toString(); 1682 } 1683 1684 1685 1686 /** 1687 * Retrieves the unqualified name (i.e., the name without package information) 1688 * for the provided class. 1689 * 1690 * @param c The class for which to retrieve the unqualified name. 1691 * 1692 * @return The unqualified name for the provided class. 1693 */ 1694 public static String getUnqualifiedClassName(final Class<?> c) 1695 { 1696 final String className = c.getName(); 1697 final int lastPeriodPos = className.lastIndexOf('.'); 1698 1699 if (lastPeriodPos > 0) 1700 { 1701 return className.substring(lastPeriodPos+1); 1702 } 1703 else 1704 { 1705 return className; 1706 } 1707 } 1708 1709 1710 1711 /** 1712 * Retrieves a {@code TimeZone} object that represents the UTC (universal 1713 * coordinated time) time zone. 1714 * 1715 * @return A {@code TimeZone} object that represents the UTC time zone. 1716 */ 1717 public static TimeZone getUTCTimeZone() 1718 { 1719 return UTC_TIME_ZONE; 1720 } 1721 1722 1723 1724 /** 1725 * Encodes the provided timestamp in generalized time format. 1726 * 1727 * @param timestamp The timestamp to be encoded in generalized time format. 1728 * It should use the same format as the 1729 * {@code System.currentTimeMillis()} method (i.e., the 1730 * number of milliseconds since 12:00am UTC on January 1, 1731 * 1970). 1732 * 1733 * @return The generalized time representation of the provided date. 1734 */ 1735 public static String encodeGeneralizedTime(final long timestamp) 1736 { 1737 return encodeGeneralizedTime(new Date(timestamp)); 1738 } 1739 1740 1741 1742 /** 1743 * Encodes the provided date in generalized time format. 1744 * 1745 * @param d The date to be encoded in generalized time format. 1746 * 1747 * @return The generalized time representation of the provided date. 1748 */ 1749 public static String encodeGeneralizedTime(final Date d) 1750 { 1751 SimpleDateFormat dateFormat = DATE_FORMATTERS.get(); 1752 if (dateFormat == null) 1753 { 1754 dateFormat = new SimpleDateFormat("yyyyMMddHHmmss.SSS'Z'"); 1755 dateFormat.setTimeZone(UTC_TIME_ZONE); 1756 DATE_FORMATTERS.set(dateFormat); 1757 } 1758 1759 return dateFormat.format(d); 1760 } 1761 1762 1763 1764 /** 1765 * Decodes the provided string as a timestamp in generalized time format. 1766 * 1767 * @param t The timestamp to be decoded. It must not be {@code null}. 1768 * 1769 * @return The {@code Date} object decoded from the provided timestamp. 1770 * 1771 * @throws ParseException If the provided string could not be decoded as a 1772 * timestamp in generalized time format. 1773 */ 1774 public static Date decodeGeneralizedTime(final String t) 1775 throws ParseException 1776 { 1777 Validator.ensureNotNull(t); 1778 1779 // Extract the time zone information from the end of the value. 1780 int tzPos; 1781 final TimeZone tz; 1782 if (t.endsWith("Z")) 1783 { 1784 tz = TimeZone.getTimeZone("UTC"); 1785 tzPos = t.length() - 1; 1786 } 1787 else 1788 { 1789 tzPos = t.lastIndexOf('-'); 1790 if (tzPos < 0) 1791 { 1792 tzPos = t.lastIndexOf('+'); 1793 if (tzPos < 0) 1794 { 1795 throw new ParseException(ERR_GENTIME_DECODE_CANNOT_PARSE_TZ.get(t), 1796 0); 1797 } 1798 } 1799 1800 tz = TimeZone.getTimeZone("GMT" + t.substring(tzPos)); 1801 if (tz.getRawOffset() == 0) 1802 { 1803 // This is the default time zone that will be returned if the value 1804 // cannot be parsed. If it's valid, then it will end in "+0000" or 1805 // "-0000". Otherwise, it's invalid and GMT was just a fallback. 1806 if (! (t.endsWith("+0000") || t.endsWith("-0000"))) 1807 { 1808 throw new ParseException(ERR_GENTIME_DECODE_CANNOT_PARSE_TZ.get(t), 1809 tzPos); 1810 } 1811 } 1812 } 1813 1814 1815 // See if the timestamp has a sub-second portion. Note that if there is a 1816 // sub-second portion, then we may need to massage the value so that there 1817 // are exactly three sub-second characters so that it can be interpreted as 1818 // milliseconds. 1819 final String subSecFormatStr; 1820 final String trimmedTimestamp; 1821 int periodPos = t.lastIndexOf('.', tzPos); 1822 if (periodPos > 0) 1823 { 1824 final int subSecondLength = tzPos - periodPos - 1; 1825 switch (subSecondLength) 1826 { 1827 case 0: 1828 subSecFormatStr = ""; 1829 trimmedTimestamp = t.substring(0, periodPos); 1830 break; 1831 case 1: 1832 subSecFormatStr = ".SSS"; 1833 trimmedTimestamp = t.substring(0, (periodPos+2)) + "00"; 1834 break; 1835 case 2: 1836 subSecFormatStr = ".SSS"; 1837 trimmedTimestamp = t.substring(0, (periodPos+3)) + '0'; 1838 break; 1839 default: 1840 subSecFormatStr = ".SSS"; 1841 trimmedTimestamp = t.substring(0, periodPos+4); 1842 break; 1843 } 1844 } 1845 else 1846 { 1847 subSecFormatStr = ""; 1848 periodPos = tzPos; 1849 trimmedTimestamp = t.substring(0, tzPos); 1850 } 1851 1852 1853 // Look at where the period is (or would be if it existed) to see how many 1854 // characters are in the integer portion. This will give us what we need 1855 // for the rest of the format string. 1856 final String formatStr; 1857 switch (periodPos) 1858 { 1859 case 10: 1860 formatStr = "yyyyMMddHH" + subSecFormatStr; 1861 break; 1862 case 12: 1863 formatStr = "yyyyMMddHHmm" + subSecFormatStr; 1864 break; 1865 case 14: 1866 formatStr = "yyyyMMddHHmmss" + subSecFormatStr; 1867 break; 1868 default: 1869 throw new ParseException(ERR_GENTIME_CANNOT_PARSE_INVALID_LENGTH.get(t), 1870 periodPos); 1871 } 1872 1873 1874 // We should finally be able to create an appropriate date format object 1875 // to parse the trimmed version of the timestamp. 1876 final SimpleDateFormat dateFormat = new SimpleDateFormat(formatStr); 1877 dateFormat.setTimeZone(tz); 1878 dateFormat.setLenient(false); 1879 return dateFormat.parse(trimmedTimestamp); 1880 } 1881 1882 1883 1884 /** 1885 * Trims only leading spaces from the provided string, leaving any trailing 1886 * spaces intact. 1887 * 1888 * @param s The string to be processed. It must not be {@code null}. 1889 * 1890 * @return The original string if no trimming was required, or a new string 1891 * without leading spaces if the provided string had one or more. It 1892 * may be an empty string if the provided string was an empty string 1893 * or contained only spaces. 1894 */ 1895 public static String trimLeading(final String s) 1896 { 1897 Validator.ensureNotNull(s); 1898 1899 int nonSpacePos = 0; 1900 final int length = s.length(); 1901 while ((nonSpacePos < length) && (s.charAt(nonSpacePos) == ' ')) 1902 { 1903 nonSpacePos++; 1904 } 1905 1906 if (nonSpacePos == 0) 1907 { 1908 // There were no leading spaces. 1909 return s; 1910 } 1911 else if (nonSpacePos >= length) 1912 { 1913 // There were no non-space characters. 1914 return ""; 1915 } 1916 else 1917 { 1918 // There were leading spaces, so return the string without them. 1919 return s.substring(nonSpacePos, length); 1920 } 1921 } 1922 1923 1924 1925 /** 1926 * Trims only trailing spaces from the provided string, leaving any leading 1927 * spaces intact. 1928 * 1929 * @param s The string to be processed. It must not be {@code null}. 1930 * 1931 * @return The original string if no trimming was required, or a new string 1932 * without trailing spaces if the provided string had one or more. 1933 * It may be an empty string if the provided string was an empty 1934 * string or contained only spaces. 1935 */ 1936 public static String trimTrailing(final String s) 1937 { 1938 Validator.ensureNotNull(s); 1939 1940 final int lastPos = s.length() - 1; 1941 int nonSpacePos = lastPos; 1942 while ((nonSpacePos >= 0) && (s.charAt(nonSpacePos) == ' ')) 1943 { 1944 nonSpacePos--; 1945 } 1946 1947 if (nonSpacePos < 0) 1948 { 1949 // There were no non-space characters. 1950 return ""; 1951 } 1952 else if (nonSpacePos == lastPos) 1953 { 1954 // There were no trailing spaces. 1955 return s; 1956 } 1957 else 1958 { 1959 // There were trailing spaces, so return the string without them. 1960 return s.substring(0, (nonSpacePos+1)); 1961 } 1962 } 1963 1964 1965 1966 /** 1967 * Wraps the contents of the specified line using the given width. It will 1968 * attempt to wrap at spaces to preserve words, but if that is not possible 1969 * (because a single "word" is longer than the maximum width), then it will 1970 * wrap in the middle of the word at the specified maximum width. 1971 * 1972 * @param line The line to be wrapped. It must not be {@code null}. 1973 * @param maxWidth The maximum width for lines in the resulting list. A 1974 * value less than or equal to zero will cause no wrapping 1975 * to be performed. 1976 * 1977 * @return A list of the wrapped lines. It may be empty if the provided line 1978 * contained only spaces. 1979 */ 1980 public static List<String> wrapLine(final String line, final int maxWidth) 1981 { 1982 return wrapLine(line, maxWidth, maxWidth); 1983 } 1984 1985 1986 1987 /** 1988 * Wraps the contents of the specified line using the given width. It will 1989 * attempt to wrap at spaces to preserve words, but if that is not possible 1990 * (because a single "word" is longer than the maximum width), then it will 1991 * wrap in the middle of the word at the specified maximum width. 1992 * 1993 * @param line The line to be wrapped. It must not be 1994 * {@code null}. 1995 * @param maxFirstLineWidth The maximum length for the first line in 1996 * the resulting list. A value less than or 1997 * equal to zero will cause no wrapping to be 1998 * performed. 1999 * @param maxSubsequentLineWidth The maximum length for all lines except the 2000 * first line. This must be greater than zero 2001 * unless {@code maxFirstLineWidth} is less 2002 * than or equal to zero. 2003 * 2004 * @return A list of the wrapped lines. It may be empty if the provided line 2005 * contained only spaces. 2006 */ 2007 public static List<String> wrapLine(final String line, 2008 final int maxFirstLineWidth, 2009 final int maxSubsequentLineWidth) 2010 { 2011 if (maxFirstLineWidth > 0) 2012 { 2013 Validator.ensureTrue(maxSubsequentLineWidth > 0); 2014 } 2015 2016 // See if the provided string already contains line breaks. If so, then 2017 // treat it as multiple lines rather than a single line. 2018 final int breakPos = line.indexOf('\n'); 2019 if (breakPos >= 0) 2020 { 2021 final ArrayList<String> lineList = new ArrayList<>(10); 2022 final StringTokenizer tokenizer = new StringTokenizer(line, "\r\n"); 2023 while (tokenizer.hasMoreTokens()) 2024 { 2025 lineList.addAll(wrapLine(tokenizer.nextToken(), maxFirstLineWidth, 2026 maxSubsequentLineWidth)); 2027 } 2028 2029 return lineList; 2030 } 2031 2032 final int length = line.length(); 2033 if ((maxFirstLineWidth <= 0) || (length < maxFirstLineWidth)) 2034 { 2035 return Collections.singletonList(line); 2036 } 2037 2038 2039 int wrapPos = maxFirstLineWidth; 2040 int lastWrapPos = 0; 2041 final ArrayList<String> lineList = new ArrayList<>(5); 2042 while (true) 2043 { 2044 final int spacePos = line.lastIndexOf(' ', wrapPos); 2045 if (spacePos > lastWrapPos) 2046 { 2047 // We found a space in an acceptable location, so use it after trimming 2048 // any trailing spaces. 2049 final String s = trimTrailing(line.substring(lastWrapPos, spacePos)); 2050 2051 // Don't bother adding the line if it contained only spaces. 2052 if (! s.isEmpty()) 2053 { 2054 lineList.add(s); 2055 } 2056 2057 wrapPos = spacePos; 2058 } 2059 else 2060 { 2061 // We didn't find any spaces, so we'll have to insert a hard break at 2062 // the specified wrap column. 2063 lineList.add(line.substring(lastWrapPos, wrapPos)); 2064 } 2065 2066 // Skip over any spaces before the next non-space character. 2067 while ((wrapPos < length) && (line.charAt(wrapPos) == ' ')) 2068 { 2069 wrapPos++; 2070 } 2071 2072 lastWrapPos = wrapPos; 2073 wrapPos += maxSubsequentLineWidth; 2074 if (wrapPos >= length) 2075 { 2076 // The last fragment can fit on the line, so we can handle that now and 2077 // break. 2078 if (lastWrapPos >= length) 2079 { 2080 break; 2081 } 2082 else 2083 { 2084 final String s = line.substring(lastWrapPos); 2085 if (! s.isEmpty()) 2086 { 2087 lineList.add(s); 2088 } 2089 break; 2090 } 2091 } 2092 } 2093 2094 return lineList; 2095 } 2096 2097 2098 2099 /** 2100 * This method returns a form of the provided argument that is safe to 2101 * use on the command line for the local platform. This method is provided as 2102 * a convenience wrapper around {@link ExampleCommandLineArgument}. Calling 2103 * this method is equivalent to: 2104 * 2105 * <PRE> 2106 * return ExampleCommandLineArgument.getCleanArgument(s).getLocalForm(); 2107 * </PRE> 2108 * 2109 * For getting direct access to command line arguments that are safe to 2110 * use on other platforms, call 2111 * {@link ExampleCommandLineArgument#getCleanArgument}. 2112 * 2113 * @param s The string to be processed. It must not be {@code null}. 2114 * 2115 * @return A cleaned version of the provided string in a form that will allow 2116 * it to be displayed as the value of a command-line argument on. 2117 */ 2118 public static String cleanExampleCommandLineArgument(final String s) 2119 { 2120 return ExampleCommandLineArgument.getCleanArgument(s).getLocalForm(); 2121 } 2122 2123 2124 2125 /** 2126 * Retrieves a single string which is a concatenation of all of the provided 2127 * strings. 2128 * 2129 * @param a The array of strings to concatenate. It must not be 2130 * {@code null}. 2131 * 2132 * @return A string containing a concatenation of all of the strings in the 2133 * provided array. 2134 */ 2135 public static String concatenateStrings(final String... a) 2136 { 2137 return concatenateStrings(null, null, " ", null, null, a); 2138 } 2139 2140 2141 2142 /** 2143 * Retrieves a single string which is a concatenation of all of the provided 2144 * strings. 2145 * 2146 * @param l The list of strings to concatenate. It must not be 2147 * {@code null}. 2148 * 2149 * @return A string containing a concatenation of all of the strings in the 2150 * provided list. 2151 */ 2152 public static String concatenateStrings(final List<String> l) 2153 { 2154 return concatenateStrings(null, null, " ", null, null, l); 2155 } 2156 2157 2158 2159 /** 2160 * Retrieves a single string which is a concatenation of all of the provided 2161 * strings. 2162 * 2163 * @param beforeList A string that should be placed at the beginning of 2164 * the list. It may be {@code null} or empty if 2165 * nothing should be placed at the beginning of the 2166 * list. 2167 * @param beforeElement A string that should be placed before each element 2168 * in the list. It may be {@code null} or empty if 2169 * nothing should be placed before each element. 2170 * @param betweenElements The separator that should be placed between 2171 * elements in the list. It may be {@code null} or 2172 * empty if no separator should be placed between 2173 * elements. 2174 * @param afterElement A string that should be placed after each element 2175 * in the list. It may be {@code null} or empty if 2176 * nothing should be placed after each element. 2177 * @param afterList A string that should be placed at the end of the 2178 * list. It may be {@code null} or empty if nothing 2179 * should be placed at the end of the list. 2180 * @param a The array of strings to concatenate. It must not 2181 * be {@code null}. 2182 * 2183 * @return A string containing a concatenation of all of the strings in the 2184 * provided list. 2185 */ 2186 public static String concatenateStrings(final String beforeList, 2187 final String beforeElement, 2188 final String betweenElements, 2189 final String afterElement, 2190 final String afterList, 2191 final String... a) 2192 { 2193 return concatenateStrings(beforeList, beforeElement, betweenElements, 2194 afterElement, afterList, Arrays.asList(a)); 2195 } 2196 2197 2198 2199 /** 2200 * Retrieves a single string which is a concatenation of all of the provided 2201 * strings. 2202 * 2203 * @param beforeList A string that should be placed at the beginning of 2204 * the list. It may be {@code null} or empty if 2205 * nothing should be placed at the beginning of the 2206 * list. 2207 * @param beforeElement A string that should be placed before each element 2208 * in the list. It may be {@code null} or empty if 2209 * nothing should be placed before each element. 2210 * @param betweenElements The separator that should be placed between 2211 * elements in the list. It may be {@code null} or 2212 * empty if no separator should be placed between 2213 * elements. 2214 * @param afterElement A string that should be placed after each element 2215 * in the list. It may be {@code null} or empty if 2216 * nothing should be placed after each element. 2217 * @param afterList A string that should be placed at the end of the 2218 * list. It may be {@code null} or empty if nothing 2219 * should be placed at the end of the list. 2220 * @param l The list of strings to concatenate. It must not 2221 * be {@code null}. 2222 * 2223 * @return A string containing a concatenation of all of the strings in the 2224 * provided list. 2225 */ 2226 public static String concatenateStrings(final String beforeList, 2227 final String beforeElement, 2228 final String betweenElements, 2229 final String afterElement, 2230 final String afterList, 2231 final List<String> l) 2232 { 2233 Validator.ensureNotNull(l); 2234 2235 final StringBuilder buffer = new StringBuilder(); 2236 2237 if (beforeList != null) 2238 { 2239 buffer.append(beforeList); 2240 } 2241 2242 final Iterator<String> iterator = l.iterator(); 2243 while (iterator.hasNext()) 2244 { 2245 if (beforeElement != null) 2246 { 2247 buffer.append(beforeElement); 2248 } 2249 2250 buffer.append(iterator.next()); 2251 2252 if (afterElement != null) 2253 { 2254 buffer.append(afterElement); 2255 } 2256 2257 if ((betweenElements != null) && iterator.hasNext()) 2258 { 2259 buffer.append(betweenElements); 2260 } 2261 } 2262 2263 if (afterList != null) 2264 { 2265 buffer.append(afterList); 2266 } 2267 2268 return buffer.toString(); 2269 } 2270 2271 2272 2273 /** 2274 * Converts a duration in seconds to a string with a human-readable duration 2275 * which may include days, hours, minutes, and seconds, to the extent that 2276 * they are needed. 2277 * 2278 * @param s The number of seconds to be represented. 2279 * 2280 * @return A string containing a human-readable representation of the 2281 * provided time. 2282 */ 2283 public static String secondsToHumanReadableDuration(final long s) 2284 { 2285 return millisToHumanReadableDuration(s * 1000L); 2286 } 2287 2288 2289 2290 /** 2291 * Converts a duration in seconds to a string with a human-readable duration 2292 * which may include days, hours, minutes, and seconds, to the extent that 2293 * they are needed. 2294 * 2295 * @param m The number of milliseconds to be represented. 2296 * 2297 * @return A string containing a human-readable representation of the 2298 * provided time. 2299 */ 2300 public static String millisToHumanReadableDuration(final long m) 2301 { 2302 final StringBuilder buffer = new StringBuilder(); 2303 long numMillis = m; 2304 2305 final long numDays = numMillis / 86_400_000L; 2306 if (numDays > 0) 2307 { 2308 numMillis -= (numDays * 86_400_000L); 2309 if (numDays == 1) 2310 { 2311 buffer.append(INFO_NUM_DAYS_SINGULAR.get(numDays)); 2312 } 2313 else 2314 { 2315 buffer.append(INFO_NUM_DAYS_PLURAL.get(numDays)); 2316 } 2317 } 2318 2319 final long numHours = numMillis / 3_600_000L; 2320 if (numHours > 0) 2321 { 2322 numMillis -= (numHours * 3_600_000L); 2323 if (buffer.length() > 0) 2324 { 2325 buffer.append(", "); 2326 } 2327 2328 if (numHours == 1) 2329 { 2330 buffer.append(INFO_NUM_HOURS_SINGULAR.get(numHours)); 2331 } 2332 else 2333 { 2334 buffer.append(INFO_NUM_HOURS_PLURAL.get(numHours)); 2335 } 2336 } 2337 2338 final long numMinutes = numMillis / 60_000L; 2339 if (numMinutes > 0) 2340 { 2341 numMillis -= (numMinutes * 60_000L); 2342 if (buffer.length() > 0) 2343 { 2344 buffer.append(", "); 2345 } 2346 2347 if (numMinutes == 1) 2348 { 2349 buffer.append(INFO_NUM_MINUTES_SINGULAR.get(numMinutes)); 2350 } 2351 else 2352 { 2353 buffer.append(INFO_NUM_MINUTES_PLURAL.get(numMinutes)); 2354 } 2355 } 2356 2357 if (numMillis == 1000) 2358 { 2359 if (buffer.length() > 0) 2360 { 2361 buffer.append(", "); 2362 } 2363 2364 buffer.append(INFO_NUM_SECONDS_SINGULAR.get(1)); 2365 } 2366 else if ((numMillis > 0) || (buffer.length() == 0)) 2367 { 2368 if (buffer.length() > 0) 2369 { 2370 buffer.append(", "); 2371 } 2372 2373 final long numSeconds = numMillis / 1000L; 2374 numMillis -= (numSeconds * 1000L); 2375 if ((numMillis % 1000L) != 0L) 2376 { 2377 final double numSecondsDouble = numSeconds + (numMillis / 1000.0); 2378 final DecimalFormat decimalFormat = new DecimalFormat("0.000"); 2379 buffer.append(INFO_NUM_SECONDS_WITH_DECIMAL.get( 2380 decimalFormat.format(numSecondsDouble))); 2381 } 2382 else 2383 { 2384 buffer.append(INFO_NUM_SECONDS_PLURAL.get(numSeconds)); 2385 } 2386 } 2387 2388 return buffer.toString(); 2389 } 2390 2391 2392 2393 /** 2394 * Converts the provided number of nanoseconds to milliseconds. 2395 * 2396 * @param nanos The number of nanoseconds to convert to milliseconds. 2397 * 2398 * @return The number of milliseconds that most closely corresponds to the 2399 * specified number of nanoseconds. 2400 */ 2401 public static long nanosToMillis(final long nanos) 2402 { 2403 return Math.max(0L, Math.round(nanos / 1_000_000.0d)); 2404 } 2405 2406 2407 2408 /** 2409 * Converts the provided number of milliseconds to nanoseconds. 2410 * 2411 * @param millis The number of milliseconds to convert to nanoseconds. 2412 * 2413 * @return The number of nanoseconds that most closely corresponds to the 2414 * specified number of milliseconds. 2415 */ 2416 public static long millisToNanos(final long millis) 2417 { 2418 return Math.max(0L, (millis * 1_000_000L)); 2419 } 2420 2421 2422 2423 /** 2424 * Indicates whether the provided string is a valid numeric OID. A numeric 2425 * OID must start and end with a digit, must have at least on period, must 2426 * contain only digits and periods, and must not have two consecutive periods. 2427 * 2428 * @param s The string to examine. It must not be {@code null}. 2429 * 2430 * @return {@code true} if the provided string is a valid numeric OID, or 2431 * {@code false} if not. 2432 */ 2433 public static boolean isNumericOID(final String s) 2434 { 2435 boolean digitRequired = true; 2436 boolean periodFound = false; 2437 for (final char c : s.toCharArray()) 2438 { 2439 switch (c) 2440 { 2441 case '0': 2442 case '1': 2443 case '2': 2444 case '3': 2445 case '4': 2446 case '5': 2447 case '6': 2448 case '7': 2449 case '8': 2450 case '9': 2451 digitRequired = false; 2452 break; 2453 2454 case '.': 2455 if (digitRequired) 2456 { 2457 return false; 2458 } 2459 else 2460 { 2461 digitRequired = true; 2462 } 2463 periodFound = true; 2464 break; 2465 2466 default: 2467 return false; 2468 } 2469 2470 } 2471 2472 return (periodFound && (! digitRequired)); 2473 } 2474 2475 2476 2477 /** 2478 * Capitalizes the provided string. The first character will be converted to 2479 * uppercase, and the rest of the string will be left unaltered. 2480 * 2481 * @param s The string to be capitalized. 2482 * 2483 * @return A capitalized version of the provided string. 2484 */ 2485 public static String capitalize(final String s) 2486 { 2487 return capitalize(s, false); 2488 } 2489 2490 2491 2492 /** 2493 * Capitalizes the provided string. The first character of the string (or 2494 * optionally the first character of each word in the string) 2495 * 2496 * @param s The string to be capitalized. 2497 * @param allWords Indicates whether to capitalize all words in the string, 2498 * or only the first word. 2499 * 2500 * @return A capitalized version of the provided string. 2501 */ 2502 public static String capitalize(final String s, final boolean allWords) 2503 { 2504 if (s == null) 2505 { 2506 return null; 2507 } 2508 2509 switch (s.length()) 2510 { 2511 case 0: 2512 return s; 2513 2514 case 1: 2515 return s.toUpperCase(); 2516 2517 default: 2518 boolean capitalize = true; 2519 final char[] chars = s.toCharArray(); 2520 final StringBuilder buffer = new StringBuilder(chars.length); 2521 for (final char c : chars) 2522 { 2523 // Whitespace and punctuation will be considered word breaks. 2524 if (Character.isWhitespace(c) || 2525 (((c >= '!') && (c <= '.')) || 2526 ((c >= ':') && (c <= '@')) || 2527 ((c >= '[') && (c <= '`')) || 2528 ((c >= '{') && (c <= '~')))) 2529 { 2530 buffer.append(c); 2531 capitalize |= allWords; 2532 } 2533 else if (capitalize) 2534 { 2535 buffer.append(Character.toUpperCase(c)); 2536 capitalize = false; 2537 } 2538 else 2539 { 2540 buffer.append(c); 2541 } 2542 } 2543 return buffer.toString(); 2544 } 2545 } 2546 2547 2548 2549 /** 2550 * Encodes the provided UUID to a byte array containing its 128-bit 2551 * representation. 2552 * 2553 * @param uuid The UUID to be encoded. It must not be {@code null}. 2554 * 2555 * @return The byte array containing the 128-bit encoded UUID. 2556 */ 2557 public static byte[] encodeUUID(final UUID uuid) 2558 { 2559 final byte[] b = new byte[16]; 2560 2561 final long mostSignificantBits = uuid.getMostSignificantBits(); 2562 b[0] = (byte) ((mostSignificantBits >> 56) & 0xFF); 2563 b[1] = (byte) ((mostSignificantBits >> 48) & 0xFF); 2564 b[2] = (byte) ((mostSignificantBits >> 40) & 0xFF); 2565 b[3] = (byte) ((mostSignificantBits >> 32) & 0xFF); 2566 b[4] = (byte) ((mostSignificantBits >> 24) & 0xFF); 2567 b[5] = (byte) ((mostSignificantBits >> 16) & 0xFF); 2568 b[6] = (byte) ((mostSignificantBits >> 8) & 0xFF); 2569 b[7] = (byte) (mostSignificantBits & 0xFF); 2570 2571 final long leastSignificantBits = uuid.getLeastSignificantBits(); 2572 b[8] = (byte) ((leastSignificantBits >> 56) & 0xFF); 2573 b[9] = (byte) ((leastSignificantBits >> 48) & 0xFF); 2574 b[10] = (byte) ((leastSignificantBits >> 40) & 0xFF); 2575 b[11] = (byte) ((leastSignificantBits >> 32) & 0xFF); 2576 b[12] = (byte) ((leastSignificantBits >> 24) & 0xFF); 2577 b[13] = (byte) ((leastSignificantBits >> 16) & 0xFF); 2578 b[14] = (byte) ((leastSignificantBits >> 8) & 0xFF); 2579 b[15] = (byte) (leastSignificantBits & 0xFF); 2580 2581 return b; 2582 } 2583 2584 2585 2586 /** 2587 * Decodes the value of the provided byte array as a Java UUID. 2588 * 2589 * @param b The byte array to be decoded as a UUID. It must not be 2590 * {@code null}. 2591 * 2592 * @return The decoded UUID. 2593 * 2594 * @throws ParseException If the provided byte array cannot be parsed as a 2595 * UUID. 2596 */ 2597 public static UUID decodeUUID(final byte[] b) 2598 throws ParseException 2599 { 2600 if (b.length != 16) 2601 { 2602 throw new ParseException(ERR_DECODE_UUID_INVALID_LENGTH.get(toHex(b)), 0); 2603 } 2604 2605 long mostSignificantBits = 0L; 2606 for (int i=0; i < 8; i++) 2607 { 2608 mostSignificantBits = (mostSignificantBits << 8) | (b[i] & 0xFF); 2609 } 2610 2611 long leastSignificantBits = 0L; 2612 for (int i=8; i < 16; i++) 2613 { 2614 leastSignificantBits = (leastSignificantBits << 8) | (b[i] & 0xFF); 2615 } 2616 2617 return new UUID(mostSignificantBits, leastSignificantBits); 2618 } 2619 2620 2621 2622 /** 2623 * Returns {@code true} if and only if the current process is running on 2624 * a Windows-based operating system. 2625 * 2626 * @return {@code true} if the current process is running on a Windows-based 2627 * operating system and {@code false} otherwise. 2628 */ 2629 public static boolean isWindows() 2630 { 2631 final String osName = toLowerCase(System.getProperty("os.name")); 2632 return ((osName != null) && osName.contains("windows")); 2633 } 2634 2635 2636 2637 /** 2638 * Attempts to parse the contents of the provided string to an argument list 2639 * (e.g., converts something like "--arg1 arg1value --arg2 --arg3 arg3value" 2640 * to a list of "--arg1", "arg1value", "--arg2", "--arg3", "arg3value"). 2641 * 2642 * @param s The string to be converted to an argument list. 2643 * 2644 * @return The parsed argument list. 2645 * 2646 * @throws ParseException If a problem is encountered while attempting to 2647 * parse the given string to an argument list. 2648 */ 2649 public static List<String> toArgumentList(final String s) 2650 throws ParseException 2651 { 2652 if ((s == null) || s.isEmpty()) 2653 { 2654 return Collections.emptyList(); 2655 } 2656 2657 int quoteStartPos = -1; 2658 boolean inEscape = false; 2659 final ArrayList<String> argList = new ArrayList<>(20); 2660 final StringBuilder currentArg = new StringBuilder(); 2661 for (int i=0; i < s.length(); i++) 2662 { 2663 final char c = s.charAt(i); 2664 if (inEscape) 2665 { 2666 currentArg.append(c); 2667 inEscape = false; 2668 continue; 2669 } 2670 2671 if (c == '\\') 2672 { 2673 inEscape = true; 2674 } 2675 else if (c == '"') 2676 { 2677 if (quoteStartPos >= 0) 2678 { 2679 quoteStartPos = -1; 2680 } 2681 else 2682 { 2683 quoteStartPos = i; 2684 } 2685 } 2686 else if (c == ' ') 2687 { 2688 if (quoteStartPos >= 0) 2689 { 2690 currentArg.append(c); 2691 } 2692 else if (currentArg.length() > 0) 2693 { 2694 argList.add(currentArg.toString()); 2695 currentArg.setLength(0); 2696 } 2697 } 2698 else 2699 { 2700 currentArg.append(c); 2701 } 2702 } 2703 2704 if (s.endsWith("\\") && (! s.endsWith("\\\\"))) 2705 { 2706 throw new ParseException(ERR_ARG_STRING_DANGLING_BACKSLASH.get(), 2707 (s.length() - 1)); 2708 } 2709 2710 if (quoteStartPos >= 0) 2711 { 2712 throw new ParseException(ERR_ARG_STRING_UNMATCHED_QUOTE.get( 2713 quoteStartPos), quoteStartPos); 2714 } 2715 2716 if (currentArg.length() > 0) 2717 { 2718 argList.add(currentArg.toString()); 2719 } 2720 2721 return Collections.unmodifiableList(argList); 2722 } 2723 2724 2725 2726 /** 2727 * Creates a modifiable list with all of the items of the provided array in 2728 * the same order. This method behaves much like {@code Arrays.asList}, 2729 * except that if the provided array is {@code null}, then it will return a 2730 * {@code null} list rather than throwing an exception. 2731 * 2732 * @param <T> The type of item contained in the provided array. 2733 * 2734 * @param array The array of items to include in the list. 2735 * 2736 * @return The list that was created, or {@code null} if the provided array 2737 * was {@code null}. 2738 */ 2739 public static <T> List<T> toList(final T[] array) 2740 { 2741 if (array == null) 2742 { 2743 return null; 2744 } 2745 2746 final ArrayList<T> l = new ArrayList<>(array.length); 2747 l.addAll(Arrays.asList(array)); 2748 return l; 2749 } 2750 2751 2752 2753 /** 2754 * Creates a modifiable list with all of the items of the provided array in 2755 * the same order. This method behaves much like {@code Arrays.asList}, 2756 * except that if the provided array is {@code null}, then it will return an 2757 * empty list rather than throwing an exception. 2758 * 2759 * @param <T> The type of item contained in the provided array. 2760 * 2761 * @param array The array of items to include in the list. 2762 * 2763 * @return The list that was created, or an empty list if the provided array 2764 * was {@code null}. 2765 */ 2766 public static <T> List<T> toNonNullList(final T[] array) 2767 { 2768 if (array == null) 2769 { 2770 return new ArrayList<>(0); 2771 } 2772 2773 final ArrayList<T> l = new ArrayList<>(array.length); 2774 l.addAll(Arrays.asList(array)); 2775 return l; 2776 } 2777 2778 2779 2780 /** 2781 * Indicates whether both of the provided objects are {@code null} or both 2782 * are logically equal (using the {@code equals} method). 2783 * 2784 * @param o1 The first object for which to make the determination. 2785 * @param o2 The second object for which to make the determination. 2786 * 2787 * @return {@code true} if both objects are {@code null} or both are 2788 * logically equal, or {@code false} if only one of the objects is 2789 * {@code null} or they are not logically equal. 2790 */ 2791 public static boolean bothNullOrEqual(final Object o1, final Object o2) 2792 { 2793 if (o1 == null) 2794 { 2795 return (o2 == null); 2796 } 2797 else if (o2 == null) 2798 { 2799 return false; 2800 } 2801 2802 return o1.equals(o2); 2803 } 2804 2805 2806 2807 /** 2808 * Indicates whether both of the provided strings are {@code null} or both 2809 * are logically equal ignoring differences in capitalization (using the 2810 * {@code equalsIgnoreCase} method). 2811 * 2812 * @param s1 The first string for which to make the determination. 2813 * @param s2 The second string for which to make the determination. 2814 * 2815 * @return {@code true} if both strings are {@code null} or both are 2816 * logically equal ignoring differences in capitalization, or 2817 * {@code false} if only one of the objects is {@code null} or they 2818 * are not logically equal ignoring capitalization. 2819 */ 2820 public static boolean bothNullOrEqualIgnoreCase(final String s1, 2821 final String s2) 2822 { 2823 if (s1 == null) 2824 { 2825 return (s2 == null); 2826 } 2827 else if (s2 == null) 2828 { 2829 return false; 2830 } 2831 2832 return s1.equalsIgnoreCase(s2); 2833 } 2834 2835 2836 2837 /** 2838 * Indicates whether the provided string arrays have the same elements, 2839 * ignoring the order in which they appear and differences in capitalization. 2840 * It is assumed that neither array contains {@code null} strings, and that 2841 * no string appears more than once in each array. 2842 * 2843 * @param a1 The first array for which to make the determination. 2844 * @param a2 The second array for which to make the determination. 2845 * 2846 * @return {@code true} if both arrays have the same set of strings, or 2847 * {@code false} if not. 2848 */ 2849 public static boolean stringsEqualIgnoreCaseOrderIndependent( 2850 final String[] a1, final String[] a2) 2851 { 2852 if (a1 == null) 2853 { 2854 return (a2 == null); 2855 } 2856 else if (a2 == null) 2857 { 2858 return false; 2859 } 2860 2861 if (a1.length != a2.length) 2862 { 2863 return false; 2864 } 2865 2866 if (a1.length == 1) 2867 { 2868 return (a1[0].equalsIgnoreCase(a2[0])); 2869 } 2870 2871 final HashSet<String> s1 = new HashSet<>(a1.length); 2872 for (final String s : a1) 2873 { 2874 s1.add(toLowerCase(s)); 2875 } 2876 2877 final HashSet<String> s2 = new HashSet<>(a2.length); 2878 for (final String s : a2) 2879 { 2880 s2.add(toLowerCase(s)); 2881 } 2882 2883 return s1.equals(s2); 2884 } 2885 2886 2887 2888 /** 2889 * Indicates whether the provided arrays have the same elements, ignoring the 2890 * order in which they appear. It is assumed that neither array contains 2891 * {@code null} elements, and that no element appears more than once in each 2892 * array. 2893 * 2894 * @param <T> The type of element contained in the arrays. 2895 * 2896 * @param a1 The first array for which to make the determination. 2897 * @param a2 The second array for which to make the determination. 2898 * 2899 * @return {@code true} if both arrays have the same set of elements, or 2900 * {@code false} if not. 2901 */ 2902 public static <T> boolean arraysEqualOrderIndependent(final T[] a1, 2903 final T[] a2) 2904 { 2905 if (a1 == null) 2906 { 2907 return (a2 == null); 2908 } 2909 else if (a2 == null) 2910 { 2911 return false; 2912 } 2913 2914 if (a1.length != a2.length) 2915 { 2916 return false; 2917 } 2918 2919 if (a1.length == 1) 2920 { 2921 return (a1[0].equals(a2[0])); 2922 } 2923 2924 final HashSet<T> s1 = new HashSet<>(Arrays.asList(a1)); 2925 final HashSet<T> s2 = new HashSet<>(Arrays.asList(a2)); 2926 return s1.equals(s2); 2927 } 2928 2929 2930 2931 /** 2932 * Determines the number of bytes in a UTF-8 character that starts with the 2933 * given byte. 2934 * 2935 * @param b The byte for which to make the determination. 2936 * 2937 * @return The number of bytes in a UTF-8 character that starts with the 2938 * given byte, or -1 if it does not appear to be a valid first byte 2939 * for a UTF-8 character. 2940 */ 2941 public static int numBytesInUTF8CharacterWithFirstByte(final byte b) 2942 { 2943 if ((b & 0x7F) == b) 2944 { 2945 return 1; 2946 } 2947 else if ((b & 0xE0) == 0xC0) 2948 { 2949 return 2; 2950 } 2951 else if ((b & 0xF0) == 0xE0) 2952 { 2953 return 3; 2954 } 2955 else if ((b & 0xF8) == 0xF0) 2956 { 2957 return 4; 2958 } 2959 else 2960 { 2961 return -1; 2962 } 2963 } 2964 2965 2966 2967 /** 2968 * Indicates whether the provided attribute name should be considered a 2969 * sensitive attribute for the purposes of {@code toCode} methods. If an 2970 * attribute is considered sensitive, then its values will be redacted in the 2971 * output of the {@code toCode} methods. 2972 * 2973 * @param name The name for which to make the determination. It may or may 2974 * not include attribute options. It must not be {@code null}. 2975 * 2976 * @return {@code true} if the specified attribute is one that should be 2977 * considered sensitive for the 2978 */ 2979 public static boolean isSensitiveToCodeAttribute(final String name) 2980 { 2981 final String lowerBaseName = Attribute.getBaseName(name).toLowerCase(); 2982 return TO_CODE_SENSITIVE_ATTRIBUTE_NAMES.contains(lowerBaseName); 2983 } 2984 2985 2986 2987 /** 2988 * Retrieves a set containing the base names (in all lowercase characters) of 2989 * any attributes that should be considered sensitive for the purposes of the 2990 * {@code toCode} methods. By default, only the userPassword and 2991 * authPassword attributes and their respective OIDs will be included. 2992 * 2993 * @return A set containing the base names (in all lowercase characters) of 2994 * any attributes that should be considered sensitive for the 2995 * purposes of the {@code toCode} methods. 2996 */ 2997 public static Set<String> getSensitiveToCodeAttributeBaseNames() 2998 { 2999 return TO_CODE_SENSITIVE_ATTRIBUTE_NAMES; 3000 } 3001 3002 3003 3004 /** 3005 * Specifies the names of any attributes that should be considered sensitive 3006 * for the purposes of the {@code toCode} methods. 3007 * 3008 * @param names The names of any attributes that should be considered 3009 * sensitive for the purposes of the {@code toCode} methods. 3010 * It may be {@code null} or empty if no attributes should be 3011 * considered sensitive. 3012 */ 3013 public static void setSensitiveToCodeAttributes(final String... names) 3014 { 3015 setSensitiveToCodeAttributes(toList(names)); 3016 } 3017 3018 3019 3020 /** 3021 * Specifies the names of any attributes that should be considered sensitive 3022 * for the purposes of the {@code toCode} methods. 3023 * 3024 * @param names The names of any attributes that should be considered 3025 * sensitive for the purposes of the {@code toCode} methods. 3026 * It may be {@code null} or empty if no attributes should be 3027 * considered sensitive. 3028 */ 3029 public static void setSensitiveToCodeAttributes( 3030 final Collection<String> names) 3031 { 3032 if ((names == null) || names.isEmpty()) 3033 { 3034 TO_CODE_SENSITIVE_ATTRIBUTE_NAMES = Collections.emptySet(); 3035 } 3036 else 3037 { 3038 final LinkedHashSet<String> nameSet = new LinkedHashSet<>(names.size()); 3039 for (final String s : names) 3040 { 3041 nameSet.add(Attribute.getBaseName(s).toLowerCase()); 3042 } 3043 3044 TO_CODE_SENSITIVE_ATTRIBUTE_NAMES = Collections.unmodifiableSet(nameSet); 3045 } 3046 } 3047 3048 3049 3050 /** 3051 * Creates a new {@code IOException} with a cause. The constructor needed to 3052 * do this wasn't available until Java SE 6, so reflection is used to invoke 3053 * this constructor in versions of Java that provide it. In Java SE 5, the 3054 * provided message will be augmented with information about the cause. 3055 * 3056 * @param message The message to use for the exception. This may be 3057 * {@code null} if the message should be generated from the 3058 * provided cause. 3059 * @param cause The underlying cause for the exception. It may be 3060 * {@code null} if the exception should have only a message. 3061 * 3062 * @return The {@code IOException} object that was created. 3063 */ 3064 public static IOException createIOExceptionWithCause(final String message, 3065 final Throwable cause) 3066 { 3067 if (cause == null) 3068 { 3069 return new IOException(message); 3070 } 3071 else if (message == null) 3072 { 3073 return new IOException(cause); 3074 } 3075 else 3076 { 3077 return new IOException(message, cause); 3078 } 3079 } 3080 3081 3082 3083 /** 3084 * Converts the provided string (which may include line breaks) into a list 3085 * containing the lines without the line breaks. 3086 * 3087 * @param s The string to convert into a list of its representative lines. 3088 * 3089 * @return A list containing the lines that comprise the given string. 3090 */ 3091 public static List<String> stringToLines(final String s) 3092 { 3093 final ArrayList<String> l = new ArrayList<>(10); 3094 3095 if (s == null) 3096 { 3097 return l; 3098 } 3099 3100 final BufferedReader reader = new BufferedReader(new StringReader(s)); 3101 3102 try 3103 { 3104 while (true) 3105 { 3106 try 3107 { 3108 final String line = reader.readLine(); 3109 if (line == null) 3110 { 3111 return l; 3112 } 3113 else 3114 { 3115 l.add(line); 3116 } 3117 } 3118 catch (final Exception e) 3119 { 3120 Debug.debugException(e); 3121 3122 // This should never happen. If it does, just return a list 3123 // containing a single item that is the original string. 3124 l.clear(); 3125 l.add(s); 3126 return l; 3127 } 3128 } 3129 } 3130 finally 3131 { 3132 try 3133 { 3134 // This is technically not necessary in this case, but it's good form. 3135 reader.close(); 3136 } 3137 catch (final Exception e) 3138 { 3139 Debug.debugException(e); 3140 // This should never happen, and there's nothing we need to do even if 3141 // it does. 3142 } 3143 } 3144 } 3145 3146 3147 3148 /** 3149 * Constructs a {@code File} object from the provided path. 3150 * 3151 * @param baseDirectory The base directory to use as the starting point. 3152 * It must not be {@code null} and is expected to 3153 * represent a directory. 3154 * @param pathElements An array of the elements that make up the remainder 3155 * of the path to the specified file, in order from 3156 * paths closest to the root of the filesystem to 3157 * furthest away (that is, the first element should 3158 * represent a file or directory immediately below the 3159 * base directory, the second is one level below that, 3160 * and so on). It may be {@code null} or empty if the 3161 * base directory should be used. 3162 * 3163 * @return The constructed {@code File} object. 3164 */ 3165 public static File constructPath(final File baseDirectory, 3166 final String... pathElements) 3167 { 3168 Validator.ensureNotNull(baseDirectory); 3169 3170 File f = baseDirectory; 3171 if (pathElements != null) 3172 { 3173 for (final String pathElement : pathElements) 3174 { 3175 f = new File(f, pathElement); 3176 } 3177 } 3178 3179 return f; 3180 } 3181 3182 3183 3184 /** 3185 * Creates a byte array from the provided integer values. All of the integer 3186 * values must be between 0x00 and 0xFF (0 and 255), inclusive. Any bits 3187 * set outside of that range will be ignored. 3188 * 3189 * @param bytes The values to include in the byte array. 3190 * 3191 * @return A byte array with the provided set of values. 3192 */ 3193 public static byte[] byteArray(final int... bytes) 3194 { 3195 if ((bytes == null) || (bytes.length == 0)) 3196 { 3197 return NO_BYTES; 3198 } 3199 3200 final byte[] byteArray = new byte[bytes.length]; 3201 for (int i=0; i < bytes.length; i++) 3202 { 3203 byteArray[i] = (byte) (bytes[i] & 0xFF); 3204 } 3205 3206 return byteArray; 3207 } 3208 3209 3210 3211 /** 3212 * Indicates whether the unit tests are currently running in this JVM. 3213 * 3214 * @return {@code true} if the unit tests are currently running, or 3215 * {@code false} if not. 3216 */ 3217 public static boolean isWithinUnitTest() 3218 { 3219 return IS_WITHIN_UNIT_TESTS; 3220 } 3221}