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