--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/messagingfw/msgsrvnstore/server/src/msvcachevisiblefolder.Cpp Fri Jun 04 10:32:16 2010 +0100
@@ -0,0 +1,1207 @@
+// Copyright (c) 2007-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:
+// CMSVCACHEVISIBLEFOLDER.CPP
+//
+//
+
+#include "msvcachevisiblefolder.h"
+#include "msventryfreepool.h"
+#include "msvcacheindextableentry.h"
+#include "msvdbadapter.h"
+#include "msvcacheentry.h"
+
+#if (defined SYMBIAN_MSGS_ENHANCED_REMOVABLE_MEDIA_SUPPORT)
+ #include "msvindexadapter.h"
+#endif
+
+#define BLOCK_SIZE 64
+#define BLOCK_THRESHOLD 16
+
+/**
+Literal Definition
+*/
+
+_LIT8(KId, "id ");
+_LIT8(KBetween, "between ");
+_LIT8(KAnd, " and ");
+_LIT8(KOr, " or ");
+_LIT8(KIn, " in ");
+_LIT8(KRtBrace, ") ");
+_LIT8(KLtBrace, "( ");
+_LIT8(KComma, ", ");
+_LIT8(KSemiColon, ";");
+_LIT8(KOrder, " order by id ASC");
+
+
+
+/**
+CMsvCacheVisibleFolder
+*/
+const TInt CMsvCacheVisibleFolder::iOffset = _FOFF(CMsvCacheVisibleFolder, iDlink);
+
+
+/**
+ NewL()
+@param TMsvId: Visible Folder TMsvId.
+@return The newly created Visible Folder.
+
+It returns an instance of CMsvCacheVisibleFolder class.
+*/
+CMsvCacheVisibleFolder* CMsvCacheVisibleFolder::NewL(TMsvId aId)
+ {
+ CMsvCacheVisibleFolder *self = new(ELeave) CMsvCacheVisibleFolder(aId);
+ return self;
+ }
+
+
+/**
+ NewL()
+@param TMsvId: Visible Folder TMsvId.
+@param RPointerArray<CMsvCacheEntry>: Rpointer Reference to children Entries
+@return The newly created Visible Folder.
+ It returns an instance of CMsvCacheVisibleFolder class.
+*/
+CMsvCacheVisibleFolder* CMsvCacheVisibleFolder::NewL(TMsvId aId, RPointerArray<CMsvCacheEntry>& aEntries)
+ {
+ CMsvCacheVisibleFolder *self = new(ELeave) CMsvCacheVisibleFolder(aId);
+ CleanupStack::PushL(self);
+ self->ConstructL(aEntries);
+ CleanupStack::Pop();
+ return self;
+ }
+
+
+/**
+ ~CMsvCacheVisibleFolder()
+@param None.
+@return None.
+ Destructor for the CMsvCacheVisibleFolder class.
+*/
+CMsvCacheVisibleFolder::~CMsvCacheVisibleFolder()
+ {
+ iIndexTable.ResetAndDestroy();
+ iIndexTable.Close();
+ }
+
+
+/**
+ CMsvCacheVisibleFolder()
+@param None.
+@return None.
+ Constructor for the CMsvCacheVisibleFolder class.
+*/
+CMsvCacheVisibleFolder::CMsvCacheVisibleFolder(TMsvId aId)
+:iFlags(EMsvCacheVisibleFolderClearFlag)
+ {
+#if (defined SYMBIAN_MSGS_ENHANCED_REMOVABLE_MEDIA_SUPPORT)
+ // Keep standard id unmasked.
+ if(IsStandardId(aId))
+ {
+ iVisibleFolderId = UnmaskTMsvId(aId);
+ }
+ else
+ {
+ iVisibleFolderId = aId;
+ }
+ // Store drive id information.
+ iDriveId = GetDriveId(aId);
+#else
+ iVisibleFolderId = aId;
+#endif
+ }
+
+
+/**
+ ConstructL()
+@param RPointerArray<CMsvCacheEntry>& aEntries.
+@return None.
+ Two Phase constructor for adding the entries passed during the creation
+ of the CMsvCacheVisibleFolder object.
+*/
+void CMsvCacheVisibleFolder::ConstructL(RPointerArray<CMsvCacheEntry>& aEntries)
+ {
+ AddEntryListL(aEntries, ETrue);
+ }
+
+
+/**
+ AddEntryL()
+@param CMsvCacheEntry&: CMsvCacheEntry Reference.
+@return none.
+
+The function Adds an Entry into the cache block
+01. check if the parent id of the entry exists; Incase if it is a child of a non-visible folder
+ we cannot add it unless the parent is present
+02. Check if The Parent Id of the Entry is same as the visible folder id.
+02.1. If yes, Set the Flag to Update the ChildMsvId array of the parent.
+03. If there is no Index Table created, Create a new index table entry
+ and add the entry into the table
+04. Iterate the Indextable entries and add the entry in the appropriate
+ IndexTable entry Block and update the child msvid array of the parent
+ If the entry is not an immediate child.
+05. In case index table is not yet created, then there is a possibility that entry Id does not fall in
+ the first block range but it can be added before the first block. In such case, add the entry in the first
+ block itself and update the first block range.
+06. If the Entry doesnt fall in the range of any of the blocks add the Entry
+ in the last block and update the child MsvId array if the entry is not an immediate child.
+*/
+void CMsvCacheVisibleFolder::AddEntryL(CMsvCacheEntry*& aEntry, TBool aReplace /* DEFAULT = EFalse */)
+ {
+ TBool updateChild=EFalse;
+ TInt noOfIndexTableEntries = iIndexTable.Count();
+ TMsvId entryId = aEntry->Entry().Id();
+ TMsvId parentId = aEntry->Entry().Parent();
+
+
+ // 02. Check if The Parent Id of the Entry is same as the visible folder id.
+ if(parentId != iVisibleFolderId)
+ {
+ // 02.1. If yes, Set the Flag to Update the ChildMsvId array of the parent.
+ updateChild = ETrue;
+ }
+
+ // 03. If there is no Index Table created, Create a new index table entry
+ // and add the entry into the table
+ if(NULL == noOfIndexTableEntries )
+ {
+ CMsvCacheIndexTableEntry* tableEntry = CMsvCacheIndexTableEntry::NewLC(aEntry);
+ tableEntry->SetMinMsvIdRange(aEntry->GetId());
+ tableEntry->SetMaxMsvIdRange(aEntry->GetId());
+ if(aEntry->Entry().Parent() != iVisibleFolderId)
+ {
+ tableEntry->SetGrandChildPresent();
+ }
+ iIndexTable.AppendL(tableEntry);
+ CleanupStack::Pop();
+ return;
+ }
+
+ // 04. Iterate the Indextable entries and add the entry in the appropriate
+ // IndexTable entry Block and update the child msvid array of the parent
+ // If the entry is not an immediate child.
+ for(TInt index=0; index < noOfIndexTableEntries; ++index)
+ {
+ if(iIndexTable[index]->IsInRange(entryId))
+ {
+ iIndexTable[index]->AddEntryL(aEntry, aReplace);
+ if(aEntry->Entry().Parent() != iVisibleFolderId)
+ {
+ iIndexTable[index]->SetGrandChildPresent();
+ }
+ if(updateChild)
+ {
+ UpdateChildMsvIdsL(parentId, entryId);
+ }
+ return;
+ }
+ //05. In case index table is not yet created, then there is a possibility that entry Id does not fall in
+ // the first block range but it can be added before the first block. In such case, add the entry in the first
+ // block itself and update the first block range.
+ if((NULL == index) && (entryId < iIndexTable[index]->GetMinMsvIdRange()))
+ {
+ iIndexTable[index]->AddEntryL(aEntry, aReplace);
+ if(aEntry->Entry().Parent() != iVisibleFolderId)
+ {
+ iIndexTable[index]->SetGrandChildPresent();
+ }
+ if(updateChild)
+ {
+ UpdateChildMsvIdsL(parentId, entryId);
+ }
+ return;
+ }
+ }
+
+ // 06. If the Entry doesnt fall in the range of any of the blocks add the Entry
+ // in the last block and update the child MsvId array if the entry is not an immediate child.
+ iIndexTable[noOfIndexTableEntries-1]->AddEntryL(aEntry, aReplace);
+ if(aEntry->Entry().Parent() != iVisibleFolderId)
+ {
+ iIndexTable[noOfIndexTableEntries-1]->SetGrandChildPresent();
+ }
+ if(entryId > iIndexTable[noOfIndexTableEntries-1]->GetMaxMsvIdRange())
+ {
+ iIndexTable[noOfIndexTableEntries-1]->SetMaxMsvIdRange(entryId);
+ }
+ if(updateChild)
+ {
+ UpdateChildMsvIdsL(parentId, entryId);
+ }
+ }
+
+
+/**
+ AddEntryListL()
+@param RPointerArray<CMsvCacheEntry>&: Entries to be added under this visible folder which are in sorted order.
+@param TBool: Set to true if the passed array of CMsvCacheEntry contains the complete childrens of VisibleFolder
+@return none.
+
+The function Adds a set of Entries into the cache
+1. If entry list is NULL, return to the caller.
+2.check if the IndexTable is not created then,
+2.1 Yes, create the required number of index tables and append the entries in the appropriate blocks
+2.2 No, Check if getchildren has already been performed on this VisibleFolder
+2.2.1 Yes Assuming the input list is already sorted, Add the entries in the appropriate
+ blocks depending on the range of each blocks
+2.2.1.1 The remaining entries should be put in the last block
+2.3 No, check if getchildren has'nt been performed on this VisibleFolder
+2.3.1 Check If index table exists
+2.3.1.1 Yes,Create a Copy of the existing index table entries
+2.3.1.2 create the required number of index tables and append the entries in the appropriate blocks
+2.3.1.3 check the copy of the previous list if any entry exists
+2.3.1.3 copy back only the locked entries or entires
+ which are not immediate childrens of the visible folder or
+ entries which are locked and release the remaining entries
+2.3.2 If indextable is not present add the entries in the appropriate blocks
+*/
+void CMsvCacheVisibleFolder::AddEntryListL(RPointerArray<CMsvCacheEntry>& aEntries, TBool aIsCompleteChildOfFolder /*DEFAULT = EFalse */)
+ {
+ //1. If entry list is NULL, return to the caller.
+ TInt size = aEntries.Count();
+ if(size <= 0)
+ {
+ if(!size && aIsCompleteChildOfFolder)
+ {
+ SetComplete(ETrue);
+ }
+ return;
+ }
+
+ TBool isGrandChildrenAdded = EFalse;
+ if(aEntries[0]->Entry().Parent() != iVisibleFolderId)
+ {
+ isGrandChildrenAdded = ETrue;
+ }
+
+
+ //2.check if the IndexTable is not created then,
+ if(0 == iIndexTable.Count())
+ {
+ //2.1 Yes, create the required number of index tables and append the entries in the appropriate blocks
+ SplitAndAppendL(aEntries);
+ }
+ else
+ {
+ //2.2 No, Check if getchildren has already been performed on this VisibleFolder
+ if(IsComplete())
+ {
+ TInt tmpIndex = 0, index = 0;
+ //2.2.1 Yes Assuming the input list is already sorted, Add the entries in the appropriate
+ // blocks depending on the range of each blocks
+ for(TInt tableIndex = 0; tableIndex < iIndexTable.Count(); ++tableIndex)
+ {
+ while((tmpIndex < size) && (iIndexTable[tableIndex]->GetMaxMsvIdRange() >= aEntries[tmpIndex]->GetId()))
+ {
+ ++tmpIndex;
+ }
+ if(index != tmpIndex)
+ {
+ // No need to set Max/Min range, as IsComplete() is true.
+ iIndexTable[tableIndex]->AddEntrySetL(aEntries, index, tmpIndex-index);
+ if(isGrandChildrenAdded)
+ {
+ iIndexTable[tableIndex]->SetGrandChildPresent();
+ }
+ }
+
+ if(aIsCompleteChildOfFolder)
+ {
+ iIndexTable[tableIndex]->ClearDirty();
+ }
+
+ if( tmpIndex >= size )
+ {
+ break;
+ }
+ index = tmpIndex;
+
+ }
+
+ //2.2.1.1 The remaining entries should be put in the last block
+ tmpIndex = size - tmpIndex; // Amount of entries remaining.
+ if(tmpIndex > BLOCK_THRESHOLD)
+ {
+ SplitAndAppendL(aEntries, index);
+ }
+ else if(0 != tmpIndex)
+ {
+ TMsvId maxId = aEntries[size-1]->GetId();
+ iIndexTable[iIndexTable.Count()-1]->AddEntrySetL(aEntries, index, tmpIndex);
+ if(isGrandChildrenAdded)
+ {
+ iIndexTable[iIndexTable.Count()-1]->SetGrandChildPresent();
+ }
+ iIndexTable[iIndexTable.Count()-1]->SetMaxMsvIdRange(maxId);
+ }
+ }
+ //2.3 No, check if getchildren has'nt been performed on this VisibleFolder
+ else
+ {
+ //2.3.1 Check If index table exists
+ if(aIsCompleteChildOfFolder)
+ {
+ // 2.3.1.1 Yes,Create a Copy of the existing index table entries
+ RPointerArray<CMsvCacheIndexTableEntry> tmpTable;
+ CleanupClosePushL(tmpTable);
+ for(TInt index=0; index<iIndexTable.Count(); index++)
+ {
+ tmpTable.AppendL(iIndexTable[index]);
+ }
+ iIndexTable.Reset();
+ // 2.3.1.2 create the required number of index tables and append the entries in the appropriate blocks
+ SplitAndAppendL(aEntries);
+ // 2.3.1.3 check the copy of the previous list if any entry exists
+ while(tmpTable.Count())
+ {
+ if(tmpTable[0]->BlockPtr() != NULL)
+ {
+ RPointerArray<CMsvCacheEntry>* blockPtr = tmpTable[0]->BlockPtr();
+ TInt count = blockPtr->Count();
+ while(count--)
+ {
+ // 2.3.1.3 copy back only the locked entries or entires
+ //which are not immediate childrens of the visible folder or
+ //entries which are locked and release the remaining entries
+ if( ((*blockPtr)[count]->Entry().Parent() != iVisibleFolderId) || !((*blockPtr)[count]->IsEntrySwappable()) )
+ {
+ AddEntryL((*blockPtr)[count], ETrue);
+ }
+ else
+ {
+ CMsvEntryFreePool::Instance()->ReleaseEntry((*blockPtr)[count]);
+ }
+ }
+ blockPtr->Reset();
+ blockPtr->Close();
+ }
+ delete tmpTable[0];
+ tmpTable.Remove(0);
+ }
+ tmpTable.Reset();
+ CleanupStack::PopAndDestroy();
+ }
+ //2.3.2 If indextable is not present add the entries in the appropriate blocks
+ else
+ {
+ TInt size = aEntries.Count();
+ while(size--)
+ {
+ AddEntryL(aEntries[size]);
+ }
+ }
+ }
+ }
+
+ if(aIsCompleteChildOfFolder)
+ {
+ SetComplete(ETrue);
+ }
+ }
+
+
+/**
+ SplitAndAppendL()
+@param RPointerArray<CMsvCacheEntry>&: Entries to be added under this visible folder which are in sorted order.
+@param TInt: Maximum range of the last block under this visible folder
+@return none.
+
+The function Adds a set Entries into the cache
+1. Calculate the number of blocks to be created.
+2. If remaining entries are less then BLOCK_THRESHOLD they will be appended to last block.
+3. Create new indextable entries and then add the child entries into the appropriate blocks
+4. If few entries are still left, append them to the last block.
+*/
+void CMsvCacheVisibleFolder::SplitAndAppendL(RPointerArray<CMsvCacheEntry>& aEntryList, TInt aInitIndex /*(DEFAULT=0)*/)
+ {
+ //1. Calculate the number of blocks to be created.
+ TInt entriesToBeAdded = aEntryList.Count() - aInitIndex;
+ TInt blocksToCreate = entriesToBeAdded /(TInt) BLOCK_SIZE;
+ TInt aLastMaxRange = -1;
+ TBool isGrandChildrenAdded = EFalse;
+
+ TInt blocksCreated = iIndexTable.Count();
+ if(blocksCreated != 0)
+ {
+ aLastMaxRange = iIndexTable[blocksCreated-1]->GetMaxMsvIdRange();
+ }
+ if( aEntryList[aInitIndex]->Entry().Parent() != iVisibleFolderId)
+ {
+ isGrandChildrenAdded = ETrue;
+ }
+
+ //2. If remaining entries are less then BLOCK_THRESHOLD they will be appended to last block.
+ if( (BLOCK_THRESHOLD < (entriesToBeAdded % BLOCK_SIZE)) ||
+ ( (NULL == (blocksCreated+blocksToCreate)) && (entriesToBeAdded <= BLOCK_THRESHOLD) )
+ )
+ {
+ ++ blocksToCreate;
+ }
+
+ TInt blockSize = entriesToBeAdded/blocksToCreate;
+
+ //3. Create new indextable entries and then add the child entries into the appropriate blocks
+ for(TInt index = 0; index < blocksToCreate; ++index)
+ {
+ CMsvCacheIndexTableEntry* newTableEntry = CMsvCacheIndexTableEntry::NewLC(aEntryList, aInitIndex, blockSize);
+ if(isGrandChildrenAdded)
+ {
+ newTableEntry->SetGrandChildPresent();
+ }
+ TMsvId maxRange = aEntryList[aInitIndex+blockSize-1]->GetId();
+ newTableEntry->SetMinMsvIdRange(++aLastMaxRange);
+ newTableEntry->SetMaxMsvIdRange(maxRange);
+ iIndexTable.AppendL(newTableEntry);
+ aLastMaxRange = maxRange;
+ aInitIndex += blockSize;
+
+ CleanupStack::Pop();
+ }
+
+ //4. If few entries are still left, append them to the last block.
+ entriesToBeAdded = aEntryList.Count() - aInitIndex;
+ if(entriesToBeAdded > 0)
+ {
+ CMsvCacheIndexTableEntry* newTableEntry = iIndexTable[iIndexTable.Count()-1];
+ newTableEntry->AddEntrySetL(aEntryList, aInitIndex, entriesToBeAdded);
+ if(isGrandChildrenAdded)
+ {
+ newTableEntry->SetGrandChildPresent();
+ }
+ if(newTableEntry->GetMaxMsvIdRange() < aEntryList[aEntryList.Count()-1]->GetId())
+ {
+ newTableEntry->SetMaxMsvIdRange(aEntryList[aEntryList.Count()-1]->GetId());
+ }
+ }
+ }
+
+
+
+/**
+ GetEntry()
+@param TMsvId: TMsvId for the Entry to be fetched
+@param CMsvCacheEntry&: CMsvCacheEntry Reference.
+@return TBool: Cache Hit or Cache Miss.
+
+The function Gets an Entry from the cache block
+1. Find the IndexTable entry where the Range of the TMsvId falls.
+2. Get the Entry in the Block to which it belongs
+*/
+TBool CMsvCacheVisibleFolder::GetEntry(TMsvId aId, CMsvCacheEntry*& aEntry)
+ {
+ // 1. Find the IndexTable entry where the Range of the TMsvId falls.
+ for(TInt index=0; index < iIndexTable.Count(); ++index)
+ {
+ if(iIndexTable[index]->IsInRange(aId))
+ {
+ //2. Get the Entry in the Block to which it belongs
+ return(iIndexTable[index]->GetEntry(aId, aEntry));
+ }
+ }
+ return EFalse;
+ }
+
+
+/**
+ GetChildren()
+@param TMsvId: TMsvId of the Parent Entry whose children are to be fetched.
+@param RPointerArray<const CMsvCacheEntry>&: Array to fill childrens
+@return TBool: Cache Hit or Cache Miss.
+
+The function Gets the childrens of the parent from the cache
+1. Check if the Parent Id is same as the Visible Folder
+1.1 yes, check if getchildren has'nt already been performed on this visible folder entry
+ or all the blocks present are dirty.
+1.1.1 Then Fetch and fill all the childrens then return the same to the caller
+1.2 yes, check if getchildren has beeb performed on this visible folder
+1.2.1 yes, only few blocks are dirty, so fetch and fill the dirty blocks
+2. If the if the Parent Id is not same as the Visible Folder
+2.1 check if the parent of the children is present
+2.2 If parent is a visible folder
+2.2.1 Yes, then return Efalse to the caller stating that
+the parent is a visible folder
+2.3 Check if Getchildren has'nt been performed on the parent
+2.3.1 Yes, Fetch and fill the childrens of the Parent from DB
+2.4 Check If GetChildren has been already performed
+2.4.1 If childrens are available in the cache get them
+2.4.2 else fetch the children from DB add them to cache
+*/
+TBool CMsvCacheVisibleFolder::GetChildrenL(TMsvId aId, CMsvDBAdapter* aDbAdapter, RPointerArray<CMsvCacheEntry>& aEntries)
+ {
+ SetGetChildrenFromVisibleFolder(ETrue);
+ //1. Check if the Parent Id is same as the Visible Folder
+ TMsvId id = aId;
+#if (defined SYMBIAN_MSGS_ENHANCED_REMOVABLE_MEDIA_SUPPORT)
+ if(IsStandardId(aId))
+ {
+ id = UnmaskTMsvId(aId);
+ }
+#endif
+ if(id == iVisibleFolderId)
+ {
+ //1.1 yes, check if getchildren has'nt already been performed on this visible folder entry
+ // or all the blocks present are dirty.
+ if( (!IsComplete()) || (IsComplete() && IsAllBlocksDirty()) )
+ {
+ //1.1.1 Then Fetch and fill all the childrens then return the same to the caller
+ aDbAdapter->GetChildrenL(id, aEntries);
+ TInt excessEntries = CMsvEntryFreePool::Instance()->ExcessMemoryAllocated();
+ if(excessEntries)
+ {
+ TInt index = 0;
+ RPointerArray<CMsvCacheEntry> entryList;
+ CleanupClosePushL(entryList);
+ for(; index<excessEntries; index++)
+ {
+ CMsvEntryFreePool::Instance()->RecordExcessMemoryL(aEntries[index]);
+ }
+ for(; index<aEntries.Count(); index++)
+ {
+ entryList.AppendL(aEntries[index]);
+ }
+ AddEntryListL(entryList, EFalse);
+ CleanupStack::PopAndDestroy();
+ }
+ else
+ {
+ AddEntryListL(aEntries, ETrue);
+ }
+ SetGetChildrenFromVisibleFolder(EFalse);
+ return ETrue;
+ }
+ //1.2 yes, check if getchildren has beeb performed on this visible folder
+ if(IsComplete())
+ {
+ //1.2.1 yes, only few blocks are dirty, so fetch and fill the dirty blocks
+ RBuf8 buf;
+ buf.Create(2000);
+ CleanupClosePushL(buf);
+ TBool isDBOperationReqd = EFalse;
+ for(TInt index=0; index < iIndexTable.Count(); ++index)
+ {
+ if(iIndexTable[index]->IsDirty())
+ {
+ if(!isDBOperationReqd)
+ {
+ buf.Append(KAnd);
+ buf.Append(KLtBrace);
+ isDBOperationReqd = ETrue;
+ }
+ else
+ {
+ buf.Append(KOr);
+ }
+ buf.Append(KId);
+ buf.Append(KBetween);
+#if (defined SYMBIAN_MSGS_ENHANCED_REMOVABLE_MEDIA_SUPPORT)
+ buf.AppendNum(UnmaskTMsvId(iIndexTable[index]->GetMinMsvIdRange()));
+ buf.Append(KAnd);
+ buf.AppendNum(UnmaskTMsvId(iIndexTable[index]->GetMaxMsvIdRange()));
+#else
+ buf.AppendNum(iIndexTable[index]->GetMinMsvIdRange());
+ buf.Append(KAnd);
+ buf.AppendNum(iIndexTable[index]->GetMaxMsvIdRange());
+#endif // #if (defined SYMBIAN_MSGS_ENHANCED_REMOVABLE_MEDIA_SUPPORT)
+ }
+ }
+ if(isDBOperationReqd)
+ {
+ buf.Append(KRtBrace);
+ buf.Append(KOrder);
+ buf.Append(KSemiColon);
+ RPointerArray<CMsvCacheEntry> childEntries;
+ CleanupClosePushL(childEntries);
+ aDbAdapter->GetChildrenL(buf, id, childEntries);
+ AddEntryListL(childEntries);
+ CleanupStack::PopAndDestroy();
+ }
+ for(TInt index=0; index < iIndexTable.Count(); ++index)
+ {
+ iIndexTable[index]->GetChildrenL(id, aEntries);
+ iIndexTable[index]->ClearDirty();
+ }
+ CleanupStack::PopAndDestroy();
+ SetGetChildrenFromVisibleFolder(EFalse);
+ return ETrue;
+ }
+ }
+ //2. If the if the Parent Id is not same as the Visible Folder
+ else
+ {
+ CMsvCacheEntry* parentEntry;
+ //2.1 check if the parent of the children is present
+ if(!GetEntry(id, parentEntry))
+ {
+ User::Leave(KErrNotFound);
+ }
+
+ //2.2 If parent is a visible folder
+ if(parentEntry->Entry().VisibleFolderFlag())
+ {
+ //2.2.1 Yes, then return Efalse to the caller stating that
+ // the parent is a visible folder
+ SetGetChildrenFromVisibleFolder(EFalse);
+ return EFalse;
+ }
+
+ //2.3 Check if Getchildren hasn't been performed on the parent
+ if(NULL == parentEntry->ChildIdArray())
+ {
+ //2.3.1 Yes, Fetch and fill the childrens of the Parent from DB
+ aDbAdapter->GetChildrenL(id, aEntries);
+ TInt excessEntries = CMsvEntryFreePool::Instance()->ExcessMemoryAllocated();
+ if(excessEntries)
+ {
+ TInt index = 0;
+ RPointerArray<CMsvCacheEntry> entryList;
+ CleanupClosePushL(entryList);
+ for(; index<excessEntries; index++)
+ {
+ CMsvEntryFreePool::Instance()->RecordExcessMemoryL(aEntries[index]);
+ }
+ for(; index<aEntries.Count(); index++)
+ {
+ entryList.AppendL(aEntries[index]);
+ }
+ AddEntryListL(entryList);
+ CleanupStack::PopAndDestroy();
+ }
+ else
+ {
+ AddEntryListL(aEntries);
+ }
+ RArray<TMsvId>* childIds = new(ELeave) RArray<TMsvId>;
+ CleanupStack::PushL(childIds);
+ CleanupClosePushL(*childIds);
+ for(TInt index=0; index < aEntries.Count(); ++index)
+ {
+ childIds->AppendL(aEntries[index]->GetId());
+ }
+ parentEntry->SetChildIdArray(childIds);
+ CleanupStack::Pop(2);
+ }
+ // 2.4 Check If GetChildren has been already performed
+ else
+ {
+ RBuf8 buf;
+ buf.Create(2000);
+ CleanupClosePushL(buf);
+ CMsvCacheEntry* childEntry;
+ TBool isDBOperationReqd = EFalse;
+ for(TInt index=0; index< parentEntry->ChildIdArray()->Count(); ++index)
+ {
+ //2.4.1 If childrens are available in the cache get them
+ if(GetEntry((*parentEntry->ChildIdArray())[index], childEntry))
+ {
+ aEntries.AppendL(childEntry);
+ }
+ //2.4.2 else fetch the children from DB add them to cache
+ else
+ {
+ if(isDBOperationReqd)
+ {
+ buf.Append(KComma);
+ }
+ if(!isDBOperationReqd)
+ {
+ buf.Append(KAnd);
+ buf.Append(KId);
+ buf.Append(KIn);
+ buf.Append(KLtBrace);
+ isDBOperationReqd = ETrue;
+ }
+ #if (defined SYMBIAN_MSGS_ENHANCED_REMOVABLE_MEDIA_SUPPORT)
+ buf.AppendNum(UnmaskTMsvId((*parentEntry->ChildIdArray())[index]));
+ #else
+ buf.AppendNum((*parentEntry->ChildIdArray())[index]);
+ #endif
+ }
+ }
+ if(isDBOperationReqd)
+ {
+ buf.Append(KRtBrace);
+ buf.Append(KSemiColon);
+
+ RPointerArray<CMsvCacheEntry> childEntries;
+ CleanupClosePushL(childEntries);
+ aDbAdapter->GetChildrenL(buf, parentEntry->GetId(), childEntries);
+ for(TInt index=0; index<childEntries.Count(); ++index)
+ {
+ aEntries.AppendL(childEntries[index]);
+ }
+ AddEntryListL(childEntries);
+ CleanupStack::PopAndDestroy();
+ }
+ CleanupStack::PopAndDestroy();
+ }
+ }
+ SetGetChildrenFromVisibleFolder(EFalse);
+ return ETrue;
+ }
+
+
+/**
+ * GetChildrenIdL()
+ * @param CMsvEntrySelection& (OUT): List of child TMsvIds of the current visible
+ * folder.
+ * @return TBool: Returns ETrue, if children list can be returned, otherwise EFalse.
+ *
+ * The function returns list of child TMsvId of the current visible folder.
+ * It will return ETrue if it is possible to return all child IDs, otherwise
+ * it returns EFalse (In case when not all children of the folder is present
+ * in cache i.e. IsComplete() is false. OR in case when some of the blocks
+ * are dirty.).
+ */
+TBool CMsvCacheVisibleFolder::GetChildrenIdL(CMsvEntrySelection& aSelection)
+ {
+ if(IsComplete())
+ {
+ for(TInt index=0; index < iIndexTable.Count(); ++index)
+ {
+ if(iIndexTable[index]->IsDirty())
+ {
+ return EFalse;
+ }
+ }
+ for(TInt index=0; index < iIndexTable.Count(); ++index)
+ {
+ CMsvCacheIndexTableEntry* entry = iIndexTable[index];
+ TInt blockSize = entry->Size();
+ RPointerArray<CMsvCacheEntry>* blkPtr = entry->BlockPtr();
+ // 1. Check if there are grandchildren present in the block.
+ if(entry->IsGrandChildPresent())
+ {
+ // 1.1 If yes, then fetch only those entries with parent Id as aParentId.
+ // If the array of entries is NULL, then allocate space for it.
+ for(TInt index1 = 0 ; index1 < blockSize ; ++index1)
+ {
+ if((*blkPtr)[index1]->Entry().Parent() == iVisibleFolderId)
+ {
+ aSelection.AppendL((*blkPtr)[index1]->GetId());
+ }
+ }
+ } //if(IsGrandChildPresent())
+ else
+ {
+ // 1.2 If not, then return a copy of the whole block.
+ for(TInt index1 = 0 ; index1 < blockSize ; ++index1)
+ {
+ aSelection.AppendL((*blkPtr)[index1]->GetId());
+ }
+ } //else
+ }
+ return ETrue;
+ }
+ else
+ {
+ return EFalse;
+ }
+ }
+
+
+
+/**
+ DeleteEntry()
+@param TMsvId: TMsvId of the Entry to be deleted.
+@param aForceDelete TBool, it indicates whether the entry needs to be locked while deleting.
+@return TInt: Error Status.
+
+The function deletes an Entry from the Cache.
+1. Iterate through the IndexTable Entries.
+2. Find the IndexTable where the entry is present
+3. Delete the Entry and update the ChildMsvId array of the parent if delete successful
+4. If the its the last Entry in the IndexTable and the complete flag is False
+ delete the IndexTable entry
+*/
+void CMsvCacheVisibleFolder::DeleteEntryL(TMsvId aId, TBool aForceDelete)
+ {
+ TMsvId parentIdOfEntry;
+
+ // 1. Iterate through the IndexTable Entries.
+ for(TInt index=0; index < iIndexTable.Count(); ++index)
+ {
+ // 2. Find the IndexTable where the entry is present
+ if(iIndexTable[index]->IsInRange(aId))
+ {
+ // 3. Delete the Entry and update the ChildMsvId array of the parent if delete successful
+ iIndexTable[index]->DeleteEntryL(aId, parentIdOfEntry, aForceDelete);
+ if(parentIdOfEntry != iVisibleFolderId)
+ {
+ UpdateChildMsvIdsL(parentIdOfEntry, aId, EFalse);
+ }
+
+ // 4. If the its the last Entry in the IndexTable and the complete flag is False
+ // delete the IndexTable entry
+ if(NULL == iIndexTable[index]->Size() )
+ {
+ if((index+1) < iIndexTable.Count())
+ {
+ iIndexTable[index+1]->SetMinMsvIdRange(iIndexTable[index]->GetMinMsvIdRange());
+ }
+ delete iIndexTable[index];
+ iIndexTable.Remove(index);
+ }
+ return;
+ }
+ }
+ User::Leave(KErrNotFound);
+ }
+
+
+/**
+ DeleteEntryList()
+@param CMsvEntrySelection& aEntrySelection.
+@return TInt: Error Status.
+
+The function deletes a set of Entries from the Cache.
+*/
+void CMsvCacheVisibleFolder::DeleteEntryListL(CMsvEntrySelection& aEntrySelection)
+ {
+ TInt count = aEntrySelection.Count();
+ for(TInt index=0; index < count; ++index)
+ {
+ DeleteEntryL(aEntrySelection.At(index));
+ }
+ }
+
+
+/**
+ IsAllBlocksDirty()
+@param None.
+@return TBool: Is All the IndexTableEntries dirty or not.
+
+The function checks whether all the IndexTable Entry are dirty.
+1. Iterate through all the IndexTable Entries.
+2. Check if the IndexTable Entry is Not Dirty.
+2.1 Yes, Then Return EFalse; stating that all IndexTable Entries are not dirty.
+2.2 No, Then Return ETrue; stating that all IndexTable Entries are dirty.
+*/
+TBool CMsvCacheVisibleFolder::IsAllBlocksDirty() const
+ {
+ // 1. If index table size is zero, return EFalse.
+ if(0 == iIndexTable.Count())
+ {
+ return (EFalse);
+ }
+
+ // 2. Iterate through all the IndexTable Entries.
+ for(TInt index=0; index < iIndexTable.Count(); ++index)
+ {
+ // 3. Check if the IndexTable Entry is Not Dirty.
+ if(!(iIndexTable[index]->IsDirty()))
+ {
+ // 3.1 Yes, Then Return EFalse; stating that all IndexTable Entries are not dirty.
+ return(EFalse);
+ }
+ }
+ // 3.2 No, Then Return ETrue; stating that all IndexTable Entries are dirty.
+ return(ETrue);
+ }
+
+
+/**
+ EntryExists()
+@param TMsvId: TMsvId of the Entry.
+@return TBool: Entry Exists or not.
+
+The function checks whether an Entry with the Specified TMsvId Exists in the cache.
+1. Check if the TMsvId is same as the visible folder id.
+1.1. If they are same then return ETrue stating that id is present
+2. Else Check through all the IndexTableEntries and check if the Entry Exists.
+3. Else Return Entry is not Present.
+*/
+TBool CMsvCacheVisibleFolder::EntryExists(TMsvId aId) const
+ {
+ // 1. Check if the TMsvId is same as the visible folder id.
+ if(aId == iVisibleFolderId)
+ {
+ // 1.1. If they are same then return ETrue stating that id is present
+ return(ETrue);
+ }
+
+ // 2. Else Check through all the IndexTableEntries and check if the Entry
+ // Exists.
+ for(TInt index=0; index < iIndexTable.Count(); ++index)
+ {
+ if(iIndexTable[index]->EntryExists(aId))
+ {
+ return(ETrue);
+ }
+ }
+
+ // 3. Else Return Entry is not Present.
+ return(EFalse);
+ }
+
+
+/**
+ UpdateChildMsvIdsL()
+@param TMsvId aParentId : parent Id whose MsvId Array has to be updated.
+@param TMsvId AChildId : Child Id which as to be updated in the MsvId Array.
+@param TBool aAppend : Whether the Entry has to be appended or removed from the Parent MsvId Array.
+@return none.
+
+The function Updates the Owner with its child TMsvIds.
+1. Iterate through the index Table entri
+2.Find the indexTable where the entry is present
+3. Update the Parent with the child TMsvIds
+*/
+void CMsvCacheVisibleFolder::UpdateChildMsvIdsL(TMsvId aParentId, TMsvId aChildId, TBool aAppend /* DEFAULT = ETrue */)
+ {
+ // 1. Iterate through the index Table entries
+ for(TInt index = 0; index < iIndexTable.Count(); ++index)
+ {
+ // 2.Find the indexTable where the entry is present
+ if(iIndexTable[index]->IsInRange(aParentId))
+ {
+ // 3. Update the Parent with the child TMsvIds
+ iIndexTable[index]->UpdateChildMsvIdsL(aParentId, aChildId, aAppend);
+ return;
+ }
+ }
+ }
+
+
+/**
+ UpdateChildMsvIdsL()
+@param RPointerArray<CMsvCacheEntry>&: CMsvCacheEntry Pointer Array.
+@return none.
+
+The function Updates the Owner with its child TMsvIds.
+1. Find the IndexTableEntry where the parent is present.
+2. Update the Parent with the child TMsvIds
+*/
+void CMsvCacheVisibleFolder::UpdateChildMsvIdsL(RPointerArray<CMsvCacheEntry>& aEntries)
+ {
+ // 1. Find the IndexTableEntry where the parent is present.
+ for(TInt index=0; index < iIndexTable.Count(); ++index)
+ {
+ if(iIndexTable[index]->IsInRange(aEntries[0]->Entry().Parent()))
+ {
+ // 2. Update the Parent with the child TMsvIds
+ iIndexTable[index]->UpdateChildMsvIdsL(aEntries);
+ return;
+ }
+ }
+ }
+
+
+/**
+ ReleaseAllBlocks()
+@param TInt : Number Entries Successfully released.
+@return TBool : Returns true if all the entries are released successfully.
+
+The function Releases the blocks owned by the all indextable entries to freepool.
+1. Iterate through the indextable entries.
+2. Release the IndexTable Entry Block to the free pool
+*/
+TBool CMsvCacheVisibleFolder::ReleaseAllBlocks(TInt& aCount)
+ {
+ TBool isAllBlocksReleased = ETrue;
+ aCount = 0;
+
+ // 1. Iterate through the indextable entries.
+ for(TInt index=0; index < iIndexTable.Count(); ++index)
+ {
+ TInt size = iIndexTable[index]->Size();
+ //2.Release the IndexTable Entry Block to the free pool
+ if(!iIndexTable[index]->ReleaseBlock())
+ {
+ isAllBlocksReleased = EFalse;
+ }
+ else
+ {
+ aCount += size;
+ }
+ }
+ if(!IsComplete() && isAllBlocksReleased)
+ {
+ iIndexTable.ResetAndDestroy();
+ }
+
+ return (isAllBlocksReleased);
+ }
+
+
+/**
+ CompareOrder()
+@param CMsvCacheIndexTableEntry : First indexTable entry (Comparison arg)
+@param CMsvCacheIndexTableEntry : Second indexTable entry (Comparison arg)
+@return TInt : Returns TTime difference.
+
+Used to sort indexTableEntry.
+*/
+static TInt CompareOrder(const CMsvCacheIndexTableEntry& aFirst, const CMsvCacheIndexTableEntry& aSecond)
+ {
+ return (TInt) (aFirst.AccessTime().Int64()-aSecond.AccessTime().Int64());
+ }
+
+
+
+/**
+ ReleaseBlocks()
+@param TInt : number of blocks to be released to free pool.
+@param TBool : Returns true if the indextable was deleted.
+@return TInt : Returns Number of entries successfully released.
+
+The function Releases the required number blocks owned by the all indextable entries to freepool.
+1. Create a Copy of the IndexTable Entries & sort them w.r.t time
+2. Iterate through all the blocks or until the expected number of entries are released
+3. Release the entries to free pool
+*/
+TInt CMsvCacheVisibleFolder::ReleaseBlocks(TInt aNumberOfEntriesToRelease, TBool& aIsFolderEmpty)
+ {
+ aIsFolderEmpty = EFalse;
+ TInt index = 0;
+ TInt sizeOfBlock = 0;
+ TInt numEntries = aNumberOfEntriesToRelease;
+ TInt addrIndex = 0;
+
+ //1. Create a Copy of the IndexTable Entries & sort them w.r.t time
+ RPointerArray<CMsvCacheIndexTableEntry> indexTableCopytoSort;
+ for(index=0; index<iIndexTable.Count(); ++index)
+ {
+ TInt err = indexTableCopytoSort.Append(iIndexTable[index]);
+ if(KErrNone != err)
+ {
+ indexTableCopytoSort.Close();
+ return err;
+ }
+ }
+
+ TLinearOrder<CMsvCacheIndexTableEntry> order(CompareOrder);
+ indexTableCopytoSort.Sort(order);
+
+ index = 0;
+ // 2. Iterate through all the blocks or until the expected number of entries are released
+ while( (aNumberOfEntriesToRelease > 0) && (index < iIndexTable.Count()))
+ {
+ // 3. Release the entries to free pool
+ sizeOfBlock = indexTableCopytoSort[index]->Size();
+ if(indexTableCopytoSort[index]->ReleaseBlock())
+ {
+ aNumberOfEntriesToRelease -= sizeOfBlock;
+ if(!IsComplete())
+ {
+ addrIndex = iIndexTable.Find(indexTableCopytoSort[index]);
+ if((addrIndex+1) < iIndexTable.Count())
+ {
+ iIndexTable[addrIndex+1]->SetMinMsvIdRange(iIndexTable[addrIndex]->GetMinMsvIdRange());
+ }
+ delete iIndexTable[addrIndex];
+ iIndexTable.Remove(addrIndex);
+ indexTableCopytoSort.Remove(index);
+ --index;
+ }
+ }
+ ++index;
+ }
+
+ if(!IsComplete() && 0 == iIndexTable.Count())
+ {
+ aIsFolderEmpty = ETrue;
+ }
+ indexTableCopytoSort.Close();
+ return (numEntries - aNumberOfEntriesToRelease);
+ }
+
+
+
+/**
+ *SplitBlock
+ *Will check if the block size will be more than 120, then call the IndexTable SplitBlock()
+ *
+ *@param None.
+ *@return void
+ */
+void CMsvCacheVisibleFolder::SplitBlockL()
+ {
+ TInt tableCount = iIndexTable.Count();
+ while(tableCount--)
+ {
+ TInt size = iIndexTable[tableCount]->Size();
+ if(size > 120)
+ {
+ iIndexTable[tableCount]->SortBlock();
+ RPointerArray<CMsvCacheEntry> splitEntryBlock;
+ CleanupClosePushL(splitEntryBlock);
+ // Split the block and get the enteries of the 2nd block
+ iIndexTable[tableCount]->SplitBlockL(splitEntryBlock);
+ TInt count = splitEntryBlock.Count();
+
+ CMsvCacheIndexTableEntry* newTableEntry = CMsvCacheIndexTableEntry::NewLC(splitEntryBlock, 0, count);
+
+ TMsvId minId = splitEntryBlock[0]->GetId();
+ // Copy the flags(dirty and grandchildren) of the parent block.
+ if(iIndexTable[tableCount]->IsDirty())
+ {
+ newTableEntry->SetDirty();
+ }
+ if(iIndexTable[tableCount]->IsGrandChildPresent())
+ {
+ newTableEntry->SetGrandChildPresent();
+ }
+ newTableEntry->SetAccessTime(iIndexTable[tableCount]->AccessTime());
+
+ iIndexTable.InsertL(newTableEntry, tableCount+1);
+ iIndexTable[tableCount+1]->SetMinMsvIdRange(minId);
+ iIndexTable[tableCount+1]->SetMaxMsvIdRange(splitEntryBlock[count-1]->GetId());
+
+ iIndexTable[tableCount]->SetMaxMsvIdRange(minId - 1);
+ CleanupStack::Pop();//newTableEntry
+ splitEntryBlock.Reset();
+ CleanupStack::PopAndDestroy();// splitEntryBlock
+ }
+ }
+ }
+
+
+
+/**
+ * GetIndexTableEntry()
+ * @param TInt : Index at which the desired indextable entry exists.
+ * @return CMsvCacheIndexTableEntry* : Returns pointer at the index specified.
+ *
+ * The function returns the pointer to the IndexTable from the list.
+ */
+RPointerArray<CMsvCacheIndexTableEntry>* CMsvCacheVisibleFolder::GetIndexTableEntry()
+ {
+ return &iIndexTable;
+ }
+
+
+
+
+#if (defined SYMBIAN_MESSAGESTORE_UNIT_TESTCODE)
+#ifdef _DEBUG
+// To print message index cache.
+void CMsvCacheVisibleFolder::Print(RFileLogger& aLogger)
+ {
+ _LIT8(KBlock, " BLOCK-ID: ");
+ TInt blockCount = iIndexTable.Count();
+
+ for(TInt index=0; index < blockCount; index++)
+ {
+ RBuf8 text;
+ text.Create(100);
+ text.Append(KBlock);
+ text.AppendNum(index+1);
+ aLogger.Write(text);
+ text.Close();
+ iIndexTable[index]->Print(aLogger);
+ aLogger.Write(_L(""));
+ }
+ }
+#endif // #ifdef _DEBUG
+#endif // #if (defined SYMBIAN_MESSAGESTORE_UNIT_TESTCODE)
+