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