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 *    https://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    public 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    /** 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}