/*
 * @(#)StringTools.java
 *
 * Summary: Miscellaneous static methods for dealing with Strings in JDK 1.5+.
 *
 * Copyright: (c) 2007-2010 Roedy Green, Canadian Mind Products, http://mindprod.com
 *
 * Licence: This software may be copied and used freely for any purpose but military.
 *          http://mindprod.com/contact/nonmil.html
 *
 * Requires: JDK 1.5+
 *
 * Created with: IntelliJ IDEA IDE.
 *
 * Version History:
 *  1.5 2005-07-14 - split off from Misc, allow for compilation with old compiler.
 *  1.6 2005-10-15 - add condense method.
 *  1.7 2006-03-04 - format with IntelliJ and prepare Javadoc
 *  1.8 2006-03-05
 *  1.9 2008-03-10 - add StringTools.firstWord
 *  2.0 2009-04-16 - augument Common11 StringTools
 *  2.1 2009-04-30 - add pruneExcessBlankLines
 */
package com.mindprod.common15;

import java.awt.Color;
import java.util.ArrayList;

import static java.lang.System.out;

/**
 * Miscellaneous static methods for dealing with Strings in JDK 1.5+.
 * <p/>
 * Most are reimplementations of methods from com.mindprod.common1.StringTools using
 * the more efficient StringBuilder instead of StringBuffer.
 *
 * @author Roedy Green, Canadian Mind Products
 * @version 2.1 2009-04-30 - add pruneExcessBlankLines
 * @since 2009-04-16
 */
