// Copyright (c) 2000-2009 Nokia Corporation and/or its subsidiary(-ies).// All rights reserved.// This component and the accompanying materials are made available// under the terms of "Eclipse Public License v1.0"// which accompanies this distribution, and is available// at the URL "http://www.eclipse.org/legal/epl-v10.html".//// Initial Contributors:// Nokia Corporation - initial contribution.//// Contributors://// Description:// CFreagmentedString.cpp// //// header#include <cfragmentedstring.h>// constantsconst TInt KFragmentedStringGranularity=128;/** Constructor */EXPORT_C CFragmentedString::CFragmentedString(): CArrayPtrFlat<HBufC>(KFragmentedStringGranularity) { }/** Destructor.This resets and destroys the HBufC array.*/EXPORT_C CFragmentedString::~CFragmentedString() { ResetAndDestroy(); }/** Copies a sub-string and then appends the copy to the string.@param aString Sub-string to append*/EXPORT_C void CFragmentedString::AddStringL(const TDesC& aString)// Adds the string to our internal array of strings { HBufC* string=aString.AllocL(); AddStringL(string); }/** Appends a sub-string to the string.@param aString Sub-string to append*/EXPORT_C void CFragmentedString::AddStringL(HBufC* aString)// Adds the string to our internal array of strings { // take ownership of aString CleanupStack::PushL(aString); AppendL(aString); CleanupStack::Pop(); // aString }/** Gets the length of the string.@return String length*/EXPORT_C TInt CFragmentedString::Length() const// Calculates the entire length of our 'complete' string. { if (Count()==0) CONST_CAST(CFragmentedString*, this)->iCurrentCharacter=0; TInt len = -iCurrentCharacter; for (TInt index = iCurrentIndex; index < Count(); index++) len+=At(index)->Length(); return len; }/** Gets a string containing all sub-strings after the current position in a newly-allocated buffer.It is the caller's responsibility to clean up the returned string.@return String containing all sub-strings after the current position in a newly-allocated buffer*/EXPORT_C HBufC* CFragmentedString::StringL() const// Returns a newly allocated non-fragmented version of our string. { if (Count()>0) return StringL(iCurrentIndex, iCurrentCharacter, Count(), At(Count()-1)->Length()); else return HBufC::NewL(1); }/** Gets the entire string in a newly-allocated buffer.It is the caller's responsibility to clean up the returned string.@return Entire string in a newly-allocated buffer*/EXPORT_C HBufC* CFragmentedString::ContentL() const// Returns a newly allocated non-fragmented version of our string. { if (Count()>0) return StringL(0, 0, Count(), At(Count()-1)->Length()); else return HBufC::NewL(1); }void CFragmentedString::StartMatch() { iMatched = ENoMatch; iMatchedToIndex = iCurrentIndex; iMatchedToCharacter = iCurrentCharacter; }// could be made more efficient by matching blocks of text/** Tests if a specified target string occurs at the current position.The function does a byte-for-byte comparison of the string contents from the current position against aString.@return Match result@param aString String to attempt to match*/EXPORT_C CFragmentedString::TStringMatch CFragmentedString::Match(const TDesC& aString) { TInt stringIndex = 0; StartMatch(); const TText* stringPtr=aString.Ptr(); while (iMatchedToIndex<Count() && stringIndex<aString.Length()) { HBufC const* current=At(iMatchedToIndex); const TText* currentPtr=current->Ptr()+iMatchedToCharacter; while (stringIndex<aString.Length() && iMatchedToCharacter<current->Length()) { if (*stringPtr!=*currentPtr) return ENoMatch; ++iMatchedToCharacter; ++stringIndex; ++stringPtr; ++currentPtr; } if (iMatchedToCharacter==current->Length()) { iMatchedToCharacter=0; iMatchedToIndex++; } } if (stringIndex<aString.Length()) iMatched=EInsufficientData; else iMatched=EMatch; return iMatched; }TBool CFragmentedString::FindNextMatchChar(TUint& aChar) { const TInt numberOfFragments=Count(); TBool finished=EFalse; while (iMatchedToIndex<numberOfFragments && !finished) { HBufC* current = At(iMatchedToIndex); finished=iMatchedToCharacter<current->Length(); if (!finished) { iMatchedToCharacter = 0; iMatchedToIndex++; } } /*if (iMatchedToIndex<numberOfFragments && iMatchedToCharacter>=At(iMatchedToIndex)->Length()) { iMatchedToCharacter=0; while (iMatchedToIndex<numberOfFragments && At(iMatchedToIndex)->Length()==0) iMatchedToIndex++; } */ const TBool foundChar=iMatchedToIndex<numberOfFragments && iMatchedToCharacter<At(iMatchedToIndex)->Length(); if (foundChar) { HBufC const* current = At(iMatchedToIndex); aChar=(*current)[iMatchedToCharacter]; } return foundChar; }/** Tests if the character at the current position is within a specified character code range.@return Match result@param aLower Lower range (inclusive) for a match@param aUpper Upper range (inclusive) for a match*/EXPORT_C CFragmentedString::TStringMatch CFragmentedString::MatchRange(TUint aLower, TUint aUpper) { StartMatch(); TUint character; if (FindNextMatchChar(character)) { if (character>=aLower && character<=aUpper) { iMatched=EMatch; ++iMatchedToCharacter; } } else iMatched = EInsufficientData; return iMatched; }CFragmentedString::TStringMatch CFragmentedString::DoMatchSelect(const TDesC& aSelection, TBool aInSelection) { StartMatch(); TUint character; if (FindNextMatchChar(character)) { if ((aSelection.Locate(character)==KErrNotFound)!=aInSelection) { iMatched=EMatch; ++iMatchedToCharacter; } } else iMatched = EInsufficientData; return iMatched; }/** Tests if the character at the current position matches any character in a specified string.@return Match result@param aSelection String specifying one or more characters, any of which will result in a successful match*/EXPORT_C CFragmentedString::TStringMatch CFragmentedString::MatchSelect(const TDesC& aSelection) { return DoMatchSelect(aSelection, ETrue); }/** Tests if the character at the current position does not match any character in a specified string.@return Match result@param aSelection String specifying one or more characters, any of which will result in a failed match*/EXPORT_C CFragmentedString::TStringMatch CFragmentedString::MatchNotSelect(const TDesC& aSelection) { return DoMatchSelect(aSelection, EFalse); }/** Removes and deletes all sub-strings occurring before the position of the last successful match.Any sub-strings at or after a mark are not deleted, regardless of the last successful match position. */EXPORT_C void CFragmentedString::ConsumeMatched() { // update currents iCurrentIndex = iMatchedToIndex; iCurrentCharacter = iMatchedToCharacter; // find out if we can delete any parts TInt freeIndex; if (iMarkStack.IsEmpty()) freeIndex=0; else { TStringMark* stringMark=iMarkStack.Last(); freeIndex=stringMark->iMarkIndex-1; } // delete any parts which are no longer needed if (freeIndex<iCurrentIndex && freeIndex>=0) { for (TInt index=iMarkStack.Count(); --index>=0;) iMarkStack[index]->iMarkIndex-=freeIndex+1; for (TInt stringIndex=freeIndex; stringIndex>=0; stringIndex--) delete(At(stringIndex)); Delete(0,freeIndex+1); iCurrentIndex-=freeIndex+1; iMatchedToIndex-=freeIndex+1; } }/** Resets the string.This resets and destroys the HBufC array, and clears all indexes and marks. */EXPORT_C void CFragmentedString::Reset() { ResetAndDestroy(); iMarkStack.Clear(); iCurrentIndex = 0; iCurrentCharacter = 0; }HBufC* CFragmentedString::StringL(TInt aStartIndex, TInt aStartCharacter, TInt aEndIndex, TInt aEndCharacter, const TDesC* aInitialText/*=NULL*/) const { // Work out how long our string is going to be TInt markedLength=-aStartCharacter; TInt index; for (index=aStartIndex; index<aEndIndex; index++) markedLength+=At(index)->Length(); if (aEndIndex<Count()) markedLength+=aEndCharacter; const TInt extraLen=aInitialText?aInitialText->Length():0; HBufC* markedString = NULL; if(markedLength || extraLen) { // Now allocate the string markedString = HBufC::NewL(markedLength+extraLen); TPtr markedStringPtr = markedString->Des(); if (aInitialText) markedStringPtr.Append(*aInitialText); // Now build the string up if (aStartIndex == aEndIndex) markedStringPtr.Append(At(aStartIndex)->Mid(aStartCharacter, markedLength)); else { if (aStartIndex < aEndIndex) markedStringPtr.Append(At(aStartIndex)->Mid(aStartCharacter)); for (index = aStartIndex+1; index < aEndIndex; index++) markedStringPtr.Append(*At(index)); if (aEndIndex < Count()) markedStringPtr.Append(At(aEndIndex)->Left(aEndCharacter)); } } return markedString; }/** Gets a new string containing the string contents from the head mark to the current index position, prepended with a specified string.It is the caller's responsibility to clean up the returned string.@return New string containing aInitialText and then the marked contents@param aInitialText String to prepend to the result*/EXPORT_C HBufC* CFragmentedString::MarkedWithInitialTextL(const TDesC& aInitialText) { TStringMark* stringMark = iMarkStack.Head(); return StringL(stringMark->iMarkIndex, stringMark->iMarkCharacter, iCurrentIndex, iCurrentCharacter, &aInitialText); }/** Gets a new string containing the string contents from the head mark to the current index position.It is the caller's responsibility to clean up the returned string.@return New string containing marked contents*/EXPORT_C HBufC* CFragmentedString::MarkedL() { TStringMark* stringMark = iMarkStack.Head(); return StringL(stringMark->iMarkIndex, stringMark->iMarkCharacter, iCurrentIndex, iCurrentCharacter); }/** Adds a mark at the current index position.This mark becomes the head mark.Note this function can leave with an out of memory error. */EXPORT_C void CFragmentedString::Mark() // Mark can leave { TStringMark* stringMark = new (ELeave) TStringMark(iCurrentIndex, iCurrentCharacter); iMarkStack.PushL(stringMark); }/** Deletes the head mark. */EXPORT_C void CFragmentedString::DeleteMark() { TStringMark* stringMark = iMarkStack.Pop(); delete stringMark; }// move current position to top (head) mark/** Moves the current index position to the head mark. */EXPORT_C void CFragmentedString::ResetToMark() { TStringMark* stringMark = iMarkStack.Head(); iCurrentIndex = stringMark->iMarkIndex; iCurrentCharacter = stringMark->iMarkCharacter; }/** Replaces the string contents to the head mark with a specified string.@param aString Replacment string*/EXPORT_C void CFragmentedString::ReplaceMarkedL(HBufC* aString) { TStringMark* stringMark = iMarkStack.Head(); DeleteToMark(*stringMark); InsertStringL(aString); }/** Replaces the string contents to the head mark with a specified string, and then advances the current index position to the next sub-string.@param aString Replacment string*/EXPORT_C void CFragmentedString::ReplaceMarkedAndSkipL(HBufC* aString) { ReplaceMarkedL(aString); iCurrentIndex++; }// could be speeded up/** Deletes from the current index position to the specified mark.@param aStringMark Mark to delete to*/EXPORT_C void CFragmentedString::DeleteToMark(const TStringMark& aStringMark) { if (iCurrentIndex>=Count()) { iCurrentIndex=Count()-1; if (iCurrentIndex==-1) { iCurrentIndex=0; iCurrentCharacter=0; } else iCurrentCharacter=At(iCurrentIndex)->Length(); } while (iCurrentIndex > aStringMark.iMarkIndex || (iCurrentIndex==aStringMark.iMarkIndex && iCurrentCharacter>aStringMark.iMarkCharacter)) { if(--iCurrentCharacter<0) { if(At(iCurrentIndex)->Length()==0) Delete(iCurrentIndex); iCurrentIndex--; iCurrentCharacter = At(iCurrentIndex)->Length(); } At(iCurrentIndex)->Des().Delete(iCurrentCharacter,1); } }/** Inserts a specified string at the current index position.@param aString String to insert*/EXPORT_C void CFragmentedString::InsertStringL(HBufC* aString) { InsertStringToL(aString, iCurrentIndex, iCurrentCharacter); }/** Inserts a string at a specified position.1. if aLengthIntoString is 0, the function inserts a new sub-string at array position aStringIndex2. if aLengthIntoString is equal to the length of the sub-string at aStringIndex, then it inserts a new sub-string at array position aStringIndex+1. 3. If aLengthIntoString is in the middle of theaStringIndex sub-string, then it: a) inserts a new sub-string at aStringIndex+1 holdingaString b) inserts a new sub-string at aStringIndex+2 holding the data from aStringIndex after aLengthIntoString c) truncates the original aStringIndex to hold only the data before aLengthIntoString@param aString String to insert@param aStringIndex Array index of the sub-string at which to insert@param aLengthIntoString Character position within the sub-string at which to insert*/EXPORT_C void CFragmentedString::InsertStringToL(HBufC* aString, TInt aStringIndex, TInt aLengthIntoString) { if (aString==NULL || aString->Length()==0) return; // In the beginning of a string if (aLengthIntoString==0) { InsertL(aStringIndex, aString); return; } // At the end of a string if (aLengthIntoString==At(aStringIndex)->Length()) { InsertL(aStringIndex+1, aString); return; } // In the middle TPtrC remainder = At(iCurrentIndex)->Mid(iCurrentCharacter); InsertL(aStringIndex+1, aString); InsertL(aStringIndex+2, remainder.AllocL() ); At(iCurrentIndex)->Des().SetLength(iCurrentCharacter); iCurrentIndex++; iCurrentCharacter = 0; }