#ifndef _T_JStringMap
#define _T_JStringMap

/******************************************************************************
 JStringMap.tmpl

	BASE CLASS = JHashTable< JStrValue<V> >

	Copyright © 1997 by Dustin Laurence.  All rights reserved.
	
	Base code generated by Codemill v0.1.0

 *****************************************************************************/

//Class Header
#include <JStringMap.h>

#include <string.h>

#include <jAssert.h>

/******************************************************************************
 Constructor

 *****************************************************************************/

template <class V>
JStringMap<V>::JStringMap
	(
	const JBoolean copyKeys
	)
	:
	JHashTable< JStrValue<V> >(),
	itsCopyKeysFlag(copyKeys)
{
	DisallowCursors();
	SetKeyComparison(Compare);
	SetHashFunction(Hash);
}

template <class V>
JStringMap<V>::JStringMap
	(
	const JSize    lgSize,  // = kJDefaultLgMinTableSize
	const JBoolean copyKeys // = kTrue
	)
	:
	JHashTable< JStrValue<V> >(lgSize),
	itsCopyKeysFlag(copyKeys)
{
	DisallowCursors();
	SetKeyComparison(Compare);
	SetHashFunction(Hash);
}

/******************************************************************************
 Destructor

 *****************************************************************************/

template <class V>
JStringMap<V>::~JStringMap()
{
	RemoveAll();
}

/******************************************************************************
 Set...Element

	The basic idea of setting a map element is simple.  However, it is
	common to write code which will set a value depending on whether it
	already exists.  Several forms of Set...Element support this notion
	more efficiently than calling Contains() and then SetElement().  In
	particular, the hash calculation and internal search will only happen
	once.

 *****************************************************************************/

/******************************************************************************
 SetElement

	The element is set if it already existed and 'type' is kIfOld or kAlways
	(the default) or if it did not exist and 'type' is kIfNew or kAlways.

	Returns kTrue if the element was set, kFalse otherwise.

 *****************************************************************************/

template <class V>
JBoolean
JStringMap<V>::SetElement
	(
	const JCharacter* const        key,
	const V&                       value,
	const JStringMapT::SetType type // = kAlways
	)
{
	JHashCursor< JStrValue<V> >* cursor = GetCursor();
	JStrValue<V> hashEntry(key, value);

	cursor->ResetKey(hashEntry);
	cursor->ForceNextMapInsertKey();

	if ( cursor->IsFull() )
		{
		if (type != JStringMapT::kIfNew)
			{
			// Otherwise we'd pass in the client's key!
			hashEntry.key = cursor->GetValue().key;
			cursor->Set(hashEntry);
			return kTrue;
			}
		else
			{
			return kFalse;
			}
		}
	else
		{
		if (type != JStringMapT::kIfOld)
			{
			if (itsCopyKeysFlag)
				{
				JCharacter* myKey = new JCharacter[strlen(key)+1];
				assert(myKey != NULL);
				strcpy(myKey, key);
				hashEntry.key = myKey;
				cursor->Set(cursor->GetCursorHashValue(), hashEntry);
				}
			else
				{
				cursor->Set(cursor->GetCursorHashValue(), hashEntry);
				}
			return kTrue;
			}
		else
			{
			return kFalse;
			}
		}
}

/******************************************************************************
 SetContains

	A sometimes useful hybrid form; always sets the element, and returns kTrue
	if the element already existed, kFalse otherwise.

 *****************************************************************************/

