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