/*
	$Id: CStr2IDTable.cpp,v 1.1 2003/04/20 23:32:51 dasenbro Exp $

	File:		CStr2IDTable.cpp

	Contains:	xxx put contents here xxx

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

	Written by:	David O'Rourke

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

 	NOT_FOR_OPEN_SOURCE <to be reevaluated at a later time>

	Notes:

		// *********************************************************************************
		// Warning: DO NOT CHANGE/ADD/REMOVE PUBLIC/PRIVATE DATA FIELDS TO THIS CLASS/STRUCT
		// WITHOUT CONSULTING WITH A APPLE MAIL TEAM MEMBER.  CHANGES TO
		// THIS STRUCTURE __WILL__ CAUSE FILE FORMAT COMPATIBILITY ISSUES FOR APPLESHARE IP 5.0
		// AND 6.0 MAIL DATABASES.
		//
		// FAILURE TO HEED THIS WARNING __WILL__ LEAD TO DATA LOSS
		// AND/OR CRASHING BUGS WHEN CUSTOMERS ATTEMPT TO USE YOUR SIMPLE MODIFICATIONS WITH
		// A PRE-EXISTING MAIL DATABASE.
		//
		// SINCE THIS FILE IS SHARED BY MORE COMPONENTS THAN JUST YOURS, PLEASE BE AWARE OF THE
		// C++ FRAGILE BASE CLASS PROBLEM AND DON'T MAKE CHANGES IN THE BASE CLASS SOURCE CODE
		// WITH OUT CONSULTING OTHER SOURCES THAT ARE DEPENDANT ON THIS STURUCTURE.
		// *********************************************************************************

	Change History:

		$Log: CStr2IDTable.cpp,v $
		Revision 1.1  2003/04/20 23:32:51  dasenbro
		Initial check-in.
		
		Revision 1.8  2002/05/09 16:58:55  dasenbro
		Changed all str... calls to CUtils::Str... to be NULL safe.
		
		Revision 1.7  2002/03/21 16:41:17  dasenbro
		Updated file version information.
		
		Revision 1.6  2001/06/21 20:50:54  dasenbro
		Updated file header info.
		
		Revision 1.5  2001/06/21 17:01:16  dasenbro
		Added Change History.
		

	Projector History:

	  <ASX4>	  6/4/99	DOR		Make sizes correct.
	  <ASX3>	  6/2/99	DOR		Use the correct sizes for the backwards compatibility checks.
		 <3>	  4/8/98	DOR		Change from using ::strncpy to CUtils::strncpy
		 <2>	  4/8/98	DOR		Change a strcpy to a ::strncpy

	To Do:
*/


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

#include "CUtils.h"
#include "CStr2IDTable.h"

void CStr2IDTable::strncpy ( char *inTargetStr, char *inSourceStr, const uInt32 inMaxLength )
{
	BaseDBAssert(inTargetStr != NULL);
	BaseDBAssert(inSourceStr != NULL);
	BaseDBAssert(inMaxLength > 0);
	
	if ((inTargetStr != NULL) && (inSourceStr != NULL) && (inMaxLength > 0))
	{
		uInt32 sourceLength = CUtils::Strlen( inSourceStr );
		BaseDBAssert(sourceLength <= inMaxLength);
		if (sourceLength > inMaxLength)
		{
			sourceLength = inMaxLength;
			::strncpy(inTargetStr, inSourceStr, sourceLength);
			inTargetStr[inMaxLength] = 0x00;
		}
		else
		{
			CUtils::Strcpy(inTargetStr, inSourceStr);
		}
	}
}

