View Javadoc
1   /*
2    *  Licensed to the Apache Software Foundation (ASF) under one
3    *  or more contributor license agreements.  See the NOTICE file
4    *  distributed with this work for additional information
5    *  regarding copyright ownership.  The ASF licenses this file
6    *  to you under the Apache License, Version 2.0 (the
7    *  "License"); you may not use this file except in compliance
8    *  with the License.  You may obtain a copy of the License at
9    *
10   *    http://www.apache.org/licenses/LICENSE-2.0
11   *
12   *  Unless required by applicable law or agreed to in writing,
13   *  software distributed under the License is distributed on an
14   *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   *  KIND, either express or implied.  See the License for the
16   *  specific language governing permissions and limitations
17   *  under the License.
18   *
19   */
20  package org.apache.directory.api.util;
21  
22  
23  import static org.apache.directory.api.util.Chars.isHex;
24  import static org.apache.directory.api.util.Hex.encodeHex;
25  import static org.apache.directory.api.util.Hex.getHexValue;
26  
27  import java.io.ByteArrayOutputStream;
28  import java.io.OutputStreamWriter;
29  import java.io.UnsupportedEncodingException;
30  import java.lang.reflect.InvocationTargetException;
31  import java.lang.reflect.Method;
32  import java.nio.charset.Charset;
33  import java.util.List;
34  import java.util.Locale;
35  import java.util.Map;
36  import java.util.Set;
37  import java.util.UUID;
38  
39  import org.apache.directory.api.i18n.I18n;
40  import org.slf4j.Logger;
41  import org.slf4j.LoggerFactory;
42  
43  
44  /**
45   * Various string manipulation methods that are more efficient then chaining
46   * string operations: all is done in the same buffer without creating a bunch of
47   * string objects.
48   *
49   * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
50   */
51  public final class Strings
52  {
53      /** A logger for this class */
54      private static final Logger LOG = LoggerFactory.getLogger( Strings.class );
55  
56      /** The default charset, because it's not provided by JDK 1.5 */
57      static String defaultCharset = null;
58  
59      /** Hex chars */
60      private static final byte[] HEX_CHAR = new byte[]
61          { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
62  
63      /** A table containing booleans when the corresponding char is printable */
64      public static final boolean[] IS_PRINTABLE_CHAR =
65          {
66              false, false, false, false, false, false, false, false, // ---, ---, ---, ---, ---, ---, ---, ---
67              false, false, false, false, false, false, false, false, // ---, ---, ---, ---, ---, ---, ---, ---
68              false, false, false, false, false, false, false, false, // ---, ---, ---, ---, ---, ---, ---, ---
69              false, false, false, false, false, false, false, false, // ---, ---, ---, ---, ---, ---, ---, ---
70              true,  false, false, false, false, false, false, true,  // ' ', ---, ---, ---, ---, ---, ---, "'"
71              true,  true,  false, true,  true,  true,  true,  true,  // '(', ')', ---, '+', ',', '-', '.', '/'
72              true,  true,  true,  true,  true,  true,  true,  true,  // '0', '1', '2', '3', '4', '5', '6', '7',
73              true,  true,  true,  false, false, true,  false, true,  // '8', '9', ':', ---, ---, '=', ---, '?'
74              false, true,  true,  true,  true,  true,  true,  true,  // ---, 'A', 'B', 'C', 'D', 'E', 'F', 'G',
75              true,  true,  true,  true,  true,  true,  true,  true,  // 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O'
76              true,  true,  true,  true,  true,  true,  true,  true,  // 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W'
77              true,  true,  true,  false, false, false, false, false, // 'X', 'Y', 'Z', ---, ---, ---, ---, ---
78              false, true,  true,  true,  true,  true,  true,  true,  // ---, 'a', 'b', 'c', 'd', 'e', 'f', 'g'
79              true,  true,  true,  true,  true,  true,  true,  true,  // 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o'
80              true,  true,  true,  true,  true,  true,  true,  true,  // 'p', 'q', 'r', 's', 't', 'u', 'v', 'w'
81              true,  true,  true,  false, false, false, false, false // 'x', 'y', 'z', ---, ---, ---, ---, ---
82      };
83  
84      public static final char[] TO_LOWER_CASE =
85          {
86              0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
87              0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
88              0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
89              0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F,
90              ' ',  0x21, 0x22, 0x23, 0x24, 0x25, 0x26, '\'',
91              '(',  ')',  0x2A, '+',  ',',  '-',  '.',  '/',
92              '0',  '1',  '2',  '3',  '4',  '5',  '6',  '7',
93              '8',  '9',  ':',  0x3B, 0x3C, '=',  0x3E, '?',
94              0x40, 'a',  'b',  'c',  'd',  'e',  'f',  'g',
95              'h',  'i',  'j',  'k',  'l',  'm',  'n',  'o',
96              'p',  'q',  'r',  's',  't',  'u',  'v',  'w',
97              'x',  'y',  'z',  0x5B, 0x5C, 0x5D, 0x5E, 0x5F,
98              0x60, 'a',  'b',  'c',  'd',  'e',  'f',  'g',
99              'h',  'i',  'j',  'k',  'l',  'm',  'n',  'o',
100             'p',  'q',  'r',  's',  't',  'u',  'v',  'w',
101             'x',  'y',  'z',  0x7B, 0x7C, 0x7D, 0x7E, 0x7F,
102             0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87,
103             0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8D, 0x8E, 0x8F,
104             0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97,
105             0x98, 0x99, 0x9A, 0x9B, 0x9C, 0x9D, 0x9E, 0x9F,
106             0xA0, 0xA1, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7,
107             0xA8, 0xA9, 0xAA, 0xAB, 0xAC, 0xAD, 0xAE, 0xAF,
108             0xB0, 0xB1, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7,
109             0xB8, 0xB9, 0xBA, 0xBB, 0xBC, 0xBD, 0xBE, 0xBF,
110             0xC0, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7,
111             0xC8, 0xC9, 0xCA, 0xCB, 0xCC, 0xCD, 0xCE, 0xCF,
112             0xD0, 0xD1, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7,
113             0xD8, 0xD9, 0xDA, 0xDB, 0xDC, 0xDD, 0xDE, 0xDF,
114             0xE0, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7,
115             0xE8, 0xE9, 0xEA, 0xEB, 0xEC, 0xED, 0xEE, 0xEF,
116             0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7,
117             0xF8, 0xF9, 0xFA, 0xFB, 0xFC, 0xFD, 0xFE, 0xFF,
118     };
119 
120     public static final byte[] TO_LOWER_CASE_BYTE =
121         {
122             0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
123             0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
124             0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
125             0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F,
126             ' ',  0x21, 0x22, 0x23, 0x24, 0x25, 0x26, '\'',
127             '(',  ')',  0x2A, '+',  ',',  '-',  '.',  '/',
128             '0',  '1',  '2',  '3',  '4',  '5',  '6',  '7',
129             '8',  '9',  ':',  0x3B, 0x3C, '=',  0x3E, '?',
130             0x40, 'a',  'b',  'c',  'd',  'e',  'f',  'g',
131             'h',  'i',  'j',  'k',  'l',  'm',  'n',  'o',
132             'p',  'q',  'r',  's',  't',  'u',  'v',  'w',
133             'x',  'y',  'z',  0x5B, 0x5C, 0x5D, 0x5E, 0x5F,
134             0x60, 'a',  'b',  'c',  'd',  'e',  'f',  'g',
135             'h',  'i',  'j',  'k',  'l',  'm',  'n',  'o',
136             'p',  'q',  'r',  's',  't',  'u',  'v',  'w',
137             'x',  'y',  'z',  0x7B, 0x7C, 0x7D, 0x7E, 0x7F,
138             ( byte ) 0x80, ( byte ) 0x81, ( byte ) 0x82, ( byte ) 0x83,
139             ( byte ) 0x84, ( byte ) 0x85, ( byte ) 0x86, ( byte ) 0x87,
140             ( byte ) 0x88, ( byte ) 0x89, ( byte ) 0x8A, ( byte ) 0x8B,
141             ( byte ) 0x8C, ( byte ) 0x8D, ( byte ) 0x8E, ( byte ) 0x8F,
142             ( byte ) 0x90, ( byte ) 0x91, ( byte ) 0x92, ( byte ) 0x93,
143             ( byte ) 0x94, ( byte ) 0x95, ( byte ) 0x96, ( byte ) 0x97,
144             ( byte ) 0x98, ( byte ) 0x99, ( byte ) 0x9A, ( byte ) 0x9B,
145             ( byte ) 0x9C, ( byte ) 0x9D, ( byte ) 0x9E, ( byte ) 0x9F,
146             ( byte ) 0xA0, ( byte ) 0xA1, ( byte ) 0xA2, ( byte ) 0xA3,
147             ( byte ) 0xA4, ( byte ) 0xA5, ( byte ) 0xA6, ( byte ) 0xA7,
148             ( byte ) 0xA8, ( byte ) 0xA9, ( byte ) 0xAA, ( byte ) 0xAB,
149             ( byte ) 0xAC, ( byte ) 0xAD, ( byte ) 0xAE, ( byte ) 0xAF,
150             ( byte ) 0xB0, ( byte ) 0xB1, ( byte ) 0xB2, ( byte ) 0xB3,
151             ( byte ) 0xB4, ( byte ) 0xB5, ( byte ) 0xB6, ( byte ) 0xB7,
152             ( byte ) 0xB8, ( byte ) 0xB9, ( byte ) 0xBA, ( byte ) 0xBB,
153             ( byte ) 0xBC, ( byte ) 0xBD, ( byte ) 0xBE, ( byte ) 0xBF,
154             ( byte ) 0xC0, ( byte ) 0xC1, ( byte ) 0xC2, ( byte ) 0xC3,
155             ( byte ) 0xC4, ( byte ) 0xC5, ( byte ) 0xC6, ( byte ) 0xC7,
156             ( byte ) 0xC8, ( byte ) 0xC9, ( byte ) 0xCA, ( byte ) 0xCB,
157             ( byte ) 0xCC, ( byte ) 0xCD, ( byte ) 0xCE, ( byte ) 0xCF,
158             ( byte ) 0xD0, ( byte ) 0xD1, ( byte ) 0xD2, ( byte ) 0xD3,
159             ( byte ) 0xD4, ( byte ) 0xD5, ( byte ) 0xD6, ( byte ) 0xD7,
160             ( byte ) 0xD8, ( byte ) 0xD9, ( byte ) 0xDA, ( byte ) 0xDB,
161             ( byte ) 0xDC, ( byte ) 0xDD, ( byte ) 0xDE, ( byte ) 0xDF,
162             ( byte ) 0xE0, ( byte ) 0xE1, ( byte ) 0xE2, ( byte ) 0xE3,
163             ( byte ) 0xE4, ( byte ) 0xE5, ( byte ) 0xE6, ( byte ) 0xE7,
164             ( byte ) 0xE8, ( byte ) 0xE9, ( byte ) 0xEA, ( byte ) 0xEB,
165             ( byte ) 0xEC, ( byte ) 0xED, ( byte ) 0xEE, ( byte ) 0xEF,
166             ( byte ) 0xF0, ( byte ) 0xF1, ( byte ) 0xF2, ( byte ) 0xF3,
167             ( byte ) 0xF4, ( byte ) 0xF5, ( byte ) 0xF6, ( byte ) 0xF7,
168             ( byte ) 0xF8, ( byte ) 0xF9, ( byte ) 0xFA, ( byte ) 0xFB,
169             ( byte ) 0xFC, ( byte ) 0xFD, ( byte ) 0xFE,( byte ) 0xFF,
170     };
171 
172     /** upperCase = 'A' .. 'Z', '0'..'9', '-' */
173     public static final char[] UPPER_CASE =
174         {
175             0, 0, 0, 0, 0, 0, 0, 0,
176             0, 0, 0, 0, 0, 0, 0, 0,
177             0, 0, 0, 0, 0, 0, 0, 0,
178             0, 0, 0, 0, 0, 0, 0, 0,
179             0, 0, 0, 0, 0, 0, 0, 0,
180             0, 0, 0, 0, 0, '-', 0, 0,
181             '0', '1', '2', '3', '4', '5', '6', '7',
182             '8', '9', 0, 0, 0, 0, 0, 0,
183             0, 'A', 'B', 'C', 'D', 'E', 'F', 'G',
184             'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O',
185             'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W',
186             'X', 'Y', 'Z', 0, 0, 0, 0, 0,
187             0, 'A', 'B', 'C', 'D', 'E', 'F', 'G',
188             'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O',
189             'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W',
190             'X', 'Y', 'Z', 0, 0, 0, 0, 0,
191             0, 0, 0, 0, 0, 0, 0, 0,
192             0, 0, 0, 0, 0, 0, 0, 0,
193             0, 0, 0, 0, 0, 0, 0, 0,
194             0, 0, 0, 0, 0, 0, 0, 0,
195             0, 0, 0, 0, 0, 0, 0, 0,
196             0, 0, 0, 0, 0, 0, 0, 0,
197             0, 0, 0, 0, 0, 0, 0, 0,
198             0, 0, 0, 0, 0, 0, 0, 0
199     };
200 
201     /** A empty byte array */
202     public static final byte[] EMPTY_BYTES = new byte[0];
203 
204 
205     /**
206      * Private constructor
207      */
208     private Strings()
209     {
210     }
211 
212 
213     /**
214      * Helper function that dump an array of bytes in hex form
215      *
216      * @param buffer The bytes array to dump
217      * @return A string representation of the array of bytes
218      */
219     public static String dumpBytes( byte[] buffer )
220     {
221         if ( buffer == null )
222         {
223             return "";
224         }
225 
226         StringBuffer sb = new StringBuffer();
227 
228         for ( int i = 0; i < buffer.length; i++ )
229         {
230             sb.append( "0x" ).append( ( char ) ( HEX_CHAR[( buffer[i] & 0x00F0 ) >> 4] ) ).append(
231                 ( char ) ( HEX_CHAR[buffer[i] & 0x000F] ) ).append( " " );
232         }
233 
234         return sb.toString();
235     }
236 
237 
238     /**
239      * Helper function that dump a byte in hex form
240      *
241      * @param octet The byte to dump
242      * @return A string representation of the byte
243      */
244     public static String dumpByte( byte octet )
245     {
246         return new String( new byte[]
247             { '0', 'x', HEX_CHAR[( octet & 0x00F0 ) >> 4], HEX_CHAR[octet & 0x000F] } );
248     }
249 
250 
251     /**
252      * Helper function that returns a char from an hex
253      *
254      * @param hex The hex to dump
255      * @return A char representation of the hex
256      */
257     public static char dumpHex( byte hex )
258     {
259         return ( char ) HEX_CHAR[hex & 0x000F];
260     }
261 
262 
263     /**
264      * Helper function that dump an array of bytes in hex pair form,
265      * without '0x' and space chars
266      *
267      * @param buffer The bytes array to dump
268      * @return A string representation of the array of bytes
269      */
270     public static String dumpHexPairs( byte[] buffer )
271     {
272         if ( buffer == null )
273         {
274             return "";
275         }
276 
277         char[] str = new char[buffer.length << 1];
278 
279         for ( int i = 0, pos = 0; i < buffer.length; i++ )
280         {
281             str[pos++] = ( char ) ( HEX_CHAR[( buffer[i] & 0x00F0 ) >> 4] );
282             str[pos++] = ( char ) ( HEX_CHAR[buffer[i] & 0x000F] );
283         }
284 
285         return new String( str );
286     }
287 
288 
289     /**
290      * Put common code to deepTrim(String) and deepTrimToLower here.
291      *
292      * @param str the string to deep trim
293      * @param toLowerCase how to normalize for case: upper or lower
294      * @return the deep trimmed string
295      * @see Strings#deepTrim( String )
296      *
297      * TODO Replace the toCharArray() by substring manipulations
298      */
299     public static String deepTrim( String str, boolean toLowerCase )
300     {
301         if ( ( null == str ) || ( str.length() == 0 ) )
302         {
303             return "";
304         }
305 
306         char ch;
307         char[] buf = str.toCharArray();
308         char[] newbuf = new char[buf.length];
309         boolean wsSeen = false;
310         boolean isStart = true;
311         int pos = 0;
312 
313         for ( int i = 0; i < str.length(); i++ )
314         {
315             ch = buf[i];
316 
317             // filter out all uppercase characters
318             if ( toLowerCase && Character.isUpperCase( ch ) )
319             {
320                 ch = Character.toLowerCase( ch );
321             }
322 
323             // Check to see if we should add space
324             if ( Character.isWhitespace( ch ) )
325             {
326                 // If the buffer has had characters added already check last
327                 // added character. Only append a spc if last character was
328                 // not whitespace.
329                 if ( wsSeen )
330                 {
331                     continue;
332                 }
333                 else
334                 {
335                     wsSeen = true;
336 
337                     if ( isStart )
338                     {
339                         isStart = false;
340                     }
341                     else
342                     {
343                         newbuf[pos++] = ch;
344                     }
345                 }
346             }
347             else
348             {
349                 // Add all non-whitespace
350                 wsSeen = false;
351                 isStart = false;
352                 newbuf[pos++] = ch;
353             }
354         }
355 
356         return ( pos == 0 ? "" : new String( newbuf, 0, ( wsSeen ? pos - 1 : pos ) ) );
357     }
358 
359 
360     /**
361      * This does the same thing as a trim but we also lowercase the string while
362      * performing the deep trim within the same buffer. This saves us from
363      * having to create multiple String and StringBuffer objects and is much
364      * more efficient.
365      *
366      * @see Strings#deepTrim( String )
367      */
368     public static String deepTrimToLower( String string )
369     {
370         return deepTrim( string, true );
371     }
372 
373 
374     /**
375      * A deep trim of a string remove whitespace from the ends as well as
376      * excessive whitespace within the inside of the string between
377      * non-whitespace characters. A deep trim reduces internal whitespace down
378      * to a single space to preserve the whitespace separated tokenization order
379      * of the String.
380      *
381      * @param string the string to deep trim.
382      * @return the trimmed string.
383      */
384     public static String deepTrim( String string )
385     {
386         return deepTrim( string, false );
387     }
388 
389 
390     /**
391      * Trims several consecutive characters into one.
392      *
393      * @param str the string to trim consecutive characters of
394      * @param ch the character to trim down
395      * @return the newly trimmed down string
396      */
397     public static String trimConsecutiveToOne( String str, char ch )
398     {
399         if ( ( null == str ) || ( str.length() == 0 ) )
400         {
401             return "";
402         }
403 
404         char[] buffer = str.toCharArray();
405         char[] newbuf = new char[buffer.length];
406         int pos = 0;
407         boolean same = false;
408 
409         for ( int i = 0; i < buffer.length; i++ )
410         {
411             char car = buffer[i];
412 
413             if ( car == ch )
414             {
415                 if ( same )
416                 {
417                     continue;
418                 }
419                 else
420                 {
421                     same = true;
422                     newbuf[pos++] = car;
423                 }
424             }
425             else
426             {
427                 same = false;
428                 newbuf[pos++] = car;
429             }
430         }
431 
432         return new String( newbuf, 0, pos );
433     }
434 
435 
436     /**
437      * Truncates large Strings showing a portion of the String's head and tail
438      * with the center cut out and replaced with '...'. Also displays the total
439      * length of the truncated string so size of '...' can be interpreted.
440      * Useful for large strings in UIs or hex dumps to log files.
441      *
442      * @param str the string to truncate
443      * @param head the amount of the head to display
444      * @param tail the amount of the tail to display
445      * @return the center truncated string
446      */
447     public static String centerTrunc( String str, int head, int tail )
448     {
449         StringBuffer buf = null;
450 
451         // Return as-is if String is smaller than or equal to the head plus the
452         // tail plus the number of characters added to the trunc representation
453         // plus the number of digits in the string length.
454         if ( str.length() <= ( head + tail + 7 + str.length() / 10 ) )
455         {
456             return str;
457         }
458 
459         buf = new StringBuffer();
460         buf.append( '[' ).append( str.length() ).append( "][" );
461         buf.append( str.substring( 0, head ) ).append( "..." );
462         buf.append( str.substring( str.length() - tail ) );
463         buf.append( ']' );
464         return buf.toString();
465     }
466 
467 
468     /**
469      * Gets a hex string from byte array.
470      *
471      * @param res the byte array
472      * @return the hex string representing the binary values in the array
473      */
474     public static String toHexString( byte[] res )
475     {
476         StringBuffer buf = new StringBuffer( res.length << 1 );
477 
478         for ( int ii = 0; ii < res.length; ii++ )
479         {
480             String digit = Integer.toHexString( 0xFF & res[ii] );
481 
482             if ( digit.length() == 1 )
483             {
484                 digit = '0' + digit;
485             }
486 
487             buf.append( digit );
488         }
489 
490         return buf.toString().toUpperCase();
491     }
492 
493 
494     /**
495      * Get byte array from hex string
496      *
497      * @param hexString the hex string to convert to a byte array
498      * @return the byte form of the hex string.
499      */
500     public static byte[] toByteArray( String hexString )
501     {
502         int arrLength = hexString.length() >> 1;
503         byte[] buf = new byte[arrLength];
504 
505         for ( int ii = 0; ii < arrLength; ii++ )
506         {
507             int index = ii << 1;
508 
509             String digit = hexString.substring( index, index + 2 );
510             buf[ii] = ( byte ) Integer.parseInt( digit, 16 );
511         }
512 
513         return buf;
514     }
515 
516 
517     /**
518      * This method is used to insert HTML block dynamically
519      *
520      * @param source the HTML code to be processes
521      * @param replaceNl if true '\n' will be replaced by &lt;br>
522      * @param replaceTag if true '<' will be replaced by &lt; and '>' will be replaced
523      *            by &gt;
524      * @param replaceQuote if true '\"' will be replaced by &quot;
525      * @return the formated html block
526      */
527     public static String formatHtml( String source, boolean replaceNl, boolean replaceTag,
528         boolean replaceQuote )
529     {
530         StringBuffer buf = new StringBuffer();
531         int len = source.length();
532 
533         for ( int ii = 0; ii < len; ii++ )
534         {
535             char ch = source.charAt( ii );
536 
537             switch ( ch )
538             {
539                 case '\"':
540                     if ( replaceQuote )
541                     {
542                         buf.append( "&quot;" );
543                     }
544                     else
545                     {
546                         buf.append( ch );
547                     }
548                     break;
549 
550                 case '<':
551                     if ( replaceTag )
552                     {
553                         buf.append( "&lt;" );
554                     }
555                     else
556                     {
557                         buf.append( ch );
558                     }
559                     break;
560 
561                 case '>':
562                     if ( replaceTag )
563                     {
564                         buf.append( "&gt;" );
565                     }
566                     else
567                     {
568                         buf.append( ch );
569                     }
570                     break;
571 
572                 case '\n':
573                     if ( replaceNl )
574                     {
575                         if ( replaceTag )
576                         {
577                             buf.append( "&lt;br&gt;" );
578                         }
579                         else
580                         {
581                             buf.append( "<br>" );
582                         }
583                     }
584                     else
585                     {
586                         buf.append( ch );
587                     }
588                     break;
589 
590                 case '\r':
591                     break;
592 
593                 case '&':
594                     buf.append( "&amp;" );
595                     break;
596 
597                 default:
598                     buf.append( ch );
599                     break;
600             }
601         }
602 
603         return buf.toString();
604     }
605 
606 
607     /**
608      * Check if a text is present at the current position in another string.
609      *
610      * @param string The string which contains the data
611      * @param index Current position in the string
612      * @param text The text we want to check
613      * @return <code>true</code> if the string contains the text.
614      */
615     public static boolean areEquals( String string, int index, String text )
616     {
617         if ( ( string == null ) || ( text == null ) )
618         {
619             return false;
620         }
621 
622         int length1 = string.length();
623         int length2 = text.length();
624 
625         if ( ( length1 == 0 ) || ( length1 <= index ) || ( index < 0 )
626             || ( length2 == 0 ) || ( length2 > ( length1 + index ) ) )
627         {
628             return false;
629         }
630         else
631         {
632             return string.substring( index ).startsWith( text );
633         }
634     }
635 
636 
637     /**
638      * Test if the current character is equal to a specific character. This
639      * function works only for character between 0 and 127, as it does compare a
640      * byte and a char (which is 16 bits wide)
641      *
642      * @param byteArray The buffer which contains the data
643      * @param index Current position in the buffer
644      * @param car The character we want to compare with the current buffer position
645      * @return <code>true</code> if the current character equals the given character.
646      */
647     public static boolean isCharASCII( byte[] byteArray, int index, char car )
648     {
649         if ( ( byteArray == null ) || ( byteArray.length == 0 ) || ( index < 0 ) || ( index >= byteArray.length ) )
650         {
651             return false;
652         }
653         else
654         {
655             return ( ( byteArray[index] == car ) ? true : false );
656         }
657     }
658 
659 
660     /**
661      * Test if the current character is equal to a specific character.
662      *
663      * @param string The String which contains the data
664      * @param index Current position in the string
665      * @param car The character we want to compare with the current string position
666      * @return <code>true</code> if the current character equals the given character.
667      */
668     public static boolean isCharASCII( String string, int index, char car )
669     {
670         if ( string == null )
671         {
672             return false;
673         }
674 
675         int length = string.length();
676 
677         if ( ( length == 0 ) || ( index < 0 ) || ( index >= length ) )
678         {
679             return false;
680         }
681         else
682         {
683             return string.charAt( index ) == car;
684         }
685     }
686 
687     private static final byte[] UTF8 = new byte[]
688         { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A,
689             0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C,
690             0x1D, 0x1E, 0x1F, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E,
691             0x2F, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F, 0x40,
692             0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, 0x50, 0x51, 0x52,
693             0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x5B, 0x5C, 0x5D, 0x5E, 0x5F, 0x60, 0x61, 0x62, 0x63, 0x64,
694             0x65, 0x66, 0x67, 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76,
695             0x77, 0x78, 0x79, 0x7A, 0x7B, 0x7C, 0x7D, 0x7E, 0x7F };
696 
697 
698     /**
699      * Return an UTF-8 encoded String
700      *
701      * @param bytes The byte array to be transformed to a String
702      * @return A String.
703      */
704     public static String utf8ToString( byte[] bytes )
705     {
706         if ( bytes == null )
707         {
708             return "";
709         }
710 
711         char[] chars = new char[bytes.length];
712         int pos = 0;
713 
714         try
715         {
716             for ( byte b : bytes )
717             {
718                 chars[pos++] = ( char ) UTF8[b];
719             }
720         }
721         catch ( ArrayIndexOutOfBoundsException aioobe )
722         {
723             try
724             {
725                 return new String( bytes, "UTF-8" );
726             }
727             catch ( UnsupportedEncodingException uee )
728             {
729                 // if this happens something is really strange
730                 throw new RuntimeException( uee );
731             }
732         }
733 
734         return new String( chars );
735     }
736 
737 
738     /**
739      * Return an UTF-8 encoded String
740      *
741      * @param bytes The byte array to be transformed to a String
742      * @param length The length of the byte array to be converted
743      * @return A String.
744      */
745     public static String utf8ToString( byte[] bytes, int length )
746     {
747         if ( bytes == null )
748         {
749             return "";
750         }
751 
752         try
753         {
754             return new String( bytes, 0, length, "UTF-8" );
755         }
756         catch ( UnsupportedEncodingException uee )
757         {
758             // if this happens something is really strange
759             throw new RuntimeException( uee );
760         }
761     }
762 
763 
764     /**
765      * Return an UTF-8 encoded String
766      *
767      * @param bytes  The byte array to be transformed to a String
768      * @param start the starting position in the byte array
769      * @param length The length of the byte array to be converted
770      * @return A String.
771      */
772     public static String utf8ToString( byte[] bytes, int start, int length )
773     {
774         if ( bytes == null )
775         {
776             return "";
777         }
778 
779         try
780         {
781             return new String( bytes, start, length, "UTF-8" );
782         }
783         catch ( UnsupportedEncodingException uee )
784         {
785             // if this happens something is really strange
786             throw new RuntimeException( uee );
787         }
788     }
789 
790 
791     /**
792      * Check if a text is present at the current position in a buffer.
793      *
794      * @param bytes The buffer which contains the data
795      * @param index Current position in the buffer
796      * @param text The text we want to check
797      * @return <code>true</code> if the buffer contains the text.
798      */
799     public static int areEquals( byte[] bytes, int index, String text )
800     {
801         if ( ( bytes == null ) || ( bytes.length == 0 ) || ( bytes.length <= index ) || ( index < 0 )
802             || ( text == null ) )
803         {
804             return StringConstants.NOT_EQUAL;
805         }
806         else
807         {
808             try
809             {
810                 byte[] data = text.getBytes( "UTF-8" );
811 
812                 return areEquals( bytes, index, data );
813             }
814             catch ( UnsupportedEncodingException uee )
815             {
816                 // if this happens something is really strange
817                 throw new RuntimeException( uee );
818             }
819         }
820     }
821 
822 
823     /**
824      * Check if a text is present at the current position in a buffer.
825      *
826      * @param chars The buffer which contains the data
827      * @param index Current position in the buffer
828      * @param text The text we want to check
829      * @return <code>true</code> if the buffer contains the text.
830      */
831     public static int areEquals( char[] chars, int index, String text )
832     {
833         return areEquals( chars, index, text, true );
834     }
835 
836 
837     /**
838      * Check if a text is present at the current position in a buffer.
839      *
840      * @param chars The buffer which contains the data
841      * @param index Current position in the buffer
842      * @param text The text we want to check
843      * @param caseSensitive If the comparison is case-sensitive
844      * @return <code>true</code> if the buffer contains the text.
845      */
846     public static int areEquals( char[] chars, int index, String text, boolean caseSensitive )
847     {
848         if ( ( chars == null ) || ( chars.length == 0 ) || ( chars.length <= index ) || ( index < 0 )
849             || ( text == null ) )
850         {
851             return StringConstants.NOT_EQUAL;
852         }
853         else
854         {
855             char[] data = text.toCharArray();
856 
857             return areEquals( chars, index, data, caseSensitive );
858         }
859     }
860 
861 
862     /**
863      * Check if a text is present at the current position in a buffer.
864      *
865      * @param chars The buffer which contains the data
866      * @param index Current position in the buffer
867      * @param chars2 The text we want to check
868      * @return <code>true</code> if the buffer contains the text.
869      */
870     public static int areEquals( char[] chars, int index, char[] chars2 )
871     {
872         return areEquals( chars, index, chars2, true );
873     }
874 
875 
876     /**
877      * Check if a text is present at the current position in a buffer.
878      *
879      * @param chars The buffer which contains the data
880      * @param index Current position in the buffer
881      * @param chars2 The text we want to check
882      * @param caseSensitive If the comparison is case-sensitive
883      * @return <code>true</code> if the buffer contains the text.
884      */
885     public static int areEquals( char[] chars, int index, char[] chars2, boolean caseSensitive )
886     {
887         if ( ( chars == null ) || ( chars.length == 0 ) || ( chars.length <= index ) || ( index < 0 )
888             || ( chars2 == null ) || ( chars2.length == 0 )
889             || ( chars2.length > ( chars.length - index ) ) )
890         {
891             return StringConstants.NOT_EQUAL;
892         }
893         else
894         {
895             for ( int i = 0; i < chars2.length; i++ )
896             {
897                 char c1 = chars[index++];
898                 char c2 = chars2[i];
899 
900                 if ( !caseSensitive )
901                 {
902                     c1 = Character.toLowerCase( c1 );
903                     c2 = Character.toLowerCase( c2 );
904                 }
905 
906                 if ( c1 != c2 )
907                 {
908                     return StringConstants.NOT_EQUAL;
909                 }
910             }
911 
912             return index;
913         }
914     }
915 
916 
917     /**
918      * Check if a text is present at the current position in a buffer.
919      *
920      * @param bytes The buffer which contains the data
921      * @param index Current position in the buffer
922      * @param bytes2 The text we want to check
923      * @return <code>true</code> if the buffer contains the text.
924      */
925     public static int areEquals( byte[] bytes, int index, byte[] bytes2 )
926     {
927         if ( ( bytes == null ) || ( bytes.length == 0 ) || ( bytes.length <= index ) || ( index < 0 )
928             || ( bytes2 == null ) || ( bytes2.length == 0 )
929             || ( bytes2.length > ( bytes.length - index ) ) )
930         {
931             return StringConstants.NOT_EQUAL;
932         }
933         else
934         {
935             for ( int i = 0; i < bytes2.length; i++ )
936             {
937                 if ( bytes[index++] != bytes2[i] )
938                 {
939                     return StringConstants.NOT_EQUAL;
940                 }
941             }
942 
943             return index;
944         }
945     }
946 
947 
948     /**
949      * <p>
950      * Checks if a String is empty ("") or null.
951      * </p>
952      *
953      * <pre>
954      *  StringUtils.isEmpty(null)      = true
955      *  StringUtils.isEmpty(&quot;&quot;)        = true
956      *  StringUtils.isEmpty(&quot; &quot;)       = false
957      *  StringUtils.isEmpty(&quot;bob&quot;)     = false
958      *  StringUtils.isEmpty(&quot;  bob  &quot;) = false
959      * </pre>
960      *
961      * <p>
962      * NOTE: This method changed in Lang version 2.0. It no longer trims the
963      * String. That functionality is available in isBlank().
964      * </p>
965      *
966      * @param str the String to check, may be null
967      * @return <code>true</code> if the String is empty or null
968      */
969     public static boolean isEmpty( String str )
970     {
971         return ( str == null ) || ( str.length() == 0 );
972     }
973 
974 
975     /**
976      * Checks if a bytes array is empty or null.
977      *
978      * @param bytes The bytes array to check, may be null
979      * @return <code>true</code> if the bytes array is empty or null
980      */
981     public static boolean isEmpty( byte[] bytes )
982     {
983         return ( bytes == null ) || ( bytes.length == 0 );
984     }
985 
986 
987     /**
988      * <p>
989      * Removes spaces (char &lt;= 32) from both start and ends of this String,
990      * handling <code>null</code> by returning <code>null</code>.
991      * </p>
992      * Trim removes start and end characters &lt;= 32.
993      *
994      * <pre>
995      *  StringUtils.trim(null)          = null
996      *  StringUtils.trim(&quot;&quot;)            = &quot;&quot;
997      *  StringUtils.trim(&quot;     &quot;)       = &quot;&quot;
998      *  StringUtils.trim(&quot;abc&quot;)         = &quot;abc&quot;
999      *  StringUtils.trim(&quot;    abc    &quot;) = &quot;abc&quot;
1000      * </pre>
1001      *
1002      * @param str the String to be trimmed, may be null
1003      * @return the trimmed string, <code>null</code> if null String input
1004      */
1005     public static String trim( String str )
1006     {
1007         return ( isEmpty( str ) ? "" : str.trim() );
1008     }
1009 
1010 
1011     /**
1012      * <p>
1013      * Removes spaces (char &lt;= 32) from both start and ends of this bytes
1014      * array, handling <code>null</code> by returning <code>null</code>.
1015      * </p>
1016      * Trim removes start and end characters &lt;= 32.
1017      *
1018      * <pre>
1019      *  StringUtils.trim(null)          = null
1020      *  StringUtils.trim(&quot;&quot;)            = &quot;&quot;
1021      *  StringUtils.trim(&quot;     &quot;)       = &quot;&quot;
1022      *  StringUtils.trim(&quot;abc&quot;)         = &quot;abc&quot;
1023      *  StringUtils.trim(&quot;    abc    &quot;) = &quot;abc&quot;
1024      * </pre>
1025      *
1026      * @param bytes the byte array to be trimmed, may be null
1027      *
1028      * @return the trimmed byte array
1029      */
1030     public static byte[] trim( byte[] bytes )
1031     {
1032         if ( isEmpty( bytes ) )
1033         {
1034             return StringConstants.EMPTY_BYTES;
1035         }
1036 
1037         int start = trimLeft( bytes, 0 );
1038         int end = trimRight( bytes, bytes.length - 1 );
1039 
1040         int length = end - start + 1;
1041 
1042         if ( length != 0 )
1043         {
1044             byte[] newBytes = new byte[end - start + 1];
1045 
1046             System.arraycopy( bytes, start, newBytes, 0, length );
1047 
1048             return newBytes;
1049         }
1050         else
1051         {
1052             return StringConstants.EMPTY_BYTES;
1053         }
1054     }
1055 
1056 
1057     /**
1058      * <p>
1059      * Removes spaces (char &lt;= 32) from start of this String, handling
1060      * <code>null</code> by returning <code>null</code>.
1061      * </p>
1062      * Trim removes start characters &lt;= 32.
1063      *
1064      * <pre>
1065      *  StringUtils.trimLeft(null)          = null
1066      *  StringUtils.trimLeft(&quot;&quot;)            = &quot;&quot;
1067      *  StringUtils.trimLeft(&quot;     &quot;)       = &quot;&quot;
1068      *  StringUtils.trimLeft(&quot;abc&quot;)         = &quot;abc&quot;
1069      *  StringUtils.trimLeft(&quot;    abc    &quot;) = &quot;abc    &quot;
1070      * </pre>
1071      *
1072      * @param str the String to be trimmed, may be null
1073      * @return the trimmed string, <code>null</code> if null String input
1074      */
1075     public static String trimLeft( String str )
1076     {
1077         if ( isEmpty( str ) )
1078         {
1079             return "";
1080         }
1081 
1082         int start = 0;
1083         int end = str.length();
1084 
1085         while ( ( start < end ) && ( str.charAt( start ) == ' ' ) )
1086         {
1087             start++;
1088         }
1089 
1090         return ( start == 0 ? str : str.substring( start ) );
1091     }
1092 
1093 
1094     /**
1095      * <p>
1096      * Removes spaces (char &lt;= 32) from start of this array, handling
1097      * <code>null</code> by returning <code>null</code>.
1098      * </p>
1099      * Trim removes start characters &lt;= 32.
1100      *
1101      * <pre>
1102      *  StringUtils.trimLeft(null)          = null
1103      *  StringUtils.trimLeft(&quot;&quot;)            = &quot;&quot;
1104      *  StringUtils.trimLeft(&quot;     &quot;)       = &quot;&quot;
1105      *  StringUtils.trimLeft(&quot;abc&quot;)         = &quot;abc&quot;
1106      *  StringUtils.trimLeft(&quot;    abc    &quot;) = &quot;abc    &quot;
1107      * </pre>
1108      *
1109      * @param chars the chars array to be trimmed, may be null
1110      * @return the position of the first char which is not a space, or the last
1111      *         position of the array.
1112      */
1113     public static int trimLeft( char[] chars, int pos )
1114     {
1115         if ( chars == null )
1116         {
1117             return pos;
1118         }
1119 
1120         while ( ( pos < chars.length ) && ( chars[pos] == ' ' ) )
1121         {
1122             pos++;
1123         }
1124 
1125         return pos;
1126     }
1127 
1128 
1129     /**
1130      * <p>
1131      * Removes spaces (char &lt;= 32) from a position in this array, handling
1132      * <code>null</code> by returning <code>null</code>.
1133      * </p>
1134      * Trim removes start characters &lt;= 32.
1135      *
1136      * <pre>
1137      *  StringUtils.trimLeft(null)          = null
1138      *  StringUtils.trimLeft(&quot;&quot;,...)            = &quot;&quot;
1139      *  StringUtils.trimLeft(&quot;     &quot;,...)       = &quot;&quot;
1140      *  StringUtils.trimLeft(&quot;abc&quot;,...)         = &quot;abc&quot;
1141      *  StringUtils.trimLeft(&quot;    abc    &quot;,...) = &quot;abc    &quot;
1142      * </pre>
1143      *
1144      * @param string the string to be trimmed, may be null
1145      * @param pos The starting position
1146      */
1147     public static void trimLeft( String string, Position pos )
1148     {
1149         if ( string == null )
1150         {
1151             return;
1152         }
1153 
1154         int length = string.length();
1155 
1156         while ( ( pos.start < length ) && ( string.charAt( pos.start ) == ' ' ) )
1157         {
1158             pos.start++;
1159         }
1160 
1161         pos.end = pos.start;
1162     }
1163 
1164 
1165     /**
1166      * <p>
1167      * Removes spaces (char &lt;= 32) from a position in this array, handling
1168      * <code>null</code> by returning <code>null</code>.
1169      * </p>
1170      * Trim removes start characters &lt;= 32.
1171      *
1172      * <pre>
1173      *  StringUtils.trimLeft(null)          = null
1174      *  StringUtils.trimLeft(&quot;&quot;,...)            = &quot;&quot;
1175      *  StringUtils.trimLeft(&quot;     &quot;,...)       = &quot;&quot;
1176      *  StringUtils.trimLeft(&quot;abc&quot;,...)         = &quot;abc&quot;
1177      *  StringUtils.trimLeft(&quot;    abc    &quot;,...) = &quot;abc    &quot;
1178      * </pre>
1179      *
1180      * @param bytes the byte array to be trimmed, may be null
1181      * @param pos The starting position
1182      */
1183     public static void trimLeft( byte[] bytes, Position pos )
1184     {
1185         if ( bytes == null )
1186         {
1187             return;
1188         }
1189 
1190         int length = bytes.length;
1191 
1192         while ( ( pos.start < length ) && ( bytes[pos.start] == ' ' ) )
1193         {
1194             pos.start++;
1195         }
1196 
1197         pos.end = pos.start;
1198     }
1199 
1200 
1201     /**
1202      * <p>
1203      * Removes spaces (char &lt;= 32) from start of this array, handling
1204      * <code>null</code> by returning <code>null</code>.
1205      * </p>
1206      * Trim removes start characters &lt;= 32.
1207      *
1208      * <pre>
1209      *  StringUtils.trimLeft(null)          = null
1210      *  StringUtils.trimLeft(&quot;&quot;)            = &quot;&quot;
1211      *  StringUtils.trimLeft(&quot;     &quot;)       = &quot;&quot;
1212      *  StringUtils.trimLeft(&quot;abc&quot;)         = &quot;abc&quot;
1213      *  StringUtils.trimLeft(&quot;    abc    &quot;) = &quot;abc    &quot;
1214      * </pre>
1215      *
1216      * @param bytes the byte array to be trimmed, may be null
1217      * @return the position of the first byte which is not a space, or the last
1218      *         position of the array.
1219      */
1220     public static int trimLeft( byte[] bytes, int pos )
1221     {
1222         if ( bytes == null )
1223         {
1224             return pos;
1225         }
1226 
1227         while ( ( pos < bytes.length ) && ( bytes[pos] == ' ' ) )
1228         {
1229             pos++;
1230         }
1231 
1232         return pos;
1233     }
1234 
1235 
1236     /**
1237      * <p>
1238      * Removes spaces (char &lt;= 32) from end of this String, handling
1239      * <code>null</code> by returning <code>null</code>.
1240      * </p>
1241      * Trim removes start characters &lt;= 32.
1242      *
1243      * <pre>
1244      *  StringUtils.trimRight(null)          = null
1245      *  StringUtils.trimRight(&quot;&quot;)            = &quot;&quot;
1246      *  StringUtils.trimRight(&quot;     &quot;)       = &quot;&quot;
1247      *  StringUtils.trimRight(&quot;abc&quot;)         = &quot;abc&quot;
1248      *  StringUtils.trimRight(&quot;    abc    &quot;) = &quot;    abc&quot;
1249      * </pre>
1250      *
1251      * @param str the String to be trimmed, may be null
1252      * @return the trimmed string, <code>null</code> if null String input
1253      */
1254     public static String trimRight( String str )
1255     {
1256         if ( isEmpty( str ) )
1257         {
1258             return "";
1259         }
1260 
1261         int length = str.length();
1262         int end = length;
1263 
1264         while ( ( end > 0 ) && ( str.charAt( end - 1 ) == ' ' ) )
1265         {
1266             if ( ( end > 1 ) && ( str.charAt( end - 2 ) == '\\' ) )
1267             {
1268                 break;
1269             }
1270 
1271             end--;
1272         }
1273 
1274         return ( end == length ? str : str.substring( 0, end ) );
1275     }
1276 
1277 
1278     /**
1279      * <p>
1280      * Removes spaces (char &lt;= 32) from end of this String, handling
1281      * <code>null</code> by returning <code>null</code>.
1282      * </p>
1283      * Trim removes start characters &lt;= 32.
1284      *
1285      * <pre>
1286      *  StringUtils.trimRight(null)          = null
1287      *  StringUtils.trimRight(&quot;&quot;)            = &quot;&quot;
1288      *  StringUtils.trimRight(&quot;     &quot;)       = &quot;&quot;
1289      *  StringUtils.trimRight(&quot;abc&quot;)         = &quot;abc&quot;
1290      *  StringUtils.trimRight(&quot;    abc    &quot;) = &quot;    abc&quot;
1291      * </pre>
1292      *
1293      * @param str the String to be trimmed, may be null
1294      * @param escapedSpace The last escaped space, if any
1295      * @return the trimmed string, <code>null</code> if null String input
1296      */
1297     public static String trimRight( String str, int escapedSpace )
1298     {
1299         if ( isEmpty( str ) )
1300         {
1301             return "";
1302         }
1303 
1304         int length = str.length();
1305         int end = length;
1306 
1307         while ( ( end > 0 ) && ( str.charAt( end - 1 ) == ' ' ) && ( end > escapedSpace ) )
1308         {
1309             if ( ( end > 1 ) && ( str.charAt( end - 2 ) == '\\' ) )
1310             {
1311                 break;
1312             }
1313 
1314             end--;
1315         }
1316 
1317         return ( end == length ? str : str.substring( 0, end ) );
1318     }
1319 
1320 
1321     /**
1322      * <p>
1323      * Removes spaces (char &lt;= 32) from end of this array, handling
1324      * <code>null</code> by returning <code>null</code>.
1325      * </p>
1326      * Trim removes start characters &lt;= 32.
1327      *
1328      * <pre>
1329      *  StringUtils.trimRight(null)          = null
1330      *  StringUtils.trimRight(&quot;&quot;)            = &quot;&quot;
1331      *  StringUtils.trimRight(&quot;     &quot;)       = &quot;&quot;
1332      *  StringUtils.trimRight(&quot;abc&quot;)         = &quot;abc&quot;
1333      *  StringUtils.trimRight(&quot;    abc    &quot;) = &quot;    abc&quot;
1334      * </pre>
1335      *
1336      * @param chars the chars array to be trimmed, may be null
1337      * @return the position of the first char which is not a space, or the last
1338      *         position of the array.
1339      */
1340     public static int trimRight( char[] chars, int pos )
1341     {
1342         if ( chars == null )
1343         {
1344             return pos;
1345         }
1346 
1347         while ( ( pos >= 0 ) && ( chars[pos - 1] == ' ' ) )
1348         {
1349             pos--;
1350         }
1351 
1352         return pos;
1353     }
1354 
1355 
1356     /**
1357      * <p>
1358      * Removes spaces (char &lt;= 32) from end of this string, handling
1359      * <code>null</code> by returning <code>null</code>.
1360      * </p>
1361      * Trim removes start characters &lt;= 32.
1362      *
1363      * <pre>
1364      *  StringUtils.trimRight(null)          = null
1365      *  StringUtils.trimRight(&quot;&quot;)            = &quot;&quot;
1366      *  StringUtils.trimRight(&quot;     &quot;)       = &quot;&quot;
1367      *  StringUtils.trimRight(&quot;abc&quot;)         = &quot;abc&quot;
1368      *  StringUtils.trimRight(&quot;    abc    &quot;) = &quot;    abc&quot;
1369      * </pre>
1370      *
1371      * @param string the string to be trimmed, may be null
1372      * @return the position of the first char which is not a space, or the last
1373      *         position of the string.
1374      */
1375     public static String trimRight( String string, Position pos )
1376     {
1377         if ( string == null )
1378         {
1379             return "";
1380         }
1381 
1382         while ( ( pos.end >= 0 ) && ( string.charAt( pos.end - 1 ) == ' ' ) )
1383         {
1384             if ( ( pos.end > 1 ) && ( string.charAt( pos.end - 2 ) == '\\' ) )
1385             {
1386                 break;
1387             }
1388 
1389             pos.end--;
1390         }
1391 
1392         return ( pos.end == string.length() ? string : string.substring( 0, pos.end ) );
1393     }
1394 
1395 
1396     /**
1397      * <p>
1398      * Removes spaces (char &lt;= 32) from end of this string, handling
1399      * <code>null</code> by returning <code>null</code>.
1400      * </p>
1401      * Trim removes start characters &lt;= 32.
1402      *
1403      * <pre>
1404      *  StringUtils.trimRight(null)          = null
1405      *  StringUtils.trimRight(&quot;&quot;)            = &quot;&quot;
1406      *  StringUtils.trimRight(&quot;     &quot;)       = &quot;&quot;
1407      *  StringUtils.trimRight(&quot;abc&quot;)         = &quot;abc&quot;
1408      *  StringUtils.trimRight(&quot;    abc    &quot;) = &quot;    abc&quot;
1409      * </pre>
1410      *
1411      * @param bytes the byte array to be trimmed, may be null
1412      * @return the position of the first char which is not a space, or the last
1413      *         position of the byte array.
1414      */
1415     public static String trimRight( byte[] bytes, Position pos )
1416     {
1417         if ( bytes == null )
1418         {
1419             return "";
1420         }
1421 
1422         while ( ( pos.end >= 0 ) && ( bytes[pos.end - 1] == ' ' ) )
1423         {
1424             if ( ( pos.end > 1 ) && ( bytes[pos.end - 2] == '\\' ) )
1425             {
1426                 break;
1427             }
1428 
1429             pos.end--;
1430         }
1431 
1432         if ( pos.end == bytes.length )
1433         {
1434             return utf8ToString( bytes );
1435         }
1436         else
1437         {
1438             return utf8ToString( bytes, pos.end );
1439         }
1440     }
1441 
1442 
1443     /**
1444      * <p>
1445      * Removes spaces (char &lt;= 32) from end of this array, handling
1446      * <code>null</code> by returning <code>null</code>.
1447      * </p>
1448      * Trim removes start characters &lt;= 32.
1449      *
1450      * <pre>
1451      *  StringUtils.trimRight(null)          = null
1452      *  StringUtils.trimRight(&quot;&quot;)            = &quot;&quot;
1453      *  StringUtils.trimRight(&quot;     &quot;)       = &quot;&quot;
1454      *  StringUtils.trimRight(&quot;abc&quot;)         = &quot;abc&quot;
1455      *  StringUtils.trimRight(&quot;    abc    &quot;) = &quot;    abc&quot;
1456      * </pre>
1457      *
1458      * @param bytes the byte array to be trimmed, may be null
1459      * @return the position of the first char which is not a space, or the last
1460      *         position of the array.
1461      */
1462     public static int trimRight( byte[] bytes, int pos )
1463     {
1464         if ( bytes == null )
1465         {
1466             return pos;
1467         }
1468 
1469         while ( ( pos >= 0 ) && ( bytes[pos] == ' ' ) )
1470         {
1471             pos--;
1472         }
1473 
1474         return pos;
1475     }
1476 
1477 
1478     /**
1479      * Get the character at a given position in a string, checking for limits
1480      *
1481      * @param string The string which contains the data
1482      * @param index Current position in the string
1483      * @return The character at the given position, or '\0' if something went wrong
1484      */
1485     public static char charAt( String string, int index )
1486     {
1487         if ( string == null )
1488         {
1489             return '\0';
1490         }
1491 
1492         int length = string.length();
1493 
1494         if ( ( length == 0 ) || ( index < 0 ) || ( index >= length ) )
1495         {
1496             return '\0';
1497         }
1498         else
1499         {
1500             return string.charAt( index );
1501         }
1502     }
1503 
1504 
1505     /**
1506      * Get the byte at a given position in a byte array, checking for limits
1507      *
1508      * @param bytes The byte[] which contains the data
1509      * @param index Current position in the byte[]
1510      * @return The byte at the given position, or '\0' if something went wrong
1511      */
1512     public static byte byteAt( byte[] bytes, int index )
1513     {
1514         if ( bytes == null )
1515         {
1516             return '\0';
1517         }
1518 
1519         int length = bytes.length;
1520 
1521         if ( ( length == 0 ) || ( index < 0 ) || ( index >= length ) )
1522         {
1523             return '\0';
1524         }
1525         else
1526         {
1527             return bytes[index];
1528         }
1529     }
1530 
1531 
1532     /**
1533      * Get the char at a given position in a byte array, checking for limits
1534      *
1535      * @param chars The char[] which contains the data
1536      * @param index Current position in the char[]
1537      * @return The byte at the given position, or '\0' if something went wrong
1538      */
1539     public static char charAt( char[] chars, int index )
1540     {
1541         if ( chars == null )
1542         {
1543             return '\0';
1544         }
1545 
1546         int length = chars.length;
1547 
1548         if ( ( length == 0 ) || ( index < 0 ) || ( index >= length ) )
1549         {
1550             return '\0';
1551         }
1552         else
1553         {
1554             return chars[index];
1555         }
1556     }
1557 
1558 
1559     /**
1560      * Transform an array of ASCII bytes to a string. the byte array should contains
1561      * only values in [0, 127].
1562      *
1563      * @param bytes The byte array to transform
1564      * @return The resulting string
1565      */
1566     public static String asciiBytesToString( byte[] bytes )
1567     {
1568         if ( ( bytes == null ) || ( bytes.length == 0 ) )
1569         {
1570             return "";
1571         }
1572 
1573         char[] result = new char[bytes.length];
1574 
1575         for ( int i = 0; i < bytes.length; i++ )
1576         {
1577             result[i] = ( char ) bytes[i];
1578         }
1579 
1580         return new String( result );
1581     }
1582 
1583 
1584     /**
1585      * Return UTF-8 encoded byte[] representation of a String
1586      *
1587      * @param string The string to be transformed to a byte array
1588      * @return The transformed byte array
1589      */
1590     public static byte[] getBytesUtf8( String string )
1591     {
1592         if ( string == null )
1593         {
1594             return EMPTY_BYTES;
1595         }
1596 
1597         try
1598         {
1599             return string.getBytes( "UTF-8" );
1600         }
1601         catch ( UnsupportedEncodingException uee )
1602         {
1603             // if this happens something is really strange
1604             throw new RuntimeException( uee );
1605         }
1606     }
1607 
1608 
1609     /**
1610      * When the string to convert to bytes is pure ascii, this is a faster 
1611      * method than the getBytesUtf8. Otherwise, it's slower.
1612      * 
1613      * @param string The string to convert to byte[]
1614      * @return The bytes 
1615      */
1616     public static byte[] getBytesUtf8Ascii( String string )
1617     {
1618         if ( string == null )
1619         {
1620             return new byte[0];
1621         }
1622 
1623         try
1624         {
1625             try
1626             {
1627                 char[] chars = string.toCharArray();
1628                 byte[] bytes = new byte[chars.length];
1629                 int pos = 0;
1630 
1631                 for ( char c : chars )
1632                 {
1633                     bytes[pos++] = UTF8[c];
1634                 }
1635 
1636                 return bytes;
1637             }
1638             catch ( ArrayIndexOutOfBoundsException aioobe )
1639             {
1640                 return string.getBytes( "UTF-8" );
1641             }
1642         }
1643         catch ( UnsupportedEncodingException uee )
1644         {
1645             // if this happens something is really strange
1646             throw new RuntimeException( uee );
1647         }
1648     }
1649 
1650 
1651     /**
1652      * Get the default charset
1653      *
1654      * @return The default charset
1655      */
1656     public static String getDefaultCharsetName()
1657     {
1658         if ( null == defaultCharset )
1659         {
1660             try
1661             {
1662                 // Try with jdk 1.5 method, if we are using a 1.5 jdk :)
1663                 Method method = Charset.class.getMethod( "defaultCharset", new Class[0] );
1664                 defaultCharset = ( ( Charset ) method.invoke( null, new Object[0] ) ).name();
1665             }
1666             catch ( NoSuchMethodException e )
1667             {
1668                 // fall back to old method
1669                 defaultCharset = new OutputStreamWriter( new ByteArrayOutputStream() ).getEncoding();
1670             }
1671             catch ( InvocationTargetException e )
1672             {
1673                 // fall back to old method
1674                 defaultCharset = new OutputStreamWriter( new ByteArrayOutputStream() ).getEncoding();
1675             }
1676             catch ( IllegalAccessException e )
1677             {
1678                 // fall back to old method
1679                 defaultCharset = new OutputStreamWriter( new ByteArrayOutputStream() ).getEncoding();
1680             }
1681         }
1682 
1683         return defaultCharset;
1684     }
1685 
1686 
1687     /**
1688      * <p>
1689      * Compares two Strings, returning <code>true</code> if they are equal.
1690      * </p>
1691      * <p>
1692      * <code>null</code>s are handled without exceptions. Two
1693      * <code>null</code> references are considered to be equal. The comparison
1694      * is case sensitive.
1695      * </p>
1696      *
1697      * <pre>
1698      *  StringUtils.equals(null, null)   = true
1699      *  StringUtils.equals(null, &quot;abc&quot;)  = false
1700      *  StringUtils.equals(&quot;abc&quot;, null)  = false
1701      *  StringUtils.equals(&quot;abc&quot;, &quot;abc&quot;) = true
1702      *  StringUtils.equals(&quot;abc&quot;, &quot;ABC&quot;) = false
1703      * </pre>
1704      *
1705      * @see String#equals(Object)
1706      * @param str1 the first String, may be null
1707      * @param str2 the second String, may be null
1708      * @return <code>true</code> if the Strings are equal, case sensitive, or
1709      *         both <code>null</code>
1710      */
1711     public static boolean equals( String str1, String str2 )
1712     {
1713         return str1 == null ? str2 == null : str1.equals( str2 );
1714     }
1715 
1716 
1717     /**
1718      * Utility method that return a String representation of a list
1719      *
1720      * @param list The list to transform to a string
1721      * @return A csv string
1722      */
1723     public static String listToString( List<?> list )
1724     {
1725         if ( ( list == null ) || ( list.size() == 0 ) )
1726         {
1727             return "";
1728         }
1729 
1730         StringBuilder sb = new StringBuilder();
1731         boolean isFirst = true;
1732 
1733         for ( Object elem : list )
1734         {
1735             if ( isFirst )
1736             {
1737                 isFirst = false;
1738             }
1739             else
1740             {
1741                 sb.append( ", " );
1742             }
1743 
1744             sb.append( elem );
1745         }
1746 
1747         return sb.toString();
1748     }
1749 
1750 
1751     /**
1752      * Utility method that return a String representation of a set
1753      *
1754      * @param set The set to transform to a string
1755      * @return A csv string
1756      */
1757     public static String setToString( Set<?> set )
1758     {
1759         if ( ( set == null ) || ( set.size() == 0 ) )
1760         {
1761             return "";
1762         }
1763 
1764         StringBuilder sb = new StringBuilder();
1765         boolean isFirst = true;
1766 
1767         for ( Object elem : set )
1768         {
1769             if ( isFirst )
1770             {
1771                 isFirst = false;
1772             }
1773             else
1774             {
1775                 sb.append( ", " );
1776             }
1777 
1778             sb.append( elem );
1779         }
1780 
1781         return sb.toString();
1782     }
1783 
1784 
1785     /**
1786      * Utility method that return a String representation of a list
1787      *
1788      * @param list The list to transform to a string
1789      * @param tabs The tabs to add in front of the elements
1790      * @return A csv string
1791      */
1792     public static String listToString( List<?> list, String tabs )
1793     {
1794         if ( ( list == null ) || ( list.size() == 0 ) )
1795         {
1796             return "";
1797         }
1798 
1799         StringBuffer sb = new StringBuffer();
1800 
1801         for ( Object elem : list )
1802         {
1803             sb.append( tabs );
1804             sb.append( elem );
1805             sb.append( '\n' );
1806         }
1807 
1808         return sb.toString();
1809     }
1810 
1811 
1812     /**
1813      * Utility method that return a String representation of a map. The elements
1814      * will be represented as "key = value"
1815      *
1816      * @param map The map to transform to a string
1817      * @return A csv string
1818      */
1819     public static String mapToString( Map<?, ?> map )
1820     {
1821         if ( ( map == null ) || ( map.size() == 0 ) )
1822         {
1823             return "";
1824         }
1825 
1826         StringBuffer sb = new StringBuffer();
1827         boolean isFirst = true;
1828 
1829         for ( Map.Entry<?, ?> entry : map.entrySet() )
1830         {
1831             if ( isFirst )
1832             {
1833                 isFirst = false;
1834             }
1835             else
1836             {
1837                 sb.append( ", " );
1838             }
1839 
1840             sb.append( entry.getKey() );
1841             sb.append( " = '" ).append( entry.getValue() ).append( "'" );
1842         }
1843 
1844         return sb.toString();
1845     }
1846 
1847 
1848     /**
1849      * Utility method that return a String representation of a map. The elements
1850      * will be represented as "key = value"
1851      *
1852      * @param map The map to transform to a string
1853      * @param tabs The tabs to add in ffront of the elements
1854      * @return A csv string
1855      */
1856     public static String mapToString( Map<?, ?> map, String tabs )
1857     {
1858         if ( ( map == null ) || ( map.size() == 0 ) )
1859         {
1860             return "";
1861         }
1862 
1863         StringBuffer sb = new StringBuffer();
1864 
1865         for ( Map.Entry<?, ?> entry : map.entrySet() )
1866         {
1867             sb.append( tabs );
1868             sb.append( entry.getKey() );
1869 
1870             sb.append( " = '" ).append( entry.getValue().toString() ).append( "'\n" );
1871         }
1872 
1873         return sb.toString();
1874     }
1875 
1876 
1877     /**
1878      * Rewrote the toLowercase method to improve performances.
1879      * In Ldap, attributesType are supposed to use ASCII chars :
1880      * 'a'-'z', 'A'-'Z', '0'-'9', '.' and '-' only.
1881      *
1882      * @param value The String to lowercase
1883      * @return The lowercase string
1884      */
1885     public static String toLowerCase( String value )
1886     {
1887         if ( ( null == value ) || ( value.length() == 0 ) )
1888         {
1889             return "";
1890         }
1891 
1892         char[] chars = value.toCharArray();
1893 
1894         for ( int i = 0; i < chars.length; i++ )
1895         {
1896             chars[i] = TO_LOWER_CASE[chars[i]];
1897         }
1898 
1899         return new String( chars );
1900     }
1901 
1902 
1903     /**
1904      * Rewrote the toLowercase method to improve performances.
1905      * In Ldap, attributesType are supposed to use ASCII chars :
1906      * 'a'-'z', 'A'-'Z', '0'-'9', '.' and '-' only.
1907      *
1908      * @param value The byte[] to lowercase
1909      * @return The lowercase string
1910      */
1911     public static String toLowerCase( byte[] value )
1912     {
1913         if ( ( null == value ) || ( value.length == 0 ) )
1914         {
1915             return "";
1916         }
1917 
1918         for ( int i = 0; i < value.length; i++ )
1919         {
1920             value[i] = TO_LOWER_CASE_BYTE[value[i]];
1921         }
1922 
1923         return new String( value );
1924     }
1925 
1926 
1927     /**
1928      * Rewrote the toLowercase method to improve performances.
1929      * In Ldap, attributesType are supposed to use ASCII chars :
1930      * 'a'-'z', 'A'-'Z', '0'-'9', '.' and '-' only.
1931      *
1932      * @param value The String to uppercase
1933      * @return The uppercase string
1934      */
1935     public static String toUpperCase( String value )
1936     {
1937         if ( ( null == value ) || ( value.length() == 0 ) )
1938         {
1939             return "";
1940         }
1941 
1942         char[] chars = value.toCharArray();
1943 
1944         for ( int i = 0; i < chars.length; i++ )
1945         {
1946             chars[i] = UPPER_CASE[chars[i]];
1947         }
1948 
1949         return new String( chars );
1950     }
1951 
1952 
1953     /**
1954      * <p>
1955      * Converts a String to upper case as per {@link String#toUpperCase()}.
1956      * </p>
1957      * <p>
1958      * A <code>null</code> input String returns <code>null</code>.
1959      * </p>
1960      *
1961      * <pre>
1962      *  StringUtils.upperCase(null)  = null
1963      *  StringUtils.upperCase(&quot;&quot;)    = &quot;&quot;
1964      *  StringUtils.upperCase(&quot;aBc&quot;) = &quot;ABC&quot;
1965      * </pre>
1966      *
1967      * @param str the String to upper case, may be null
1968      * @return the upper cased String, <code>null</code> if null String input
1969      */
1970     public static String upperCase( String str )
1971     {
1972         if ( str == null )
1973         {
1974             return null;
1975         }
1976 
1977         return str.toUpperCase();
1978     }
1979 
1980 
1981     /**
1982      * <p>
1983      * Converts a String to lower case as per {@link String#toLowerCase()}.
1984      * </p>
1985      * <p>
1986      * A <code>null</code> input String returns <code>null</code>.
1987      * </p>
1988      *
1989      * <pre>
1990      *  StringUtils.lowerCase(null)  = null
1991      *  StringUtils.lowerCase(&quot;&quot;)    = &quot;&quot;
1992      *  StringUtils.lowerCase(&quot;aBc&quot;) = &quot;abc&quot;
1993      * </pre>
1994      *
1995      * @param str the String to lower case, may be null
1996      * @return the lower cased String, <code>null</code> if null String input
1997      */
1998     public static String lowerCase( String str )
1999     {
2000         if ( str == null )
2001         {
2002             return null;
2003         }
2004 
2005         return str.toLowerCase( Locale.ENGLISH );
2006     }
2007 
2008 
2009     /**
2010      * Rewrote the toLowercase method to improve performances.
2011      * In Ldap, attributesType are supposed to use ASCII chars :
2012      * 'a'-'z', 'A'-'Z', '0'-'9', '.' and '-' only. We will take
2013      * care of any other chars either.
2014      *
2015      * @param str The String to lowercase
2016      * @return The lowercase string
2017      */
2018     public static String lowerCaseAscii( String str )
2019     {
2020         if ( str == null )
2021         {
2022             return null;
2023         }
2024 
2025         char[] chars = str.toCharArray();
2026         int pos = 0;
2027 
2028         for ( char c : chars )
2029         {
2030             chars[pos++] = TO_LOWER_CASE[c];
2031         }
2032 
2033         return new String( chars );
2034     }
2035 
2036 
2037     /**
2038      *
2039      * Check that a String is a valid PrintableString. A PrintableString contains only
2040      * the following set of chars :
2041      * { ' ', ''', '(', ')', '+', '-', '.', '/', [0-9], ':', '=', '?', [A-Z], [a-z]}
2042      *
2043      * @param str The String to check
2044      * @return <code>true</code> if the string is a PrintableString or is empty,
2045      * <code>false</code> otherwise
2046      */
2047     public static boolean isPrintableString( String str )
2048     {
2049         if ( ( str == null ) || ( str.length() == 0 ) )
2050         {
2051             return true;
2052         }
2053 
2054         for ( char c : str.toCharArray() )
2055         {
2056             if ( ( c > 127 ) || !IS_PRINTABLE_CHAR[c] )
2057             {
2058                 return false;
2059             }
2060         }
2061 
2062         return true;
2063     }
2064 
2065 
2066     /**
2067      * <p>
2068      * Checks if a String is not empty ("") and not null.
2069      * </p>
2070      *
2071      * <pre>
2072      *  StringUtils.isNotEmpty(null)      = false
2073      *  StringUtils.isNotEmpty(&quot;&quot;)        = false
2074      *  StringUtils.isNotEmpty(&quot; &quot;)       = true
2075      *  StringUtils.isNotEmpty(&quot;bob&quot;)     = true
2076      *  StringUtils.isNotEmpty(&quot;  bob  &quot;) = true
2077      * </pre>
2078      *
2079      * @param str the String to check, may be null
2080      * @return <code>true</code> if the String is not empty and not null
2081      */
2082     public static boolean isNotEmpty( String str )
2083     {
2084         return ( str != null ) && ( str.length() > 0 );
2085     }
2086 
2087 
2088     /**
2089      *
2090      * Check that a String is a valid IA5String. An IA5String contains only
2091      * char which values is between [0, 7F]
2092      *
2093      * @param str The String to check
2094      * @return <code>true</code> if the string is an IA5String or is empty,
2095      * <code>false</code> otherwise
2096      */
2097     public static boolean isIA5String( String str )
2098     {
2099         if ( ( str == null ) || ( str.length() == 0 ) )
2100         {
2101             return true;
2102         }
2103 
2104         // All the chars must be in [0x00, 0x7F]
2105         for ( char c : str.toCharArray() )
2106         {
2107             if ( ( c < 0 ) || ( c > 0x7F ) )
2108             {
2109                 return false;
2110             }
2111         }
2112 
2113         return true;
2114     }
2115 
2116 
2117     /**
2118      * Checks to see if a String is a valid UUID.
2119      *
2120      * @param uuid the UUID to check for validity
2121      * @return true if the UUID is valid, false otherwise
2122      */
2123     public static boolean isValidUuid( String uuid )
2124     {
2125         byte[] b = uuid.getBytes();
2126 
2127         if ( b.length < 36 )
2128         {
2129             return false;
2130         }
2131 
2132         if ( isHex( b[0] ) && isHex( b[1] ) && isHex( b[2] ) && isHex( b[3] )
2133             && isHex( b[4] ) && isHex( b[5] ) && isHex( b[6] ) && isHex( b[7] )
2134             && ( b[8] == '-' )
2135             && isHex( b[9] ) && isHex( b[10] ) && isHex( b[11] ) && isHex( b[12] )
2136             && ( b[13] == '-' )
2137             && isHex( b[14] ) && isHex( b[15] ) && isHex( b[16] ) && isHex( b[17] )
2138             && ( b[18] == '-' )
2139             && isHex( b[19] ) && isHex( b[20] ) && isHex( b[21] ) && isHex( b[22] )
2140             && ( b[23] == '-' )
2141             && isHex( b[24] ) && isHex( b[25] ) && isHex( b[26] ) && isHex( b[27] )
2142             && isHex( b[28] ) && isHex( b[29] ) && isHex( b[30] ) && isHex( b[31] )
2143             && isHex( b[32] ) && isHex( b[33] ) && isHex( b[34] ) && isHex( b[35] ) )
2144         {
2145             // There is not that much more we can check.
2146             LOG.debug( "Syntax valid for '{}'", uuid );
2147             return true;
2148         }
2149 
2150         LOG.debug( "Syntax invalid for '{}'", uuid );
2151         return false;
2152     }
2153 
2154 
2155     /**
2156      * converts the bytes of a UUID to string
2157      *
2158      * @param bytes bytes of a UUID
2159      * @return UUID in string format
2160      */
2161     public static String uuidToString( byte[] bytes )
2162     {
2163         if ( ( bytes == null ) || ( bytes.length != 16 ) )
2164         {
2165             return "Invalid UUID";
2166         }
2167 
2168         char[] hex = encodeHex( bytes );
2169         StringBuffer sb = new StringBuffer();
2170         sb.append( hex, 0, 8 );
2171         sb.append( '-' );
2172         sb.append( hex, 8, 4 );
2173         sb.append( '-' );
2174         sb.append( hex, 12, 4 );
2175         sb.append( '-' );
2176         sb.append( hex, 16, 4 );
2177         sb.append( '-' );
2178         sb.append( hex, 20, 12 );
2179 
2180         return Strings.toLowerCase( sb.toString() );
2181     }
2182 
2183 
2184     /**
2185      * converts the string representation of an UUID to bytes
2186      *
2187      * @param string the string representation of an UUID
2188      * @return the bytes, null if the the syntax is not valid
2189      */
2190     public static byte[] uuidToBytes( String string )
2191     {
2192         if ( !isValidUuid( string ) )
2193         {
2194             return null;
2195         }
2196 
2197         char[] chars = string.toCharArray();
2198         byte[] bytes = new byte[16];
2199         bytes[0] = getHexValue( chars[0], chars[1] );
2200         bytes[1] = getHexValue( chars[2], chars[3] );
2201         bytes[2] = getHexValue( chars[4], chars[5] );
2202         bytes[3] = getHexValue( chars[6], chars[7] );
2203 
2204         bytes[4] = getHexValue( chars[9], chars[10] );
2205         bytes[5] = getHexValue( chars[11], chars[12] );
2206 
2207         bytes[6] = getHexValue( chars[14], chars[15] );
2208         bytes[7] = getHexValue( chars[16], chars[17] );
2209 
2210         bytes[8] = getHexValue( chars[19], chars[20] );
2211         bytes[9] = getHexValue( chars[21], chars[22] );
2212 
2213         bytes[10] = getHexValue( chars[24], chars[25] );
2214         bytes[11] = getHexValue( chars[26], chars[27] );
2215         bytes[12] = getHexValue( chars[28], chars[29] );
2216         bytes[13] = getHexValue( chars[30], chars[31] );
2217         bytes[14] = getHexValue( chars[32], chars[33] );
2218         bytes[15] = getHexValue( chars[34], chars[35] );
2219 
2220         return bytes;
2221     }
2222 
2223 
2224     /**
2225      * Copy a byte array into a new byte array
2226      *
2227      * @param value the byte array to copy
2228      * @return The copied byte array
2229      */
2230     public static byte[] copy( byte[] value )
2231     {
2232         if ( isEmpty( value ) )
2233         {
2234             return StringConstants.EMPTY_BYTES;
2235         }
2236 
2237         byte[] copy = new byte[value.length];
2238         System.arraycopy( value, 0, copy, 0, value.length );
2239 
2240         return copy;
2241     }
2242 
2243 
2244     /**
2245      * From commons-httpclients. Converts the byte array of HTTP content
2246      * characters to a string. If the specified charset is not supported,
2247      * default system encoding is used.
2248      *
2249      * @param data the byte array to be encoded
2250      * @param offset the index of the first byte to encode
2251      * @param length the number of bytes to encode
2252      * @param charset the desired character encoding
2253      * @return The result of the conversion.
2254      * @since 3.0
2255      */
2256     public static String getString( final byte[] data, int offset, int length, String charset )
2257     {
2258         if ( data == null )
2259         {
2260             throw new IllegalArgumentException( I18n.err( I18n.ERR_04411 ) );
2261         }
2262 
2263         if ( ( charset == null ) || ( charset.length() == 0 ) )
2264         {
2265             throw new IllegalArgumentException( I18n.err( I18n.ERR_04412 ) );
2266         }
2267 
2268         try
2269         {
2270             return new String( data, offset, length, charset );
2271         }
2272         catch ( UnsupportedEncodingException e )
2273         {
2274             return new String( data, offset, length );
2275         }
2276     }
2277 
2278 
2279     /**
2280      * From commons-httpclients. Converts the byte array of HTTP content
2281      * characters to a string. If the specified charset is not supported,
2282      * default system encoding is used.
2283      *
2284      * @param data the byte array to be encoded
2285      * @param charset the desired character encoding
2286      * @return The result of the conversion.
2287      * @since 3.0
2288      */
2289     public static String getString( final byte[] data, String charset )
2290     {
2291         return getString( data, 0, data.length, charset );
2292     }
2293 
2294 
2295     /**
2296      * Create a new UUID using a long as the least significant bits
2297      * 
2298      * @param value The least significant bits.
2299      * @return
2300      */
2301     public static String getUUID( long value )
2302     {
2303         return new UUID( 0, value ).toString();
2304     }
2305 }