/*
	$Id: CTokenizer.cpp,v 1.1 2003/04/20 23:29:40 dasenbro Exp $

	File:	CTokenizer.cpp

	Contains: Implementation of the Token Parsing class

	Version:	Apple Mail Server - Mac OS X :  $Revision: 1.1 $

	Written by: Claris Corporation 

	Copyright:	© 1996-2001 by Apple Computer, Inc., all rights reserved.

 	NOT_FOR_OPEN_SOURCE <to be reevaluated at a later time>

	Change History:

		$Log: CTokenizer.cpp,v $
		Revision 1.1  2003/04/20 23:29:40  dasenbro
		Initial check-in.
		
		Revision 1.9  2002/05/09 16:47:25  dasenbro
		Changed all str... calls to CUtils::Str... to be NULL safe.
		
		Revision 1.8  2002/03/21 16:41:47  dasenbro
		Updated file version information.
		
		Revision 1.7  2002/02/20 20:22:40  dasenbro
		Syntax changes.
		
		Revision 1.6  2001/06/21 20:51:11  dasenbro
		Updated file header info.
		
		Revision 1.5  2001/06/21 20:10:47  dasenbro
		Added Change History.
		

	Projector History:

		 <2>	 5/20/98	MED		Do a compare on the entire command.

	To Do:
 */


// stdlib
#include <ctype.h>
#include <string.h>

// App
#include "CUtils.h"
#include "CTokenizer.h"
#include "UException.h"

static char mytoupper(char c);

//------------------------------------------------------------------------------
// *ÊConstants
//------------------------------------------------------------------------------

const	int	kInitialTableSize	= 10;

//------------------------------------------------------------------------------
// *ÊCTokenizer Constructor / Destructor
//------------------------------------------------------------------------------

CTokenizer::CTokenizer(CString& targ, const char* delim)
{
	InitTokenizer();
	
	ThrowIfNULL_(delim);
	ThrowIf_(*delim == '\0');
	
	BuildTokenTable(targ, delim);
}

CTokenizer::CTokenizer()
{
	InitTokenizer();
}

CTokenizer::~CTokenizer()
{
	if (mTokenTable)
	{
		delete mTokenTable;
		mTokenTable = NULL;
	}
	
	if (mTokenLengthTable)
	{
		delete mTokenLengthTable;
		mTokenLengthTable = NULL;
	}
}

//------------------------------------------------------------------------------
// *ÊInitTokenizer
//------------------------------------------------------------------------------
// Initialize the object with default values.

void CTokenizer::InitTokenizer()
{
	mTokenTable = NULL;
	mTokenLengthTable = NULL;
	mTokenCount = 0;
	mTokenTableSize = kInitialTableSize;
	
	ThrowIfNULL_( mTokenTable = new char*[mTokenTableSize] );
	ThrowIfNULL_( mTokenLengthTable = new int[mTokenTableSize] );
}

//------------------------------------------------------------------------------
// *ÊRetokenize
//------------------------------------------------------------------------------
// Rebuild the token table with a new string

void CTokenizer::Retokenize(CString &targ, const char *delim)
{
	mTokenCount = 0;

	ThrowIfNULL_(delim);
	ThrowIf_(*delim == '\0');

	BuildTokenTable(targ, delim);
}

//------------------------------------------------------------------------------
// *ÊGet
//------------------------------------------------------------------------------
// Get the i-th token (0-based) from the target string

char* CTokenizer::Get(int n)
{
	if ( (n < 0) || (n >= mTokenCount) )
		Throw_( CTokenizer::kRangeErr );

	return mTokenTable[n];
}

//------------------------------------------------------------------------------
// *ÊGetLength
//------------------------------------------------------------------------------
// Get the length of the i-th token (0-based)

int CTokenizer::GetLength(int n)
{
	if ( (n < 0) || (n >= mTokenCount) )
		Throw_( CTokenizer::kRangeErr );

	return mTokenLengthTable[n];
}

//------------------------------------------------------------------------------
// *ÊCompare
//------------------------------------------------------------------------------
// Compare the given string to the i-th token

