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