/******************************************************************************
 JString.h

	Interface for the JString class

	Copyright © 1994-98 by John Lindal. All rights reserved.

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

#ifndef _H_JString
#define _H_JString

#include <JPtrArray.h>
#include <JIndexRange.h>
#include <string.h>

class JString
{
	friend istream& operator>>(istream&, JString&);
	friend ostream& operator<<(ostream&, const JString&);

public:

	enum
	{
		kPrecisionAsNeeded = -1
	};

	enum ExponentDisplay
	{
		kStandardExponent = -10000,
		kForceExponent    = -9999,
		kForceNoExponent  = 0,
		kUseGivenExponent = 1
	};

public:

	JString();
	JString(const JString& source);
	JString(const JCharacter* str);
	JString(const JCharacter* str, const JSize length);
	JString(const JCharacter* str, const JIndexRange& range);
	JString(const JFloat number, const int precision = kPrecisionAsNeeded,
			const ExponentDisplay expDisplay = kStandardExponent,
			const int exponent = 0);

	~JString();

	const JString& operator=(const JString& str);
	const JString& operator=(const JCharacter* str);

	JString& operator+=(const JString& str);
	JString& operator+=(const JCharacter* str);

	operator const JCharacter*() const;

	void	Set(const JString& str);
	void	Set(const JCharacter* str);
	void	Set(const JCharacter* str, const JSize length);

	JBoolean			ContainsNULL() const;
	const JCharacter*	GetCString() const;
	JCharacter*			AllocateCString() const;	// client must call delete [] when finished with it

	void	InsertSubstring(const JString& str, const JIndex insertionIndex);
	void	InsertSubstring(const JCharacter* str, const JIndex insertionIndex);
	void	InsertSubstring(const JCharacter* str, const JSize length,
							const JIndex insertionIndex);
	void	InsertCharacter(const JCharacter c, const JIndex insertionIndex);

	void	Prepend(const JString& str);
	void	Prepend(const JCharacter* str);
	void	Prepend(const JCharacter* str, const JSize length);
	void	PrependCharacter(const JCharacter c);

	void	Append(const JString& str);
	void	Append(const JCharacter* str);
	void	Append(const JCharacter* str, const JSize length);
	void	AppendCharacter(const JCharacter c);

	JBoolean	IsEmpty() const;
	JSize		GetLength() const;
	JBoolean	IndexValid(const JIndex index) const;

	JCharacter	GetCharacter(const JIndex index) const;
	JCharacter	GetFirstCharacter() const;
	JCharacter	GetLastCharacter() const;
	void		SetCharacter(const JIndex index, const JCharacter c);

	void	Clear();
	void	TrimWhitespace();
	void	ToLower();
	void	ToUpper();

	JBoolean	Contains(const JString& str, const JBoolean caseSensitive = kTrue) const;
	JBoolean	Contains(const JCharacter* str, const JBoolean caseSensitive = kTrue) const;
	JBoolean	Contains(const JCharacter* str, const JSize length,
						 const JBoolean caseSensitive = kTrue) const;

	JBoolean	LocateSubstring(const JString& str, JIndex* startIndex) const;
	JBoolean	LocateSubstring(const JCharacter* str, JIndex* startIndex) const;
	JBoolean	LocateSubstring(const JCharacter* str, const JSize length,
								JIndex* startIndex) const;
	JBoolean	LocateSubstring(const JString& str, const JBoolean caseSensitive,
								JIndex* startIndex) const;
	JBoolean	LocateSubstring(const JCharacter* str, const JBoolean caseSensitive,
								JIndex* startIndex) const;
	JBoolean	LocateSubstring(const JCharacter* str, const JSize length,
								const JBoolean caseSensitive, JIndex* startIndex) const;

	JBoolean	LocateNextSubstring(const JString& str, JIndex* startIndex) const;
	JBoolean	LocateNextSubstring(const JCharacter* str, JIndex* startIndex) const;
	JBoolean	LocateNextSubstring(const JCharacter* str, const JSize length,
									JIndex* startIndex) const;
	JBoolean	LocateNextSubstring(const JString& str, const JBoolean caseSensitive,
									JIndex* startIndex) const;
	JBoolean	LocateNextSubstring(const JCharacter* str, const JBoolean caseSensitive,
									JIndex* startIndex) const;
	JBoolean	LocateNextSubstring(const JCharacter* str, const JSize length,
									const JBoolean caseSensitive, JIndex* startIndex) const;

	JBoolean	LocatePrevSubstring(const JString& str, JIndex* startIndex) const;
	JBoolean	LocatePrevSubstring(const JCharacter* str, JIndex* startIndex) const;
	JBoolean	LocatePrevSubstring(const JCharacter* str, const JSize length,
									JIndex* startIndex) const;
	JBoolean	LocatePrevSubstring(const JString& str, const JBoolean caseSensitive,
									JIndex* startIndex) const;
	JBoolean	LocatePrevSubstring(const JCharacter* str, const JBoolean caseSensitive,
									JIndex* startIndex) const;
	JBoolean	LocatePrevSubstring(const JCharacter* str, const JSize length,
									const JBoolean caseSensitive, JIndex* startIndex) const;

	JBoolean	LocateLastSubstring(const JString& str, JIndex* startIndex) const;
	JBoolean	LocateLastSubstring(const JCharacter* str, JIndex* startIndex) const;
	JBoolean	LocateLastSubstring(const JCharacter* str, const JSize length,
									JIndex* startIndex) const;
	JBoolean	LocateLastSubstring(const JString& str, const JBoolean caseSensitive,
									JIndex* startIndex) const;
	JBoolean	LocateLastSubstring(const JCharacter* str, const JBoolean caseSensitive,
									JIndex* startIndex) const;
	JBoolean	LocateLastSubstring(const JCharacter* str, const JSize length,
									const JBoolean caseSensitive, JIndex* startIndex) const;

	JBoolean	BeginsWith(const JString& str, const JBoolean caseSensitive = kTrue) const;
	JBoolean	BeginsWith(const JCharacter* str, const JBoolean caseSensitive = kTrue) const;
	JBoolean	BeginsWith(const JCharacter* str, const JSize length,
						   const JBoolean caseSensitive = kTrue) const;

	JBoolean	EndsWith(const JString& str, const JBoolean caseSensitive = kTrue) const;
	JBoolean	EndsWith(const JCharacter* str, const JBoolean caseSensitive = kTrue) const;
	JBoolean	EndsWith(const JCharacter* str, const JSize length,
						 const JBoolean caseSensitive = kTrue) const;

	JString		GetSubstring(const JIndex firstCharIndex, const JIndex lastCharIndex) const;
	JString		GetSubstring(const JIndexRange& range) const;	// allows empty range

	void		Extract(const JArray<JIndexRange>& rangeList,
						JPtrArray<JString>* substringList) const;

	void		ReplaceSubstring(const JIndex firstCharIndex,
								 const JIndex lastCharIndex,
								 const JString& str);
	void		ReplaceSubstring(const JIndex firstCharIndex,
								 const JIndex lastCharIndex,
								 const JCharacter* str);
	void		ReplaceSubstring(const JIndex firstCharIndex,
								 const JIndex lastCharIndex,
								 const JCharacter* str, const JSize length);
	void		ReplaceSubstring(const JIndexRange& range,
								 const JString& str, JIndexRange* newRange);
	void		ReplaceSubstring(const JIndexRange& range,
								 const JCharacter* str, JIndexRange* newRange);
	void		ReplaceSubstring(const JIndexRange& range,
								 const JCharacter* str, const JSize length,
								 JIndexRange* newRange);

	void		RemoveSubstring(const JIndex firstCharIndex, const JIndex lastCharIndex);
	void		RemoveSubstring(const JIndexRange& range);

	JBoolean	MatchCase(const JString& source, const JIndexRange& range);
	JBoolean	MatchCase(const JCharacter* source, const JIndexRange& range);

	JBoolean	IsFloat() const;
	JBoolean	ConvertToFloat(JFloat* value) const;

	JBoolean	IsInteger(const JSize base = 10) const;
	JBoolean	ConvertToInteger(JInteger* value, const JSize base = 10) const;

	JBoolean	IsUInt(const JSize base = 10) const;
	JBoolean	IsHex() const;
	JBoolean	ConvertToUInt(JUInt* value, const JSize base = 10) const;

	void		Read(istream& input, const JSize count);
	void		Print(ostream& output) const;

	JSize		GetBlockSize() const;
	void		SetBlockSize(const JSize blockSize);

private:

	JCharacter* itsString;			// characters
	JSize		itsStringLength;	// number of characters used
	JSize		itsAllocLength;		// number of characters we have space for
	JSize		itsBlockSize;		// size by which to shrink and grow allocation

private:

	void		CopyToPrivateString(const JCharacter* str);
	void		CopyToPrivateString(const JCharacter* str, const JSize length);

	JCharacter	PrivateGetCharacter(const JIndex index) const;
	JCharacter*	GetCharacterPtr(const JIndex index) const;

	JBoolean	CompleteConversion(const JCharacter* startPtr, const JSize length,
								   const JCharacter* convEndPtr) const;
};


// declarations of global functions for dealing with strings

int JStringCompare(const JCharacter* s1, const JCharacter* s2,
				   const JBoolean caseSensitive = kTrue);
int JStringCompare(const JCharacter* s1, const JSize length1,
				   const JCharacter* s2, const JSize length2,
				   const JBoolean caseSensitive = kTrue);

JBoolean	JCompareMaxN(const JCharacter* s1, const JCharacter* s2, const JSize N,
						 const JBoolean caseSensitive = kTrue);
JBoolean	JCompareMaxN(const JCharacter* s1, const JSize length1,
						 const JCharacter* s2, const JSize length2,
						 const JSize N, const JBoolean caseSensitive = kTrue);

JSize		JCalcMatchLength(const JCharacter* s1, const JCharacter* s2,
							 const JBoolean caseSensitive = kTrue);

JBoolean	JCopyMaxN(const JCharacter* source, const JIndex maxBytes,
					  JCharacter* destination);


/******************************************************************************
 Cast to JCharacter*

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

inline
JString::operator const JCharacter*()
	const
{
	return itsString;
}

/******************************************************************************
 ContainsNULL

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

inline JBoolean
JString::ContainsNULL()
	const
{
	return JConvertToBoolean( itsStringLength != strlen(itsString) );
}

/******************************************************************************
 GetCString

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

inline const JCharacter*
JString::GetCString()
	const
{
	return itsString;
}

/******************************************************************************
 IsEmpty

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

inline JBoolean
JString::IsEmpty()
	const
{
	return JConvertToBoolean( itsStringLength == 0 );
}

/******************************************************************************
 JStringEmpty

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

inline JBoolean
JStringEmpty
	(
	const JCharacter* s
	)
{
	return JConvertToBoolean( s == NULL || s[0] == '\0' );
}

/******************************************************************************
 GetLength

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

inline JSize
JString::GetLength()
	const
{
	return itsStringLength;
}

/******************************************************************************
 Is number

	Returns kTrue if we can convert ourselves to a number.

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

inline JBoolean
JString::IsFloat()
	const
{
	JFloat value;
	return ConvertToFloat(&value);
}

inline JBoolean
JString::IsInteger
	(
	const JSize base
	)
	const
{
	JInteger value;
	return ConvertToInteger(&value, base);
}

inline JBoolean
JString::IsUInt
	(
	const JSize base
	)
	const
{
	JUInt value;
	return ConvertToUInt(&value, base);
}

/******************************************************************************
 GetFirstCharacter

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

inline JCharacter
JString::GetFirstCharacter()
	const
{
	return GetCharacter(1);
}

/******************************************************************************
 GetLastCharacter

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

inline JCharacter
JString::GetLastCharacter()
	const
{
	return GetCharacter(itsStringLength);
}

/******************************************************************************
 LocateSubstring

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

inline JBoolean
JString::LocateSubstring
	(
	const JString&	str,
	JIndex*			startIndex
	)
	const
{
	return LocateSubstring(str.itsString, str.itsStringLength, kTrue, startIndex);
}

inline JBoolean
JString::LocateSubstring
	(
	const JCharacter*	str,
	JIndex*				startIndex
	)
	const
{
	return LocateSubstring(str, strlen(str), kTrue, startIndex);
}

inline JBoolean
JString::LocateSubstring
	(
	const JCharacter*	str,
	const JSize			length,
	JIndex*				startIndex
	)
	const
{
	return LocateSubstring(str, length, kTrue, startIndex);
}

inline JBoolean
JString::LocateSubstring
	(
	const JString&	str,
	const JBoolean	caseSensitive,
	JIndex*			startIndex
	)
	const
{
	return LocateSubstring(str.itsString, str.itsStringLength, caseSensitive, startIndex);
}

inline JBoolean
JString::LocateSubstring
	(
	const JCharacter*	str,
	const JBoolean		caseSensitive,
	JIndex*				startIndex
	)
	const
{
	return LocateSubstring(str, strlen(str), caseSensitive, startIndex);
}

/******************************************************************************
 LocateNextSubstring

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

inline JBoolean
JString::LocateNextSubstring
	(
	const JString&	str,
	JIndex*			startIndex
	)
	const
{
	return LocateNextSubstring(str.itsString, str.itsStringLength, kTrue, startIndex);
}

inline JBoolean
JString::LocateNextSubstring
	(
	const JCharacter*	str,
	JIndex*				startIndex
	)
	const
{
	return LocateNextSubstring(str, strlen(str), kTrue, startIndex);
}

inline JBoolean
JString::LocateNextSubstring
	(
	const JCharacter*	str,
	const JSize			length,
	JIndex*				startIndex
	)
	const
{
	return LocateNextSubstring(str, length, kTrue, startIndex);
}

inline JBoolean
JString::LocateNextSubstring
	(
	const JString&	str,
	const JBoolean	caseSensitive,
	JIndex*			startIndex
	)
	const
{
	return LocateNextSubstring(str.itsString, str.itsStringLength, caseSensitive, startIndex);
}

inline JBoolean
JString::LocateNextSubstring
	(
	const JCharacter*	str,
	const JBoolean		caseSensitive,
	JIndex*				startIndex
	)
	const
{
	return LocateNextSubstring(str, strlen(str), caseSensitive, startIndex);
}

/******************************************************************************
 LocatePrevSubstring

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

inline JBoolean
JString::LocatePrevSubstring
	(
	const JString&	str,
	JIndex*			startIndex
	)
	const
{
	return LocatePrevSubstring(str.itsString, str.itsStringLength, kTrue, startIndex);
}

inline JBoolean
JString::LocatePrevSubstring
	(
	const JCharacter*	str,
	JIndex*				startIndex
	)
	const
{
	return LocatePrevSubstring(str, strlen(str), kTrue, startIndex);
}

inline JBoolean
JString::LocatePrevSubstring
	(
	const JCharacter*	str,
	const JSize			length,
	JIndex*				startIndex
	)
	const
{
	return LocatePrevSubstring(str, length, kTrue, startIndex);
}

inline JBoolean
JString::LocatePrevSubstring
	(
	const JString&	str,
	const JBoolean	caseSensitive,
	JIndex*			startIndex
	)
	const
{
	return LocatePrevSubstring(str.itsString, str.itsStringLength, caseSensitive, startIndex);
}

inline JBoolean
JString::LocatePrevSubstring
	(
	const JCharacter*	str,
	const JBoolean		caseSensitive,
	JIndex*				startIndex
	)
	const
{
	return LocatePrevSubstring(str, strlen(str), caseSensitive, startIndex);
}

/******************************************************************************
 LocateLastSubstring

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

inline JBoolean
JString::LocateLastSubstring
	(
	const JString&	str,
	JIndex*			startIndex
	)
	const
{
	return LocateLastSubstring(str.itsString, str.itsStringLength, kTrue, startIndex);
}

inline JBoolean
JString::LocateLastSubstring
	(
	const JCharacter*	str,
	JIndex*				startIndex
	)
	const
{
	return LocateLastSubstring(str, strlen(str), kTrue, startIndex);
}

inline JBoolean
JString::LocateLastSubstring
	(
	const JCharacter*	str,
	const JSize			length,
	JIndex*				startIndex
	)
	const
{
	return LocateLastSubstring(str, length, kTrue, startIndex);
}

inline JBoolean
JString::LocateLastSubstring
	(
	const JString&	str,
	const JBoolean	caseSensitive,
	JIndex*			startIndex
	)
	const
{
	return LocateLastSubstring(str.itsString, str.itsStringLength, caseSensitive, startIndex);
}

inline JBoolean
JString::LocateLastSubstring
	(
	const JCharacter*	str,
	const JBoolean		caseSensitive,
	JIndex*				startIndex
	)
	const
{
	return LocateLastSubstring(str, strlen(str), caseSensitive, startIndex);
}

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

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

inline JBoolean
JString::Contains
	(
	const JString&	str,
	const JBoolean	caseSensitive
	)
	const
{
	JIndex i;
	return LocateSubstring(str, caseSensitive, &i);
}

inline JBoolean
JString::Contains
	(
	const JCharacter*	str,
	const JBoolean		caseSensitive
	)
	const
{
	JIndex i;
	return LocateSubstring(str, caseSensitive, &i);
}

inline JBoolean
JString::Contains
	(
	const JCharacter*	str,
	const JSize			length,
	const JBoolean		caseSensitive
	)
	const
{
	JIndex i;
	return LocateSubstring(str, length, caseSensitive, &i);
}

/******************************************************************************
 BeginsWith

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

inline JBoolean
JString::BeginsWith
	(
	const JString&	str,
	const JBoolean	caseSensitive
	)
	const
{
	return BeginsWith(str, str.itsStringLength, caseSensitive);
}

/******************************************************************************
 EndsWith

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

inline JBoolean
JString::EndsWith
	(
	const JString&	str,
	const JBoolean	caseSensitive
	)
	const
{
	return EndsWith(str, str.itsStringLength, caseSensitive);
}

inline JBoolean
JString::EndsWith
	(
	const JCharacter*	str,
	const JBoolean		caseSensitive
	)
	const
{
	return EndsWith(str, strlen(str), caseSensitive);
}

/******************************************************************************
 Concatenation

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

inline JString&
JString::operator+=
	(
	const JString& str
	)
{
	InsertSubstring(str.itsString, str.itsStringLength, itsStringLength+1);
	return *this;
}

inline JString&
JString::operator+=
	(
	const JCharacter* str
	)
{
	InsertSubstring(str, strlen(str), itsStringLength+1);
	return *this;
}

/******************************************************************************
 InsertSubstring

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

inline void
JString::InsertSubstring
	(
	const JString&	str,
	const JIndex	insertionIndex
	)
{
	InsertSubstring(str.itsString, str.itsStringLength, insertionIndex);
}

inline void
JString::InsertSubstring
	(
	const JCharacter*	str,
	const JIndex		insertionIndex
	)
{
	InsertSubstring(str, strlen(str), insertionIndex);
}

inline void
JString::InsertCharacter
	(
	const JCharacter	c,
	const JIndex		insertionIndex
	)
{
	JCharacter str[] = { c, '\0' };
	InsertSubstring(str, 1, insertionIndex);
}

/******************************************************************************
 Prepend

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

inline void
JString::Prepend
	(
	const JString& str
	)
{
	InsertSubstring(str.itsString, str.itsStringLength, 1);
}

inline void
JString::Prepend
	(
	const JCharacter* str
	)
{
	InsertSubstring(str, strlen(str), 1);
}

inline void
JString::Prepend
	(
	const JCharacter*	str,
	const JSize			length
	)
{
	InsertSubstring(str, length, 1);
}

inline void
JString::PrependCharacter
	(
	const JCharacter c
	)
{
	JCharacter str[] = { c, '\0' };
	InsertSubstring(str, 1, 1);
}

/******************************************************************************
 Append

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

inline void
JString::Append
	(
	const JString& str
	)
{
	InsertSubstring(str.itsString, str.itsStringLength, itsStringLength+1);
}

inline void
JString::Append
	(
	const JCharacter* str
	)
{
	InsertSubstring(str, strlen(str), itsStringLength+1);
}

inline void
JString::Append
	(
	const JCharacter*	str,
	const JSize			length
	)
{
	InsertSubstring(str, length, itsStringLength+1);
}

inline void
JString::AppendCharacter
	(
	const JCharacter c
	)
{
	JCharacter str[] = { c, '\0' };
	InsertSubstring(str, 1, itsStringLength+1);
}

/******************************************************************************
 ReplaceSubstring

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

inline void
JString::ReplaceSubstring
	(
	const JIndex	firstCharIndex,
	const JIndex	lastCharIndex,
	const JString&	str
	)
{
	ReplaceSubstring(firstCharIndex, lastCharIndex, str.itsString, str.itsStringLength);
}

inline void
JString::ReplaceSubstring
	(
	const JIndex		firstCharIndex,
	const JIndex		lastCharIndex,
	const JCharacter*	str
	)
{
	ReplaceSubstring(firstCharIndex, lastCharIndex, str, strlen(str));
}

inline void
JString::ReplaceSubstring
	(
	const JIndexRange&	range,
	const JString&		str,
	JIndexRange*		newRange
	)
{
	ReplaceSubstring(range.first, range.last, str.itsString, str.itsStringLength);
	if (newRange != NULL)
		{
		newRange->SetFirstAndLength(range.first, str.itsStringLength);
		}
}

inline void
JString::ReplaceSubstring
	(
	const JIndexRange&	range,
	const JCharacter*	str,
	JIndexRange*		newRange
	)
{
	const JSize length = strlen(str);
	ReplaceSubstring(range.first, range.last, str, length);
	if (newRange != NULL)
		{
		newRange->SetFirstAndLength(range.first, length);
		}
}

inline void
JString::ReplaceSubstring
	(
	const JIndexRange&	range,
	const JCharacter*	str,
	const JSize			length,
	JIndexRange*		newRange
	)
{
	ReplaceSubstring(range.first, range.last, str, length);
	if (newRange != NULL)
		{
		newRange->SetFirstAndLength(range.first, length);
		}
}

/******************************************************************************
 RemoveSubstring

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

inline void
JString::RemoveSubstring
	(
	const JIndex firstCharIndex,
	const JIndex lastCharIndex
	)
{
	ReplaceSubstring(firstCharIndex, lastCharIndex, "");
}

inline void
JString::RemoveSubstring
	(
	const JIndexRange& range
	)
{
	ReplaceSubstring(range.first, range.last, "");
}

/******************************************************************************
 MatchCase

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

inline JBoolean
JString::MatchCase
	(
	const JString&		source,
	const JIndexRange&	range
	)
{
	return MatchCase(source.itsString, range);
}

/******************************************************************************
 IndexValid

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

inline JBoolean
JString::IndexValid
	(
	const JIndex index
	)
	const
{
	return JConvertToBoolean( 1 <= index && index <= itsStringLength );
}

/******************************************************************************
 Block size

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

inline JSize
JString::GetBlockSize()
	const
{
	return itsBlockSize;
}

inline void
JString::SetBlockSize
	(
	const JSize blockSize
	)
{
	itsBlockSize = blockSize;
}

/******************************************************************************
 Addition

	We provide five types so no constructors are called.

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

inline JString
operator+
	(
	const JString& s1,
	const JString& s2
	)
{
	JString sum = s1;
	sum += s2;
	return sum;
}

inline JString
operator+
	(
	const JString&		s,
	const JCharacter*	str
	)
{
	JString sum = s;
	sum += str;
	return sum;
}

inline JString
operator+
	(
	const JCharacter*	str,
	const JString&		s
	)
{
	JString sum = str;
	sum += s;
	return sum;
}

/******************************************************************************
 Equality (case-sensitive)

	We provide three types so no constructors are called.
	We don't need access to the private data because we have a conversion operator.

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

// operator==

inline int
operator==
	(
	const JString& s1,
	const JString& s2
	)
{
	return (JStringCompare(s1, s1.GetLength(), s2, s2.GetLength(), kTrue) == 0);
}

inline int
operator==
	(
	const JString&		s,
	const JCharacter*	str
	)
{
	return (JStringCompare(s, s.GetLength(), str, strlen(str), kTrue) == 0);
}

inline int
operator==
	(
	const JCharacter*	str,
	const JString&		s
	)
{
	return (JStringCompare(s, s.GetLength(), str, strlen(str), kTrue) == 0);
}

// operator!=

inline int
operator!=
	(
	const JString& s1,
	const JString& s2
	)
{
	return (JStringCompare(s1, s1.GetLength(), s2, s2.GetLength(), kTrue) != 0);
}

inline int
operator!=
	(
	const JString&		s,
	const JCharacter*	str
	)
{
	return (JStringCompare(s, s.GetLength(), str, strlen(str), kTrue) != 0);
}

inline int
operator!=
	(
	const JCharacter*	str,
	const JString&		s
	)
{
	return (JStringCompare(s, s.GetLength(), str, strlen(str), kTrue) != 0);
}

/******************************************************************************
 Comparison (case-insensitive)

	We provide three types so no constructors are called.

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

// operator<

inline int
operator<
	(
	const JString& s1,
	const JString& s2
	)
{
	return (JStringCompare(s1, s1.GetLength(), s2, s2.GetLength(), kFalse) < 0);
}

inline int
operator<
	(
	const JString&		s,
	const JCharacter*	str
	)
{
	return (JStringCompare(s, s.GetLength(), str, strlen(str), kFalse) < 0);
}

inline int
operator<
	(
	const JCharacter*	str,
	const JString&		s
	)
{
	return (JStringCompare(s, s.GetLength(), str, strlen(str), kFalse) < 0);
}

// operator<=

inline int
operator<=
	(
	const JString& s1,
	const JString& s2
	)
{
	return (JStringCompare(s1, s1.GetLength(), s2, s2.GetLength(), kFalse) <= 0);
}

inline int
operator<=
	(
	const JString&		s,
	const JCharacter*	str
	)
{
	return (JStringCompare(s, s.GetLength(), str, strlen(str), kFalse) <= 0);
}

inline int
operator<=
	(
	const JCharacter*	str,
	const JString&		s
	)
{
	return (JStringCompare(s, s.GetLength(), str, strlen(str), kFalse) <= 0);
}

// operator>

inline int
operator>
	(
	const JString& s1,
	const JString& s2
	)
{
	return (JStringCompare(s1, s1.GetLength(), s2, s2.GetLength(), kFalse) > 0);
}

inline int
operator>
	(
	const JString&		s,
	const JCharacter*	str
	)
{
	return (JStringCompare(s, s.GetLength(), str, strlen(str), kFalse) > 0);
}

inline int
operator>
	(
	const JCharacter*	str,
	const JString&		s
	)
{
	return (JStringCompare(s, s.GetLength(), str, strlen(str), kFalse) > 0);
}

// operator>=

inline int
operator>=
	(
	const JString& s1,
	const JString& s2
	)
{
	return (JStringCompare(s1, s1.GetLength(), s2, s2.GetLength(), kFalse) >= 0);
}

inline int
operator>=
	(
	const JString&		s,
	const JCharacter*	str
	)
{
	return (JStringCompare(s, s.GetLength(), str, strlen(str), kFalse) >= 0);
}

inline int
operator>=
	(
	const JCharacter*	str,
	const JString&		s
	)
{
	return (JStringCompare(s, s.GetLength(), str, strlen(str), kFalse) >= 0);
}

/******************************************************************************
 JStringCompare

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

inline int
JStringCompare
	(
	const JString&	s1,
	const JString&	s2,
	const JBoolean	caseSensitive
	)
{
	return JStringCompare(s1, s1.GetLength(), s2, s2.GetLength(), caseSensitive);
}

inline int
JStringCompare
	(
	const JString&		s1,
	const JCharacter*	s2,
	const JBoolean		caseSensitive
	)
{
	return JStringCompare(s1, s1.GetLength(), s2, strlen(s2), caseSensitive);
}

inline int
JStringCompare
	(
	const JCharacter*	s1,
	const JString&		s2,
	const JBoolean		caseSensitive
	)
{
	return JStringCompare(s1, strlen(s1), s2, s2.GetLength(), caseSensitive);
}

/******************************************************************************
 CopyToPrivateString (private)

	Copy the given string into our private string.

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

inline void
JString::CopyToPrivateString
	(
	const JCharacter* str
	)
{
	CopyToPrivateString(str, strlen(str));
}

/******************************************************************************
 PrivateGetCharacter (private)

	*** This routine does no bounds checking!

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

inline JCharacter
JString::PrivateGetCharacter
	(
	const JIndex index
	)
	const
{
	return itsString [ index - 1 ];
}

/******************************************************************************
 GetCharacterPtr (private)

	*** This routine does no bounds checking!

	Return a pointer to the character at the specified index.

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

inline JCharacter*
JString::GetCharacterPtr
	(
	const JIndex index
	)
	const
{
	return itsString + index - 1;
}

/******************************************************************************
 Set

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

inline void
JString::Set
	(
	const JString& str
	)
{
	if (this->itsString != str.itsString)
		{
		CopyToPrivateString(str.itsString, str.itsStringLength);
		}
}

inline void
JString::Set
	(
	const JCharacter* str
	)
{
	if (this->itsString != str)
		{
		CopyToPrivateString(str);
		}
}

inline void
JString::Set
	(
	const JCharacter*	str,
	const JSize			length
	)
{
	if (this->itsString != str)
		{
		CopyToPrivateString(str, length);
		}
}

/******************************************************************************
 Assignment operator

	We do not copy itsBlockSize because we assume the client has set them
	appropriately.

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

inline const JString&
JString::operator=
	(
	const JString& source
	)
{
	Set(source);
	return *this;
}

inline const JString&
JString::operator=
	(
	const JCharacter* str
	)
{
	Set(str);
	return *this;
}

#endif
