diff -r 000000000000 -r 8466d47a6819 ipsservices/ipssosplugin/src/ipsplgmsgiterator.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ipsservices/ipssosplugin/src/ipsplgmsgiterator.cpp Thu Dec 17 08:39:21 2009 +0200 @@ -0,0 +1,484 @@ +/* +* Copyright (c) 2007 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: Freestyle message iterator implementation for Symbian Message +* store +* +*/ + + +#include "emailtrace.h" +#include "ipsplgheaders.h" + +// --------------------------------------------------------------------------- +// --------------------------------------------------------------------------- +// +CIpsPlgMsgIterator* CIpsPlgMsgIterator::NewL( + CIpsPlgSosBasePlugin& aPlugin, + CMsvSession& aMsvSession, + const TFSMailMsgId& aMailboxId, + const TFSMailMsgId aFolderId, + const TFSMailDetails aDetails, + const RArray& aSorting) + { + FUNC_LOG; + CIpsPlgMsgIterator* self = + new( ELeave ) CIpsPlgMsgIterator( aPlugin, aMailboxId, aDetails, + aSorting ); + CleanupStack::PushL( self ); + self->ConstructL( aMsvSession, aFolderId, aSorting ); + CleanupStack::Pop( self ); + return self; + } + +// --------------------------------------------------------------------------- +// --------------------------------------------------------------------------- +// +CIpsPlgMsgIterator* CIpsPlgMsgIterator::NewL( + CIpsPlgSosBasePlugin& aPlugin, + CMsvEntry* aFolderEntry, + const TFSMailMsgId& aMailboxId, + const TFSMailDetails aDetails, + const RArray& aSorting) + { + FUNC_LOG; + CIpsPlgMsgIterator* self = + new( ELeave ) CIpsPlgMsgIterator( aPlugin, aMailboxId, aDetails, + aSorting ); + CleanupStack::PushL( self ); + self->ConstructL( aFolderEntry, aSorting ); + CleanupStack::Pop( self ); + return self; + } + + +// --------------------------------------------------------------------------- +// Basic destructor +// --------------------------------------------------------------------------- +// +CIpsPlgMsgIterator::~CIpsPlgMsgIterator() + { + FUNC_LOG; + delete iFolderEntry; + delete iMsgMapper; + delete iMsgSortKey; + delete iMsgSwapper; + } + +// --------------------------------------------------------------------------- +// Searches the message matching with aCurrentMessageId and requests the +// chunk of messages following the matching message +// --------------------------------------------------------------------------- +// +TBool CIpsPlgMsgIterator::NextL( + TFSMailMsgId aCurrentMessageId, + TUint aCount, + RPointerArray& aMessages) + { + FUNC_LOG; + TBool result = EFalse; + TInt baseIndex; + + // Messages are sorted always before reading the messages + Sort(); + + CMsvEntrySelection* messages = FilterMessagesL(); + CleanupStack::PushL( messages ); + + if ( !aCurrentMessageId.IsNullId() ) + { + baseIndex = messages->Find( aCurrentMessageId.Id() ); + + // aCurrentMessageId is not included to the result set + if ( baseIndex != KErrNotFound ) + { + baseIndex += 1; + } + } + else + { + // start from the beginning of the message list + baseIndex = 0; + } + + if ( ( baseIndex != KErrNotFound ) && + ( baseIndex < messages->Count() ) ) + { + result = NextL(baseIndex, messages, aCount, aMessages); + } + + CleanupStack::PopAndDestroy(messages); + return result; + } + +// --------------------------------------------------------------------------- +// Searches message matching with the search string and returns a chunk of +// messages starting from the 1st matched message +// --------------------------------------------------------------------------- +// +TBool CIpsPlgMsgIterator::NextL( + const TDesC& aStartWith, + TUint aCount, + RPointerArray& aMessages) + { + FUNC_LOG; + TBool result = EFalse; + TInt status; + TInt baseIndex; + + // Messages are sorted always before reading the messages + Sort(); + + CMsvEntrySelection* messages = FilterMessagesL(); + CleanupStack::PushL( messages ); + + status = SearchL( messages, aStartWith, baseIndex ); + + if ( status == KErrNone ) + { + result = NextL(baseIndex, messages, aCount, aMessages); + } + + CleanupStack::PopAndDestroy(messages); + return result; + } + +// --------------------------------------------------------------------------- +// Like NextL() with similar parameters, the method searches the message +// matching with aCurrentMessageId and requests a chunk of messages preceding +// the matching message +// --------------------------------------------------------------------------- +// +TBool CIpsPlgMsgIterator::PreviousL( + TFSMailMsgId aCurrentMessageId, + TUint aCount, + RPointerArray& aMessages) + { + FUNC_LOG; + TBool result = EFalse; + TInt baseIndex; + + // Messages are sorted always before reading the messages + Sort(); + + CMsvEntrySelection* messages = FilterMessagesL(); + CleanupStack::PushL(messages); + + if ( !aCurrentMessageId.IsNullId() ) + { + baseIndex = messages->Find(aCurrentMessageId.Id()); + + // aCurrentMessageId is not included to the result set + if ( baseIndex != KErrNotFound ) + { + baseIndex -= 1; + } + } + else + { + // check whether we should start from the end of + // the message list in the case of a NULL ID + baseIndex = messages->Count() - 1; + } + + // Actually, if the matching message is the first one, baseIndex is equal + // to -1 which is also the value of KErrNotFound. So, the condition could be + // simpler, but it would not inappropriate to trust that the numerical value + // of the error code is known. + if ( ( baseIndex != KErrNotFound ) && + ( baseIndex >= 0 ) ) + { + result = PreviousL(baseIndex, messages, aCount, aMessages); + } + + CleanupStack::PopAndDestroy(messages); + return result; + } + +// --------------------------------------------------------------------------- +// Searches the first message matching with the search string and requests +// a chunk of messages including the matching message and messages preceding +// it. +// The return value tells currently only, if there are more messages, but it +// is not checked whether they are deleted. It is probably more important +// to return as fast as possible than check the remaining messages. The same +// concerns PreviousL(). +// --------------------------------------------------------------------------- +// +TBool CIpsPlgMsgIterator::PreviousL( + const TDesC& aStartWith, + TUint aCount, + RPointerArray& aMessages) + { + FUNC_LOG; + TBool result = EFalse; + TInt status; + TInt baseIndex; + + // Messages are sorted always before reading the messages + Sort(); + + CMsvEntrySelection* messages = FilterMessagesL(); + CleanupStack::PushL( messages ); + + status = SearchL( messages, aStartWith, baseIndex ); + + if ( status == KErrNone ) + { + result = PreviousL(baseIndex, messages, aCount, aMessages); + } + + CleanupStack::PopAndDestroy(messages); + return result; + } + +// --------------------------------------------------------------------------- +// Loops over the requested messages noticing the ends of the message array +// --------------------------------------------------------------------------- +// +TBool CIpsPlgMsgIterator::NextL( + TInt aStart, + CMsvEntrySelection* aMessageEntries, + TUint aCount, + RPointerArray& aMessages) + { + FUNC_LOG; + TInt i = aStart; + TInt counter( 0 ); + CFSMailMessage* fsMsg; + + while (( counter < aCount ) && ( i < aMessageEntries->Count() ) ) + { + const TMsvEmailEntry& entry( + iFolderEntry->ChildDataL( aMessageEntries->At(i) ) ); + if ( ( EDisconnectedDeleteOperation != entry.DisconnectedOperation() ) && + (( entry.iMtm != KSenduiMtmImap4Uid ) || !entry.DeletedIMAP4Flag() ) && + ( entry.iType == KUidMsvMessageEntry ) ) + { + fsMsg = iMsgMapper->GetMailMessageL( iMailboxId, entry, + iRequestedDetails ); + aMessages.Append( fsMsg ); + counter++; + } + i++; + } + + return ( i < aMessageEntries->Count() ); + } + +// --------------------------------------------------------------------------- +// Loops over the requested messages noticing the ends of the message array. +// The implementation is somewhat more complex than the corresponding NextL() +// because the messages are processed in the order they are inserted to the +// result array. +// --------------------------------------------------------------------------- +// +TBool CIpsPlgMsgIterator::PreviousL( + TInt aEnd, + CMsvEntrySelection* aMessageEntries, + TUint aCount, + RPointerArray& aMessages) + { + FUNC_LOG; + __ASSERT_DEBUG( ( aEnd < aMessageEntries->Count() ), + User::Panic( KIpsPlgPanicCategory, EIpsPlgInvalidMessageIndex ) ); + + TInt i = aEnd; + TInt counter( 0 ); + CFSMailMessage* fsMsg; + + while (( counter < aCount ) && ( i >= 0 ) ) + { + const TMsvEmailEntry& entry( + iFolderEntry->ChildDataL( aMessageEntries->At(i) ) ); + if ( ( EDisconnectedDeleteOperation != entry.DisconnectedOperation() ) && + (( entry.iMtm != KSenduiMtmImap4Uid ) || !entry.DeletedIMAP4Flag() ) && + ( entry.iType == KUidMsvMessageEntry ) ) + { + fsMsg = iMsgMapper->GetMailMessageL( iMailboxId, entry, + iRequestedDetails ); + aMessages.Insert( fsMsg, 0 ); + counter++; + } + i--; + } + + return ( i > 0 ); + } + +// --------------------------------------------------------------------------- +// Constructor. Initializes the static data members. +// --------------------------------------------------------------------------- +// +CIpsPlgMsgIterator::CIpsPlgMsgIterator( + CIpsPlgSosBasePlugin& aPlugin, + const TFSMailMsgId& aMailboxId, + const TFSMailDetails aDetails, + const RArray& aSorting ) + : iPlugin( aPlugin ), iRequestedDetails( aDetails ), + iSortingCriteria( aSorting ), iMailboxId( aMailboxId ) + { + FUNC_LOG; + // none + } + +// --------------------------------------------------------------------------- +// Second level constructor. Creates the data members to the heap. +// Creates also the folder CMsvEntry. +// --------------------------------------------------------------------------- +// +void CIpsPlgMsgIterator::ConstructL( + CMsvSession& aMsvSession, + const TFSMailMsgId aFolderId, + const RArray& aSorting ) + { + FUNC_LOG; + iFolderEntry = aMsvSession.GetEntryL( aFolderId.Id() ); + iMsgSortKey = new (ELeave) TIpsPlgMsgKey( *iFolderEntry, aSorting ); + iMsgSwapper = new (ELeave) TIpsPlgMsgSwap( *iFolderEntry ); + iSortingOn = + ( aSorting.Count() > 0 ) && ( aSorting[0].iField != EFSMailDontCare); + iMsgMapper = CIpsPlgMsgMapper::NewL( aMsvSession, iPlugin ); + iMsvSession = &aMsvSession; + } + +// --------------------------------------------------------------------------- +// Second level constructor. Creates the data members to the heap. +// Version which gets the folder CMsvEntry as a parameter. +// --------------------------------------------------------------------------- +// +void CIpsPlgMsgIterator::ConstructL( + CMsvEntry* aFolderEntry, + const RArray& aSorting ) + { + FUNC_LOG; + iFolderEntry = aFolderEntry; + iMsgSortKey = new (ELeave) TIpsPlgMsgKey( *iFolderEntry, aSorting ); + iMsgSwapper = new (ELeave) TIpsPlgMsgSwap( *iFolderEntry ); + iSortingOn = + ( aSorting.Count() > 0 ) && ( aSorting[0].iField != EFSMailDontCare); + iMsgMapper = CIpsPlgMsgMapper::NewL( aFolderEntry->Session(), iPlugin ); + iMsvSession = NULL; + } + +// --------------------------------------------------------------------------- +// Sorting is based on the quick sort algorithm implemented in User module +// --------------------------------------------------------------------------- + +void CIpsPlgMsgIterator::Sort() + { + FUNC_LOG; + if ( iSortingOn ) + { + User::QuickSort( iFolderEntry->Count(), *iMsgSortKey, *iMsgSwapper ); + } + } + +// --------------------------------------------------------------------------- +// Searches the first message matching the search string. +// This function is supported only when the messages are sorted by the subject +// or by the sender (or the receiver in Drafts/Outbox/Sent folders) as +// a primary sorting key. The fields to be compared is selected on the basis +// of the sorting key. +// --------------------------------------------------------------------------- + +TInt CIpsPlgMsgIterator::SearchL( + CMsvEntrySelection* aMessageEntries, + const TDesC& aStartWith, + TInt& aIndex ) + { + FUNC_LOG; + TInt result( KErrNotFound ); + TInt i; + TInt findStatus; + TInt offset; + TFSMailSortField primaryKey = EFSMailDontCare; + TPtrC subject; + + // Check whether the search is supported + if ( iSortingCriteria.Count() > 0 ) + { + primaryKey = iSortingCriteria[0].iField; + } + if ( !iSortingOn || + ( ( primaryKey != EFSMailSortBySender ) && + ( primaryKey != EFSMailSortBySubject ) ) ) + { + result = KErrNotSupported; + } + + // Actual search + i = 0; + while ( ( result == KErrNotFound ) && ( i < aMessageEntries->Count() ) ) + { + const TMsvEntry& entry( + iFolderEntry->ChildDataL( aMessageEntries->At(i) ) ); + + findStatus = KErrNotFound; + + if ( primaryKey == EFSMailSortBySender ) + { + findStatus = entry.iDetails.FindF( aStartWith ); + } + if ( primaryKey == EFSMailSortBySubject ) + { + // Strip the prefixes (Re:, Fwd: etc) from the subject before + // matching + offset = iMsgSortKey->FindSubjectStart( entry.iDescription ); + subject.Set( + entry.iDescription.Ptr() + offset, + entry.iDescription.Length() - offset ); + findStatus = subject.FindF( aStartWith ); + } + + // Checks whether a matching message has been found + if ( findStatus == 0 ) + { + result = KErrNone; + aIndex = i; + } + + i++; + } + + return result; + } +// --------------------------------------------------------------------------- +// Filters messages in global folders (Drafts/Sent/Outbox) that belongs to +// specified mailbox. When messages in inbox asked, returns selection from +// wanted mailbox. +// --------------------------------------------------------------------------- +CMsvEntrySelection* CIpsPlgMsgIterator::FilterMessagesL() + { + FUNC_LOG; + TMsvId serviceId; + TMsvEntry tEntry; + TMsvEntry mailboxServiceEntry; + CMsvEntrySelection* filteredEntries; + + if( iMsvSession && iFolderEntry->Entry().Parent() == KMsvLocalServiceIndexEntryIdValue ) + { + User::LeaveIfError( + iMsvSession->GetEntry( iMailboxId.Id(), serviceId, tEntry ) ); + User::LeaveIfError( + iMsvSession->GetEntry( tEntry.iRelatedId, serviceId, mailboxServiceEntry ) ); + + filteredEntries = iFolderEntry->ChildrenWithServiceL( serviceId ); + } + else + { + filteredEntries = iFolderEntry->ChildrenWithServiceL( iMailboxId.Id() ); + } + return filteredEntries; + } +