--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/messagingfw/msgsrvnstore/server/src/MSVARRAY.CPP Fri Jun 04 10:32:16 2010 +0100
@@ -0,0 +1,664 @@
+// Copyright (c) 1998-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 "MSVARRAY.H"
+#include "MSVUIDS.H"
+#include "MSVPANIC.H"
+#include "cmsvdescriptionarray.h"
+
+const TInt KEntryArrayGranularity=32;
+const TInt KSortingMtmListGranularity=8;
+
+
+//**********************************
+// TKeyArrayFixPtr
+//**********************************
+
+EXPORT_C TAny* TKeyArrayFixPtr::At(TInt anIndex) const
+//
+// Key class for sorting on dereferenced pointers
+//
+ {
+ if (anIndex==KIndexPtr)
+ return((TUint8 *)((*((const TUint8 **)iPtr))+iKeyOffset));
+ return((TAny *)((*((const TUint8 **)(iBase->Ptr(anIndex*iRecordLength).Ptr())))+iKeyOffset));
+ }
+
+
+//**********************************
+// CMsvEntryArray
+//**********************************
+
+EXPORT_C CMsvEntryArray* CMsvEntryArray::NewL(const CArrayFix<TUid>& aMtmList)
+ {
+ CMsvEntryArray* self = new(ELeave) CMsvEntryArray(aMtmList);
+ return self;
+ }
+
+EXPORT_C CMsvEntryArray* CMsvEntryArray::NewLC(const CArrayFix<TUid>& aMtmList)
+ {
+ CMsvEntryArray* self = new(ELeave) CMsvEntryArray(aMtmList);
+ CleanupStack::PushL(self);
+ return self;
+ }
+
+CMsvEntryArray::CMsvEntryArray(const CArrayFix<TUid>& aMtmList)
+: CArrayPtrFlat<const TMsvEntry>(KEntryArrayGranularity), iOrigMtmList(aMtmList)
+ {}
+
+
+EXPORT_C CMsvEntryArray::~CMsvEntryArray()
+ {
+ delete iActualMtmList;
+ }
+
+
+// Subject based sorting is a special case because text such as Re: , Fwd: etc is removed
+// before the sort. This means email threads are now appear in sequence.
+// The original TMsvEntry SUBJECT string is preserved as the sort takes place on a temporary
+// array of CDescription.
+// Second part of the method is a bubble sort on DATE which deals with duplicates.
+// This means that email threads will be in DATE order, oldest date first.
+void CMsvEntryArray::SubjectBasedSortL(TBool aReverse, const TDesC& aSubjectSkipString)
+ {
+ if(Count() < 2) // One or less no sort required
+ return;
+
+ // Create a temporary array of CDescription's and set the offset for the sort to iDescription
+ CMsvDescriptionArray* descriptionArray = new (ELeave) CMsvDescriptionArray();
+ CleanupStack::PushL(descriptionArray);
+ TKeyArrayFixPtr key(_FOFF(TMsvDescription,iDescription),ECmpCollated);
+ TInt count = Count(); // Use this count for both loops
+ // Loop through the class instance array and construct a CDescription array ready
+ // for the sort.
+ for(TInt i =0;i<count;++i)
+ {
+ TMsvDescription* modified = new (ELeave) TMsvDescription();
+ CleanupStack::PushL(modified);
+ modified->iEntry = const_cast<TMsvEntry*>(At(i)); // Pointer to the real TMsvEntry
+ modified->iDescription.Set(At(i)->iDescription);// Copy of its TPtrC (may be modified)
+ TPtrC ptr(modified->iDescription);
+ TInt offset(0);
+ // Perform the search and skip Re: Fwd: etc if found
+ // offset points to the start of the real subject string.
+ if((offset = FindSubjectStart(ptr,aSubjectSkipString)) != KErrNotFound)
+ {
+ ptr.Set(ptr.Ptr()+offset,ptr.Length()-offset);
+ modified->iDescription.Set(ptr);
+ }
+ descriptionArray->InsertIsqAllowDuplicatesL(modified,key); // Insert into temporary array
+ CleanupStack::Pop(modified);
+ }
+ // Reverse before we do the duplicate sort
+ if(aReverse)
+ {
+ descriptionArray->ReverseOrder();
+ }
+ // Bubble sort individual email threads if any, on DATE
+ // Achieved in a single loop by resetting current to the beginning of the email thread if
+ // we are still bubble sorting the thread.
+ // Less complicated than creating new (N email threads) x CArrayFix derived classes just
+ // for its sort methods
+ TMsvDescription** current = &descriptionArray->At(0);
+ TMsvDescription** startSort(NULL); // Beginning of an email thread
+ TBool threadSorting(EFalse); // Continue bubble sorting a thread whilst this flag is set
+ // Loop exits when sorting is false and end of list
+ for(;;)
+ {
+ ++current;
+ // IF not the end of list AND still in email thread
+ if(current <= &descriptionArray->At(count-1) && (*current)->iDescription == (*(current-1))->iDescription) // Compare current and previous
+ {
+ if(startSort == NULL) // Duplicate
+ {
+ startSort = current-1; // Keep reference to the start of the email thread.
+ }
+ if((*(current-1))->iEntry->iDate < (*current)->iEntry->iDate) // Check the DATE
+ {
+ threadSorting = ETrue; // Set this flag so we loop through this thread again
+ // Perform the swap
+ TMsvDescription* temp = *(current-1);
+ *(current-1) = *current;
+ *current = temp;
+ }
+ }
+ else if(!threadSorting)
+ {
+ // (end of thread OR end of list). Either continue to next email thread or finished the list
+ if(current >= &descriptionArray->At(count-1))
+ {
+ break; // End of list
+ }
+ else
+ {
+ startSort = NULL; // Continue to next email thread, if any.
+ }
+ }
+ else
+ {
+ // End of thread but not yet sorted.
+ // Reset the list pointer to the start of the email thread.
+ threadSorting = EFalse;
+ current=startSort;
+ }
+ }
+ // Reset the CMsvEntryArray class instance list then repopulate it with the bubble sorted one.
+ // The TMsvEntry's are pointers to the ones in the original list
+ Reset();
+ for(TInt i=0;i<descriptionArray->Count();++i)
+ {
+ AppendL(descriptionArray->At(i)->iEntry);
+ }
+ CleanupStack::PopAndDestroy(descriptionArray);
+ }
+
+// Return an offset of the real subject. Skips Re: Fwd: etc.
+// The string to skip is in the class instance iSubjectSkipString.
+// Returns a valid offset or KErrNotFound
+TInt CMsvEntryArray::FindSubjectStart(const TDesC& aSubject, const TDesC& aSubjectSkipString) const
+ {
+ // Set up temporary's for more readable code
+ // current moves along the descriptor
+ // offset is the index into the descriptor of the real subject. (Can be KErrNotFound)
+ TPtrC ptr(aSubject);
+ TInt offset(0);
+ TInt current(0);
+ TInt skipLength = aSubjectSkipString.Length();
+ do
+ {
+ current = ptr.FindF(aSubjectSkipString);
+ if(current != KErrNotFound)
+ {
+ // Found a match so increment past the skip string instance. (Could be more)
+ offset+= current + skipLength;
+ ptr.Set(aSubject.Ptr()+offset,aSubject.Length()-offset);
+ }
+ else if(offset == 0)
+ {
+ // None found
+ offset = KErrNotFound;
+ }
+ }while(current != KErrNotFound);
+ return offset;
+ }
+
+CMsvDescriptionArray::CMsvDescriptionArray() : CArrayPtrFlat<TMsvDescription>(8)
+ {
+ }
+
+CMsvDescriptionArray::~CMsvDescriptionArray()
+ {
+ ResetAndDestroy();
+ }
+
+// Reverse on CDescription array
+void CMsvDescriptionArray::ReverseOrder()
+ {
+ TInt c=Count()-1;
+ if (c>0)
+ {
+ TMsvDescription** low=&At(0);
+ TMsvDescription** high=low+c;
+ while (low<high)
+ {
+ TMsvDescription* t=*low;
+ *low++=*high;
+ *high--=t;
+ }
+ }
+ }
+
+
+
+EXPORT_C void CMsvEntryArray::SortL(TMsvSelectionOrdering aOrdering)
+//
+// sorts this array
+//
+ {
+ // If we're going to reverse the array first sort by id so sorting twice will give the same result
+ switch(aOrdering.Sorting())
+ {
+ case EMsvSortByDateReverse:
+ case EMsvSortByIdReverse:
+ case EMsvSortBySizeReverse:
+ case EMsvSortByDescriptionReverse:
+ case EMsvSortByDetailsReverse:
+ {
+ TKeyArrayFixPtr key = MessageSortKey(EMsvSortById);
+ User::LeaveIfError(Sort(key));
+ break;
+ }
+ default: // Not required - break
+ break;
+ }
+ if (Count() && (aOrdering.Sorting()!=EMsvSortByNone || aOrdering.GroupingOn()))
+ GroupL(EGroupByStandardFolders, aOrdering, (aOrdering.Sorting()!=EMsvSortByNone));
+ }
+
+void CMsvEntryArray::GroupL(TGroupCriterion aGroupCriterion,TMsvSelectionOrdering aOrdering,TBool aDoSort)
+//
+// This function works recursively, grouping and sorting the entry selection. The
+// 'sort' happens at the same time as the 1st 'group', then separate grouped arrays
+// are grouped indidvidually and then merged together at the end. The order in which
+// the grouping occurs is determined by the CMsvEntryArray::TGroupCriterion enum
+//
+ {
+ TMsvSorting sortType=aOrdering.Sorting();
+ TKeyArrayFixPtr key=MessageSortKey(sortType);
+
+ if (aGroupCriterion==EStopGrouping)
+ { // if you haven't sorted yet
+ if (aDoSort)
+ {
+ // Subject based sorting requires a special algorithm. Only message entries are treated as other entries normally
+ // do not have a prefix like e.g. "re: " or "fwd: "
+ if(At(0)->iType == KUidMsvMessageEntry && (sortType == EMsvSortByDescription || sortType == EMsvSortByDescriptionReverse))
+ {
+ SubjectBasedSortL(sortType == EMsvSortByDescriptionReverse,aOrdering.SubjectSkipString());
+ }
+ else
+ {
+ CMsvEntryArray* temp=CMsvEntryArray::NewLC(iOrigMtmList);
+ TInt count=Count();
+ if (count)
+ temp->InsertL(0,&(*(this))[0],count);
+ Reset();
+ const TMsvEntry** entry = &temp->At(0);
+ while (count--)
+ InsertIsqAllowDuplicatesL(*entry++, key); // Sorted
+ ReverseOrder(sortType);
+ CleanupStack::PopAndDestroy(); // temp
+ }
+ if (At(0)->iType == KUidMsvMessageEntry && (sortType == EMsvSortByDetails || sortType == EMsvSortByDetailsReverse))
+ {
+ DetailBasedSortL(); // Sort blocks of messages with matching details into newest first
+ }
+ }
+ else
+ {
+ // The aDoSort flag is not set, but we still need to do a subject
+ // based sort if this array contains only message entries, and we are
+ // sorting by description. Alternatively, we need to do a date based sort
+ // if this array contains only message entries and we are sorting by detail.
+ // In order to ensure the array contains only message entries, we
+ // check that we have previously grouped the entries by type which would
+ // have put all the message entries together in their own array.
+ if (Count() > 0 && At(0)->iType == KUidMsvMessageEntry && OkToGroup(EGroupByType, aOrdering))
+ {
+ if (sortType == EMsvSortByDescription || sortType == EMsvSortByDescriptionReverse)
+ {
+ SubjectBasedSortL(sortType == EMsvSortByDescriptionReverse,aOrdering.SubjectSkipString());
+ }
+ else if (sortType == EMsvSortByDetails || sortType == EMsvSortByDetailsReverse)
+ {
+ DetailBasedSortL(); // Sort blocks of messages with matching details into newest first
+ }
+ }
+ }
+ return;
+ }
+
+ if (OkToGroup(aGroupCriterion, aOrdering))
+ {
+ //
+ // Copy contents into temp and then put new grouped contents into 'this'
+ TInt count=Count();
+ if (count==0)
+ return; // nothing to do here
+ const TInt numberOfArrays=NumberOfArraysToSplitIntoL(aGroupCriterion);
+ if (numberOfArrays<1) // cannot group on this so move on to next grouping
+ {
+ GroupL(TGroupCriterion(aGroupCriterion+1), aOrdering, aDoSort);
+ return;
+ }
+ CMsvEntryArray* temp;
+ if (iActualMtmList)
+ temp = CMsvEntryArray::NewLC(*iActualMtmList);
+ else
+ temp = CMsvEntryArray::NewLC(iOrigMtmList);
+ temp->InsertL(0,&(*(this))[0],count);
+ Reset();
+
+ //
+ // create the separate arrays for each group
+ CArrayFixFlat<CMsvEntryArray*>* arrays=new(ELeave) CArrayFixFlat<CMsvEntryArray*>(numberOfArrays);
+ CleanupStack::PushL(arrays);
+ for (TInt ii=0; ii<numberOfArrays; ii++)
+ {
+ if (iActualMtmList)
+ arrays->AppendL(CMsvEntryArray::NewLC(*iActualMtmList));
+ else
+ arrays->AppendL(CMsvEntryArray::NewLC(iOrigMtmList));
+ }
+
+ //
+ // split the selection into the correct group,
+ // sorting aswell if needed and not doing standard folders
+ const TMsvEntry** entry = &temp->At(0);
+ if (!aDoSort || aGroupCriterion==EGroupByStandardFolders)
+ {
+ while (count--)
+ {
+ arrays->At(ArrayId(*entry,aGroupCriterion))->AppendL(*entry);
+ entry++;
+ }
+ }
+ else if (aGroupCriterion==EGroupByType)
+ {
+ TKeyArrayFixPtr folderKey = TKeyArrayFixPtr(_FOFF(TMsvEntry,iDetails),ECmpCollated);
+ while (count--)
+ {
+ if ((*entry)->iType==KUidMsvFolderEntry)
+ arrays->At(ArrayId(*entry, aGroupCriterion))->InsertIsqAllowDuplicatesL(*entry, folderKey);
+ else
+ arrays->At(ArrayId(*entry, aGroupCriterion))->InsertIsqAllowDuplicatesL(*entry, key);
+ entry++;
+ }
+ for (TInt jj=0; jj<numberOfArrays; jj++)
+ {
+ if (arrays->At(jj)->Count() && arrays->At(jj)->At(0)->iType!=KUidMsvFolderEntry)
+ arrays->At(jj)->ReverseOrder(sortType);
+ }
+ aDoSort=EFalse;
+ }
+ else
+ {
+ while (count--)
+ {
+ arrays->At(ArrayId(*entry, aGroupCriterion))->InsertIsqAllowDuplicatesL(*entry, key); // Sorted
+ entry++;
+ }
+ for (TInt jj=0; jj<numberOfArrays; jj++)
+ arrays->At(jj)->ReverseOrder(sortType);
+ aDoSort=EFalse;
+ }
+
+
+
+
+
+ //
+ // group further - but check that standard entries and grouped folders are not grouped anymore
+ if (aGroupCriterion==EGroupByStandardFolders)
+ {
+ __ASSERT_DEBUG(numberOfArrays==2, PanicServer(EMsvToManyGroups));
+ arrays->At(0)->GroupL(TGroupCriterion(aGroupCriterion+1), aOrdering, aDoSort);
+ }
+ else if (aGroupCriterion==EGroupByType)
+ {
+ for (TInt jj=0; jj<numberOfArrays; jj++)
+ if (arrays->At(jj)->Count() && arrays->At(jj)->At(0)->iType!=KUidMsvFolderEntry)
+ arrays->At(jj)->GroupL(TGroupCriterion(aGroupCriterion+1), aOrdering, aDoSort);
+ }
+ else
+ {
+ for (TInt jj=0; jj<numberOfArrays; jj++)
+ arrays->At(jj)->GroupL(TGroupCriterion(aGroupCriterion+1), aOrdering, aDoSort);
+ }
+
+ //
+ // merge the separate arrays into 'this'
+ for (TInt kk=0; kk<numberOfArrays; kk++)
+ {
+ count=arrays->At(kk)->Count();
+ if (count)
+ InsertL(0,&(*(arrays->At(kk)))[0],count);
+ }
+ CleanupStack::PopAndDestroy(numberOfArrays+2); // arrays contents + temp + arrays
+ }
+ else // move on to the next grouping
+ GroupL(TGroupCriterion(aGroupCriterion+1), aOrdering, aDoSort);
+ }
+
+
+TInt CMsvEntryArray::NumberOfArraysToSplitIntoL(TGroupCriterion aGroupCriterion)
+//
+// Returns the number of arrays the selection will be split into with this grouping method
+//
+ {
+ switch (aGroupCriterion)
+ {
+ case EGroupByType:
+ return 5;
+ case EGroupByStandardFolders:
+ return 2;
+ case EGroupByPriority:
+ return 3;
+ case EGroupByMtm:
+ {
+ // copy the orig mtm list
+ if (iActualMtmList==NULL)
+ iActualMtmList = new(ELeave) CArrayFixFlat<TUid>(KSortingMtmListGranularity);
+ else
+ iActualMtmList->Reset();
+
+ if (iOrigMtmList.Count())
+ iActualMtmList->InsertL(0, &iOrigMtmList.At(0), iOrigMtmList.Count());
+
+ // need to add any unknown MTM to the list
+ TKeyArrayFix key = TKeyArrayFix(0, ECmpTUint);
+ for (TInt ii=0; ii<Count(); ii++)
+ {
+ TUid uid = At(ii)->iMtm;
+ TInt temp;
+ if (iActualMtmList->Find(uid, key, temp))
+ iActualMtmList->AppendL(uid);
+ }
+ return iActualMtmList->Count();
+ }
+ default: // case EStopGrouping:
+ return 0;
+ }
+ }
+
+
+TBool CMsvEntryArray::OkToGroup(TGroupCriterion aGroupCriterion,TMsvSelectionOrdering aOrdering) const
+//
+// Returns true if the current grouping method is selected
+//
+ {
+ switch (aGroupCriterion)
+ {
+ case EGroupByType:
+ return aOrdering.GroupByType();
+ case EGroupByStandardFolders:
+ return aOrdering.GroupStandardFolders();
+ case EGroupByPriority:
+ return aOrdering.GroupByPriority();
+ case EGroupByMtm:
+ return aOrdering.GroupByMtm();
+ default: //case EStopGrouping:
+ return EFalse;
+ }
+// return EFalse;
+ }
+
+TInt CMsvEntryArray::ArrayId(const TMsvEntry* aEntry, TGroupCriterion aGroupCriterion) const
+//
+// Returns the id of the array to plave the entry into
+//
+ {
+ switch (aGroupCriterion)
+ {
+ case EGroupByType:
+ {
+ switch (aEntry->iType.iUid)
+ {
+ case KUidMsvAttachmentEntryValue:
+ return 1;
+ case KUidMsvMessageEntryValue:
+ return 2;
+ case KUidMsvFolderEntryValue:
+ return 3;
+ case KUidMsvServiceEntryValue:
+ return 4;
+ default:
+ return 0; // unknown types are grouped after attachments
+ }
+ }
+ case EGroupByPriority:
+ {
+ switch (aEntry->Priority())
+ {
+ case EMsvLowPriority:
+ return 0;
+ case EMsvMediumPriority:
+ return 1;
+ case EMsvHighPriority:
+ return 2;
+ default:
+ PanicServer(EMsvUnknownPriority);
+ }
+ }
+ case EGroupByMtm:
+ {
+ __ASSERT_DEBUG(iActualMtmList, PanicServer(EMsvMtmListNotDefined));
+ TInt index;
+ TKeyArrayFix key(0, ECmpTUint);
+ TInt error = iActualMtmList->Find(aEntry->iMtm, key, index);
+ __ASSERT_ALWAYS(error==KErrNone, PanicServer(EMsvUnknownMtm));
+ return iActualMtmList->Count() - index - 1;
+ }
+ case EGroupByStandardFolders:
+ return aEntry->StandardFolder() ? 1 : 0;
+ default:
+ return -1;
+ }
+ }
+
+
+TKeyArrayFixPtr CMsvEntryArray::MessageSortKey(TMsvSorting aSortType) const
+//
+// Return appropriate key for desired sort
+//
+ {
+ switch(aSortType)
+ {
+ case EMsvSortByDate:
+ case EMsvSortByDateReverse:
+ {
+ return TKeyArrayFixPtr(_FOFF(TMsvEntry,iDate),ECmpTInt64);
+ }
+ case EMsvSortBySize:
+ case EMsvSortBySizeReverse:
+ {
+ return TKeyArrayFixPtr(_FOFF(TMsvEntry,iSize),ECmpTUint);
+ }
+ case EMsvSortByDescription:
+ case EMsvSortByDescriptionReverse:
+ {
+ return TKeyArrayFixPtr(_FOFF(TMsvEntry,iDescription),ECmpCollated);
+ }
+ case EMsvSortByDetails:
+ case EMsvSortByDetailsReverse:
+ {
+ return TKeyArrayFixPtr(_FOFF(TMsvEntry,iDetails),ECmpCollated);
+ }
+ case EMsvSortById:
+ case EMsvSortByIdReverse:
+ {
+ return TKeyArrayFixPtr(_FOFF(TMsvEntry,iId),ECmpTInt32);
+ }
+ // case EMsvSortByNone:
+ default:
+ return TKeyArrayFixPtr(0,ECmpNormal);
+ }
+ }
+
+void CMsvEntryArray::ReverseOrder(TMsvSorting aSortType)
+//
+// Reverse based in SortBy value
+//
+ {
+ switch(aSortType)
+ {
+ case EMsvSortByDateReverse:
+ case EMsvSortByIdReverse:
+ case EMsvSortBySizeReverse:
+ case EMsvSortByDescriptionReverse:
+ case EMsvSortByDetailsReverse:
+ ReverseOrder();
+ break;
+ default: // Not required - break
+ return;
+ }
+ }
+
+void CMsvEntryArray::ReverseOrder()
+//
+// Atomic selection reverse function
+//
+ {
+ TInt c=Count()-1;
+ if (c>0)
+ {
+ const TMsvEntry** low=&At(0);
+ const TMsvEntry** high=low+c;
+ while (low<high)
+ {
+ const TMsvEntry* t=*low;
+ *low++=*high;
+ *high--=t;
+ }
+ }
+ }
+
+void CMsvEntryArray::DetailBasedSortL()
+ {
+ // Find blocks of entries with matching details
+ TInt blockStart = 0;
+
+ while (blockStart < Count())
+ {
+ const TMsvEntry* firstEntry = At(blockStart);
+ TInt nextBlock = blockStart + 1;
+
+ // Find the start of the next block
+ while (nextBlock < Count() && At(nextBlock)->iDetails == firstEntry->iDetails)
+ {
+ nextBlock++;
+ }
+ const TInt blockSize = nextBlock - blockStart;
+
+ // Sort the block by date, newest first
+ if (blockSize > 1)
+ {
+ TKeyArrayFixPtr key(_FOFF(TMsvEntry,iDate),ECmpTInt64);
+ CMsvEntryArray* temp = CMsvEntryArray::NewLC(iOrigMtmList);
+ const TMsvEntry** entry = &At(blockStart);
+
+ // Copy block entries in sequence to temporary array
+ for (TInt tempIndex = 0; tempIndex < blockSize; tempIndex++, entry++)
+ {
+ temp->InsertIsqAllowDuplicatesL(*entry, key);
+ }
+ // Reverse to give newest first
+ temp->ReverseOrder();
+
+ // Replace block with sorted entries
+ entry = &At(blockStart);
+ for (TInt tempIndex = 0; tempIndex < blockSize; tempIndex++, entry++)
+ {
+ *entry = temp->At(tempIndex);
+ }
+
+ CleanupStack::PopAndDestroy(temp);
+ }
+
+ // Move on to next block of entries
+ blockStart = nextBlock;
+ }
+ }
+