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