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