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