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.mavibot.btree.util;
021
022
023import java.io.UnsupportedEncodingException;
024import java.nio.ByteBuffer;
025import java.util.List;
026import java.util.Map;
027import java.util.Set;
028
029
030/**
031 * Various string manipulation methods that are more efficient then chaining
032 * string operations: all is done in the same buffer without creating a bunch of
033 * string objects.
034 *
035 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
036 */
037public final class Strings
038{
039    /** Hex chars */
040    private static final byte[] HEX_CHAR = new byte[]
041        { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
042
043    /** A empty byte array */
044    public static final byte[] EMPTY_BYTES = new byte[0];
045
046
047    /**
048     * Helper function that dump an array of bytes in hex form
049     *
050     * @param buffer The bytes array to dump
051     * @return A string representation of the array of bytes
052     */
053    public static String dumpBytes( byte[] buffer )
054    {
055        if ( buffer == null )
056        {
057            return "";
058        }
059
060        StringBuffer sb = new StringBuffer();
061
062        for ( byte b : buffer )
063        {
064            sb.append( "0x" ).append( ( char ) ( HEX_CHAR[( b & 0x00F0 ) >> 4] ) ).append(
065                ( char ) ( HEX_CHAR[b & 0x000F] ) ).append( " " );
066        }
067
068        return sb.toString();
069    }
070
071
072    /**
073     * Helper function that dump a byte in hex form
074     *
075     * @param octet The byte to dump
076     * @return A string representation of the byte
077     */
078    public static String dumpByte( byte octet )
079    {
080        return new String( new byte[]
081            { '0', 'x', HEX_CHAR[( octet & 0x00F0 ) >> 4], HEX_CHAR[octet & 0x000F] } );
082    }
083
084
085    /**
086     * Helper function that returns a char from an hex
087     *
088     * @param hex The hex to dump
089     * @return A char representation of the hex
090     */
091    public static char dumpHex( byte hex )
092    {
093        return ( char ) HEX_CHAR[hex & 0x000F];
094    }
095
096
097    /**
098     * Helper function that dump an array of bytes in hex pair form,
099     * without '0x' and space chars
100     *
101     * @param buffer The bytes array to dump
102     * @return A string representation of the array of bytes
103     */
104    public static String dumpHexPairs( byte[] buffer )
105    {
106        if ( buffer == null )
107        {
108            return "";
109        }
110
111        char[] str = new char[buffer.length << 1];
112
113        int pos = 0;
114
115        for ( byte b : buffer )
116        {
117            str[pos++] = ( char ) ( HEX_CHAR[( b & 0x00F0 ) >> 4] );
118            str[pos++] = ( char ) ( HEX_CHAR[b & 0x000F] );
119        }
120
121        return new String( str );
122    }
123
124
125    /**
126     * Gets a hex string from byte array.
127     *
128     * @param res the byte array
129     * @return the hex string representing the binary values in the array
130     */
131    public static String toHexString( byte[] res )
132    {
133        StringBuffer buf = new StringBuffer( res.length << 1 );
134
135        for ( byte b : res )
136        {
137            String digit = Integer.toHexString( 0xFF & b );
138
139            if ( digit.length() == 1 )
140            {
141                digit = '0' + digit;
142            }
143
144            buf.append( digit );
145        }
146
147        return buf.toString().toUpperCase();
148    }
149
150
151    /**
152     * Get byte array from hex string
153     *
154     * @param hexString the hex string to convert to a byte array
155     * @return the byte form of the hex string.
156     */
157    public static byte[] toByteArray( String hexString )
158    {
159        int arrLength = hexString.length() >> 1;
160        byte[] buf = new byte[arrLength];
161
162        for ( int ii = 0; ii < arrLength; ii++ )
163        {
164            int index = ii << 1;
165
166            String digit = hexString.substring( index, index + 2 );
167            buf[ii] = ( byte ) Integer.parseInt( digit, 16 );
168        }
169
170        return buf;
171    }
172
173    private static final byte[] UTF8 = new byte[]
174        { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A,
175            0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C,
176            0x1D, 0x1E, 0x1F, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E,
177            0x2F, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F, 0x40,
178            0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, 0x50, 0x51, 0x52,
179            0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x5B, 0x5C, 0x5D, 0x5E, 0x5F, 0x60, 0x61, 0x62, 0x63, 0x64,
180            0x65, 0x66, 0x67, 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76,
181            0x77, 0x78, 0x79, 0x7A, 0x7B, 0x7C, 0x7D, 0x7E, 0x7F };
182
183
184    /**
185     * Return an UTF-8 encoded String
186     *
187     * @param bytes The byte array to be transformed to a String
188     * @return A String.
189     */
190    public static String utf8ToString( byte[] bytes )
191    {
192        if ( bytes == null )
193        {
194            return "";
195        }
196
197        char[] chars = new char[bytes.length];
198        int pos = 0;
199
200        try
201        {
202            for ( byte b : bytes )
203            {
204                chars[pos++] = ( char ) UTF8[b];
205            }
206        }
207        catch ( ArrayIndexOutOfBoundsException aioobe )
208        {
209            try
210            {
211                return new String( bytes, "UTF-8" );
212            }
213            catch ( UnsupportedEncodingException uee )
214            {
215                // if this happens something is really strange
216                throw new RuntimeException( uee );
217            }
218        }
219
220        return new String( chars );
221    }
222
223
224    /**
225     * Return an UTF-8 encoded String
226     *
227     * @param bytes The byte array to be transformed to a String
228     * @return A String.
229     */
230    public static String utf8ToString( ByteBuffer bytes )
231    {
232        if ( bytes == null )
233        {
234            return "";
235        }
236
237        char[] chars = new char[bytes.limit()];
238        int pos = 0;
239        int currentPos = bytes.position();
240
241        do
242        {
243            chars[pos++] = ( char ) UTF8[bytes.get()];
244        }
245        while ( bytes.position() < bytes.limit() );
246
247        // restore the buffer
248        bytes.position( currentPos );
249
250        return new String( chars );
251    }
252
253
254    /**
255     * Return an UTF-8 encoded String
256     *
257     * @param bytes The byte array to be transformed to a String
258     * @param length The length of the byte array to be converted
259     * @return A String.
260     */
261    public static String utf8ToString( byte[] bytes, int length )
262    {
263        if ( bytes == null )
264        {
265            return "";
266        }
267
268        try
269        {
270            return new String( bytes, 0, length, "UTF-8" );
271        }
272        catch ( UnsupportedEncodingException uee )
273        {
274            // if this happens something is really strange
275            throw new RuntimeException( uee );
276        }
277    }
278
279
280    /**
281     * Return an UTF-8 encoded String
282     *
283     * @param bytes  The byte array to be transformed to a String
284     * @param start the starting position in the byte array
285     * @param length The length of the byte array to be converted
286     * @return A String.
287     */
288    public static String utf8ToString( byte[] bytes, int start, int length )
289    {
290        if ( bytes == null )
291        {
292            return "";
293        }
294
295        try
296        {
297            return new String( bytes, start, length, "UTF-8" );
298        }
299        catch ( UnsupportedEncodingException uee )
300        {
301            // if this happens something is really strange
302            throw new RuntimeException( uee );
303        }
304    }
305
306
307    /**
308     * <p>
309     * Checks if a String is empty ("") or null.
310     * </p>
311     *
312     * <pre>
313     *  StringUtils.isEmpty(null)      = true
314     *  StringUtils.isEmpty(&quot;&quot;)        = true
315     *  StringUtils.isEmpty(&quot; &quot;)       = false
316     *  StringUtils.isEmpty(&quot;bob&quot;)     = false
317     *  StringUtils.isEmpty(&quot;  bob  &quot;) = false
318     * </pre>
319     *
320     * <p>
321     * NOTE: This method changed in Lang version 2.0. It no longer trims the
322     * String. That functionality is available in isBlank().
323     * </p>
324     *
325     * @param str the String to check, may be null
326     * @return <code>true</code> if the String is empty or null
327     */
328    public static boolean isEmpty( String str )
329    {
330        return ( str == null ) || ( str.length() == 0 );
331    }
332
333
334    /**
335     * Checks if a bytes array is empty or null.
336     *
337     * @param bytes The bytes array to check, may be null
338     * @return <code>true</code> if the bytes array is empty or null
339     */
340    public static boolean isEmpty( byte[] bytes )
341    {
342        return ( bytes == null ) || ( bytes.length == 0 );
343    }
344
345
346    /**
347     * Return UTF-8 encoded byte[] representation of a String
348     *
349     * @param string The string to be transformed to a byte array
350     * @return The transformed byte array
351     */
352    public static byte[] getBytesUtf8( String string )
353    {
354        if ( string == null )
355        {
356            return EMPTY_BYTES;
357        }
358
359        try
360        {
361            return string.getBytes( "UTF-8" );
362        }
363        catch ( UnsupportedEncodingException uee )
364        {
365            // if this happens something is really strange
366            throw new RuntimeException( uee );
367        }
368    }
369
370
371    /**
372     * When the string to convert to bytes is pure ascii, this is a faster 
373     * method than the getBytesUtf8. Otherwise, it's slower.
374     * 
375     * @param string The string to convert to byte[]
376     * @return The bytes 
377     */
378    public static byte[] getBytesUtf8Ascii( String string )
379    {
380        if ( string == null )
381        {
382            return new byte[0];
383        }
384
385        try
386        {
387            try
388            {
389                char[] chars = string.toCharArray();
390                byte[] bytes = new byte[chars.length];
391                int pos = 0;
392
393                for ( char c : chars )
394                {
395                    bytes[pos++] = UTF8[c];
396                }
397
398                return bytes;
399            }
400            catch ( ArrayIndexOutOfBoundsException aioobe )
401            {
402                return string.getBytes( "UTF-8" );
403            }
404        }
405        catch ( UnsupportedEncodingException uee )
406        {
407            // if this happens something is really strange
408            throw new RuntimeException( uee );
409        }
410    }
411
412
413    /**
414     * Utility method that return a String representation of a list
415     *
416     * @param list The list to transform to a string
417     * @return A csv string
418     */
419    public static String listToString( List<?> list )
420    {
421        if ( ( list == null ) || ( list.size() == 0 ) )
422        {
423            return "";
424        }
425
426        StringBuilder sb = new StringBuilder();
427        boolean isFirst = true;
428
429        for ( Object elem : list )
430        {
431            if ( isFirst )
432            {
433                isFirst = false;
434            }
435            else
436            {
437                sb.append( ", " );
438            }
439
440            sb.append( elem );
441        }
442
443        return sb.toString();
444    }
445
446
447    /**
448     * Utility method that return a String representation of a set
449     *
450     * @param set The set to transform to a string
451     * @return A csv string
452     */
453    public static String setToString( Set<?> set )
454    {
455        if ( ( set == null ) || ( set.size() == 0 ) )
456        {
457            return "";
458        }
459
460        StringBuilder sb = new StringBuilder();
461        boolean isFirst = true;
462
463        for ( Object elem : set )
464        {
465            if ( isFirst )
466            {
467                isFirst = false;
468            }
469            else
470            {
471                sb.append( ", " );
472            }
473
474            sb.append( elem );
475        }
476
477        return sb.toString();
478    }
479
480
481    /**
482     * Utility method that return a String representation of a list
483     *
484     * @param list The list to transform to a string
485     * @param tabs The tabs to add in ffront of the elements
486     * @return A csv string
487     */
488    public static String listToString( List<?> list, String tabs )
489    {
490        if ( ( list == null ) || ( list.size() == 0 ) )
491        {
492            return "";
493        }
494
495        StringBuffer sb = new StringBuffer();
496
497        for ( Object elem : list )
498        {
499            sb.append( tabs );
500            sb.append( elem );
501            sb.append( '\n' );
502        }
503
504        return sb.toString();
505    }
506
507
508    /**
509     * Utility method that return a String representation of a map. The elements
510     * will be represented as "key = value"
511     *
512     * @param map The map to transform to a string
513     * @return A csv string
514     */
515    public static String mapToString( Map<?, ?> map )
516    {
517        if ( ( map == null ) || ( map.size() == 0 ) )
518        {
519            return "";
520        }
521
522        StringBuffer sb = new StringBuffer();
523        boolean isFirst = true;
524
525        for ( Map.Entry<?, ?> entry : map.entrySet() )
526        {
527            if ( isFirst )
528            {
529                isFirst = false;
530            }
531            else
532            {
533                sb.append( ", " );
534            }
535
536            sb.append( entry.getKey() );
537            sb.append( " = '" ).append( entry.getValue() ).append( "'" );
538        }
539
540        return sb.toString();
541    }
542
543
544    /**
545     * Utility method that return a String representation of a map. The elements
546     * will be represented as "key = value"
547     *
548     * @param map The map to transform to a string
549     * @param tabs The tabs to add in ffront of the elements
550     * @return A csv string
551     */
552    public static String mapToString( Map<?, ?> map, String tabs )
553    {
554        if ( ( map == null ) || ( map.size() == 0 ) )
555        {
556            return "";
557        }
558
559        StringBuffer sb = new StringBuffer();
560
561        for ( Map.Entry<?, ?> entry : map.entrySet() )
562        {
563            sb.append( tabs );
564            sb.append( entry.getKey() );
565
566            sb.append( " = '" ).append( entry.getValue().toString() ).append( "'\n" );
567        }
568
569        return sb.toString();
570    }
571}