/*
 *  soapUI, copyright (C) 2004-2012 smartbear.com 
 *
 *  soapUI is free software; you can redistribute it and/or modify it under the 
 *  terms of version 2.1 of the GNU Lesser General Public License as published by 
 *  the Free Software Foundation.
 *
 *  soapUI is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without 
 *  even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 
 *  See the GNU Lesser General Public License for more details at gnu.org.
 */

package org.syntax.jedit.tokenmarker;

import javax.swing.text.Segment;

import org.syntax.jedit.KeywordMap;

/**
 * SQL token marker.
 * 
 * @author mike dillon
 * @version $Id: SQLTokenMarker.java,v 1.6 1999/04/19 05:38:20 sp Exp $
 */
public class SQLTokenMarker extends TokenMarker
{
	private int offset, lastOffset, lastKeyword, length;

	// public members
	public SQLTokenMarker( KeywordMap k )
	{
		this( k, false );
	}

	public SQLTokenMarker( KeywordMap k, boolean tsql )
	{
		keywords = k;
		isTSQL = tsql;
	}

	public byte markTokensImpl( byte token, Segment line, int lineIndex )
	{
		offset = lastOffset = lastKeyword = line.offset;
		length = line.count + offset;

		loop : for( int i = offset; i < length; i++ )
		{
			switch( line.array[i] )
			{
			case '*' :
				if( token == Token.COMMENT1 && length - i >= 1 && line.array[i + 1] == '/' )
				{
					token = Token.NULL;
					i++ ;
					addToken( ( i + 1 ) - lastOffset, Token.COMMENT1 );
					lastOffset = i + 1;
				}
				else if( token == Token.NULL )
				{
					searchBack( line, i );
					addToken( 1, Token.OPERATOR );
					lastOffset = i + 1;
				}
				break;
			case '[' :
				if( token == Token.NULL )
				{
					searchBack( line, i );
					token = Token.LITERAL1;
					literalChar = '[';
					lastOffset = i;
				}
				break;
			case ']' :
				if( token == Token.LITERAL1 && literalChar == '[' )
				{
					token = Token.NULL;
					literalChar = 0;
					addToken( ( i + 1 ) - lastOffset, Token.LITERAL1 );
					lastOffset = i + 1;
				}
				break;
			case '.' :
			case ',' :
			case '(' :
			case ')' :
				if( token == Token.NULL )
				{
					searchBack( line, i );
					addToken( 1, Token.NULL );
					lastOffset = i + 1;
				}
				break;
			case '+' :
			case '%' :
			case '&' :
			case '|' :
			case '^' :
			case '~' :
			case '<' :
			case '>' :
			case '=' :
				if( token == Token.NULL )
				{
					searchBack( line, i );
					addToken( 1, Token.OPERATOR );
					lastOffset = i + 1;
				}
				break;
			case ' ' :
			case '\t' :
				if( token == Token.NULL )
				{
					searchBack( line, i, false );
				}
				break;
			case ':' :
				if( token == Token.NULL )
				{
					addToken( ( i + 1 ) - lastOffset, Token.LABEL );
					lastOffset = i + 1;
				}
				break;
			case '/' :
				if( token == Token.NULL )
				{
					if( length - i >= 2 && line.array[i + 1] == '*' )
					{
						searchBack( line, i );
						token = Token.COMMENT1;
						lastOffset = i;
						i++ ;
					}
					else
					{
						searchBack( line, i );
						addToken( 1, Token.OPERATOR );
						lastOffset = i + 1;
					}
				}
				break;
			case '-' :
				if( token == Token.NULL )
				{
					if( length - i >= 2 && line.array[i + 1] == '-' )
					{
						searchBack( line, i );
						addToken( length - i, Token.COMMENT1 );
						lastOffset = length;
						break loop;
					}
					else
					{
						searchBack( line, i );
						addToken( 1, Token.OPERATOR );
						lastOffset = i + 1;
					}
				}
				break;
			case '!' :
				if( isTSQL && token == Token.NULL && length - i >= 2
						&& ( line.array[i + 1] == '=' || line.array[i + 1] == '<' || line.array[i + 1] == '>' ) )
				{
					searchBack( line, i );
					addToken( 1, Token.OPERATOR );
					lastOffset = i + 1;
				}
				break;
			case '"' :
			case '\'' :
				if( token == Token.NULL )
				{
					token = Token.LITERAL1;
					literalChar = line.array[i];
					addToken( i - lastOffset, Token.NULL );
					lastOffset = i;
				}
				else if( token == Token.LITERAL1 && literalChar == line.array[i] )
				{
					token = Token.NULL;
					literalChar = 0;
					addToken( ( i + 1 ) - lastOffset, Token.LITERAL1 );
					lastOffset = i + 1;
				}
				break;
			default :
				break;
			}
		}
		if( token == Token.NULL )
			searchBack( line, length, false );
		if( lastOffset != length )
			addToken( length - lastOffset, token );
		return token;
	}

	// protected members
	protected boolean isTSQL = false;

	// private members
	private KeywordMap keywords;
	private char literalChar = 0;

	private void searchBack( Segment line, int pos )
	{
		searchBack( line, pos, true );
	}

	private void searchBack( Segment line, int pos, boolean padNull )
	{
		int len = pos - lastKeyword;
		byte id = keywords.lookup( line, lastKeyword, len );
		if( id != Token.NULL )
		{
			if( lastKeyword != lastOffset )
				addToken( lastKeyword - lastOffset, Token.NULL );
			addToken( len, id );
			lastOffset = pos;
		}
		lastKeyword = pos + 1;
		if( padNull && lastOffset < pos )
			addToken( pos - lastOffset, Token.NULL );
	}
}
