All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.alee.utils.TextUtils Maven / Gradle / Ivy

There is a newer version: 2.2.1
Show newest version
/*
 * This file is part of WebLookAndFeel library.
 *
 * WebLookAndFeel library is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * WebLookAndFeel library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with WebLookAndFeel library.  If not, see .
 */

package com.alee.utils;

import com.alee.utils.compare.Filter;
import com.alee.utils.text.DelayFormatException;
import com.alee.utils.text.SimpleTextProvider;
import com.alee.utils.text.TextProvider;
import com.alee.utils.xml.ColorConverter;
import com.alee.utils.xml.InsetsConverter;
import com.alee.utils.xml.PointConverter;
import com.alee.utils.xml.RectangleConverter;

import java.awt.*;
import java.lang.reflect.Array;
import java.util.*;
import java.util.List;

/**
 * This class provides a set of utilities to work with various text usage cases.
 *
 * @author Mikle Garin
 */

public final class TextUtils
{
    /**
     * Constants for time calculations.
     */
    private static final int msInWeek = 604800000;
    private static final int msInDay = 86400000;
    private static final int msInHour = 3600000;
    private static final int msInMinute = 60000;
    private static final int msInSecond = 1000;

    /**
     * Separators used to determine words in text.
     */
    private static final List textSeparators =
            Arrays.asList ( " ", ".", ",", ":", ";", "/", "\\", "\n", "\t", "|", "{", "}", "[", "]", "(", ")", "<", ">", "-", "+", "\"",
                    "'", "*", "%", "$", "#", "@", "!", "~", "^", "&", "?" );

    /**
     * Text provider for any type of objects.
     */
    private static final SimpleTextProvider simpleTextProvider = new SimpleTextProvider ();

    /**
     * Default ID part length.
     */
    private static final int idPartLength = 5;

    /**
     * Default ID prefix.
     */
    private static final String defaultIdPrefix = "WebLaF";

    /**
     * Default ID suffix.
     */
    private static final String defaultIdSuffix = "ID";

    /**
     * Returns message formatted with common string representations of the provided objects.
     *
     * @param text    message to format
     * @param objects objects to use for message formatting
     * @return message formatted with common string representations of the provided objects
     */
    public static String format ( final String text, final Object... objects )
    {
        final Object[] data = new Object[ objects != null ? objects.length : 0 ];
        if ( objects != null )
        {
            for ( int i = 0; i < objects.length; i++ )
            {
                data[ i ] = toString ( objects[ i ] );
            }
        }
        return String.format ( text, data );
    }

    /**
     * Returns text with replaced character at the specified index.
     *
     * @param text      text to replace character in
     * @param index     index of the character to replace
     * @param character replacement character
     * @return text with replaced character at the specified index
     */
    public static String replace ( final String text, final int index, final char character )
    {
        final StringBuilder sb = new StringBuilder ( text );
        sb.setCharAt ( index, character );
        return sb.toString ();
    }

    /**
     * Returns common string representation of the specified object.
     * Unlike the common {@link Object#toString()} method it will not throw {@link java.lang.NullPointerException} and returns slightly
     * different results depending on the object class type. For most it will still return the same result as {@link Object#toString()}.
     *
     * @param object object to return common string representation for
     * @return common string representation of the specified object
     */
    public static String toString ( final Object object )
    {
        if ( object == null )
        {
            return "null";
        }
        else if ( object instanceof String )
        {
            return ( String ) object;
        }
        else
        {
            return object.toString ();
        }
    }

    /**
     * Returns list of strings based on single pattern parsed using different number values in range.
     *
     * @param pattern values pattern
     * @param from    range start
     * @param to      range end
     * @return list of strings based on single pattern parsed using different number values in range
     */
    public static List numbered ( final String pattern, final int from, final int to )
    {
        final List list = new ArrayList ( Math.abs ( from - to ) );
        if ( from < to )
        {
            for ( int i = from; i <= to; i++ )
            {
                list.add ( format ( pattern, i ) );
            }
        }
        else
        {
            for ( int i = from; i >= to; i-- )
            {
                list.add ( format ( pattern, i ) );
            }
        }
        return list;
    }