Boolean CTokenizer::Compare(int n, char* pattern)
{
	if ( (n < 0) || (n >= mTokenCount) )
		Throw_( CTokenizer::kRangeErr );
	
	ThrowIfNULL_(pattern);

	return ( ::strncmp(mTokenTable[n], pattern, mTokenLengthTable[n]) == 0 );
}

//------------------------------------------------------------------------------
// *ÊCompareNoCase
//------------------------------------------------------------------------------
// Case insensitive comparison

Boolean CTokenizer::CompareNoCase ( int n, char* pattern )
{
	int		i = 0;
	if ( (n < 0) || (n >= mTokenCount) )
	{
		Throw_( CTokenizer::kRangeErr );
	}

	ThrowIfNULL_( pattern );

	char	*p1		= mTokenTable[n];
	char	*p2		= pattern;
	int		len1	= mTokenLengthTable[n];
	int		len2	= CUtils::Strlen( p2 );

	for ( i = 0; i < len1; i++ )
	{
		if ( mytoupper( *p1++ ) != mytoupper( *p2++ ) )
		{
			return( false );
		}
	}

	if ( i == len2 )
	{
		return( true );
	}
	else
	{
		return( false );
	}
} // CompareNoCase



// US-ASCII toupper
static char mytoupper(char c)
{
	if ((c >= 'a') && (c <= 'z'))
		return (c - 'a' + 'A');
	else
		return c;
}

//------------------------------------------------------------------------------
// *ÊAddToken
//------------------------------------------------------------------------------
// Add the token to the table. Expand the internal tables if needed

void CTokenizer::AddToken(char *ptr, int length)
{
	// expand the token tables if needed...
	if (mTokenCount == mTokenTableSize)
	{
		int newSize = mTokenTableSize * 2;
		
		// Allocate new tables
		char **newTable = NULL;
		int   *newLengthTable = NULL;
		
		ThrowIfNULL_( newTable = new char*[newSize] );
		ThrowIfNULL_( newLengthTable = new int[newSize] );
		
		// Copy existing data
		memcpy( newTable, mTokenTable, mTokenTableSize * sizeof(char**) );
		memcpy( newLengthTable, mTokenLengthTable, mTokenTableSize * sizeof(int*) );
		
		// Free old tables
		delete mTokenTable;
		delete mTokenLengthTable;
		
		// Replace with new
		mTokenTable = newTable;
		mTokenLengthTable = newLengthTable;
		mTokenTableSize = newSize;
	}

	// Record the new token...
	ThrowIf_( mTokenCount >= mTokenTableSize );
	
	mTokenTable[mTokenCount]			= ptr;
	mTokenLengthTable[mTokenCount++]	= length;
}

//------------------------------------------------------------------------------
// *ÊBuildTokenTable
//------------------------------------------------------------------------------
// Build a table of tokens based on the given list of token delimiter characters

void CTokenizer::BuildTokenTable(CString& targ, const char* delim)
{
	ThrowIfNULL_( mTokenTable );
	mTokenCount = 0;

	char	*ptr	= targ.GetData();
	char	*nextp	= NULL;
	char	*lastChar = targ.GetData() + targ.GetLength();
	char	delimFound = '\0';

	ThrowIfNULL_( ptr );
	if (targ.GetLength() == 0)
		return;
	
	nextp = ::strpbrk(ptr, delim);
	while (nextp)
	{
		// save the delimiter we found
		delimFound = *nextp;
		
		// Add current, set up to add next...
		AddToken(ptr, (nextp - ptr));

		// eliminate all sequential occurrences of delimFound
		// be sure not to scan past the end of the buffer
		while (nextp < lastChar)
		{
			// delimFound is the same as the next char
			if (*(nextp+1) == delimFound)
				nextp++;					// skip to it
			else
				break;						// real token starts at next char
		}
		
		ptr = nextp + 1;		// skip over the last delimiter and start at the next token

		nextp = ::strpbrk(ptr, delim);
	}
	
	AddToken(ptr, CUtils::Strlen(ptr)); // get the last one.
}
