diff -r 9f5ae1728557 -r db3f5fa34ec7 messagingfw/msgsrvnstore/server/src/msvcacheindextableentry.Cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/messagingfw/msgsrvnstore/server/src/msvcacheindextableentry.Cpp Wed Nov 03 22:41:46 2010 +0530 @@ -0,0 +1,747 @@ +// 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: +// CMSVCACHEINDEXTABLEENTRY.CPP +// HEADER FILES +// +// + +#include "msvcacheindextableentry.h" +#include "msventryfreepool.h" +#include "msvcacheentry.h" + +#if (defined SYMBIAN_MSGS_ENHANCED_REMOVABLE_MEDIA_SUPPORT) + #include "msvindexadapter.h" +#endif + +/** + * FUNCTION DEFINITION + */ + + +//**************************** +//CMsvCacheIndexTableEntry +//**************************** + +/** + * NewL() + * @param None. + * @return The newly created index table entry + * + * It returns an instance of the CMsvCacheIndexTableEntry class, i.e. a newly + * constructed index table with no entries in it. + */ +CMsvCacheIndexTableEntry* CMsvCacheIndexTableEntry::NewL() + { + CMsvCacheIndexTableEntry *self = new(ELeave) CMsvCacheIndexTableEntry(); + return self; + } + + +/** + * NewL() + * @param CMsvCacheEntry& : a new cache entry + * @return The newly created index table entry + * + * It returns an instance of the CMsvCacheIndexTableEntry class with the passed entry + * added. + */ +CMsvCacheIndexTableEntry* CMsvCacheIndexTableEntry::NewL(CMsvCacheEntry*& aEntry) + { + CMsvCacheIndexTableEntry *self = CMsvCacheIndexTableEntry::NewLC(aEntry); + CleanupStack::Pop(); + return self; + } + + +/** + * NewLC() + * @param CMsvCacheEntry& : a new cache entry + * @return The newly created index table entry + * + * It returns an instance of the CMsvCacheIndexTableEntry class + * with the passed entry added. It leaves the object into cleanup stack. + */ +CMsvCacheIndexTableEntry* CMsvCacheIndexTableEntry::NewLC(CMsvCacheEntry*& aEntry) + { + CMsvCacheIndexTableEntry *self = new(ELeave) CMsvCacheIndexTableEntry(); + CleanupStack::PushL(self); + self->AddEntryL(aEntry); + return self; + } + + + + +/** + * NewL() + * @param RPointerArrayCMsvCacheEntry>& : RPointerArray reference to child entries + * @return The newly created index table entry + * + * It returns an instance of the CMsvCacheIndexTableEntry class filled with child + * entries. + */ +CMsvCacheIndexTableEntry* CMsvCacheIndexTableEntry::NewL(RPointerArray& aEntries, TInt aInitIndex /*(DEFALUT=0)*/, TInt aCount /* DEFAULT = -1*/) + { + CMsvCacheIndexTableEntry *self = CMsvCacheIndexTableEntry::NewLC(aEntries, aInitIndex, aCount); + CleanupStack::Pop(); + return self; + } + + + + +/** + * NewLC() + * @param RPointerArrayCMsvCacheEntry>& : RPointerArray reference to child entries + * @return The newly created index table entry + * + * It returns an instance of the CMsvCacheIndexTableEntry class filled with child + * entries. It leaves the object into cleanup stack. + */ +CMsvCacheIndexTableEntry* CMsvCacheIndexTableEntry::NewLC(RPointerArray& aEntries, TInt aInitIndex /*(DEFALUT=0)*/, TInt aCount /* DEFAULT = -1*/) + { + CMsvCacheIndexTableEntry *self = new(ELeave) CMsvCacheIndexTableEntry(); + CleanupStack::PushL(self); + self->AddEntrySetL(aEntries, aInitIndex, aCount); + return self; + } + + + + +/** + * CMsvCacheIndexTableEntry() + * @param None. + * @return None. + * + * Constructor + */ +CMsvCacheIndexTableEntry::CMsvCacheIndexTableEntry() +:iMinMsvId(0), iMaxMsvId(0), iFlags(EMsvCacheIndexTableClearFlag) + { + iTimeStamp.HomeTime(); //set the timestamp to number of seconds since Epoc. + iBlockPtr = NULL; + } + + +/** + * ~CMsvCacheIndexTableEntry() + * @param None. + * @return None. + * + * Destructor - releases the block to the cache free pool and sets it to NULL. + */ +CMsvCacheIndexTableEntry::~CMsvCacheIndexTableEntry() + { + //destructor should delete everything no matter if entries are locked or not + ReleaseBlock(ETrue); + } + + +/** + * AddEntryL() + * @param CMsvCacheEntry& : reference to the CMsvCacheEntry to be added. + * @return None. + * + * The function adds an entry into the index table. The entity who passes the entry + * to the function will give up ownership of the same and hence cannot modify or + * delete it. + * 1. If the block is NULL, it creates a new one and appends the entry to the block + * and accordingly sets the TMsvId ranges. + * 2. If block is not NULL, then append the entry to it. + * 2.1 If the entry already exists in the block then release the old entry from the block and + * append the new one. + * 2.2 If the entry fits only at the end of the last block then we need to change + * the iMaxMsvid. + * If the indextable has not been created and if the folder has just one block, and this block contains + * random entries, then we may need to set the iMinMsvId. + */ +void CMsvCacheIndexTableEntry::AddEntryL(CMsvCacheEntry*& aEntry, TBool aReplace /* DEFAULT = EFalse*/) + { + TMsvId entryId = aEntry->GetId(); + // 1. If the block is NULL, it creates a new one and appends the entry to the block + // and accordingly sets the TMsvId ranges. + if(NULL == iBlockPtr) + { + iBlockPtr = new(ELeave) RPointerArray; + iBlockPtr->AppendL(aEntry); + SetMinMsvIdRange(entryId); + SetMaxMsvIdRange(entryId); + } //if(NULL == iBlockPtr) + + // 2. If block is not NULL: + else + { + // 2.1 If the entry already exists in the block then release the old entry from the + // block and append the new one. + TInt blockSize = Size(); + for(TInt blockIndex = 0; blockIndex < blockSize; ++blockIndex) + { + if((*iBlockPtr)[blockIndex]->GetId() == aEntry->GetId()) + { + if(!aReplace) + { + CMsvEntryFreePool::Instance()->ReleaseEntry(aEntry); + aEntry = (*iBlockPtr)[blockIndex]; + return; + } + CMsvEntryFreePool::Instance()->ReleaseEntry((*iBlockPtr)[blockIndex]); + iBlockPtr->Remove(blockIndex); + iBlockPtr->Insert(aEntry, blockIndex); + return; + } + } + iBlockPtr->AppendL(aEntry); + + // 2.2 If the entry fits only at the end of the last block then we need to + // change the iMaxMsvid. + // If the indextable has not been created and if the folder has just + // one block, and this block contains random entries, then we may need to + // set the iMinMsvId. + if(entryId > iMaxMsvId) + { + SetMaxMsvIdRange(entryId); + } + + if(entryId < iMinMsvId) + { + SetMinMsvIdRange(entryId); + } + } //else + iTimeStamp.HomeTime(); + } + + + + +/** + * AddEntrySetL() + * @param RPointerArray& : reference to the new entry set to be added. + * These entries can either be immediate children + * or non-immediate children, but not both. + * @param TInt aInitIndex : Start index of the entry to be added in the entryList. + * @param TInt aCount : Number of entries that should be added. + * @return None. + * + * The function adds a set of entries to the index table. The caller who passes the + * array of entries to the function will give up ownership of the entries in the array. + * 1. When the block is NULL: + * 1.1 If children of a non-immediate folder are present in the array then set the + * grandchildren and dirty flags. + * 1.2 If there aren't, then clear the above two flags. + * 1.3 Assign the new entry set to the block, but reset the passed set so that + * the caller cannot modify or delete the cache entries. + * 2. When the block is not NULL: + * 2.1 If children of a non-immediate folder are present in the array then + * set the grandchildren flag and simply append entries to the block. + * 2.2 If there aren't, append entries avoiding duplication. + * 2.2.1 Search for each entry that is present in the current block in the + * new set passed. If the entry is present then release it from the + * current block. If not then append to the new set. + * 2.2.2 All entries are now in the passed array. Reset the current block, + * allocate space for it and copy the entries to it. Also reset the + * passed array so that callers do not have access to the cache + * entries after the addition. + * 3. Calculate and set the maximum and minimum TMsvId ranges in the newly added set. + */ +void CMsvCacheIndexTableEntry::AddEntrySetL(RPointerArray& aEntries, TInt aInitIndex /*(DEFAULT=0)*/, TInt aCount /* DEFAULT=-1 */) + { + // Add all entries if aCount is -1. + TInt arrSize = aEntries.Count(); + if(-1 == aCount) + { + aCount = arrSize; + } + if(aInitIndex < 0 || aInitIndex >= arrSize) + { + return; + } + + // 1. When the block is NULL: + if(NULL == iBlockPtr) + { + // Assign the new entry set to the block, but reset the passed set so that + // the caller cannot modify or delete the cache entries. + // Update the ranges of the block. + + iBlockPtr = new(ELeave) RPointerArray; + for(TInt listIndex = 0; (listIndex < aCount) && (arrSize > listIndex+aInitIndex); ++listIndex) + { + iBlockPtr->AppendL(aEntries[listIndex+aInitIndex]); + } + + } //if(NULL == iBlockPtr) + + // 2. When the block is not NULL append the entries. If there is a duplicate entry, + // discard the new entry. + else + { + TBool entryFound = EFalse; + TInt blockSize = Size(); + for(TInt listCount = 0; (listCount < aCount) && (arrSize > listCount+aInitIndex); ++listCount) + { + entryFound = EFalse; + for(TInt blockIndex = 0; blockIndex < blockSize; ++blockIndex) + { + if(aEntries[listCount+aInitIndex]->GetId() == (*iBlockPtr)[blockIndex]->GetId()) + { + CMsvEntryFreePool::Instance()->ReleaseEntry(aEntries[listCount+aInitIndex]); + aEntries.Remove(listCount+aInitIndex); + aEntries.Insert((*iBlockPtr)[blockIndex], listCount+aInitIndex); + entryFound = ETrue; + break; + } + } //for() + if(!entryFound) + { + iBlockPtr->AppendL(aEntries[listCount+aInitIndex]); + } + } //for() + } //else + iTimeStamp.HomeTime(); + } + + + + +/** + * GetEntry() + * @param TMsvId : the TMsvId of the entry to be fetched. + * @param CMsvCacheEntry*& : output parameter for the entry. + * @return TBool. + * + * The function fetches the entry with the specified TMsvId. + * 1. If the block is NULL, there is no entry that can be fetched. Return EFalse. + * 2. Iterate through the block searching for the TMsvId and return ETrue for a hit + * 3. Return EFalse if the entry is not present in the block. + */ +TBool CMsvCacheIndexTableEntry::GetEntry(TMsvId aId, CMsvCacheEntry*& aEntry) + { + // 1. If the block is NULL, there is no entry that can be fetched. Return EFalse. + if(NULL == iBlockPtr) + { + aEntry = NULL; + return EFalse; + } + + // 2. Iterate through the block searching for the TMsvId and return ETrue for a hit. + TInt blockSize = Size(); + for(TInt index = 0 ; index < blockSize ; ++index) + { + if((*iBlockPtr)[index]->GetId() == aId) + { + aEntry = (*iBlockPtr)[index]; + iTimeStamp.HomeTime(); + return ETrue; + } + } //for + + // 3. Return EFalse if the entry is not present in the block. + return EFalse; + } + + +/** + * GetChildrenL() + * @param TMsvId : TMsvId of the parent entry of the entries to be fetched. + * @param RPointerArray*& : output parameter for the entries to be + * fetched. + * @return None. + * + * The function fetches a set of entries with the specified parent TMsvId. + * 1. Check if there are grandchildren present in the block. + * 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. + * 1.2. If not, then return a copy of the whole block. + */ +void CMsvCacheIndexTableEntry::GetChildrenL(TMsvId aParentId, RPointerArray& aEntries) /*const*/ + { + // 1. Check if there are grandchildren present in the block. + if(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. + TInt blockSize = Size(); + for(TInt index = 0 ; index < blockSize ; ++index) + { + if((*iBlockPtr)[index]->Entry().Parent() == aParentId) + { + aEntries.AppendL((*iBlockPtr)[index]); + } + } + } //if(IsGrandChildPresent()) + + else + { + // 1.2 If not, then return a copy of the whole block. + TInt blockSize = Size(); + for(TInt index = 0 ; index < blockSize ; ++index) + { + aEntries.AppendL((*iBlockPtr)[index]); + } + } //else + iTimeStamp.HomeTime(); + } + + +/** + * DeleteEntryL() + * @param TMsvId : the TMsvId of the entry to be deleted. + * @param aParentIdOfEntry + * @param aForceDelete TBool, it indicates whether the entry needs to be locked while deleting. + * @return None. + * + * The function deletes an entry from the cache releasing the entry to the cache free + * pool. The function is called when the entry is being deleted from both the DB and + * cache. + * 1. Return if the block is NULL. + * 2. Iterate through the block searching for the TMsvId. + * 2.1 If the entry exists, remove it and release to the free pool. Deletion + * is possible only if the entry is locked in the cache, otherwise the function + * returns KErrAccessDenied. + * 2.1.1 If the entry deleted was the last entry in the block, delete the + * block. + * 3. Leave with KErrNotFound if the entry does not exist in the block. + */ +void CMsvCacheIndexTableEntry::DeleteEntryL(TMsvId aId, TMsvId& aParentIdOfEntry /*DEFAULT = NULL*/, TBool aForceDelete /*DEFAULT=EFalse*/) + { + // 1. Return if the block is NULL. + if(NULL == iBlockPtr) + { + User::Leave(KErrNotFound); + } + + // 2. Iterate through the block searching for the TMsvId. + TInt blockSize = Size(); + for(TInt index = 0 ; index < blockSize ; ++index) + { + // 2.1 If the entry exists, remove it and release to the free pool. Deletion + // is possible only if the entry is locked in the cache. + if((*iBlockPtr)[index]->GetId() == aId) + { + if(aForceDelete || (*iBlockPtr)[index]->IsEntryLocked()) + { + aParentIdOfEntry = (*iBlockPtr)[index]->Entry().Parent(); + CMsvEntryFreePool::Instance()->ReleaseEntry((*iBlockPtr)[index]); + iBlockPtr->Remove(index); + // 2.1.1. If the entry deleted was the last entry in the block, + // delete the block. + if(iBlockPtr->Count() == 0) + { + iBlockPtr->Close(); + delete iBlockPtr; + iBlockPtr = NULL; + } + iTimeStamp.HomeTime(); + } + else + { + User::Leave(KErrAccessDenied); + } + return; + } + } //for + // 3. Leave with KErrNotFound if the entry does not exist in the block. + User::Leave(KErrNotFound); + } + + +/** + * ReleaseBlock() + * @param TBool: internally used flag which is used by destructor to forcibly swap out + * the block even if entries have been locked. + * @return TBool: EFalse if block cannot be released as a result of entries having been + * locked in the cache, ETrue if otherwise. + * + * The function releases a block to the cache free pool. + * 1. Return EFalse if any of the entries in the block have been locked. Do not + * swap out the block to the free pool if so. + * 2. No entries are locked and/or the block is to be swapped out forcibly. + * 2.1 Release the entries to the free pool. + * 2.2 Free the memory allocated to the block itself. + * 2.3 Set the dirty flag. + */ +TBool CMsvCacheIndexTableEntry::ReleaseBlock(TBool aForceRelease /*DEFAULT = EFalse*/) + { + if(NULL == iBlockPtr) + { + return ETrue; + } + // 1. Return EFalse if any of the entries in the block have been locked. Do not + // swap out the block to the free pool if so. + if(!aForceRelease) + { + TInt blockSize = Size(); + for(TInt index = 0; index < blockSize; ++index) + { + if(!(*iBlockPtr)[index]->IsEntrySwappable()) + { + return EFalse; + } + } + } + + // 2. No entries are locked and the block can be swapped out safely. + CMsvEntryFreePool::Instance()->ReleaseEntrySet(*iBlockPtr); + + iBlockPtr->Close(); + delete iBlockPtr; + iBlockPtr = NULL; + SetDirty(); + return ETrue; + } + + +/** + * CompareOrder() + * @param const CMsvCacheEntry& : the first operand + * @param const CMsvCacheEntry& : the second operand + * @return TInt : the order (-1/0/+1) + * + * The function returns the order for the sort operation. + */ +static TInt CompareOrder(const CMsvCacheEntry& aFirst, const CMsvCacheEntry& aSecond) + { + return aFirst.GetId() - aSecond.GetId(); + } + + +/** + * SortBlockL() + * @param None. + * @return None. + * + * The function is called by a background active object to sort the index table + * according to TMsvIds of the entries. + * 1. Sort based on the order in the TLinearOrder package. + */ +void CMsvCacheIndexTableEntry::SortBlock() + { + if(iBlockPtr != NULL) + { + // 1. Sort based on the order in the TLinearOrder package. + TLinearOrder order(CompareOrder); + iBlockPtr->Sort(order); + } + } + + +/** + * EntryExists() + * @param TMsvId : the Id of the entry to be checked for existence. + * @return TBool : success/failure. + * + * The function return EFalse if either the block is NULL or the entry does not exist. + * ETrue is returned for a hit. + */ +TBool CMsvCacheIndexTableEntry::EntryExists(TMsvId aId) const + { + if(NULL == iBlockPtr) + { + return EFalse; + } + TInt blockSize = Size(); + for(TInt index = 0 ; index < blockSize ; ++index) + { + if(aId == (*iBlockPtr)[index]->GetId()) + { + return ETrue; + } + } //for + return EFalse; + } + + +/** + * UpdateChildMsvIdsL() + * @param TMsvId : TMsvId of the parent entry. + * @param TMsvId : TMsvId of the child entry. + * @param TBool : flag, set to ETrue if aChildId is to be added, and EFalse if it is + * to be removed from the parent's child array. + * @return None. + * + * The function updates the child array of the parent entry if it is not NULL. + * 1. Return if the block is NULL, i.e. the parent is not in the cache. + * 2. Iterate through the block searching for the parent entry. + * 2.1 If the iChildIdArray is not NULL: + * 2.1.1 If the child has to be added as a consequence of it being + * added to the cache, then append it to the child array. + * 2.1.2 If the child has to be removed as a consequence to the child + * entry being deleted, then remove it from the child array. + */ +void CMsvCacheIndexTableEntry::UpdateChildMsvIdsL(TMsvId aParentId, TMsvId aChildId, TBool aAdd /*DEFAULT = ETrue*/) + { + // 1. Return if the block is NULL, i.e. the parent is not in the cache. + if(NULL == iBlockPtr) + { + return; + } + + // 2. Iterate through the block searching for the parent entry. + TInt blockSize = Size(); + for(TInt index = 0 ; index < blockSize ; ++index) + { + if((*iBlockPtr)[index]->GetId() == aParentId) + { + RArray* childArr = (*iBlockPtr)[index]->ChildIdArray(); + // 2.1 If the iChildIdArray is not NULL: i.e GetChildren has been performed + if(childArr != NULL) + { + // 2.1.1 If the child has to be added as a consequence of it being + // added to the cache, then append it to the child array. + TInt arrSize = childArr->Count(); + if(aAdd) + { + TInt index = childArr->Find(aChildId); + if(index == KErrNotFound) + { + childArr->AppendL(aChildId); + } + } + + // 2.1.2 If the child has to be removed as a consequence to the child + // entry being deleted, then remove it from the child array. + else + { + TInt index = childArr->Find(aChildId); + if(index != KErrNotFound) + { + childArr->Remove(index); + } + } + } //if(((*iBlockPtr)[index]->ChildIdArray()) != NULL) + return; + } //if((*iBlockPtr)[index]->GetId() == aParentId) + } //for(TInt index = 0 ; index < Size() ; index++) +} + + +/** + * UpdateChildMsvIdsL() + * @param RPointerArray& : array of complete set of child + * CMsvCacheEntries sorted by TMsvId. + * @return None. + * + * The function updates, or creates if necessary, a parent entry's iChildIdArray with + * the children entries passed to it in the form of a RPointerArray. + * 1. Return if the block is NULL, i.e. the parent is not in the cache. + * 2. Otherwise, find the parent sequentially. + * 2.1 If the parent is found: + * 2.1.1 If the parent already has a iChildIdArray, i.e. if GetChildren() has + * been performed on it, delete and create a new child array with the + * entries passed. If iChildIdArray is NULL then GetChildren() has not been + * performed on the entry yet, in which case append the passed children. + */ +void CMsvCacheIndexTableEntry::UpdateChildMsvIdsL(RPointerArray& aEntries) + { + + // 1. Return if the block is NULL, i.e. the parent is not in the cache. + if(NULL == iBlockPtr) + { + return; + } + + // 2. Otherwise, find the parent sequentially. + TMsvId parentId = aEntries[0]->Entry().Parent(); + TInt blockSize = Size(); + for(TInt index = 0 ; index < blockSize ; ++index) + { + // 2.1 If the parent is found: + if((*iBlockPtr)[index]->Entry().Id() == parentId) + { + // 2.1.1 If the parent already has a iChildIdArray, i.e. if GetChildren() has + // been performed on it, delete and create a new child array with the + // entries passed. If iChildIdArray is NULL then GetChildren() has not been + // performed on the entry yet, in which case append the passed children. + RArray* childArr = (*iBlockPtr)[index]->ChildIdArray(); + TInt entryCount = aEntries.Count(); + if(NULL == childArr) + { + childArr = new(ELeave) RArray; + (*iBlockPtr)[index]->SetChildIdArray(childArr); + } + else + { + childArr->Reset(); + } + while(0 < entryCount) + { + TInt leave = childArr->Append(aEntries[--entryCount]->GetId()); + if(KErrNone != leave) + { + childArr->Close(); + delete childArr; + (*iBlockPtr)[index]->SetChildIdArray(NULL); + User::Leave(leave); + } + } + return; + } //if((*iBlockPtr)[index]->Entry().Parent() == parentId) + } //for(TInt index = 0 ; index < blockSize ; ++index) + } + + +/** + *SplitBlock + *This will be called when the block size will be more than 120, this will split the block + * into 2 blocks. + *@param aSplitBlock RPointerArray& that will be filled with CMsvCacheEntries, + *@param aMaxId The maximum TMsvId of the first block. + *@return void + * + */ +void CMsvCacheIndexTableEntry::SplitBlockL(RPointerArray& aSplitBlock) + { + TInt size = Size(); + TInt i = size/2; + while(i < size) + { + aSplitBlock.AppendL((*iBlockPtr)[size/2]); + iBlockPtr->Remove(size/2); + i++; + } + } + + + + + +#if (defined SYMBIAN_MESSAGESTORE_UNIT_TESTCODE) +#ifdef _DEBUG +// To print message index cache. +void CMsvCacheIndexTableEntry::Print(RFileLogger& aLogger) + { + _LIT(KId, " ID: "); + _LIT(KParentId, " ParentId: "); + TInt blockSize = Size(); + for(TInt index=0; indexEntry().Id()); + text.Append(KParentId); + text.AppendNum((*iBlockPtr)[index]->Entry().Parent()); + text.Append(_L(" VisibleEntry: ")); + text.AppendNum((*iBlockPtr)[index]->Entry().VisibleFolderFlag()); + text.Append(_L(" Complete flag:")); + text.AppendNum((*iBlockPtr)[index]->Entry().Complete()); + text.Append(_L(" Description:")); + text.Append((*iBlockPtr)[index]->Entry().iDetails); + aLogger.Write(text); + text.Close(); + } + } +#endif // #ifdef _DEBUG +#endif // #if (defined SYMBIAN_MESSAGESTORE_UNIT_TESTCODE)