    /**
     * Returns text with all line breaks removed.
     *
     * @param text text to remove line breaks from
     * @return text with all line breaks removed
     */
    public static String removeLineBreaks ( final String text )
    {
        return text.replaceAll ( "\\r\\n|\\r|\\n", "" );
    }

    /**
     * Returns first number found in text.
     *
     * @param text text to search through
     * @return first found number
     */
    public static Integer findFirstNumber ( final String text )
    {
        final StringBuilder sb = new StringBuilder ( "" );
        for ( int j = 0; j < text.length (); j++ )
        {
            final char ch = text.charAt ( j );
            if ( Character.isDigit ( ch ) )
            {
                sb.append ( ch );
            }
            else if ( sb.length () > 0 )
            {
                break;
            }
        }
        return Integer.parseInt ( sb.toString () );
    }

    /**
     * Returns a word from text at the specified location.
     *
     * @param text     text to retrieve the word from
     * @param location word location
     * @return word
     */
    public static String getWord ( final String text, final int location )
    {
        int wordStart = location;
        int wordEnd = location;

        // At the word start
        while ( wordEnd < text.length () - 1 && !textSeparators.contains ( text.substring ( wordEnd, wordEnd + 1 ) ) )
        {
            wordEnd++;
        }

        // At the word end
        while ( wordStart > 0 && !textSeparators.contains ( text.substring ( wordStart - 1, wordStart ) ) )
        {
            wordStart--;
        }

        return wordStart == wordEnd ? null : text.substring ( wordStart, wordEnd );
    }

    /**
     * Returns a word start index at the specified location.
     *
     * @param text     text to retrieve the word start index from
     * @param location word location
     * @return word start index
     */
    public static int getWordStart ( final String text, final int location )
    {
        int wordStart = location;
        while ( wordStart > 0 && !textSeparators.contains ( text.substring ( wordStart - 1, wordStart ) ) )
        {
            wordStart--;
        }
        return wordStart;
    }

    /**
     * Returns a word end index at the specified location.
     *
     * @param text     text to retrieve the word end index from
     * @param location word location
     * @return word end index
     */
    public static int getWordEnd ( final String text, final int location )
    {
        int wordEnd = location;
        while ( wordEnd < text.length () - 1 && !textSeparators.contains ( text.substring ( wordEnd, wordEnd + 1 ) ) )
        {
            wordEnd++;
        }
        return wordEnd;
    }

    /**
     * Returns text with first lines removed.
     *
     * @param text  text to crop
     * @param count lines count to crop
     * @return cropped text
     */
    public static String removeFirstLines ( final String text, final int count )
    {
        int found = 0;
        int index = 0;
        while ( found < count )
        {
            index = text.indexOf ( "\n", index );
            if ( index != -1 )
            {
                index += 1;
                found++;
            }
            else
            {
                return "";
            }
        }
        return text.substring ( index );
    }

    /**
     * Returns point extracted from text.
     *
     * @param text text to extract point from
     * @return extracted point
     */
    public static Point parsePoint ( final String text )
    {
        return parsePoint ( text, "," );
    }

    /**
     * Returns point extracted from text.
     *
     * @param text      text to extract point from
     * @param separator point values separator
     * @return extracted point
     */
    public static Point parsePoint ( final String text, final String separator )
    {
        final String[] parts = text.split ( separator );
        return parts.length == 2 ? new Point ( Integer.parseInt ( parts[ 0 ].trim () ), Integer.parseInt ( parts[ 1 ].trim () ) ) : null;
    }