CStr2IDTable::CStr2IDTable ( Boolean inCaseSensitiveFlag )
{
	// *********************************************************************************
	// Warning: DO NOT CHANGE/ADD/REMOVE PUBLIC/PRIVATE DATA FIELDS TO THIS CLASS/STRUCT
	// WITHOUT CONSULTING WITH A APPLESHARE IP MAIL TEAM MEMBER.  CHANGES TO
	// THIS STRUCTURE __WILL__ CAUSE FILE FORMAT COMPATIBILITY ISSUES FOR APPLESHARE IP 5.0
	// AND 6.0 MAIL DATABASES.
	//
	// FAILURE TO HEED THIS WARNING __WILL__ LEAD TO DATA LOSS
	// AND/OR CRASHING BUGS WHEN CUSTOMERS ATTEMPT TO USE YOUR SIMPLE MODIFICATIONS WITH
	// A PRE-EXISTING MAIL DATABASE.
	//
	// SINCE THIS FILE IS SHARED BY MORE COMPONENTS THAN JUST YOURS, PLEASE BE AWARE OF THE
	// C++ FRAGILE BASE CLASS PROBLEM AND DON'T MAKE CHANGES IN THE BASE CLASS SOURCE CODE
	// WITH OUT CONSULTING OTHER SOURCES THAT ARE DEPENDANT ON THIS STURUCTURE.
	// *********************************************************************************

	fPageID = 0;
	fTempHeader.fDataTableVersion = kStr2IDTableCurVersion;
	
	fTempHeader.fTableType = kStr2IDTableType;
	fTempHeader.fCaseSensitiveFlag = inCaseSensitiveFlag;			// current number of entries in this table
	fTempHeader.fCount = 0;			// current number of entries in this table

	fTempHeader.fNextID = 0;
	fTempHeader.fPrevID = 0;
	fTempHeader.fParentID = 0;
	
	fTempHeader.fStringHeap = 0;

	::memset(fTempHeader.fCurrentMinString, 0, kStr2IDMaxStringSize);
	fTempHeader.f1stStringEntry[0].fStrOffset = 0;
	fTempHeader.f1stStringEntry[0].fObjID = 0;
	
	fStr2IDTablePtr = NULL;
	
	CStr2IDTable::CheckBackwardCompatibility();
}

void CStr2IDTable::CheckBackwardCompatibility ( void )
{
	//*************************************************************************************************
	// if any of these checks cause a debugger break, then some change has caused the binary size of
	// these data types to be different than what was shipped with AppleShare IP 5.0 & 6.0....
	// this means that there will be data-loss or crashing bugs with this version of CIDTable since
	// portions of CIDTable have been written to disk in their native binary format....
//	CStr2IDTable::ReportBackwardCompatibility(sizeof(ObjID),				4);		// NOTE: this is hard coded for a reason!!
//	CStr2IDTable::ReportBackwardCompatibility(sizeof(PageID),				4);		// NOTE: this is hard coded for a reason!!
//	CStr2IDTable::ReportBackwardCompatibility(sizeof(SStr2IDEntry),			12);	// NOTE: this is hard coded for a reason!!
//	CStr2IDTable::ReportBackwardCompatibility(sizeof(SStr2IDTableHeader),	300);	// NOTE: this is hard coded for a reason!!
//	CStr2IDTable::ReportBackwardCompatibility(kStandardPageSize,			8192);	// NOTE: this is hard coded for a reason!!
//	CStr2IDTable::ReportBackwardCompatibility(sizeof(SStr2IDTable),			8192);	// NOTE: this is hard coded for a reason!!
}

void CStr2IDTable::ReportBackwardCompatibility ( const uInt32 curSize, const uInt32 correctSize )
{
	if (curSize != correctSize)
	{
//		DebugStr("\pWARNING: CStr2IDTable Base Class data element size problem.  Potential File format compatibility problem detected!");
	}
}

CStr2IDTable::~CStr2IDTable ( void )
{
	// *********************************************************************************
	// Warning: DO NOT CHANGE/ADD/REMOVE PUBLIC/PRIVATE DATA FIELDS TO THIS CLASS/STRUCT
	// WITHOUT CONSULTING WITH A APPLESHARE IP MAIL TEAM MEMBER.  CHANGES TO
	// THIS STRUCTURE __WILL__ CAUSE FILE FORMAT COMPATIBILITY ISSUES FOR APPLESHARE IP 5.0
	// AND 6.0 MAIL DATABASES.
	//
	// FAILURE TO HEED THIS WARNING __WILL__ LEAD TO DATA LOSS
	// AND/OR CRASHING BUGS WHEN CUSTOMERS ATTEMPT TO USE YOUR SIMPLE MODIFICATIONS WITH
	// A PRE-EXISTING MAIL DATABASE.
	//
	// SINCE THIS FILE IS SHARED BY MORE COMPONENTS THAN JUST YOURS, PLEASE BE AWARE OF THE
	// C++ FRAGILE BASE CLASS PROBLEM AND DON'T MAKE CHANGES IN THE BASE CLASS SOURCE CODE
	// WITH OUT CONSULTING OTHER SOURCES THAT ARE DEPENDANT ON THIS STURUCTURE.
	// *********************************************************************************

	fPageID = 0;
	fTempHeader.fDataTableVersion = kStr2IDTableCurVersion;
	
	fTempHeader.fTableType = kStr2IDTableType;
	fTempHeader.fCaseSensitiveFlag = false;			// current number of entries in this table
	fTempHeader.fCount = 0;			// current number of entries in this table

	fTempHeader.fNextID = 0;
	fTempHeader.fPrevID = 0;
	fTempHeader.fParentID = 0;
	
	fTempHeader.fStringHeap = 0;

	fTempHeader.f1stStringEntry[0].fStrOffset = 0;
	fTempHeader.f1stStringEntry[0].fObjID = 0;
	::memset(fTempHeader.fCurrentMinString, 0, kStr2IDMaxStringSize);
	
	fStr2IDTablePtr = NULL;
}

