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