// 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<CMsvCacheEntry>& 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<CMsvCacheEntry>& 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<CMsvCacheEntry>; 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<CMsvCacheEntry>& : 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<CMsvCacheEntry>& 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<CMsvCacheEntry>; 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<CMsvCacheEntry>*& : 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<CMsvCacheEntry>& 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<CMsvCacheEntry> 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<TMsvId>* 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<CMsvCacheEntry>& : 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<CMsvCacheEntry>& 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<TMsvId>* childArr = (*iBlockPtr)[index]->ChildIdArray(); TInt entryCount = aEntries.Count(); if(NULL == childArr) { childArr = new(ELeave) RArray<TMsvId>; (*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<CMsvCacheEntry>& that will be filled with CMsvCacheEntries, *@param aMaxId The maximum TMsvId of the first block. *@return void * */void CMsvCacheIndexTableEntry::SplitBlockL(RPointerArray<CMsvCacheEntry>& 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; index<blockSize; ++index) { RBuf text; text.Create(150); text.Append(KId); text.AppendNum((*iBlockPtr)[index]->Entry().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)