001/*
002 * Copyright 2007-2014 UnboundID Corp.
003 * All Rights Reserved.
004 */
005/*
006 * Copyright (C) 2008-2014 UnboundID Corp.
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.text.DecimalFormat;
026import java.text.ParseException;
027import java.text.SimpleDateFormat;
028import java.util.ArrayList;
029import java.util.Arrays;
030import java.util.Collections;
031import java.util.Date;
032import java.util.HashSet;
033import java.util.Iterator;
034import java.util.List;
035import java.util.StringTokenizer;
036import java.util.TimeZone;
037import java.util.UUID;
038
039import com.unboundid.ldap.sdk.Control;
040import com.unboundid.ldap.sdk.Version;
041
042import static com.unboundid.util.Debug.*;
043import static com.unboundid.util.UtilityMessages.*;
044import static com.unboundid.util.Validator.*;
045
046
047
048/**
049 * This class provides a number of static utility functions.
050 */
051@ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE)
052public final class StaticUtils
053{
054  /**
055   * A pre-allocated byte array containing zero bytes.
056   */
057  public static final byte[] NO_BYTES = new byte[0];
058
059
060
061  /**
062   * A pre-allocated empty control array.
063   */
064  public static final Control[] NO_CONTROLS = new Control[0];
065
066
067
068  /**
069   * A pre-allocated empty string array.
070   */
071  public static final String[] NO_STRINGS = new String[0];
072
073
074
075  /**
076   * The end-of-line marker for this platform.
077   */
078  public static final String EOL = System.getProperty("line.separator");
079
080
081
082  /**
083   * A byte array containing the end-of-line marker for this platform.
084   */
085  public static final byte[] EOL_BYTES = getBytes(EOL);
086
087
088
089  /**
090   * The thread-local date formatter used to encode generalized time values.
091   */
092  private static final ThreadLocal<SimpleDateFormat> dateFormatters =
093       new ThreadLocal<SimpleDateFormat>();
094
095
096
097  /**
098   * Prevent this class from being instantiated.
099   */
100  private StaticUtils()
101  {
102    // No implementation is required.
103  }
104
105
106
107  /**
108   * Retrieves a UTF-8 byte representation of the provided string.
109   *
110   * @param  s  The string for which to retrieve the UTF-8 byte representation.
111   *
112   * @return  The UTF-8 byte representation for the provided string.
113   */
114  public static byte[] getBytes(final String s)
115  {
116    final int length;
117    if ((s == null) || ((length = s.length()) == 0))
118    {
119      return NO_BYTES;
120    }
121
122    final byte[] b = new byte[length];
123    for (int i=0; i < length; i++)
124    {
125      final char c = s.charAt(i);
126      if (c <= 0x7F)
127      {
128        b[i] = (byte) (c & 0x7F);
129      }
130      else
131      {
132        try
133        {
134          return s.getBytes("UTF-8");
135        }
136        catch (Exception e)
137        {
138          // This should never happen.
139          debugException(e);
140          return s.getBytes();
141        }
142      }
143    }
144
145    return b;
146  }
147
148
149
150  /**
151   * Indicates whether the contents of the provided byte array represent an
152   * ASCII string, which is also known in LDAP terminology as an IA5 string.
153   * An ASCII string is one that contains only bytes in which the most
154   * significant bit is zero.
155   *
156   * @param  b  The byte array for which to make the determination.  It must
157   *            not be {@code null}.
158   *
159   * @return  {@code true} if the contents of the provided array represent an
160   *          ASCII string, or {@code false} if not.
161   */
162  public static boolean isASCIIString(final byte[] b)
163  {
164    for (final byte by : b)
165    {
166      if ((by & 0x80) == 0x80)
167      {
168        return false;
169      }
170    }
171
172    return true;
173  }
174
175
176
177  /**
178   * Indicates whether the contents of the provided byte array represent a
179   * printable LDAP string, as per RFC 4517 section 3.2.  The only characters
180   * allowed in a printable string are:
181   * <UL>
182   *   <LI>All uppercase and lowercase ASCII alphabetic letters</LI>
183   *   <LI>All ASCII numeric digits</LI>
184   *   <LI>The following additional ASCII characters:  single quote, left
185   *       parenthesis, right parenthesis, plus, comma, hyphen, period, equals,
186   *       forward slash, colon, question mark, space.</LI>
187   * </UL>
188   * If the provided array contains anything other than the above characters
189   * (i.e., if the byte array contains any non-ASCII characters, or any ASCII
190   * control characters, or if it contains excluded ASCII characters like
191   * the exclamation point, double quote, octothorpe, dollar sign, etc.), then
192   * it will not be considered printable.
193   *
194   * @param  b  The byte array for which to make the determination.  It must
195   *            not be {@code null}.
196   *
197   * @return  {@code true} if the contents of the provided byte array represent
198   *          a printable LDAP string, or {@code false} if not.
199   */
200  public static boolean isPrintableString(final byte[] b)
201  {
202    for (final byte by : b)
203    {
204      if ((by & 0x80) == 0x80)
205      {
206        return false;
207      }
208
209      if (((by >= 'a') && (by <= 'z')) ||
210          ((by >= 'A') && (by <= 'Z')) ||
211          ((by >= '0') && (by <= '9')))
212      {
213        continue;
214      }
215
216      switch (by)
217      {
218        case '\'':
219        case '(':
220        case ')':
221        case '+':
222        case ',':
223        case '-':
224        case '.':
225        case '=':
226        case '/':
227        case ':':
228        case '?':
229        case ' ':
230          continue;
231        default:
232          return false;
233      }
234    }
235
236    return true;
237  }
238
239
240
241  /**
242   * Retrieves a string generated from the provided byte array using the UTF-8
243   * encoding.
244   *
245   * @param  b  The byte array for which to return the associated string.
246   *
247   * @return  The string generated from the provided byte array using the UTF-8
248   *          encoding.
249   */
250  public static String toUTF8String(final byte[] b)
251  {
252    try
253    {
254      return new String(b, "UTF-8");
255    }
256    catch (Exception e)
257    {
258      // This should never happen.
259      debugException(e);
260      return new String(b);
261    }
262  }
263
264
265
266  /**
267   * Retrieves a string generated from the specified portion of the provided
268   * byte array using the UTF-8 encoding.
269   *
270   * @param  b       The byte array for which to return the associated string.
271   * @param  offset  The offset in the array at which the value begins.
272   * @param  length  The number of bytes in the value to convert to a string.
273   *
274   * @return  The string generated from the specified portion of the provided
275   *          byte array using the UTF-8 encoding.
276   */
277  public static String toUTF8String(final byte[] b, final int offset,
278                                    final int length)
279  {
280    try
281    {
282      return new String(b, offset, length, "UTF-8");
283    }
284    catch (Exception e)
285    {
286      // This should never happen.
287      debugException(e);
288      return new String(b, offset, length);
289    }
290  }
291
292
293
294  /**
295   * Retrieves a version of the provided string with the first character
296   * converted to lowercase but all other characters retaining their original
297   * capitalization.
298   *
299   * @param  s  The string to be processed.
300   *
301   * @return  A version of the provided string with the first character
302   *          converted to lowercase but all other characters retaining their
303   *          original capitalization.
304   */
305  public static String toInitialLowerCase(final String s)
306  {
307    if ((s == null) || (s.length() == 0))
308    {
309      return s;
310    }
311    else if (s.length() == 1)
312    {
313      return toLowerCase(s);
314    }
315    else
316    {
317      final char c = s.charAt(0);
318      if (((c >= 'A') && (c <= 'Z')) || (c < ' ') || (c > '~'))
319      {
320        final StringBuilder b = new StringBuilder(s);
321        b.setCharAt(0, Character.toLowerCase(c));
322        return b.toString();
323      }
324      else
325      {
326        return s;
327      }
328    }
329  }
330
331
332
333  /**
334   * Retrieves an all-lowercase version of the provided string.
335   *
336   * @param  s  The string for which to retrieve the lowercase version.
337   *
338   * @return  An all-lowercase version of the provided string.
339   */
340  public static String toLowerCase(final String s)
341  {
342    if (s == null)
343    {
344      return null;
345    }
346
347    final int length = s.length();
348    final char[] charArray = s.toCharArray();
349    for (int i=0; i < length; i++)
350    {
351      switch (charArray[i])
352      {
353        case 'A':
354          charArray[i] = 'a';
355          break;
356        case 'B':
357          charArray[i] = 'b';
358          break;
359        case 'C':
360          charArray[i] = 'c';
361          break;
362        case 'D':
363          charArray[i] = 'd';
364          break;
365        case 'E':
366          charArray[i] = 'e';
367          break;
368        case 'F':
369          charArray[i] = 'f';
370          break;
371        case 'G':
372          charArray[i] = 'g';
373          break;
374        case 'H':
375          charArray[i] = 'h';
376          break;
377        case 'I':
378          charArray[i] = 'i';
379          break;
380        case 'J':
381          charArray[i] = 'j';
382          break;
383        case 'K':
384          charArray[i] = 'k';
385          break;
386        case 'L':
387          charArray[i] = 'l';
388          break;
389        case 'M':
390          charArray[i] = 'm';
391          break;
392        case 'N':
393          charArray[i] = 'n';
394          break;
395        case 'O':
396          charArray[i] = 'o';
397          break;
398        case 'P':
399          charArray[i] = 'p';
400          break;
401        case 'Q':
402          charArray[i] = 'q';
403          break;
404        case 'R':
405          charArray[i] = 'r';
406          break;
407        case 'S':
408          charArray[i] = 's';
409          break;
410        case 'T':
411          charArray[i] = 't';
412          break;
413        case 'U':
414          charArray[i] = 'u';
415          break;
416        case 'V':
417          charArray[i] = 'v';
418          break;
419        case 'W':
420          charArray[i] = 'w';
421          break;
422        case 'X':
423          charArray[i] = 'x';
424          break;
425        case 'Y':
426          charArray[i] = 'y';
427          break;
428        case 'Z':
429          charArray[i] = 'z';
430          break;
431        default:
432          if (charArray[i] > 0x7F)
433          {
434            return s.toLowerCase();
435          }
436          break;
437      }
438    }
439
440    return new String(charArray);
441  }
442
443
444
445  /**
446   * Indicates whether the provided character is a valid hexadecimal digit.
447   *
448   * @param  c  The character for which to make the determination.
449   *
450   * @return  {@code true} if the provided character does represent a valid
451   *          hexadecimal digit, or {@code false} if not.
452   */
453  public static boolean isHex(final char c)
454  {
455    switch (c)
456    {
457      case '0':
458      case '1':
459      case '2':
460      case '3':
461      case '4':
462      case '5':
463      case '6':
464      case '7':
465      case '8':
466      case '9':
467      case 'a':
468      case 'A':
469      case 'b':
470      case 'B':
471      case 'c':
472      case 'C':
473      case 'd':
474      case 'D':
475      case 'e':
476      case 'E':
477      case 'f':
478      case 'F':
479        return true;
480
481      default:
482        return false;
483    }
484  }
485
486
487
488  /**
489   * Retrieves a hexadecimal representation of the provided byte.
490   *
491   * @param  b  The byte to encode as hexadecimal.
492   *
493   * @return  A string containing the hexadecimal representation of the provided
494   *          byte.
495   */
496  public static String toHex(final byte b)
497  {
498    final StringBuilder buffer = new StringBuilder(2);
499    toHex(b, buffer);
500    return buffer.toString();
501  }
502
503
504
505  /**
506   * Appends a hexadecimal representation of the provided byte to the given
507   * buffer.
508   *
509   * @param  b       The byte to encode as hexadecimal.
510   * @param  buffer  The buffer to which the hexadecimal representation is to be
511   *                 appended.
512   */
513  public static void toHex(final byte b, final StringBuilder buffer)
514  {
515    switch (b & 0xF0)
516    {
517      case 0x00:
518        buffer.append('0');
519        break;
520      case 0x10:
521        buffer.append('1');
522        break;
523      case 0x20:
524        buffer.append('2');
525        break;
526      case 0x30:
527        buffer.append('3');
528        break;
529      case 0x40:
530        buffer.append('4');
531        break;
532      case 0x50:
533        buffer.append('5');
534        break;
535      case 0x60:
536        buffer.append('6');
537        break;
538      case 0x70:
539        buffer.append('7');
540        break;
541      case 0x80:
542        buffer.append('8');
543        break;
544      case 0x90:
545        buffer.append('9');
546        break;
547      case 0xA0:
548        buffer.append('a');
549        break;
550      case 0xB0:
551        buffer.append('b');
552        break;
553      case 0xC0:
554        buffer.append('c');
555        break;
556      case 0xD0:
557        buffer.append('d');
558        break;
559      case 0xE0:
560        buffer.append('e');
561        break;
562      case 0xF0:
563        buffer.append('f');
564        break;
565    }
566
567    switch (b & 0x0F)
568    {
569      case 0x00:
570        buffer.append('0');
571        break;
572      case 0x01:
573        buffer.append('1');
574        break;
575      case 0x02:
576        buffer.append('2');
577        break;
578      case 0x03:
579        buffer.append('3');
580        break;
581      case 0x04:
582        buffer.append('4');
583        break;
584      case 0x05:
585        buffer.append('5');
586        break;
587      case 0x06:
588        buffer.append('6');
589        break;
590      case 0x07:
591        buffer.append('7');
592        break;
593      case 0x08:
594        buffer.append('8');
595        break;
596      case 0x09:
597        buffer.append('9');
598        break;
599      case 0x0A:
600        buffer.append('a');
601        break;
602      case 0x0B:
603        buffer.append('b');
604        break;
605      case 0x0C:
606        buffer.append('c');
607        break;
608      case 0x0D:
609        buffer.append('d');
610        break;
611      case 0x0E:
612        buffer.append('e');
613        break;
614      case 0x0F:
615        buffer.append('f');
616        break;
617    }
618  }
619
620
621
622  /**
623   * Retrieves a hexadecimal representation of the contents of the provided byte
624   * array.  No delimiter character will be inserted between the hexadecimal
625   * digits for each byte.
626   *
627   * @param  b  The byte array to be represented as a hexadecimal string.  It
628   *            must not be {@code null}.
629   *
630   * @return  A string containing a hexadecimal representation of the contents
631   *          of the provided byte array.
632   */
633  public static String toHex(final byte[] b)
634  {
635    ensureNotNull(b);
636
637    final StringBuilder buffer = new StringBuilder(2 * b.length);
638    toHex(b, buffer);
639    return buffer.toString();
640  }
641
642
643
644  /**
645   * Retrieves a hexadecimal representation of the contents of the provided byte
646   * array.  No delimiter character will be inserted between the hexadecimal
647   * digits for each byte.
648   *
649   * @param  b       The byte array to be represented as a hexadecimal string.
650   *                 It must not be {@code null}.
651   * @param  buffer  A buffer to which the hexadecimal representation of the
652   *                 contents of the provided byte array should be appended.
653   */
654  public static void toHex(final byte[] b, final StringBuilder buffer)
655  {
656    toHex(b, null, buffer);
657  }
658
659
660
661  /**
662   * Retrieves a hexadecimal representation of the contents of the provided byte
663   * array.  No delimiter character will be inserted between the hexadecimal
664   * digits for each byte.
665   *
666   * @param  b          The byte array to be represented as a hexadecimal
667   *                    string.  It must not be {@code null}.
668   * @param  delimiter  A delimiter to be inserted between bytes.  It may be
669   *                    {@code null} if no delimiter should be used.
670   * @param  buffer     A buffer to which the hexadecimal representation of the
671   *                    contents of the provided byte array should be appended.
672   */
673  public static void toHex(final byte[] b, final String delimiter,
674                           final StringBuilder buffer)
675  {
676    boolean first = true;
677    for (final byte bt : b)
678    {
679      if (first)
680      {
681        first = false;
682      }
683      else if (delimiter != null)
684      {
685        buffer.append(delimiter);
686      }
687
688      toHex(bt, buffer);
689    }
690  }
691
692
693
694  /**
695   * Retrieves a hex-encoded representation of the contents of the provided
696   * array, along with an ASCII representation of its contents next to it.  The
697   * output will be split across multiple lines, with up to sixteen bytes per
698   * line.  For each of those sixteen bytes, the two-digit hex representation
699   * will be appended followed by a space.  Then, the ASCII representation of
700   * those sixteen bytes will follow that, with a space used in place of any
701   * byte that does not have an ASCII representation.
702   *
703   * @param  array   The array whose contents should be processed.
704   * @param  indent  The number of spaces to insert on each line prior to the
705   *                 first hex byte.
706   *
707   * @return  A hex-encoded representation of the contents of the provided
708   *          array, along with an ASCII representation of its contents next to
709   *          it.
710   */
711  public static String toHexPlusASCII(final byte[] array, final int indent)
712  {
713    final StringBuilder buffer = new StringBuilder();
714    toHexPlusASCII(array, indent, buffer);
715    return buffer.toString();
716  }
717
718
719
720  /**
721   * Appends a hex-encoded representation of the contents of the provided array
722   * to the given buffer, along with an ASCII representation of its contents
723   * next to it.  The output will be split across multiple lines, with up to
724   * sixteen bytes per line.  For each of those sixteen bytes, the two-digit hex
725   * representation will be appended followed by a space.  Then, the ASCII
726   * representation of those sixteen bytes will follow that, with a space used
727   * in place of any byte that does not have an ASCII representation.
728   *
729   * @param  array   The array whose contents should be processed.
730   * @param  indent  The number of spaces to insert on each line prior to the
731   *                 first hex byte.
732   * @param  buffer  The buffer to which the encoded data should be appended.
733   */
734  public static void toHexPlusASCII(final byte[] array, final int indent,
735                                    final StringBuilder buffer)
736  {
737    if ((array == null) || (array.length == 0))
738    {
739      return;
740    }
741
742    for (int i=0; i < indent; i++)
743    {
744      buffer.append(' ');
745    }
746
747    int pos = 0;
748    int startPos = 0;
749    while (pos < array.length)
750    {
751      toHex(array[pos++], buffer);
752      buffer.append(' ');
753
754      if ((pos % 16) == 0)
755      {
756        buffer.append("  ");
757        for (int i=startPos; i < pos; i++)
758        {
759          if ((array[i] < ' ') || (array[i] > '~'))
760          {
761            buffer.append(' ');
762          }
763          else
764          {
765            buffer.append((char) array[i]);
766          }
767        }
768        buffer.append(EOL);
769        startPos = pos;
770
771        if (pos < array.length)
772        {
773          for (int i=0; i < indent; i++)
774          {
775            buffer.append(' ');
776          }
777        }
778      }
779    }
780
781    // If the last line isn't complete yet, then finish it off.
782    if ((array.length % 16) != 0)
783    {
784      final int missingBytes = (16 - (array.length % 16));
785      if (missingBytes > 0)
786      {
787        for (int i=0; i < missingBytes; i++)
788        {
789          buffer.append("   ");
790        }
791        buffer.append("  ");
792        for (int i=startPos; i < array.length; i++)
793        {
794          if ((array[i] < ' ') || (array[i] > '~'))
795          {
796            buffer.append(' ');
797          }
798          else
799          {
800            buffer.append((char) array[i]);
801          }
802        }
803        buffer.append(EOL);
804      }
805    }
806  }
807
808
809
810  /**
811   * Appends a hex-encoded representation of the provided character to the given
812   * buffer.  Each byte of the hex-encoded representation will be prefixed with
813   * a backslash.
814   *
815   * @param  c       The character to be encoded.
816   * @param  buffer  The buffer to which the hex-encoded representation should
817   *                 be appended.
818   */
819  public static void hexEncode(final char c, final StringBuilder buffer)
820  {
821    final byte[] charBytes;
822    if (c <= 0x7F)
823    {
824      charBytes = new byte[] { (byte) (c & 0x7F) };
825    }
826    else
827    {
828      charBytes = getBytes(String.valueOf(c));
829    }
830
831    for (final byte b : charBytes)
832    {
833      buffer.append('\\');
834      toHex(b, buffer);
835    }
836  }
837
838
839
840  /**
841   * Retrieves a single-line string representation of the stack trace for the
842   * provided {@code Throwable}.  It will include the unqualified name of the
843   * {@code Throwable} class, a list of source files and line numbers (if
844   * available) for the stack trace, and will also include the stack trace for
845   * the cause (if present).
846   *
847   * @param  t  The {@code Throwable} for which to retrieve the stack trace.
848   *
849   * @return  A single-line string representation of the stack trace for the
850   *          provided {@code Throwable}.
851   */
852  public static String getStackTrace(final Throwable t)
853  {
854    final StringBuilder buffer = new StringBuilder();
855    getStackTrace(t, buffer);
856    return buffer.toString();
857  }
858
859
860
861  /**
862   * Appends a single-line string representation of the stack trace for the
863   * provided {@code Throwable} to the given buffer.  It will include the
864   * unqualified name of the {@code Throwable} class, a list of source files and
865   * line numbers (if available) for the stack trace, and will also include the
866   * stack trace for the cause (if present).
867   *
868   * @param  t       The {@code Throwable} for which to retrieve the stack
869   *                 trace.
870   * @param  buffer  The buffer to which the information should be appended.
871   */
872  public static void getStackTrace(final Throwable t,
873                                   final StringBuilder buffer)
874  {
875    buffer.append(getUnqualifiedClassName(t.getClass()));
876    buffer.append('(');
877
878    final String message = t.getMessage();
879    if (message != null)
880    {
881      buffer.append("message='");
882      buffer.append(message);
883      buffer.append("', ");
884    }
885
886    buffer.append("trace='");
887    getStackTrace(t.getStackTrace(), buffer);
888    buffer.append('\'');
889
890    final Throwable cause = t.getCause();
891    if (cause != null)
892    {
893      buffer.append(", cause=");
894      getStackTrace(cause, buffer);
895    }
896    buffer.append(", revision=");
897    buffer.append(Version.REVISION_NUMBER);
898    buffer.append(')');
899  }
900
901
902
903  /**
904   * Returns a single-line string representation of the stack trace.  It will
905   * include a list of source files and line numbers (if available) for the
906   * stack trace.
907   *
908   * @param  elements  The stack trace.
909   *
910   * @return  A single-line string representation of the stack trace.
911   */
912  public static String getStackTrace(final StackTraceElement[] elements)
913  {
914    final StringBuilder buffer = new StringBuilder();
915    getStackTrace(elements, buffer);
916    return buffer.toString();
917  }
918
919
920
921  /**
922   * Appends a single-line string representation of the stack trace to the given
923   * buffer.  It will include a list of source files and line numbers
924   * (if available) for the stack trace.
925   *
926   * @param  elements  The stack trace.
927   * @param  buffer  The buffer to which the information should be appended.
928   */
929  public static void getStackTrace(final StackTraceElement[] elements,
930                                   final StringBuilder buffer)
931  {
932    for (int i=0; i < elements.length; i++)
933    {
934      if (i > 0)
935      {
936        buffer.append(" / ");
937      }
938
939      buffer.append(elements[i].getMethodName());
940      buffer.append('(');
941      buffer.append(elements[i].getFileName());
942
943      final int lineNumber = elements[i].getLineNumber();
944      if (lineNumber > 0)
945      {
946        buffer.append(':');
947        buffer.append(lineNumber);
948      }
949      buffer.append(')');
950    }
951  }
952
953
954
955  /**
956   * Retrieves a string representation of the provided {@code Throwable} object
957   * suitable for use in a message.  For runtime exceptions and errors, then a
958   * full stack trace for the exception will be provided.  For exception types
959   * defined in the LDAP SDK, then its {@code getExceptionMessage} method will
960   * be used to get the string representation.  For all other types of
961   * exceptions, then the standard string representation will be used.
962   * <BR><BR>
963   * For all types of exceptions, the message will also include the cause if one
964   * exists.
965   *
966   * @param  t  The {@code Throwable} for which to generate the exception
967   *            message.
968   *
969   * @return  A string representation of the provided {@code Throwable} object
970   *          suitable for use in a message.
971   */
972  public static String getExceptionMessage(final Throwable t)
973  {
974    if (t == null)
975    {
976      return ERR_NO_EXCEPTION.get();
977    }
978
979    final StringBuilder buffer = new StringBuilder();
980    if (t instanceof LDAPSDKException)
981    {
982      buffer.append(((LDAPSDKException) t).getExceptionMessage());
983    }
984    else if (t instanceof LDAPSDKRuntimeException)
985    {
986      buffer.append(((LDAPSDKRuntimeException) t).getExceptionMessage());
987    }
988    if ((t instanceof RuntimeException) || (t instanceof Error))
989    {
990      return getStackTrace(t);
991    }
992    else
993    {
994      buffer.append(String.valueOf(t));
995    }
996
997    final Throwable cause = t.getCause();
998    if (cause != null)
999    {
1000      buffer.append(" caused by ");
1001      buffer.append(getExceptionMessage(cause));
1002    }
1003
1004    return buffer.toString();
1005  }
1006
1007
1008
1009  /**
1010   * Retrieves the unqualified name (i.e., the name without package information)
1011   * for the provided class.
1012   *
1013   * @param  c  The class for which to retrieve the unqualified name.
1014   *
1015   * @return  The unqualified name for the provided class.
1016   */
1017  public static String getUnqualifiedClassName(final Class<?> c)
1018  {
1019    final String className     = c.getName();
1020    final int    lastPeriodPos = className.lastIndexOf('.');
1021
1022    if (lastPeriodPos > 0)
1023    {
1024      return className.substring(lastPeriodPos+1);
1025    }
1026    else
1027    {
1028      return className;
1029    }
1030  }
1031
1032
1033
1034  /**
1035   * Encodes the provided date in generalized time format.
1036   *
1037   * @param  d  The date to be encoded in generalized time format.
1038   *
1039   * @return  The generalized time representation of the provided date.
1040   */
1041  public static String encodeGeneralizedTime(final Date d)
1042  {
1043    SimpleDateFormat dateFormat = dateFormatters.get();
1044    if (dateFormat == null)
1045    {
1046      dateFormat = new SimpleDateFormat("yyyyMMddHHmmss.SSS'Z'");
1047      dateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
1048      dateFormatters.set(dateFormat);
1049    }
1050
1051    return dateFormat.format(d);
1052  }
1053
1054
1055
1056  /**
1057   * Decodes the provided string as a timestamp in generalized time format.
1058   *
1059   * @param  t  The timestamp to be decoded.  It must not be {@code null}.
1060   *
1061   * @return  The {@code Date} object decoded from the provided timestamp.
1062   *
1063   * @throws  ParseException  If the provided string could not be decoded as a
1064   *                          timestamp in generalized time format.
1065   */
1066  public static Date decodeGeneralizedTime(final String t)
1067         throws ParseException
1068  {
1069    ensureNotNull(t);
1070
1071    // Extract the time zone information from the end of the value.
1072    int tzPos;
1073    final TimeZone tz;
1074    if (t.endsWith("Z"))
1075    {
1076      tz = TimeZone.getTimeZone("UTC");
1077      tzPos = t.length() - 1;
1078    }
1079    else
1080    {
1081      tzPos = t.lastIndexOf('-');
1082      if (tzPos < 0)
1083      {
1084        tzPos = t.lastIndexOf('+');
1085        if (tzPos < 0)
1086        {
1087          throw new ParseException(ERR_GENTIME_DECODE_CANNOT_PARSE_TZ.get(t),
1088                                   0);
1089        }
1090      }
1091
1092      tz = TimeZone.getTimeZone("GMT" + t.substring(tzPos));
1093      if (tz.getRawOffset() == 0)
1094      {
1095        // This is the default time zone that will be returned if the value
1096        // cannot be parsed.  If it's valid, then it will end in "+0000" or
1097        // "-0000".  Otherwise, it's invalid and GMT was just a fallback.
1098        if (! (t.endsWith("+0000") || t.endsWith("-0000")))
1099        {
1100          throw new ParseException(ERR_GENTIME_DECODE_CANNOT_PARSE_TZ.get(t),
1101                                   tzPos);
1102        }
1103      }
1104    }
1105
1106
1107    // See if the timestamp has a sub-second portion.  Note that if there is a
1108    // sub-second portion, then we may need to massage the value so that there
1109    // are exactly three sub-second characters so that it can be interpreted as
1110    // milliseconds.
1111    final String subSecFormatStr;
1112    final String trimmedTimestamp;
1113    int periodPos = t.lastIndexOf('.', tzPos);
1114    if (periodPos > 0)
1115    {
1116      final int subSecondLength = tzPos - periodPos - 1;
1117      switch (subSecondLength)
1118      {
1119        case 0:
1120          subSecFormatStr  = "";
1121          trimmedTimestamp = t.substring(0, periodPos);
1122          break;
1123        case 1:
1124          subSecFormatStr  = ".SSS";
1125          trimmedTimestamp = t.substring(0, (periodPos+2)) + "00";
1126          break;
1127        case 2:
1128          subSecFormatStr  = ".SSS";
1129          trimmedTimestamp = t.substring(0, (periodPos+3)) + '0';
1130          break;
1131        default:
1132          subSecFormatStr  = ".SSS";
1133          trimmedTimestamp = t.substring(0, periodPos+4);
1134          break;
1135      }
1136    }
1137    else
1138    {
1139      subSecFormatStr  = "";
1140      periodPos        = tzPos;
1141      trimmedTimestamp = t.substring(0, tzPos);
1142    }
1143
1144
1145    // Look at where the period is (or would be if it existed) to see how many
1146    // characters are in the integer portion.  This will give us what we need
1147    // for the rest of the format string.
1148    final String formatStr;
1149    switch (periodPos)
1150    {
1151      case 10:
1152        formatStr = "yyyyMMddHH" + subSecFormatStr;
1153        break;
1154      case 12:
1155        formatStr = "yyyyMMddHHmm" + subSecFormatStr;
1156        break;
1157      case 14:
1158        formatStr = "yyyyMMddHHmmss" + subSecFormatStr;
1159        break;
1160      default:
1161        throw new ParseException(ERR_GENTIME_CANNOT_PARSE_INVALID_LENGTH.get(t),
1162                                 periodPos);
1163    }
1164
1165
1166    // We should finally be able to create an appropriate date format object
1167    // to parse the trimmed version of the timestamp.
1168    final SimpleDateFormat dateFormat = new SimpleDateFormat(formatStr);
1169    dateFormat.setTimeZone(tz);
1170    dateFormat.setLenient(false);
1171    return dateFormat.parse(trimmedTimestamp);
1172  }
1173
1174
1175
1176  /**
1177   * Trims only leading spaces from the provided string, leaving any trailing
1178   * spaces intact.
1179   *
1180   * @param  s  The string to be processed.  It must not be {@code null}.
1181   *
1182   * @return  The original string if no trimming was required, or a new string
1183   *          without leading spaces if the provided string had one or more.  It
1184   *          may be an empty string if the provided string was an empty string
1185   *          or contained only spaces.
1186   */
1187  public static String trimLeading(final String s)
1188  {
1189    ensureNotNull(s);
1190
1191    int nonSpacePos = 0;
1192    final int length = s.length();
1193    while ((nonSpacePos < length) && (s.charAt(nonSpacePos) == ' '))
1194    {
1195      nonSpacePos++;
1196    }
1197
1198    if (nonSpacePos == 0)
1199    {
1200      // There were no leading spaces.
1201      return s;
1202    }
1203    else if (nonSpacePos >= length)
1204    {
1205      // There were no non-space characters.
1206      return "";
1207    }
1208    else
1209    {
1210      // There were leading spaces, so return the string without them.
1211      return s.substring(nonSpacePos, length);
1212    }
1213  }
1214
1215
1216
1217  /**
1218   * Trims only trailing spaces from the provided string, leaving any leading
1219   * spaces intact.
1220   *
1221   * @param  s  The string to be processed.  It must not be {@code null}.
1222   *
1223   * @return  The original string if no trimming was required, or a new string
1224   *          without trailing spaces if the provided string had one or more.
1225   *          It may be an empty string if the provided string was an empty
1226   *          string or contained only spaces.
1227   */
1228  public static String trimTrailing(final String s)
1229  {
1230    ensureNotNull(s);
1231
1232    final int lastPos = s.length() - 1;
1233    int nonSpacePos = lastPos;
1234    while ((nonSpacePos >= 0) && (s.charAt(nonSpacePos) == ' '))
1235    {
1236      nonSpacePos--;
1237    }
1238
1239    if (nonSpacePos < 0)
1240    {
1241      // There were no non-space characters.
1242      return "";
1243    }
1244    else if (nonSpacePos == lastPos)
1245    {
1246      // There were no trailing spaces.
1247      return s;
1248    }
1249    else
1250    {
1251      // There were trailing spaces, so return the string without them.
1252      return s.substring(0, (nonSpacePos+1));
1253    }
1254  }
1255
1256
1257
1258  /**
1259   * Wraps the contents of the specified line using the given width.  It will
1260   * attempt to wrap at spaces to preserve words, but if that is not possible
1261   * (because a single "word" is longer than the maximum width), then it will
1262   * wrap in the middle of the word at the specified maximum width.
1263   *
1264   * @param  line      The line to be wrapped.  It must not be {@code null}.
1265   * @param  maxWidth  The maximum width for lines in the resulting list.  A
1266   *                   value less than or equal to zero will cause no wrapping
1267   *                   to be performed.
1268   *
1269   * @return  A list of the wrapped lines.  It may be empty if the provided line
1270   *          contained only spaces.
1271   */
1272  public static List<String> wrapLine(final String line, final int maxWidth)
1273  {
1274    // See if the provided string already contains line breaks.  If so, then
1275    // treat it as multiple lines rather than a single line.
1276    final int breakPos = line.indexOf('\n');
1277    if (breakPos >= 0)
1278    {
1279      final ArrayList<String> lineList = new ArrayList<String>(10);
1280      final StringTokenizer tokenizer = new StringTokenizer(line, "\r\n");
1281      while (tokenizer.hasMoreTokens())
1282      {
1283        lineList.addAll(wrapLine(tokenizer.nextToken(), maxWidth));
1284      }
1285
1286      return lineList;
1287    }
1288
1289    final int length = line.length();
1290    if ((maxWidth <= 0) || (length < maxWidth))
1291    {
1292      return Arrays.asList(line);
1293    }
1294
1295
1296    int wrapPos = maxWidth;
1297    int lastWrapPos = 0;
1298    final ArrayList<String> lineList = new ArrayList<String>(5);
1299    while (true)
1300    {
1301      final int spacePos = line.lastIndexOf(' ', wrapPos);
1302      if (spacePos > lastWrapPos)
1303      {
1304        // We found a space in an acceptable location, so use it after trimming
1305        // any trailing spaces.
1306        final String s = trimTrailing(line.substring(lastWrapPos, spacePos));
1307
1308        // Don't bother adding the line if it contained only spaces.
1309        if (s.length() > 0)
1310        {
1311          lineList.add(s);
1312        }
1313
1314        wrapPos = spacePos;
1315      }
1316      else
1317      {
1318        // We didn't find any spaces, so we'll have to insert a hard break at
1319        // the specified wrap column.
1320        lineList.add(line.substring(lastWrapPos, wrapPos));
1321      }
1322
1323      // Skip over any spaces before the next non-space character.
1324      while ((wrapPos < length) && (line.charAt(wrapPos) == ' '))
1325      {
1326        wrapPos++;
1327      }
1328
1329      lastWrapPos = wrapPos;
1330      wrapPos += maxWidth;
1331      if (wrapPos >= length)
1332      {
1333        // The last fragment can fit on the line, so we can handle that now and
1334        // break.
1335        if (lastWrapPos >= length)
1336        {
1337          break;
1338        }
1339        else
1340        {
1341          final String s = trimTrailing(line.substring(lastWrapPos));
1342          if (s.length() > 0)
1343          {
1344            lineList.add(s);
1345          }
1346          break;
1347        }
1348      }
1349    }
1350
1351    return lineList;
1352  }
1353
1354
1355
1356  /**
1357   * This method returns a form of the provided argument that is safe to
1358   * use on the command line for the local platform. This method is provided as
1359   * a convenience wrapper around {@link ExampleCommandLineArgument}.  Calling
1360   * this method is equivalent to:
1361   *
1362   * <PRE>
1363   *  return ExampleCommandLineArgument.getCleanArgument(s).getLocalForm();
1364   * </PRE>
1365   *
1366   * For getting direct access to command line arguments that are safe to
1367   * use on other platforms, call
1368   * {@link ExampleCommandLineArgument#getCleanArgument}.
1369   *
1370   * @param  s  The string to be processed.  It must not be {@code null}.
1371   *
1372   * @return  A cleaned version of the provided string in a form that will allow
1373   *          it to be displayed as the value of a command-line argument on.
1374   */
1375  public static String cleanExampleCommandLineArgument(final String s)
1376  {
1377    return ExampleCommandLineArgument.getCleanArgument(s).getLocalForm();
1378  }
1379
1380
1381
1382  /**
1383   * Retrieves a single string which is a concatenation of all of the provided
1384   * strings.
1385   *
1386   * @param  a  The array of strings to concatenate.  It must not be
1387   *            {@code null}.
1388   *
1389   * @return  A string containing a concatenation of all of the strings in the
1390   *          provided array.
1391   */
1392  public static String concatenateStrings(final String... a)
1393  {
1394    return concatenateStrings(null, null, "  ", null, null, a);
1395  }
1396
1397
1398
1399  /**
1400   * Retrieves a single string which is a concatenation of all of the provided
1401   * strings.
1402   *
1403   * @param  l  The list of strings to concatenate.  It must not be
1404   *            {@code null}.
1405   *
1406   * @return  A string containing a concatenation of all of the strings in the
1407   *          provided list.
1408   */
1409  public static String concatenateStrings(final List<String> l)
1410  {
1411    return concatenateStrings(null, null, "  ", null, null, l);
1412  }
1413
1414
1415
1416  /**
1417   * Retrieves a single string which is a concatenation of all of the provided
1418   * strings.
1419   *
1420   * @param  beforeList       A string that should be placed at the beginning of
1421   *                          the list.  It may be {@code null} or empty if
1422   *                          nothing should be placed at the beginning of the
1423   *                          list.
1424   * @param  beforeElement    A string that should be placed before each element
1425   *                          in the list.  It may be {@code null} or empty if
1426   *                          nothing should be placed before each element.
1427   * @param  betweenElements  The separator that should be placed between
1428   *                          elements in the list.  It may be {@code null} or
1429   *                          empty if no separator should be placed between
1430   *                          elements.
1431   * @param  afterElement     A string that should be placed after each element
1432   *                          in the list.  It may be {@code null} or empty if
1433   *                          nothing should be placed after each element.
1434   * @param  afterList        A string that should be placed at the end of the
1435   *                          list.  It may be {@code null} or empty if nothing
1436   *                          should be placed at the end of the list.
1437   * @param  a                The array of strings to concatenate.  It must not
1438   *                          be {@code null}.
1439   *
1440   * @return  A string containing a concatenation of all of the strings in the
1441   *          provided list.
1442   */
1443  public static String concatenateStrings(final String beforeList,
1444                                          final String beforeElement,
1445                                          final String betweenElements,
1446                                          final String afterElement,
1447                                          final String afterList,
1448                                          final String... a)
1449  {
1450    return concatenateStrings(beforeList, beforeElement, betweenElements,
1451         afterElement, afterList, Arrays.asList(a));
1452  }
1453
1454
1455
1456  /**
1457   * Retrieves a single string which is a concatenation of all of the provided
1458   * strings.
1459   *
1460   * @param  beforeList       A string that should be placed at the beginning of
1461   *                          the list.  It may be {@code null} or empty if
1462   *                          nothing should be placed at the beginning of the
1463   *                          list.
1464   * @param  beforeElement    A string that should be placed before each element
1465   *                          in the list.  It may be {@code null} or empty if
1466   *                          nothing should be placed before each element.
1467   * @param  betweenElements  The separator that should be placed between
1468   *                          elements in the list.  It may be {@code null} or
1469   *                          empty if no separator should be placed between
1470   *                          elements.
1471   * @param  afterElement     A string that should be placed after each element
1472   *                          in the list.  It may be {@code null} or empty if
1473   *                          nothing should be placed after each element.
1474   * @param  afterList        A string that should be placed at the end of the
1475   *                          list.  It may be {@code null} or empty if nothing
1476   *                          should be placed at the end of the list.
1477   * @param  l                The list of strings to concatenate.  It must not
1478   *                          be {@code null}.
1479   *
1480   * @return  A string containing a concatenation of all of the strings in the
1481   *          provided list.
1482   */
1483  public static String concatenateStrings(final String beforeList,
1484                                          final String beforeElement,
1485                                          final String betweenElements,
1486                                          final String afterElement,
1487                                          final String afterList,
1488                                          final List<String> l)
1489  {
1490    ensureNotNull(l);
1491
1492    final StringBuilder buffer = new StringBuilder();
1493
1494    if (beforeList != null)
1495    {
1496      buffer.append(beforeList);
1497    }
1498
1499    final Iterator<String> iterator = l.iterator();
1500    while (iterator.hasNext())
1501    {
1502      if (beforeElement != null)
1503      {
1504        buffer.append(beforeElement);
1505      }
1506
1507      buffer.append(iterator.next());
1508
1509      if (afterElement != null)
1510      {
1511        buffer.append(afterElement);
1512      }
1513
1514      if ((betweenElements != null) && iterator.hasNext())
1515      {
1516        buffer.append(betweenElements);
1517      }
1518    }
1519
1520    if (afterList != null)
1521    {
1522      buffer.append(afterList);
1523    }
1524
1525    return buffer.toString();
1526  }
1527
1528
1529
1530  /**
1531   * Converts a duration in seconds to a string with a human-readable duration
1532   * which may include days, hours, minutes, and seconds, to the extent that
1533   * they are needed.
1534   *
1535   * @param  s  The number of seconds to be represented.
1536   *
1537   * @return  A string containing a human-readable representation of the
1538   *          provided time.
1539   */
1540  public static String secondsToHumanReadableDuration(final long s)
1541  {
1542    return millisToHumanReadableDuration(s * 1000L);
1543  }
1544
1545
1546
1547  /**
1548   * Converts a duration in seconds to a string with a human-readable duration
1549   * which may include days, hours, minutes, and seconds, to the extent that
1550   * they are needed.
1551   *
1552   * @param  m  The number of milliseconds to be represented.
1553   *
1554   * @return  A string containing a human-readable representation of the
1555   *          provided time.
1556   */
1557  public static String millisToHumanReadableDuration(final long m)
1558  {
1559    final StringBuilder buffer = new StringBuilder();
1560    long numMillis = m;
1561
1562    final long numDays = numMillis / 86400000L;
1563    if (numDays > 0)
1564    {
1565      numMillis -= (numDays * 86400000L);
1566      if (numDays == 1)
1567      {
1568        buffer.append(INFO_NUM_DAYS_SINGULAR.get(numDays));
1569      }
1570      else
1571      {
1572        buffer.append(INFO_NUM_DAYS_PLURAL.get(numDays));
1573      }
1574    }
1575
1576    final long numHours = numMillis / 3600000L;
1577    if (numHours > 0)
1578    {
1579      numMillis -= (numHours * 3600000L);
1580      if (buffer.length() > 0)
1581      {
1582        buffer.append(", ");
1583      }
1584
1585      if (numHours == 1)
1586      {
1587        buffer.append(INFO_NUM_HOURS_SINGULAR.get(numHours));
1588      }
1589      else
1590      {
1591        buffer.append(INFO_NUM_HOURS_PLURAL.get(numHours));
1592      }
1593    }
1594
1595    final long numMinutes = numMillis / 60000L;
1596    if (numMinutes > 0)
1597    {
1598      numMillis -= (numMinutes * 60000L);
1599      if (buffer.length() > 0)
1600      {
1601        buffer.append(", ");
1602      }
1603
1604      if (numMinutes == 1)
1605      {
1606        buffer.append(INFO_NUM_MINUTES_SINGULAR.get(numMinutes));
1607      }
1608      else
1609      {
1610        buffer.append(INFO_NUM_MINUTES_PLURAL.get(numMinutes));
1611      }
1612    }
1613
1614    if (numMillis == 1000)
1615    {
1616      if (buffer.length() > 0)
1617      {
1618        buffer.append(", ");
1619      }
1620
1621      buffer.append(INFO_NUM_SECONDS_SINGULAR.get(1));
1622    }
1623    else if ((numMillis > 0) || (buffer.length() == 0))
1624    {
1625      if (buffer.length() > 0)
1626      {
1627        buffer.append(", ");
1628      }
1629
1630      final long numSeconds = numMillis / 1000L;
1631      numMillis -= (numSeconds * 1000L);
1632      if ((numMillis % 1000L) != 0L)
1633      {
1634        final double numSecondsDouble = numSeconds + (numMillis / 1000.0);
1635        final DecimalFormat decimalFormat = new DecimalFormat("0.000");
1636        buffer.append(INFO_NUM_SECONDS_WITH_DECIMAL.get(
1637             decimalFormat.format(numSecondsDouble)));
1638      }
1639      else
1640      {
1641        buffer.append(INFO_NUM_SECONDS_PLURAL.get(numSeconds));
1642      }
1643    }
1644
1645    return buffer.toString();
1646  }
1647
1648
1649
1650  /**
1651   * Converts the provided number of nanoseconds to milliseconds.
1652   *
1653   * @param  nanos  The number of nanoseconds to convert to milliseconds.
1654   *
1655   * @return  The number of milliseconds that most closely corresponds to the
1656   *          specified number of nanoseconds.
1657   */
1658  public static long nanosToMillis(final long nanos)
1659  {
1660    return Math.max(0L, Math.round(nanos / 1000000.0d));
1661  }
1662
1663
1664
1665  /**
1666   * Converts the provided number of milliseconds to nanoseconds.
1667   *
1668   * @param  millis  The number of milliseconds to convert to nanoseconds.
1669   *
1670   * @return  The number of nanoseconds that most closely corresponds to the
1671   *          specified number of milliseconds.
1672   */
1673  public static long millisToNanos(final long millis)
1674  {
1675    return Math.max(0L, (millis * 1000000L));
1676  }
1677
1678
1679
1680  /**
1681   * Indicates whether the provided string is a valid numeric OID.  A numeric
1682   * OID must start and end with a digit, must have at least on period, must
1683   * contain only digits and periods, and must not have two consecutive periods.
1684   *
1685   * @param  s  The string to examine.  It must not be {@code null}.
1686   *
1687   * @return  {@code true} if the provided string is a valid numeric OID, or
1688   *          {@code false} if not.
1689   */
1690  public static boolean isNumericOID(final String s)
1691  {
1692    boolean digitRequired = true;
1693    boolean periodFound   = false;
1694    for (final char c : s.toCharArray())
1695    {
1696      switch (c)
1697      {
1698        case '0':
1699        case '1':
1700        case '2':
1701        case '3':
1702        case '4':
1703        case '5':
1704        case '6':
1705        case '7':
1706        case '8':
1707        case '9':
1708          digitRequired = false;
1709          break;
1710
1711        case '.':
1712          if (digitRequired)
1713          {
1714            return false;
1715          }
1716          else
1717          {
1718            digitRequired = true;
1719          }
1720          periodFound = true;
1721          break;
1722
1723        default:
1724          return false;
1725      }
1726
1727    }
1728
1729    return (periodFound && (! digitRequired));
1730  }
1731
1732
1733
1734  /**
1735   * Capitalizes the provided string.  The first character will be converted to
1736   * uppercase, and the rest of the string will be left unaltered.
1737   *
1738   * @param  s  The string to be capitalized.
1739   *
1740   * @return  A capitalized version of the provided string.
1741   */
1742  public static String capitalize(final String s)
1743  {
1744    if (s == null)
1745    {
1746      return null;
1747    }
1748
1749    switch (s.length())
1750    {
1751      case 0:
1752        return s;
1753
1754      case 1:
1755        return s.toUpperCase();
1756
1757      default:
1758        final char c = s.charAt(0);
1759        if (Character.isUpperCase(c))
1760        {
1761          return s;
1762        }
1763        else
1764        {
1765          return Character.toUpperCase(c) + s.substring(1);
1766        }
1767    }
1768  }
1769
1770
1771
1772  /**
1773   * Encodes the provided UUID to a byte array containing its 128-bit
1774   * representation.
1775   *
1776   * @param  uuid  The UUID to be encoded.  It must not be {@code null}.
1777   *
1778   * @return  The byte array containing the 128-bit encoded UUID.
1779   */
1780  public static byte[] encodeUUID(final UUID uuid)
1781  {
1782    final byte[] b = new byte[16];
1783
1784    final long mostSignificantBits  = uuid.getMostSignificantBits();
1785    b[0]  = (byte) ((mostSignificantBits >> 56) & 0xFF);
1786    b[1]  = (byte) ((mostSignificantBits >> 48) & 0xFF);
1787    b[2]  = (byte) ((mostSignificantBits >> 40) & 0xFF);
1788    b[3]  = (byte) ((mostSignificantBits >> 32) & 0xFF);
1789    b[4]  = (byte) ((mostSignificantBits >> 24) & 0xFF);
1790    b[5]  = (byte) ((mostSignificantBits >> 16) & 0xFF);
1791    b[6]  = (byte) ((mostSignificantBits >> 8) & 0xFF);
1792    b[7]  = (byte) (mostSignificantBits & 0xFF);
1793
1794    final long leastSignificantBits = uuid.getLeastSignificantBits();
1795    b[8]  = (byte) ((leastSignificantBits >> 56) & 0xFF);
1796    b[9]  = (byte) ((leastSignificantBits >> 48) & 0xFF);
1797    b[10] = (byte) ((leastSignificantBits >> 40) & 0xFF);
1798    b[11] = (byte) ((leastSignificantBits >> 32) & 0xFF);
1799    b[12] = (byte) ((leastSignificantBits >> 24) & 0xFF);
1800    b[13] = (byte) ((leastSignificantBits >> 16) & 0xFF);
1801    b[14] = (byte) ((leastSignificantBits >> 8) & 0xFF);
1802    b[15] = (byte) (leastSignificantBits & 0xFF);
1803
1804    return b;
1805  }
1806
1807
1808
1809  /**
1810   * Decodes the value of the provided byte array as a Java UUID.
1811   *
1812   * @param  b  The byte array to be decoded as a UUID.  It must not be
1813   *            {@code null}.
1814   *
1815   * @return  The decoded UUID.
1816   *
1817   * @throws  ParseException  If the provided byte array cannot be parsed as a
1818   *                         UUID.
1819   */
1820  public static UUID decodeUUID(final byte[] b)
1821         throws ParseException
1822  {
1823    if (b.length != 16)
1824    {
1825      throw new ParseException(ERR_DECODE_UUID_INVALID_LENGTH.get(toHex(b)), 0);
1826    }
1827
1828    long mostSignificantBits = 0L;
1829    for (int i=0; i < 8; i++)
1830    {
1831      mostSignificantBits = (mostSignificantBits << 8) | (b[i] & 0xFF);
1832    }
1833
1834    long leastSignificantBits = 0L;
1835    for (int i=8; i < 16; i++)
1836    {
1837      leastSignificantBits = (leastSignificantBits << 8) | (b[i] & 0xFF);
1838    }
1839
1840    return new UUID(mostSignificantBits, leastSignificantBits);
1841  }
1842
1843
1844
1845  /**
1846   * Returns {@code true} if and only if the current process is running on
1847   * a Windows-based operating system.
1848   *
1849   * @return  {@code true} if the current process is running on a Windows-based
1850   *          operating system and {@code false} otherwise.
1851   */
1852  public static boolean isWindows()
1853  {
1854    final String osName = toLowerCase(System.getProperty("os.name"));
1855    return ((osName != null) && osName.contains("windows"));
1856  }
1857
1858
1859
1860  /**
1861   * Attempts to parse the contents of the provided string to an argument list
1862   * (e.g., converts something like "--arg1 arg1value --arg2 --arg3 arg3value"
1863   * to a list of "--arg1", "arg1value", "--arg2", "--arg3", "arg3value").
1864   *
1865   * @param  s  The string to be converted to an argument list.
1866   *
1867   * @return  The parsed argument list.
1868   *
1869   * @throws  ParseException  If a problem is encountered while attempting to
1870   *                          parse the given string to an argument list.
1871   */
1872  public static List<String> toArgumentList(final String s)
1873         throws ParseException
1874  {
1875    if ((s == null) || (s.length() == 0))
1876    {
1877      return Collections.emptyList();
1878    }
1879
1880    int quoteStartPos = -1;
1881    boolean inEscape = false;
1882    final ArrayList<String> argList = new ArrayList<String>();
1883    final StringBuilder currentArg = new StringBuilder();
1884    for (int i=0; i < s.length(); i++)
1885    {
1886      final char c = s.charAt(i);
1887      if (inEscape)
1888      {
1889        currentArg.append(c);
1890        inEscape = false;
1891        continue;
1892      }
1893
1894      if (c == '\\')
1895      {
1896        inEscape = true;
1897      }
1898      else if (c == '"')
1899      {
1900        if (quoteStartPos >= 0)
1901        {
1902          quoteStartPos = -1;
1903        }
1904        else
1905        {
1906          quoteStartPos = i;
1907        }
1908      }
1909      else if (c == ' ')
1910      {
1911        if (quoteStartPos >= 0)
1912        {
1913          currentArg.append(c);
1914        }
1915        else if (currentArg.length() > 0)
1916        {
1917          argList.add(currentArg.toString());
1918          currentArg.setLength(0);
1919        }
1920      }
1921      else
1922      {
1923        currentArg.append(c);
1924      }
1925    }
1926
1927    if (s.endsWith("\\") && (! s.endsWith("\\\\")))
1928    {
1929      throw new ParseException(ERR_ARG_STRING_DANGLING_BACKSLASH.get(),
1930           (s.length() - 1));
1931    }
1932
1933    if (quoteStartPos >= 0)
1934    {
1935      throw new ParseException(ERR_ARG_STRING_UNMATCHED_QUOTE.get(
1936           quoteStartPos), quoteStartPos);
1937    }
1938
1939    if (currentArg.length() > 0)
1940    {
1941      argList.add(currentArg.toString());
1942    }
1943
1944    return Collections.unmodifiableList(argList);
1945  }
1946
1947
1948
1949  /**
1950   * Creates a modifiable list with all of the items of the provided array in
1951   * the same order.  This method behaves much like {@code Arrays.asList},
1952   * except that if the provided array is {@code null}, then it will return a
1953   * {@code null} list rather than throwing an exception.
1954   *
1955   * @param  <T>  The type of item contained in the provided array.
1956   *
1957   * @param  array  The array of items to include in the list.
1958   *
1959   * @return  The list that was created, or {@code null} if the provided array
1960   *          was {@code null}.
1961   */
1962  public static <T> List<T> toList(final T[] array)
1963  {
1964    if (array == null)
1965    {
1966      return null;
1967    }
1968
1969    final ArrayList<T> l = new ArrayList<T>(array.length);
1970    l.addAll(Arrays.asList(array));
1971    return l;
1972  }
1973
1974
1975
1976  /**
1977   * Creates a modifiable list with all of the items of the provided array in
1978   * the same order.  This method behaves much like {@code Arrays.asList},
1979   * except that if the provided array is {@code null}, then it will return an
1980   * empty list rather than throwing an exception.
1981   *
1982   * @param  <T>  The type of item contained in the provided array.
1983   *
1984   * @param  array  The array of items to include in the list.
1985   *
1986   * @return  The list that was created, or an empty list if the provided array
1987   *          was {@code null}.
1988   */
1989  public static <T> List<T> toNonNullList(final T[] array)
1990  {
1991    if (array == null)
1992    {
1993      return new ArrayList<T>(0);
1994    }
1995
1996    final ArrayList<T> l = new ArrayList<T>(array.length);
1997    l.addAll(Arrays.asList(array));
1998    return l;
1999  }
2000
2001
2002
2003  /**
2004   * Indicates whether both of the provided objects are {@code null} or both
2005   * are logically equal (using the {@code equals} method).
2006   *
2007   * @param  o1  The first object for which to make the determination.
2008   * @param  o2  The second object for which to make the determination.
2009   *
2010   * @return  {@code true} if both objects are {@code null} or both are
2011   *          logically equal, or {@code false} if only one of the objects is
2012   *          {@code null} or they are not logically equal.
2013   */
2014  public static boolean bothNullOrEqual(final Object o1, final Object o2)
2015  {
2016    if (o1 == null)
2017    {
2018      return (o2 == null);
2019    }
2020    else if (o2 == null)
2021    {
2022      return false;
2023    }
2024
2025    return o1.equals(o2);
2026  }
2027
2028
2029
2030  /**
2031   * Indicates whether both of the provided strings are {@code null} or both
2032   * are logically equal ignoring differences in capitalization (using the
2033   * {@code equalsIgnoreCase} method).
2034   *
2035   * @param  s1  The first string for which to make the determination.
2036   * @param  s2  The second string for which to make the determination.
2037   *
2038   * @return  {@code true} if both strings are {@code null} or both are
2039   *          logically equal ignoring differences in capitalization, or
2040   *          {@code false} if only one of the objects is {@code null} or they
2041   *          are not logically equal ignoring capitalization.
2042   */
2043  public static boolean bothNullOrEqualIgnoreCase(final String s1,
2044                                                  final String s2)
2045  {
2046    if (s1 == null)
2047    {
2048      return (s2 == null);
2049    }
2050    else if (s2 == null)
2051    {
2052      return false;
2053    }
2054
2055    return s1.equalsIgnoreCase(s2);
2056  }
2057
2058
2059
2060  /**
2061   * Indicates whether the provided string arrays have the same elements,
2062   * ignoring the order in which they appear and differences in capitalization.
2063   * It is assumed that neither array contains {@code null} strings, and that
2064   * no string appears more than once in each array.
2065   *
2066   * @param  a1  The first array for which to make the determination.
2067   * @param  a2  The second array for which to make the determination.
2068   *
2069   * @return  {@code true} if both arrays have the same set of strings, or
2070   *          {@code false} if not.
2071   */
2072  public static boolean stringsEqualIgnoreCaseOrderIndependent(
2073                             final String[] a1, final String[] a2)
2074  {
2075    if (a1 == null)
2076    {
2077      return (a2 == null);
2078    }
2079    else if (a2 == null)
2080    {
2081      return false;
2082    }
2083
2084    if (a1.length != a2.length)
2085    {
2086      return false;
2087    }
2088
2089    if (a1.length == 1)
2090    {
2091      return (a1[0].equalsIgnoreCase(a2[0]));
2092    }
2093
2094    final HashSet<String> s1 = new HashSet<String>(a1.length);
2095    for (final String s : a1)
2096    {
2097      s1.add(toLowerCase(s));
2098    }
2099
2100    final HashSet<String> s2 = new HashSet<String>(a2.length);
2101    for (final String s : a2)
2102    {
2103      s2.add(toLowerCase(s));
2104    }
2105
2106    return s1.equals(s2);
2107  }
2108
2109
2110
2111  /**
2112   * Indicates whether the provided arrays have the same elements, ignoring the
2113   * order in which they appear.  It is assumed that neither array contains
2114   * {@code null} elements, and that no element appears more than once in each
2115   * array.
2116   *
2117   * @param  <T>  The type of element contained in the arrays.
2118   *
2119   * @param  a1  The first array for which to make the determination.
2120   * @param  a2  The second array for which to make the determination.
2121   *
2122   * @return  {@code true} if both arrays have the same set of elements, or
2123   *          {@code false} if not.
2124   */
2125  public static <T> boolean arraysEqualOrderIndependent(final T[] a1,
2126                                                        final T[] a2)
2127  {
2128    if (a1 == null)
2129    {
2130      return (a2 == null);
2131    }
2132    else if (a2 == null)
2133    {
2134      return false;
2135    }
2136
2137    if (a1.length != a2.length)
2138    {
2139      return false;
2140    }
2141
2142    if (a1.length == 1)
2143    {
2144      return (a1[0].equals(a2[0]));
2145    }
2146
2147    final HashSet<T> s1 = new HashSet<T>(Arrays.asList(a1));
2148    final HashSet<T> s2 = new HashSet<T>(Arrays.asList(a2));
2149    return s1.equals(s2);
2150  }
2151
2152
2153
2154  /**
2155   * Determines the number of bytes in a UTF-8 character that starts with the
2156   * given byte.
2157   *
2158   * @param  b  The byte for which to make the determination.
2159   *
2160   * @return  The number of bytes in a UTF-8 character that starts with the
2161   *          given byte, or -1 if it does not appear to be a valid first byte
2162   *          for a UTF-8 character.
2163   */
2164  public static int numBytesInUTF8CharacterWithFirstByte(final byte b)
2165  {
2166    if ((b & 0x7F) == b)
2167    {
2168      return 1;
2169    }
2170    else if ((b & 0xE0) == 0xC0)
2171    {
2172      return 2;
2173    }
2174    else if ((b & 0xF0) == 0xE0)
2175    {
2176      return 3;
2177    }
2178    else if ((b & 0xF8) == 0xF0)
2179    {
2180      return 4;
2181    }
2182    else
2183    {
2184      return -1;
2185    }
2186  }
2187}