OSStatus CStr2IDTable::SetTable ( void *inTablePtr, const PageID inPageID)
{
	BaseDBAssert(this != NULL);
	if (this == NULL)
	{
		return kStr2IDBadThisObject;
	}
		
	fPageID = inPageID;
	fStr2IDTablePtr = (SStr2IDTable *) inTablePtr;
	
	fTempHeader.fStringHeap = kMaxStr2IDTableSize-1;
	return kStr2IDTableNoErr;
}

OSStatus CStr2IDTable::ClearStr2IDTable	( Boolean inCaseSensitiveFlag )
{
	// Basic Error checking before doing the "meat" of the function...	
	BaseDBAssert(this != NULL);
	if (this == NULL)
	{
		return kStr2IDBadThisObject;
	}

	BaseDBAssert(fStr2IDTablePtr != NULL);
	if (fStr2IDTablePtr == NULL)
	{
		return kStr2IDBadTablePtr;
	}
	
	fTempHeader.fNextID = 0;
	fTempHeader.fPrevID = 0;
	fTempHeader.fParentID = 0;

	fTempHeader.fCaseSensitiveFlag = inCaseSensitiveFlag;			// current number of entries in this table
	fTempHeader.fCount = 0;			// current number of entries in this table

	::memset(fTempHeader.fCurrentMinString, 0, kStr2IDMaxStringSize);

	fTempHeader.f1stStringEntry[0].fStrOffset = 0;
	fTempHeader.f1stStringEntry[0].fObjID = 0;

	fTempHeader.fStringHeap = kMaxStr2IDTableSize-1;
	
	if (fStr2IDTablePtr != NULL)
	{
		fStr2IDTablePtr->fStr2IDTableHeader = fTempHeader;
		memset(fStr2IDTablePtr->fStr2IDDTableData, 0, kMaxStr2IDTableSize-1);
	}
	
	return kStr2IDTableNoErr;
}

