

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

/**
 * Regular expression token definitions are implemented as a {@link Map}
 * that maps each (states,kind) pair to a {@link List} of regular
 * expressions in declaration order.  Inner class {@link #Datum} holds the
 * parts of a regular expression token definition.
 *
 * @layer<collect>
 */
    
public class RegexTokensData extends HashMap {

    public void addNode( RegexDefinitionNode node ) {

        List states = statesList( ( StatesNode ) node.arg[0].arg [0] ) ;
        String kind = node.arg[1].tok[0].getTokenName() ;
        boolean noCase = ( node.arg[2].arg [0] != null ) ;

        List regexList =
                new ArrayList( ( ( AstList ) node.arg[3] ) . toList() ) ;

        Datum newDatum = new Datum( states, kind, noCase, regexList ) ;
        String key = newDatum.getKey() ;
        if ( ! containsKey( key ) ) {
            put( key, newDatum ) ;
            return ;
        }

        Datum oldDatum = ( Datum ) get( key ) ;
        oldDatum.regexList.addAll( regexList ) ;
    }

    /**
     * Formats regular expression tokens by concatenating them with blank
     * line separators.
     *
     * @layer<collect>
     */
    public String toString() {

        if ( size() < 1 )
            return "// No regular expression tokens in Bali grammar." ;

        List keys = new ArrayList( keySet() ) ;
        Collections.sort( keys ) ;

        StringBuffer buffer = new StringBuffer() ;

        for ( Iterator p = keys.iterator() ; p.hasNext() ; ) {
            Datum value = ( Datum ) get( p.next() ) ;
            buffer.append( value ).append( Main.LINE_SEPARATOR ) ;
        }

        return buffer.toString() ;
    }
    static protected class Datum {

        public Datum( List states, String kind, boolean noCase, List re ) {

            this.states = states ;
            this.kind = kind ;
            this.noCase = noCase ;
            this.regexList = re ;

            this.key = keyToString() ;
        }

        public String getKey() {
            return key ;
        }

        public String keyToString() {

            StringBuffer buffer = new StringBuffer() ;

            if ( states.size() > 0 ) {
                buffer.append( '<' ) ;

                Iterator p = states.iterator() ;
                buffer.append( p.next().toString() ) ;
                while ( p.hasNext() )
                    buffer.append( ',' ).append( p.next().toString() ) ;

                buffer.append( "> " ) ;
            }

            buffer.append( kind ) ;

            if ( noCase )
                buffer.append( " IGNORE_CASE" ) ;

            return buffer.toString() ;
        }

        public String toString() {

            StringBuffer buffer = new StringBuffer( key ) ;

            buffer.append( ": {" ) ;

            Iterator p = regexList.iterator() ;
            buffer.append( p.next().toString() ) ;
            while ( p.hasNext() )
                buffer.append( " | " ).append( p.next().toString() ) ;

            buffer.append( Main.LINE_SEPARATOR ).append( '}' ) ;
            return buffer.toString() ;
        }

        final protected String key ;
        final protected String kind ;
        final protected boolean noCase ;
        final protected List regexList ;
        final protected List states ;
    }

    /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */

    /**
     * Returns the list of states for this regular expression token as a
     * {@link List} of {@link String} objects naming the states.  An empty
     * {@link List} means *all* states.
     *
     * @layer<collect>
     */
    final private List statesList( StatesNode node ) {

        if ( node == null )
            return Collections.EMPTY_LIST ;

        StatesSpecifier subNode = ( StatesSpecifier ) node.arg[0] ;
        if ( subNode instanceof  StarStatesNode )
            return Collections.EMPTY_LIST ;

        Main.DEBUG.info( "subNode.class="
                + subNode.getClass()
                + "; subNode="
                + subNode ) ;

        List states = new ArrayList() ;

        Iterator p = ( ( AstList )subNode.arg[0] ).toList().iterator() ;
        while ( p.hasNext() ) {
            StateNameNode nameNode = ( StateNameNode ) p.next() ;
            states.add( nameNode.tok[0].getTokenName() ) ;
        }

        return Collections.unmodifiableList( Arrays.asList( states.toArray( new String [states.size()] ) ) ) ;
    }

}
