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