diff -r 000000000000 -r e4d67989cc36 genericservices/httputils/DelimitedParser/DelimitedParser.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/genericservices/httputils/DelimitedParser/DelimitedParser.cpp Tue Feb 02 02:01:42 2010 +0200 @@ -0,0 +1,2017 @@ +// Copyright (c) 2001-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: +// + +#include +#include +#include "DelimitedParserInternal.h" +#include + +// Panic category +// +_LIT(KDelimitedParserPanicCategory,"DELIM-PARSER"); + +// +// +// Implementation of TDelimitedParserBase8 +// +// + +/** + Constructor. + + @since 6.0 +*/ +EXPORT_C TDelimitedParserBase8::TDelimitedParserBase8() +: iDataDes(0,0), iCurrentSegment(0,0), iNextSegmentPos(-1), iMode(EDelimitedDataNotParsed), iDelimiter(0) + { + } + +/** + Resets the internal pointer position to the start or end or the descriptor + depending on whether the decriptor is parsing mode. + + @warning There will be a KUriUtilsErrBadDelimitedParserMode panic if the data mode has + not been correctly set. + */ +EXPORT_C void TDelimitedParserBase8::Reset() const + { + iNextSegmentPos = InitialDelimiterPosition(iDataDes, iMode); + } + +/** + Retrieves the current segment and then parses the data to the next one. + + @warning There will be a KDelimitedParserErrNotParsed panic if the data has + not been parsed, and a KDelimitedParserErrNoDelimiter panic if the delimiter has not + been set. + @since 6.0 + @param aSegment This is an output argument that is set to the current segment. + @return A error value of KErrNotFound if there is no current segment. The + value KErrNone if there is a current segment. + @pre The string must have been initially parsed by Parse() or ParseReverse(). + @post The current segment is updated to the next one. +*/ +EXPORT_C TInt TDelimitedParserBase8::GetNext(TPtrC8& aSegment) const + { + __ASSERT_ALWAYS(iMode != EDelimitedDataNotParsed, User::Panic(KDelimitedParserPanicCategory, KUriUtilsErrNotParsed)); + __ASSERT_ALWAYS(iDelimiter != 0, User::Panic(KDelimitedParserPanicCategory, KUriUtilsErrNoDelimiter)); + + // Check that there is a segment + if( iNextSegmentPos == KErrNotFound ) + { + // There is no segment + return KErrNotFound; + } + // There is one - set aSegment + aSegment.Set(iCurrentSegment); + // Parse the next segment + iNextSegmentPos = FindNextSegment(iNextSegmentPos); + return KErrNone; + } + +/** + Parses to the next segment. + + @warning There will be a KDelimitedParserErrNotParsed panic if the data has + not been parsed, and a KDelimitedParserErrNoDelimiter panic if the delimiter has not + been set. + @since 6.0 + @return A error value of KErrNotFound if there is no current segment. The + value KErrNone if there is a current segment. + @pre The string must have been initially parsed by Parse() or ParseReverse(). + @post The current segment is updated to the next one. + */ +EXPORT_C TInt TDelimitedParserBase8::Inc() const + { + __ASSERT_ALWAYS(iMode != EDelimitedDataNotParsed, User::Panic(KDelimitedParserPanicCategory, KUriUtilsErrNotParsed)); + __ASSERT_ALWAYS(iDelimiter != 0, User::Panic(KDelimitedParserPanicCategory, KUriUtilsErrNoDelimiter)); + + // Check that there is a segment + if( iNextSegmentPos == KErrNotFound ) + { + // There is no segment + return KErrNotFound; + } + // Parse the next segment + iNextSegmentPos = FindNextSegment(iNextSegmentPos); + return KErrNone; + } + +/** + Parses back to the previous segment. + + @warning There will be a KDelimitedParserErrNotParsed panic if the data has + not been parsed, and a KDelimitedParserErrNoDelimiter panic if the delimiter has not + been set. + @since 6.0 + @return A error value of KErrNotFound if the current segment is the initial + segment. The value KErrNone if the data has been parsed to the previous segment. + @pre The string must have been initially parsed by Parse() or ParseReverse(). + @post If the parse was successful then the current segment is updated + to the previous one. Otherwise there is no change. +*/ +EXPORT_C TInt TDelimitedParserBase8::Dec() const + { + __ASSERT_ALWAYS(iMode != EDelimitedDataNotParsed, User::Panic(KDelimitedParserPanicCategory, KUriUtilsErrNotParsed)); + __ASSERT_ALWAYS(iDelimiter != 0, User::Panic(KDelimitedParserPanicCategory, KUriUtilsErrNoDelimiter)); + + // Find position of previous delimiter + TInt prev = PrevDelimiterPosition(iDataDes, iNextSegmentPos, iDelimiter, iMode); + + // Get the previous segment + if( FindPrevSegment(prev) == KErrNotFound ) + { + // There is no previous segment - set to start of data + return KErrNotFound; + } + // Update next segment position + iNextSegmentPos = prev; + return KErrNone; + } + +/** + Retrieves the current segment. + + @warning There will be a KDelimitedParserErrNotParsed panic if the data has + not been parsed, and a KDelimitedParserErrNoDelimiter panic if the delimiter has not + been set. + @since 6.0 + @param aSegment This is an output argument that is set to the current segment. + @return A error value of KErrNotFound if there is no current segment. The + value KErrNone if there is a current segment. + @pre The string must have been initially parsed by Parse() or ParseReverse(). +*/ +EXPORT_C TInt TDelimitedParserBase8::Peek(TPtrC8& aSegment) const + { + __ASSERT_ALWAYS(iMode != EDelimitedDataNotParsed, User::Panic(KDelimitedParserPanicCategory, KUriUtilsErrNotParsed)); + __ASSERT_ALWAYS(iDelimiter != 0, User::Panic(KDelimitedParserPanicCategory, KUriUtilsErrNoDelimiter)); + + // Check that there is a segment + if( iNextSegmentPos == KErrNotFound ) + { + // There is no segment + return KErrNotFound; + } + // There is one - set aSegment + aSegment.Set(iCurrentSegment); + return KErrNone; + } + +/** + Indicates whether the end of the data has been reached and there are no more segments + to parse. + + @warning There will be a KDelimitedParserErrNotParsed panic if the data has + not been parsed, and a KDelimitedParserErrNoDelimiter panic if the delimiter has not + been set. + @since 6.0 + @return A boolean value of ETrue if the end of the data has been reached, + or EFalse if there are more segements to parse. + @pre The string must have been initially parsed by Parse() or ParseReverse(). +*/ +EXPORT_C TBool TDelimitedParserBase8::Eos() const + { + __ASSERT_ALWAYS(iMode != EDelimitedDataNotParsed, User::Panic(KDelimitedParserPanicCategory, KUriUtilsErrNotParsed)); + + TBool eos = iNextSegmentPos == KErrNotFound ? ETrue : EFalse; + return eos; + } + +/** + Checks for a delimiter at the front (left) of the data. + + @warning There will be a KDelimitedParserErrNotParsed panic if the data has + not been parsed, and a KDelimitedParserErrNoDelimiter panic if the delimiter has not been set. + @since 6.0 + @return A boolean of value ETrue if there is a front delimiter, or EFalse + if there is no front delimiter. + @pre The string must have been initially parsed by Parse() or ParseReverse(). +*/ +EXPORT_C TBool TDelimitedParserBase8::FrontDelimiter() const + { + __ASSERT_ALWAYS(iDelimiter != 0, User::Panic(KDelimitedParserPanicCategory, KUriUtilsErrNoDelimiter)); + + return (iDataDes.Locate(iDelimiter) == 0); + } + +/** + Checks for a delimiter at the back (right) of the data. + + @warning There will be a KDelimitedParserErrNotParsed panic if the data has + not been parsed, and a KDelimitedParserErrNoDelimiter panic if the delimiter has not + been set. + @since 6.0 + @return A boolean of value ETrue if there is a back delimiter, or EFalse + if there is no back delimiter. + @pre The string must have been initially parsed by Parse() or ParseReverse(). +*/ +EXPORT_C TBool TDelimitedParserBase8::BackDelimiter() const + { + __ASSERT_ALWAYS(iDelimiter != 0, User::Panic(KDelimitedParserPanicCategory, KUriUtilsErrNoDelimiter)); + + TInt delimiterPos = iDataDes.LocateReverse(iDelimiter); + if( delimiterPos == KErrNotFound ) + return EFalse; + return (delimiterPos == iDataDes.Length() - 1); + } + +/** + Retrieves the descriptor reference with the data + + @since 6.0 + @return A const descriptor reference with the data. +*/ +EXPORT_C const TDesC8& TDelimitedParserBase8::Des() const + { + return iDataDes; + } + +/** + Gives the remainder of the data from (and including) the current segment. Any other segments + that have parsed through are not included. + + @warning There will be a KDelimitedParserErrNotParsed panic if the data has + not been parsed, and a KDelimitedParserErrNoDelimiter panic if the delimiter has not + been set. + @since 6.0 + @param aRemainder This is an output argument that is set to the remaining data. + @return An error value of KErrNotFound if there is no remaining data, or + value of KErrNone if there is remaining data. + @pre The data must have been initially parsed by Parse() or ParseReverse(). + */ +EXPORT_C TInt TDelimitedParserBase8::Remainder(TPtrC8& aRemainder) const + { + __ASSERT_ALWAYS(iMode != EDelimitedDataNotParsed, User::Panic(KDelimitedParserPanicCategory, KUriUtilsErrNotParsed)); + __ASSERT_ALWAYS(iDelimiter != 0, User::Panic(KDelimitedParserPanicCategory, KUriUtilsErrNoDelimiter)); + + // Check to see if there is a segment left + if( iNextSegmentPos == KErrNotFound ) + { + // There is no segment + return KErrNotFound; + } + // Find the previous delimiter -> the start of the current segment + TInt prev = PrevDelimiterPosition(iDataDes, iNextSegmentPos, iDelimiter, iMode); + + // Need to see which direction the parsing is going to set the remainder + switch(iMode) + { + case EDelimitedDataForward: + { + aRemainder.Set(iDataDes.Right(iDataDes.Length() - prev)); + } break; + case EDelimitedDataReverse: + { + aRemainder.Set(iDataDes.Left(prev)); + } break; + default: + // Bad mode! + User::Panic(KDelimitedParserPanicCategory, KUriUtilsErrBadDelimitedParserMode); + break; + } + return KErrNone; + } + +/** + This parses the data into segments from left to right. + + @warning There will be a KDelimitedParserErrNoDelimiter panic if the delimiter + has not been set. + @since 6.0 + @param aData A descriptor containing the data. + @pre The delimiter must have been set. + @post The current segment is the leftmost segment and the direction of + parsing is set from left to right (EDelimitedDataFroward). + */ +EXPORT_C void TDelimitedParserBase8::Parse(const TDesC8& aData) + { + __ASSERT_ALWAYS(iDelimiter != 0, User::Panic(KDelimitedParserPanicCategory, KUriUtilsErrNoDelimiter)); + + // Initialise data for EForward direction + iMode = EDelimitedDataForward; + DoParse(aData); + } + +/** + This parses the data into segments from lright to left. + + @warning There will be a KDelimitedParserErrNoDelimiter panic if the delimiter + has not been set. + @since 6.0 + @param aData A descriptor containing the data. + @pre The delimiter must have been set. + @post The current segment is the leftmost segment and the direction of + parsing is set from right to left (EDelimitedDataReverse). + */ +EXPORT_C void TDelimitedParserBase8::ParseReverse(const TDesC8& aData) + { + __ASSERT_ALWAYS(iDelimiter != 0, User::Panic(KDelimitedParserPanicCategory, KUriUtilsErrNoDelimiter)); + + // Initialise data for EReverse direction + iMode = EDelimitedDataReverse; + DoParse(aData); + } + +/** + Sets the delimiting character. + + @since 6.0 + @param aDelimiter The delimiting character. + @post The delimiting character is set. +*/ +EXPORT_C void TDelimitedParserBase8::SetDelimiter(TChar aDelimiter) + { + iDelimiter = aDelimiter; + } + +/** + Initialises the parsing of the data. + + @since 6.0 + @param aData A descriptor reference with the data. + @pre The delimiting character has been set. + @post The data descriptor is set to the input argument. The current + segment refers to the initial segment of the data. +*/ +void TDelimitedParserBase8::DoParse(const TDesC8& aData) + { + // Reset the segment information, then set the new Data - set pointer to NULL and length to zero + iCurrentSegment.Set(NULL,0); + iDataDes.Set(aData); + + // Check that there is a string! + if( iDataDes.Length() == 0 ) + { + // No string - ensure functionality blocked for this descriptor + iNextSegmentPos = KErrNotFound; + return; + } + // Find the segment - search from initial start position + iNextSegmentPos = FindNextSegment(InitialDelimiterPosition(iDataDes, iMode)); + } + +/** + Finds the next segment from the given start position. + + @since 6.0 + @param aStartPos The position from where to start the search for the + next segment. + @return The position of delimiter after the specified start position, or + an error value of KErrNotFound if no more delimiters are found. +*/ +TInt TDelimitedParserBase8::FindNextSegment(TInt aStartPos) const + { + // Find position of next delimiter + TInt next = NextDelimiterPosition(iDataDes, aStartPos, iDelimiter, iMode); + + if( next != KErrNotFound ) + { + TInt startPos = next < aStartPos ? next : aStartPos; + TInt endPos = next < aStartPos ? aStartPos : next; + if( iDataDes[startPos] == iDelimiter ) + { + // Move past delimiter + ++startPos; + } + TInt length = endPos - startPos; + iCurrentSegment.Set(iDataDes.Mid(startPos, length)); + } + return next; + } + +/** + Finds the previous segment from the given start position. + + @since 6.0 + @param aStartPos The position from where to start the search for the + previous segment. + @return The position of delimiter before the specified start position, or + an error value of KErrNotFound if no more delimiters are found. +*/ +TInt TDelimitedParserBase8::FindPrevSegment(TInt aStartPos) const + { + // Find position of previous delimiter + TInt prev = PrevDelimiterPosition(iDataDes, aStartPos, iDelimiter, iMode); + + if( prev != KErrNotFound ) + { + TInt startPos = prev < aStartPos ? prev : aStartPos; + TInt endPos = prev < aStartPos ? aStartPos : prev; + if( iDataDes[startPos] == iDelimiter ) + { + // Move past delimiter + ++startPos; + } + TInt length = endPos - startPos; + iCurrentSegment.Set(iDataDes.Mid(startPos, length)); + } + return prev; + } + +// +// +// Implementation of CDelimitedDataBase8 +// +// + +/** + Destructor. + + @since 6.0 +*/ +EXPORT_C CDelimitedDataBase8::~CDelimitedDataBase8() + { + delete iDataBuf; + } + +/** + Inserts the new segment in a position before the current parsed segment. The new + segment can be made up of several segments and have delimiters at either extreme. + The insert functionality will ensure that there is always a delimiter at the front + of the new segment. The parser is left in a state where its current segment is the + same one as before the insertion. + + @warning There will be a KDelimitedParserErrNotParsed panic if the data has not been + parsed, and a KDelimitedParserErrNoDelimiter panic if the delimiter has not been set. + @since 6.0 + @param aSegment A descriptor with the new segment to be inserted. + @pre The data must have been initially parsed by Parse() or ParseReverse(). + @post The data will have been extended to include the new segment. The + current segment will remain as the one before the insertion. +*/ +EXPORT_C void CDelimitedDataBase8::InsertCurrentL(const TDesC8& aSegment) + { + __ASSERT_ALWAYS(iParser.iMode != EDelimitedDataNotParsed, User::Panic(KDelimitedParserPanicCategory, KUriUtilsErrNotParsed)); + __ASSERT_ALWAYS(iParser.iDelimiter != 0, User::Panic(KDelimitedParserPanicCategory, KUriUtilsErrNoDelimiter)); + + DoInsertL(aSegment); + } + +/** + Removes the current segment. After removing the segment, the parser's new current segment + will be the next segment. If the last segment is the one that is removed then the parser + will be set to the end of the data. + + @warning There will be a KDelimitedParserErrNotParsed panic if the data has + not been parsed, and a KDelimitedParserErrNoDelimiter panic if the delimiter has not + been set. + @since 6.0 + @pre The data must have been initially parsed by Parse() or ParseReverse(). + @post The data will have been reduced to exclude the removed segment. + The current segment will be set to what was the next segment. If the removed segment was + the last segment, the parser is at the end of the data. +*/ +EXPORT_C void CDelimitedDataBase8::RemoveCurrentL() + { + __ASSERT_ALWAYS(iParser.iMode != EDelimitedDataNotParsed, User::Panic(KDelimitedParserPanicCategory, KUriUtilsErrNotParsed)); + __ASSERT_ALWAYS(iParser.iDelimiter != 0, User::Panic(KDelimitedParserPanicCategory, KUriUtilsErrNoDelimiter)); + + DoRemoveL(); + } + +/** + Adds a new segment to the end of the data. The new segment can be made up of several segments + and have delimiters at either extreme. The insert functionality will ensure that there is + always a delimiter at the front of the new segment. The data must re-parsed to ensure that the + parser is valid. + + @warning There will be a KDelimitedParserErrNotParsed panic if the data has + not been parsed, and a KDelimitedParserErrNoDelimiter panic if the delimiter has not + been set. A re-parse is required to ensure that the parser is valid. + @since 6.0 + @param aSegment A descriptor with the new segment to be inserted. + @pre The delimiter must have been set. + @post The data will have been extended to include the new segment. +*/ +EXPORT_C void CDelimitedDataBase8::PushBackL(const TDesC8& aSegment) + { + __ASSERT_ALWAYS(iParser.iDelimiter != 0, User::Panic(KDelimitedParserPanicCategory, KUriUtilsErrNoDelimiter)); + + // Parse the string in reverse direction - sets last segment as current + iParser.ParseReverse(*iDataBuf); + + // Insert the segment + DoInsertL(aSegment); + + // Make sure that a re-parse is required + iParser.iMode = EDelimitedDataNotParsed; + } + +/** + Removes the last segment from the data. The data must be re-parsed to ensure that the parser is valid. + + @warning There will be a KDelimitedParserErrNotParsed panic if the data has + not been parsed, and a KDelimitedParserErrNoDelimiter panic if the delimiter has not + been set. A re-parse is required to ensure that the parser is valid. + @since 6.0 + @pre The delimiter must have been set. + @post The data will have been reduced to exclude the last segment. +*/ +EXPORT_C void CDelimitedDataBase8::PopBackL() + { + __ASSERT_ALWAYS(iParser.iDelimiter != 0, User::Panic(KDelimitedParserPanicCategory, KUriUtilsErrNoDelimiter)); + + // Parse the string in reverse direction - sets last segment as current + iParser.ParseReverse(*iDataBuf); + + // Remove the current segment + DoRemoveL(); + + // Make sure that a re-parse is required + iParser.iMode = EDelimitedDataNotParsed; + } + +/** + Adds a new segment to the front of the data. The new segment can be made up of several segments + and have delimiters at either extreme. The insert functionality will ensure that there is always + a delimiter at the front of the new segment. The data must re-parsed to ensure that the parser + is valid. + + @warning There will be a KDelimitedParserErrNotParsed panic if the data has + not been parsed, and a KDelimitedParserErrNoDelimiter panic if the delimiter has not + been set. A re-parse is required to ensure that the parser is valid. + @since 6.0 + @param aSegment A descriptor with the new segment to be inserted. + @pre The delimiter must have been set. + @post The data will have been extended to include the new segment. +*/ +EXPORT_C void CDelimitedDataBase8::PushFrontL(const TDesC8& aSegment) + { + __ASSERT_ALWAYS(iParser.iDelimiter != 0, User::Panic(KDelimitedParserPanicCategory, KUriUtilsErrNoDelimiter)); + + // Parse the string in forward direction - sets first segment as current + iParser.Parse(*iDataBuf); + + // Insert the segment + DoInsertL(aSegment); + + // Make sure that a re-parse is required + iParser.iMode = EDelimitedDataNotParsed; + } + +/** + Removes the first segment from the data. The data must be re-parsed to ensure that the parser is valid. + + @warning There will be a KDelimitedParserErrNotParsed panic if the data has + not been parsed, and a KDelimitedParserErrNoDelimiter panic if the delimiter has not + been set. A re-parse is required to ensure that the parser is valid. + @since 6.0 + @pre The delimiter must have been set. + @post The data will have been reduced to exclude the last segment. +*/ +EXPORT_C void CDelimitedDataBase8::PopFrontL() + { + __ASSERT_ALWAYS(iParser.iDelimiter != 0, User::Panic(KDelimitedParserPanicCategory, KUriUtilsErrNoDelimiter)); + + // Parse the string in forward direction - sets first segment as current + iParser.Parse(*iDataBuf); + + // Remove the current segment + DoRemoveL(); + + // Make sure that a re-parse is required + iParser.iMode = EDelimitedDataNotParsed; + } + +/** + Removes the front delimiter (if exists) from the data. + + @warning There will be a KDelimitedParserErrNotParsed panic if the data has + not been parsed, and a KDelimitedParserErrNoDelimiter panic if the delimiter has not + been set. A re-parse is required to ensure that the parser is valid. + @since 6.0 + @pre The delimiter must have been set. + @post The data might have been reduced to exclude the front delimiter. +*/ +EXPORT_C void CDelimitedDataBase8::TrimFrontDelimiterL() + { + __ASSERT_ALWAYS(iParser.iDelimiter != 0, User::Panic(KDelimitedParserPanicCategory, KUriUtilsErrNoDelimiter)); + + // Search for delimiter + if( iParser.FrontDelimiter() ) + { + // Remove front delimiter and update member data + SetDataL(iParser.iDataDes.Right(iParser.iDataDes.Length() - 1)); + } + // Make sure that a re-parse is required + iParser.iMode = EDelimitedDataNotParsed; + } + +/** + Adds a delimiter to the front of the data (if it doesn't exist). + + @warning There will be a KDelimitedParserErrNotParsed panic if the data has + not been parsed, and a KDelimitedParserErrNoDelimiter panic if the delimiter has not + been set. A re-parse is required to ensure that the parser is valid. + @since 6.0 + @pre The delimiter must have been set. + @post The data might have been extended to include a front delimiter. +*/ +EXPORT_C void CDelimitedDataBase8::AddFrontDelimiterL() + { + __ASSERT_ALWAYS(iParser.iDelimiter != 0, User::Panic(KDelimitedParserPanicCategory, KUriUtilsErrNoDelimiter)); + + if( !iParser.FrontDelimiter() ) + { + // Create a new buffer of correct size + HBufC8* buf = HBufC8::NewL(iParser.iDataDes.Length() + 1); + TPtr8 str = buf->Des(); + + // Append a delimiter, then append the current string + str.Append(iParser.iDelimiter); + str.Append(iParser.iDataDes); + + // Set buffer to this new string + SetData(buf); + } + // Make sure that a re-parse is required + iParser.iMode = EDelimitedDataNotParsed; + } + +/** + Removes the back delimiter (if exists) from the data. + + @warning There will be a KDelimitedParserErrNotParsed panic if the data has + not been parsed, and a KDelimitedParserErrNoDelimiter panic if the delimiter has not + been set. A re-parse is required to ensure that the parser is valid. + @since 6.0 + @pre The delimiter must have been set. + @post The data might have been reduced to exclude the front delimiter. +*/ +EXPORT_C void CDelimitedDataBase8::TrimBackDelimiterL() + { + __ASSERT_ALWAYS(iParser.iDelimiter != 0, User::Panic(KDelimitedParserPanicCategory, KUriUtilsErrNoDelimiter)); + + // Search for delimiter + if( iParser.BackDelimiter() ) + { + // Remove back delimiter and update member data + SetDataL(iParser.iDataDes.Left(iParser.iDataDes.Length() - 1)); + } + // Make sure that a re-parse is required + iParser.iMode = EDelimitedDataNotParsed; + } + +/** + Adds a delimiter to the back of the data (if it doesn't exist). + + @warning There will be a KDelimitedParserErrNotParsed panic if the data has + not been parsed, and a KDelimitedParserErrNoDelimiter panic if the delimiter has not + been set. A re-parse is required to ensure that the parser is valid. + @since 6.0 + @pre The delimiter must have been set. + @post The data might have been extended to include a front delimiter. +*/ +EXPORT_C void CDelimitedDataBase8::AddBackDelimiterL() + { + __ASSERT_ALWAYS(iParser.iDelimiter != 0, User::Panic(KDelimitedParserPanicCategory, KUriUtilsErrNoDelimiter)); + + if( !iParser.BackDelimiter() ) + { + // Create a new buffer of correct size + HBufC8* buf = HBufC8::NewL(iParser.iDataDes.Length() + 1); + TPtr8 str = buf->Des(); + + // Append the current string, then append a delimiter + str.Append(iParser.iDataDes); + str.Append(iParser.iDelimiter); + + // Set buffer to this new string + SetData(buf); + } + // Make sure that a re-parse is required + iParser.iMode = EDelimitedDataNotParsed; + } + +/** + This parses the data into segments from left to right. + + @warning There will be a KDelimitedParserErrNoDelimiter panic if the delimiter + has not been set. + @since 6.0 + @pre The delimiter must have been set. + @post The current segment is the leftmost segment and the direction of + parsing is set from left to right (EDelimitedDataFroward). +*/ +EXPORT_C void CDelimitedDataBase8::Parse() + { + // This call will panic with KUriUtilsErrNoDelimiter if the delimiter is not set + iParser.Parse(*iDataBuf); + } + +/** + This parses the string into segments from right to left. + + @since 6.0 + @pre The delimiter must have been set. Will get a KDelimitedParserErrNoDelimiter panic if + the delimiter has not been initialized. + @post The current segment is the leftmost segment and the direction of parsing is right to left. +*/ +EXPORT_C void CDelimitedDataBase8::ParseReverse() + { + // This call will panic with KUriUtilsErrNoDelimiter if the delimiter is not set + iParser.ParseReverse(*iDataBuf); + } + +/** + Retrieves a const reference to the delimited data parser. + + @since 6.0 + @return A const reference to the delimited data parser. +*/ +EXPORT_C const TDelimitedParserBase8& CDelimitedDataBase8::Parser() const + { + return iParser; + } + +/** + Sets the delimiting character. + + @since 6.0 + @param aDelimiter The delimiting character. + @post The delimiting character is updated. +*/ +EXPORT_C void CDelimitedDataBase8::SetDelimiter(TChar aDelimiter) + { + iParser.SetDelimiter(aDelimiter); + } + +/** + Constructor. First phase of two-phase construction method. Does non-allocating construction. + + @since 6.0 +*/ +EXPORT_C CDelimitedDataBase8::CDelimitedDataBase8() + { + } + +/** + Second phase of two-phase construction method. Does any allocations required to fully construct + the object. + + @since 6.0 + @param aData A descriptor with the initial string. + @pre First phase of construction is complete. + @post The object is fully constructed. +*/ +EXPORT_C void CDelimitedDataBase8::ConstructL(const TDesC8& aData) + { + // Create copy of string and set descriptor in the parser + SetDataL(aData); + } + +/** + Inserts the new segment in a position before the current segment. The new segment can be made up + of several segments and have delimiters at either extreme. The insert functionality will ensure + that there is always a delimiter at the front of the new segment. The parser will be left in a + state where its current segment is the same one as before the insertion. + + @since 6.0 + @param aSegment The descriptor with the segment to be inserted. + @pre The string must have been parsed. + @post The string will have been extended to include the new segment. The current segment will + remain as the one before the insertion. +*/ +void CDelimitedDataBase8::DoInsertL(const TDesC8& aSegment) + { + // Get previous delimiter to split the current string into prefix and suffix to the new segment + TInt prevPos = PrevDelimiterPosition(iParser.iDataDes, iParser.iNextSegmentPos, iParser.iDelimiter, iParser.iMode); + TPtrC8 prefix = iParser.iDataDes.Left(prevPos); + + TInt suffixLength = iParser.iDataDes.Length() - prevPos; + TPtrC8 suffix = iParser.iDataDes.Right(suffixLength); + if( suffixLength && suffix[0] == iParser.iDelimiter ) + { + // Remove front delimiter on suffix + suffix.Set(suffix.Right(--suffixLength)); + } + + // Check for delimiters + TPtrC8 segment = aSegment; + TInt segmentLength = segment.Length(); + TBool segmentBackDelimiter = (segmentLength && segment[segmentLength - 1] == iParser.iDelimiter); + if( segmentBackDelimiter ) + { + // Remove back delimiter from the segment + segment.Set(segment.Left(--segmentLength)); + } + if( segmentLength && segment[0] == iParser.iDelimiter ) + { + // Remove front delimiter from the segment + segment.Set(segment.Right(--segmentLength)); + } + + // Check if a back delimiter is needed - NOTE always add a front delimiter + TInt extra = 1; + TBool needBackDelimiter = EFalse; + if( suffix.Length() || segmentBackDelimiter ) + { + ++extra; + needBackDelimiter = ETrue; + } + // Create space for new string + HBufC8* buf = HBufC8::NewL(prevPos + segmentLength + suffixLength + extra); + TPtr8 str = buf->Des(); + + // Form the new string + str.Append(prefix); + str.Append(iParser.iDelimiter); + str.Append(segment); + if( needBackDelimiter ) + str.Append(iParser.iDelimiter); + str.Append(suffix); + + // Update string data + SetData(buf); + + // Check to see if the internal parser object (iParser) has been parsed + // (can tell if it has if the data pointer in iCurrentSegment is not NULL) + // If so update iCurrentSegment to ensure that iParser remains valid + if( iParser.iCurrentSegment.Ptr() ) + { + // Ensure parser is in correct position and current segment is correct + iParser.iNextSegmentPos = prevPos; + if( iParser.iMode == EDelimitedDataForward ) + { + // Move iterator to delimiter before iCurrentSegment - length of segment + a delimiter + iParser.iNextSegmentPos += segmentLength + 1; + } + // Get the next segment + iParser.iNextSegmentPos = iParser.FindNextSegment(iParser.iNextSegmentPos); + } + } + +/** + Removes the current segment. After removing the segment, the parser's new current segment will be + the next segment. If the last segment is the one that is removed then the parser will be set to the + end of the data. + + @since 6.0 + @pre The data must have been parsed. + @post The data will have been reduced to exclude the removed data. The + current segment is set to what was the next segment. If the removed segment was + the last segment, the parser is at the end of the data. +*/ +void CDelimitedDataBase8::DoRemoveL() + { + // Check if there is anything to remove + if( iParser.iDataDes.Length() == 0 ) + { + return; + } + // Find the previous delimiter + TInt prev = PrevDelimiterPosition(iParser.iDataDes, iParser.iNextSegmentPos, iParser.iDelimiter, iParser.iMode); + + // Set up the start and end position of current segment + TInt endPos = iParser.iNextSegmentPos; + TInt startPos = iParser.iNextSegmentPos; + if( prev < iParser.iNextSegmentPos ) + startPos = prev; + else + endPos = prev; + + // Ok, get the prefix and suffix parts + TPtrC8 prefix = iParser.iDataDes.Left(startPos); + TInt suffixLength = iParser.iDataDes.Length() - endPos; + TPtrC8 suffix = iParser.iDataDes.Right(suffixLength); + + // Create the space + HBufC8* buf = HBufC8::NewL(startPos + suffixLength); + TPtr8 str = buf->Des(); + + // Form the new string + str.Append(prefix); + str.Append(suffix); + + // Update string data + SetData(buf); + + // Ensure parser is in correct position + iParser.iNextSegmentPos = iParser.FindNextSegment(startPos); + } + +/** + Updates internal data buffer with the new data. Creates a copy of the new data. + + @since 6.0 + @param aData A descriptor with the new string. + @post The internal data buffer now contains a copy of the new data and the + parser is set to the new data. +*/ +void CDelimitedDataBase8::SetDataL(const TDesC8& aData) + { + // Cleanup old data and set new + HBufC8* buf = aData.AllocL(); + SetData(buf); + } + +/** + Sets internal data buffer and parser. Cleans up the old data and uses the data buffer. + The parser is set to the new data. + + @since 6.0 + @param aDataBuf A pointer to a decriptor buffer with the new data. + @post The internal data buffer now points to the new buffer and the parser + is set to the data in the new buffer.. +*/ +void CDelimitedDataBase8::SetData(HBufC8* aDataBuf) + { + delete iDataBuf; + iDataBuf = aDataBuf; + iParser.iDataDes.Set(*iDataBuf); + } + +// +// +// Implementation of TDelimitedParserBase16 +// +// +/** + Constructor. + + @since 6.0 + */ +EXPORT_C TDelimitedParserBase16::TDelimitedParserBase16() +: iDataDes(0,0), iCurrentSegment(0,0), iNextSegmentPos(-1), iMode(EDelimitedDataNotParsed), iDelimiter(0) + { + } + +/** + Resets the internal pointer position to the start or end or the descriptor depending + on whether the decriptor is parsing mode. + + @warning There will be a KUriUtilsErrBadDelimitedParserMode panic if the data mode has + not been correctly set. + */ +EXPORT_C void TDelimitedParserBase16::Reset() const + { + __ASSERT_ALWAYS(iMode != EDelimitedDataNotParsed, User::Panic(KDelimitedParserPanicCategory, KUriUtilsErrNotParsed)); + __ASSERT_ALWAYS(iDelimiter != 0, User::Panic(KDelimitedParserPanicCategory, KUriUtilsErrNoDelimiter)); + + iNextSegmentPos = InitialDelimiterPosition(iDataDes, iMode); + } +/** + Retrieves the current segment and then parses the data to the next one. + + @warning There will be a KDelimitedParserErrNotParsed panic if the data has not + been parsed, and a KDelimitedParserErrNoDelimiter panic if the delimiter has not been set. + @since 6.0 + @param aSegment This is an output argument that is set to the current segment. + @return A error value of KErrNotFound if there is no current segment. The + value KErrNone if there is a current segment. + @pre The string must have been initially parsed by Parse() or ParseReverse(). + @post The current segment is updated to the next one. + */ +EXPORT_C TInt TDelimitedParserBase16::GetNext(TPtrC16& aSegment) const + { + __ASSERT_ALWAYS(iMode != EDelimitedDataNotParsed, User::Panic(KDelimitedParserPanicCategory, KUriUtilsErrNotParsed)); + __ASSERT_ALWAYS(iDelimiter != 0, User::Panic(KDelimitedParserPanicCategory, KUriUtilsErrNoDelimiter)); + + // Check that there is a segment + if( iNextSegmentPos == KErrNotFound ) + { + // There is no segment + return KErrNotFound; + } + // There is one - set aSegment + aSegment.Set(iCurrentSegment); + // Parse the next segment + iNextSegmentPos = FindNextSegment(iNextSegmentPos); + return KErrNone; + } + +/** + Parses to the next segment. + + @warning There will be a KDelimitedParserErrNotParsed panic if the data has not + been parsed, and a KDelimitedParserErrNoDelimiter panic if the delimiter has not been set. + @since 6.0 + @return A error value of KErrNotFound if there is no current segment. The + value KErrNone if there is a current segment. + @pre The string must have been initially parsed by Parse() or ParseReverse(). + @post The current segment is updated to the next one. + */ +EXPORT_C TInt TDelimitedParserBase16::Inc() const + { + __ASSERT_ALWAYS(iMode != EDelimitedDataNotParsed, User::Panic(KDelimitedParserPanicCategory, KUriUtilsErrNotParsed)); + __ASSERT_ALWAYS(iDelimiter != 0, User::Panic(KDelimitedParserPanicCategory, KUriUtilsErrNoDelimiter)); + + // Check that there is a segment + if( iNextSegmentPos == KErrNotFound ) + { + // There is no segment + return KErrNotFound; + } + // Parse the next segment + iNextSegmentPos = FindNextSegment(iNextSegmentPos); + return KErrNone; + } + +/** + Parses back to the previous segment. + + @warning There will be a KDelimitedParserErrNotParsed panic if the data has not + been parsed, and a KDelimitedParserErrNoDelimiter panic if the delimiter has not been set. + @since 6.0 + @return A error value of KErrNotFound if the current segment is the initial + segment. The value KErrNone if the data has been parsed to the previous segment. + @pre The string must have been initially parsed by Parse() or ParseReverse(). + @post If the parse was successful then the current segment is updated + to the previous one. Otherwise there is no change. + */ +EXPORT_C TInt TDelimitedParserBase16::Dec() const + { + __ASSERT_ALWAYS(iMode != EDelimitedDataNotParsed, User::Panic(KDelimitedParserPanicCategory, KUriUtilsErrNotParsed)); + __ASSERT_ALWAYS(iDelimiter != 0, User::Panic(KDelimitedParserPanicCategory, KUriUtilsErrNoDelimiter)); + + // Find position of previous delimiter + TInt prev = PrevDelimiterPosition(iDataDes, iNextSegmentPos, iDelimiter, iMode); + + // Get the previous segment + if( FindPrevSegment(prev) == KErrNotFound ) + { + // There is no previous segment - set to start of data + return KErrNotFound; + } + // Update next segment position + iNextSegmentPos = prev; + return KErrNone; + } + +/** + Retrieves the current segment. + + @warning There will be a KDelimitedParserErrNotParsed panic if the data has not + been parsed, and a KDelimitedParserErrNoDelimiter panic if the delimiter has not been set. + @since 6.0 + @param aSegment This is an output argument that is set to the current segment. + @return A error value of KErrNotFound if there is no current segment. The + value KErrNone if there is a current segment. + @pre The string must have been initially parsed by Parse() or ParseReverse(). + */ +EXPORT_C TInt TDelimitedParserBase16::Peek(TPtrC16& aSegment) const + { + __ASSERT_ALWAYS(iMode != EDelimitedDataNotParsed, User::Panic(KDelimitedParserPanicCategory, KUriUtilsErrNotParsed)); + __ASSERT_ALWAYS(iDelimiter != 0, User::Panic(KDelimitedParserPanicCategory, KUriUtilsErrNoDelimiter)); + + // Check that there is a segment + if( iNextSegmentPos == KErrNotFound ) + { + // There is no segment + return KErrNotFound; + } + // There is one - set aSegment + aSegment.Set(iCurrentSegment); + return KErrNone; + } + +/** + Indicates whether the end of the data has been reached and there are no more segments to parse. + + @warning There will be a KDelimitedParserErrNotParsed panic if the data has not + been parsed, and a KDelimitedParserErrNoDelimiter panic if the delimiter has not been set. + @since 6.0 + @return A boolean value of ETrue if the end of the data has been reached, + or EFalse if there are more segements to parse. + @pre The string must have been initially parsed by Parse() or ParseReverse(). + */ +EXPORT_C TBool TDelimitedParserBase16::Eos() const + { + __ASSERT_ALWAYS(iMode != EDelimitedDataNotParsed, User::Panic(KDelimitedParserPanicCategory, KUriUtilsErrNotParsed)); + + TBool eos = iNextSegmentPos == KErrNotFound ? ETrue : EFalse; + return eos; + } + +/** + Checks for a delimiter at the front (left) of the data. + + @warning There will be a KDelimitedParserErrNotParsed panic if the data has not + been parsed, and a KDelimitedParserErrNoDelimiter panic if the delimiter has not been set. + @since 6.0 + @return A boolean of value ETrue if there is a front delimiter, or EFalse + if there is no front delimiter. + @pre The string must have been initially parsed by Parse() or ParseReverse(). + */ +EXPORT_C TBool TDelimitedParserBase16::FrontDelimiter() const + { + __ASSERT_ALWAYS(iDelimiter != 0, User::Panic(KDelimitedParserPanicCategory, KUriUtilsErrNoDelimiter)); + + return (iDataDes.Locate(iDelimiter) == 0); + } + +/** + Checks for a delimiter at the back (right) of the data. + + @warning There will be a KDelimitedParserErrNotParsed panic if the data has not + been parsed, and a KDelimitedParserErrNoDelimiter panic if the delimiter has not been set. + @since 6.0 + @return A boolean of value ETrue if there is a back delimiter, or EFalse + if there is no back delimiter. + @pre The string must have been initially parsed by Parse() or ParseReverse(). + */ +EXPORT_C TBool TDelimitedParserBase16::BackDelimiter() const + { + __ASSERT_ALWAYS(iDelimiter != 0, User::Panic(KDelimitedParserPanicCategory, KUriUtilsErrNoDelimiter)); + + TInt delimiterPos = iDataDes.LocateReverse(iDelimiter); + if( delimiterPos == KErrNotFound ) + return EFalse; + return (delimiterPos == iDataDes.Length() - 1); + } + +/** + Retrieves the descriptor reference with the data + + @since 6.0 + @return A const descriptor reference with the data. + */ +EXPORT_C const TDesC16& TDelimitedParserBase16::Des() const + { + return iDataDes; + } + +/** + Gives the remainder of the data from (and including) the current segment. Any other segments that + have parsed through are not included. + + @warning There will be a KDelimitedParserErrNotParsed panic if the data has not been parsed, + and a KDelimitedParserErrNoDelimiter panic if the delimiter has not been set. + @since 6.0 + @param aRemainder This is an output argument that is set to the remaining data. + @return An error value of KErrNotFound if there is no remaining data, or value of KErrNone + if there is remaining data. + @pre The data must have been initially parsed by Parse() or ParseReverse(). + */ +EXPORT_C TInt TDelimitedParserBase16::Remainder(TPtrC16& aRemainder) const + { + __ASSERT_ALWAYS(iMode != EDelimitedDataNotParsed, User::Panic(KDelimitedParserPanicCategory, KUriUtilsErrNotParsed)); + __ASSERT_ALWAYS(iDelimiter != 0, User::Panic(KDelimitedParserPanicCategory, KUriUtilsErrNoDelimiter)); + + // Check to see if there is a segment left + if( iNextSegmentPos == KErrNotFound ) + { + // There is no segment + return KErrNotFound; + } + // Find the previous delimiter -> the start of the current segment + TInt prev = PrevDelimiterPosition(iDataDes, iNextSegmentPos, iDelimiter, iMode); + + // Need to see which direction the parsing is going to set the remainder + switch(iMode) + { + case EDelimitedDataForward: + { + aRemainder.Set(iDataDes.Right(iDataDes.Length() - prev)); + } break; + case EDelimitedDataReverse: + { + aRemainder.Set(iDataDes.Left(prev)); + } break; + default: + // Bad mode! + User::Panic(KDelimitedParserPanicCategory, KUriUtilsErrBadDelimitedParserMode); + break; + } + return KErrNone; + } + +/** + This parses the data into segments from left to right. + + @warning There will be a KDelimitedParserErrNoDelimiter panic if the delimiter + has not been set. + @since 6.0 + @param aData A descriptor containing the data. + @pre The delimiter must have been set. + @post The current segment is the leftmost segment and the direction of + parsing is set from left to right (EDelimitedDataFroward). + */ +EXPORT_C void TDelimitedParserBase16::Parse(const TDesC16& aData) + { + __ASSERT_ALWAYS(iDelimiter != 0, User::Panic(KDelimitedParserPanicCategory, KUriUtilsErrNoDelimiter)); + + // Initialise data for EForward direction + iMode = EDelimitedDataForward; + DoParse(aData); + } + +/** + This parses the data into segments from lright to left. + + @warning There will be a KDelimitedParserErrNoDelimiter panic if the delimiter + has not been set. + @since 6.0 + @param aData A descriptor containing the data. + @pre The delimiter must have been set. + @post The current segment is the leftmost segment and the direction of + parsing is set from right to left (EDelimitedDataReverse). + */ +EXPORT_C void TDelimitedParserBase16::ParseReverse(const TDesC16& aData) + { + __ASSERT_ALWAYS(iDelimiter != 0, User::Panic(KDelimitedParserPanicCategory, KUriUtilsErrNoDelimiter)); + + // Initialise data for EReverse direction + iMode = EDelimitedDataReverse; + DoParse(aData); + } + +/** + Sets the delimiting character. + + @since 6.0 + @param aDelimiter The delimiting character. + @post The delimiting character is set. +*/ +EXPORT_C void TDelimitedParserBase16::SetDelimiter(TChar aDelimiter) + { + iDelimiter = aDelimiter; + } + +/** + Initialises the parsing of the data. + + @since 6.0 + @param aData A descriptor reference with the data. + @pre The delimiting character has been set. + @post The data descriptor is set to the input argument. The current segment + refers to the initial segment of the data. + */ +void TDelimitedParserBase16::DoParse(const TDesC16& aData) + { + // Reset the segment information, then set the new Data - set pointer to NULL and length to zero + iCurrentSegment.Set(NULL,0); + iDataDes.Set(aData); + + // Check that there is a string! + if( iDataDes.Length() == 0 ) + { + // No string - ensure functionality blocked for this descriptor + iNextSegmentPos = KErrNotFound; + return; + } + // Find the segment - search from initial start position + iNextSegmentPos = FindNextSegment(InitialDelimiterPosition(iDataDes, iMode)); + } + +/** + Finds the next segment from the given start position. + + @since 6.0 + @param aStartPos The position from where to start the search for the + next segment. + @return The position of delimiter after the specified start position, or + an error value of KErrNotFound if no more delimiters are found. + */ +TInt TDelimitedParserBase16::FindNextSegment(TInt aStartPos) const + { + // Find position of next delimiter + TInt next = NextDelimiterPosition(iDataDes, aStartPos, iDelimiter, iMode); + + if( next != KErrNotFound ) + { + TInt startPos = next < aStartPos ? next : aStartPos; + TInt endPos = next < aStartPos ? aStartPos : next; + if( iDataDes[startPos] == iDelimiter ) + { + // Move past delimiter + ++startPos; + } + TInt length = endPos - startPos; + iCurrentSegment.Set(iDataDes.Mid(startPos, length)); + } + return next; + } + +/** + Finds the previous segment from the given start position. + + @since 6.0 + @param aStartPos The position from where to start the search for the + previous segment. + @return The position of delimiter before the specified start position, or + an error value of KErrNotFound if no more delimiters are found. +*/ +TInt TDelimitedParserBase16::FindPrevSegment(TInt aStartPos) const + { + // Find position of previous delimiter + TInt prev = PrevDelimiterPosition(iDataDes, aStartPos, iDelimiter, iMode); + + if( prev != KErrNotFound ) + { + TInt startPos = prev < aStartPos ? prev : aStartPos; + TInt endPos = prev < aStartPos ? aStartPos : prev; + if( iDataDes[startPos] == iDelimiter ) + { + // Move past delimiter + ++startPos; + } + TInt length = endPos - startPos; + iCurrentSegment.Set(iDataDes.Mid(startPos, length)); + } + return prev; + } + +// +// +// Implementation of CDelimitedDataBase16 +// +// + +/** + Destructor. + + @since 6.0 + */ +EXPORT_C CDelimitedDataBase16::~CDelimitedDataBase16() + { + delete iDataBuf; + } + +/** + Inserts the new segment in a position before the current parsed segment. The new segment can be + made up of several segments and have delimiters at either extreme. The insert functionality will + ensure that there is always a delimiter at the front of the new segment. The parser is left in a + state where its current segment is the same one as before the insertion. + + @warning There will be a KDelimitedParserErrNotParsed panic if the data has not been parsed, + and a KDelimitedParserErrNoDelimiter panic if the delimiter has not been set. + @since 6.0 + @param aSegment A descriptor with the new segment to be inserted. + @pre The data must have been initially parsed by Parse() or ParseReverse(). + @post The data will have been extended to include the new segment. The current segment + will remain as the one before the insertion. + */ +EXPORT_C void CDelimitedDataBase16::InsertCurrentL(const TDesC16& aSegment) + { + __ASSERT_ALWAYS(iParser.iMode != EDelimitedDataNotParsed, User::Panic(KDelimitedParserPanicCategory, KUriUtilsErrNotParsed)); + __ASSERT_ALWAYS(iParser.iDelimiter != 0, User::Panic(KDelimitedParserPanicCategory, KUriUtilsErrNoDelimiter)); + + DoInsertL(aSegment); + } + +/** + Removes the current segment. After removing the segment, the parser's new current segment will be the + next segment. If the last segment is the one that is removed then the parser will be set to the end of + the data. + + @warning There will be a KDelimitedParserErrNotParsed panic if the data has not been parsed, and + a KDelimitedParserErrNoDelimiter panic if the delimiter has not been set. + @since 6.0 + @pre The data must have been initially parsed by Parse() or ParseReverse(). + @post The data will have been reduced to exclude the removed segment. The current segment will + be set to what was the next segment. If the removed segment was the last segment, the parser is at the end + of the data. + */ +EXPORT_C void CDelimitedDataBase16::RemoveCurrentL() + { + __ASSERT_ALWAYS(iParser.iMode != EDelimitedDataNotParsed, User::Panic(KDelimitedParserPanicCategory, KUriUtilsErrNotParsed)); + __ASSERT_ALWAYS(iParser.iDelimiter != 0, User::Panic(KDelimitedParserPanicCategory, KUriUtilsErrNoDelimiter)); + + DoRemoveL(); + } + +/** + Adds a new segment to the end of the data. The new segment can be made up of several segments and have + delimiters at either extreme. The insert functionality will ensure that there is always a delimiter at + the front of the new segment. The data must re-parsed to ensure that the parser is valid. + + @warning There will be a KDelimitedParserErrNotParsed panic if the data has not been parsed, and a + KDelimitedParserErrNoDelimiter panic if the delimiter has not been set. A re-parse is required to ensure + that the parser is valid. + @since 6.0 + @param aSegment A descriptor with the new segment to be inserted. + @pre The delimiter must have been set. + @post The data will have been extended to include the new segment. + */ +EXPORT_C void CDelimitedDataBase16::PushBackL(const TDesC16& aSegment) + { + __ASSERT_ALWAYS(iParser.iDelimiter != 0, User::Panic(KDelimitedParserPanicCategory, KUriUtilsErrNoDelimiter)); + + // Parse the string in reverse direction - sets last segment as current + iParser.ParseReverse(*iDataBuf); + + // Insert the segment + DoInsertL(aSegment); + + // Make sure that a re-parse is required + iParser.iMode = EDelimitedDataNotParsed; + } + +/** + Removes the last segment from the data. The data must be re-parsed to ensure that the parser is valid. + + @warning There will be a KDelimitedParserErrNotParsed panic if the data has not been parsed, + and a KDelimitedParserErrNoDelimiter panic if the delimiter has not been set. A re-parse is required + to ensure that the parser is valid. + @since 6.0 + @pre The delimiter must have been set. + @post The data will have been reduced to exclude the last segment. + */ +EXPORT_C void CDelimitedDataBase16::PopBackL() + { + __ASSERT_ALWAYS(iParser.iDelimiter != 0, User::Panic(KDelimitedParserPanicCategory, KUriUtilsErrNoDelimiter)); + + // Parse the string in reverse direction - sets last segment as current + iParser.ParseReverse(*iDataBuf); + + // Remove the current segment + DoRemoveL(); + + // Make sure that a re-parse is required + iParser.iMode = EDelimitedDataNotParsed; + } + + +/** + Adds a new segment to the front of the data. The new segment can be made up of several segments and have + delimiters at either extreme. The insert functionality will ensure that there is always a delimiter at + the front of the new segment. The data must re-parsed to ensure that the parser is valid. + + @warning There will be a KDelimitedParserErrNotParsed panic if the data has not been parsed, and + a KDelimitedParserErrNoDelimiter panic if the delimiter has not been set. A re-parse is required to ensure + that the parser is valid. + @since 6.0 + @param aSegment A descriptor with the new segment to be inserted. + @pre The delimiter must have been set. + @post The data will have been extended to include the new segment. + */ +EXPORT_C void CDelimitedDataBase16::PushFrontL(const TDesC16& aSegment) + { + __ASSERT_ALWAYS(iParser.iDelimiter != 0, User::Panic(KDelimitedParserPanicCategory, KUriUtilsErrNoDelimiter)); + + // Parse the string in forward direction - sets first segment as current + iParser.Parse(*iDataBuf); + + // Insert the segment + DoInsertL(aSegment); + + // Make sure that a re-parse is required + iParser.iMode = EDelimitedDataNotParsed; + } + +/** + Removes the first segment from the data. The data must be re-parsed to ensure that the parser is valid. + + @warning There will be a KDelimitedParserErrNotParsed panic if the data has not been + parsed, and a KDelimitedParserErrNoDelimiter panic if the delimiter has not been set. A re-parse + is required to ensure that the parser is valid. + @since 6.0 + @pre The delimiter must have been set. + @post The data will have been reduced to exclude the last segment. + */ +EXPORT_C void CDelimitedDataBase16::PopFrontL() + { + __ASSERT_ALWAYS(iParser.iDelimiter != 0, User::Panic(KDelimitedParserPanicCategory, KUriUtilsErrNoDelimiter)); + + // Parse the string in forward direction - sets first segment as current + iParser.Parse(*iDataBuf); + + // Remove the current segment + DoRemoveL(); + + // Make sure that a re-parse is required + iParser.iMode = EDelimitedDataNotParsed; + } + + +/** + Removes the front delimiter (if exists) from the data. + + @warning There will be a KDelimitedParserErrNotParsed panic if the data has + not been parsed, and a KDelimitedParserErrNoDelimiter panic if the delimiter has not + been set. A re-parse is required to ensure that the parser is valid. + @since 6.0 + @pre The delimiter must have been set. + @post The data might have been reduced to exclude the front delimiter. + */ +EXPORT_C void CDelimitedDataBase16::TrimFrontDelimiterL() + { + __ASSERT_ALWAYS(iParser.iDelimiter != 0, User::Panic(KDelimitedParserPanicCategory, KUriUtilsErrNoDelimiter)); + + // Search for delimiter + if( iParser.FrontDelimiter() ) + { + // Remove front delimiter and update member data + SetDataL(iParser.iDataDes.Right(iParser.iDataDes.Length() - 1)); + } + // Make sure that a re-parse is required + iParser.iMode = EDelimitedDataNotParsed; + } + +/** + Adds a delimiter to the front of the data (if it doesn't exist). + + @warning There will be a KDelimitedParserErrNotParsed panic if the data has not + been parsed, and a KDelimitedParserErrNoDelimiter panic if the delimiter has not been set. + A re-parse is required to ensure that the parser is valid. + @since 6.0 + @pre The delimiter must have been set. + @post The data might have been extended to include a front delimiter. + */ +EXPORT_C void CDelimitedDataBase16::AddFrontDelimiterL() + { + __ASSERT_ALWAYS(iParser.iDelimiter != 0, User::Panic(KDelimitedParserPanicCategory, KUriUtilsErrNoDelimiter)); + + if( !iParser.FrontDelimiter() ) + { + // Create a new buffer of correct size + HBufC16* buf = HBufC16::NewL(iParser.iDataDes.Length() + 1); + TPtr16 str = buf->Des(); + + // Append a delimiter, then append the current string + str.Append(iParser.iDelimiter); + str.Append(iParser.iDataDes); + + // Set buffer to this new string + SetData(buf); + } + // Make sure that a re-parse is required + iParser.iMode = EDelimitedDataNotParsed; + } + +/** + Removes the back delimiter (if exists) from the data. + + @warning There will be a KDelimitedParserErrNotParsed panic if the data has + not been parsed, and a KDelimitedParserErrNoDelimiter panic if the delimiter has not + been set. A re-parse is required to ensure that the parser is valid. + @since 6.0 + @pre The delimiter must have been set. + @post The data might have been reduced to exclude the front delimiter. + */ +EXPORT_C void CDelimitedDataBase16::TrimBackDelimiterL() + { + __ASSERT_ALWAYS(iParser.iDelimiter != 0, User::Panic(KDelimitedParserPanicCategory, KUriUtilsErrNoDelimiter)); + + // Search for delimiter + if( iParser.BackDelimiter() ) + { + // Remove back delimiter and update member data + SetDataL(iParser.iDataDes.Left(iParser.iDataDes.Length() - 1)); + } + // Make sure that a re-parse is required + iParser.iMode = EDelimitedDataNotParsed; + } + +/** + Adds a delimiter to the back of the data (if it doesn't exist). + + @warning There will be a KDelimitedParserErrNotParsed panic if the data has not + been parsed, and a KDelimitedParserErrNoDelimiter panic if the delimiter has not been set. + A re-parse is required to ensure that the parser is valid. + @since 6.0 + @pre The delimiter must have been set. + @post The data might have been extended to include a front delimiter. + */ +EXPORT_C void CDelimitedDataBase16::AddBackDelimiterL() + { + __ASSERT_ALWAYS(iParser.iDelimiter != 0, User::Panic(KDelimitedParserPanicCategory, KUriUtilsErrNoDelimiter)); + + if( !iParser.BackDelimiter() ) + { + // Create a new buffer of correct size + HBufC16* buf = HBufC16::NewL(iParser.iDataDes.Length() + 1); + TPtr16 str = buf->Des(); + + // Append the current string, then append a delimiter + str.Append(iParser.iDataDes); + str.Append(iParser.iDelimiter); + + // Set buffer to this new string + SetData(buf); + } + // Make sure that a re-parse is required + iParser.iMode = EDelimitedDataNotParsed; + } + +/** + This parses the data into segments from left to right. + + @warning There will be a KDelimitedParserErrNoDelimiter panic if the delimiter + has not been set. + @since 6.0 + @pre The delimiter must have been set. + @post The current segment is the leftmost segment and the direction of + parsing is set from left to right (EDelimitedDataFroward). + */ +EXPORT_C void CDelimitedDataBase16::Parse() + { + // This call will panic with KUriUtilsErrNoDelimiter if the delimiter is not set + iParser.Parse(*iDataBuf); + } + +/** + This parses the string into segments from right to left. + + @since 6.0 + @pre The delimiter must have been set. Will get a KDelimitedParserErrNoDelimiter panic if + the delimiter has not been initialized. + @post The current segment is the leftmost segment and the direction of parsing is right to left. + */ +EXPORT_C void CDelimitedDataBase16::ParseReverse() + { + // This call will panic with KUriUtilsErrNoDelimiter if the delimiter is not set + iParser.ParseReverse(*iDataBuf); + } + +/** + Retrieves a const reference to the delimited data parser. + + @since 6.0 + @return A const reference to the delimited data parser. + */ +EXPORT_C const TDelimitedParserBase16& CDelimitedDataBase16::Parser() const + { + return iParser; + } + +/** + Sets the delimiting character. + + @since 6.0 + @param aDelimiter The delimiting character. + @post The delimiting character is updated. + */ +EXPORT_C void CDelimitedDataBase16::SetDelimiter(TChar aDelimiter) + { + iParser.SetDelimiter(aDelimiter); + } + +/** + Constructor. First phase of two-phase construction method. Does non-allocating construction. + + @since 6.0 + */ +EXPORT_C CDelimitedDataBase16::CDelimitedDataBase16() + { + } + +/** + Second phase of two-phase construction method. Does any allocations required to fully construct + the object. + + @since 6.0 + @param aData A descriptor with the initial string. + @pre First phase of construction is complete. + @post The object is fully constructed. + */ +EXPORT_C void CDelimitedDataBase16::ConstructL(const TDesC16& aData) + { + // Create copy of string and set descriptor in the parser + SetDataL(aData); + } + +/** + Inserts the new segment in a position before the current segment. The new segment can be made up of + several segments and have delimiters at either extreme. The insert functionality will ensure that + there is always a delimiter at the front of the new segment. The parser will be left in a state where + its current segment is the same one as before the insertion. + + @since 6.0 + @param aSegment The descriptor with the segment to be inserted. + @pre The string must have been parsed. + @post The string will have been extended to include the new segment. The current segment will + remain as the one before the insertion. + */ +void CDelimitedDataBase16::DoInsertL(const TDesC16& aSegment) + { + TInt prevPos = PrevDelimiterPosition(iParser.iDataDes, iParser.iNextSegmentPos, iParser.iDelimiter, iParser.iMode); + TPtrC16 prefix = iParser.iDataDes.Left(prevPos); + + TInt suffixLength = iParser.iDataDes.Length() - prevPos; + TPtrC16 suffix = iParser.iDataDes.Right(suffixLength); + if( suffixLength && suffix[0] == iParser.iDelimiter ) + { + // Remove front delimiter on suffix + suffix.Set(suffix.Right(--suffixLength)); + } + + // Check for delimiters... + TPtrC16 segment = aSegment; + TInt segmentLength = segment.Length(); + // Check the last character in segment + TBool segmentBackDelimiter = (segmentLength && segment[segmentLength - 1] == iParser.iDelimiter); + if( segmentBackDelimiter ) + { + // Remove back delimiter from the segment + segment.Set(segment.Left(--segmentLength)); + } + // Check the first character in segment... + if( segmentLength && segment[0] == iParser.iDelimiter ) + { + // Remove front delimiter from the segment + segment.Set(segment.Right(--segmentLength)); + } + + // Check if a back delimiter is needed - NOTE always add a front delimiter + TInt extra = 1; + TBool needBackDelimiter = EFalse; + if( suffix.Length() || segmentBackDelimiter ) + { + ++extra; + needBackDelimiter = ETrue; + } + // Create space for new string + HBufC16* buf = HBufC16::NewL(prevPos + segmentLength + suffixLength + extra); + TPtr16 str = buf->Des(); + + // Form the new string + str.Append(prefix); + str.Append(iParser.iDelimiter); + str.Append(segment); + if( needBackDelimiter ) + str.Append(iParser.iDelimiter); + str.Append(suffix); + + // Update string data + SetData(buf); + + // Check to see if the internal parser object (iParser) has been parsed + // (can tell if it has if the data pointer in iCurrentSegment is not NULL) + // If so update iCurrentSegment to ensure that iParser remains valid + if( iParser.iCurrentSegment.Ptr() ) + { + // Ensure parser is in correct position and current segment is correct + iParser.iNextSegmentPos = prevPos; + if( iParser.iMode == EDelimitedDataForward ) + { + // Move iterator to delimiter before iCurrentSegment - length of segment + a delimiter + iParser.iNextSegmentPos += segmentLength + 1; + } + // Get the next segment + iParser.iNextSegmentPos = iParser.FindNextSegment(iParser.iNextSegmentPos); + } + } + +/** + Removes the current segment. After removing the segment, the parser's new current segment will be the + next segment. If the last segment is the one that is removed then the parser will be set to the end of + the data. + + @since 6.0 + @pre The data must have been parsed. + @post The data will have been reduced to exclude the removed data. The current segment + is set to what was the next segment. If the removed segment was the last segment, the parser is + at the end of the data. + */ +void CDelimitedDataBase16::DoRemoveL() + { + // Check if there is anything to remove + if( iParser.iDataDes.Length() == 0 ) + { + return; + } + // Find the previous delimiter + TInt prev = PrevDelimiterPosition(iParser.iDataDes, iParser.iNextSegmentPos, iParser.iDelimiter, iParser.iMode); + + // Set up the start and end position of current segment + TInt endPos = iParser.iNextSegmentPos; + TInt startPos = iParser.iNextSegmentPos; + if( prev < iParser.iNextSegmentPos ) + startPos = prev; + else + endPos = prev; + + // Ok, get the prefix and suffix parts + TPtrC16 prefix = iParser.iDataDes.Left(startPos); + TInt suffixLength = iParser.iDataDes.Length() - endPos; + TPtrC16 suffix = iParser.iDataDes.Right(suffixLength); + + // Create the space + HBufC16* buf = HBufC16::NewL(startPos + suffixLength); + TPtr16 str = buf->Des(); + + // Form the new string + str.Append(prefix); + str.Append(suffix); + + // Update string data + SetData(buf); + + // Ensure parser is in correct position + iParser.iNextSegmentPos = iParser.FindNextSegment(startPos); + } + +/** + Updates internal data buffer with the new data. Creates a copy of the new data. + + @since 6.0 + @param aData A descriptor with the new string. + @post The internal data buffer now contains a copy of the new data and the + parser is set to the new data. + */ +void CDelimitedDataBase16::SetDataL(const TDesC16& aData) + { + // Cleanup old data and set new + HBufC16* buf = aData.AllocL(); + SetData(buf); + } + +/** + Sets internal data buffer and parser. Cleans up the old data and uses the data buffer. The + parser is set to the new data. + + @since 6.0 + @param aDataBuf A pointer to a decriptor buffer with the new data. + @post The internal data buffer now points to the new buffer and the parser + is set to the data in the new buffer.. + */ +void CDelimitedDataBase16::SetData(HBufC16* aDataBuf) + { + delete iDataBuf; + iDataBuf = aDataBuf; + iParser.iDataDes.Set(*iDataBuf); + } + +// +// +// Implementation of LOCAL functions +// +// + +/** + Finds the position of the next delimiter in the data. + + @since 6.0 + @param aData A descriptor with the delimited data. + @param aStartPos The position from where to start the search for the delimiter. + @param aDelimiter The delimiting character. + @param aMode The parsing mode. + @return The position of delimiter after the specified start position, or + an error value of KErrNotFound if no more delimiters are found. + @pre None + @post Unspecified + */ +template +TInt NextDelimiterPosition(const TDesCType& aData, TInt aStartPos, TInt aDelimiter, TDelimitedDataParseMode aMode) + { + if( aStartPos == KErrNotFound ) + { + // Have got to the end - initialise the iterator + return InitialDelimiterPosition(aData, aMode); + } + TInt next = KErrNotFound; + switch( aMode ) + { + case EDelimitedDataForward: + { + // Search parsed string for next delimiter + next = LeftDelimiterPosition(aData, aStartPos, aDelimiter); + } break; + case EDelimitedDataReverse: + { + // Search parsed string for next delimiter + next = RightDelimiterPosition(aData, aStartPos, aDelimiter); + } break; + default: + // Bad mode! + User::Panic(KDelimitedParserPanicCategory, KUriUtilsErrBadDelimitedParserMode); + break; + } + return next; + } + + +/** + Finds the position of the previous delimiter in the data from the given start position. + + @since 6.0 + @param aData A descriptor with the delimited data. + @param aStartPos The position from where to start the search for the delimiter. + @param aDelimiter The delimiting character. + @param aMode The parsing mode. + @return The position of delimiter before the specified start position, or + an error value of KErrNotFound if no more delimiters are found. + @pre None + @post Unspecified + */ +template +TInt PrevDelimiterPosition(const TDesCType& aData, TInt aStartPos, TInt aDelimiter, TDelimitedDataParseMode aMode) + { + // Switch modes, then find the next delimiter, switch back + TDelimitedDataParseMode mode = aMode == EDelimitedDataForward ? EDelimitedDataReverse : EDelimitedDataForward; + return NextDelimiterPosition(aData, aStartPos, aDelimiter, mode); + } + +/** + Finds the position of the delimiter to the right of the given start position. + + @since 6.0 + @param aData A descriptor with the delimited data. + @param aStartPos The position from where to start the search for the delimiter. + @param aDelimiter The delimiting character. + @return The position of delimiter to the right of the specified start position, or + an error value of KErrNotFound if no more delimiters are found. + @pre None + @post Unspecified + */ +template +TInt RightDelimiterPosition(const TDesCType& aData, TInt aStartPos, TInt aDelimiter) + { + // Find position of right-most delimiter in the descriptor data to left of aStartPos + if( aStartPos == 0 ) + { + // There is no data + return KErrNotFound; + } + TInt rightDelimiterPos = aData.Left(aStartPos).LocateReverse(aDelimiter); + + // See if a delimiter was found + if( rightDelimiterPos == KErrNotFound ) + { + // No - start of string delimits + rightDelimiterPos = 0; + } + return rightDelimiterPos; + } + +/** + Finds the position of the delimiter to the left of the given start position. + + @since 6.0 + @param aData A descriptor with the delimited data. + @param aStartPos The position from where to start the search for the delimiter. + @param aDelimiter The delimiting character. + @return The position of delimiter to the left of the specified start position, or + an error value of KErrNotFound if no more delimiters are found. + @pre None + @post Unspecified + */ +template +TInt LeftDelimiterPosition(const TDesCType& aData, TInt aStartPos, TInt aDelimiter) + { + // Find position of left-most delimiter in the descriptor data to right of aStartPos + const TInt length = aData.Length(); + TInt rightLength = length - aStartPos; + if( rightLength == 0 ) + { + // There is no data + return KErrNotFound; + } + // Ok there is some string to search - remove delimiter + --rightLength; + TInt leftDelimiterPos = aData.Right(rightLength).Locate(aDelimiter); + + // See if a delimiter was found + if( leftDelimiterPos == KErrNotFound ) + { + // No - end of string delimits + leftDelimiterPos = length; + } + else + { + // Offset the delimiter found - include delimiter that was removed + leftDelimiterPos += aStartPos + 1; + } + return leftDelimiterPos; + } + +/** + Retrieves the initial position for searching delimited data for a given parsing mode. + + @since 6.0 + @param aData A descriptor with the delimited data. + @param aMode The parsing mode. + @return The initial position for parsing the data. + @pre None + @post Unspecified + */ +template +TInt InitialDelimiterPosition(const TDesCType& aData, TDelimitedDataParseMode aMode) +// +// Initialises iNextSegmentPos + { + TInt initPos = KErrNotFound; + switch( aMode ) + { + case EDelimitedDataForward: + { + // Search parsed string for next delimiter + initPos = 0; + } break; + case EDelimitedDataReverse: + { + // Search parsed string for next delimiter + initPos = aData.Length(); + } break; + default: + // Bad mode! + User::Panic(KDelimitedParserPanicCategory, KUriUtilsErrBadDelimitedParserMode); + break; + } + return initPos; + } +