/*
 *  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. 
 *  
 */
package org.apache.directory.studio.schemaeditor.model.alias;


public class AliasesStringScanner
{
    private static final char CHAR_COMMA = ',';
    private static final char CHAR_EOF = '\u0000';

    /** The aliases to scan */
    private String aliases;

    /** The current position */
    private int pos;

    /** The last token type. */
    private int lastTokenType;


    /**
     * Creates a new instance of LdapFilterScanner.
     */
    public AliasesStringScanner()
    {
        super();
        aliases = ""; //$NON-NLS-1$
    }


    /**
     * Resets this scanner.
     * 
     * @param aliases the new aliases to scan
     */
    public void reset( String aliases )
    {
        this.aliases = aliases;
        pos = -1;
        lastTokenType = AliasesStringToken.START;
    }


    /**
     * Gets the character at the current position.
     * 
     * @return the character at the current position
     */
    private char currentChar()
    {
        return 0 <= pos && pos < aliases.length() ? aliases.charAt( pos ) : CHAR_EOF;
    }


    /**
     * Increments the position counter and gets
     * the character at that positon.
     * 
     * @return the character at the next position
     */
    private char nextChar()
    {
        pos++;
        return currentChar();
    }


    /**
     * Decrements the position counter and gets
     * the character at that positon.
     * 
     * @return the character at the previous position
     */
    private char prevChar()
    {
        pos--;
        return currentChar();
    }


    /**
     * Gets the next token.
     * 
     * @return the next token
     */
    public AliasesStringToken nextToken()
    {
        char c;

        // check for EOF
        c = nextChar();
        if ( c == CHAR_EOF )
        {
            lastTokenType = AliasesStringToken.EOF;
            return new AliasesStringToken( lastTokenType, "", pos ); //$NON-NLS-1$
        }
        prevChar();

        // check the substring if there was an error
        c = nextChar();
        if ( lastTokenType == AliasesStringToken.ERROR_ALIAS_PART
            || lastTokenType == AliasesStringToken.ERROR_ALIAS_START )
        {
            StringBuffer sb = new StringBuffer();
            while ( c != CHAR_COMMA && c != CHAR_EOF )
            {
                sb.append( c );
                c = nextChar();
            }

            lastTokenType = AliasesStringToken.ERROR_ALIAS_SUBSTRING;
            return new AliasesStringToken( lastTokenType, sb.toString(), pos - sb.length() + 1 );
        }
        prevChar();

        // check for ignorable whitespaces
        c = nextChar();
        if ( Character.isWhitespace( c ) )
        {
            StringBuffer sb = new StringBuffer();
            while ( Character.isWhitespace( c ) )
            {
                sb.append( c );
                c = nextChar();
            }
            prevChar();

            lastTokenType = AliasesStringToken.WHITESPACE;
            return new AliasesStringToken( lastTokenType, sb.toString(), pos - sb.length() + 1 );
        }
        prevChar();

        // check special characters
        c = nextChar();
        if ( c == CHAR_COMMA )
        {
            lastTokenType = AliasesStringToken.COMMA;
            return new AliasesStringToken( lastTokenType, ",", pos ); //$NON-NLS-1$
        }
        prevChar();

        // check Alias
        c = nextChar();
        if ( isAliasSafeCharStart( c ) )
        {
            StringBuffer sb = new StringBuffer();
            boolean hasError = false;

            sb.append( c );

            c = nextChar();
            while ( c != CHAR_COMMA && c != CHAR_EOF )
            {
                sb.append( c );

                if ( !isAliasSafeCharPart( c ) )
                {
                    hasError = true;
                    break;
                }

                c = nextChar();
            }

            if ( hasError )
            {
                lastTokenType = AliasesStringToken.ERROR_ALIAS_PART;
                return new AliasesStringToken( lastTokenType, sb.toString(), pos - sb.length() + 1 );
            }
            else
            {
                prevChar();

                lastTokenType = AliasesStringToken.ALIAS;
                return new AliasesStringToken( lastTokenType, sb.toString(), pos - sb.length() + 1 );
            }
        }
        else
        {
            lastTokenType = AliasesStringToken.ERROR_ALIAS_START;
            return new AliasesStringToken( lastTokenType, c + "", pos ); //$NON-NLS-1$
        }
    }