template <class V>
JBoolean
JStringMap<V>::SetContains
	(
	const JCharacter* const key,
	const V&                value
	)
{
	JHashCursor< JStrValue<V> >* cursor = GetCursor();
	JStrValue<V> hashEntry(key, value);

	cursor->ResetKey(hashEntry);
	cursor->ForceNextMapInsertKey();

	if ( cursor->IsFull() )
		{
		// Otherwise we'd pass in the client's key!
		hashEntry.key = cursor->GetValue().key;
		cursor->Set(hashEntry);
		return kTrue;
		}
	else
		{
		if (itsCopyKeysFlag)
			{
			JCharacter* myKey = new JCharacter[strlen(key)+1];
			assert(myKey != NULL);
			strcpy(myKey, key);
			hashEntry.key = myKey;
			cursor->Set(cursor->GetCursorHashValue(), hashEntry);
			}
		else
			{
			cursor->Set(cursor->GetCursorHashValue(), hashEntry);
			}
		return kFalse;
		}
}

/******************************************************************************
 GetElement

	If the element is found, sets value to it and returns kTrue; otherwise,
	leaves value unmodified and returns kFalse.

 *****************************************************************************/

template <class V>
JBoolean
JStringMap<V>::GetElement
	(
	const JCharacter* const key,
	V*       value
	)
	const
{
	JConstHashCursor< JStrValue<V> >* cursor = GetCursor();
	// We know the hash value does not depend on V, so this is safe
	JStrValue<V> hashEntry;
	hashEntry.key = key;
	cursor->ResetKey(hashEntry);

	if ( cursor->NextKey() )
		{
		*value = cursor->GetValue().value;
		return kTrue;
		}
	else
		{
		return kFalse;
		}
}

/******************************************************************************
 Contains

 *****************************************************************************/

template <class V>
JBoolean
JStringMap<V>::Contains
	(
	const JCharacter* const key
	)
	const
{
	JConstHashCursor< JStrValue<V> >* cursor = GetCursor();
	// We know the hash value does not depend on V, so this is safe
	JStrValue<V> hashEntry;
	hashEntry.key = key;
	cursor->ResetKey(hashEntry);
	return cursor->NextKey();
}

/******************************************************************************
 RemoveElement

 *****************************************************************************/

template <class V>
JBoolean
JStringMap<V>::RemoveElement
	(
	const JCharacter* const key
	)
{
	JHashCursor< JStrValue<V> >* cursor = GetCursor();
	// We know the hash value does not depend on V, so this is safe
	JStrValue<V> hashEntry;
	hashEntry.key = key;
	cursor->ResetKey(hashEntry);
	if ( cursor->NextKey() )
		{
		if (itsCopyKeysFlag)
			{
			delete[] const_cast<JCharacter*>(cursor->GetValue().key);
			}
		cursor->Remove();
		return kTrue;
		}
	else
		{
		return kFalse;
		}
}

/******************************************************************************
 RemoveAll

 *****************************************************************************/

template <class V>
void
JStringMap<V>::RemoveAll()
{
	if (itsCopyKeysFlag && GetElementCount() > 0)
		{
		JStringMapCursor<V> cursor(this);
		while ( cursor.Next() )
			{
			// delete, but don't mark empty to avoid multiple resizing
			delete[] const_cast<JCharacter*>(cursor.GetKey());
			}
		}

	// Mark all at once and possibly resize; we want to call even if the
	// hash table is already empty to zero out the load count
	MarkAllEmpty();
}

/******************************************************************************
 Compare (static private)

	Too bad this can't be a member function, but JHashTable can't know anything
	about it.

 *****************************************************************************/

template <class V>
JBoolean
JStringMap<V>::Compare
	(
	const JStrValue<V>& lhs,
	const JStrValue<V>& rhs
	)
{
	return JConvertToBoolean(strcmp(lhs.key, rhs.key) == 0);
}

/******************************************************************************
 Hash (static private)

	Too bad this can't be a member function, but JHashTable can't know anything
	about it.

 *****************************************************************************/

template <class V>
JHashValue
JStringMap<V>::Hash
	(
	const JStrValue<V>& value
	)
{
	return JHash7Bit(value.key);
}

#endif

// Instantiate the template for the specified type.

#if defined JTemplateType && !defined JOnlyWantTemplateDefinition
	#define JTemplateName JStringMap
	#include <instantiate_template.h>
	#undef JTemplateName
#endif
