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