    /**
     * Determines if the specified character is
     * permissible as the first character in an attribute type or object class
     * alias.
     * <p>
     * A character may start an attribute type or object class alias if and
     * only if one of the following conditions is true:
     * <ul>
     * <li> it is a letter between 'a' to 'z' and between 'A' to 'Z'
     * </ul>
     *
     * <p><b>Note:</b> This method cannot handle <a
     * href="#supplementary"> supplementary characters</a>. To support
     * all Unicode characters, including supplementary characters, use
     * the {@link #isJavaIdentifierStart(int)} method.
     *
     * @param   c the character to be tested.
     * @return  <code>true</code> if the character may start an attribute type
     * or object class alias.; <code>false</code> otherwise.
     */
    private boolean isAliasSafeCharStart( char c )
    {
        return ( c == 'a' || c == 'b' || c == 'c' || c == 'd' || c == 'e' || c == 'f' || c == 'g' || c == 'h'
            || c == 'i' || c == 'j' || c == 'k' || c == 'l' || c == 'm' || c == 'n' || c == 'o' || c == 'p' || c == 'q'
            || c == 'r' || c == 's' || c == 't' || c == 'u' || c == 'v' || c == 'w' || c == 'x' || c == 'y' || c == 'z'
            || c == 'A' || c == 'B' || c == 'C' || c == 'D' || c == 'E' || c == 'F' || c == 'G' || c == 'H' || c == 'I'
            || c == 'J' || c == 'K' || c == 'L' || c == 'M' || c == 'N' || c == 'O' || c == 'P' || c == 'Q' || c == 'R'
            || c == 'S' || c == 'T' || c == 'U' || c == 'V' || c == 'W' || c == 'X' || c == 'Y' || c == 'Z' );
    }


    /**
     * Determines if the specified character may be part of an attribute type or
     * object class alias as other than the first character.
     * <p>
     * A character may be part of an attribute type or object class alias if any 
     * of the following are true:
     * <ul>
     * <li>  it is a letter between 'a' to 'z' and between 'A' to 'Z'
     * <li>  it is a digit
     * <li>  it is a hyphen ('-')
     * <li>  it is a semi-colon (';')
     * </ul>
     *
     * @param   c      the character to be tested.
     * @return <code>true</code> if the character may be part of an attribute 
     * type or object class alias; <code>false</code> otherwise.
     */
    private boolean isAliasSafeCharPart( char c )
    {
        return ( c == 'a' || c == 'b' || c == 'c' || c == 'd' || c == 'e' || c == 'f' || c == 'g' || c == 'h'
            || c == 'i' || c == 'j' || c == 'k' || c == 'l' || c == 'm' || c == 'n' || c == 'o' || c == 'p' || c == 'q'
            || c == 'r' || c == 's' || c == 't' || c == 'u' || c == 'v' || c == 'w' || c == 'x' || c == 'y' || c == 'z'
            || c == 'A' || c == 'B' || c == 'C' || c == 'D' || c == 'E' || c == 'F' || c == 'G' || c == 'H' || c == 'I'
            || c == 'J' || c == 'K' || c == 'L' || c == 'M' || c == 'N' || c == 'O' || c == 'P' || c == 'Q' || c == 'R'
            || c == 'S' || c == 'T' || c == 'U' || c == 'V' || c == 'W' || c == 'X' || c == 'Y' || c == 'Z' || c == '0'
            || c == '1' || c == '2' || c == '3' || c == '4' || c == '5' || c == '6' || c == '7' || c == '8' || c == '9' || c == '-' );
    }
}