OSStatus CStr2IDTable::AddStr2List ( const char *inEntryString, const ObjID inEntryID, const PageID inObjPageID, Boolean allowNewMin)
{
	OSStatus		result = kStr2IDTableNoErr;
	const uInt32	inStrLength = CUtils::Strlen(inEntryString);
	uInt32			anIndex = 0;
	uInt32			aStrIndex = 0;
	char			*aStrEntry = NULL;
	char			*aKeyString = NULL;
	ObjID			anObjID = 0;
	SStr2IDEntry	anEntry;
	SStr2IDEntry	aTempEntry;
	long			aCmpResult = 0;

	// Basic Error checking before doing the "meat" of the function...	
	BaseDBAssert(this != NULL);
	if (this == NULL)
	{
		return kStr2IDBadThisObject;
	}

	BaseDBAssert(fStr2IDTablePtr != NULL);
	if (fStr2IDTablePtr == NULL)
	{
		return kStr2IDBadTablePtr;
	}
	
	BaseDBAssert(inEntryString != NULL);
	if (inEntryString == NULL)
	{
		return kStr2IDBadString;
	}

	BaseDBAssert(inStrLength < kStr2IDMaxStringSize);
	if (inStrLength >= kStr2IDMaxStringSize)
	{
		return kStr2IDString2Big;
	}
	BaseDBAssert(inEntryID != 0);

	if (this->CalcFreeSpace() > ((inStrLength) + sizeof(SStr2IDEntry)))
	{
		result = this->FindEntry(inEntryString, anIndex);
		if (result != kStr2IDTableNoErr)
		{
			// lets check to see if we have a new min. string value...
			if (CUtils::Strlen(this->GetMinCharStar()) > 0)
			{
				aCmpResult = CUtils::Strcmp(inEntryString, this->GetMinCharStar());
				if ((aCmpResult < 0) && (allowNewMin == false))
				{	// we have a new min. value, but we're not allowed to add it to the list...
					return kStr2IDNewMinNotAllowed;
				}
			}
			else
			{
				aCmpResult = -1;	// fake a new min. value...
			}
			
			if ((aCmpResult < 0) && (allowNewMin == true))
			{
				memset(this->GetMinCharStar(), 0, kStr2IDMaxStringSize);
				CStr2IDTable::strncpy(this->GetMinCharStar(), (char *) inEntryString, kStr2IDMaxStringSize);
			}

			// lets copy the new string into the string-heap...
			this->SetStringHeap(this->GetStringHeap() - (inStrLength+1));
			anEntry.fStrOffset = this->GetStringHeap();
			anEntry.fObjID = inEntryID;
			anEntry.fObjPageID = inObjPageID;
			
			aKeyString = this->GetCharStar(anEntry.fStrOffset);
			CStr2IDTable::strncpy(aKeyString, (char *) inEntryString, inStrLength);
			aKeyString[inStrLength] = 0x00;
			
			if (this->GetCaseSensitiveFlag() == false)
			{
				for (aStrIndex = 0; aStrIndex < inStrLength; aStrIndex++)
				{
					aKeyString[aStrIndex] = ::tolower(aKeyString[aStrIndex]);
				}
			}

			if (this->GetEntryCount() > 0)
			{
				result = this->GetEntry(anIndex, aTempEntry);
				aCmpResult = CUtils::Strcmp(this->GetCharStar(aTempEntry.fStrOffset), aKeyString);
				
				if (aCmpResult < 0)
				{
					anIndex = anIndex + 1;
				}
				else if (aCmpResult == 0)
				{
					BaseDBAssert(aCmpResult != 0);
				}
			}
			else
			{
				anIndex = 0;
			}		
			
			result = InsertEntry(anIndex, anEntry);
		}
		else
		{
			result = kStr2IDTableDuplicateObject;
		}
	}
	else
	{
		result = kStr2IDTableFull;
	}
	
	return result;
}

char * CStr2IDTable::GetCharStar ( const unsigned short inStringOffset )
{
	char *result = NULL;
	
	// Basic Error checking before doing the "meat" of the function...	
	BaseDBAssert(this != NULL);
	if (this == NULL)
	{
		return result;
	}

	BaseDBAssert(fStr2IDTablePtr != NULL);
	if (fStr2IDTablePtr == NULL)
	{
		return result;
	}
	
	result = &(fStr2IDTablePtr->fStr2IDDTableData[inStringOffset]);
	
	return result;
}

char * CStr2IDTable::GetMinCharStar ( void )
{
	char *result = NULL;
	
	// Basic Error checking before doing the "meat" of the function...	
	BaseDBAssert(this != NULL);
	if (this == NULL)
	{
		return result;
	}

	BaseDBAssert(fStr2IDTablePtr != NULL);
	if (fStr2IDTablePtr == NULL)
	{
		return result;
	}
	
	result = this->fStr2IDTablePtr->fStr2IDTableHeader.fCurrentMinString;
	
	return result;
}

uInt32 CStr2IDTable::CalcFreeSpace ( void )
{
	uInt32	dataSize = 0;
	uInt32	freeSpace = 0;
	
	// Basic Error checking before doing the "meat" of the function...	
	BaseDBAssert(this != NULL);
	if (this == NULL)
	{
		return freeSpace;
	}

	BaseDBAssert(fStr2IDTablePtr != NULL);
	if (fStr2IDTablePtr == NULL)
	{
		return freeSpace;
	}
	
	dataSize = this->GetEntryCount() * sizeof(SStr2IDEntry);
	freeSpace = this->GetStringHeap() - dataSize;
	
	return freeSpace;
}

OSStatus CStr2IDTable::RemoveStrFromList ( const char *inEntryString )
{
	OSStatus	result = kStr2IDTableNoErr;
	uInt32		anIndex = 0;
	
	// Basic Error checking before doing the "meat" of the function...	
	BaseDBAssert(this != NULL);
	if (this == NULL)
	{
		return kStr2IDBadThisObject;
	}

	BaseDBAssert(fStr2IDTablePtr != NULL);
	if (fStr2IDTablePtr == NULL)
	{
		return kStr2IDBadTablePtr;
	}
	
	result = this->FindEntry(inEntryString, anIndex);
	if (result == kStr2IDTableNoErr)
	{
		result = this->DeleteEntry(anIndex);
	}
	
	return result;
}

