package org.codehaus.mojo.animal_sniffer;
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements.  See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership.  The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License.  You may obtain a copy of the License at
*
*  http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied.  See the License for the
* specific language governing permissions and limitations
* under the License.
*/

import java.util.regex.Pattern;

/**
 * Utility methods to help with regex manipulation.
 *
 * @author Stephen Connolly
 * @since 1.3
 */
public final class RegexUtils
{
    /**
     * The end of a regex literal sequence.
     *
     * @since 1.3
     */
    public static final String REGEX_QUOTE_END = "\\E";

    /**
     * The start of a regex literal sequence.
     *
     * @since 1.3
     */
    public static final String REGEX_QUOTE_START = "\\Q";

    /**
     * Escape the escapes.
     *
     * @since 1.3
     */
    public static final String REGEX_QUOTE_END_ESCAPED = REGEX_QUOTE_END + '\\' + REGEX_QUOTE_END + REGEX_QUOTE_START;

    private RegexUtils()
    {
        throw new IllegalAccessError( "Utility classes should never be instantiated" );
    }

    /**
     * Takes a string and returns the regex that will match that string exactly.
     *
     * @param s The string to match.
     * @return The regex that will match the string exactly.
     * @since 1.3
     */
    public static String quote( String s )
    {
        int i = s.indexOf( REGEX_QUOTE_END );
        if ( i == -1 )
        {
            // we're safe as nobody has a crazy \E in the string
            return REGEX_QUOTE_START + s + REGEX_QUOTE_END;
        }

        // damn there's at least one \E in the string
        StringBuilder sb = new StringBuilder( s.length() + 32 );
        // each escape-escape takes 10 chars...
        // hope there's less than 4 of them

        sb.append( REGEX_QUOTE_START );
        int pos = 0;
        do
        {
            // we are safe from pos to i
            sb.append( s.substring( pos, i ) );
            // now escape-escape
            sb.append( REGEX_QUOTE_END_ESCAPED );
            // move the working start
            pos = i + REGEX_QUOTE_END.length();
            i = s.indexOf( REGEX_QUOTE_END, pos );
        }
        while ( i != -1 );

        sb.append( s.substring( pos, s.length() ) );
        sb.append( REGEX_QUOTE_END );

        return sb.toString();
    }

    /**
     * Converts a wildcard rule to a regex rule.
     *
     * @param wildcardRule the wildcard rule.
     * @param exactMatch   <code>true</code> results in an regex that will match the entire string, while
     *                     <code>false</code> will match the start of the string.
     * @return The regex rule.
     */
    public static String convertWildcardsToRegex( String wildcardRule, boolean exactMatch )
    {
        StringBuilder regex = new StringBuilder();
        int index = 0;
        final int len = wildcardRule.length();
        while ( index < len )
        {
            final int nextQ = wildcardRule.indexOf( '?', index );
            final int nextS = wildcardRule.indexOf( '*', index );
            if ( nextQ == -1 && nextS == -1 )
            {
                regex.append( quote( wildcardRule.substring( index ) ) );
                break;
            }
            int nextIndex;
            if ( nextQ == -1 )
            {
                nextIndex = nextS;
            }
            else if ( nextS == -1 )
            {
                nextIndex = nextQ;
            }
            else
            {
                nextIndex = Math.min( nextQ, nextS );
            }
            if ( index < nextIndex )
            {
                // we have some characters to match
                regex.append( quote( wildcardRule.substring( index, nextIndex ) ) );
            }
            char c = wildcardRule.charAt( nextIndex );
            if ( c == '?' )
            {
                regex.append( '.' );
            }
            else
            {
                regex.append( ".*" );
            }
            index = nextIndex + 1;
        }
        if ( !exactMatch )
        {
            regex.append( ".*" );
        }
        return regex.toString();
    }

    /**
     * Compiles a pattern matcher using wildcard based matching.
     * @param wildcard The wildcards rule to match.
     * @return A pattern to match the supplied wildcards rule.
     */
    public static Pattern compileWildcard( String wildcard )
    {
        return Pattern.compile( convertWildcardsToRegex( wildcard, true ) );
    }
}
