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