/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* * The contents of this file are subject to the Netscape Public * License Version 1.1 (the "License"); you may not use this file * except in compliance with the License. You may obtain a copy of * the License at http://www.mozilla.org/NPL/ * * Software distributed under the License is distributed on an "AS * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or * implied. See the License for the specific language governing * rights and limitations under the License. * * The Original Code is mozilla.org code. * * The Initial Developer of the Original Code is Netscape * Communications Corporation. Portions created by Netscape are * Copyright (C) 1998 Netscape Communications Corporation. All * Rights Reserved. * * Contributor(s): * Scott Collins */ #ifndef _nsAReadableString_h__ #define _nsAReadableString_h__ // WORK IN PROGRESS #include "nscore.h" // for |PRUnichar| #include // for |char_traits| #include // for |bidirectional_iterator_tag| /* This file defines the abstract interfaces |nsAReadableString| and |nsAReadableCString| (the 'A' is for 'abstract', as opposed to the 'I' in [XP]COM interface names). These types are intended to be as source compatible as possible with the original definitions of |const nsString&| and |const nsCString&|, respectively. In otherwords, these interfaces provide only non-mutating access to the underlying strings. We split the these interfaces out from the mutating parts (see "nsAWritableString.h") because tests showed that we could exploit specialized implementations in some areas; we need an abstract interface to bring the whole family of strings together. |nsAReadableString| is a string of |PRUnichar|s. |nsAReadableCString| (note the 'C') is a string of |char|s. */ #define NS_DEF_1_STRING_COMPARISON_OPERATOR(comp, T1, T2) \ template \ inline \ PRBool \ operator comp( T1 lhs, T2 rhs ) \ { \ return PRBool(Compare(lhs, rhs) comp 0); \ } #define NS_DEF_STRING_COMPARISON_OPERATORS(T1, T2) \ NS_DEF_1_STRING_COMPARISON_OPERATOR(!=, T1, T2) \ NS_DEF_1_STRING_COMPARISON_OPERATOR(< , T1, T2) \ NS_DEF_1_STRING_COMPARISON_OPERATOR(<=, T1, T2) \ NS_DEF_1_STRING_COMPARISON_OPERATOR(==, T1, T2) \ NS_DEF_1_STRING_COMPARISON_OPERATOR(>=, T1, T2) \ NS_DEF_1_STRING_COMPARISON_OPERATOR(> , T1, T2) #define NS_DEF_STRING_COMPARISONS(T) \ NS_DEF_STRING_COMPARISON_OPERATORS(const T&, const CharT*) \ NS_DEF_STRING_COMPARISON_OPERATORS(const CharT*, const T&) template class basic_nsAWritableString; // ...because we sometimes use them as `out' params template class basic_nsAReadableString /* ... */ { protected: struct ConstFragment { const CharT* mStart; const CharT* mEnd; const basic_nsAReadableString* mOwningString; void* mFragmentIdentifier; explicit ConstFragment( const basic_nsAReadableString* aOwner = 0 ) : mStart(0), mEnd(0), mOwningString(aOwner), mFragmentIdentifier(0) { // nothing else to do here } }; public: enum FragmentRequest { kPrevFragment, kFirstFragment, kLastFragment, kNextFragment, kFragmentAt }; // Damn! Had to make |GetFragment| public because the compilers suck. Should be protected. virtual const CharT* GetFragment( ConstFragment&, FragmentRequest, PRUint32 = 0 ) const = 0; friend class ConstIterator; class ConstIterator : public std::bidirectional_iterator_tag { friend class basic_nsAReadableString; ConstFragment mFragment; const CharT* mPosition; void normalize_forward() { if ( mPosition == mFragment.mEnd ) if ( mFragment.mOwningString->GetFragment(mFragment, kNextFragment) ) mPosition = mFragment.mStart; } void normalize_backward() { if ( mPosition == mFragment.mStart ) if ( mFragment.mOwningString->GetFragment(mFragment, kPrevFragment) ) mPosition = mFragment.mEnd; } ConstIterator( const ConstFragment& aFragment, const CharT* aStartingPosition ) : mFragment(aFragment), mPosition(aStartingPosition) { // nothing else to do here } public: // ConstIterator( const ConstIterator& ); ...use default copy-constructor // ConstIterator& operator=( const ConstIterator& ); ...use default copy-assignment operator CharT operator*() { return *mPosition; } ConstIterator& operator++() { ++mPosition; normalize_forward(); return *this; } ConstIterator operator++( int ) { ConstIterator result(*this); ++mPosition; normalize_forward(); return result; } ConstIterator& operator--() { normalize_backward(); --mPosition; return *this; } ConstIterator operator--( int ) { ConstIterator result(*this); normalize_backward(); --mPosition; return result; } // Damn again! Problems with templates made me implement comparisons as members. PRBool operator==( const ConstIterator& rhs ) { return mPosition == rhs.mPosition; } PRBool operator!=( const ConstIterator& rhs ) { return mPosition != rhs.mPosition; } }; public: ConstIterator Begin( PRUint32 aOffset = 0 ) const { ConstFragment fragment(this); const CharT* startPos = GetFragment(fragment, kFragmentAt, aOffset); return ConstIterator(fragment, startPos); } ConstIterator End( PRUint32 aOffset = 0 ) const { ConstFragment fragment(this); const CharT* startPos = GetFragment(fragment, kFragmentAt, max(0U, Length()-aOffset)); return ConstIterator(fragment, startPos); } public: virtual ~basic_nsAReadableString() { } virtual PRUint32 Length() const = 0; PRBool IsEmpty() const { return Length()==0; } // PRBool IsOrdered() const; PRBool IsUnicode() const { return PR_FALSE; } // ...but note specialization for |PRUnichar|, below const char* GetBuffer() const { return 0; } const PRUnichar* GetUnicode() const { return 0; } // ...but note specializations for |char| and |PRUnichar|, below // CharT operator[]( PRUint32 ) const; // CharT CharAt( PRUint32 ) const; // CharT First() const; // CharT Last() const; // void ToLowerCase( basic_nsAWritableString& ) const; // void ToUpperCase( basic_nsAWritableString& ) const; // PRUint32 CountChar( char_type ) const; // nsString* ToNewString() const; NO! The right way to say this is // new nsString( fromAReadableString ) // char* ToNewCString() const; // char* ToNewUTF8String() const; // PRUnichar* ToNewUnicode() const; // char* ToCString( char*, PRUint32, PRUint32 ) const; // double ToFLoat( PRInt32* aErrorCode ) const; // long ToInteger( PRInt32* aErrorCode, PRUint32 aRadix ); PRUint32 Left( basic_nsAWritableString&, PRUint32 ) const; PRUint32 Mid( basic_nsAWritableString&, PRUint32, PRUint32 ) const; PRUint32 Right( basic_nsAWritableString&, PRUint32 ) const; // PRUint32 BinarySearch( CharT ) const; // Find( ... ) const; // FindChar( ... ) const; // FindCharInSet( ... ) const; // RFind( ... ) const; // RFindChar( ... ) const; // RFindCharInSet( ... ) const; int Compare( const basic_nsAReadableString& rhs ) const; // int Compare( const CharT*, const CharT* ) const; // Equals // EqualsIgnoreCase // IsASCII // IsSpace // IsAlpha // IsDigit /* Normally you wouldn't declare these as members... ...explanation to come... */ PRBool operator!=( const basic_nsAReadableString& rhs ) const { return Compare(rhs)!=0; } PRBool operator< ( const basic_nsAReadableString& rhs ) const { return Compare(rhs)< 0; } PRBool operator<=( const basic_nsAReadableString& rhs ) const { return Compare(rhs)<=0; } PRBool operator==( const basic_nsAReadableString& rhs ) const { return Compare(rhs)==0; } PRBool operator>=( const basic_nsAReadableString& rhs ) const { return Compare(rhs)>=0; } PRBool operator> ( const basic_nsAReadableString& rhs ) const { return Compare(rhs)> 0; } }; NS_DEF_STRING_COMPARISONS(basic_nsAReadableString) NS_SPECIALIZE_TEMPLATE inline PRBool basic_nsAReadableString::IsUnicode() const { return PR_TRUE; } NS_SPECIALIZE_TEMPLATE inline const PRUnichar* basic_nsAReadableString::GetUnicode() const // DEPRECATED: use the iterators instead { ConstFragment fragment; GetFragment(fragment, kFirstFragment); return fragment.mStart; } NS_SPECIALIZE_TEMPLATE inline const char* basic_nsAReadableString::GetBuffer() const // DEPRECATED: use the iterators instead { ConstFragment fragment; GetFragment(fragment, kFirstFragment); return fragment.mStart; } template class basic_nsLiteralString : public basic_nsAReadableString { typedef typename basic_nsAReadableString::FragmentRequest FragmentRequest; typedef typename basic_nsAWritableString::ConstFragment ConstFragment; protected: virtual const CharT* GetFragment( ConstFragment&, FragmentRequest, PRUint32 ) const; public: // Note: _not_ explicit basic_nsLiteralString( const CharT* aLiteral ) : mStart(aLiteral), mEnd(mStart + char_traits::length(mStart)) { // nothing else to do here } basic_nsLiteralString( const CharT* aLiteral, PRUint32 aLength ) : mStart(aLiteral) mEnd(mStart + aLength) { // nothing else to do here } virtual PRUint32 Length() const; private: const CharT* mStart; const CharT* mEnd; }; NS_DEF_STRING_COMPARISONS(basic_nsLiteralString) template class nsPromiseConcatenation : public basic_nsAReadableString /* ...not unlike RickG's original |nsSubsumeString| in _intent_. */ { typedef typename basic_nsAReadableString::FragmentRequest FragmentRequest; typedef typename basic_nsAWritableString::ConstFragment ConstFragment; protected: virtual const CharT* GetFragment( ConstFragment&, FragmentRequest, PRUint32 ) const; static const int kLeftString = 0; static const int kRightString = 1; public: nsPromiseConcatenation( const basic_nsAReadableString& aLeftString, const basic_nsAReadableString& aRightString ) { mStrings[kLeftString] = &aLeftString; mStrings[kRightString] = &aRightString; } virtual PRUint32 Length() const; private: const basic_nsAReadableString* mStrings[2]; mutable ConstFragment mFragment; }; NS_DEF_STRING_COMPARISONS(nsPromiseConcatenation) template PRUint32 nsPromiseConcatenation::Length() const { return mStrings[kLeftString]->Length() + mStrings[kRightString]->Length(); } template const CharT* nsPromiseConcatenation::GetFragment( ConstFragment& aFragment, FragmentRequest aRequest, PRUint32 aPosition ) const { const int kLeftString = 0; const int kRightString = 1; int whichString; switch ( aRequest ) { case kPrevFragment: case kNextFragment: whichString = reinterpret_cast(aFragment.mFragmentIdentifier); break; case kFirstFragment: aFragment.mFragmentIdentifier = reinterpret_cast(whichString = kLeftString); break; case kLastFragment: aFragment.mFragmentIdentifier = reinterpret_cast(whichString = kRightString); break; case kFragmentAt: PRUint32 leftLength = mStrings[kLeftString]->Length(); if ( aPosition < leftLength ) whichString = kLeftString; else { whichString = kRightString; aPosition -= leftLength; } aFragment.mFragmentIdentifier = reinterpret_cast(whichString); break; } const CharT* result; bool done; do { done = true; result = mStrings[whichString]->GetFragment(mFragment, aRequest, aPosition); if ( !result ) { done = false; if ( aRequest == kNextFragment && whichString == kLeftString ) { aRequest = kFirstFragment; aFragment.mFragmentIdentifier = reinterpret_cast(whichString = kRightString); } else if ( aRequest == kPrevFragment && whichString == kRightString ) { aRequest = kLastFragment; aFragment.mFragmentIdentifier = reinterpret_cast(whichString = kLeftString); } else done = true; } } while ( !done ); aFragment.mStart = mFragment.mStart; aFragment.mEnd = mFragment.mEnd; return result; } template class nsPromiseSubstring : public basic_nsAReadableString { typedef typename basic_nsAReadableString::FragmentRequest FragmentRequest; typedef typename basic_nsAWritableString::ConstFragment ConstFragment; protected: virtual const CharT* GetFragment( ConstFragment&, FragmentRequest, PRUint32 ) const; public: nsPromiseSubstring( const basic_nsAReadableString& aString, PRUint32 aStartPos, PRUint32 aLength ) : mString(aString), mStartPos( min(aStartPos, aString.Length()) ), mLength( min(aLength, aString.Length()-mStartPos) ) { // nothing else to do here } virtual PRUint32 Length() const; private: const basic_nsAReadableString& mString; PRUint32 mStartPos; PRUint32 mLength; }; NS_DEF_STRING_COMPARISONS(nsPromiseSubstring) template PRUint32 nsPromiseSubstring::Length() const { return mLength; } template const CharT* nsPromiseSubstring::GetFragment( ConstFragment& aFragment, FragmentRequest aRequest, PRUint32 aPosition ) const { if ( aRequest == kFirstFragment ) { aPosition = mStartPos; aRequest = kFragmentAt; } else if ( aRequest == kLastFragment ) { aPosition = mLength + mStartPos; aRequest = kFragmentAt; } else if ( aRequest == kFragmentAt ) aPosition += mStartPos; return mString.GetFragment(aFragment, aRequest, aPosition); } template nsPromiseSubstring Substring( const basic_nsAReadableString& aString, PRUint32 aStartPos, PRUint32 aSubstringLength ) { return nsPromiseSubstring(aString, aStartPos, aSubstringLength); } template const CharT* basic_nsLiteralString::GetFragment( ConstFragment& aFragment, FragmentRequest aRequest, PRUint32 aOffset ) const { switch ( aRequest ) { case kFirstFragment: case kLastFragment: case kFragmentAt: aFragment.mStart = mStart; aFragment.mEnd = mEnd; return mStart + aOffset; case kPrevFragment: case kNextFragment: default: return 0; } } template PRUint32 basic_nsLiteralString::Length() const { return PRUint32(mEnd - mStart); } template PRUint32 basic_nsAReadableString::Left( basic_nsAWritableString& aResult, PRUint32 aLengthToCopy ) const { aResult = Substring(*this, 0, aLengthToCopy); return aResult.Length(); } template PRUint32 basic_nsAReadableString::Mid( basic_nsAWritableString& aResult, PRUint32 aStartPos, PRUint32 aLengthToCopy ) const { aResult = Substring(*this, aStartPos, aLengthToCopy); return aResult.Length(); } template PRUint32 basic_nsAReadableString::Right( basic_nsAWritableString& aResult, PRUint32 aLengthToCopy ) const { PRUint32 myLength = Length(); aLengthToCopy = min(myLength, aLengthToCopy); aResult = Substring(*this, myLength-aLengthToCopy, aLengthToCopy); return aResult.Length(); } template int Compare( const basic_nsAReadableString& lhs, const basic_nsAReadableString& rhs ) { /* If this turns out to be too slow (after measurement), there are two important modifications 1) chunky iterators 2) use char_traits::compare */ PRUint32 lLength = lhs.Length(); PRUint32 rLength = rhs.Length(); int result = 0; if ( lLength < rLength ) result = -1; else if ( lLength > rLength ) result = 1; PRUint32 lengthToCompare = min(lLength, rLength); typedef typename basic_nsAReadableString::ConstIterator ConstIterator; ConstIterator lPos = lhs.Begin(); ConstIterator lEnd = lhs.Begin(lengthToCompare); ConstIterator rPos = rhs.Begin(); while ( lPos != lEnd ) { if ( *lPos < *rPos ) return -1; if ( *rPos < *lPos ) return 1; ++lPos; ++rPos; } return result; } template int Compare( const basic_nsAReadableString& lhs, const CharT* rhs ) { return Compare(lhs, basic_nsLiteralString(rhs)); } template int Compare( const CharT* lhs, const basic_nsAReadableString& rhs ) { return Compare(basic_nsLiteralString(lhs), rhs); } template inline int basic_nsAReadableString::Compare( const basic_nsAReadableString& rhs ) const { return ::Compare(*this, rhs); } /* How shall we provide |operator+()|? What would it return? It has to return a stack based object, because the client will not be given an opportunity to handle memory management in an expression like myWritableString = stringA + stringB + stringC; ...so the `obvious' answer of returning a new |nsSharedString| is no good. We could return an |nsString|, if that name were in scope here, though there's no telling what the client will really want to do with the result. What might be better, though, is to return a `promise' to concatenate some strings... By making |nsPromiseConcatenation| inherit from readable strings, we automatically handle assignment and other interesting uses within writable strings, plus we drastically reduce the number of cases we have to write |operator+()| for. The cost is extra temporary concat strings in the evaluation of strings of '+'s, e.g., |A + B + C + D|, and that we have to do some work to implement the virtual functions of readables. */ template nsPromiseConcatenation operator+( const basic_nsAReadableString& lhs, const basic_nsAReadableString& rhs ) { return nsPromiseConcatenation(lhs, rhs); } template nsPromiseConcatenation operator+( const basic_nsAReadableString& lhs, const basic_nsLiteralString& rhs ) { return nsPromiseConcatenation(lhs, rhs); } template nsPromiseConcatenation operator+( const basic_nsLiteralString& lhs, const basic_nsAReadableString& rhs ) { return nsPromiseConcatenation(lhs, rhs); } template nsPromiseConcatenation operator+( const basic_nsLiteralString& lhs, const basic_nsLiteralString& rhs ) { return nsPromiseConcatenation(lhs, rhs); } template basic_ostream& operator<<( basic_ostream& os, const basic_nsAReadableString& s ) { std::copy(s.Begin(), s.End(), ostream_iterator(os)); return os; } typedef basic_nsAReadableString nsAReadableString; typedef basic_nsAReadableString nsAReadableCString; typedef basic_nsLiteralString nsLiteralString; typedef basic_nsLiteralString nsLiteralCString; #endif // !defined(_nsAReadableString_h__)