public final class StringTools extends com.mindprod.common11.StringTools
    {
    // ------------------------------ CONSTANTS ------------------------------

    /**
     * true if you want extra debugging output and test code
     */
    private static final boolean DEBUGGING = false;
    // -------------------------- PUBLIC STATIC METHODS --------------------------

    /**
     * Collapse multiple spaces in string down to a single space. Remove lead and trailing spaces.
     * Does not collapse other whitespace.
     *
     * @param s String to strip of blanks.
     *
     * @return String with all blanks, lead/trail/embedded removed.
     * @noinspection WeakerAccess,SameParameterValue
     * @see #squish(String)
     */
    public static String condense( String s )
        {
        if ( s == null )
            {
            return null;
            }
        s = s.trim();
        if ( s.indexOf( "  " ) < 0 )
            {
            return s;
            }
        int len = s.length();
        StringBuilder b = new StringBuilder( len - 1 );
        boolean suppressSpaces = false;
        for ( int i = 0; i < len; i++ )
            {
            char c = s.charAt( i );
            if ( c == ' ' )
                {
                if ( suppressSpaces )
                    {
                    // subsequent space
                    }
                else
                    {
                    // first space
                    b.append( c );
                    suppressSpaces = true;
                    }
                }
            else
                {
                // was not a space
                b.append( c );
                suppressSpaces = false;
                }
            }// end for
        return b.toString();
        }// end condense

    /**
     * Collapse multiple blank lines down to one.  Discards lead and trail blank lines.
     * Blank lines are lines that when trimmed have length 0.
     * Degraded version available in com.mindprod.common11.StringTools for JDK 1.1.
     *
     * @param lines               array of lines to tidy.
     * @param minBlankLinesToKeep usually 1 meaning 1+ consecutive blank lines become 1, effectively collapsing
     *                            runs of blank lines down to 1.
     *                            if 2, 1 blank line is removed, and 2+ consecutive blanks lines become 1,
     *                            effectively undouble spacing.
     *                            if zero, non-blank lines will be separated by one blank line, even if there was not
     *                            one there to begin with, completely independent of preexisting blank lines,
     *                            effectively double spacing..
     *                            9999 effectively removes all blank lines.
     *
     * @return array of lines with lead and trail blank lines removed, and excess blank lines collapsed down to one or 0.
     *         The results are NOT trimmed.
     */
    public static String[] pruneExcessBlankLines( String[] lines, int minBlankLinesToKeep )
        {
        int firstNonBlankLine = lines.length;
        for ( int i = 0; i < lines.length; i++ )
            {
            if ( lines[ i ].trim().length() > 0 )
                {
                firstNonBlankLine = i;
                break;
                }
            }

        int lastNonBlankLine = -1;
        for ( int i = lines.length - 1; i > 0; i-- )
            {
            if ( lines[ i ].trim().length() > 0 )
                {
                lastNonBlankLine = i;
                break;
                }
            }
        if ( firstNonBlankLine > lastNonBlankLine )
            {
            return new String[0];
            }

        // collapse blank lines in the middle chunk

        ArrayList<String> keep = new ArrayList<String>( lastNonBlankLine - firstNonBlankLine + 1 );
        int pendingBlankLines = 0;
        for ( int i = firstNonBlankLine; i <= lastNonBlankLine; i++ )
            {
            if ( lines[ i ].trim().length() == 0 )
                {
                pendingBlankLines++;
                }
            else
                {
                if ( pendingBlankLines >= minBlankLinesToKeep )
                    {
                    keep.add( "" );
                    }
                keep.add( lines[ i ] );  // we don't trim. That is up to caller.
                pendingBlankLines = 0;
                }
            }
        return keep.toArray( new String[keep.size()] );
        }

    /**
     * used to prepare SQL string literals by doubling each embedded ' and wrapping in ' at each end. Further quoting is
     * required to use the results in Java String literals. If you use PreparedStatement, then this method is not
     * needed. The ' quoting is automatically handled for you.
     *
     * @param sql Raw SQL string literal
     *
     * @return sql String literal enclosed in '
     * @noinspection WeakerAccess,SameParameterValue
     */
    public static String quoteSQL( String sql )
        {
        StringBuilder sb = new StringBuilder( sql.length() + 5 );
        sb.append( '\'' );
        for ( int i = 0; i < sql.length(); i++ )
            {
            char c = sql.charAt( i );
            if ( c == '\'' )
                {
                sb.append( "\'\'" );
                }
            else
                {
                sb.append( c );
                }
            }
        sb.append( '\'' );
        return sb.toString();
        }

    /**
     * Remove all spaces from a String. Does not touch other whitespace.
     *
     * @param s String to strip of blanks.
     *
     * @return String with all blanks, lead/trail/embedded removed.
     * @see #condense(String)
     */
    public static String squish( String s )
        {
        if ( s == null )
            {
            return null;
            }
        s = s.trim();
        if ( s.indexOf( ' ' ) < 0 )
            {
            return s;
            }
        int len = s.length();
        // have to use StringBuilder for JDK 1.1
        StringBuilder b = new StringBuilder( len - 1 );
        for ( int i = 0; i < len; i++ )
            {
            char c = s.charAt( i );
            if ( c != ' ' )
                {
                b.append( c );
                }
            }// end for
        return b.toString();
        }// end squish

    // --------------------------- CONSTRUCTORS ---------------------------

    /**
     * StringTools contains only static methods.
     */
    protected StringTools()
        {
        }

    // --------------------------- main() method ---------------------------

    /**
     * Test harness, used in debugging
     *
     * @param args not used
     */
    public static void main( String[] args )
        {
        if ( DEBUGGING )
            {
            out.println( condense( "   this  is   spaced.  " ) );
            out.println( trimLeading( "   trim   " ) );
            out.println( trimTrailing( "   trim   " ) );

            out.println( trimLeading( "*****t*r*i*m****", '*' ) );
            out.println( trimTrailing( "*****t*r*i*m****", '*' ) );

            out.println( toString( Color.RED ) );
            out.println( toHexString( -3 ) );
            out.println( toHexString( 3 ) );
            out.println( countLeading( "none", ' ' ) );
            out.println( countLeading( "*one***", '*' ) );
            out.println( countLeading( "\n\ntw\n\n\no\n\n\n\n", '\n' ) );
            out.println( countTrailing( "none", ' ' ) );
            out.println( countTrailing( "***one*", '*' ) );
            out.println( countTrailing( "\n\n\n\nt\n\n\n\nwo\n\n", '\n' ) );

            out.println( quoteSQL( "Judy's Place" ) );
            out.println( parseLongPennies( "$5.00" ) );
            out.println( parseLongPennies( "$50" ) );
            out.println( parseLongPennies( "50" ) );
            out.println( parseLongPennies( "$50-" ) );

            out.println( penniesToString( 0 ) );
            out.println( penniesToString( -1 ) );
            out.println( penniesToString( 20 ) );
            out.println( penniesToString( 302 ) );
            out.println( penniesToString( -100000 ) );
            out.println( toBookTitleCase( "handbook to HIGHER consciousness" ) );
            out.println( toBookTitleCase( "THE HISTORY OF THE U.S.A." ) );
            out.println( toBookTitleCase( "THE HISTORY OF THE USA" ) );

            out.println( rightPad( "abc", 6, true ) + "*" );
            out.println( rightPad( "abc", 2, true ) + "*" );
            out.println( rightPad( "abc", 2, false ) + "*" );
            out.println( rightPad( "abc", 3, true ) + "*" );
            out.println( rightPad( "abc", 3, false ) + "*" );
            out.println( rightPad( "abc", 0, true ) + "*" );
            out.println( rightPad( "abc", 20, true ) + "*" );
            out.println( rightPad( "abc", 29, true ) + "*" );
            out.println( rightPad( "abc", 30, true ) + "*" );
            out.println( rightPad( "abc", 31, true ) + "*" );
            out.println( rightPad( "abc", 40, true ) + "*" );

            out.println( toUpperCase( 'q' ) );
            out.println( toUpperCase( 'Q' ) );
            out.println( toUpperCase( "The quick brown fox was 10 feet tall." ) );
            out.println( toUpperCase( "THE QUICK BROWN FOX WAS 10 FEET TALL." ) );
            out.println( toUpperCase( "the quick brown fox was 10 feet tall." ) );
            out.println( toLowerCase( 'q' ) );
            out.println( toLowerCase( 'Q' ) );
            out.println( toLowerCase( "The quick brown fox was 10 feet tall." ) );
            out.println( toLowerCase( "THE QUICK BROWN FOX WAS 10 FEET TALL." ) );
            out.println( toLowerCase( "the quick brown fox was 10 feet tall." ) );

            out.println( "count instances should be 4: " + countInstances( " abab abcdefgab", "ab" ) );
            }
        }// end main
    }