    /**
     * Returns text with all control symbols removed.
     * This method works faster than simple replaceAll("\\p{Cntrl}", "").
     *
     * @param text text to modify
     * @return text without control symbols
     */
    public static String removeControlSymbols ( final String text )
    {
        final int length = text.length ();
        final char[] oldChars = new char[ length ];
        text.getChars ( 0, length, oldChars, 0 );
        int newLen = 0;
        for ( int j = 0; j < length; j++ )
        {
            final char ch = oldChars[ j ];
            if ( ch >= ' ' )
            {
                oldChars[ newLen ] = ch;
                newLen++;
            }
        }
        return text;
    }

    /**
     * Returns shortened text.
     *
     * @param text      text to shorten
     * @param maxLength maximum shortened text length
     * @param addDots   add dots at the end of the text when shortened
     * @return shortened text
     */
    public static String shortenText ( final String text, final int maxLength, final boolean addDots )
    {
        return text.length () <= maxLength ? text :
                text.substring ( 0, maxLength > 3 && addDots ? maxLength - 3 : maxLength ) + ( addDots ? "..." : "" );
    }

    /**
     * Returns shortened text.
     *
     * @param text      text to shorten
     * @param maxLength maximum shortened text length
     * @param addDots   add dots at the end of the text when shortened
     * @return shortened text
     */
    public static String shortenTextEnd ( final String text, final int maxLength, final boolean addDots )
    {
        return text.length () <= maxLength ? text :
                ( addDots ? "..." : "" ) + text.substring ( text.length () - ( maxLength > 3 && addDots ? maxLength - 3 : maxLength ) );
    }

    /**
     * Returns a list of text parts split using specified separator.
     *
     * @param string    text to split
     * @param separator text parts separator
     * @return list of split parts
     */
    public static List stringToList ( final String string, final String separator )
    {
        final List strings = new ArrayList ();
        if ( string != null )
        {
            final StringTokenizer tokenizer = new StringTokenizer ( string, separator, false );
            while ( tokenizer.hasMoreTokens () )
            {
                strings.add ( tokenizer.nextToken ().trim () );
            }
        }
        return strings;
    }

    /**
     * Returns a list of integer parts split using specified separator.
     *
     * @param string    text to split
     * @param separator text parts separator
     * @return list of split parts
     */
    public static List stringToIntList ( final String string, final String separator )
    {
        final List stringList = stringToList ( string, separator );
        if ( stringList != null )
        {
            final List intList = new ArrayList ( stringList.size () );
            for ( final String s : stringList )
            {
                intList.add ( Integer.parseInt ( s ) );
            }
            return intList;
        }
        else
        {
            return null;
        }
    }

    /**
     * Returns a list of float parts split using specified separator.
     *
     * @param string    text to split
     * @param separator text parts separator
     * @return list of split parts
     */
    public static List stringToFloatList ( final String string, final String separator )
    {
        final List stringList = stringToList ( string, separator );
        if ( stringList != null )
        {
            final List intList = new ArrayList ( stringList.size () );
            for ( final String s : stringList )
            {
                intList.add ( Float.parseFloat ( s ) );
            }
            return intList;
        }
        else
        {
            return null;
        }
    }

    /**
     * Returns single text combined using list of strings and specified separator.
     *
     * @param list      list to combine into single text
     * @param separator text parts separator
     * @return single text
     */
    public static String listToString ( final List list, final String separator )
    {
        return listToString ( list, separator, simpleTextProvider );
    }

    /**
     * Returns single text combined using list of strings and specified separator.
     *
     * @param list         list to combine into single text
     * @param separator    text parts separator
     * @param textProvider text provider
     * @return single text
     */
    public static  String listToString ( final List list, final String separator, final TextProvider textProvider )
    {
        return listToString ( list, separator, textProvider, null );
    }

