/*
 *  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.codehaus.groovy.syntax;

import org.codehaus.groovy.GroovyBugError;


/**
 *  A <code>CSTNode</code> produced by the <code>Lexer</code>.
 *
 *  @see antlr.Parser
 *  @see antlr.Token
 *  @see Reduction
 *  @see Types
 *
 *  @author <a href="mailto:bob@werken.com">bob mcwhirter</a>
 *  @author <a href="mailto:cpoirier@dreaming.org">Chris Poirier</a>
 */

public class Token extends CSTNode
{
    public static final Token NULL = new Token();
    public static final Token EOF  = new Token( Types.EOF, "", -1, -1 );


  //---------------------------------------------------------------------------
  // TOKEN INITIALIZATION AND SUCH

    private int type        = Types.UNKNOWN;  // the actual type identified by the lexer
    private int meaning     = Types.UNKNOWN;  // an interpretation applied to the token after the fact

    private String     text = "";             // the text of the token
    private int   startLine = -1;             // the source line on which the token begins
    private int startColumn = -1;             // the source column on which the token begins


   /**
    *  Initializes the Token with the specified information.
    */

    public Token( int type, String text, int startLine, int startColumn )
    {
        this.type        = type;
        this.meaning     = type;
        this.text        = text;
        this.startLine   = startLine;
        this.startColumn = startColumn;
    }


   /**
    *  Initializes the NULL Token.
    */

    private Token() { }



   /**
    *  Returns a copy of this Token.
    */

    public Token dup()
    {
        Token token = new Token( this.type, this.text, this.startLine, this.startColumn );
        token.setMeaning( this.meaning );

        return token;
    }




  //---------------------------------------------------------------------------
  // NODE IDENTIFICATION AND MEANING


   /**
    *  Returns the meaning of this node.  If the node isEmpty(), returns
    *  the type of Token.NULL.
    */

    public int getMeaning()
    {
        return meaning;
    }



   /**
    *  Sets the meaning for this node (and it's root Token).  Not
    *  valid if the node isEmpty().  Returns this token, for
    *  convenience.
    */

    public CSTNode setMeaning( int meaning )
    {
        this.meaning = meaning;
        return this;
    }



   /**
    *  Returns the actual type of the node.  If the node isEmpty(), returns
    *  the type of Token.NULL.
    */

    public int getType()
    {
        return type;
    }




  //---------------------------------------------------------------------------
  // MEMBER ACCESS


   /**
    *  Returns the number of elements in the node (including root).
    */

    public int size()
    {
        return 1;
    }



   /**
    *  Returns the specified element, or null.
    */

    public CSTNode get( int index )
    {
        if( index > 0 )
        {
            throw new GroovyBugError( "attempt to access Token element other than root" );
        }

        return this;
    }



   /**
    *  Returns the root of the node.  By convention, all nodes have
    *  a Token as the first element (or root), which indicates the type
    *  of the node.  May return null if the node <code>isEmpty()</code>.
    */

    public Token getRoot()
    {
        return this;
    }



   /**
    *  Returns the text of the root node.  Uses <code>getRoot(true)</code>
    *  to get the root, so you will only receive null in return if the
    *  root token returns it.
    */

    public String getRootText()
    {
        return text;
    }



   /**
    *  Returns the text of the token.  Equivalent to
    *  <code>getRootText()</code> when called directly.
    */

    public String getText()
    {
        return text;
    }



   /**
    *  Not advisable, but if you need to adjust the token's text, this
    *  will do it.
    */

    public void setText( String text )
    {
        this.text = text;
    }



   /**
    *  Returns the starting line of the node.  Returns -1
    *  if not known.
    */

    public int getStartLine()
    {
        return startLine;
    }



   /**
    *  Returns the starting column of the node.  Returns -1
    *  if not known.
    */

    public int getStartColumn()
    {
        return startColumn;
    }




  //---------------------------------------------------------------------------
  // OPERATIONS


   /**
    *  Creates a <code>Reduction</code> from this token.  Returns self if the
    *  node is already a <code>Reduction</code>.
    */

    public Reduction asReduction()
    {
        return new Reduction( this );
    }



   /**
    *  Creates a <code>Reduction</code> from this token, adding the supplied
    *  node as the second element.
    */

    public Reduction asReduction( CSTNode second )
    {
        Reduction created = asReduction();
        created.add( second );
        return created;
    }



   /**
    *  Creates a <code>Reduction</code> from this token, adding the supplied
    *  nodes as the second and third element, respectively.
    */

    public Reduction asReduction( CSTNode second, CSTNode third )
    {
        Reduction created = asReduction( second );
        created.add( third );
        return created;
    }



   /**
    *  Creates a <code>Reduction</code> from this token, adding the supplied
    *  nodes as the second, third, and fourth element, respectively.
    */

    public Reduction asReduction( CSTNode second, CSTNode third, CSTNode fourth )
    {
        Reduction created = asReduction( second, third );
        created.add( fourth );
        return created;
    }




  //---------------------------------------------------------------------------
  // TOKEN FACTORIES


   /**
    *  Creates a token that represents a keyword.  Returns null if the
    *  specified text isn't a keyword.
    */

    public static Token newKeyword( String text, int startLine, int startColumn )
    {

        int type = Types.lookupKeyword( text );
        if( type != Types.UNKNOWN )
        {
            return new Token( type, text, startLine, startColumn );
        }

        return null;

    }


   /**
    *  Creates a token that represents a double-quoted string.
    */

    public static Token newString( String text, int startLine, int startColumn )
    {
        return new Token( Types.STRING, text, startLine, startColumn );
    }


   /**
    *  Creates a token that represents an identifier.
    */

    public static Token newIdentifier( String text, int startLine, int startColumn )
    {
        return new Token( Types.IDENTIFIER, text, startLine, startColumn );
    }


   /**
    *  Creates a token that represents an integer.
    */

    public static Token newInteger( String text, int startLine, int startColumn )
    {
        return new Token( Types.INTEGER_NUMBER, text, startLine, startColumn );
    }


   /**
    *  Creates a token that represents a decimal number.
    */

    public static Token newDecimal( String text, int startLine, int startColumn )
    {
        return new Token( Types.DECIMAL_NUMBER, text, startLine, startColumn );
    }


   /**
    *  Creates a token that represents a symbol, using a library for the text.
    */

    public static Token newSymbol( int type, int startLine, int startColumn )
    {
        return new Token( type, Types.getText(type), startLine, startColumn );
    }


   /**
    *  Creates a token that represents a symbol, using a library for the type.
    */

    public static Token newSymbol( String type, int startLine, int startColumn )
    {
        return new Token( Types.lookupSymbol(type), type, startLine, startColumn );
    }


   /**
    *  Creates a token with the specified meaning.
    */

    public static Token newPlaceholder( int type )
    {
        Token token = new Token( Types.UNKNOWN, "", -1, -1 );
        token.setMeaning( type );

        return token;
    }

}
