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