    /**
     * Returns single text combined using list of strings and specified separator.
     *
     * @param list         list to combine into single text
     * @param separator    text parts separator
     * @param textProvider text provider
     * @param filter       list elements filter
     * @return single text
     */
    public static  String listToString ( final List list, final String separator, final TextProvider textProvider,
                                            final Filter filter )
    {
        if ( list != null && list.size () > 0 )
        {
            final StringBuilder stringBuilder = new StringBuilder ();
            boolean hasPreviouslyAccepted = false;
            for ( final T object : list )
            {
                if ( filter == null || filter.accept ( object ) )
                {
                    if ( hasPreviouslyAccepted )
                    {
                        stringBuilder.append ( separator );
                    }
                    stringBuilder.append ( textProvider.getText ( object ) );
                    hasPreviouslyAccepted = true;
                }
            }
            return stringBuilder.toString ();
        }
        else
        {
            return null;
        }
    }

    /**
     * Converts list of enumeration constants into string with list of enumeration constants and returns it.
     *
     * @param enumList enumeration constants list
     * @param       enumeration type
     * @return string with list of enumeration constants
     */
    public static > String enumListToString ( final List enumList )
    {
        if ( enumList != null && enumList.size () > 0 )
        {
            final int end = enumList.size () - 1;
            final StringBuilder stringBuilder = new StringBuilder ();
            for ( int i = 0; i <= end; i++ )
            {
                stringBuilder.append ( enumList.get ( i ) );
                stringBuilder.append ( i != end ? "," : "" );
            }
            return stringBuilder.toString ();
        }
        else
        {
            return null;
        }
    }

    /**
     * Converts string with list of enumeration constants into real list of enumeration constants and returns it.
     *
     * @param enumString enumeration constants string list
     * @param enumClass  enumeration class
     * @param         enumeration type
     * @return list of enumeration constants
     */
    public static > List enumStringToList ( final String enumString, final Class enumClass )
    {
        final List enumerations;
        if ( enumString != null )
        {
            final StringTokenizer tokenizer = new StringTokenizer ( enumString, ",", false );
            enumerations = new ArrayList ();
            while ( tokenizer.hasMoreTokens () )
            {
                enumerations.add ( Enum.valueOf ( enumClass, tokenizer.nextToken ().trim () ) );
            }
        }
        else
        {
            enumerations = new ArrayList ( 0 );
        }
        return enumerations;
    }

    /**
     * Converts and returns specified parts which are not null into single string.
     *
     * @param separator separator to place between parts
     * @param parts     parts to unite
     * @return united non-null parts
     */
    public static String unite ( final String separator, final String... parts )
    {
        if ( parts != null && parts.length > 0 )
        {
            final StringBuilder sb = new StringBuilder ();
            boolean hasPrevious = false;
            for ( final String part : parts )
            {
                if ( !isEmpty ( part ) )
                {
                    if ( hasPrevious )
                    {
                        sb.append ( separator );
                    }
                    sb.append ( part );
                    hasPrevious = true;
                }
            }
            return sb.toString ();
        }
        else
        {
            return "";
        }
    }

    /**
     * Returns whether specified text is empty or not.
     *
     * @param text text to process
     * @return true if specified text is empty, false otherwise
     */
    public static boolean isEmpty ( final String text )
    {
        return text == null || text.trim ().isEmpty ();
    }

    /**
     * Creates new string filled with specified amount of same characters.
     *
     * @param character character to fill string with
     * @param length    string length
     * @return new string filled with specified amount of same characters
     */
    public static String createString ( final String character, int length )
    {
        final StringBuilder sb = new StringBuilder ( length );
        while ( length-- > 0 )
        {
            sb.append ( character );
        }
        return sb.toString ();
    }

