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     */
021    package com.unboundid.util;
022    
023    
024    
025    import java.lang.reflect.Constructor;
026    import java.io.IOException;
027    import java.text.DecimalFormat;
028    import java.text.ParseException;
029    import java.text.SimpleDateFormat;
030    import java.util.ArrayList;
031    import java.util.Arrays;
032    import java.util.Collections;
033    import java.util.Date;
034    import java.util.HashSet;
035    import java.util.Iterator;
036    import java.util.List;
037    import java.util.StringTokenizer;
038    import java.util.TimeZone;
039    import java.util.UUID;
040    
041    import com.unboundid.ldap.sdk.Control;
042    import com.unboundid.ldap.sdk.Version;
043    
044    import static com.unboundid.util.Debug.*;
045    import static com.unboundid.util.UtilityMessages.*;
046    import 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)
054    public 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    }