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