    /**
     * Replaces all occurrences of str found in the specified text with provided text.
     *
     * @param text       text to replace string occurrences in
     * @param ignoreCase whether should ignore case while searching for occurrences or not
     * @param str        text to replace
     * @param provider   text replacement
     * @return text with replaced occurrences of the specified str
     */
    public static String replaceAll ( final String text, final boolean ignoreCase, final String str, final TextProvider provider )
    {
        final String exp = ignoreCase ? str.toLowerCase ( Locale.ROOT ) : str;
        int match = 0;
        int prev = 0;
        final StringBuilder builder = new StringBuilder ( text.length () );
        for ( int i = 0; i < text.length (); i++ )
        {
            final char ch = text.charAt ( i );
            if ( exp.charAt ( match ) == ( ignoreCase ? Character.toLowerCase ( ch ) : ch ) )
            {
                match++;
            }
            else if ( exp.charAt ( 0 ) == ( ignoreCase ? Character.toLowerCase ( ch ) : ch ) )
            {
                match = 1;
            }
            else
            {
                match = 0;
            }
            if ( match == exp.length () )
            {
                final int start = i - exp.length () + 1;
                final String part = text.substring ( start, start + exp.length () );
                builder.append ( text.substring ( prev, start ) );
                builder.append ( provider.getText ( part ) );
                prev = start + exp.length ();
                match = 0;
            }
            else if ( i == text.length () - 1 )
            {
                builder.append ( text.substring ( prev ) );
            }
        }
        return builder.toString ();
    }

    /**
     * Returns random ID with default prefix and suffix.
     *
     * @return ID
     */
    public static String generateId ()
    {
        return generateId ( null, null );
    }

    /**
     * Returns random ID with specified prefix and default suffix.
     *
     * @param prefix id prefix
     * @return ID
     */
    public static String generateId ( final String prefix )
    {
        return generateId ( prefix, null );
    }

    /**
     * Returns random ID with specified prefix and suffix.
     *
     * @param prefix id prefix
     * @param suffix id suffix
     * @return ID
     */
    public static String generateId ( final String prefix, final String suffix )
    {
        return ( prefix == null ? defaultIdPrefix : prefix ) + "-" + generateId ( idPartLength ) + "-" + generateId ( idPartLength ) + "-" +
                generateId ( idPartLength ) + "-" + generateId ( idPartLength ) + "-" + ( suffix == null ? defaultIdSuffix : suffix );
    }

    /**
     * Returns randomly generated ID part with specified length.
     *
     * @param length part length in symbols
     * @return ID part
     */
    public static String generateId ( final int length )
    {
        final StringBuilder stringBuilder = new StringBuilder ( length );
        for ( int i = 0; i < length; i++ )
        {
            char next = 0;
            int range = 10;
            switch ( MathUtils.random ( 3 ) )
            {
                case 0:
                {
                    next = '0';
                    range = 10;
                    break;
                }
                case 1:
                {
                    next = 'a';
                    range = 26;
                    break;
                }
                case 2:
                {
                    next = 'A';
                    range = 26;
                    break;
                }
            }
            stringBuilder.append ( ( char ) ( MathUtils.random ( range ) + next ) );
        }
        return stringBuilder.toString ();
    }

    /**
     * Returns settings combined into a single key.
     * This method might be useful for generating complex cache keys.
     *
     * @param settings settings to combine
     * @return key for the specified shape settings
     */
    public static String getSettingsKey ( final Object... settings )
    {
        final StringBuilder stringBuilder = new StringBuilder ();
        for ( final Object object : settings )
        {
            if ( stringBuilder.length () > 0 )
            {
                stringBuilder.append ( ";" );
            }
            if ( object != null )
            {
                if ( object.getClass ().isArray () )
                {
                    final int length = Array.getLength ( object );
                    for ( int i = 0; i < length; i++ )
                    {
                        if ( i > 0 )
                        {
                            stringBuilder.append ( ";" );
                        }
                        stringBuilder.append ( getSettingsKey ( Array.get ( object, i ) ) );
                    }
                }
                else if ( object instanceof Collection )
                {
                    final Collection collection = ( Collection ) object;
                    stringBuilder.append ( getSettingsKey ( collection.toArray () ) );
                }
                else
                {
                    stringBuilder.append ( getSettingKey ( object ) );
                }
            }
            else
            {
                stringBuilder.append ( getSettingKey ( object ) );
            }
        }
        return stringBuilder.toString ();
    }

