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