char * CStr2IDTable::GetNewString ( const char *inSourceString, Boolean inMakeLowerCaseFlag )
{
	uInt32	index = 0;
	uInt32	strSize = 0;
	char	*aNewString = NULL;
	
	strSize = CUtils::Strlen(inSourceString);
	aNewString = new char[strSize + 1];
	BaseDBAssert(aNewString != NULL);
	
	CStr2IDTable::strncpy(aNewString, (char *) inSourceString, (strSize + 1));
	if (inMakeLowerCaseFlag == true)
	{
		for (index = 0; index < strSize; index++)
		{
			aNewString[index] = ::tolower(aNewString[index]);
		}
	}
	
	return aNewString;
}

OSStatus CStr2IDTable::FindEntry ( const char *inEntryString, uInt32 &outIndex )
{
	OSStatus		result = kStr2IDTableNoSuchObject;
	uInt32			index = 0;
	const uInt32	strSize = CUtils::Strlen(inEntryString);
	long			high = 0;
	long			low = 0;
	long			cmpResult = 0;
	char			*aSearchStr = NULL;
	SStr2IDEntry	anEntry;
	Boolean			doSearch = true;
	
	outIndex = kStr2IDNullIndex;

	// Basic Error checking before doing the "meat" of the function...	
	BaseDBAssert(this != NULL);
	if (this == NULL)
	{
		return kStr2IDBadThisObject;
	}

	BaseDBAssert(fStr2IDTablePtr != NULL);
	if (fStr2IDTablePtr == NULL)
	{
		return kStr2IDBadTablePtr;
	}
	
	BaseDBAssert(strSize < kStr2IDMaxStringSize);
	if (strSize >= kStr2IDMaxStringSize)
	{
		return kStr2IDString2Big;
	}

	if (this->GetEntryCount() > 0)
	{
		// now that we've made it past the error checking, lets start the "meat" of the
		// function

		// setup the boundaries for the binary search...
		low = 0;
		high = this->GetEntryCount() - 1;

		aSearchStr = this->GetNewString(inEntryString, (this->GetCaseSensitiveFlag() == false));
		BaseDBAssert(aSearchStr != NULL);

		if (this->GetEntryCount() > 2)
		{	// we'll do some summary checks to see if we should even bother looking...
			// we only do the summary boundary checks if there are more than 2 entires, since we'll do
			// at least two string compares in this function, if there are only 2 entries in the array there's
			// no point duplicating the effort with the normal algorythm.
			
			result = GetEntry(0, anEntry);
			BaseDBAssert(result == kStr2IDTableNoErr);
			
			cmpResult = CUtils::Strcmp(aSearchStr, this->GetCharStar(anEntry.fStrOffset));
			if (cmpResult == 0)
			{	// WOW, we found that the search string matches the 1st entry in the array
				// lucky US...this is like an O(1) function performance....
				doSearch = false;
				outIndex = 0;
				result = kStr2IDTableNoErr;
			}
			else if (cmpResult < 0)
			{	// hmmm, the string we're searching for is less than the min. entry in the array, no point searching
				// the array for this string, it's not there....
				doSearch = false;
				outIndex = 0;
				result = kStr2IDTableNoSuchObject;
			}
			else
			{
				low = 1;		// we've already checked the zero entry, no point in checking it again...
				doSearch = true;
				result = kStr2IDTableNoSuchObject;
			}

			if (doSearch)
			{
				result = GetEntry((this->GetEntryCount() - 1), anEntry);
				BaseDBAssert(result == kStr2IDTableNoErr);
				cmpResult = CUtils::Strcmp(aSearchStr, this->GetCharStar(anEntry.fStrOffset));
				if (cmpResult == 0)
				{	// WOW, we found that the search string matches the last entry in the array
					// lucky US...this is like an O(2) function performance....
					doSearch = false;
					outIndex = (this->GetEntryCount() - 1);
					result = kStr2IDTableNoErr;
				}
				else if (cmpResult > 0)
				{	// ok the string we're searching for is greater than the last entry in the array..
					// no point in going any further with this charade...
					doSearch = false;
					outIndex = (this->GetEntryCount() - 1);
					result = kStr2IDTableNoSuchObject;
				}
				else
				{
					high =  this->GetEntryCount() - 2;	// we've already checked the last entry, no point in checking it again...
					doSearch = true;
					result = kStr2IDTableNoSuchObject;
				}
			}			
		}

		if (doSearch)
		{
			while (low <= high)
			{
				index = low + ((high - low) / 2);
				outIndex = index;

				result = GetEntry(index, anEntry);
				BaseDBAssert(result == kStr2IDTableNoErr);
				
				cmpResult = CUtils::Strcmp(this->GetCharStar(anEntry.fStrOffset), aSearchStr);
				if (cmpResult == 0)
				{
					result = kStr2IDTableNoErr;
					break;
				}
				else
				{
					result = kStr2IDTableNoSuchObject;
					if (cmpResult < 0)
						low = index + 1;
					else
						high = index - 1;
				}
			}
		}		

		if (aSearchStr != NULL)
		{
			delete aSearchStr;
			aSearchStr = NULL;
		}
	}
	
	return result;
}

OSStatus CStr2IDTable::GetEntry ( const uInt32 inIndex, SStr2IDEntry &outEntry )
{
	OSStatus	result = kStr2IDTableNoErr;

	// Basic Error checking before doing the "meat" of the function...	
	BaseDBAssert(this != NULL);
	if (this == NULL)
	{
		return kStr2IDBadThisObject;
	}

	BaseDBAssert(fStr2IDTablePtr != NULL);
	if (fStr2IDTablePtr == NULL)
	{
		return kStr2IDBadTablePtr;
	}
	
	BaseDBAssert((inIndex >= 0) && (inIndex < this->GetEntryCount()));
	
	if ((inIndex >= 0) && (inIndex < this->GetEntryCount()))
	{
		outEntry = this->fStr2IDTablePtr->fStr2IDTableHeader.f1stStringEntry[inIndex];
	}
	else
	{
		result = kStr2IDTableIndexOutOfRange;
	}
	
	return result;
}

OSType CStr2IDTable::GetTableType ( void )
{
	// Basic Error checking before doing the "meat" of the function...	
	BaseDBAssert(this != NULL);
	if (this == NULL)
	{
		return NULL;
	}

	BaseDBAssert(fStr2IDTablePtr != NULL);
	if (fStr2IDTablePtr == NULL)
	{
		return NULL;
	}
	
	return this->fStr2IDTablePtr->fStr2IDTableHeader.fTableType;
}
		
OSStatus CStr2IDTable::SetStringHeap ( const unsigned short inStringHeap )
{
	// Basic Error checking before doing the "meat" of the function...	
	BaseDBAssert(this != NULL);
	if (this == NULL)
	{
		return kStr2IDBadThisObject;
	}

	BaseDBAssert(fStr2IDTablePtr != NULL);
	if (fStr2IDTablePtr == NULL)
	{
		return kStr2IDBadTablePtr;
	}
	
	if (inStringHeap != (this->GetStringHeap()))
	{
		this->fStr2IDTablePtr->fStr2IDTableHeader.fStringHeap = inStringHeap;
	}
	
	return kStr2IDTableNoErr;
}

Boolean CStr2IDTable::GetCaseSensitiveFlag ( void )
{
	Boolean	result = false;

	// Basic Error checking before doing the "meat" of the function...	
	BaseDBAssert(this != NULL);
	if (this == NULL)
	{
		return result;
	}

	BaseDBAssert(fStr2IDTablePtr != NULL);
	if (fStr2IDTablePtr == NULL)
	{
		return result;
	}
	
	result = this->fStr2IDTablePtr->fStr2IDTableHeader.fCaseSensitiveFlag;
	return result;
}

unsigned short CStr2IDTable::GetStringHeap ( void )
{
	unsigned short	result = 0;

	// Basic Error checking before doing the "meat" of the function...	
	BaseDBAssert(this != NULL);
	if (this == NULL)
	{
		return result;
	}

	BaseDBAssert(fStr2IDTablePtr != NULL);
	if (fStr2IDTablePtr == NULL)
	{
		return result;
	}
	
	result = this->fStr2IDTablePtr->fStr2IDTableHeader.fStringHeap;
	return result;
}

OSStatus CStr2IDTable::SetEntryCount ( const uInt32 inEntryCount )
{
	// Basic Error checking before doing the "meat" of the function...	
	BaseDBAssert(this != NULL);
	if (this == NULL)
	{
		return kStr2IDBadThisObject;
	}

	BaseDBAssert(fStr2IDTablePtr != NULL);
	if (fStr2IDTablePtr == NULL)
	{
		return kStr2IDBadTablePtr;
	}
	
	if (inEntryCount != (this->GetEntryCount()))
	{
		this->fStr2IDTablePtr->fStr2IDTableHeader.fCount = inEntryCount;
	}
	
	return kStr2IDTableNoErr;
}

uInt32 CStr2IDTable::GetEntryCount ( void )
{
	uInt32	result = 0;
	// Basic Error checking before doing the "meat" of the function...	
	BaseDBAssert(this != NULL);
	if (this == NULL)
	{
		return result;
	}

	BaseDBAssert(fStr2IDTablePtr != NULL);
	if (fStr2IDTablePtr == NULL)
	{
		return result;
	}
	
	result = this->fStr2IDTablePtr->fStr2IDTableHeader.fCount;
	
	return result;
}

PageID CStr2IDTable::GetNextID ( void )
{
	PageID	result = 0;

	// Basic Error checking before doing the "meat" of the function...	
	BaseDBAssert(this != NULL);
	if (this == NULL)
	{
		return result;
	}

	BaseDBAssert(fStr2IDTablePtr != NULL);
	if (fStr2IDTablePtr == NULL)
	{
		return result;
	}
	
	result = this->fStr2IDTablePtr->fStr2IDTableHeader.fNextID;
	
	return result;
}

OSStatus CStr2IDTable::SetNextID ( const PageID inNextIDTable )
{
	// Basic Error checking before doing the "meat" of the function...	
	BaseDBAssert(this != NULL);
	if (this == NULL)
	{
		return kStr2IDBadThisObject;
	}

	BaseDBAssert(fStr2IDTablePtr != NULL);
	if (fStr2IDTablePtr == NULL)
	{
		return kStr2IDBadTablePtr;
	}
	
	this->fStr2IDTablePtr->fStr2IDTableHeader.fNextID = inNextIDTable;
	
	return kStr2IDTableNoErr;
}


PageID CStr2IDTable::GetPrevID ( void )
{
	PageID	result = 0;

	// Basic Error checking before doing the "meat" of the function...	
	BaseDBAssert(this != NULL);
	if (this == NULL)
	{
		return result;
	}

	BaseDBAssert(fStr2IDTablePtr != NULL);
	if (fStr2IDTablePtr == NULL)
	{
		return result;
	}
	
	result = this->fStr2IDTablePtr->fStr2IDTableHeader.fPrevID;
	
	return result;
}

OSStatus CStr2IDTable::SetPrevID ( const PageID inPrevIDTable )
{
	// Basic Error checking before doing the "meat" of the function...	
	BaseDBAssert(this != NULL);
	if (this == NULL)
	{
		return kStr2IDBadThisObject;
	}

	BaseDBAssert(fStr2IDTablePtr != NULL);
	if (fStr2IDTablePtr == NULL)
	{
		return kStr2IDBadTablePtr;
	}
	
	this->fStr2IDTablePtr->fStr2IDTableHeader.fPrevID = inPrevIDTable;
	
	return kStr2IDTableNoErr;
}

PageID CStr2IDTable::GetParentID ( void )
{
	PageID	result = 0;

	// Basic Error checking before doing the "meat" of the function...	
	BaseDBAssert(this != NULL);
	if (this == NULL)
	{
		return result;
	}

	BaseDBAssert(fStr2IDTablePtr != NULL);
	if (fStr2IDTablePtr == NULL)
	{
		return result;
	}
	
	result = this->fStr2IDTablePtr->fStr2IDTableHeader.fParentID;
	
	return result;
}

OSStatus CStr2IDTable::SetParentID ( const PageID inParentIDTable )
{
	// Basic Error checking before doing the "meat" of the function...	
	BaseDBAssert(this != NULL);
	if (this == NULL)
	{
		return kStr2IDBadThisObject;
	}

	BaseDBAssert(fStr2IDTablePtr != NULL);
	if (fStr2IDTablePtr == NULL)
	{
		return kStr2IDBadTablePtr;
	}
	
	this->fStr2IDTablePtr->fStr2IDTableHeader.fParentID = inParentIDTable;
	
	return kStr2IDTableNoErr;
}

OSStatus CStr2IDTable::SetTablePageID ( const PageID inPageID )
{
	// Basic Error checking before doing the "meat" of the function...	
	BaseDBAssert(this != NULL);
	if (this == NULL)
	{
		return kStr2IDBadThisObject;
	}
	
	fPageID = inPageID;

	return kStr2IDTableNoErr;
}