    /**
     * Returns setting string representation.
     *
     * @param setting setting to be converted
     * @return setting string representation
     */
    private static String getSettingKey ( final Object setting )
    {
        if ( setting == null )
        {
            return "null";
        }
        else if ( setting instanceof Insets )
        {
            return InsetsConverter.insetsToString ( ( Insets ) setting );
        }
        else if ( setting instanceof Rectangle )
        {
            return RectangleConverter.rectangleToString ( ( Rectangle ) setting );
        }
        else if ( setting instanceof Point )
        {
            return PointConverter.pointToString ( ( Point ) setting );
        }
        else if ( setting instanceof Color )
        {
            return ColorConverter.colorToString ( ( Color ) setting );
        }
        else
        {
            return setting.toString ();
        }
    }

    /**
     * Either returns delay retrieved from string or throws an exception if it cannot be parsed.
     * Full string format is "Xd Yh Zm s ms" but you can skip any part of it. Yet you must specify atleast one value.
     * For example string "2h 5s" will be a valid delay declaration and will be converted into (2*60*60*1000+5*1000) long value.
     *
     * @param delay string delay
     * @return delay retrieved from string
     * @throws com.alee.utils.text.DelayFormatException when delay cannot be parsed
     */
    public static long parseDelay ( final String delay ) throws DelayFormatException
    {
        try
        {
            long summ = 0;
            final String[] parts = delay.split ( " " );
            for ( final String part : parts )
            {
                if ( !TextUtils.isEmpty ( part ) )
                {
                    for ( int i = 0; i < part.length (); i++ )
                    {
                        if ( !Character.isDigit ( part.charAt ( i ) ) )
                        {
                            final int time = Integer.parseInt ( part.substring ( 0, i ) );
                            final PartType type = PartType.valueOf ( part.substring ( i ) );
                            switch ( type )
                            {
                                case w:
                                    summ += time * msInWeek;
                                    break;

                                case d:
                                    summ += time * msInDay;
                                    break;

                                case h:
                                    summ += time * msInHour;
                                    break;

                                case m:
                                    summ += time * msInMinute;
                                    break;

                                case s:
                                    summ += time * msInSecond;
                                    break;

                                case ms:
                                    summ += time;
                                    break;
                            }
                            break;
                        }
                    }
                }
            }
            return summ;
        }
        catch ( final Throwable e )
        {
            throw new DelayFormatException ( e );
        }
    }

    /**
     * Returns delay string representation.
     * Delay cannot be less or equal to zero.
     *
     * @param delay delay to process
     * @return delay string representation
     */
    public static String toStringDelay ( final long delay )
    {
        if ( delay <= 0 )
        {
            throw new IllegalArgumentException ( "Invalid delay: " + delay );
        }

        long time = delay;

        final long w = time / msInWeek;
        time = time - w * msInWeek;

        final long d = time / msInDay;
        time = time - d * msInDay;

        final long h = time / msInHour;
        time = time - h * msInHour;

        final long m = time / msInMinute;
        time = time - m * msInMinute;

        final long s = time / msInSecond;
        time = time - s * msInSecond;

        final long ms = time;

        final String stringDelay = ( w > 0 ? w + "w " : "" ) +
                ( d > 0 ? d + "d " : "" ) +
                ( h > 0 ? h + "h " : "" ) +
                ( m > 0 ? m + "m " : "" ) +
                ( s > 0 ? s + "s " : "" ) +
                ( ms > 0 ? ms + "ms " : "" );

        return stringDelay.trim ();
    }

    /**
     * Time part type enumeration used to parse string delay.
     */
    protected static enum PartType
    {
        w, d, h, m, s, ms
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy