001/* 002 * Copyright 2007-2022 Ping Identity Corporation 003 * All Rights Reserved. 004 */ 005/* 006 * Copyright 2007-2022 Ping Identity Corporation 007 * 008 * Licensed under the Apache License, Version 2.0 (the "License"); 009 * you may not use this file except in compliance with the License. 010 * You may obtain a copy of the License at 011 * 012 * http://www.apache.org/licenses/LICENSE-2.0 013 * 014 * Unless required by applicable law or agreed to in writing, software 015 * distributed under the License is distributed on an "AS IS" BASIS, 016 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 017 * See the License for the specific language governing permissions and 018 * limitations under the License. 019 */ 020/* 021 * Copyright (C) 2007-2022 Ping Identity Corporation 022 * 023 * This program is free software; you can redistribute it and/or modify 024 * it under the terms of the GNU General Public License (GPLv2 only) 025 * or the terms of the GNU Lesser General Public License (LGPLv2.1 only) 026 * as published by the Free Software Foundation. 027 * 028 * This program is distributed in the hope that it will be useful, 029 * but WITHOUT ANY WARRANTY; without even the implied warranty of 030 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 031 * GNU General Public License for more details. 032 * 033 * You should have received a copy of the GNU General Public License 034 * along with this program; if not, see <http://www.gnu.org/licenses>. 035 */ 036package com.unboundid.util; 037 038 039 040import java.io.BufferedReader; 041import java.io.File; 042import java.io.FileOutputStream; 043import java.io.FileReader; 044import java.io.IOException; 045import java.io.PrintWriter; 046import java.io.StringReader; 047import java.lang.reflect.Array; 048import java.net.Inet4Address; 049import java.net.Inet6Address; 050import java.net.InetAddress; 051import java.net.NetworkInterface; 052import java.nio.charset.StandardCharsets; 053import java.text.DecimalFormat; 054import java.text.ParseException; 055import java.text.SimpleDateFormat; 056import java.util.ArrayList; 057import java.util.Arrays; 058import java.util.Collection; 059import java.util.Collections; 060import java.util.Date; 061import java.util.Enumeration; 062import java.util.GregorianCalendar; 063import java.util.HashSet; 064import java.util.Iterator; 065import java.util.LinkedHashMap; 066import java.util.LinkedHashSet; 067import java.util.List; 068import java.util.Map; 069import java.util.Properties; 070import java.util.Random; 071import java.util.Set; 072import java.util.StringTokenizer; 073import java.util.TimeZone; 074import java.util.TreeSet; 075import java.util.UUID; 076import java.util.logging.Handler; 077import java.util.logging.Level; 078import java.util.logging.Logger; 079 080import com.unboundid.ldap.sdk.Attribute; 081import com.unboundid.ldap.sdk.Control; 082import com.unboundid.ldap.sdk.LDAPConnectionOptions; 083import com.unboundid.ldap.sdk.NameResolver; 084import com.unboundid.ldap.sdk.Version; 085 086import static com.unboundid.util.UtilityMessages.*; 087 088 089 090/** 091 * This class provides a number of static utility functions. 092 */ 093@ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE) 094public final class StaticUtils 095{ 096 /** 097 * A pre-allocated byte array containing zero bytes. 098 */ 099 @NotNull public static final byte[] NO_BYTES = new byte[0]; 100 101 102 103 /** 104 * A pre-allocated empty character array. 105 */ 106 @NotNull public static final char[] NO_CHARS = new char[0]; 107 108 109 110 /** 111 * A pre-allocated empty control array. 112 */ 113 @NotNull public static final Control[] NO_CONTROLS = new Control[0]; 114 115 116 117 /** 118 * A pre-allocated empty integer array. 119 */ 120 @NotNull public static final int[] NO_INTS = new int[0]; 121 122 123 124 /** 125 * A pre-allocated empty string array. 126 */ 127 @NotNull public static final String[] NO_STRINGS = new String[0]; 128 129 130 131 /** 132 * The end-of-line marker for the platform on which the LDAP SDK is 133 * currently running. 134 */ 135 @NotNull public static final String EOL = 136 getSystemProperty("line.separator", "\n"); 137 138 139 140 /** 141 * The end-of-line marker that consists of a carriage return character 142 * followed by a line feed character, as used on Windows systems. 143 */ 144 @NotNull public static final String EOL_CR_LF = "\r\n"; 145 146 147 148 /** 149 * The end-of-line marker that consists of just the line feed character, as 150 * used on UNIX-based systems. 151 */ 152 @NotNull public static final String EOL_LF = "\n"; 153 154 155 156 /** 157 * A byte array containing the end-of-line marker for the platform on which 158 * the LDAP SDK is currently running. 159 */ 160 @NotNull public static final byte[] EOL_BYTES = getBytes(EOL); 161 162 163 164 /** 165 * A byte array containing the end-of-line marker that consists of a carriage 166 * return character followed by a line feed character, as used on Windows 167 * systems. 168 */ 169 @NotNull public static final byte[] EOL_BYTES_CR_LF = getBytes(EOL_CR_LF); 170 171 172 173 /** 174 * A byte array containing the end-of-line marker that consists of just the 175 * line feed character, as used on UNIX-based systems. 176 */ 177 @NotNull public static final byte[] EOL_BYTES_LF = getBytes(EOL_LF); 178 179 180 181 /** 182 * Indicates whether the unit tests are currently running. 183 */ 184 private static final boolean IS_WITHIN_UNIT_TESTS = 185 Boolean.getBoolean("com.unboundid.ldap.sdk.RunningUnitTests") || 186 Boolean.getBoolean("com.unboundid.directory.server.RunningUnitTests"); 187 188 189 190 /** 191 * The thread-local date formatter used to encode generalized time values. 192 */ 193 @NotNull private static final ThreadLocal<SimpleDateFormat> 194 GENERALIZED_TIME_FORMATTERS = new ThreadLocal<>(); 195 196 197 198 /** 199 * The thread-local date formatter used to encode RFC 3339 time values. 200 */ 201 @NotNull private static final ThreadLocal<SimpleDateFormat> 202 RFC_3339_TIME_FORMATTERS = new ThreadLocal<>(); 203 204 205 206 /** 207 * The {@code TimeZone} object that represents the UTC (universal coordinated 208 * time) time zone. 209 */ 210 @NotNull private static final TimeZone UTC_TIME_ZONE = 211 TimeZone.getTimeZone("UTC"); 212 213 214 215 /** 216 * A set containing the names of attributes that will be considered sensitive 217 * by the {@code toCode} methods of various request and data structure types. 218 */ 219 @NotNull private static volatile Set<String> 220 TO_CODE_SENSITIVE_ATTRIBUTE_NAMES = setOf("userpassword", "2.5.4.35", 221 "authpassword", "1.3.6.1.4.1.4203.1.3.4"); 222 223 224 225 /** 226 * The width of the terminal window, in columns. 227 */ 228 public static final int TERMINAL_WIDTH_COLUMNS; 229 static 230 { 231 // Try to dynamically determine the size of the terminal window using the 232 // COLUMNS environment variable. 233 int terminalWidth = 80; 234 final String columnsEnvVar = getEnvironmentVariable("COLUMNS"); 235 if (columnsEnvVar != null) 236 { 237 try 238 { 239 terminalWidth = Integer.parseInt(columnsEnvVar); 240 } 241 catch (final Exception e) 242 { 243 Debug.debugException(e); 244 } 245 } 246 247 TERMINAL_WIDTH_COLUMNS = terminalWidth; 248 } 249 250 251 252 /** 253 * An array containing the set of lowercase ASCII letters. 254 */ 255 @NotNull private static final char[] LOWERCASE_LETTERS = 256 "abcdefghijklmnopqrstuvwxyz".toCharArray(); 257 258 259 260 /** 261 * An array containing the set of ASCII numeric digits. 262 */ 263 @NotNull private static final char[] NUMERIC_DIGITS = 264 "0123456789".toCharArray(); 265 266 267 268 /** 269 * An array containing the set of ASCII alphanumeric characters. It will 270 * include both uppercase and lowercase letters. 271 */ 272 @NotNull private static final char[] ALPHANUMERIC_CHARACTERS = 273 ("abcdefghijklmnopqrstuvwxyz" + 274 "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + 275 "0123456789").toCharArray(); 276 277 278 279 /** 280 * Prevent this class from being instantiated. 281 */ 282 private StaticUtils() 283 { 284 // No implementation is required. 285 } 286 287 288 289 /** 290 * Retrieves the set of currently defined system properties. If possible, 291 * this will simply return the result of a call to 292 * {@code System.getProperties}. However, the LDAP SDK is known to be used in 293 * environments where a security manager prevents setting system properties, 294 * and in that case, calls to {@code System.getProperties} will be rejected 295 * with a {@code SecurityException} because the returned structure is mutable 296 * and could be used to alter system property values. In such cases, a new 297 * empty {@code Properties} object will be created, and may optionally be 298 * populated with the values of a specific set of named properties. 299 * 300 * @param propertyNames An optional set of property names whose values (if 301 * defined) should be included in the 302 * {@code Properties} object that will be returned if a 303 * security manager prevents retrieving the full set of 304 * system properties. This may be {@code null} or 305 * empty if no specific properties should be retrieved. 306 * 307 * @return The value returned by a call to {@code System.getProperties} if 308 * possible, or a newly-created properties map (possibly including 309 * the values of a specified set of system properties) if it is not 310 * possible to get a mutable set of the system properties. 311 */ 312 @NotNull() 313 public static Properties getSystemProperties( 314 @Nullable final String... propertyNames) 315 { 316 try 317 { 318 final Properties properties = System.getProperties(); 319 320 final String forceThrowPropertyName = 321 StaticUtils.class.getName() + ".forceGetSystemPropertiesToThrow"; 322 323 // To ensure that we can get coverage for the code below in which there is 324 // a restrictive security manager in place, look for a system property 325 // that will cause us to throw an exception. 326 final Object forceThrowPropertyValue = 327 properties.getProperty(forceThrowPropertyName); 328 if (forceThrowPropertyValue != null) 329 { 330 throw new SecurityException(forceThrowPropertyName + '=' + 331 forceThrowPropertyValue); 332 } 333 334 return properties; 335 } 336 catch (final SecurityException e) 337 { 338 Debug.debugException(e); 339 } 340 341 342 // If we have gotten here, then we can assume that a security manager 343 // prevents us from accessing all system properties. Create a new proper 344 final Properties properties = new Properties(); 345 if (propertyNames != null) 346 { 347 for (final String propertyName : propertyNames) 348 { 349 final Object propertyValue = System.getProperty(propertyName); 350 if (propertyValue != null) 351 { 352 properties.put(propertyName, propertyValue); 353 } 354 } 355 } 356 357 return properties; 358 } 359 360 361 362 /** 363 * Retrieves the value of the specified system property. 364 * 365 * @param name The name of the system property for which to retrieve the 366 * value. 367 * 368 * @return The value of the requested system property, or {@code null} if 369 * that variable was not set or its value could not be retrieved 370 * (for example, because a security manager prevents it). 371 */ 372 @Nullable() 373 public static String getSystemProperty(@NotNull final String name) 374 { 375 try 376 { 377 return System.getProperty(name); 378 } 379 catch (final Throwable t) 380 { 381 // It is possible that the call to System.getProperty could fail under 382 // some security managers. In that case, simply swallow the error and 383 // act as if that system property is not set. 384 Debug.debugException(t); 385 return null; 386 } 387 } 388 389 390 391 /** 392 * Retrieves the value of the specified system property. 393 * 394 * @param name The name of the system property for which to retrieve 395 * the value. 396 * @param defaultValue The default value to return if the specified 397 * system property is not set or could not be 398 * retrieved. 399 * 400 * @return The value of the requested system property, or the provided 401 * default value if that system property was not set or its value 402 * could not be retrieved (for example, because a security manager 403 * prevents it). 404 */ 405 @Nullable() 406 public static String getSystemProperty(@NotNull final String name, 407 @Nullable final String defaultValue) 408 { 409 try 410 { 411 return System.getProperty(name, defaultValue); 412 } 413 catch (final Throwable t) 414 { 415 // It is possible that the call to System.getProperty could fail under 416 // some security managers. In that case, simply swallow the error and 417 // act as if that system property is not set. 418 Debug.debugException(t); 419 return defaultValue; 420 } 421 } 422 423 424 425 /** 426 * Attempts to set the value of the specified system property. Note that this 427 * may not be permitted by some security managers, in which case the attempt 428 * will have no effect. 429 * 430 * @param name The name of the System property to set. It must not be 431 * {@code null}. 432 * @param value The value to use for the system property. If it is 433 * {@code null}, then the property will be cleared. 434 * 435 * @return The former value of the system property, or {@code null} if it 436 * did not have a value or if it could not be set (for example, 437 * because a security manager prevents it). 438 */ 439 @Nullable() 440 public static String setSystemProperty(@NotNull final String name, 441 @Nullable final String value) 442 { 443 try 444 { 445 if (value == null) 446 { 447 return System.clearProperty(name); 448 } 449 else 450 { 451 return System.setProperty(name, value); 452 } 453 } 454 catch (final Throwable t) 455 { 456 // It is possible that the call to System.setProperty or 457 // System.clearProperty could fail under some security managers. In that 458 // case, simply swallow the error and act as if that system property is 459 // not set. 460 Debug.debugException(t); 461 return null; 462 } 463 } 464 465 466 467 /** 468 * Attempts to clear the value of the specified system property. Note that 469 * this may not be permitted by some security managers, in which case the 470 * attempt will have no effect. 471 * 472 * @param name The name of the System property to clear. It must not be 473 * {@code null}. 474 * 475 * @return The former value of the system property, or {@code null} if it 476 * did not have a value or if it could not be set (for example, 477 * because a security manager prevents it). 478 */ 479 @Nullable() 480 public static String clearSystemProperty(@NotNull final String name) 481 { 482 try 483 { 484 return System.clearProperty(name); 485 } 486 catch (final Throwable t) 487 { 488 // It is possible that the call to System.clearProperty could fail under 489 // some security managers. In that case, simply swallow the error and 490 // act as if that system property is not set. 491 Debug.debugException(t); 492 return null; 493 } 494 } 495 496 497 498 /** 499 * Retrieves a map of all environment variables defined in the JVM's process. 500 * 501 * @return A map of all environment variables defined in the JVM's process, 502 * or an empty map if no environment variables are set or the actual 503 * set could not be retrieved (for example, because a security 504 * manager prevents it). 505 */ 506 @NotNull() 507 public static Map<String,String> getEnvironmentVariables() 508 { 509 try 510 { 511 return System.getenv(); 512 } 513 catch (final Throwable t) 514 { 515 // It is possible that the call to System.getenv could fail under some 516 // security managers. In that case, simply swallow the error and pretend 517 // that the environment variable is not set. 518 Debug.debugException(t); 519 return Collections.emptyMap(); 520 } 521 } 522 523 524 525 /** 526 * Retrieves the value of the specified environment variable. 527 * 528 * @param name The name of the environment variable for which to retrieve 529 * the value. 530 * 531 * @return The value of the requested environment variable, or {@code null} 532 * if that variable was not set or its value could not be retrieved 533 * (for example, because a security manager prevents it). 534 */ 535 @Nullable() 536 public static String getEnvironmentVariable(@NotNull final String name) 537 { 538 try 539 { 540 return System.getenv(name); 541 } 542 catch (final Throwable t) 543 { 544 // It is possible that the call to System.getenv could fail under some 545 // security managers. In that case, simply swallow the error and pretend 546 // that the environment variable is not set. 547 Debug.debugException(t); 548 return null; 549 } 550 } 551 552 553 554 /** 555 * Retrieves the value of the specified environment variable. 556 * 557 * @param name The name of the environment variable for which to 558 * retrieve the value. 559 * @param defaultValue The default value to use if the specified environment 560 * variable is not set. It may be {@code null} if no 561 * default should be used. 562 * 563 * @return The value of the requested environment variable, or {@code null} 564 * if that variable was not set or its value could not be retrieved 565 * (for example, because a security manager prevents it) and there 566 * is no default value. 567 */ 568 @Nullable() 569 public static String getEnvironmentVariable(@NotNull final String name, 570 @Nullable final String defaultValue) 571 { 572 final String value = getEnvironmentVariable(name); 573 if (value == null) 574 { 575 return defaultValue; 576 } 577 else 578 { 579 return value; 580 } 581 } 582 583 584 585 /** 586 * Attempts to set the desired log level for the specified logger. Note that 587 * this may not be permitted by some security managers, in which case the 588 * attempt will have no effect. 589 * 590 * @param logger The logger whose level should be updated. 591 * @param logLevel The log level to set for the logger. 592 */ 593 public static void setLoggerLevel(@NotNull final Logger logger, 594 @NotNull final Level logLevel) 595 { 596 try 597 { 598 logger.setLevel(logLevel); 599 } 600 catch (final Throwable t) 601 { 602 Debug.debugException(t); 603 } 604 } 605 606 607 608 /** 609 * Attempts to set the desired log level for the specified log handler. Note 610 * that this may not be permitted by some security managers, in which case the 611 * attempt will have no effect. 612 * 613 * @param logHandler The log handler whose level should be updated. 614 * @param logLevel The log level to set for the log handler. 615 */ 616 public static void setLogHandlerLevel(@NotNull final Handler logHandler, 617 @NotNull final Level logLevel) 618 { 619 try 620 { 621 logHandler.setLevel(logLevel); 622 } 623 catch (final Throwable t) 624 { 625 Debug.debugException(t); 626 } 627 } 628 629 630 631 /** 632 * Retrieves a UTF-8 byte representation of the provided string. 633 * 634 * @param s The string for which to retrieve the UTF-8 byte representation. 635 * 636 * @return The UTF-8 byte representation for the provided string. 637 */ 638 @NotNull() 639 public static byte[] getBytes(@Nullable final String s) 640 { 641 final int length; 642 if ((s == null) || ((length = s.length()) == 0)) 643 { 644 return NO_BYTES; 645 } 646 647 final byte[] b = new byte[length]; 648 for (int i=0; i < length; i++) 649 { 650 final char c = s.charAt(i); 651 if (c <= 0x7F) 652 { 653 b[i] = (byte) (c & 0x7F); 654 } 655 else 656 { 657 return s.getBytes(StandardCharsets.UTF_8); 658 } 659 } 660 661 return b; 662 } 663 664 665 666 /** 667 * Retrieves a byte array containing the UTF-8 representation of the bytes 668 * that comprise the provided Unicode code point. 669 * 670 * @param codePoint The code point for which to retrieve the UTF-8 bytes. 671 * 672 * @return A byte array containing the UTF-8 representation of the bytes that 673 * comprise the provided Unicode code point. 674 */ 675 @NotNull() 676 public static byte[] getBytesForCodePoint(final int codePoint) 677 { 678 if (codePoint <= 0x7F) 679 { 680 return new byte[] { (byte) codePoint }; 681 } 682 else 683 { 684 final String codePointString = new String(new int[] { codePoint }, 0, 1); 685 return codePointString.getBytes(StandardCharsets.UTF_8); 686 } 687 } 688 689 690 691 /** 692 * Indicates whether the contents of the provided byte array represent an 693 * ASCII string, which is also known in LDAP terminology as an IA5 string. 694 * An ASCII string is one that contains only bytes in which the most 695 * significant bit is zero. 696 * 697 * @param b The byte array for which to make the determination. It must 698 * not be {@code null}. 699 * 700 * @return {@code true} if the contents of the provided array represent an 701 * ASCII string, or {@code false} if not. 702 */ 703 public static boolean isASCIIString(@NotNull final byte[] b) 704 { 705 for (final byte by : b) 706 { 707 if ((by & 0x80) == 0x80) 708 { 709 return false; 710 } 711 } 712 713 return true; 714 } 715 716 717 718 /** 719 * Indicates whether the contents of the provided string represent an ASCII 720 * string, which is also known in LDAP terminology as an IA5 string. An ASCII 721 * string is one that contains only bytes in which the most significant bit is 722 * zero. 723 * 724 * @param s The string for which to make the determination. It must not be 725 * {@code null}. 726 * 727 * @return {@code true} if the contents of the provided string represent an 728 * ASCII string, or {@code false} if not. 729 */ 730 public static boolean isASCIIString(@NotNull final String s) 731 { 732 return isASCIIString(getBytes(s)); 733 } 734 735 736 737 /** 738 * Indicates whether the provided character is a printable ASCII character, as 739 * per RFC 4517 section 3.2. The only printable characters are: 740 * <UL> 741 * <LI>All uppercase and lowercase ASCII alphabetic letters</LI> 742 * <LI>All ASCII numeric digits</LI> 743 * <LI>The following additional ASCII characters: single quote, left 744 * parenthesis, right parenthesis, plus, comma, hyphen, period, equals, 745 * forward slash, colon, question mark, space.</LI> 746 * </UL> 747 * 748 * @param c The character for which to make the determination. 749 * 750 * @return {@code true} if the provided character is a printable ASCII 751 * character, or {@code false} if not. 752 */ 753 public static boolean isPrintable(final char c) 754 { 755 if (((c >= 'a') && (c <= 'z')) || 756 ((c >= 'A') && (c <= 'Z')) || 757 ((c >= '0') && (c <= '9'))) 758 { 759 return true; 760 } 761 762 switch (c) 763 { 764 case '\'': 765 case '(': 766 case ')': 767 case '+': 768 case ',': 769 case '-': 770 case '.': 771 case '=': 772 case '/': 773 case ':': 774 case '?': 775 case ' ': 776 return true; 777 default: 778 return false; 779 } 780 } 781 782 783 784 /** 785 * Indicates whether the contents of the provided byte array represent a 786 * printable LDAP string, as per RFC 4517 section 3.2. The only characters 787 * allowed in a printable string are: 788 * <UL> 789 * <LI>All uppercase and lowercase ASCII alphabetic letters</LI> 790 * <LI>All ASCII numeric digits</LI> 791 * <LI>The following additional ASCII characters: single quote, left 792 * parenthesis, right parenthesis, plus, comma, hyphen, period, equals, 793 * forward slash, colon, question mark, space.</LI> 794 * </UL> 795 * If the provided array contains anything other than the above characters 796 * (i.e., if the byte array contains any non-ASCII characters, or any ASCII 797 * control characters, or if it contains excluded ASCII characters like 798 * the exclamation point, double quote, octothorpe, dollar sign, etc.), then 799 * it will not be considered printable. 800 * 801 * @param b The byte array for which to make the determination. It must 802 * not be {@code null}. 803 * 804 * @return {@code true} if the contents of the provided byte array represent 805 * a printable LDAP string, or {@code false} if not. 806 */ 807 public static boolean isPrintableString(@NotNull final byte[] b) 808 { 809 for (final byte by : b) 810 { 811 if ((by & 0x80) == 0x80) 812 { 813 return false; 814 } 815 816 if (((by >= 'a') && (by <= 'z')) || 817 ((by >= 'A') && (by <= 'Z')) || 818 ((by >= '0') && (by <= '9'))) 819 { 820 continue; 821 } 822 823 switch (by) 824 { 825 case '\'': 826 case '(': 827 case ')': 828 case '+': 829 case ',': 830 case '-': 831 case '.': 832 case '=': 833 case '/': 834 case ':': 835 case '?': 836 case ' ': 837 continue; 838 default: 839 return false; 840 } 841 } 842 843 return true; 844 } 845 846 847 848 /** 849 * Indicates whether the provided string represents a printable LDAP string, 850 * as per RFC 4517 section 3.2. The only characters allowed in a printable 851 * string are: 852 * <UL> 853 * <LI>All uppercase and lowercase ASCII alphabetic letters</LI> 854 * <LI>All ASCII numeric digits</LI> 855 * <LI>The following additional ASCII characters: single quote, left 856 * parenthesis, right parenthesis, plus, comma, hyphen, period, equals, 857 * forward slash, colon, question mark, space.</LI> 858 * </UL> 859 * If the provided array contains anything other than the above characters 860 * (i.e., if the byte array contains any non-ASCII characters, or any ASCII 861 * control characters, or if it contains excluded ASCII characters like 862 * the exclamation point, double quote, octothorpe, dollar sign, etc.), then 863 * it will not be considered printable. 864 * 865 * @param s The string for which to make the determination. It must not be 866 * {@code null}. 867 * 868 * @return {@code true} if the provided string represents a printable LDAP 869 * string, or {@code false} if not. 870 */ 871 public static boolean isPrintableString(@NotNull final String s) 872 { 873 final int length = s.length(); 874 for (int i=0; i < length; i++) 875 { 876 final char c = s.charAt(i); 877 if ((c & 0x80) == 0x80) 878 { 879 return false; 880 } 881 882 if (((c >= 'a') && (c <= 'z')) || 883 ((c >= 'A') && (c <= 'Z')) || 884 ((c >= '0') && (c <= '9'))) 885 { 886 continue; 887 } 888 889 switch (c) 890 { 891 case '\'': 892 case '(': 893 case ')': 894 case '+': 895 case ',': 896 case '-': 897 case '.': 898 case '=': 899 case '/': 900 case ':': 901 case '?': 902 case ' ': 903 continue; 904 default: 905 return false; 906 } 907 } 908 909 return true; 910 } 911 912 913 914 /** 915 * Indicates whether the specified Unicode code point represents a character 916 * that is believed to be displayable. Displayable characters include 917 * letters, numbers, spaces, dashes, punctuation, and symbols. 918 * Non-displayable characters include control characters, combining marks, 919 * enclosing marks, directionality indicators, format characters, and 920 * surrogate characters. 921 * 922 * @param codePoint The code point for which to make the determination. 923 * 924 * @return {@code true} if the specified Unicode character is believed to be 925 * displayable, or {@code false} if not. 926 */ 927 public static boolean isLikelyDisplayableCharacter(final int codePoint) 928 { 929 final int charType = Character.getType(codePoint); 930 switch (charType) 931 { 932 case Character.UPPERCASE_LETTER: 933 case Character.LOWERCASE_LETTER: 934 case Character.TITLECASE_LETTER: 935 case Character.MODIFIER_LETTER: 936 case Character.OTHER_LETTER: 937 case Character.DECIMAL_DIGIT_NUMBER: 938 case Character.LETTER_NUMBER: 939 case Character.OTHER_NUMBER: 940 case Character.SPACE_SEPARATOR: 941 case Character.DASH_PUNCTUATION: 942 case Character.START_PUNCTUATION: 943 case Character.END_PUNCTUATION: 944 case Character.CONNECTOR_PUNCTUATION: 945 case Character.OTHER_PUNCTUATION: 946 case Character.INITIAL_QUOTE_PUNCTUATION: 947 case Character.FINAL_QUOTE_PUNCTUATION: 948 case Character.MATH_SYMBOL: 949 case Character.CURRENCY_SYMBOL: 950 case Character.OTHER_SYMBOL: 951 return true; 952 default: 953 return false; 954 } 955 } 956 957 958 959 /** 960 *Indicates whether the provided string is comprised entirely of characters 961 * that are believed to be displayable (as determined by the 962 * {@link #isLikelyDisplayableCharacter} method). 963 * 964 * @param s The string for which to make the determination. It must not e 965 * {@code null}. 966 * 967 * @return {@code true} if the provided string is believed to be displayable, 968 * or {@code false} if not. 969 */ 970 public static boolean isLikelyDisplayableString(@NotNull final String s) 971 { 972 int pos = 0; 973 while (pos < s.length()) 974 { 975 final int codePoint = s.codePointAt(pos); 976 if (! isLikelyDisplayableCharacter(codePoint)) 977 { 978 return false; 979 } 980 981 pos += Character.charCount(codePoint); 982 } 983 984 return true; 985 } 986 987 988 989 /** 990 * Indicates whether the contents of the provided array are valid UTF-8. 991 * 992 * @param b The byte array to examine. It must not be {@code null}. 993 * 994 * @return {@code true} if the byte array can be parsed as a valid UTF-8 995 * string, or {@code false} if not. 996 */ 997 public static boolean isValidUTF8(@NotNull final byte[] b) 998 { 999 int i = 0; 1000 while (i < b.length) 1001 { 1002 final byte currentByte = b[i++]; 1003 1004 // If the most significant bit is not set, then this represents a valid 1005 // single-byte character. 1006 if ((currentByte & 0b1000_0000) == 0b0000_0000) 1007 { 1008 continue; 1009 } 1010 1011 // If the first byte starts with 0b110, then it must be followed by 1012 // another byte that starts with 0b10. 1013 if ((currentByte & 0b1110_0000) == 0b1100_0000) 1014 { 1015 if (! hasExpectedSubsequentUTF8Bytes(b, i, 1)) 1016 { 1017 return false; 1018 } 1019 1020 i++; 1021 continue; 1022 } 1023 1024 // If the first byte starts with 0b1110, then it must be followed by two 1025 // more bytes that start with 0b10. 1026 if ((currentByte & 0b1111_0000) == 0b1110_0000) 1027 { 1028 if (! hasExpectedSubsequentUTF8Bytes(b, i, 2)) 1029 { 1030 return false; 1031 } 1032 1033 i += 2; 1034 continue; 1035 } 1036 1037 // If the first byte starts with 0b11110, then it must be followed by 1038 // three more bytes that start with 0b10. 1039 if ((currentByte & 0b1111_1000) == 0b1111_0000) 1040 { 1041 if (! hasExpectedSubsequentUTF8Bytes(b, i, 3)) 1042 { 1043 return false; 1044 } 1045 1046 i += 3; 1047 continue; 1048 } 1049 1050 // If the first byte starts with 0b111110, then it must be followed by 1051 // four more bytes that start with 0b10. 1052 if ((currentByte & 0b1111_1100) == 0b1111_1000) 1053 { 1054 if (! hasExpectedSubsequentUTF8Bytes(b, i, 4)) 1055 { 1056 return false; 1057 } 1058 1059 i += 4; 1060 continue; 1061 } 1062 1063 // If the first byte starts with 0b1111110, then it must be followed by 1064 // five more bytes that start with 0b10. 1065 if ((currentByte & 0b1111_1110) == 0b1111_1100) 1066 { 1067 if (! hasExpectedSubsequentUTF8Bytes(b, i, 5)) 1068 { 1069 return false; 1070 } 1071 1072 i += 5; 1073 continue; 1074 } 1075 1076 // This is not a valid first byte for a UTF-8 character. 1077 return false; 1078 } 1079 1080 1081 // If we've gotten here, then the provided array represents a valid UTF-8 1082 // string. 1083 return true; 1084 } 1085 1086 1087 1088 /** 1089 * Ensures that the provided array has the expected number of bytes that start 1090 * with 0b10 starting at the specified position in the array. 1091 * 1092 * @param b The byte array to examine. 1093 * @param p The position in the byte array at which to start looking. 1094 * @param n The number of bytes to examine. 1095 * 1096 * @return {@code true} if the provided byte array has the expected number of 1097 * bytes that start with 0b10, or {@code false} if not. 1098 */ 1099 private static boolean hasExpectedSubsequentUTF8Bytes(@NotNull final byte[] b, 1100 final int p, 1101 final int n) 1102 { 1103 if (b.length < (p + n)) 1104 { 1105 return false; 1106 } 1107 1108 for (int i=0; i < n; i++) 1109 { 1110 if ((b[p+i] & 0b1100_0000) != 0b1000_0000) 1111 { 1112 return false; 1113 } 1114 } 1115 1116 return true; 1117 } 1118 1119 1120 1121 /** 1122 * Retrieves a string generated from the provided byte array using the UTF-8 1123 * encoding. 1124 * 1125 * @param b The byte array for which to return the associated string. 1126 * 1127 * @return The string generated from the provided byte array using the UTF-8 1128 * encoding. 1129 */ 1130 @NotNull() 1131 public static String toUTF8String(@NotNull final byte[] b) 1132 { 1133 try 1134 { 1135 return new String(b, StandardCharsets.UTF_8); 1136 } 1137 catch (final Exception e) 1138 { 1139 // This should never happen. 1140 Debug.debugException(e); 1141 return new String(b); 1142 } 1143 } 1144 1145 1146 1147 /** 1148 * Retrieves a string generated from the specified portion of the provided 1149 * byte array using the UTF-8 encoding. 1150 * 1151 * @param b The byte array for which to return the associated string. 1152 * @param offset The offset in the array at which the value begins. 1153 * @param length The number of bytes in the value to convert to a string. 1154 * 1155 * @return The string generated from the specified portion of the provided 1156 * byte array using the UTF-8 encoding. 1157 */ 1158 @NotNull() 1159 public static String toUTF8String(@NotNull final byte[] b, final int offset, 1160 final int length) 1161 { 1162 try 1163 { 1164 return new String(b, offset, length, StandardCharsets.UTF_8); 1165 } 1166 catch (final Exception e) 1167 { 1168 // This should never happen. 1169 Debug.debugException(e); 1170 return new String(b, offset, length); 1171 } 1172 } 1173 1174 1175 1176 /** 1177 * Retrieves a version of the provided string with the first character 1178 * converted to lowercase but all other characters retaining their original 1179 * capitalization. 1180 * 1181 * @param s The string to be processed. 1182 * 1183 * @return A version of the provided string with the first character 1184 * converted to lowercase but all other characters retaining their 1185 * original capitalization. It may be {@code null} if the provided 1186 * string is {@code null}. 1187 */ 1188 @Nullable() 1189 public static String toInitialLowerCase(@Nullable final String s) 1190 { 1191 if ((s == null) || s.isEmpty()) 1192 { 1193 return s; 1194 } 1195 else if (s.length() == 1) 1196 { 1197 return toLowerCase(s); 1198 } 1199 else 1200 { 1201 final char c = s.charAt(0); 1202 if (((c >= 'A') && (c <= 'Z')) || (c < ' ') || (c > '~')) 1203 { 1204 final StringBuilder b = new StringBuilder(s); 1205 b.setCharAt(0, Character.toLowerCase(c)); 1206 return b.toString(); 1207 } 1208 else 1209 { 1210 return s; 1211 } 1212 } 1213 } 1214 1215 1216 1217 /** 1218 * Retrieves an all-lowercase version of the provided string. 1219 * 1220 * @param s The string for which to retrieve the lowercase version. 1221 * 1222 * @return An all-lowercase version of the provided string, or {@code null} 1223 * if the provided string was {@code null}. 1224 */ 1225 @Nullable() 1226 public static String toLowerCase(@Nullable final String s) 1227 { 1228 if (s == null) 1229 { 1230 return null; 1231 } 1232 1233 final int length = s.length(); 1234 final char[] charArray = s.toCharArray(); 1235 for (int i=0; i < length; i++) 1236 { 1237 switch (charArray[i]) 1238 { 1239 case 'A': 1240 charArray[i] = 'a'; 1241 break; 1242 case 'B': 1243 charArray[i] = 'b'; 1244 break; 1245 case 'C': 1246 charArray[i] = 'c'; 1247 break; 1248 case 'D': 1249 charArray[i] = 'd'; 1250 break; 1251 case 'E': 1252 charArray[i] = 'e'; 1253 break; 1254 case 'F': 1255 charArray[i] = 'f'; 1256 break; 1257 case 'G': 1258 charArray[i] = 'g'; 1259 break; 1260 case 'H': 1261 charArray[i] = 'h'; 1262 break; 1263 case 'I': 1264 charArray[i] = 'i'; 1265 break; 1266 case 'J': 1267 charArray[i] = 'j'; 1268 break; 1269 case 'K': 1270 charArray[i] = 'k'; 1271 break; 1272 case 'L': 1273 charArray[i] = 'l'; 1274 break; 1275 case 'M': 1276 charArray[i] = 'm'; 1277 break; 1278 case 'N': 1279 charArray[i] = 'n'; 1280 break; 1281 case 'O': 1282 charArray[i] = 'o'; 1283 break; 1284 case 'P': 1285 charArray[i] = 'p'; 1286 break; 1287 case 'Q': 1288 charArray[i] = 'q'; 1289 break; 1290 case 'R': 1291 charArray[i] = 'r'; 1292 break; 1293 case 'S': 1294 charArray[i] = 's'; 1295 break; 1296 case 'T': 1297 charArray[i] = 't'; 1298 break; 1299 case 'U': 1300 charArray[i] = 'u'; 1301 break; 1302 case 'V': 1303 charArray[i] = 'v'; 1304 break; 1305 case 'W': 1306 charArray[i] = 'w'; 1307 break; 1308 case 'X': 1309 charArray[i] = 'x'; 1310 break; 1311 case 'Y': 1312 charArray[i] = 'y'; 1313 break; 1314 case 'Z': 1315 charArray[i] = 'z'; 1316 break; 1317 default: 1318 if (charArray[i] > 0x7F) 1319 { 1320 return s.toLowerCase(); 1321 } 1322 break; 1323 } 1324 } 1325 1326 return new String(charArray); 1327 } 1328 1329 1330 1331 /** 1332 * Retrieves an all-uppercase version of the provided string. 1333 * 1334 * @param s The string for which to retrieve the uppercase version. 1335 * 1336 * @return An all-uppercase version of the provided string, or {@code null} 1337 * if the provided string was {@code null}. 1338 */ 1339 @Nullable() 1340 public static String toUpperCase(@Nullable final String s) 1341 { 1342 if (s == null) 1343 { 1344 return null; 1345 } 1346 1347 final int length = s.length(); 1348 final char[] charArray = s.toCharArray(); 1349 for (int i=0; i < length; i++) 1350 { 1351 switch (charArray[i]) 1352 { 1353 case 'a': 1354 charArray[i] = 'A'; 1355 break; 1356 case 'b': 1357 charArray[i] = 'B'; 1358 break; 1359 case 'c': 1360 charArray[i] = 'C'; 1361 break; 1362 case 'd': 1363 charArray[i] = 'D'; 1364 break; 1365 case 'e': 1366 charArray[i] = 'E'; 1367 break; 1368 case 'f': 1369 charArray[i] = 'F'; 1370 break; 1371 case 'g': 1372 charArray[i] = 'G'; 1373 break; 1374 case 'h': 1375 charArray[i] = 'H'; 1376 break; 1377 case 'i': 1378 charArray[i] = 'I'; 1379 break; 1380 case 'j': 1381 charArray[i] = 'J'; 1382 break; 1383 case 'k': 1384 charArray[i] = 'K'; 1385 break; 1386 case 'l': 1387 charArray[i] = 'L'; 1388 break; 1389 case 'm': 1390 charArray[i] = 'M'; 1391 break; 1392 case 'n': 1393 charArray[i] = 'N'; 1394 break; 1395 case 'o': 1396 charArray[i] = 'O'; 1397 break; 1398 case 'p': 1399 charArray[i] = 'P'; 1400 break; 1401 case 'q': 1402 charArray[i] = 'Q'; 1403 break; 1404 case 'r': 1405 charArray[i] = 'R'; 1406 break; 1407 case 's': 1408 charArray[i] = 'S'; 1409 break; 1410 case 't': 1411 charArray[i] = 'T'; 1412 break; 1413 case 'u': 1414 charArray[i] = 'U'; 1415 break; 1416 case 'v': 1417 charArray[i] = 'V'; 1418 break; 1419 case 'w': 1420 charArray[i] = 'W'; 1421 break; 1422 case 'x': 1423 charArray[i] = 'X'; 1424 break; 1425 case 'y': 1426 charArray[i] = 'Y'; 1427 break; 1428 case 'z': 1429 charArray[i] = 'Z'; 1430 break; 1431 default: 1432 if (charArray[i] > 0x7F) 1433 { 1434 return s.toUpperCase(); 1435 } 1436 break; 1437 } 1438 } 1439 1440 return new String(charArray); 1441 } 1442 1443 1444 1445 /** 1446 * Indicates whether the provided character is a valid hexadecimal digit. 1447 * 1448 * @param c The character for which to make the determination. 1449 * 1450 * @return {@code true} if the provided character does represent a valid 1451 * hexadecimal digit, or {@code false} if not. 1452 */ 1453 public static boolean isHex(final char c) 1454 { 1455 switch (c) 1456 { 1457 case '0': 1458 case '1': 1459 case '2': 1460 case '3': 1461 case '4': 1462 case '5': 1463 case '6': 1464 case '7': 1465 case '8': 1466 case '9': 1467 case 'a': 1468 case 'A': 1469 case 'b': 1470 case 'B': 1471 case 'c': 1472 case 'C': 1473 case 'd': 1474 case 'D': 1475 case 'e': 1476 case 'E': 1477 case 'f': 1478 case 'F': 1479 return true; 1480 1481 default: 1482 return false; 1483 } 1484 } 1485 1486 1487 1488 /** 1489 * Retrieves a hexadecimal representation of the provided byte. 1490 * 1491 * @param b The byte to encode as hexadecimal. 1492 * 1493 * @return A string containing the hexadecimal representation of the provided 1494 * byte. 1495 */ 1496 @NotNull() 1497 public static String toHex(final byte b) 1498 { 1499 final StringBuilder buffer = new StringBuilder(2); 1500 toHex(b, buffer); 1501 return buffer.toString(); 1502 } 1503 1504 1505 1506 /** 1507 * Appends a hexadecimal representation of the provided byte to the given 1508 * buffer. 1509 * 1510 * @param b The byte to encode as hexadecimal. 1511 * @param buffer The buffer to which the hexadecimal representation is to be 1512 * appended. 1513 */ 1514 public static void toHex(final byte b, @NotNull final StringBuilder buffer) 1515 { 1516 switch (b & 0xF0) 1517 { 1518 case 0x00: 1519 buffer.append('0'); 1520 break; 1521 case 0x10: 1522 buffer.append('1'); 1523 break; 1524 case 0x20: 1525 buffer.append('2'); 1526 break; 1527 case 0x30: 1528 buffer.append('3'); 1529 break; 1530 case 0x40: 1531 buffer.append('4'); 1532 break; 1533 case 0x50: 1534 buffer.append('5'); 1535 break; 1536 case 0x60: 1537 buffer.append('6'); 1538 break; 1539 case 0x70: 1540 buffer.append('7'); 1541 break; 1542 case 0x80: 1543 buffer.append('8'); 1544 break; 1545 case 0x90: 1546 buffer.append('9'); 1547 break; 1548 case 0xA0: 1549 buffer.append('a'); 1550 break; 1551 case 0xB0: 1552 buffer.append('b'); 1553 break; 1554 case 0xC0: 1555 buffer.append('c'); 1556 break; 1557 case 0xD0: 1558 buffer.append('d'); 1559 break; 1560 case 0xE0: 1561 buffer.append('e'); 1562 break; 1563 case 0xF0: 1564 buffer.append('f'); 1565 break; 1566 } 1567 1568 switch (b & 0x0F) 1569 { 1570 case 0x00: 1571 buffer.append('0'); 1572 break; 1573 case 0x01: 1574 buffer.append('1'); 1575 break; 1576 case 0x02: 1577 buffer.append('2'); 1578 break; 1579 case 0x03: 1580 buffer.append('3'); 1581 break; 1582 case 0x04: 1583 buffer.append('4'); 1584 break; 1585 case 0x05: 1586 buffer.append('5'); 1587 break; 1588 case 0x06: 1589 buffer.append('6'); 1590 break; 1591 case 0x07: 1592 buffer.append('7'); 1593 break; 1594 case 0x08: 1595 buffer.append('8'); 1596 break; 1597 case 0x09: 1598 buffer.append('9'); 1599 break; 1600 case 0x0A: 1601 buffer.append('a'); 1602 break; 1603 case 0x0B: 1604 buffer.append('b'); 1605 break; 1606 case 0x0C: 1607 buffer.append('c'); 1608 break; 1609 case 0x0D: 1610 buffer.append('d'); 1611 break; 1612 case 0x0E: 1613 buffer.append('e'); 1614 break; 1615 case 0x0F: 1616 buffer.append('f'); 1617 break; 1618 } 1619 } 1620 1621 1622 1623 /** 1624 * Appends a hexadecimal representation of the provided byte to the given 1625 * buffer. 1626 * 1627 * @param b The byte to encode as hexadecimal. 1628 * @param buffer The buffer to which the hexadecimal representation is to be 1629 * appended. 1630 */ 1631 public static void toHex(final byte b, @NotNull final ByteStringBuffer buffer) 1632 { 1633 switch (b & 0xF0) 1634 { 1635 case 0x00: 1636 buffer.append((byte) '0'); 1637 break; 1638 case 0x10: 1639 buffer.append((byte) '1'); 1640 break; 1641 case 0x20: 1642 buffer.append((byte) '2'); 1643 break; 1644 case 0x30: 1645 buffer.append((byte) '3'); 1646 break; 1647 case 0x40: 1648 buffer.append((byte) '4'); 1649 break; 1650 case 0x50: 1651 buffer.append((byte) '5'); 1652 break; 1653 case 0x60: 1654 buffer.append((byte) '6'); 1655 break; 1656 case 0x70: 1657 buffer.append((byte) '7'); 1658 break; 1659 case 0x80: 1660 buffer.append((byte) '8'); 1661 break; 1662 case 0x90: 1663 buffer.append((byte) '9'); 1664 break; 1665 case 0xA0: 1666 buffer.append((byte) 'a'); 1667 break; 1668 case 0xB0: 1669 buffer.append((byte) 'b'); 1670 break; 1671 case 0xC0: 1672 buffer.append((byte) 'c'); 1673 break; 1674 case 0xD0: 1675 buffer.append((byte) 'd'); 1676 break; 1677 case 0xE0: 1678 buffer.append((byte) 'e'); 1679 break; 1680 case 0xF0: 1681 buffer.append((byte) 'f'); 1682 break; 1683 } 1684 1685 switch (b & 0x0F) 1686 { 1687 case 0x00: 1688 buffer.append((byte) '0'); 1689 break; 1690 case 0x01: 1691 buffer.append((byte) '1'); 1692 break; 1693 case 0x02: 1694 buffer.append((byte) '2'); 1695 break; 1696 case 0x03: 1697 buffer.append((byte) '3'); 1698 break; 1699 case 0x04: 1700 buffer.append((byte) '4'); 1701 break; 1702 case 0x05: 1703 buffer.append((byte) '5'); 1704 break; 1705 case 0x06: 1706 buffer.append((byte) '6'); 1707 break; 1708 case 0x07: 1709 buffer.append((byte) '7'); 1710 break; 1711 case 0x08: 1712 buffer.append((byte) '8'); 1713 break; 1714 case 0x09: 1715 buffer.append((byte) '9'); 1716 break; 1717 case 0x0A: 1718 buffer.append((byte) 'a'); 1719 break; 1720 case 0x0B: 1721 buffer.append((byte) 'b'); 1722 break; 1723 case 0x0C: 1724 buffer.append((byte) 'c'); 1725 break; 1726 case 0x0D: 1727 buffer.append((byte) 'd'); 1728 break; 1729 case 0x0E: 1730 buffer.append((byte) 'e'); 1731 break; 1732 case 0x0F: 1733 buffer.append((byte) 'f'); 1734 break; 1735 } 1736 } 1737 1738 1739 1740 /** 1741 * Retrieves a hexadecimal representation of the contents of the provided byte 1742 * array. No delimiter character will be inserted between the hexadecimal 1743 * digits for each byte. 1744 * 1745 * @param b The byte array to be represented as a hexadecimal string. It 1746 * must not be {@code null}. 1747 * 1748 * @return A string containing a hexadecimal representation of the contents 1749 * of the provided byte array. 1750 */ 1751 @NotNull() 1752 public static String toHex(@NotNull final byte[] b) 1753 { 1754 Validator.ensureNotNull(b); 1755 1756 final StringBuilder buffer = new StringBuilder(2 * b.length); 1757 toHex(b, buffer); 1758 return buffer.toString(); 1759 } 1760 1761 1762 1763 /** 1764 * Retrieves a hexadecimal representation of the contents of the provided byte 1765 * array. No delimiter character will be inserted between the hexadecimal 1766 * digits for each byte. 1767 * 1768 * @param b The byte array to be represented as a hexadecimal string. 1769 * It must not be {@code null}. 1770 * @param buffer A buffer to which the hexadecimal representation of the 1771 * contents of the provided byte array should be appended. 1772 */ 1773 public static void toHex(@NotNull final byte[] b, 1774 @NotNull final StringBuilder buffer) 1775 { 1776 toHex(b, null, buffer); 1777 } 1778 1779 1780 1781 /** 1782 * Retrieves a hexadecimal representation of the contents of the provided byte 1783 * array. No delimiter character will be inserted between the hexadecimal 1784 * digits for each byte. 1785 * 1786 * @param b The byte array to be represented as a hexadecimal 1787 * string. It must not be {@code null}. 1788 * @param delimiter A delimiter to be inserted between bytes. It may be 1789 * {@code null} if no delimiter should be used. 1790 * @param buffer A buffer to which the hexadecimal representation of the 1791 * contents of the provided byte array should be appended. 1792 */ 1793 public static void toHex(@NotNull final byte[] b, 1794 @Nullable final String delimiter, 1795 @NotNull final StringBuilder buffer) 1796 { 1797 boolean first = true; 1798 for (final byte bt : b) 1799 { 1800 if (first) 1801 { 1802 first = false; 1803 } 1804 else if (delimiter != null) 1805 { 1806 buffer.append(delimiter); 1807 } 1808 1809 toHex(bt, buffer); 1810 } 1811 } 1812 1813 1814 1815 /** 1816 * Retrieves a hex-encoded representation of the contents of the provided 1817 * array, along with an ASCII representation of its contents next to it. The 1818 * output will be split across multiple lines, with up to sixteen bytes per 1819 * line. For each of those sixteen bytes, the two-digit hex representation 1820 * will be appended followed by a space. Then, the ASCII representation of 1821 * those sixteen bytes will follow that, with a space used in place of any 1822 * byte that does not have an ASCII representation. 1823 * 1824 * @param array The array whose contents should be processed. 1825 * @param indent The number of spaces to insert on each line prior to the 1826 * first hex byte. 1827 * 1828 * @return A hex-encoded representation of the contents of the provided 1829 * array, along with an ASCII representation of its contents next to 1830 * it. 1831 */ 1832 @NotNull() 1833 public static String toHexPlusASCII(@NotNull final byte[] array, 1834 final int indent) 1835 { 1836 final StringBuilder buffer = new StringBuilder(); 1837 toHexPlusASCII(array, indent, buffer); 1838 return buffer.toString(); 1839 } 1840 1841 1842 1843 /** 1844 * Appends a hex-encoded representation of the contents of the provided array 1845 * to the given buffer, along with an ASCII representation of its contents 1846 * next to it. The output will be split across multiple lines, with up to 1847 * sixteen bytes per line. For each of those sixteen bytes, the two-digit hex 1848 * representation will be appended followed by a space. Then, the ASCII 1849 * representation of those sixteen bytes will follow that, with a space used 1850 * in place of any byte that does not have an ASCII representation. 1851 * 1852 * @param array The array whose contents should be processed. 1853 * @param indent The number of spaces to insert on each line prior to the 1854 * first hex byte. 1855 * @param buffer The buffer to which the encoded data should be appended. 1856 */ 1857 public static void toHexPlusASCII(@Nullable final byte[] array, 1858 final int indent, 1859 @NotNull final StringBuilder buffer) 1860 { 1861 if ((array == null) || (array.length == 0)) 1862 { 1863 return; 1864 } 1865 1866 for (int i=0; i < indent; i++) 1867 { 1868 buffer.append(' '); 1869 } 1870 1871 int pos = 0; 1872 int startPos = 0; 1873 while (pos < array.length) 1874 { 1875 toHex(array[pos++], buffer); 1876 buffer.append(' '); 1877 1878 if ((pos % 16) == 0) 1879 { 1880 buffer.append(" "); 1881 for (int i=startPos; i < pos; i++) 1882 { 1883 if ((array[i] < ' ') || (array[i] > '~')) 1884 { 1885 buffer.append(' '); 1886 } 1887 else 1888 { 1889 buffer.append((char) array[i]); 1890 } 1891 } 1892 buffer.append(EOL); 1893 startPos = pos; 1894 1895 if (pos < array.length) 1896 { 1897 for (int i=0; i < indent; i++) 1898 { 1899 buffer.append(' '); 1900 } 1901 } 1902 } 1903 } 1904 1905 // If the last line isn't complete yet, then finish it off. 1906 if ((array.length % 16) != 0) 1907 { 1908 final int missingBytes = (16 - (array.length % 16)); 1909 for (int i=0; i < missingBytes; i++) 1910 { 1911 buffer.append(" "); 1912 } 1913 buffer.append(" "); 1914 for (int i=startPos; i < array.length; i++) 1915 { 1916 if ((array[i] < ' ') || (array[i] > '~')) 1917 { 1918 buffer.append(' '); 1919 } 1920 else 1921 { 1922 buffer.append((char) array[i]); 1923 } 1924 } 1925 buffer.append(EOL); 1926 } 1927 } 1928 1929 1930 1931 /** 1932 * Retrieves the bytes that correspond to the provided hexadecimal string. 1933 * 1934 * @param hexString The hexadecimal string for which to retrieve the bytes. 1935 * It must not be {@code null}, and there must not be any 1936 * delimiter between bytes. 1937 * 1938 * @return The bytes that correspond to the provided hexadecimal string. 1939 * 1940 * @throws ParseException If the provided string does not represent valid 1941 * hexadecimal data, or if the provided string does 1942 * not contain an even number of characters. 1943 */ 1944 @NotNull() 1945 public static byte[] fromHex(@NotNull final String hexString) 1946 throws ParseException 1947 { 1948 if ((hexString.length() % 2) != 0) 1949 { 1950 throw new ParseException( 1951 ERR_FROM_HEX_ODD_NUMBER_OF_CHARACTERS.get(hexString.length()), 1952 hexString.length()); 1953 } 1954 1955 final byte[] decodedBytes = new byte[hexString.length() / 2]; 1956 for (int i=0, j=0; i < decodedBytes.length; i++, j+= 2) 1957 { 1958 switch (hexString.charAt(j)) 1959 { 1960 case '0': 1961 // No action is required. 1962 break; 1963 case '1': 1964 decodedBytes[i] = 0x10; 1965 break; 1966 case '2': 1967 decodedBytes[i] = 0x20; 1968 break; 1969 case '3': 1970 decodedBytes[i] = 0x30; 1971 break; 1972 case '4': 1973 decodedBytes[i] = 0x40; 1974 break; 1975 case '5': 1976 decodedBytes[i] = 0x50; 1977 break; 1978 case '6': 1979 decodedBytes[i] = 0x60; 1980 break; 1981 case '7': 1982 decodedBytes[i] = 0x70; 1983 break; 1984 case '8': 1985 decodedBytes[i] = (byte) 0x80; 1986 break; 1987 case '9': 1988 decodedBytes[i] = (byte) 0x90; 1989 break; 1990 case 'a': 1991 case 'A': 1992 decodedBytes[i] = (byte) 0xA0; 1993 break; 1994 case 'b': 1995 case 'B': 1996 decodedBytes[i] = (byte) 0xB0; 1997 break; 1998 case 'c': 1999 case 'C': 2000 decodedBytes[i] = (byte) 0xC0; 2001 break; 2002 case 'd': 2003 case 'D': 2004 decodedBytes[i] = (byte) 0xD0; 2005 break; 2006 case 'e': 2007 case 'E': 2008 decodedBytes[i] = (byte) 0xE0; 2009 break; 2010 case 'f': 2011 case 'F': 2012 decodedBytes[i] = (byte) 0xF0; 2013 break; 2014 default: 2015 throw new ParseException(ERR_FROM_HEX_NON_HEX_CHARACTER.get(j), j); 2016 } 2017 2018 switch (hexString.charAt(j+1)) 2019 { 2020 case '0': 2021 // No action is required. 2022 break; 2023 case '1': 2024 decodedBytes[i] |= 0x01; 2025 break; 2026 case '2': 2027 decodedBytes[i] |= 0x02; 2028 break; 2029 case '3': 2030 decodedBytes[i] |= 0x03; 2031 break; 2032 case '4': 2033 decodedBytes[i] |= 0x04; 2034 break; 2035 case '5': 2036 decodedBytes[i] |= 0x05; 2037 break; 2038 case '6': 2039 decodedBytes[i] |= 0x06; 2040 break; 2041 case '7': 2042 decodedBytes[i] |= 0x07; 2043 break; 2044 case '8': 2045 decodedBytes[i] |= 0x08; 2046 break; 2047 case '9': 2048 decodedBytes[i] |= 0x09; 2049 break; 2050 case 'a': 2051 case 'A': 2052 decodedBytes[i] |= 0x0A; 2053 break; 2054 case 'b': 2055 case 'B': 2056 decodedBytes[i] |= 0x0B; 2057 break; 2058 case 'c': 2059 case 'C': 2060 decodedBytes[i] |= 0x0C; 2061 break; 2062 case 'd': 2063 case 'D': 2064 decodedBytes[i] |= 0x0D; 2065 break; 2066 case 'e': 2067 case 'E': 2068 decodedBytes[i] |= 0x0E; 2069 break; 2070 case 'f': 2071 case 'F': 2072 decodedBytes[i] |= 0x0F; 2073 break; 2074 default: 2075 throw new ParseException(ERR_FROM_HEX_NON_HEX_CHARACTER.get(j+1), 2076 j+1); 2077 } 2078 } 2079 2080 return decodedBytes; 2081 } 2082 2083 2084 2085 /** 2086 * Appends a hex-encoded representation of the provided character to the given 2087 * buffer. Each byte of the hex-encoded representation will be prefixed with 2088 * a backslash. 2089 * 2090 * @param c The character to be encoded. 2091 * @param buffer The buffer to which the hex-encoded representation should 2092 * be appended. 2093 */ 2094 public static void hexEncode(final char c, 2095 @NotNull final StringBuilder buffer) 2096 { 2097 final byte[] charBytes; 2098 if (c <= 0x7F) 2099 { 2100 charBytes = new byte[] { (byte) (c & 0x7F) }; 2101 } 2102 else 2103 { 2104 charBytes = getBytes(String.valueOf(c)); 2105 } 2106 2107 for (final byte b : charBytes) 2108 { 2109 buffer.append('\\'); 2110 toHex(b, buffer); 2111 } 2112 } 2113 2114 2115 2116 /** 2117 * Appends a hex-encoded representation of the provided code point to the 2118 * given buffer. Each byte of the hex-encoded representation will be prefixed 2119 * with a backslash. 2120 * 2121 * @param codePoint The code point to be encoded. 2122 * @param buffer The buffer to which the hex-encoded representation 2123 * should be appended. 2124 */ 2125 public static void hexEncode(final int codePoint, 2126 @NotNull final StringBuilder buffer) 2127 { 2128 final byte[] charBytes = 2129 getBytes(new String(new int[] { codePoint }, 0, 1)); 2130 2131 for (final byte b : charBytes) 2132 { 2133 buffer.append('\\'); 2134 toHex(b, buffer); 2135 } 2136 } 2137 2138 2139 2140 /** 2141 * Appends the Java code that may be used to create the provided byte 2142 * array to the given buffer. 2143 * 2144 * @param array The byte array containing the data to represent. It must 2145 * not be {@code null}. 2146 * @param buffer The buffer to which the code should be appended. 2147 */ 2148 public static void byteArrayToCode(@NotNull final byte[] array, 2149 @NotNull final StringBuilder buffer) 2150 { 2151 buffer.append("new byte[] {"); 2152 for (int i=0; i < array.length; i++) 2153 { 2154 if (i > 0) 2155 { 2156 buffer.append(','); 2157 } 2158 2159 buffer.append(" (byte) 0x"); 2160 toHex(array[i], buffer); 2161 } 2162 buffer.append(" }"); 2163 } 2164 2165 2166 2167 /** 2168 * Retrieves a single-line string representation of the stack trace for the 2169 * provided {@code Throwable}. It will include the unqualified name of the 2170 * {@code Throwable} class, a list of source files and line numbers (if 2171 * available) for the stack trace, and will also include the stack trace for 2172 * the cause (if present). 2173 * 2174 * @param t The {@code Throwable} for which to retrieve the stack trace. 2175 * 2176 * @return A single-line string representation of the stack trace for the 2177 * provided {@code Throwable}. 2178 */ 2179 @NotNull() 2180 public static String getStackTrace(@NotNull final Throwable t) 2181 { 2182 final StringBuilder buffer = new StringBuilder(); 2183 getStackTrace(t, buffer); 2184 return buffer.toString(); 2185 } 2186 2187 2188 2189 /** 2190 * Appends a single-line string representation of the stack trace for the 2191 * provided {@code Throwable} to the given buffer. It will include the 2192 * unqualified name of the {@code Throwable} class, a list of source files and 2193 * line numbers (if available) for the stack trace, and will also include the 2194 * stack trace for the cause (if present). 2195 * 2196 * @param t The {@code Throwable} for which to retrieve the stack 2197 * trace. 2198 * @param buffer The buffer to which the information should be appended. 2199 */ 2200 public static void getStackTrace(@NotNull final Throwable t, 2201 @NotNull final StringBuilder buffer) 2202 { 2203 buffer.append(getUnqualifiedClassName(t.getClass())); 2204 buffer.append('('); 2205 2206 final String message = t.getMessage(); 2207 if (message != null) 2208 { 2209 buffer.append("message='"); 2210 buffer.append(message); 2211 buffer.append("', "); 2212 } 2213 2214 buffer.append("trace='"); 2215 getStackTrace(t.getStackTrace(), buffer); 2216 buffer.append('\''); 2217 2218 final Throwable cause = t.getCause(); 2219 if (cause != null) 2220 { 2221 buffer.append(", cause="); 2222 getStackTrace(cause, buffer); 2223 } 2224 2225 final String ldapSDKVersionString = ", ldapSDKVersion=" + 2226 Version.NUMERIC_VERSION_STRING + ", revision=" + Version.REVISION_ID; 2227 if (buffer.indexOf(ldapSDKVersionString) < 0) 2228 { 2229 buffer.append(ldapSDKVersionString); 2230 } 2231 2232 buffer.append(')'); 2233 } 2234 2235 2236 2237 /** 2238 * Returns a single-line string representation of the stack trace. It will 2239 * include a list of source files and line numbers (if available) for the 2240 * stack trace. 2241 * 2242 * @param elements The stack trace. 2243 * 2244 * @return A single-line string representation of the stack trace. 2245 */ 2246 @NotNull() 2247 public static String getStackTrace( 2248 @NotNull final StackTraceElement[] elements) 2249 { 2250 final StringBuilder buffer = new StringBuilder(); 2251 getStackTrace(elements, buffer); 2252 return buffer.toString(); 2253 } 2254 2255 2256 2257 /** 2258 * Appends a single-line string representation of the stack trace to the given 2259 * buffer. It will include a list of source files and line numbers 2260 * (if available) for the stack trace. 2261 * 2262 * @param elements The stack trace. 2263 * @param buffer The buffer to which the information should be appended. 2264 */ 2265 public static void getStackTrace(@NotNull final StackTraceElement[] elements, 2266 @NotNull final StringBuilder buffer) 2267 { 2268 getStackTrace(elements, buffer, -1); 2269 } 2270 2271 2272 2273 /** 2274 * Appends a single-line string representation of the stack trace to the given 2275 * buffer. It will include a list of source files and line numbers 2276 * (if available) for the stack trace. 2277 * 2278 * @param elements The stack trace. 2279 * @param buffer The buffer to which the information should be 2280 * appended. 2281 * @param maxPreSDKFrames The maximum number of stack trace frames to 2282 * include from code invoked before calling into the 2283 * LDAP SDK. A value of zero indicates that only 2284 * stack trace frames from the LDAP SDK itself (or 2285 * things that it calls) will be included. A 2286 * negative value indicates that 2287 */ 2288 public static void getStackTrace(@NotNull final StackTraceElement[] elements, 2289 @NotNull final StringBuilder buffer, 2290 final int maxPreSDKFrames) 2291 { 2292 boolean sdkElementFound = false; 2293 int numPreSDKElementsFound = 0; 2294 for (int i=0; i < elements.length; i++) 2295 { 2296 if (i > 0) 2297 { 2298 buffer.append(" / "); 2299 } 2300 2301 if (elements[i].getClassName().startsWith("com.unboundid.")) 2302 { 2303 sdkElementFound = true; 2304 } 2305 else if (sdkElementFound) 2306 { 2307 if ((maxPreSDKFrames >= 0) && 2308 (numPreSDKElementsFound >= maxPreSDKFrames)) 2309 { 2310 buffer.append("..."); 2311 return; 2312 } 2313 2314 numPreSDKElementsFound++; 2315 } 2316 2317 buffer.append(elements[i].getMethodName()); 2318 buffer.append('('); 2319 buffer.append(elements[i].getFileName()); 2320 2321 final int lineNumber = elements[i].getLineNumber(); 2322 if (lineNumber > 0) 2323 { 2324 buffer.append(':'); 2325 buffer.append(lineNumber); 2326 } 2327 else if (elements[i].isNativeMethod()) 2328 { 2329 buffer.append(":native"); 2330 } 2331 else 2332 { 2333 buffer.append(":unknown"); 2334 } 2335 buffer.append(')'); 2336 } 2337 } 2338 2339 2340 2341 /** 2342 * Retrieves a string representation of the provided {@code Throwable} object 2343 * suitable for use in a message. For runtime exceptions and errors, then a 2344 * full stack trace for the exception will be provided. For exception types 2345 * defined in the LDAP SDK, then its {@code getExceptionMessage} method will 2346 * be used to get the string representation. For all other types of 2347 * exceptions, then the standard string representation will be used. 2348 * <BR><BR> 2349 * For all types of exceptions, the message will also include the cause if one 2350 * exists. 2351 * 2352 * @param t The {@code Throwable} for which to generate the exception 2353 * message. 2354 * 2355 * @return A string representation of the provided {@code Throwable} object 2356 * suitable for use in a message. 2357 */ 2358 @NotNull() 2359 public static String getExceptionMessage(@NotNull final Throwable t) 2360 { 2361 final boolean includeCause = 2362 Boolean.getBoolean(Debug.PROPERTY_INCLUDE_CAUSE_IN_EXCEPTION_MESSAGES); 2363 final boolean includeStackTrace = Boolean.getBoolean( 2364 Debug.PROPERTY_INCLUDE_STACK_TRACE_IN_EXCEPTION_MESSAGES); 2365 2366 return getExceptionMessage(t, includeCause, includeStackTrace); 2367 } 2368 2369 2370 2371 /** 2372 * Retrieves a string representation of the provided {@code Throwable} object 2373 * suitable for use in a message. For runtime exceptions and errors, then a 2374 * full stack trace for the exception will be provided. For exception types 2375 * defined in the LDAP SDK, then its {@code getExceptionMessage} method will 2376 * be used to get the string representation. For all other types of 2377 * exceptions, then the standard string representation will be used. 2378 * <BR><BR> 2379 * For all types of exceptions, the message will also include the cause if one 2380 * exists. 2381 * 2382 * @param t The {@code Throwable} for which to generate the 2383 * exception message. 2384 * @param includeCause Indicates whether to include information about 2385 * the cause (if any) in the exception message. 2386 * @param includeStackTrace Indicates whether to include a condensed 2387 * representation of the stack trace in the 2388 * exception message. 2389 * 2390 * @return A string representation of the provided {@code Throwable} object 2391 * suitable for use in a message. 2392 */ 2393 @NotNull() 2394 public static String getExceptionMessage(@Nullable final Throwable t, 2395 final boolean includeCause, 2396 final boolean includeStackTrace) 2397 { 2398 if (t == null) 2399 { 2400 return ERR_NO_EXCEPTION.get(); 2401 } 2402 2403 final StringBuilder buffer = new StringBuilder(); 2404 if (t instanceof LDAPSDKException) 2405 { 2406 buffer.append(((LDAPSDKException) t).getExceptionMessage()); 2407 } 2408 else if (t instanceof LDAPSDKRuntimeException) 2409 { 2410 buffer.append(((LDAPSDKRuntimeException) t).getExceptionMessage()); 2411 } 2412 else if (t instanceof NullPointerException) 2413 { 2414 // For NullPointerExceptions, we'll always print at least a portion of 2415 // the stack trace that includes all of the LDAP SDK code, and up to 2416 // three frames of whatever called into the SDK. 2417 buffer.append("NullPointerException("); 2418 getStackTrace(t.getStackTrace(), buffer, 3); 2419 buffer.append(')'); 2420 } 2421 else if ((t.getMessage() == null) || t.getMessage().isEmpty() || 2422 t.getMessage().equalsIgnoreCase("null")) 2423 { 2424 getStackTrace(t, buffer); 2425 } 2426 else 2427 { 2428 buffer.append(t.getClass().getSimpleName()); 2429 buffer.append('('); 2430 buffer.append(t.getMessage()); 2431 buffer.append(')'); 2432 2433 if (includeStackTrace) 2434 { 2435 buffer.append(" trace="); 2436 getStackTrace(t, buffer); 2437 } 2438 else if (includeCause) 2439 { 2440 final Throwable cause = t.getCause(); 2441 if (cause != null) 2442 { 2443 buffer.append(" caused by "); 2444 buffer.append(getExceptionMessage(cause)); 2445 } 2446 } 2447 } 2448 2449 final String ldapSDKVersionString = ", ldapSDKVersion=" + 2450 Version.NUMERIC_VERSION_STRING + ", revision=" + Version.REVISION_ID; 2451 if (buffer.indexOf(ldapSDKVersionString) < 0) 2452 { 2453 buffer.append(ldapSDKVersionString); 2454 } 2455 2456 return buffer.toString(); 2457 } 2458 2459 2460 2461 /** 2462 * Retrieves the unqualified name (i.e., the name without package information) 2463 * for the provided class. 2464 * 2465 * @param c The class for which to retrieve the unqualified name. 2466 * 2467 * @return The unqualified name for the provided class. 2468 */ 2469 @NotNull() 2470 public static String getUnqualifiedClassName(@NotNull final Class<?> c) 2471 { 2472 final String className = c.getName(); 2473 final int lastPeriodPos = className.lastIndexOf('.'); 2474 2475 if (lastPeriodPos > 0) 2476 { 2477 return className.substring(lastPeriodPos+1); 2478 } 2479 else 2480 { 2481 return className; 2482 } 2483 } 2484 2485 2486 2487 /** 2488 * Retrieves a {@code TimeZone} object that represents the UTC (universal 2489 * coordinated time) time zone. 2490 * 2491 * @return A {@code TimeZone} object that represents the UTC time zone. 2492 */ 2493 @NotNull() 2494 public static TimeZone getUTCTimeZone() 2495 { 2496 return UTC_TIME_ZONE; 2497 } 2498 2499 2500 2501 /** 2502 * Encodes the provided timestamp in generalized time format. 2503 * 2504 * @param timestamp The timestamp to be encoded in generalized time format. 2505 * It should use the same format as the 2506 * {@code System.currentTimeMillis()} method (i.e., the 2507 * number of milliseconds since 12:00am UTC on January 1, 2508 * 1970). 2509 * 2510 * @return The generalized time representation of the provided date. 2511 */ 2512 @NotNull() 2513 public static String encodeGeneralizedTime(final long timestamp) 2514 { 2515 return encodeGeneralizedTime(new Date(timestamp)); 2516 } 2517 2518 2519 2520 /** 2521 * Encodes the provided date in generalized time format. 2522 * 2523 * @param d The date to be encoded in generalized time format. 2524 * 2525 * @return The generalized time representation of the provided date. 2526 */ 2527 @NotNull() 2528 public static String encodeGeneralizedTime(@NotNull final Date d) 2529 { 2530 SimpleDateFormat dateFormat = GENERALIZED_TIME_FORMATTERS.get(); 2531 if (dateFormat == null) 2532 { 2533 dateFormat = new SimpleDateFormat("yyyyMMddHHmmss.SSS'Z'"); 2534 dateFormat.setTimeZone(UTC_TIME_ZONE); 2535 GENERALIZED_TIME_FORMATTERS.set(dateFormat); 2536 } 2537 2538 return dateFormat.format(d); 2539 } 2540 2541 2542 2543 /** 2544 * Decodes the provided string as a timestamp in generalized time format. 2545 * 2546 * @param t The timestamp to be decoded. It must not be {@code null}. 2547 * 2548 * @return The {@code Date} object decoded from the provided timestamp. 2549 * 2550 * @throws ParseException If the provided string could not be decoded as a 2551 * timestamp in generalized time format. 2552 */ 2553 @NotNull() 2554 public static Date decodeGeneralizedTime(@NotNull final String t) 2555 throws ParseException 2556 { 2557 Validator.ensureNotNull(t); 2558 2559 // Extract the time zone information from the end of the value. 2560 int tzPos; 2561 final TimeZone tz; 2562 if (t.endsWith("Z")) 2563 { 2564 tz = TimeZone.getTimeZone("UTC"); 2565 tzPos = t.length() - 1; 2566 } 2567 else 2568 { 2569 tzPos = t.lastIndexOf('-'); 2570 if (tzPos < 0) 2571 { 2572 tzPos = t.lastIndexOf('+'); 2573 if (tzPos < 0) 2574 { 2575 throw new ParseException(ERR_GENTIME_DECODE_CANNOT_PARSE_TZ.get(t), 2576 0); 2577 } 2578 } 2579 2580 tz = TimeZone.getTimeZone("GMT" + t.substring(tzPos)); 2581 if (tz.getRawOffset() == 0) 2582 { 2583 // This is the default time zone that will be returned if the value 2584 // cannot be parsed. If it's valid, then it will end in "+0000" or 2585 // "-0000". Otherwise, it's invalid and GMT was just a fallback. 2586 if (! (t.endsWith("+0000") || t.endsWith("-0000"))) 2587 { 2588 throw new ParseException(ERR_GENTIME_DECODE_CANNOT_PARSE_TZ.get(t), 2589 tzPos); 2590 } 2591 } 2592 } 2593 2594 2595 // See if the timestamp has a sub-second portion. Note that if there is a 2596 // sub-second portion, then we may need to massage the value so that there 2597 // are exactly three sub-second characters so that it can be interpreted as 2598 // milliseconds. 2599 final String subSecFormatStr; 2600 final String trimmedTimestamp; 2601 int periodPos = t.lastIndexOf('.', tzPos); 2602 if (periodPos > 0) 2603 { 2604 final int subSecondLength = tzPos - periodPos - 1; 2605 switch (subSecondLength) 2606 { 2607 case 0: 2608 subSecFormatStr = ""; 2609 trimmedTimestamp = t.substring(0, periodPos); 2610 break; 2611 case 1: 2612 subSecFormatStr = ".SSS"; 2613 trimmedTimestamp = t.substring(0, (periodPos+2)) + "00"; 2614 break; 2615 case 2: 2616 subSecFormatStr = ".SSS"; 2617 trimmedTimestamp = t.substring(0, (periodPos+3)) + '0'; 2618 break; 2619 default: 2620 subSecFormatStr = ".SSS"; 2621 trimmedTimestamp = t.substring(0, periodPos+4); 2622 break; 2623 } 2624 } 2625 else 2626 { 2627 subSecFormatStr = ""; 2628 periodPos = tzPos; 2629 trimmedTimestamp = t.substring(0, tzPos); 2630 } 2631 2632 2633 // Look at where the period is (or would be if it existed) to see how many 2634 // characters are in the integer portion. This will give us what we need 2635 // for the rest of the format string. 2636 final String formatStr; 2637 switch (periodPos) 2638 { 2639 case 10: 2640 formatStr = "yyyyMMddHH" + subSecFormatStr; 2641 break; 2642 case 12: 2643 formatStr = "yyyyMMddHHmm" + subSecFormatStr; 2644 break; 2645 case 14: 2646 formatStr = "yyyyMMddHHmmss" + subSecFormatStr; 2647 break; 2648 default: 2649 throw new ParseException(ERR_GENTIME_CANNOT_PARSE_INVALID_LENGTH.get(t), 2650 periodPos); 2651 } 2652 2653 2654 // We should finally be able to create an appropriate date format object 2655 // to parse the trimmed version of the timestamp. 2656 final SimpleDateFormat dateFormat = new SimpleDateFormat(formatStr); 2657 dateFormat.setTimeZone(tz); 2658 dateFormat.setLenient(false); 2659 return dateFormat.parse(trimmedTimestamp); 2660 } 2661 2662 2663 2664 /** 2665 * Encodes the provided timestamp to the ISO 8601 format described in RFC 2666 * 3339. 2667 * 2668 * @param timestamp The timestamp to be encoded in the RFC 3339 format. 2669 * It should use the same format as the 2670 * {@code System.currentTimeMillis()} method (i.e., the 2671 * number of milliseconds since 12:00am UTC on January 1, 2672 * 1970). 2673 * 2674 * @return The RFC 3339 representation of the provided date. 2675 */ 2676 @NotNull() 2677 public static String encodeRFC3339Time(final long timestamp) 2678 { 2679 return encodeRFC3339Time(new Date(timestamp)); 2680 } 2681 2682 2683 2684 /** 2685 * Encodes the provided timestamp to the ISO 8601 format described in RFC 2686 * 3339. 2687 * 2688 * @param d The date to be encoded in the RFC 3339 format. 2689 * 2690 * @return The RFC 3339 representation of the provided date. 2691 */ 2692 @NotNull() 2693 public static String encodeRFC3339Time(@NotNull final Date d) 2694 { 2695 SimpleDateFormat dateFormat = RFC_3339_TIME_FORMATTERS.get(); 2696 if (dateFormat == null) 2697 { 2698 dateFormat = new SimpleDateFormat("yyyy'-'MM'-'dd'T'HH':'mm':'ss.SSS'Z'"); 2699 dateFormat.setTimeZone(UTC_TIME_ZONE); 2700 RFC_3339_TIME_FORMATTERS.set(dateFormat); 2701 } 2702 2703 return dateFormat.format(d); 2704 } 2705 2706 2707 2708 /** 2709 * Decodes the provided string as a timestamp encoded in the ISO 8601 format 2710 * described in RFC 3339. 2711 * 2712 * @param timestamp The timestamp to be decoded in the RFC 3339 format. 2713 * 2714 * @return The {@code Date} object decoded from the provided timestamp. 2715 * 2716 * @throws ParseException If the provided string could not be decoded as a 2717 * timestamp in the RFC 3339 time format. 2718 */ 2719 @NotNull() 2720 public static Date decodeRFC3339Time(@NotNull final String timestamp) 2721 throws ParseException 2722 { 2723 // Make sure that the string representation has the minimum acceptable 2724 // length. 2725 if (timestamp.length() < 20) 2726 { 2727 throw new ParseException(ERR_RFC_3339_TIME_TOO_SHORT.get(timestamp), 0); 2728 } 2729 2730 2731 // Parse the year, month, day, hour, minute, and second components from the 2732 // timestamp, and make sure the appropriate separator characters are between 2733 // those components. 2734 final int year = parseRFC3339Number(timestamp, 0, 4); 2735 validateRFC3339TimestampSeparatorCharacter(timestamp, 4, '-'); 2736 final int month = parseRFC3339Number(timestamp, 5, 2); 2737 validateRFC3339TimestampSeparatorCharacter(timestamp, 7, '-'); 2738 final int day = parseRFC3339Number(timestamp, 8, 2); 2739 validateRFC3339TimestampSeparatorCharacter(timestamp, 10, 'T'); 2740 final int hour = parseRFC3339Number(timestamp, 11, 2); 2741 validateRFC3339TimestampSeparatorCharacter(timestamp, 13, ':'); 2742 final int minute = parseRFC3339Number(timestamp, 14, 2); 2743 validateRFC3339TimestampSeparatorCharacter(timestamp, 16, ':'); 2744 final int second = parseRFC3339Number(timestamp, 17, 2); 2745 2746 2747 // Make sure that the month and day values are acceptable. 2748 switch (month) 2749 { 2750 case 1: 2751 case 3: 2752 case 5: 2753 case 7: 2754 case 8: 2755 case 10: 2756 case 12: 2757 // January, March, May, July, August, October, and December all have 31 2758 // days. 2759 if ((day < 1) || (day > 31)) 2760 { 2761 throw new ParseException( 2762 ERR_RFC_3339_TIME_INVALID_DAY_FOR_MONTH.get(timestamp, day, 2763 month), 2764 8); 2765 } 2766 break; 2767 2768 case 4: 2769 case 6: 2770 case 9: 2771 case 11: 2772 // April, June, September, and November all have 30 days. 2773 if ((day < 1) || (day > 30)) 2774 { 2775 throw new ParseException( 2776 ERR_RFC_3339_TIME_INVALID_DAY_FOR_MONTH.get(timestamp, day, 2777 month), 2778 8); 2779 } 2780 break; 2781 2782 case 2: 2783 // February can have 28 or 29 days, depending on whether it's a leap 2784 // year. Although we could determine whether the provided year is a 2785 // leap year, we'll just always accept up to 29 days for February. 2786 if ((day < 1) || (day > 29)) 2787 { 2788 throw new ParseException( 2789 ERR_RFC_3339_TIME_INVALID_DAY_FOR_MONTH.get(timestamp, day, 2790 month), 2791 8); 2792 } 2793 break; 2794 2795 default: 2796 throw new ParseException( 2797 ERR_RFC_3339_TIME_INVALID_MONTH.get(timestamp, month), 5); 2798 } 2799 2800 2801 // Make sure that the hour, minute, and second values are acceptable. Note 2802 // that while ISO 8601 permits a value of 24 for the hour, RFC 3339 only 2803 // permits hour values between 0 and 23. Also note that some minutes can 2804 // have up to 61 seconds for leap seconds, so we'll always account for that. 2805 if ((hour < 0) || (hour > 23)) 2806 { 2807 throw new ParseException( 2808 ERR_RFC_3339_TIME_INVALID_HOUR.get(timestamp, hour), 11); 2809 } 2810 2811 if ((minute < 0) || (minute > 59)) 2812 { 2813 throw new ParseException( 2814 ERR_RFC_3339_TIME_INVALID_MINUTE.get(timestamp, minute), 14); 2815 } 2816 2817 if ((second < 0) || (second > 60)) 2818 { 2819 throw new ParseException( 2820 ERR_RFC_3339_TIME_INVALID_SECOND.get(timestamp, second), 17); 2821 } 2822 2823 2824 // See if there is a sub-second portion. If so, then there will be a 2825 // period at position 19 followed by at least one digit. This 2826 // implementation will only support timestamps with no more than three 2827 // sub-second digits. 2828 int milliseconds = 0; 2829 int timeZoneStartPos = -1; 2830 if (timestamp.charAt(19) == '.') 2831 { 2832 int numDigits = 0; 2833 final StringBuilder subSecondString = new StringBuilder(3); 2834 for (int pos=20; pos < timestamp.length(); pos++) 2835 { 2836 final char c = timestamp.charAt(pos); 2837 switch (c) 2838 { 2839 case '0': 2840 numDigits++; 2841 if (subSecondString.length() > 0) 2842 { 2843 // Only add a zero if it's not the first digit. 2844 subSecondString.append(c); 2845 } 2846 break; 2847 case '1': 2848 case '2': 2849 case '3': 2850 case '4': 2851 case '5': 2852 case '6': 2853 case '7': 2854 case '8': 2855 case '9': 2856 numDigits++; 2857 subSecondString.append(c); 2858 break; 2859 case 'Z': 2860 case '+': 2861 case '-': 2862 timeZoneStartPos = pos; 2863 break; 2864 default: 2865 throw new ParseException( 2866 ERR_RFC_3339_TIME_INVALID_SUB_SECOND_CHAR.get(timestamp, c, 2867 pos), 2868 pos); 2869 } 2870 2871 if (timeZoneStartPos > 0) 2872 { 2873 break; 2874 } 2875 2876 if (numDigits > 3) 2877 { 2878 throw new ParseException( 2879 ERR_RFC_3339_TIME_TOO_MANY_SUB_SECOND_DIGITS.get(timestamp), 2880 20); 2881 } 2882 } 2883 2884 if (timeZoneStartPos < 0) 2885 { 2886 throw new ParseException( 2887 ERR_RFC_3339_TIME_MISSING_TIME_ZONE_AFTER_SUB_SECOND.get( 2888 timestamp), 2889 (timestamp.length() - 1)); 2890 } 2891 2892 if (numDigits == 0) 2893 { 2894 throw new ParseException( 2895 ERR_RFC_3339_TIME_NO_SUB_SECOND_DIGITS.get(timestamp), 19); 2896 } 2897 2898 if (subSecondString.length() == 0) 2899 { 2900 // This is possible if the sub-second portion is all zeroes. 2901 subSecondString.append('0'); 2902 } 2903 2904 milliseconds = Integer.parseInt(subSecondString.toString()); 2905 if (numDigits == 1) 2906 { 2907 milliseconds *= 100; 2908 } 2909 else if (numDigits == 2) 2910 { 2911 milliseconds *= 10; 2912 } 2913 } 2914 else 2915 { 2916 timeZoneStartPos = 19; 2917 } 2918 2919 2920 // The remainder of the timestamp should be the time zone. 2921 final TimeZone timeZone; 2922 if (timestamp.substring(timeZoneStartPos).equals("Z")) 2923 { 2924 // This is shorthand for the UTC time zone. 2925 timeZone = UTC_TIME_ZONE; 2926 } 2927 else 2928 { 2929 // This is an offset from UTC, which should be in the form "+HH:MM" or 2930 // "-HH:MM". Make sure it has the expected length. 2931 if ((timestamp.length() - timeZoneStartPos) != 6) 2932 { 2933 throw new ParseException( 2934 ERR_RFC_3339_TIME_INVALID_TZ.get(timestamp), timeZoneStartPos); 2935 } 2936 2937 // Make sure it starts with "+" or "-". 2938 final int firstChar = timestamp.charAt(timeZoneStartPos); 2939 if ((firstChar != '+') && (firstChar != '-')) 2940 { 2941 throw new ParseException( 2942 ERR_RFC_3339_TIME_INVALID_TZ.get(timestamp), timeZoneStartPos); 2943 } 2944 2945 2946 // Make sure the hour offset is valid. 2947 final int timeZoneHourOffset = 2948 parseRFC3339Number(timestamp, (timeZoneStartPos+1), 2); 2949 if ((timeZoneHourOffset < 0) || (timeZoneHourOffset > 23)) 2950 { 2951 throw new ParseException( 2952 ERR_RFC_3339_TIME_INVALID_TZ.get(timestamp), timeZoneStartPos); 2953 } 2954 2955 2956 // Make sure there is a colon between the hour and the minute portions of 2957 // the offset. 2958 if (timestamp.charAt(timeZoneStartPos+3) != ':') 2959 { 2960 throw new ParseException( 2961 ERR_RFC_3339_TIME_INVALID_TZ.get(timestamp), timeZoneStartPos); 2962 } 2963 2964 final int timeZoneMinuteOffset = 2965 parseRFC3339Number(timestamp, (timeZoneStartPos+4), 2); 2966 if ((timeZoneMinuteOffset < 0) || (timeZoneMinuteOffset > 59)) 2967 { 2968 throw new ParseException( 2969 ERR_RFC_3339_TIME_INVALID_TZ.get(timestamp), timeZoneStartPos); 2970 } 2971 2972 timeZone = TimeZone.getTimeZone( 2973 "GMT" + timestamp.substring(timeZoneStartPos)); 2974 } 2975 2976 2977 // Put everything together to construct the appropriate date. 2978 final GregorianCalendar calendar = 2979 new GregorianCalendar(year, 2980 (month-1), // NOTE: Calendar stupidly uses zero-indexed months. 2981 day, hour, minute, second); 2982 calendar.set(GregorianCalendar.MILLISECOND, milliseconds); 2983 calendar.setTimeZone(timeZone); 2984 return calendar.getTime(); 2985 } 2986 2987 2988 2989 /** 2990 * Ensures that the provided timestamp string has the expected character at 2991 * the specified position. 2992 * 2993 * @param timestamp The timestamp to examine. 2994 * It must not be {@code null}. 2995 * @param pos The position of the character to examine. 2996 * @param expectedChar The character expected at the specified position. 2997 * 2998 * @throws ParseException If the provided timestamp does not have the 2999 * expected 3000 */ 3001 private static void validateRFC3339TimestampSeparatorCharacter( 3002 @NotNull final String timestamp, final int pos, 3003 final char expectedChar) 3004 throws ParseException 3005 { 3006 if (timestamp.charAt(pos) != expectedChar) 3007 { 3008 throw new ParseException( 3009 ERR_RFC_3339_INVALID_SEPARATOR.get(timestamp, timestamp.charAt(pos), 3010 pos, expectedChar), 3011 pos); 3012 } 3013 } 3014 3015 3016 3017 /** 3018 * Parses the number at the specified location in the timestamp. 3019 * 3020 * @param timestamp The timestamp to examine. It must not be {@code null}. 3021 * @param pos The position at which to begin parsing the number. 3022 * @param numDigits The number of digits in the number. 3023 * 3024 * @return The number parsed from the provided timestamp. 3025 * 3026 * @throws ParseException If a problem is encountered while trying to parse 3027 * the number from the timestamp. 3028 */ 3029 private static int parseRFC3339Number(@NotNull final String timestamp, 3030 final int pos, final int numDigits) 3031 throws ParseException 3032 { 3033 int value = 0; 3034 for (int i=0; i < numDigits; i++) 3035 { 3036 value *= 10; 3037 switch (timestamp.charAt(pos+i)) 3038 { 3039 case '0': 3040 break; 3041 case '1': 3042 value += 1; 3043 break; 3044 case '2': 3045 value += 2; 3046 break; 3047 case '3': 3048 value += 3; 3049 break; 3050 case '4': 3051 value += 4; 3052 break; 3053 case '5': 3054 value += 5; 3055 break; 3056 case '6': 3057 value += 6; 3058 break; 3059 case '7': 3060 value += 7; 3061 break; 3062 case '8': 3063 value += 8; 3064 break; 3065 case '9': 3066 value += 9; 3067 break; 3068 default: 3069 throw new ParseException( 3070 ERR_RFC_3339_INVALID_DIGIT.get(timestamp, 3071 timestamp.charAt(pos+i), (pos+i)), 3072 (pos+i)); 3073 } 3074 } 3075 3076 return value; 3077 } 3078 3079 3080 3081 /** 3082 * Trims only leading spaces from the provided string, leaving any trailing 3083 * spaces intact. 3084 * 3085 * @param s The string to be processed. It must not be {@code null}. 3086 * 3087 * @return The original string if no trimming was required, or a new string 3088 * without leading spaces if the provided string had one or more. It 3089 * may be an empty string if the provided string was an empty string 3090 * or contained only spaces. 3091 */ 3092 @NotNull() 3093 public static String trimLeading(@NotNull final String s) 3094 { 3095 Validator.ensureNotNull(s); 3096 3097 int nonSpacePos = 0; 3098 final int length = s.length(); 3099 while ((nonSpacePos < length) && (s.charAt(nonSpacePos) == ' ')) 3100 { 3101 nonSpacePos++; 3102 } 3103 3104 if (nonSpacePos == 0) 3105 { 3106 // There were no leading spaces. 3107 return s; 3108 } 3109 else if (nonSpacePos >= length) 3110 { 3111 // There were no non-space characters. 3112 return ""; 3113 } 3114 else 3115 { 3116 // There were leading spaces, so return the string without them. 3117 return s.substring(nonSpacePos, length); 3118 } 3119 } 3120 3121 3122 3123 /** 3124 * Trims only trailing spaces from the provided string, leaving any leading 3125 * spaces intact. 3126 * 3127 * @param s The string to be processed. It must not be {@code null}. 3128 * 3129 * @return The original string if no trimming was required, or a new string 3130 * without trailing spaces if the provided string had one or more. 3131 * It may be an empty string if the provided string was an empty 3132 * string or contained only spaces. 3133 */ 3134 @NotNull() 3135 public static String trimTrailing(@NotNull final String s) 3136 { 3137 Validator.ensureNotNull(s); 3138 3139 final int lastPos = s.length() - 1; 3140 int nonSpacePos = lastPos; 3141 while ((nonSpacePos >= 0) && (s.charAt(nonSpacePos) == ' ')) 3142 { 3143 nonSpacePos--; 3144 } 3145 3146 if (nonSpacePos < 0) 3147 { 3148 // There were no non-space characters. 3149 return ""; 3150 } 3151 else if (nonSpacePos == lastPos) 3152 { 3153 // There were no trailing spaces. 3154 return s; 3155 } 3156 else 3157 { 3158 // There were trailing spaces, so return the string without them. 3159 return s.substring(0, (nonSpacePos+1)); 3160 } 3161 } 3162 3163 3164 3165 /** 3166 * Wraps the contents of the specified line using the given width. It will 3167 * attempt to wrap at spaces to preserve words, but if that is not possible 3168 * (because a single "word" is longer than the maximum width), then it will 3169 * wrap in the middle of the word at the specified maximum width. 3170 * 3171 * @param line The line to be wrapped. It must not be {@code null}. 3172 * @param maxWidth The maximum width for lines in the resulting list. A 3173 * value less than or equal to zero will cause no wrapping 3174 * to be performed. 3175 * 3176 * @return A list of the wrapped lines. It may be empty if the provided line 3177 * contained only spaces. 3178 */ 3179 @NotNull() 3180 public static List<String> wrapLine(@NotNull final String line, 3181 final int maxWidth) 3182 { 3183 return wrapLine(line, maxWidth, maxWidth); 3184 } 3185 3186 3187 3188 /** 3189 * Wraps the contents of the specified line using the given width. It will 3190 * attempt to wrap at spaces to preserve words, but if that is not possible 3191 * (because a single "word" is longer than the maximum width), then it will 3192 * wrap in the middle of the word at the specified maximum width. 3193 * 3194 * @param line The line to be wrapped. It must not be 3195 * {@code null}. 3196 * @param maxFirstLineWidth The maximum length for the first line in 3197 * the resulting list. A value less than or 3198 * equal to zero will cause no wrapping to be 3199 * performed. 3200 * @param maxSubsequentLineWidth The maximum length for all lines except the 3201 * first line. This must be greater than zero 3202 * unless {@code maxFirstLineWidth} is less 3203 * than or equal to zero. 3204 * 3205 * @return A list of the wrapped lines. It may be empty if the provided line 3206 * contained only spaces. 3207 */ 3208 @NotNull() 3209 public static List<String> wrapLine(@NotNull final String line, 3210 final int maxFirstLineWidth, 3211 final int maxSubsequentLineWidth) 3212 { 3213 if (maxFirstLineWidth > 0) 3214 { 3215 Validator.ensureTrue(maxSubsequentLineWidth > 0); 3216 } 3217 3218 // See if the provided string already contains line breaks. If so, then 3219 // treat it as multiple lines rather than a single line. 3220 final int breakPos = line.indexOf('\n'); 3221 if (breakPos >= 0) 3222 { 3223 final ArrayList<String> lineList = new ArrayList<>(10); 3224 final StringTokenizer tokenizer = new StringTokenizer(line, "\r\n"); 3225 while (tokenizer.hasMoreTokens()) 3226 { 3227 lineList.addAll(wrapLine(tokenizer.nextToken(), maxFirstLineWidth, 3228 maxSubsequentLineWidth)); 3229 } 3230 3231 return lineList; 3232 } 3233 3234 final int length = line.length(); 3235 if ((maxFirstLineWidth <= 0) || (length < maxFirstLineWidth)) 3236 { 3237 return Collections.singletonList(line); 3238 } 3239 3240 3241 int wrapPos = maxFirstLineWidth; 3242 int lastWrapPos = 0; 3243 final ArrayList<String> lineList = new ArrayList<>(5); 3244 while (true) 3245 { 3246 final int spacePos = line.lastIndexOf(' ', wrapPos); 3247 if (spacePos > lastWrapPos) 3248 { 3249 // We found a space in an acceptable location, so use it after trimming 3250 // any trailing spaces. 3251 final String s = trimTrailing(line.substring(lastWrapPos, spacePos)); 3252 3253 // Don't bother adding the line if it contained only spaces. 3254 if (! s.isEmpty()) 3255 { 3256 lineList.add(s); 3257 } 3258 3259 wrapPos = spacePos; 3260 } 3261 else 3262 { 3263 // We didn't find any spaces, so we'll have to insert a hard break at 3264 // the specified wrap column. 3265 lineList.add(line.substring(lastWrapPos, wrapPos)); 3266 } 3267 3268 // Skip over any spaces before the next non-space character. 3269 while ((wrapPos < length) && (line.charAt(wrapPos) == ' ')) 3270 { 3271 wrapPos++; 3272 } 3273 3274 lastWrapPos = wrapPos; 3275 wrapPos += maxSubsequentLineWidth; 3276 if (wrapPos >= length) 3277 { 3278 // The last fragment can fit on the line, so we can handle that now and 3279 // break. 3280 if (lastWrapPos >= length) 3281 { 3282 break; 3283 } 3284 else 3285 { 3286 final String s = line.substring(lastWrapPos); 3287 lineList.add(s); 3288 break; 3289 } 3290 } 3291 } 3292 3293 return lineList; 3294 } 3295 3296 3297 3298 /** 3299 * This method returns a form of the provided argument that is safe to 3300 * use on the command line for the local platform. This method is provided as 3301 * a convenience wrapper around {@link ExampleCommandLineArgument}. Calling 3302 * this method is equivalent to: 3303 * 3304 * <PRE> 3305 * return ExampleCommandLineArgument.getCleanArgument(s).getLocalForm(); 3306 * </PRE> 3307 * 3308 * For getting direct access to command line arguments that are safe to 3309 * use on other platforms, call 3310 * {@link ExampleCommandLineArgument#getCleanArgument}. 3311 * 3312 * @param s The string to be processed. It must not be {@code null}. 3313 * 3314 * @return A cleaned version of the provided string in a form that will allow 3315 * it to be displayed as the value of a command-line argument on. 3316 */ 3317 @NotNull() 3318 public static String cleanExampleCommandLineArgument(@NotNull final String s) 3319 { 3320 return ExampleCommandLineArgument.getCleanArgument(s).getLocalForm(); 3321 } 3322 3323 3324 3325 /** 3326 * Retrieves a single string which is a concatenation of all of the provided 3327 * strings. 3328 * 3329 * @param a The array of strings to concatenate. It must not be 3330 * {@code null} but may be empty. 3331 * 3332 * @return A string containing a concatenation of all of the strings in the 3333 * provided array. 3334 */ 3335 @NotNull() 3336 public static String concatenateStrings(@NotNull final String... a) 3337 { 3338 return concatenateStrings(null, null, " ", null, null, a); 3339 } 3340 3341 3342 3343 /** 3344 * Retrieves a single string which is a concatenation of all of the provided 3345 * strings. 3346 * 3347 * @param l The list of strings to concatenate. It must not be 3348 * {@code null} but may be empty. 3349 * 3350 * @return A string containing a concatenation of all of the strings in the 3351 * provided list. 3352 */ 3353 @NotNull() 3354 public static String concatenateStrings(@NotNull final List<String> l) 3355 { 3356 return concatenateStrings(null, null, " ", null, null, l); 3357 } 3358 3359 3360 3361 /** 3362 * Retrieves a single string which is a concatenation of all of the provided 3363 * strings. 3364 * 3365 * @param beforeList A string that should be placed at the beginning of 3366 * the list. It may be {@code null} or empty if 3367 * nothing should be placed at the beginning of the 3368 * list. 3369 * @param beforeElement A string that should be placed before each element 3370 * in the list. It may be {@code null} or empty if 3371 * nothing should be placed before each element. 3372 * @param betweenElements The separator that should be placed between 3373 * elements in the list. It may be {@code null} or 3374 * empty if no separator should be placed between 3375 * elements. 3376 * @param afterElement A string that should be placed after each element 3377 * in the list. It may be {@code null} or empty if 3378 * nothing should be placed after each element. 3379 * @param afterList A string that should be placed at the end of the 3380 * list. It may be {@code null} or empty if nothing 3381 * should be placed at the end of the list. 3382 * @param a The array of strings to concatenate. It must not 3383 * be {@code null} but may be empty. 3384 * 3385 * @return A string containing a concatenation of all of the strings in the 3386 * provided list. 3387 */ 3388 @NotNull() 3389 public static String concatenateStrings(@Nullable final String beforeList, 3390 @Nullable final String beforeElement, 3391 @Nullable final String betweenElements, 3392 @Nullable final String afterElement, 3393 @Nullable final String afterList, 3394 @NotNull final String... a) 3395 { 3396 return concatenateStrings(beforeList, beforeElement, betweenElements, 3397 afterElement, afterList, Arrays.asList(a)); 3398 } 3399 3400 3401 3402 /** 3403 * Retrieves a single string which is a concatenation of all of the provided 3404 * strings. 3405 * 3406 * @param beforeList A string that should be placed at the beginning of 3407 * the list. It may be {@code null} or empty if 3408 * nothing should be placed at the beginning of the 3409 * list. 3410 * @param beforeElement A string that should be placed before each element 3411 * in the list. It may be {@code null} or empty if 3412 * nothing should be placed before each element. 3413 * @param betweenElements The separator that should be placed between 3414 * elements in the list. It may be {@code null} or 3415 * empty if no separator should be placed between 3416 * elements. 3417 * @param afterElement A string that should be placed after each element 3418 * in the list. It may be {@code null} or empty if 3419 * nothing should be placed after each element. 3420 * @param afterList A string that should be placed at the end of the 3421 * list. It may be {@code null} or empty if nothing 3422 * should be placed at the end of the list. 3423 * @param l The list of strings to concatenate. It must not 3424 * be {@code null} but may be empty. 3425 * 3426 * @return A string containing a concatenation of all of the strings in the 3427 * provided list. 3428 */ 3429 @NotNull() 3430 public static String concatenateStrings(@Nullable final String beforeList, 3431 @Nullable final String beforeElement, 3432 @Nullable final String betweenElements, 3433 @Nullable final String afterElement, 3434 @Nullable final String afterList, 3435 @NotNull final List<String> l) 3436 { 3437 Validator.ensureNotNull(l); 3438 3439 final StringBuilder buffer = new StringBuilder(); 3440 3441 if (beforeList != null) 3442 { 3443 buffer.append(beforeList); 3444 } 3445 3446 final Iterator<String> iterator = l.iterator(); 3447 while (iterator.hasNext()) 3448 { 3449 if (beforeElement != null) 3450 { 3451 buffer.append(beforeElement); 3452 } 3453 3454 buffer.append(iterator.next()); 3455 3456 if (afterElement != null) 3457 { 3458 buffer.append(afterElement); 3459 } 3460 3461 if ((betweenElements != null) && iterator.hasNext()) 3462 { 3463 buffer.append(betweenElements); 3464 } 3465 } 3466 3467 if (afterList != null) 3468 { 3469 buffer.append(afterList); 3470 } 3471 3472 return buffer.toString(); 3473 } 3474 3475 3476 3477 /** 3478 * Converts a duration in seconds to a string with a human-readable duration 3479 * which may include days, hours, minutes, and seconds, to the extent that 3480 * they are needed. 3481 * 3482 * @param s The number of seconds to be represented. 3483 * 3484 * @return A string containing a human-readable representation of the 3485 * provided time. 3486 */ 3487 @NotNull() 3488 public static String secondsToHumanReadableDuration(final long s) 3489 { 3490 return millisToHumanReadableDuration(s * 1000L); 3491 } 3492 3493 3494 3495 /** 3496 * Converts a duration in seconds to a string with a human-readable duration 3497 * which may include days, hours, minutes, and seconds, to the extent that 3498 * they are needed. 3499 * 3500 * @param m The number of milliseconds to be represented. 3501 * 3502 * @return A string containing a human-readable representation of the 3503 * provided time. 3504 */ 3505 @NotNull() 3506 public static String millisToHumanReadableDuration(final long m) 3507 { 3508 final StringBuilder buffer = new StringBuilder(); 3509 long numMillis = m; 3510 3511 final long numDays = numMillis / 86_400_000L; 3512 if (numDays > 0) 3513 { 3514 numMillis -= (numDays * 86_400_000L); 3515 if (numDays == 1) 3516 { 3517 buffer.append(INFO_NUM_DAYS_SINGULAR.get(numDays)); 3518 } 3519 else 3520 { 3521 buffer.append(INFO_NUM_DAYS_PLURAL.get(numDays)); 3522 } 3523 } 3524 3525 final long numHours = numMillis / 3_600_000L; 3526 if (numHours > 0) 3527 { 3528 numMillis -= (numHours * 3_600_000L); 3529 if (buffer.length() > 0) 3530 { 3531 buffer.append(", "); 3532 } 3533 3534 if (numHours == 1) 3535 { 3536 buffer.append(INFO_NUM_HOURS_SINGULAR.get(numHours)); 3537 } 3538 else 3539 { 3540 buffer.append(INFO_NUM_HOURS_PLURAL.get(numHours)); 3541 } 3542 } 3543 3544 final long numMinutes = numMillis / 60_000L; 3545 if (numMinutes > 0) 3546 { 3547 numMillis -= (numMinutes * 60_000L); 3548 if (buffer.length() > 0) 3549 { 3550 buffer.append(", "); 3551 } 3552 3553 if (numMinutes == 1) 3554 { 3555 buffer.append(INFO_NUM_MINUTES_SINGULAR.get(numMinutes)); 3556 } 3557 else 3558 { 3559 buffer.append(INFO_NUM_MINUTES_PLURAL.get(numMinutes)); 3560 } 3561 } 3562 3563 if (numMillis == 1000) 3564 { 3565 if (buffer.length() > 0) 3566 { 3567 buffer.append(", "); 3568 } 3569 3570 buffer.append(INFO_NUM_SECONDS_SINGULAR.get(1)); 3571 } 3572 else if ((numMillis > 0) || (buffer.length() == 0)) 3573 { 3574 if (buffer.length() > 0) 3575 { 3576 buffer.append(", "); 3577 } 3578 3579 final long numSeconds = numMillis / 1000L; 3580 numMillis -= (numSeconds * 1000L); 3581 if ((numMillis % 1000L) != 0L) 3582 { 3583 final double numSecondsDouble = numSeconds + (numMillis / 1000.0); 3584 final DecimalFormat decimalFormat = new DecimalFormat("0.000"); 3585 buffer.append(INFO_NUM_SECONDS_WITH_DECIMAL.get( 3586 decimalFormat.format(numSecondsDouble))); 3587 } 3588 else 3589 { 3590 buffer.append(INFO_NUM_SECONDS_PLURAL.get(numSeconds)); 3591 } 3592 } 3593 3594 return buffer.toString(); 3595 } 3596 3597 3598 3599 /** 3600 * Converts the provided number of nanoseconds to milliseconds. 3601 * 3602 * @param nanos The number of nanoseconds to convert to milliseconds. 3603 * 3604 * @return The number of milliseconds that most closely corresponds to the 3605 * specified number of nanoseconds. 3606 */ 3607 public static long nanosToMillis(final long nanos) 3608 { 3609 return Math.max(0L, Math.round(nanos / 1_000_000.0d)); 3610 } 3611 3612 3613 3614 /** 3615 * Converts the provided number of milliseconds to nanoseconds. 3616 * 3617 * @param millis The number of milliseconds to convert to nanoseconds. 3618 * 3619 * @return The number of nanoseconds that most closely corresponds to the 3620 * specified number of milliseconds. 3621 */ 3622 public static long millisToNanos(final long millis) 3623 { 3624 return Math.max(0L, (millis * 1_000_000L)); 3625 } 3626 3627 3628 3629 /** 3630 * Indicates whether the provided string is a valid numeric OID. A numeric 3631 * OID must start and end with a digit, must have at least on period, must 3632 * contain only digits and periods, and must not have two consecutive periods. 3633 * 3634 * @param s The string to examine. It must not be {@code null}. 3635 * 3636 * @return {@code true} if the provided string is a valid numeric OID, or 3637 * {@code false} if not. 3638 */ 3639 public static boolean isNumericOID(@NotNull final String s) 3640 { 3641 boolean digitRequired = true; 3642 boolean periodFound = false; 3643 for (final char c : s.toCharArray()) 3644 { 3645 switch (c) 3646 { 3647 case '0': 3648 case '1': 3649 case '2': 3650 case '3': 3651 case '4': 3652 case '5': 3653 case '6': 3654 case '7': 3655 case '8': 3656 case '9': 3657 digitRequired = false; 3658 break; 3659 3660 case '.': 3661 if (digitRequired) 3662 { 3663 return false; 3664 } 3665 else 3666 { 3667 digitRequired = true; 3668 } 3669 periodFound = true; 3670 break; 3671 3672 default: 3673 return false; 3674 } 3675 3676 } 3677 3678 return (periodFound && (! digitRequired)); 3679 } 3680 3681 3682 3683 /** 3684 * Capitalizes the provided string. The first character will be converted to 3685 * uppercase, and the rest of the string will be left unaltered. 3686 * 3687 * @param s The string to be capitalized. 3688 * 3689 * @return A capitalized version of the provided string, or {@code null} if 3690 * the provided string was {@code null}. 3691 */ 3692 @Nullable() 3693 public static String capitalize(@Nullable final String s) 3694 { 3695 return capitalize(s, false); 3696 } 3697 3698 3699 3700 /** 3701 * Capitalizes the provided string. The first character of the string (or 3702 * optionally the first character of each word in the string) 3703 * 3704 * @param s The string to be capitalized. 3705 * @param allWords Indicates whether to capitalize all words in the string, 3706 * or only the first word. 3707 * 3708 * @return A capitalized version of the provided string, or {@code null} if 3709 * the provided string was {@code null}. 3710 */ 3711 @Nullable() 3712 public static String capitalize(@Nullable final String s, 3713 final boolean allWords) 3714 { 3715 if (s == null) 3716 { 3717 return null; 3718 } 3719 3720 switch (s.length()) 3721 { 3722 case 0: 3723 return s; 3724 3725 case 1: 3726 return s.toUpperCase(); 3727 3728 default: 3729 boolean capitalize = true; 3730 final char[] chars = s.toCharArray(); 3731 final StringBuilder buffer = new StringBuilder(chars.length); 3732 for (final char c : chars) 3733 { 3734 // Whitespace and punctuation will be considered word breaks. 3735 if (Character.isWhitespace(c) || 3736 (((c >= '!') && (c <= '.')) || 3737 ((c >= ':') && (c <= '@')) || 3738 ((c >= '[') && (c <= '`')) || 3739 ((c >= '{') && (c <= '~')))) 3740 { 3741 buffer.append(c); 3742 capitalize |= allWords; 3743 } 3744 else if (capitalize) 3745 { 3746 buffer.append(Character.toUpperCase(c)); 3747 capitalize = false; 3748 } 3749 else 3750 { 3751 buffer.append(c); 3752 } 3753 } 3754 return buffer.toString(); 3755 } 3756 } 3757 3758 3759 3760 /** 3761 * Encodes the provided UUID to a byte array containing its 128-bit 3762 * representation. 3763 * 3764 * @param uuid The UUID to be encoded. It must not be {@code null}. 3765 * 3766 * @return The byte array containing the 128-bit encoded UUID. 3767 */ 3768 @NotNull() 3769 public static byte[] encodeUUID(@NotNull final UUID uuid) 3770 { 3771 final byte[] b = new byte[16]; 3772 3773 final long mostSignificantBits = uuid.getMostSignificantBits(); 3774 b[0] = (byte) ((mostSignificantBits >> 56) & 0xFF); 3775 b[1] = (byte) ((mostSignificantBits >> 48) & 0xFF); 3776 b[2] = (byte) ((mostSignificantBits >> 40) & 0xFF); 3777 b[3] = (byte) ((mostSignificantBits >> 32) & 0xFF); 3778 b[4] = (byte) ((mostSignificantBits >> 24) & 0xFF); 3779 b[5] = (byte) ((mostSignificantBits >> 16) & 0xFF); 3780 b[6] = (byte) ((mostSignificantBits >> 8) & 0xFF); 3781 b[7] = (byte) (mostSignificantBits & 0xFF); 3782 3783 final long leastSignificantBits = uuid.getLeastSignificantBits(); 3784 b[8] = (byte) ((leastSignificantBits >> 56) & 0xFF); 3785 b[9] = (byte) ((leastSignificantBits >> 48) & 0xFF); 3786 b[10] = (byte) ((leastSignificantBits >> 40) & 0xFF); 3787 b[11] = (byte) ((leastSignificantBits >> 32) & 0xFF); 3788 b[12] = (byte) ((leastSignificantBits >> 24) & 0xFF); 3789 b[13] = (byte) ((leastSignificantBits >> 16) & 0xFF); 3790 b[14] = (byte) ((leastSignificantBits >> 8) & 0xFF); 3791 b[15] = (byte) (leastSignificantBits & 0xFF); 3792 3793 return b; 3794 } 3795 3796 3797 3798 /** 3799 * Decodes the value of the provided byte array as a Java UUID. 3800 * 3801 * @param b The byte array to be decoded as a UUID. It must not be 3802 * {@code null}. 3803 * 3804 * @return The decoded UUID. 3805 * 3806 * @throws ParseException If the provided byte array cannot be parsed as a 3807 * UUID. 3808 */ 3809 @NotNull() 3810 public static UUID decodeUUID(@NotNull final byte[] b) 3811 throws ParseException 3812 { 3813 if (b.length != 16) 3814 { 3815 throw new ParseException(ERR_DECODE_UUID_INVALID_LENGTH.get(toHex(b)), 0); 3816 } 3817 3818 long mostSignificantBits = 0L; 3819 for (int i=0; i < 8; i++) 3820 { 3821 mostSignificantBits = (mostSignificantBits << 8) | (b[i] & 0xFF); 3822 } 3823 3824 long leastSignificantBits = 0L; 3825 for (int i=8; i < 16; i++) 3826 { 3827 leastSignificantBits = (leastSignificantBits << 8) | (b[i] & 0xFF); 3828 } 3829 3830 return new UUID(mostSignificantBits, leastSignificantBits); 3831 } 3832 3833 3834 3835 /** 3836 * Returns {@code true} if and only if the current process is running on 3837 * a Windows-based operating system. 3838 * 3839 * @return {@code true} if the current process is running on a Windows-based 3840 * operating system and {@code false} otherwise. 3841 */ 3842 public static boolean isWindows() 3843 { 3844 final String osName = toLowerCase(getSystemProperty("os.name")); 3845 return ((osName != null) && osName.contains("windows")); 3846 } 3847 3848 3849 3850 /** 3851 * Retrieves the string that should be appended to the end of all but the last 3852 * line of a multi-line command to indicate that the command continues onto 3853 * the next line. 3854 * <BR><BR> 3855 * This will be the caret (also called a circumflex accent) character on 3856 * Windows systems, and a backslash (also called a reverse solidus) character 3857 * on Linux and UNIX-based systems. 3858 * <BR><BR> 3859 * The string value that is returned will not include a space, but it should 3860 * generally be preceded by one or more space to separate it from the previous 3861 * component on the command line. 3862 * 3863 * @return The string that should be appended (generally after one or more 3864 * spaces to separate it from the previous component) to the end of 3865 * all but the last line of a multi-line command to indicate that the 3866 * command continues onto the next line. 3867 */ 3868 @NotNull() 3869 public static String getCommandLineContinuationString() 3870 { 3871 if (isWindows()) 3872 { 3873 return "^"; 3874 } 3875 else 3876 { 3877 return "\\"; 3878 } 3879 } 3880 3881 3882 3883 /** 3884 * Attempts to parse the contents of the provided string to an argument list 3885 * (e.g., converts something like "--arg1 arg1value --arg2 --arg3 arg3value" 3886 * to a list of "--arg1", "arg1value", "--arg2", "--arg3", "arg3value"). 3887 * 3888 * @param s The string to be converted to an argument list. 3889 * 3890 * @return The parsed argument list. 3891 * 3892 * @throws ParseException If a problem is encountered while attempting to 3893 * parse the given string to an argument list. 3894 */ 3895 @NotNull() 3896 public static List<String> toArgumentList(@Nullable final String s) 3897 throws ParseException 3898 { 3899 if ((s == null) || s.isEmpty()) 3900 { 3901 return Collections.emptyList(); 3902 } 3903 3904 int quoteStartPos = -1; 3905 boolean inEscape = false; 3906 final ArrayList<String> argList = new ArrayList<>(20); 3907 final StringBuilder currentArg = new StringBuilder(); 3908 for (int i=0; i < s.length(); i++) 3909 { 3910 final char c = s.charAt(i); 3911 if (inEscape) 3912 { 3913 currentArg.append(c); 3914 inEscape = false; 3915 continue; 3916 } 3917 3918 if (c == '\\') 3919 { 3920 inEscape = true; 3921 } 3922 else if (c == '"') 3923 { 3924 if (quoteStartPos >= 0) 3925 { 3926 quoteStartPos = -1; 3927 } 3928 else 3929 { 3930 quoteStartPos = i; 3931 } 3932 } 3933 else if (c == ' ') 3934 { 3935 if (quoteStartPos >= 0) 3936 { 3937 currentArg.append(c); 3938 } 3939 else if (currentArg.length() > 0) 3940 { 3941 argList.add(currentArg.toString()); 3942 currentArg.setLength(0); 3943 } 3944 } 3945 else 3946 { 3947 currentArg.append(c); 3948 } 3949 } 3950 3951 if (s.endsWith("\\") && (! s.endsWith("\\\\"))) 3952 { 3953 throw new ParseException(ERR_ARG_STRING_DANGLING_BACKSLASH.get(), 3954 (s.length() - 1)); 3955 } 3956 3957 if (quoteStartPos >= 0) 3958 { 3959 throw new ParseException(ERR_ARG_STRING_UNMATCHED_QUOTE.get( 3960 quoteStartPos), quoteStartPos); 3961 } 3962 3963 if (currentArg.length() > 0) 3964 { 3965 argList.add(currentArg.toString()); 3966 } 3967 3968 return Collections.unmodifiableList(argList); 3969 } 3970 3971 3972 3973 /** 3974 * Retrieves an array containing the elements of the provided collection. 3975 * 3976 * @param <T> The type of element included in the provided 3977 * collection. 3978 * @param collection The collection to convert to an array. 3979 * @param type The type of element contained in the collection. 3980 * 3981 * @return An array containing the elements of the provided list, or 3982 * {@code null} if the provided list is {@code null}. 3983 */ 3984 @Nullable() 3985 public static <T> T[] toArray(@Nullable final Collection<T> collection, 3986 @NotNull final Class<T> type) 3987 { 3988 if (collection == null) 3989 { 3990 return null; 3991 } 3992 3993 @SuppressWarnings("unchecked") 3994 final T[] array = (T[]) Array.newInstance(type, collection.size()); 3995 3996 return collection.toArray(array); 3997 } 3998 3999 4000 4001 /** 4002 * Creates a modifiable list with all of the items of the provided array in 4003 * the same order. This method behaves much like {@code Arrays.asList}, 4004 * except that if the provided array is {@code null}, then it will return a 4005 * {@code null} list rather than throwing an exception. 4006 * 4007 * @param <T> The type of item contained in the provided array. 4008 * 4009 * @param array The array of items to include in the list. 4010 * 4011 * @return The list that was created, or {@code null} if the provided array 4012 * was {@code null}. 4013 */ 4014 @Nullable() 4015 public static <T> List<T> toList(@Nullable final T[] array) 4016 { 4017 if (array == null) 4018 { 4019 return null; 4020 } 4021 4022 final ArrayList<T> l = new ArrayList<>(array.length); 4023 l.addAll(Arrays.asList(array)); 4024 return l; 4025 } 4026 4027 4028 4029 /** 4030 * Creates a modifiable list with all of the items of the provided array in 4031 * the same order. This method behaves much like {@code Arrays.asList}, 4032 * except that if the provided array is {@code null}, then it will return an 4033 * empty list rather than throwing an exception. 4034 * 4035 * @param <T> The type of item contained in the provided array. 4036 * 4037 * @param array The array of items to include in the list. 4038 * 4039 * @return The list that was created, or an empty list if the provided array 4040 * was {@code null}. 4041 */ 4042 @NotNull() 4043 public static <T> List<T> toNonNullList(@Nullable final T[] array) 4044 { 4045 if (array == null) 4046 { 4047 return new ArrayList<>(0); 4048 } 4049 4050 final ArrayList<T> l = new ArrayList<>(array.length); 4051 l.addAll(Arrays.asList(array)); 4052 return l; 4053 } 4054 4055 4056 4057 /** 4058 * Indicates whether both of the provided objects are {@code null} or both 4059 * are logically equal (using the {@code equals} method). 4060 * 4061 * @param o1 The first object for which to make the determination. 4062 * @param o2 The second object for which to make the determination. 4063 * 4064 * @return {@code true} if both objects are {@code null} or both are 4065 * logically equal, or {@code false} if only one of the objects is 4066 * {@code null} or they are not logically equal. 4067 */ 4068 public static boolean bothNullOrEqual(@Nullable final Object o1, 4069 @Nullable final Object o2) 4070 { 4071 if (o1 == null) 4072 { 4073 return (o2 == null); 4074 } 4075 else if (o2 == null) 4076 { 4077 return false; 4078 } 4079 4080 return o1.equals(o2); 4081 } 4082 4083 4084 4085 /** 4086 * Indicates whether both of the provided strings are {@code null} or both 4087 * are logically equal ignoring differences in capitalization (using the 4088 * {@code equalsIgnoreCase} method). 4089 * 4090 * @param s1 The first string for which to make the determination. 4091 * @param s2 The second string for which to make the determination. 4092 * 4093 * @return {@code true} if both strings are {@code null} or both are 4094 * logically equal ignoring differences in capitalization, or 4095 * {@code false} if only one of the objects is {@code null} or they 4096 * are not logically equal ignoring capitalization. 4097 */ 4098 public static boolean bothNullOrEqualIgnoreCase(@Nullable final String s1, 4099 @Nullable final String s2) 4100 { 4101 if (s1 == null) 4102 { 4103 return (s2 == null); 4104 } 4105 else if (s2 == null) 4106 { 4107 return false; 4108 } 4109 4110 return s1.equalsIgnoreCase(s2); 4111 } 4112 4113 4114 4115 /** 4116 * Indicates whether the provided string arrays have the same elements, 4117 * ignoring the order in which they appear and differences in capitalization. 4118 * It is assumed that neither array contains {@code null} strings, and that 4119 * no string appears more than once in each array. 4120 * 4121 * @param a1 The first array for which to make the determination. 4122 * @param a2 The second array for which to make the determination. 4123 * 4124 * @return {@code true} if both arrays have the same set of strings, or 4125 * {@code false} if not. 4126 */ 4127 public static boolean stringsEqualIgnoreCaseOrderIndependent( 4128 @Nullable final String[] a1, 4129 @Nullable final String[] a2) 4130 { 4131 if (a1 == null) 4132 { 4133 return (a2 == null); 4134 } 4135 else if (a2 == null) 4136 { 4137 return false; 4138 } 4139 4140 if (a1.length != a2.length) 4141 { 4142 return false; 4143 } 4144 4145 if (a1.length == 1) 4146 { 4147 return (a1[0].equalsIgnoreCase(a2[0])); 4148 } 4149 4150 final HashSet<String> s1 = new HashSet<>(computeMapCapacity(a1.length)); 4151 for (final String s : a1) 4152 { 4153 s1.add(toLowerCase(s)); 4154 } 4155 4156 final HashSet<String> s2 = new HashSet<>(computeMapCapacity(a2.length)); 4157 for (final String s : a2) 4158 { 4159 s2.add(toLowerCase(s)); 4160 } 4161 4162 return s1.equals(s2); 4163 } 4164 4165 4166 4167 /** 4168 * Indicates whether the provided arrays have the same elements, ignoring the 4169 * order in which they appear. It is assumed that neither array contains 4170 * {@code null} elements, and that no element appears more than once in each 4171 * array. 4172 * 4173 * @param <T> The type of element contained in the arrays. 4174 * 4175 * @param a1 The first array for which to make the determination. 4176 * @param a2 The second array for which to make the determination. 4177 * 4178 * @return {@code true} if both arrays have the same set of elements, or 4179 * {@code false} if not. 4180 */ 4181 public static <T> boolean arraysEqualOrderIndependent(@Nullable final T[] a1, 4182 @Nullable final T[] a2) 4183 { 4184 if (a1 == null) 4185 { 4186 return (a2 == null); 4187 } 4188 else if (a2 == null) 4189 { 4190 return false; 4191 } 4192 4193 if (a1.length != a2.length) 4194 { 4195 return false; 4196 } 4197 4198 if (a1.length == 1) 4199 { 4200 return (a1[0].equals(a2[0])); 4201 } 4202 4203 final HashSet<T> s1 = new HashSet<>(Arrays.asList(a1)); 4204 final HashSet<T> s2 = new HashSet<>(Arrays.asList(a2)); 4205 return s1.equals(s2); 4206 } 4207 4208 4209 4210 /** 4211 * Determines the number of bytes in a UTF-8 character that starts with the 4212 * given byte. 4213 * 4214 * @param b The byte for which to make the determination. 4215 * 4216 * @return The number of bytes in a UTF-8 character that starts with the 4217 * given byte, or -1 if it does not appear to be a valid first byte 4218 * for a UTF-8 character. 4219 */ 4220 public static int numBytesInUTF8CharacterWithFirstByte(final byte b) 4221 { 4222 if ((b & 0x7F) == b) 4223 { 4224 return 1; 4225 } 4226 else if ((b & 0xE0) == 0xC0) 4227 { 4228 return 2; 4229 } 4230 else if ((b & 0xF0) == 0xE0) 4231 { 4232 return 3; 4233 } 4234 else if ((b & 0xF8) == 0xF0) 4235 { 4236 return 4; 4237 } 4238 else 4239 { 4240 return -1; 4241 } 4242 } 4243 4244 4245 4246 /** 4247 * Indicates whether the provided attribute name should be considered a 4248 * sensitive attribute for the purposes of {@code toCode} methods. If an 4249 * attribute is considered sensitive, then its values will be redacted in the 4250 * output of the {@code toCode} methods. 4251 * 4252 * @param name The name for which to make the determination. It may or may 4253 * not include attribute options. It must not be {@code null}. 4254 * 4255 * @return {@code true} if the specified attribute is one that should be 4256 * considered sensitive for the 4257 */ 4258 public static boolean isSensitiveToCodeAttribute(@NotNull final String name) 4259 { 4260 final String lowerBaseName = Attribute.getBaseName(name).toLowerCase(); 4261 return TO_CODE_SENSITIVE_ATTRIBUTE_NAMES.contains(lowerBaseName); 4262 } 4263 4264 4265 4266 /** 4267 * Retrieves a set containing the base names (in all lowercase characters) of 4268 * any attributes that should be considered sensitive for the purposes of the 4269 * {@code toCode} methods. By default, only the userPassword and 4270 * authPassword attributes and their respective OIDs will be included. 4271 * 4272 * @return A set containing the base names (in all lowercase characters) of 4273 * any attributes that should be considered sensitive for the 4274 * purposes of the {@code toCode} methods. 4275 */ 4276 @NotNull() 4277 public static Set<String> getSensitiveToCodeAttributeBaseNames() 4278 { 4279 return TO_CODE_SENSITIVE_ATTRIBUTE_NAMES; 4280 } 4281 4282 4283 4284 /** 4285 * Specifies the names of any attributes that should be considered sensitive 4286 * for the purposes of the {@code toCode} methods. 4287 * 4288 * @param names The names of any attributes that should be considered 4289 * sensitive for the purposes of the {@code toCode} methods. 4290 * It may be {@code null} or empty if no attributes should be 4291 * considered sensitive. 4292 */ 4293 public static void setSensitiveToCodeAttributes( 4294 @Nullable final String... names) 4295 { 4296 setSensitiveToCodeAttributes(toList(names)); 4297 } 4298 4299 4300 4301 /** 4302 * Specifies the names of any attributes that should be considered sensitive 4303 * for the purposes of the {@code toCode} methods. 4304 * 4305 * @param names The names of any attributes that should be considered 4306 * sensitive for the purposes of the {@code toCode} methods. 4307 * It may be {@code null} or empty if no attributes should be 4308 * considered sensitive. 4309 */ 4310 public static void setSensitiveToCodeAttributes( 4311 @Nullable final Collection<String> names) 4312 { 4313 if ((names == null) || names.isEmpty()) 4314 { 4315 TO_CODE_SENSITIVE_ATTRIBUTE_NAMES = Collections.emptySet(); 4316 } 4317 else 4318 { 4319 final LinkedHashSet<String> nameSet = new LinkedHashSet<>(names.size()); 4320 for (final String s : names) 4321 { 4322 nameSet.add(Attribute.getBaseName(s).toLowerCase()); 4323 } 4324 4325 TO_CODE_SENSITIVE_ATTRIBUTE_NAMES = Collections.unmodifiableSet(nameSet); 4326 } 4327 } 4328 4329 4330 4331 /** 4332 * Creates a new {@code IOException} with a cause. The constructor needed to 4333 * do this wasn't available until Java SE 6, so reflection is used to invoke 4334 * this constructor in versions of Java that provide it. In Java SE 5, the 4335 * provided message will be augmented with information about the cause. 4336 * 4337 * @param message The message to use for the exception. This may be 4338 * {@code null} if the message should be generated from the 4339 * provided cause. 4340 * @param cause The underlying cause for the exception. It may be 4341 * {@code null} if the exception should have only a message. 4342 * 4343 * @return The {@code IOException} object that was created. 4344 */ 4345 @NotNull() 4346 public static IOException createIOExceptionWithCause( 4347 @Nullable final String message, 4348 @Nullable final Throwable cause) 4349 { 4350 if (cause == null) 4351 { 4352 return new IOException(message); 4353 } 4354 else if (message == null) 4355 { 4356 return new IOException(cause); 4357 } 4358 else 4359 { 4360 return new IOException(message, cause); 4361 } 4362 } 4363 4364 4365 4366 /** 4367 * Converts the provided string (which may include line breaks) into a list 4368 * containing the lines without the line breaks. 4369 * 4370 * @param s The string to convert into a list of its representative lines. 4371 * 4372 * @return A list containing the lines that comprise the given string. 4373 */ 4374 @NotNull() 4375 public static List<String> stringToLines(@Nullable final String s) 4376 { 4377 final ArrayList<String> l = new ArrayList<>(10); 4378 4379 if (s == null) 4380 { 4381 return l; 4382 } 4383 4384 final BufferedReader reader = new BufferedReader(new StringReader(s)); 4385 4386 try 4387 { 4388 while (true) 4389 { 4390 try 4391 { 4392 final String line = reader.readLine(); 4393 if (line == null) 4394 { 4395 return l; 4396 } 4397 else 4398 { 4399 l.add(line); 4400 } 4401 } 4402 catch (final Exception e) 4403 { 4404 Debug.debugException(e); 4405 4406 // This should never happen. If it does, just return a list 4407 // containing a single item that is the original string. 4408 l.clear(); 4409 l.add(s); 4410 return l; 4411 } 4412 } 4413 } 4414 finally 4415 { 4416 try 4417 { 4418 // This is technically not necessary in this case, but it's good form. 4419 reader.close(); 4420 } 4421 catch (final Exception e) 4422 { 4423 Debug.debugException(e); 4424 // This should never happen, and there's nothing we need to do even if 4425 // it does. 4426 } 4427 } 4428 } 4429 4430 4431 4432 /** 4433 * Creates a string that is a concatenation of all of the provided lines, with 4434 * a line break (using the end-of-line sequence appropriate for the underlying 4435 * platform) after each line (including the last line). 4436 * 4437 * @param lines The lines to include in the string. 4438 * 4439 * @return The string resulting from concatenating the provided lines with 4440 * line breaks. 4441 */ 4442 @NotNull() 4443 public static String linesToString(@Nullable final CharSequence... lines) 4444 { 4445 if (lines == null) 4446 { 4447 return ""; 4448 } 4449 4450 return linesToString(Arrays.asList(lines)); 4451 } 4452 4453 4454 4455 /** 4456 * Creates a string that is a concatenation of all of the provided lines, with 4457 * a line break (using the end-of-line sequence appropriate for the underlying 4458 * platform) after each line (including the last line). 4459 * 4460 * @param lines The lines to include in the string. 4461 * 4462 * @return The string resulting from concatenating the provided lines with 4463 * line breaks. 4464 */ 4465 @NotNull() 4466 public static String linesToString( 4467 @Nullable final List<? extends CharSequence> lines) 4468 { 4469 if (lines == null) 4470 { 4471 return ""; 4472 } 4473 4474 final StringBuilder buffer = new StringBuilder(); 4475 for (final CharSequence line : lines) 4476 { 4477 buffer.append(line); 4478 buffer.append(EOL); 4479 } 4480 4481 return buffer.toString(); 4482 } 4483 4484 4485 4486 /** 4487 * Constructs a {@code File} object from the provided path. 4488 * 4489 * @param baseDirectory The base directory to use as the starting point. 4490 * It must not be {@code null} and is expected to 4491 * represent a directory. 4492 * @param pathElements An array of the elements that make up the remainder 4493 * of the path to the specified file, in order from 4494 * paths closest to the root of the filesystem to 4495 * furthest away (that is, the first element should 4496 * represent a file or directory immediately below the 4497 * base directory, the second is one level below that, 4498 * and so on). It may be {@code null} or empty if the 4499 * base directory should be used. 4500 * 4501 * @return The constructed {@code File} object. 4502 */ 4503 @NotNull() 4504 public static File constructPath(@NotNull final File baseDirectory, 4505 @Nullable final String... pathElements) 4506 { 4507 Validator.ensureNotNull(baseDirectory); 4508 4509 File f = baseDirectory; 4510 if (pathElements != null) 4511 { 4512 for (final String pathElement : pathElements) 4513 { 4514 f = new File(f, pathElement); 4515 } 4516 } 4517 4518 return f; 4519 } 4520 4521 4522 4523 /** 4524 * Creates a byte array from the provided integer values. All of the integer 4525 * values must be between 0x00 and 0xFF (0 and 255), inclusive. Any bits 4526 * set outside of that range will be ignored. 4527 * 4528 * @param bytes The values to include in the byte array. 4529 * 4530 * @return A byte array with the provided set of values. 4531 */ 4532 @NotNull() 4533 public static byte[] byteArray(@Nullable final int... bytes) 4534 { 4535 if ((bytes == null) || (bytes.length == 0)) 4536 { 4537 return NO_BYTES; 4538 } 4539 4540 final byte[] byteArray = new byte[bytes.length]; 4541 for (int i=0; i < bytes.length; i++) 4542 { 4543 byteArray[i] = (byte) (bytes[i] & 0xFF); 4544 } 4545 4546 return byteArray; 4547 } 4548 4549 4550 4551 /** 4552 * Indicates whether the unit tests are currently running in this JVM. 4553 * 4554 * @return {@code true} if the unit tests are currently running, or 4555 * {@code false} if not. 4556 */ 4557 public static boolean isWithinUnitTest() 4558 { 4559 return IS_WITHIN_UNIT_TESTS; 4560 } 4561 4562 4563 4564 /** 4565 * Throws an {@code Error} or a {@code RuntimeException} based on the provided 4566 * {@code Throwable} object. This method will always throw something, 4567 * regardless of the provided {@code Throwable} object. 4568 * 4569 * @param throwable The {@code Throwable} object to use to create the 4570 * exception to throw. 4571 * 4572 * @throws Error If the provided {@code Throwable} object is an 4573 * {@code Error} instance, then that {@code Error} instance 4574 * will be re-thrown. 4575 * 4576 * @throws RuntimeException If the provided {@code Throwable} object is a 4577 * {@code RuntimeException} instance, then that 4578 * {@code RuntimeException} instance will be 4579 * re-thrown. Otherwise, it must be a checked 4580 * exception and that checked exception will be 4581 * re-thrown as a {@code RuntimeException}. 4582 */ 4583 public static void throwErrorOrRuntimeException( 4584 @NotNull final Throwable throwable) 4585 throws Error, RuntimeException 4586 { 4587 Validator.ensureNotNull(throwable); 4588 4589 if (throwable instanceof Error) 4590 { 4591 throw (Error) throwable; 4592 } 4593 else if (throwable instanceof RuntimeException) 4594 { 4595 throw (RuntimeException) throwable; 4596 } 4597 else 4598 { 4599 throw new RuntimeException(throwable); 4600 } 4601 } 4602 4603 4604 4605 /** 4606 * Re-throws the provided {@code Throwable} instance only if it is an 4607 * {@code Error} or a {@code RuntimeException} instance; otherwise, this 4608 * method will return without taking any action. 4609 * 4610 * @param throwable The {@code Throwable} object to examine and potentially 4611 * re-throw. 4612 * 4613 * @throws Error If the provided {@code Throwable} object is an 4614 * {@code Error} instance, then that {@code Error} instance 4615 * will be re-thrown. 4616 * 4617 * @throws RuntimeException If the provided {@code Throwable} object is a 4618 * {@code RuntimeException} instance, then that 4619 * {@code RuntimeException} instance will be 4620 * re-thrown. 4621 */ 4622 public static void rethrowIfErrorOrRuntimeException( 4623 @NotNull final Throwable throwable) 4624 throws Error, RuntimeException 4625 { 4626 if (throwable instanceof Error) 4627 { 4628 throw (Error) throwable; 4629 } 4630 else if (throwable instanceof RuntimeException) 4631 { 4632 throw (RuntimeException) throwable; 4633 } 4634 } 4635 4636 4637 4638 /** 4639 * Re-throws the provided {@code Throwable} instance only if it is an 4640 * {@code Error}; otherwise, this method will return without taking any 4641 * action. 4642 * 4643 * @param throwable The {@code Throwable} object to examine and potentially 4644 * re-throw. 4645 * 4646 * @throws Error If the provided {@code Throwable} object is an 4647 * {@code Error} instance, then that {@code Error} instance 4648 * will be re-thrown. 4649 */ 4650 public static void rethrowIfError(@NotNull final Throwable throwable) 4651 throws Error 4652 { 4653 if (throwable instanceof Error) 4654 { 4655 throw (Error) throwable; 4656 } 4657 } 4658 4659 4660 4661 /** 4662 * Computes the capacity that should be used for a map or a set with the 4663 * expected number of elements, which can help avoid the need to re-hash or 4664 * re-balance the map if too many items are added. This method bases its 4665 * computation on the default map load factor of 0.75. 4666 * 4667 * @param expectedItemCount The expected maximum number of items that will 4668 * be placed in the map or set. It must be greater 4669 * than or equal to zero. 4670 * 4671 * @return The capacity that should be used for a map or a set with the 4672 * expected number of elements 4673 */ 4674 public static int computeMapCapacity(final int expectedItemCount) 4675 { 4676 switch (expectedItemCount) 4677 { 4678 case 0: 4679 return 0; 4680 case 1: 4681 return 2; 4682 case 2: 4683 return 3; 4684 case 3: 4685 return 5; 4686 case 4: 4687 return 6; 4688 case 5: 4689 return 7; 4690 case 6: 4691 return 9; 4692 case 7: 4693 return 10; 4694 case 8: 4695 return 11; 4696 case 9: 4697 return 13; 4698 case 10: 4699 return 14; 4700 case 11: 4701 return 15; 4702 case 12: 4703 return 17; 4704 case 13: 4705 return 18; 4706 case 14: 4707 return 19; 4708 case 15: 4709 return 21; 4710 case 16: 4711 return 22; 4712 case 17: 4713 return 23; 4714 case 18: 4715 return 25; 4716 case 19: 4717 return 26; 4718 case 20: 4719 return 27; 4720 case 30: 4721 return 41; 4722 case 40: 4723 return 54; 4724 case 50: 4725 return 67; 4726 case 60: 4727 return 81; 4728 case 70: 4729 return 94; 4730 case 80: 4731 return 107; 4732 case 90: 4733 return 121; 4734 case 100: 4735 return 134; 4736 case 110: 4737 return 147; 4738 case 120: 4739 return 161; 4740 case 130: 4741 return 174; 4742 case 140: 4743 return 187; 4744 case 150: 4745 return 201; 4746 case 160: 4747 return 214; 4748 case 170: 4749 return 227; 4750 case 180: 4751 return 241; 4752 case 190: 4753 return 254; 4754 case 200: 4755 return 267; 4756 default: 4757 Validator.ensureTrue((expectedItemCount >= 0), 4758 "StaticUtils.computeMapOrSetCapacity.expectedItemCount must be " + 4759 "greater than or equal to zero."); 4760 4761 // NOTE: 536,870,911 is Integer.MAX_VALUE/4. If the value is larger 4762 // than that, then we'll fall back to using floating-point arithmetic 4763 // 4764 if (expectedItemCount > 536_870_911) 4765 { 4766 final int computedCapacity = ((int) (expectedItemCount / 0.75)) + 1; 4767 if (computedCapacity <= expectedItemCount) 4768 { 4769 // This suggests that the expected number of items is so big that 4770 // the computed capacity can't be adequately represented by an 4771 // integer. In that case, we'll just return the expected item 4772 // count and let the map or set get re-hashed/re-balanced if it 4773 // actually gets anywhere near that size. 4774 return expectedItemCount; 4775 } 4776 else 4777 { 4778 return computedCapacity; 4779 } 4780 } 4781 else 4782 { 4783 return ((expectedItemCount * 4) / 3) + 1; 4784 } 4785 } 4786 } 4787 4788 4789 4790 /** 4791 * Creates an unmodifiable set containing the provided items. The iteration 4792 * order of the provided items will be preserved. 4793 * 4794 * @param <T> The type of item to include in the set. 4795 * @param items The items to include in the set. It must not be 4796 * {@code null}, but may be empty. 4797 * 4798 * @return An unmodifiable set containing the provided items. 4799 */ 4800 @SafeVarargs() 4801 @SuppressWarnings("varargs") 4802 @NotNull() 4803 public static <T> Set<T> setOf(@NotNull final T... items) 4804 { 4805 return Collections.unmodifiableSet( 4806 new LinkedHashSet<>(Arrays.asList(items))); 4807 } 4808 4809 4810 4811 /** 4812 * Creates a {@code HashSet} containing the provided items. 4813 * 4814 * @param <T> The type of item to include in the set. 4815 * @param items The items to include in the set. It must not be 4816 * {@code null}, but may be empty. 4817 * 4818 * @return A {@code HashSet} containing the provided items. 4819 */ 4820 @SafeVarargs() 4821 @SuppressWarnings("varargs") 4822 @NotNull() 4823 public static <T> HashSet<T> hashSetOf(@NotNull final T... items) 4824 { 4825 return new HashSet<>(Arrays.asList(items)); 4826 } 4827 4828 4829 4830 /** 4831 * Creates a {@code LinkedHashSet} containing the provided items. 4832 * 4833 * @param <T> The type of item to include in the set. 4834 * @param items The items to include in the set. It must not be 4835 * {@code null}, but may be empty. 4836 * 4837 * @return A {@code LinkedHashSet} containing the provided items. 4838 */ 4839 @SafeVarargs() 4840 @SuppressWarnings("varargs") 4841 @NotNull() 4842 public static <T> LinkedHashSet<T> linkedHashSetOf(@NotNull final T... items) 4843 { 4844 return new LinkedHashSet<>(Arrays.asList(items)); 4845 } 4846 4847 4848 4849 /** 4850 * Creates a {@code TreeSet} containing the provided items. 4851 * 4852 * @param <T> The type of item to include in the set. 4853 * @param items The items to include in the set. It must not be 4854 * {@code null}, but may be empty. 4855 * 4856 * @return A {@code LinkedHashSet} containing the provided items. 4857 */ 4858 @SafeVarargs() 4859 @SuppressWarnings("varargs") 4860 @NotNull() 4861 public static <T> TreeSet<T> treeSetOf(@NotNull final T... items) 4862 { 4863 return new TreeSet<>(Arrays.asList(items)); 4864 } 4865 4866 4867 4868 /** 4869 * Creates an unmodifiable map containing the provided items. 4870 * 4871 * @param <K> The type for the map keys. 4872 * @param <V> The type for the map values. 4873 * @param key The only key to include in the map. 4874 * @param value The only value to include in the map. 4875 * 4876 * @return The unmodifiable map that was created. 4877 */ 4878 @NotNull() 4879 public static <K,V> Map<K,V> mapOf(@NotNull final K key, 4880 @NotNull final V value) 4881 { 4882 return Collections.singletonMap(key, value); 4883 } 4884 4885 4886 4887 /** 4888 * Creates an unmodifiable map containing the provided items. 4889 * 4890 * @param <K> The type for the map keys. 4891 * @param <V> The type for the map values. 4892 * @param key1 The first key to include in the map. 4893 * @param value1 The first value to include in the map. 4894 * @param key2 The second key to include in the map. 4895 * @param value2 The second value to include in the map. 4896 * 4897 * @return The unmodifiable map that was created. 4898 */ 4899 @NotNull() 4900 public static <K,V> Map<K,V> mapOf(@NotNull final K key1, 4901 @NotNull final V value1, 4902 @NotNull final K key2, 4903 @NotNull final V value2) 4904 { 4905 final LinkedHashMap<K,V> map = new LinkedHashMap<>(computeMapCapacity(2)); 4906 4907 map.put(key1, value1); 4908 map.put(key2, value2); 4909 4910 return Collections.unmodifiableMap(map); 4911 } 4912 4913 4914 4915 /** 4916 * Creates an unmodifiable map containing the provided items. 4917 * 4918 * @param <K> The type for the map keys. 4919 * @param <V> The type for the map values. 4920 * @param key1 The first key to include in the map. 4921 * @param value1 The first value to include in the map. 4922 * @param key2 The second key to include in the map. 4923 * @param value2 The second value to include in the map. 4924 * @param key3 The third key to include in the map. 4925 * @param value3 The third value to include in the map. 4926 * 4927 * @return The unmodifiable map that was created. 4928 */ 4929 @NotNull() 4930 public static <K,V> Map<K,V> mapOf(@NotNull final K key1, 4931 @NotNull final V value1, 4932 @NotNull final K key2, 4933 @NotNull final V value2, 4934 @NotNull final K key3, 4935 @NotNull final V value3) 4936 { 4937 final LinkedHashMap<K,V> map = new LinkedHashMap<>(computeMapCapacity(3)); 4938 4939 map.put(key1, value1); 4940 map.put(key2, value2); 4941 map.put(key3, value3); 4942 4943 return Collections.unmodifiableMap(map); 4944 } 4945 4946 4947 4948 /** 4949 * Creates an unmodifiable map containing the provided items. 4950 * 4951 * @param <K> The type for the map keys. 4952 * @param <V> The type for the map values. 4953 * @param key1 The first key to include in the map. 4954 * @param value1 The first value to include in the map. 4955 * @param key2 The second key to include in the map. 4956 * @param value2 The second value to include in the map. 4957 * @param key3 The third key to include in the map. 4958 * @param value3 The third value to include in the map. 4959 * @param key4 The fourth key to include in the map. 4960 * @param value4 The fourth value to include in the map. 4961 * 4962 * @return The unmodifiable map that was created. 4963 */ 4964 @NotNull() 4965 public static <K,V> Map<K,V> mapOf(@NotNull final K key1, 4966 @NotNull final V value1, 4967 @NotNull final K key2, 4968 @NotNull final V value2, 4969 @NotNull final K key3, 4970 @NotNull final V value3, 4971 @NotNull final K key4, 4972 @NotNull final V value4) 4973 { 4974 final LinkedHashMap<K,V> map = new LinkedHashMap<>(computeMapCapacity(4)); 4975 4976 map.put(key1, value1); 4977 map.put(key2, value2); 4978 map.put(key3, value3); 4979 map.put(key4, value4); 4980 4981 return Collections.unmodifiableMap(map); 4982 } 4983 4984 4985 4986 /** 4987 * Creates an unmodifiable map containing the provided items. 4988 * 4989 * @param <K> The type for the map keys. 4990 * @param <V> The type for the map values. 4991 * @param key1 The first key to include in the map. 4992 * @param value1 The first value to include in the map. 4993 * @param key2 The second key to include in the map. 4994 * @param value2 The second value to include in the map. 4995 * @param key3 The third key to include in the map. 4996 * @param value3 The third value to include in the map. 4997 * @param key4 The fourth key to include in the map. 4998 * @param value4 The fourth value to include in the map. 4999 * @param key5 The fifth key to include in the map. 5000 * @param value5 The fifth value to include in the map. 5001 * 5002 * @return The unmodifiable map that was created. 5003 */ 5004 @NotNull() 5005 public static <K,V> Map<K,V> mapOf(@NotNull final K key1, 5006 @NotNull final V value1, 5007 @NotNull final K key2, 5008 @NotNull final V value2, 5009 @NotNull final K key3, 5010 @NotNull final V value3, 5011 @NotNull final K key4, 5012 @NotNull final V value4, 5013 @NotNull final K key5, 5014 @NotNull final V value5) 5015 { 5016 final LinkedHashMap<K,V> map = new LinkedHashMap<>(computeMapCapacity(5)); 5017 5018 map.put(key1, value1); 5019 map.put(key2, value2); 5020 map.put(key3, value3); 5021 map.put(key4, value4); 5022 map.put(key5, value5); 5023 5024 return Collections.unmodifiableMap(map); 5025 } 5026 5027 5028 5029 /** 5030 * Creates an unmodifiable map containing the provided items. 5031 * 5032 * @param <K> The type for the map keys. 5033 * @param <V> The type for the map values. 5034 * @param key1 The first key to include in the map. 5035 * @param value1 The first value to include in the map. 5036 * @param key2 The second key to include in the map. 5037 * @param value2 The second value to include in the map. 5038 * @param key3 The third key to include in the map. 5039 * @param value3 The third value to include in the map. 5040 * @param key4 The fourth key to include in the map. 5041 * @param value4 The fourth value to include in the map. 5042 * @param key5 The fifth key to include in the map. 5043 * @param value5 The fifth value to include in the map. 5044 * @param key6 The sixth key to include in the map. 5045 * @param value6 The sixth value to include in the map. 5046 * 5047 * @return The unmodifiable map that was created. 5048 */ 5049 @NotNull() 5050 public static <K,V> Map<K,V> mapOf(@NotNull final K key1, 5051 @NotNull final V value1, 5052 @NotNull final K key2, 5053 @NotNull final V value2, 5054 @NotNull final K key3, 5055 @NotNull final V value3, 5056 @NotNull final K key4, 5057 @NotNull final V value4, 5058 @NotNull final K key5, 5059 @NotNull final V value5, 5060 @NotNull final K key6, 5061 @NotNull final V value6) 5062 { 5063 final LinkedHashMap<K,V> map = new LinkedHashMap<>(computeMapCapacity(6)); 5064 5065 map.put(key1, value1); 5066 map.put(key2, value2); 5067 map.put(key3, value3); 5068 map.put(key4, value4); 5069 map.put(key5, value5); 5070 map.put(key6, value6); 5071 5072 return Collections.unmodifiableMap(map); 5073 } 5074 5075 5076 5077 /** 5078 * Creates an unmodifiable map containing the provided items. 5079 * 5080 * @param <K> The type for the map keys. 5081 * @param <V> The type for the map values. 5082 * @param key1 The first key to include in the map. 5083 * @param value1 The first value to include in the map. 5084 * @param key2 The second key to include in the map. 5085 * @param value2 The second value to include in the map. 5086 * @param key3 The third key to include in the map. 5087 * @param value3 The third value to include in the map. 5088 * @param key4 The fourth key to include in the map. 5089 * @param value4 The fourth value to include in the map. 5090 * @param key5 The fifth key to include in the map. 5091 * @param value5 The fifth value to include in the map. 5092 * @param key6 The sixth key to include in the map. 5093 * @param value6 The sixth value to include in the map. 5094 * @param key7 The seventh key to include in the map. 5095 * @param value7 The seventh value to include in the map. 5096 * 5097 * @return The unmodifiable map that was created. 5098 */ 5099 @NotNull() 5100 public static <K,V> Map<K,V> mapOf(@NotNull final K key1, 5101 @NotNull final V value1, 5102 @NotNull final K key2, 5103 @NotNull final V value2, 5104 @NotNull final K key3, 5105 @NotNull final V value3, 5106 @NotNull final K key4, 5107 @NotNull final V value4, 5108 @NotNull final K key5, 5109 @NotNull final V value5, 5110 @NotNull final K key6, 5111 @NotNull final V value6, 5112 @NotNull final K key7, 5113 @NotNull final V value7) 5114 { 5115 final LinkedHashMap<K,V> map = new LinkedHashMap<>(computeMapCapacity(7)); 5116 5117 map.put(key1, value1); 5118 map.put(key2, value2); 5119 map.put(key3, value3); 5120 map.put(key4, value4); 5121 map.put(key5, value5); 5122 map.put(key6, value6); 5123 map.put(key7, value7); 5124 5125 return Collections.unmodifiableMap(map); 5126 } 5127 5128 5129 5130 /** 5131 * Creates an unmodifiable map containing the provided items. 5132 * 5133 * @param <K> The type for the map keys. 5134 * @param <V> The type for the map values. 5135 * @param key1 The first key to include in the map. 5136 * @param value1 The first value to include in the map. 5137 * @param key2 The second key to include in the map. 5138 * @param value2 The second value to include in the map. 5139 * @param key3 The third key to include in the map. 5140 * @param value3 The third value to include in the map. 5141 * @param key4 The fourth key to include in the map. 5142 * @param value4 The fourth value to include in the map. 5143 * @param key5 The fifth key to include in the map. 5144 * @param value5 The fifth value to include in the map. 5145 * @param key6 The sixth key to include in the map. 5146 * @param value6 The sixth value to include in the map. 5147 * @param key7 The seventh key to include in the map. 5148 * @param value7 The seventh value to include in the map. 5149 * @param key8 The eighth key to include in the map. 5150 * @param value8 The eighth value to include in the map. 5151 * 5152 * @return The unmodifiable map that was created. 5153 */ 5154 @NotNull() 5155 public static <K,V> Map<K,V> mapOf(@NotNull final K key1, 5156 @NotNull final V value1, 5157 @NotNull final K key2, 5158 @NotNull final V value2, 5159 @NotNull final K key3, 5160 @NotNull final V value3, 5161 @NotNull final K key4, 5162 @NotNull final V value4, 5163 @NotNull final K key5, 5164 @NotNull final V value5, 5165 @NotNull final K key6, 5166 @NotNull final V value6, 5167 @NotNull final K key7, 5168 @NotNull final V value7, 5169 @NotNull final K key8, 5170 @NotNull final V value8) 5171 { 5172 final LinkedHashMap<K,V> map = new LinkedHashMap<>(computeMapCapacity(8)); 5173 5174 map.put(key1, value1); 5175 map.put(key2, value2); 5176 map.put(key3, value3); 5177 map.put(key4, value4); 5178 map.put(key5, value5); 5179 map.put(key6, value6); 5180 map.put(key7, value7); 5181 map.put(key8, value8); 5182 5183 return Collections.unmodifiableMap(map); 5184 } 5185 5186 5187 5188 /** 5189 * Creates an unmodifiable map containing the provided items. 5190 * 5191 * @param <K> The type for the map keys. 5192 * @param <V> The type for the map values. 5193 * @param key1 The first key to include in the map. 5194 * @param value1 The first value to include in the map. 5195 * @param key2 The second key to include in the map. 5196 * @param value2 The second value to include in the map. 5197 * @param key3 The third key to include in the map. 5198 * @param value3 The third value to include in the map. 5199 * @param key4 The fourth key to include in the map. 5200 * @param value4 The fourth value to include in the map. 5201 * @param key5 The fifth key to include in the map. 5202 * @param value5 The fifth value to include in the map. 5203 * @param key6 The sixth key to include in the map. 5204 * @param value6 The sixth value to include in the map. 5205 * @param key7 The seventh key to include in the map. 5206 * @param value7 The seventh value to include in the map. 5207 * @param key8 The eighth key to include in the map. 5208 * @param value8 The eighth value to include in the map. 5209 * @param key9 The ninth key to include in the map. 5210 * @param value9 The ninth value to include in the map. 5211 * 5212 * @return The unmodifiable map that was created. 5213 */ 5214 @NotNull() 5215 public static <K,V> Map<K,V> mapOf(@NotNull final K key1, 5216 @NotNull final V value1, 5217 @NotNull final K key2, 5218 @NotNull final V value2, 5219 @NotNull final K key3, 5220 @NotNull final V value3, 5221 @NotNull final K key4, 5222 @NotNull final V value4, 5223 @NotNull final K key5, 5224 @NotNull final V value5, 5225 @NotNull final K key6, 5226 @NotNull final V value6, 5227 @NotNull final K key7, 5228 @NotNull final V value7, 5229 @NotNull final K key8, 5230 @NotNull final V value8, 5231 @NotNull final K key9, 5232 @NotNull final V value9) 5233 { 5234 final LinkedHashMap<K,V> map = new LinkedHashMap<>(computeMapCapacity(9)); 5235 5236 map.put(key1, value1); 5237 map.put(key2, value2); 5238 map.put(key3, value3); 5239 map.put(key4, value4); 5240 map.put(key5, value5); 5241 map.put(key6, value6); 5242 map.put(key7, value7); 5243 map.put(key8, value8); 5244 map.put(key9, value9); 5245 5246 return Collections.unmodifiableMap(map); 5247 } 5248 5249 5250 5251 /** 5252 * Creates an unmodifiable map containing the provided items. 5253 * 5254 * @param <K> The type for the map keys. 5255 * @param <V> The type for the map values. 5256 * @param key1 The first key to include in the map. 5257 * @param value1 The first value to include in the map. 5258 * @param key2 The second key to include in the map. 5259 * @param value2 The second value to include in the map. 5260 * @param key3 The third key to include in the map. 5261 * @param value3 The third value to include in the map. 5262 * @param key4 The fourth key to include in the map. 5263 * @param value4 The fourth value to include in the map. 5264 * @param key5 The fifth key to include in the map. 5265 * @param value5 The fifth value to include in the map. 5266 * @param key6 The sixth key to include in the map. 5267 * @param value6 The sixth value to include in the map. 5268 * @param key7 The seventh key to include in the map. 5269 * @param value7 The seventh value to include in the map. 5270 * @param key8 The eighth key to include in the map. 5271 * @param value8 The eighth value to include in the map. 5272 * @param key9 The ninth key to include in the map. 5273 * @param value9 The ninth value to include in the map. 5274 * @param key10 The tenth key to include in the map. 5275 * @param value10 The tenth value to include in the map. 5276 * 5277 * @return The unmodifiable map that was created. 5278 */ 5279 @NotNull() 5280 public static <K,V> Map<K,V> mapOf(@NotNull final K key1, 5281 @NotNull final V value1, 5282 @NotNull final K key2, 5283 @NotNull final V value2, 5284 @NotNull final K key3, 5285 @NotNull final V value3, 5286 @NotNull final K key4, 5287 @NotNull final V value4, 5288 @NotNull final K key5, 5289 @NotNull final V value5, 5290 @NotNull final K key6, 5291 @NotNull final V value6, 5292 @NotNull final K key7, 5293 @NotNull final V value7, 5294 @NotNull final K key8, 5295 @NotNull final V value8, 5296 @NotNull final K key9, 5297 @NotNull final V value9, 5298 @NotNull final K key10, 5299 @NotNull final V value10) 5300 { 5301 final LinkedHashMap<K,V> map = new LinkedHashMap<>(computeMapCapacity(10)); 5302 5303 map.put(key1, value1); 5304 map.put(key2, value2); 5305 map.put(key3, value3); 5306 map.put(key4, value4); 5307 map.put(key5, value5); 5308 map.put(key6, value6); 5309 map.put(key7, value7); 5310 map.put(key8, value8); 5311 map.put(key9, value9); 5312 map.put(key10, value10); 5313 5314 return Collections.unmodifiableMap(map); 5315 } 5316 5317 5318 5319 /** 5320 * Creates an unmodifiable map containing the provided items. The map entries 5321 * must have the same data type for keys and values. 5322 * 5323 * @param <T> The type for the map keys and values. 5324 * @param items The items to include in the map. If it is null or empty, 5325 * the map will be empty. If it is non-empty, then the number 5326 * of elements in the array must be a multiple of two. 5327 * Elements in even-numbered indexes will be the keys for the 5328 * map entries, while elements in odd-numbered indexes will be 5329 * the map values. 5330 * 5331 * @return The unmodifiable map that was created. 5332 */ 5333 @SafeVarargs() 5334 @NotNull() 5335 public static <T> Map<T,T> mapOf(@Nullable final T... items) 5336 { 5337 if ((items == null) || (items.length == 0)) 5338 { 5339 return Collections.emptyMap(); 5340 } 5341 5342 Validator.ensureTrue(((items.length % 2) == 0), 5343 "StaticUtils.mapOf.items must have an even number of elements"); 5344 5345 final int numEntries = items.length / 2; 5346 final LinkedHashMap<T,T> map = 5347 new LinkedHashMap<>(computeMapCapacity(numEntries)); 5348 for (int i=0; i < items.length; ) 5349 { 5350 map.put(items[i++], items[i++]); 5351 } 5352 5353 return Collections.unmodifiableMap(map); 5354 } 5355 5356 5357 5358 /** 5359 * Creates an unmodifiable map containing the provided items. 5360 * 5361 * @param <K> The type for the map keys. 5362 * @param <V> The type for the map values. 5363 * @param items The items to include in the map. 5364 * 5365 * @return The unmodifiable map that was created. 5366 */ 5367 @SafeVarargs() 5368 @NotNull() 5369 public static <K,V> Map<K,V> mapOfObjectPairs( 5370 @Nullable final ObjectPair<K,V>... items) 5371 { 5372 if ((items == null) || (items.length == 0)) 5373 { 5374 return Collections.emptyMap(); 5375 } 5376 5377 final LinkedHashMap<K,V> map = new LinkedHashMap<>( 5378 computeMapCapacity(items.length)); 5379 for (final ObjectPair<K,V> item : items) 5380 { 5381 map.put(item.getFirst(), item.getSecond()); 5382 } 5383 5384 return Collections.unmodifiableMap(map); 5385 } 5386 5387 5388 5389 /** 5390 * Attempts to determine all addresses associated with the local system, 5391 * including loopback addresses. 5392 * 5393 * @param nameResolver The name resolver to use to determine the local host 5394 * and loopback addresses. If this is {@code null}, 5395 * then the LDAP SDK's default name resolver will be 5396 * used. 5397 * 5398 * @return A set of the local addresses that were identified. 5399 */ 5400 @NotNull() 5401 public static Set<InetAddress> getAllLocalAddresses( 5402 @Nullable final NameResolver nameResolver) 5403 { 5404 return getAllLocalAddresses(nameResolver, true); 5405 } 5406 5407 5408 5409 /** 5410 * Attempts to determine all addresses associated with the local system, 5411 * optionally including loopback addresses. 5412 * 5413 * @param nameResolver The name resolver to use to determine the local 5414 * host and loopback addresses. If this is 5415 * {@code null}, then the LDAP SDK's default name 5416 * resolver will be used. 5417 * @param includeLoopback Indicates whether to include loopback addresses in 5418 * the set that is returned. 5419 * 5420 * @return A set of the local addresses that were identified. 5421 */ 5422 @NotNull() 5423 public static Set<InetAddress> getAllLocalAddresses( 5424 @Nullable final NameResolver nameResolver, 5425 final boolean includeLoopback) 5426 { 5427 final NameResolver resolver; 5428 if (nameResolver == null) 5429 { 5430 resolver = LDAPConnectionOptions.DEFAULT_NAME_RESOLVER; 5431 } 5432 else 5433 { 5434 resolver = nameResolver; 5435 } 5436 5437 final LinkedHashSet<InetAddress> localAddresses = 5438 new LinkedHashSet<>(computeMapCapacity(10)); 5439 5440 try 5441 { 5442 final InetAddress localHostAddress = resolver.getLocalHost(); 5443 if (includeLoopback || (! localHostAddress.isLoopbackAddress())) 5444 { 5445 localAddresses.add(localHostAddress); 5446 } 5447 } 5448 catch (final Exception e) 5449 { 5450 Debug.debugException(e); 5451 } 5452 5453 try 5454 { 5455 final Enumeration<NetworkInterface> networkInterfaces = 5456 NetworkInterface.getNetworkInterfaces(); 5457 while (networkInterfaces.hasMoreElements()) 5458 { 5459 final NetworkInterface networkInterface = 5460 networkInterfaces.nextElement(); 5461 if (includeLoopback || (! networkInterface.isLoopback())) 5462 { 5463 final Enumeration<InetAddress> interfaceAddresses = 5464 networkInterface.getInetAddresses(); 5465 while (interfaceAddresses.hasMoreElements()) 5466 { 5467 final InetAddress address = interfaceAddresses.nextElement(); 5468 if (includeLoopback || (! address.isLoopbackAddress())) 5469 { 5470 localAddresses.add(address); 5471 } 5472 } 5473 } 5474 } 5475 } 5476 catch (final Exception e) 5477 { 5478 Debug.debugException(e); 5479 } 5480 5481 if (includeLoopback) 5482 { 5483 try 5484 { 5485 localAddresses.add(resolver.getLoopbackAddress()); 5486 } 5487 catch (final Exception e) 5488 { 5489 Debug.debugException(e); 5490 } 5491 } 5492 5493 return Collections.unmodifiableSet(localAddresses); 5494 } 5495 5496 5497 5498 /** 5499 * Retrieves the canonical host name for the provided address, if it can be 5500 * resolved to a name. 5501 * 5502 * @param nameResolver The name resolver to use to obtain the canonical 5503 * host name. If this is {@code null}, then the LDAP 5504 * SDK's default name resolver will be used. 5505 * @param address The {@code InetAddress} for which to attempt to 5506 * obtain the canonical host name. 5507 * 5508 * @return The canonical host name for the provided address, or {@code null} 5509 * if it cannot be obtained (either because the attempt returns 5510 * {@code null}, which shouldn't happen, or because it matches the 5511 * IP address). 5512 */ 5513 @Nullable() 5514 public static String getCanonicalHostNameIfAvailable( 5515 @Nullable final NameResolver nameResolver, 5516 @NotNull final InetAddress address) 5517 { 5518 final NameResolver resolver; 5519 if (nameResolver == null) 5520 { 5521 resolver = LDAPConnectionOptions.DEFAULT_NAME_RESOLVER; 5522 } 5523 else 5524 { 5525 resolver = nameResolver; 5526 } 5527 5528 final String hostAddress = address.getHostAddress(); 5529 final String trimmedHostAddress = 5530 trimInterfaceNameFromHostAddress(hostAddress); 5531 5532 final String canonicalHostName = resolver.getCanonicalHostName(address); 5533 if ((canonicalHostName == null) || 5534 canonicalHostName.equalsIgnoreCase(hostAddress) || 5535 canonicalHostName.equalsIgnoreCase(trimmedHostAddress)) 5536 { 5537 return null; 5538 } 5539 5540 return canonicalHostName; 5541 } 5542 5543 5544 5545 /** 5546 * Retrieves the canonical host names for the provided set of 5547 * {@code InetAddress} objects. If any of the provided addresses cannot be 5548 * resolved to a canonical host name (in which case the attempt to get the 5549 * canonical host name will return its IP address), it will be excluded from 5550 * the returned set. 5551 * 5552 * @param nameResolver The name resolver to use to obtain the canonical 5553 * host names. If this is {@code null}, then the LDAP 5554 * SDK's default name resolver will be used. 5555 * @param addresses The set of addresses for which to obtain the 5556 * canonical host names. 5557 * 5558 * @return A set of the canonical host names that could be obtained from the 5559 * provided addresses. 5560 */ 5561 @NotNull() 5562 public static Set<String> getAvailableCanonicalHostNames( 5563 @Nullable final NameResolver nameResolver, 5564 @NotNull final Collection<InetAddress> addresses) 5565 { 5566 final NameResolver resolver; 5567 if (nameResolver == null) 5568 { 5569 resolver = LDAPConnectionOptions.DEFAULT_NAME_RESOLVER; 5570 } 5571 else 5572 { 5573 resolver = nameResolver; 5574 } 5575 5576 final Set<String> canonicalHostNames = 5577 new LinkedHashSet<>(computeMapCapacity(addresses.size())); 5578 for (final InetAddress address : addresses) 5579 { 5580 final String canonicalHostName = 5581 getCanonicalHostNameIfAvailable(resolver, address); 5582 if (canonicalHostName != null) 5583 { 5584 canonicalHostNames.add(canonicalHostName); 5585 } 5586 } 5587 5588 return Collections.unmodifiableSet(canonicalHostNames); 5589 } 5590 5591 5592 5593 /** 5594 * Retrieves a version of the provided host address with the interface name 5595 * stripped off. Java sometimes follows an IP address with a percent sign and 5596 * the interface name. If that interface name is present in the provided 5597 * host address, then this method will trim it off, leaving just the IP 5598 * address. If the provided host address does not include the interface name, 5599 * then the provided address will be returned as-is. 5600 * 5601 * @param hostAddress The host address to be trimmed. 5602 * 5603 * @return The provided host address without the interface name. 5604 */ 5605 @NotNull() 5606 public static String trimInterfaceNameFromHostAddress( 5607 @NotNull final String hostAddress) 5608 { 5609 final int percentPos = hostAddress.indexOf('%'); 5610 if (percentPos > 0) 5611 { 5612 return hostAddress.substring(0, percentPos); 5613 } 5614 else 5615 { 5616 return hostAddress; 5617 } 5618 } 5619 5620 5621 5622 /** 5623 * Indicates whether the provided address is marked as reserved in the IANA 5624 * IPv4 address space registry at 5625 * https://www.iana.org/assignments/ipv4-address-space/ipv4-address-space.txt 5626 * or the IPv6 address space registry at 5627 * https://www.iana.org/assignments/ipv6-address-space/ipv6-address-space.txt. 5628 * 5629 * @param address 5630 * The address for which to make the determination. It must 5631 * not be {@code null}, and it must be an IPv4 or IPv6 address. 5632 * @param includePrivateUseNetworkAddresses 5633 * Indicates whether to consider addresses in a private-use 5634 * network address range (including 10.0.0.0/8, 172.16.0.0/12, 5635 * 192.168.0.0/16, and fc00::/7) as reserved addresses. If this 5636 * is {@code true}, then this method will return {@code true} for 5637 * addresses in a private-use network range; if it is 5638 * {@code false}, then this method will return {@code false} for 5639 * addresses in those ranges. This does not have any effect for 5640 * addresses in other reserved address ranges. 5641 * 5642 * @return {@code true} if the provided address is in a reserved address 5643 * range, or {@code false} if not. 5644 */ 5645 public static boolean isIANAReservedIPAddress( 5646 @NotNull final InetAddress address, 5647 final boolean includePrivateUseNetworkAddresses) 5648 { 5649 if (address instanceof Inet4Address) 5650 { 5651 return isIANAReservedIPv4Address((Inet4Address) address, 5652 includePrivateUseNetworkAddresses); 5653 } 5654 else if (address instanceof Inet6Address) 5655 { 5656 return isIANAReservedIPv6Address((Inet6Address) address, 5657 includePrivateUseNetworkAddresses); 5658 } 5659 else 5660 { 5661 // It's an unrecognized address type. We have to assume it's not 5662 // reserved. 5663 return false; 5664 } 5665 } 5666 5667 5668 5669 /** 5670 * Indicates whether the provided address is marked as reserved in the IANA 5671 * IPv4 address space registry at 5672 * https://www.iana.org/assignments/ipv4-address-space/ipv4-address-space.txt. 5673 * This implementation is based on the version of the registry that was 5674 * updated on 2019-12-27. 5675 * 5676 * @param address 5677 * The IPv4 address for which to make the determination. It must 5678 * not be {@code null}, and it must be an IPv4 address. 5679 * @param includePrivateUseNetworkAddresses 5680 * Indicates whether to consider addresses in a private-use 5681 * network address range as reserved addresses. 5682 * 5683 * @return {@code true} if the provided address is in a reserved address 5684 * range, or {@code false} if not. 5685 */ 5686 public static boolean isIANAReservedIPv4Address( 5687 @NotNull final Inet4Address address, 5688 final boolean includePrivateUseNetworkAddresses) 5689 { 5690 final byte[] addressBytes = address.getAddress(); 5691 final int firstOctet = addressBytes[0] & 0xFF; 5692 final int secondOctet = addressBytes[1] & 0xFF; 5693 final int thirdOctet = addressBytes[2] & 0xFF; 5694 5695 switch (firstOctet) 5696 { 5697 // * Addresses 0.*.*.* are reserved for self-identification. 5698 case 0: 5699 5700 // * Addresses 127.*.*.* are reserved for loopback addresses. 5701 case 127: 5702 5703 // * Addresses 224.*.*.* through 239.*.*.* are reserved for multicast. 5704 case 224: 5705 case 225: 5706 case 226: 5707 case 227: 5708 case 228: 5709 case 229: 5710 case 230: 5711 case 231: 5712 case 232: 5713 case 233: 5714 case 234: 5715 case 235: 5716 case 236: 5717 case 237: 5718 case 238: 5719 case 239: 5720 5721 // * Addresses 240.*.*.* through 255.*.*.* are reserved for future use. 5722 case 240: 5723 case 241: 5724 case 242: 5725 case 243: 5726 case 244: 5727 case 245: 5728 case 246: 5729 case 247: 5730 case 248: 5731 case 249: 5732 case 250: 5733 case 251: 5734 case 252: 5735 case 253: 5736 case 254: 5737 case 255: 5738 return true; 5739 5740 // * Addresses 10.*.*.* are reserved for private-use networks. 5741 case 10: 5742 return includePrivateUseNetworkAddresses; 5743 5744 // * Addresses 100.64.0.0 through 100.127.255.255. are in the shared 5745 // address space range described in RFC 6598. 5746 case 100: // First octet 100 -- Partially reserved 5747 return ((secondOctet >= 64) && (secondOctet <= 127)); 5748 5749 // * Addresses 169.254.*.* are reserved for link-local addresses. 5750 case 169: 5751 return (secondOctet == 254); 5752 5753 // * Addresses 172.16.0.0 through 172.31.255.255 are reserved for 5754 // private-use networks. 5755 case 172: 5756 if ((secondOctet >= 16) && (secondOctet <= 31)) 5757 { 5758 return includePrivateUseNetworkAddresses; 5759 } 5760 else 5761 { 5762 return false; 5763 } 5764 5765 // * Addresses 192.0.0.* are reserved for IPv4 Special Purpose Address. 5766 // * Addresses 192.0.2.* are reserved for TEST-NET-1. 5767 // * Addresses 192.88.99.* are reserved for 6to4 Relay Anycast. 5768 // * Addresses 192.168.*.* are reserved for private-use networks. 5769 case 192: 5770 if (secondOctet == 0) 5771 { 5772 return ((thirdOctet == 0) || (thirdOctet == 2)); 5773 } 5774 else if (secondOctet == 88) 5775 { 5776 return (thirdOctet == 99); 5777 } 5778 else if (secondOctet == 168) 5779 { 5780 return includePrivateUseNetworkAddresses; 5781 } 5782 else 5783 { 5784 return false; 5785 } 5786 5787 // * Addresses 198.18.0.0 through 198.19.255.255 are reserved for Network 5788 // Interconnect Device Benchmark Testing. 5789 // * Addresses 198.51.100.* are reserved for TEST-NET-2. 5790 case 198: 5791 if ((secondOctet >= 18) && (secondOctet <= 19)) 5792 { 5793 return true; 5794 } 5795 else 5796 { 5797 return ((secondOctet == 51) && (thirdOctet == 100)); 5798 } 5799 5800 // * Addresses 203.0.113.* are reserved for TEST-NET-3. 5801 case 203: 5802 return ((secondOctet == 0) && (thirdOctet == 113)); 5803 5804 // All other addresses are not reserved. 5805 default: 5806 return false; 5807 } 5808 } 5809 5810 5811 5812 /** 5813 * Indicates whether the provided address is marked as reserved in the IANA 5814 * IPv6 address space registry at 5815 * https://www.iana.org/assignments/ipv6-address-space/ipv6-address-space.txt. 5816 * This implementation is based on the version of the registry that was 5817 * updated on 2019-09-13. 5818 * 5819 * @param address 5820 * The IPv4 address for which to make the determination. It must 5821 * not be {@code null}, and it must be an IPv6 address. 5822 * @param includePrivateUseNetworkAddresses 5823 * Indicates whether to consider addresses in a private-use 5824 * network address range as reserved addresses. 5825 * 5826 * @return {@code true} if the provided address is in a reserved address 5827 * range, or {@code false} if not. 5828 */ 5829 public static boolean isIANAReservedIPv6Address( 5830 @NotNull final Inet6Address address, 5831 final boolean includePrivateUseNetworkAddresses) 5832 { 5833 final byte[] addressBytes = address.getAddress(); 5834 final int firstOctet = addressBytes[0] & 0xFF; 5835 5836 // Addresses with a first octet between 0x20 and 0x3F are not reserved. 5837 if ((firstOctet >= 0x20) && (firstOctet <= 0x3F)) 5838 { 5839 return false; 5840 } 5841 5842 // Addresses with a first octet between 0xFC and 0xFD are reserved for 5843 // private-use networks. 5844 if ((firstOctet >= 0xFC) && (firstOctet <= 0xFD)) 5845 { 5846 return includePrivateUseNetworkAddresses; 5847 } 5848 5849 // All other addresses are reserved. 5850 return true; 5851 } 5852 5853 5854 5855 /** 5856 * Reads the bytes that comprise the specified file. 5857 * 5858 * @param path The path to the file to be read. 5859 * 5860 * @return The bytes that comprise the specified file. 5861 * 5862 * @throws IOException If a problem occurs while trying to read the file. 5863 */ 5864 @NotNull() 5865 public static byte[] readFileBytes(@NotNull final String path) 5866 throws IOException 5867 { 5868 return readFileBytes(new File(path)); 5869 } 5870 5871 5872 5873 /** 5874 * Reads the bytes that comprise the specified file. 5875 * 5876 * @param file The file to be read. 5877 * 5878 * @return The bytes that comprise the specified file. 5879 * 5880 * @throws IOException If a problem occurs while trying to read the file. 5881 */ 5882 @NotNull() 5883 public static byte[] readFileBytes(@NotNull final File file) 5884 throws IOException 5885 { 5886 final ByteStringBuffer buffer = new ByteStringBuffer((int) file.length()); 5887 buffer.readFrom(file); 5888 return buffer.toByteArray(); 5889 } 5890 5891 5892 5893 /** 5894 * Reads the contents of the specified file as a string. All line breaks in 5895 * the file will be preserved, with the possible exception of the one on the 5896 * last line. 5897 * 5898 * @param path The path to the file to be read. 5899 * @param includeFinalLineBreak Indicates whether the final line break (if 5900 * there is one) should be preserved. 5901 * 5902 * @return The contents of the specified file as a string. 5903 * 5904 * @throws IOException If a problem occurs while trying to read the file. 5905 */ 5906 @NotNull() 5907 public static String readFileAsString(@NotNull final String path, 5908 final boolean includeFinalLineBreak) 5909 throws IOException 5910 { 5911 return readFileAsString(new File(path), includeFinalLineBreak); 5912 } 5913 5914 5915 5916 /** 5917 * Reads the contents of the specified file as a string. All line breaks in 5918 * the file will be preserved, with the possible exception of the one on the 5919 * last line. 5920 * 5921 * @param file The file to be read. 5922 * @param includeFinalLineBreak Indicates whether the final line break (if 5923 * there is one) should be preserved. 5924 * 5925 * @return The contents of the specified file as a string. 5926 * 5927 * @throws IOException If a problem occurs while trying to read the file. 5928 */ 5929 @NotNull() 5930 public static String readFileAsString(@NotNull final File file, 5931 final boolean includeFinalLineBreak) 5932 throws IOException 5933 { 5934 final ByteStringBuffer buffer = new ByteStringBuffer((int) file.length()); 5935 buffer.readFrom(file); 5936 5937 if (! includeFinalLineBreak) 5938 { 5939 if (buffer.endsWith(EOL_BYTES_CR_LF)) 5940 { 5941 buffer.setLength(buffer.length() - EOL_BYTES_CR_LF.length); 5942 } 5943 else if (buffer.endsWith(EOL_BYTES_LF)) 5944 { 5945 buffer.setLength(buffer.length() - EOL_BYTES_LF.length); 5946 } 5947 } 5948 5949 return buffer.toString(); 5950 } 5951 5952 5953 5954 /** 5955 * Reads the lines that comprise the specified file. 5956 * 5957 * @param path The path to the file to be read. 5958 * 5959 * @return The lines that comprise the specified file. 5960 * 5961 * @throws IOException If a problem occurs while trying to read the file. 5962 */ 5963 @NotNull() 5964 public static List<String> readFileLines(@NotNull final String path) 5965 throws IOException 5966 { 5967 return readFileLines(new File(path)); 5968 } 5969 5970 5971 5972 /** 5973 * Reads the lines that comprise the specified file. 5974 * 5975 * @param file The file to be read. 5976 * 5977 * @return The lines that comprise the specified file. 5978 * 5979 * @throws IOException If a problem occurs while trying to read the file. 5980 */ 5981 @NotNull() 5982 public static List<String> readFileLines(@NotNull final File file) 5983 throws IOException 5984 { 5985 try (FileReader fileReader = new FileReader(file); 5986 BufferedReader bufferedReader = new BufferedReader(fileReader)) 5987 { 5988 final List<String> lines = new ArrayList<>(); 5989 while (true) 5990 { 5991 final String line = bufferedReader.readLine(); 5992 if (line == null) 5993 { 5994 return Collections.unmodifiableList(lines); 5995 } 5996 5997 lines.add(line); 5998 } 5999 } 6000 } 6001 6002 6003 6004 /** 6005 * Writes the provided bytes to the specified file. If the file already 6006 * exists, it will be overwritten. 6007 * 6008 * @param path The path to the file to be written. 6009 * @param bytes The bytes to be written to the specified file. 6010 * 6011 * @throws IOException If a problem is encountered while writing the file. 6012 */ 6013 public static void writeFile(@NotNull final String path, 6014 @NotNull final byte[] bytes) 6015 throws IOException 6016 { 6017 writeFile(new File(path), bytes); 6018 } 6019 6020 6021 6022 /** 6023 * Writes the provided bytes to the specified file. If the file already 6024 * exists, it will be overwritten. 6025 * 6026 * @param file The file to be written. 6027 * @param bytes The bytes to be written to the specified file. 6028 * 6029 * @throws IOException If a problem is encountered while writing the file. 6030 */ 6031 public static void writeFile(@NotNull final File file, 6032 @NotNull final byte[] bytes) 6033 throws IOException 6034 { 6035 try (FileOutputStream outputStream = new FileOutputStream(file)) 6036 { 6037 outputStream.write(bytes); 6038 } 6039 } 6040 6041 6042 6043 /** 6044 * Writes the provided lines to the specified file, with each followed by an 6045 * appropriate end-of-line marker for the current platform. If the file 6046 * already exists, it will be overwritten. 6047 * 6048 * @param path The path to the file to be written. 6049 * @param lines The lines to be written to the specified file. 6050 * 6051 * @throws IOException If a problem is encountered while writing the file. 6052 */ 6053 public static void writeFile(@NotNull final String path, 6054 @NotNull final CharSequence... lines) 6055 throws IOException 6056 { 6057 writeFile(new File(path), lines); 6058 } 6059 6060 6061 6062 /** 6063 * Writes the provided lines to the specified file, with each followed by an 6064 * appropriate end-of-line marker for the current platform. If the file 6065 * already exists, it will be overwritten. 6066 * 6067 * @param file The file to be written. 6068 * @param lines The lines to be written to the specified file. 6069 * 6070 * @throws IOException If a problem is encountered while writing the file. 6071 */ 6072 public static void writeFile(@NotNull final File file, 6073 @NotNull final CharSequence... lines) 6074 throws IOException 6075 { 6076 writeFile(file, toList(lines)); 6077 } 6078 6079 6080 6081 /** 6082 * Writes the provided lines to the specified file, with each followed by an 6083 * appropriate end-of-line marker for the current platform. If the file 6084 * already exists, it will be overwritten. 6085 * 6086 * @param path The path to the file to be written. 6087 * @param lines The lines to be written to the specified file. 6088 * 6089 * @throws IOException If a problem is encountered while writing the file. 6090 */ 6091 public static void writeFile(@NotNull final String path, 6092 @Nullable final List<? extends CharSequence> lines) 6093 throws IOException 6094 { 6095 writeFile(new File(path), lines); 6096 } 6097 6098 6099 6100 /** 6101 * Writes the provided lines to the specified file, with each followed by an 6102 * appropriate end-of-line marker for the current platform. If the file 6103 * already exists, it will be overwritten. 6104 * 6105 * @param file The file to be written. 6106 * @param lines The lines to be written to the specified file. 6107 * 6108 * @throws IOException If a problem is encountered while writing the file. 6109 */ 6110 public static void writeFile(@NotNull final File file, 6111 @Nullable final List<? extends CharSequence> lines) 6112 throws IOException 6113 { 6114 try (PrintWriter writer = new PrintWriter(file)) 6115 { 6116 if (lines != null) 6117 { 6118 for (final CharSequence line : lines) 6119 { 6120 writer.println(line); 6121 } 6122 } 6123 } 6124 } 6125 6126 6127 6128 /** 6129 * Retrieves a byte array with the specified number of randomly selected 6130 * bytes. 6131 * 6132 * @param numBytes The number of bytes of random data to retrieve. It must 6133 * be greater than or equal to zero. 6134 * @param secure Indicates whether to use a cryptographically secure 6135 * random number generator. 6136 * 6137 * @return A byte array with the specified number of randomly selected 6138 * bytes. 6139 */ 6140 @NotNull() 6141 public static byte[] randomBytes(final int numBytes, 6142 final boolean secure) 6143 { 6144 final byte[] byteArray = new byte[numBytes]; 6145 getThreadLocalRandom(secure).nextBytes(byteArray); 6146 return byteArray; 6147 } 6148 6149 6150 6151 /** 6152 * Retrieves a randomly selected integer between the given upper and lower 6153 * bounds. 6154 * 6155 * @param lowerBound The lowest value that may be selected at random. It 6156 * must be less than or equal to the upper bound. 6157 * @param upperBound The highest value that may be selected at random. It 6158 * must be greater than or equal to the lower bound. 6159 * @param secure Indicates whether to use a cryptographically secure 6160 * random number generator. 6161 * 6162 * @return A randomly selected integer between the given upper and lower 6163 * bounds. 6164 */ 6165 public static int randomInt(final int lowerBound, final int upperBound, 6166 final boolean secure) 6167 { 6168 // Compute the span of values. We need to use a long for this, because it's 6169 // possible that this could cause an integer overflow. 6170 final long span = 1L + upperBound - lowerBound; 6171 6172 6173 // Select a random long value between zero and that span. 6174 final long randomLong = getThreadLocalRandom(secure).nextLong(); 6175 final long positiveLong = randomLong & 0x7F_FF_FF_FF_FF_FF_FF_FFL; 6176 final long valueWithinSpan = positiveLong % span; 6177 return (int) (lowerBound + valueWithinSpan); 6178 } 6179 6180 6181 6182 /** 6183 * Retrieves a string containing the specified number of randomly selected 6184 * ASCII letters. It will contain only lowercase letters. 6185 * 6186 * @param length The number of letters to include in the string. It must be 6187 * greater than or equal to zero. 6188 * @param secure Indicates whether to use a cryptographically secure random 6189 * number generator. 6190 * 6191 * @return The randomly generated alphabetic string. 6192 */ 6193 @NotNull() 6194 public static String randomAlphabeticString(final int length, 6195 final boolean secure) 6196 { 6197 return randomString(length, LOWERCASE_LETTERS, secure); 6198 } 6199 6200 6201 6202 /** 6203 * Retrieves a string containing the specified number of randomly selected 6204 * ASCII numeric digits. 6205 * 6206 * @param length The number of digits to include in the string. It must be 6207 * greater than or equal to zero. 6208 * @param secure Indicates whether to use a cryptographically secure random 6209 * number generator. 6210 * 6211 * @return The randomly generated numeric string. 6212 */ 6213 @NotNull() 6214 public static String randomNumericString(final int length, 6215 final boolean secure) 6216 { 6217 return randomString(length, NUMERIC_DIGITS, secure); 6218 } 6219 6220 6221 6222 /** 6223 * Retrieves a string containing the specified number of randomly selected 6224 * ASCII alphanumeric characters. It may contain a mix of lowercase letters, 6225 * uppercase letters, and numeric digits. 6226 * 6227 * @param length The number of characters to include in the string. It must 6228 * be greater than or equal to zero. 6229 * @param secure Indicates whether to use a cryptographically secure random 6230 * number generator. 6231 * 6232 * @return The randomly generated alphanumeric string. 6233 */ 6234 @NotNull() 6235 public static String randomAlphanumericString(final int length, 6236 final boolean secure) 6237 { 6238 return randomString(length, ALPHANUMERIC_CHARACTERS, secure); 6239 } 6240 6241 6242 6243 /** 6244 * Retrieves a string containing the specified number of randomly selected 6245 * characters from the given set. 6246 * 6247 * @param length The number of characters to include in the string. 6248 * It must be greater than or equal to zero. 6249 * @param allowedChars The set of characters that are allowed to be included 6250 * in the string. It must not be {@code null} or 6251 * empty. 6252 * @param secure Indicates whether to use a cryptographically secure 6253 * random number generator. 6254 * 6255 * @return The randomly generated string. 6256 */ 6257 @NotNull() 6258 public static String randomString(final int length, 6259 @NotNull final char[] allowedChars, 6260 final boolean secure) 6261 { 6262 final StringBuilder buffer = new StringBuilder(length); 6263 6264 final Random random = getThreadLocalRandom(secure); 6265 for (int i=0; i < length; i++) 6266 { 6267 buffer.append(allowedChars[random.nextInt(allowedChars.length)]); 6268 } 6269 6270 return buffer.toString(); 6271 } 6272 6273 6274 6275 /** 6276 * Retrieves a thread-local random number generator. 6277 * 6278 * @param secure Indicates whether to retrieve a cryptographically secure 6279 * random number generator. 6280 * 6281 * @return The thread-local random number generator. 6282 */ 6283 @NotNull() 6284 private static Random getThreadLocalRandom(final boolean secure) 6285 { 6286 if (secure) 6287 { 6288 return ThreadLocalSecureRandom.get(); 6289 } 6290 else 6291 { 6292 return ThreadLocalRandom.get(); 6293 } 6294 } 6295}