PageID CStr2IDTable::GetTablePageID ( void )
{
	// Basic Error checking before doing the "meat" of the function...	
	BaseDBAssert(this != NULL);
	if (this == NULL)
	{
		return 0;
	}
	
	return fPageID;
}

OSStatus CStr2IDTable::DeleteEntry ( const uInt32 inIndex )
{
	SStr2IDEntry	anEntry;
	OSStatus		result			= kStr2IDTableNoErr;
	uInt32			anIndex			= 0;
	uInt32			bytes2Move		= 0;
	uInt32			distance		= 0;
	char			*anEntryString	= NULL;
	char			*the1stString	= NULL;
	
	BaseDBAssert(this != NULL);
	
	// 1st we've got to compress the string heap....	
	result = this->GetEntry( inIndex, anEntry );
	BaseDBAssert( result == kStr2IDTableNoErr );

	anEntryString = this->GetCharStar( anEntry.fStrOffset );
	the1stString = this->GetCharStar( this->GetStringHeap() );
	
	bytes2Move = anEntryString - the1stString;
	distance = CUtils::Strlen(anEntryString) + 1;
	
	for (anEntryString = anEntryString - 1; anEntryString >= the1stString; anEntryString--)
	{
		anEntryString[distance] = *anEntryString;
	}

	this->SetStringHeap( this->GetStringHeap() + distance );
	::memset( the1stString, 0, distance );

	anEntryString = this->GetCharStar(anEntry.fStrOffset);

	// buzz through the array and adjust any offsets that were affected by our
	// recent string heap compression...
	for (anIndex = 0; anIndex < this->GetEntryCount(); anIndex++)
	{
		result = this->GetEntry(anIndex, anEntry);
		BaseDBAssert(result == kStr2IDTableNoErr);
		
		if (this->GetCharStar(anEntry.fStrOffset) < anEntryString)
		{
			anEntry.fStrOffset += distance;
			result = this->SetEntry(anIndex, anEntry);
			BaseDBAssert(result == kStr2IDTableNoErr);
		}
	}
	
	// now that we've compressed the string heap, we can compress the entry array..	
	for (anIndex = inIndex + 1; anIndex < this->GetEntryCount(); anIndex++)
	{
		result = this->GetEntry(anIndex, anEntry);
		BaseDBAssert(result == kStr2IDTableNoErr);
		
		result = this->SetEntry(anIndex-1, anEntry);
		BaseDBAssert(result == kStr2IDTableNoErr);
	}
	
	this->SetEntryCount(this->GetEntryCount() - 1);
	return result;
}

OSStatus CStr2IDTable::SetEntry (const uInt32 inIndex, const SStr2IDEntry inEntry)
{
	OSStatus	result = kStr2IDTableNoErr;

	BaseDBAssert(this != NULL);
	BaseDBAssert((inIndex >= 0) && (inIndex <= this->GetEntryCount()));
	
	if ((inIndex >= 0) && (inIndex <= this->GetEntryCount()))
	{
		this->fStr2IDTablePtr->fStr2IDTableHeader.f1stStringEntry[inIndex] = inEntry;
	}
	else
	{
		result = kStr2IDTableIndexOutOfRange;
	}
	
	return result;
}

OSStatus CStr2IDTable::InsertEntry (const uInt32 inIndex, const SStr2IDEntry inEntry)
{
	OSStatus		result = kStr2IDTableNoErr;
	uInt32			anIndex = 0;
	SStr2IDEntry	anEntry;
	
	BaseDBAssert(this != NULL);
	
	if (this->GetEntryCount() > 0)
	{
		for (anIndex = this->GetEntryCount(); anIndex > inIndex; anIndex--)
		{
			result = this->GetEntry(anIndex-1, anEntry);
			BaseDBAssert(result == kStr2IDTableNoErr);
			
			result = this->SetEntry(anIndex, anEntry);
			BaseDBAssert(result == kStr2IDTableNoErr);
		}
	}
	else
	{
		BaseDBAssert(inIndex == 0);
	}
	
	this->SetEntryCount(this->GetEntryCount() + 1);
	this->SetEntry(inIndex, inEntry);
	
	return result;
}

OSStatus CStr2IDTable::SanityCheck ( void )
{
	return kStr2IDTableNoErr;
}
