// 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:
//
#ifdef _DEBUG
#undef _NO_SERVER_LOGGING_
#endif
/**
* User Includes
*/
#include "MSVSERV.H"
#include "MSVARRAY.H"
#include "msvdbadapter.h"
#include "msvcacheentry.h"
#include "msvindexadapter.h"
#include "msventryfreepool.h"
#include "msvcachevisiblefolder.h"
#include "msvsearchsortdeltacache.h"
#include "msvsearchsortcachemanager.h"
/**
*System Includes
*/
#include <msvuids.h>
#include <msvstd.h>
#ifdef SYMBIAN_ENABLE_SPLIT_HEADERS
#include "msvconsts.h"
#endif
/**
* MACRO DEFINITIONS
*/
#if (defined SYMBIAN_MSGS_ENHANCED_REMOVABLE_MEDIA_SUPPORT)
#define INITIAL_NUM_ENTRIES 10
#else
#define INITIAL_NUM_ENTRIES 13
#endif
/**
* CONSTANT DEFINITIONS
*/
const TInt KMsvMtmListGranularity=8;
const TInt KMsvChangeAttributesListGranularity=32;
/**
* FUNCTION DEFINITIONS
*/
/**
* CMsvIndexAdapter()
*/
CMsvIndexAdapter::CMsvIndexAdapter(CMsvServer& aServer)
:CActive(EPriorityStandard), iServer(aServer), iFolderListHeader(CMsvCacheVisibleFolder::iOffset)
{
CActiveScheduler::Add(this);
}
/**
* ~CMsvIndexAdapter()
*/
CMsvIndexAdapter::~CMsvIndexAdapter()
{
#ifndef _NO_SERVER_LOGGING_
iServer.Log(_L("Destroying CMsvIndexAdapter object."));
#endif
Cancel();
// 1. Close the database.
#ifndef _NO_SERVER_LOGGING_
iServer.Log(_L("Closing the database."));
#endif
if(iDbAdapter != NULL)
delete iDbAdapter;
// 2. Delete Cache.
#ifndef _NO_SERVER_LOGGING_
iServer.Log(_L("Destroying Cache."));
#endif
CMsvCacheVisibleFolder* folderNode = NULL;
TDblQueIter<CMsvCacheVisibleFolder> dqIter(iFolderListHeader);
dqIter.SetToFirst();
while ((folderNode = dqIter++) != NULL)
{
folderNode->iDlink.Deque();
delete folderNode;
}
iFreePoolInstance->ReleaseEntry(iRootEntry);
iRootEntry = NULL;
// 3. Delete internal data structure.
iNonCommittedAddedEntryList.ResetAndDestroy();
iNonCommittedChangedEntryList.Reset();
iNonCommittedAddedEntryList.Close();
iNonCommittedChangedEntryList.Close();
// 4. Flush extra memory in entry freepool.
if(iFreePoolInstance != NULL)
{
iFreePoolInstance->FlushExcessMemory();
}
#if (defined SYMBIAN_MSGS_ENHANCED_REMOVABLE_MEDIA_SUPPORT)
// Added for 557.
iMaxMsvIdList.Close();
#endif
delete iIdle;
}
#if (defined SYMBIAN_MSGS_ENHANCED_REMOVABLE_MEDIA_SUPPORT)
/**
* NewL()
* @return The newly created index adapter object.
*
* Function added as part of PREQ 557.
* It returns an instance of CMsvIndexAdapter class.
*/
CMsvIndexAdapter* CMsvIndexAdapter::NewL(CMsvServer& aServer)
{
CMsvIndexAdapter *self = new(ELeave) CMsvIndexAdapter(aServer);
CleanupStack::PushL(self);
self->ConstructL();
CleanupStack::Pop();
return self;
}
/**
* ConstructL()
*
* Function added as part of PREQ 557.
* The function is called from NewL().
* 1. Create the DB adapter object.
* 2. Initialize freepool object.
* 3. Create the root entry in cache.
* 4. Browse through the preferred drive list.
* i. For each drive in the preferred drive list the function
* assigns a unique drive-id (0< Id <8).
* ii. Attach the DB to the primary database.
* iii. Update the maxId of the database to MaxId table.
* iv. If the database is not compatible or corrupt, update the
* appropriate drive status in CMsvServer data structure.
* v. Fetch children of root entry and Local-Service entry from the DB to the cache.
*/
void CMsvIndexAdapter::ConstructL()
{
#ifndef _NO_SERVER_LOGGING_
iServer.Log(_L("Inside ConstructL()..."));
#endif
// Create database adapter object.
iDbAdapter = CMsvDBAdapter::NewL();
#ifndef _NO_SERVER_LOGGING_
iServer.Log(_L("The database object is created succesfully."));
#endif
iProgress.iCompleted = 0;
iProgress.iTotal = iProgress.iRemaining = INITIAL_NUM_ENTRIES;
// Initialize freepool object.
iFreePoolInstance = CMsvEntryFreePool::Instance();
iFreePoolInstance->SetEssentialParam(&iFolderListHeader, this);
// Drive id zero will be synonym of KCurrentDriveId. Drive
// id of a drive (including current) drive will never be zero.
// Drive of the current drive will always by KCurrentDriveId.
TUint driveId = KCurrentDriveId;
// Since driveId starts from 1, the first
// entry in iMaxMsvIdList is not used.
// Enter a dummy entry in location zero.
iMaxMsvIdList.InsertL(0, 0);
iProgress.iCompleted++;
iProgress.iRemaining--;
iProgress.iId = KMsvRootIndexEntryId;
// For each drive in the preferred drive list, perform followings...
// 1. Check if a valid message store is available.
// 2. If not, skip the drive.
// 3. Assign a new drive Id to the drive and
// update the entry in preferred drive list.
// 4. Attach the drive database to the primary DB.
// 5. Update the max Id list.
// 6. For current drive, fetch the children of root entry into cache.
// 7. Fetch children of localService into cache.
CMsvPreferredDriveList *driveList = CMsvPreferredDriveList::GetDriveList();
TUint curDriveIndex = driveList->CurrentDriveIndex();
for(TInt index=curDriveIndex; index<driveList->Count(); index++)
{
TMsvPreferredDrive driveEntry;
driveList->DriveInfoL(index, driveEntry);
if(EMsvMessageStoreAvailableStatus == driveEntry.status)
{
// Assign a unique drive-id.
driveList->UpdateDriveIdL(index, driveId);
// Attach the database.
TMsvId maxId;
iDbAdapter->AttachDBL(driveEntry.driveNum, driveId, maxId);
// Insert the max Id in the list.
iMaxMsvIdList.InsertL( ((maxId >= MaskTMsvId(driveId, KFirstFreeEntryId))? (maxId+1): MaskTMsvId(driveId, KFirstFreeEntryId)), driveId);
driveId++;
}
iProgress.iCompleted++;
iProgress.iRemaining--;
}
iFirstFreeDriveId = (driveId%8)? (driveId%8): 2;
// Initializing remaining entries in MaxMsvId list.
for(TInt index=driveId; index<8; index++)
{
iMaxMsvIdList.InsertL(NULL, index);
}
// For current drive fetch children of root entries also.
RPointerArray<CMsvCacheEntry> childEntryList;
CleanupClosePushL(childEntryList);
// Create root entry node in the cache. A root folder node will never
// be swapped out of cache. The children of root entry will be the
// children of root entry in the current drive.
// Root id is masked to set the driveId in the folder node.
iRootNode = CMsvCacheVisibleFolder::NewL(MaskTMsvId(KCurrentDriveId, KMsvRootIndexEntryId));
iFolderListHeader.AddFirst(*iRootNode);
iRootNode->SetComplete(ETrue);
iDbAdapter->GetChildrenL(KMsvRootIndexEntryId, childEntryList);
iRootNode->AddEntryListL(childEntryList, ETrue);
childEntryList.Reset();
// Fetch child of LocalServices.
CMsvCacheVisibleFolder* localServiceFolder = CMsvCacheVisibleFolder::NewL(MaskTMsvId(KCurrentDriveId, KMsvLocalServiceIndexEntryId));
iFolderListHeader.AddFirst(*localServiceFolder);
iDbAdapter->GetChildrenL(KMsvLocalServiceIndexEntryId, childEntryList);
localServiceFolder->AddEntryListL(childEntryList, ETrue);
CleanupStack::PopAndDestroy(1); //childEntryList
// Create root entry.
iRootEntry = iFreePoolInstance->EntryL(); //taking it from the freepool.
iRootEntry->Entry().iType = KUidMsvRootEntry;
iRootEntry->Entry().iDate.UniversalTime();
iRootEntry->Entry().SetId(KMsvRootIndexEntryId);
iRootEntry->Entry().SetParent(KErrNotFound);
iRootEntry->Entry().SetOwner(ETrue);
iRootEntry->Entry().iSize = 0;
// Update progress
iProgress.iCompleted = iProgress.iTotal;
iProgress.iRemaining = 0;
iProgress.iId = KMsvRootIndexEntryId;
#ifndef _NO_SERVER_LOGGING_
iServer.Log(_L("Cache initialized succesfully."));
#endif
iIdle = CIdle::NewL(CActive::EPriorityIdle);
CompleteSelf();
}
/**
GetInPreparationIds
*/
void CMsvIndexAdapter::GetInPreparationIds(CMsvEntrySelection& aSelection, TUint aDriveId /*DEFAULT = 0 */)
{
if(iDbAdapter)
{
TRAP_IGNORE(iDbAdapter->GetInPreparationIdL(aSelection, aDriveId));
}
}
#else // #if (defined SYMBIAN_MSGS_ENHANCED_REMOVABLE_MEDIA_SUPPORT)
/**
* NewL()
* @param TFileName: DB File Name.
* @return The newly created index adapter.
*
* It returns an instance of CMsvIndexAdapter class.
*/
CMsvIndexAdapter* CMsvIndexAdapter::NewL(CMsvServer& aServer, const TFileName& aDbFileName)
{
CMsvIndexAdapter *self = new(ELeave) CMsvIndexAdapter(aServer);
CleanupStack::PushL(self);
self->ConstructNewL(aDbFileName);
CleanupStack::Pop();
return self;
}
/**
* OpenL()
* @param TFileName: DB File Name.
* @return The newly created index adapter.
*
* It returns an instance of CMsvIndexAdapter class.
*/
CMsvIndexAdapter* CMsvIndexAdapter::OpenL(CMsvServer& aServer, const TFileName& aDbFileName)
{
CMsvIndexAdapter *self = new(ELeave) CMsvIndexAdapter(aServer);
CleanupStack::PushL(self);
self->ConstructOpenL(aDbFileName);
CleanupStack::Pop();
return self;
}
/**
* ConstructNewL()
*
* The function is called from NewL().
* 1. Check if a database with the passed name already exists.
* 1.1. If yes, open the database.
* 1.2. If no, create a new database with the same name.
* 2. Creates entry cache.
* 3. If a new database is created, create root entry also.
*/
void CMsvIndexAdapter::ConstructNewL(const TFileName& aDbFileName)
{
#ifndef _NO_SERVER_LOGGING_
iServer.Log(_L("Creating CMsvIndexAdapter instance..."));
#endif
// Delete the database first, if it already exists.
CMsvDBAdapter::DeleteDB(aDbFileName);
// Get the instance of free pool.
iFreePoolInstance = CMsvEntryFreePool::Instance();
// Create the new database with the same name.
// If this leaves, nothing can be done and let NewL() leave.
iDbAdapter = CMsvDBAdapter::NewL(aDbFileName);
#ifndef _NO_SERVER_LOGGING_
iServer.Log(_L("A new database %s is created..."), &aDbFileName);
#endif
iProgress.iTotal = iProgress.iRemaining = 1; //root
// Create root entry.
CreateInitialSetOfEntriesL();
#ifndef _NO_SERVER_LOGGING_
// iServer.Log(_L("Cache initialized succesfully."));
#endif
iNextCreateId = KFirstFreeEntryId;
iIdle = CIdle::NewL(CActive::EPriorityIdle);
CompleteSelf();
}
/**
* ConstructOpenL()
*
* The function is called from NewL().
* 1. Check if a database with the passed name already exists.
* 1.1. If yes, open the database.
* 1.2. If no, create a new database with the same name.
* 2. Creates entry cache.
* 3. If a new database is created, it then read message resource file
* and creates entry as mentioned in cache and database.
*/
void CMsvIndexAdapter::ConstructOpenL(const TFileName& aDbFileName)
{
#ifndef _NO_SERVER_LOGGING_
iServer.Log(_L("Creating CMsvIndexAdapter instance..."));
#endif
// Get the instance of free pool
iFreePoolInstance = CMsvEntryFreePool::Instance();
// Open the database.
iDbAdapter = CMsvDBAdapter::OpenL(aDbFileName);
#ifndef _NO_SERVER_LOGGING_
iServer.Log(_L("Opening database %s is succesful."), &aDbFileName);
#endif
iProgress.iTotal = iProgress.iRemaining = INITIAL_NUM_ENTRIES;
CreateInitialCacheL();
#ifndef _NO_SERVER_LOGGING_
iServer.Log(_L("Cache initialized succesfully."));
#endif
TMsvId tmpNextId = NULL;
iDbAdapter->GetMaxTMsvIdL(tmpNextId);
iNextCreateId = (tmpNextId >= KFirstFreeEntryId)? (tmpNextId+1) : KFirstFreeEntryId;
iIdle = CIdle::NewL(CActive::EPriorityIdle);
CompleteSelf();
}
/**
* CreateInitialSetOfEntries()
*
* The function is called from ConstructL(), whenever a new DB is created.
* The function creates root entry into database and cache.
*/
void CMsvIndexAdapter::CreateInitialSetOfEntriesL()
{
#ifndef _NO_SERVER_LOGGING_
iServer.Log(_L("Inside CreateInitialSetOfEntriesL(): Creating standard entries for new DB."));
#endif
// 1. Initialize the instance of free pool.
iFreePoolInstance->SetEssentialParam(&iFolderListHeader, this);
// 2. Creating root entry into database.
iRootEntry = iFreePoolInstance->EntryL(); //taking it from the freepool.
iRootEntry->Entry().iType = KUidMsvRootEntry;
iRootEntry->Entry().iDate.UniversalTime();
iRootEntry->Entry().SetId(KMsvRootIndexEntryId);
iRootEntry->Entry().SetParent(KErrNotFound);
iRootEntry->Entry().iSize = 0;
TMsvId visibleFolderId = KMsvRootIndexEntryId;
iDbAdapter->CreateEntryL(iRootEntry->Entry(), visibleFolderId);
// 3. Add root entry to cache as well
CMsvCacheVisibleFolder* rootFolder = CMsvCacheVisibleFolder::NewL(KMsvRootIndexEntryId);
iFolderListHeader.AddFirst(*rootFolder);
rootFolder->SetComplete(ETrue);
// Update progress
iProgress.iCompleted++;
iProgress.iRemaining--;
iProgress.iId = KMsvRootIndexEntryId;
}
/**
* CreateInitialCache()
*
* The function is called from NewL(), whenever an existing DB is opened.
* It creates the intial cache. It creates root entry in cache and fetches
* child of root from DB and push them into cache. It then fetches child
* entries of LocalServices and push them too into cache.
*
* >>>> The function can be enhanced to load children of additional folders
* to cache along with the above standard folders. These additional folders
* can be mentioned by licensees in msgs.ini file.
*/
void CMsvIndexAdapter::CreateInitialCacheL()
{
#ifndef _NO_SERVER_LOGGING_
iServer.Log(_L("Inside CreateInitialCache(): Initializing Cache."));
#endif
//1. Initialize the instance of free pool.
iFreePoolInstance->SetEssentialParam(&iFolderListHeader, this);
// 2. Add root entry to cache.
CMsvCacheVisibleFolder* rootFolder = CMsvCacheVisibleFolder::NewL(KMsvRootIndexEntryId);
iFolderListHeader.AddFirst(*rootFolder);
// 2.1 Update progress
iProgress.iCompleted++;
iProgress.iRemaining--;
iProgress.iId = KMsvRootIndexEntryId;
// 3. Fetch child of root entries from DB.
RPointerArray<CMsvCacheEntry> childrenOfRoot;
iDbAdapter->GetChildrenL(KMsvRootIndexEntryId, childrenOfRoot);
// 3.1 Get the root entry from DB.
CMsvCacheEntry *rootEntry = NULL;
TMsvId visibleFolder = NULL;
iDbAdapter->GetEntryL(KMsvRootIndexEntryId, rootEntry, visibleFolder);
// 3.2 Copy the TMsvEntry of root from CMsvCacheEntry.
//Mem::Copy(&iRootEntry->Entry(), &rootEntry->Entry(), sizeof(TMsvEntry));
iRootEntry = rootEntry;
// 3.3 Release the cache entry back to the freepool.
//iFreePoolInstance->ReleaseEntry(rootEntry);
// 4. Add children to cache.
rootFolder->AddEntryListL(childrenOfRoot, ETrue);
// 4.1 Update progress
iProgress.iCompleted += 4;
iProgress.iRemaining -= 4;
iProgress.iId = KMsvLocalServiceIndexEntryId;
// 5. Set the visible folder as complete
rootFolder->SetComplete(ETrue);
// 6. Fetch child of LocalServices
childrenOfRoot.Reset();
iDbAdapter->GetChildrenL(KMsvLocalServiceIndexEntryId, childrenOfRoot);
CMsvCacheVisibleFolder* localFolder = CMsvCacheVisibleFolder::NewL(KMsvLocalServiceIndexEntryId);
iFolderListHeader.AddFirst(*localFolder);
// 7. Add children to cache.
localFolder->AddEntryListL(childrenOfRoot);
// 7.1 Update progress
iProgress.iCompleted = iProgress.iTotal;
iProgress.iRemaining = 0;
iProgress.iId = KMsvGlobalInBoxIndexEntryId;
// 8. Set the visible folder as complete
localFolder->SetComplete(ETrue);
childrenOfRoot.Close();
}
/**
GetInPreparationIds
*/
void CMsvIndexAdapter::GetInPreparationIds(CMsvEntrySelection& aSelection)
{
if(iDbAdapter)
{
TRAP_IGNORE(iDbAdapter->GetInPreparationIdL(aSelection));
}
}
#endif // #if (defined SYMBIAN_MSGS_ENHANCED_REMOVABLE_MEDIA_SUPPORT)
/**
*CompleteSelf
*This will make sure that Runl is called.
*/
void CMsvIndexAdapter::CompleteSelf()
{
iStatus=KRequestPending;
SetActive();
TRequestStatus* st=&iStatus;
User::RequestComplete(st, KErrNone);
}
/**
* RunL()
*/
void CMsvIndexAdapter::RunL()
{
// Set the state for the background state machine.
iBackgroundOperationState = ERemoveDeletedEntries;
iBackgroundOperationPerformed = 0;
}
/**
* DoCancel()
*/
void CMsvIndexAdapter::DoCancel()
{
iIdle->Cancel();
}
/**
* AddEntry()
*/
TInt CMsvIndexAdapter::AddEntry(TMsvEntry& aEntry, TSecureId aOwnerId, TBool aAutoAssignId)
{
TRAPD(err, DoAddEntryL(aEntry, aOwnerId, aAutoAssignId));
return err;
}
/**
* AddEntryNoCommit()
*/
TInt CMsvIndexAdapter::AddEntryNoCommit(TMsvEntry& aEntry, TSecureId aOwnerId, TBool aAutoAssignId)
{
if(iDbAdapter)
{
TRAP_IGNORE(iDbAdapter->BeginTransactionL());
}
else
{
return ErrorState();
}
TRAPD(err, DoAddEntryL(aEntry, aOwnerId, aAutoAssignId, EFalse));
if(err)
{
RollbackAdditions();
}
return err;
}
/**
* DoAddEntryL()
*
* Add the entry to the index and then the file.
* If either fails the function leaves. Creates
* its own copy of TMsvEntry (SetEntryL() code).
* This can be optimized further.
*/
void CMsvIndexAdapter::DoAddEntryL(TMsvEntry& aEntry, TSecureId aOwnerId, TBool aAutoAssignId, TBool aCommitToDb)
{
// Leave if the message store is not currently available.
User::LeaveIfError(iErrorState);
#if (defined SYMBIAN_MSGS_ENHANCED_REMOVABLE_MEDIA_SUPPORT)
// get and set the unique id
if (aAutoAssignId)
{
aEntry.SetId(NextId(GetDriveId(aEntry.Parent())));
}
if (aEntry.iType == KUidMsvServiceEntry)
{
if(KMsvLocalServiceIndexEntryId == UnmaskTMsvId(aEntry.iId))
{
aEntry.iServiceId = KMsvLocalServiceIndexEntryId;
}
else
{
aEntry.iServiceId = aEntry.Id();
}
}
#else // #if (defined SYMBIAN_MSGS_ENHANCED_REMOVABLE_MEDIA_SUPPORT)
if (aAutoAssignId)
{
aEntry.SetId(NextId());
}
if (aEntry.iType == KUidMsvServiceEntry)
{
aEntry.iServiceId = aEntry.Id();
}
#endif // #if (defined SYMBIAN_MSGS_ENHANCED_REMOVABLE_MEDIA_SUPPORT)
CMsvCacheEntry* parentEntry = NULL;
FindEntryL(aEntry.Parent(), parentEntry);
#if (defined SYMBIAN_MSGS_ENHANCED_REMOVABLE_MEDIA_SUPPORT)
if(UnmaskTMsvId(aEntry.iId) == KMsvDeletedEntryFolderEntryId)
{
aEntry.SetVisibleFolderFlag(ETrue);
}
else
#endif
{
// If entry is set to visible while creation, then set the visible folder
// flag to ETrue, this will not change for the lifetime of an TMsvEntry.
if(parentEntry->Entry().VisibleFolderFlag())
{
if( (aEntry.Visible()) &&
(aEntry.iType == KUidMsvFolderEntry || aEntry.iType == KUidMsvServiceEntry)
)
{
aEntry.SetVisibleFolderFlag(ETrue);
}
else
{
aEntry.SetVisibleFolderFlag(EFalse);
}
}
else
{
aEntry.SetVisibleFolderFlag(EFalse);
}
}
aEntry.SetOwner(EFalse);
TMsvId visibleFolderId;
if(!GetVisibleFolderId(aEntry.Parent(), visibleFolderId))
{
User::Leave(KErrNotFound);
}
// Add entry to cache.
CMsvCacheEntry* newCacheEntry = NULL;
newCacheEntry = iFreePoolInstance->EntryL();
// Set the owner ID of the entry - the SID of the owning process.
newCacheEntry->SetEntryOwnerId(aOwnerId);
CMsvCacheVisibleFolder* visibleFolder = NULL;
TRAPD(err, newCacheEntry->SetEntryL(aEntry);
visibleFolder = AddEntryToCacheL(visibleFolderId, newCacheEntry);
);
if(err)
{
iFreePoolInstance->ReleaseEntryWithoutTransaction(newCacheEntry);
User::Leave(err);
}
#if (defined SYMBIAN_MSGS_ENHANCED_REMOVABLE_MEDIA_SUPPORT)
TRAP(err,
if(aCommitToDb)
iDbAdapter->BeginTransactionL();
iDbAdapter->CreateEntryL(aEntry, visibleFolderId);
// Set the owner flag of parent entry if not already done.
// For standard entry created from indexContext, parent Id will not be masked.
iDbAdapter->UpdateOwnerStatusL(MaskTMsvId(GetDriveId(aEntry.iId), aEntry.Parent()), parentEntry->Entry(), ETrue);
if(aCommitToDb)
iDbAdapter->CommitTransactionL();
);
#else
TRAP(err,
if(aCommitToDb)
iDbAdapter->BeginTransactionL();
iDbAdapter->CreateEntryL(aEntry, visibleFolderId);
// Set the owner flag of parent entry if not already done.
// For standard entry created from indexContext, parent Id will not be masked.
iDbAdapter->UpdateOwnerStatusL(aEntry.Parent(), parentEntry->Entry(), ETrue);
if(aCommitToDb)
iDbAdapter->CommitTransactionL();
);
#endif
if(err & KSqlErrConstraint != err)
{
if(aCommitToDb)
iDbAdapter->RollbackTransactionL();
// This will not leave...
TRAP_IGNORE(visibleFolder->DeleteEntryL(newCacheEntry->GetId()));
User::Leave(err);
}
parentEntry->Entry().SetOwner(ETrue);
if(!aCommitToDb)
{
// Add entry to the list of non-committed entries.
CNonCommittedAddedEntries* nonCommittedAddedEntries = new(ELeave)CNonCommittedAddedEntries(visibleFolderId, newCacheEntry);
CleanupStack::PushL(nonCommittedAddedEntries);
iNonCommittedAddedEntryList.AppendL(nonCommittedAddedEntries);
CleanupStack::Pop(nonCommittedAddedEntries);
}
if(aEntry.iType.iUid == KUidMsvMessageEntryValue && CMSvSearchSortCacheManager::Instance()->iManagerEntry != NULL)
{
TMsgType aType(ENewMsg);
if(CMSvSearchSortCacheManager::Instance()->iManagerEntry->Count()>0)
{
CMsvSearchSortDeltaCache::Instance()->EntryInDeltaCache(aEntry.Id(),aType);
}
}
}
/**
* GetVisibleFolderId()
*
* Returns ETrue, if the parent entry exists. Otherwise returns EFalse.
* IN: Parent Id of the entry, whose immediate visible folder has to be found.
* OUT: Visible folder Id.
*/
TBool CMsvIndexAdapter::GetVisibleFolderId(TMsvId aParentId, TMsvId& aVisibleFolderId)
{
// If the entry is either a root entry (parent=KErrNotFound) or
// child of root entry its visibleFolderId will be root entry itself.
if((KErrNotFound == aParentId) || (KMsvRootIndexEntryId == aParentId))
{
aVisibleFolderId = KMsvRootIndexEntryId;
return ETrue;
}
// Get the visible flag of parent entry.
// If parent entry is visible, then immediateVisibleFolder of child
// should be parent Id. And if it is invisible then child's immediateVisibleFolder
// should be same as parent's immediateVisibleFolder.
// First check in cache.
if(!iFolderListHeader.IsEmpty())
{
CMsvCacheVisibleFolder* folderNode = NULL;
TDblQueIter<CMsvCacheVisibleFolder> dqIter(iFolderListHeader);
dqIter.SetToFirst();
while((folderNode = dqIter++)!=NULL)
{
// Check if the current folder node is parent.
if( aParentId == folderNode->GetFolderId() )
{
aVisibleFolderId = aParentId;
return ETrue;
}
}
dqIter.SetToFirst();
CMsvCacheEntry* entry;
while((folderNode = dqIter++)!=NULL)
{
// Check if the current folder node is parent.
if(folderNode->GetEntry(aParentId, entry))
{
if(entry->Entry().VisibleFolderFlag())
{
aVisibleFolderId = aParentId;
}
else
{
//If the visible folder is not the parent, then
//the current folder must be the visible folder,
//as otherwise the parent would have been under another
//folder node.
aVisibleFolderId = folderNode->GetFolderId();
}
return ETrue;
}
}
}
// Entry cannot be found in cache,
// Now search in DB.
TInt err = KErrNone;
if(iDbAdapter)
{
TRAP(err, iDbAdapter->GetVisibleFolderIdL(aParentId, aVisibleFolderId));
}
else
{
return EFalse;
}
if(KErrNone == err)
{
return ETrue;
}
return EFalse;
}
/**
* DeleteEntry()
*/
TInt CMsvIndexAdapter::DeleteEntry(TMsvId aId)
{
CMsvEntrySelection* selection = NULL;
TRAPD(error, selection = new(ELeave)CMsvEntrySelection; selection->AppendL(aId););
if(KErrNone == error)
{
error = DeleteSelection(*selection);
}
TMsgType aType(EDeletedMsg);
if(CMSvSearchSortCacheManager::Instance()->iManagerEntry->Count()>0 && error == KErrNone)
{
CMsvSearchSortDeltaCache::Instance()->EntryInDeltaCache(aId,aType);
}
delete selection;
return error;
}
/**
* DeleteSelection
* Deletes a selection of entries.
*
* @param aSelection Selection of TMsvIds of the entries to be deleted
* @return TInt System-wide error code.
*/
TInt CMsvIndexAdapter::DeleteSelection(const CMsvEntrySelection& aSelection)
{
TInt count = aSelection.Count();
if(!iDbAdapter)
{
return ErrorState();
}
// Only start a transaction if there are more than 1 entries to be deleted.
if(count > 1)
{
TRAP_IGNORE(iDbAdapter->BeginTransactionL());
}
// If the transaction is opened, deleted
// entry will be released temporarily.
iFreePoolInstance->BeginTransaction();
TInt error = KErrNone;
TRAP(error, DoDeleteSelectionL(aSelection));
if (error)
{
if(count > 1)
{
TRAP_IGNORE(iDbAdapter->RollbackTransactionL());
}
iFreePoolInstance->RollbackTransaction();
}
else
{
if(count > 1)
{
TRAP_IGNORE(iDbAdapter->CommitTransactionL());
}
iFreePoolInstance->CommitTransaction();
}
return error;
}
/**
* DoDeleteSelectionL
*/
void CMsvIndexAdapter::DoDeleteSelectionL(const CMsvEntrySelection& aSelection)
{
User::LeaveIfError(iErrorState);
CMsvCacheVisibleFolder* folderNode = NULL;
TDblQueIter<CMsvCacheVisibleFolder> dqIter(iFolderListHeader);
const TInt count = aSelection.Count();
if(count <= 0)
{
return;
}
CMsvCacheEntry* entry = NULL;
TBool releaseEntry = FindEntryL(aSelection.At(0), entry, EFalse);
TMsvId parentId = entry->Entry().Parent();
if(releaseEntry)
{
iFreePoolInstance->ReleaseEntry(entry, ETrue);
}
CMsvEntrySelection* children = new(ELeave)CMsvEntrySelection;
CleanupStack::PushL(children);
for (TInt index=0; index<count; ++index)
{
dqIter.SetToFirst();
TBool found = EFalse;
while((folderNode = dqIter++)!=NULL)
{
TRAPD(err, folderNode->DeleteEntryL(aSelection.At(index)));
if(err != KErrNotFound && err != KErrNone)
{
User::Leave(err);
}
else if(err == KErrNone)
{
found = ETrue;
break;
}
}
if(!found)
{
if(iDbAdapter)
{
if(iDbAdapter->EntryExistsL(aSelection.At(index)))
{
User::Leave(KErrAccessDenied);
}
}
else
{
User::Leave(ErrorState());
}
User::Leave(KErrNotFound);
}
if(iDbAdapter)
{
iDbAdapter->DeleteEntryL(aSelection.At(index));
}
else
{
User::Leave(ErrorState());
}
}
//Find the parent entry in cache and check for any children.
//If there are no more children under the parent, then
//reset the owner flag in cache and database.
TRAPD(err, releaseEntry = FindEntryL(parentId, entry, EFalse));
//If parent was also deleted then nothing left to do.
if(err == KErrNotFound)
{
CleanupStack::PopAndDestroy(); //children
return;
}
TRAP(err,
GetChildrenIdL(parentId, *children);
if(0 == children->Count() && entry->Entry().Owner())
{
if(iDbAdapter)
{
iDbAdapter->UpdateOwnerStatusL(parentId, entry->Entry(), EFalse);
}
else
{
User::Leave(ErrorState());
}
entry->Entry().SetOwner(EFalse);
}
);
// Release the entry, since it is not added to cache.
if(releaseEntry)
{
iFreePoolInstance->ReleaseEntry(entry, ETrue);
}
User::LeaveIfError(err);
CleanupStack::PopAndDestroy(); //children
}
/**
* ExpandSelectionRecursively()
* Expands a selection to include all the descendents of the entries. They are order
* in such a way that they can be deleted in order, children first
*
* @param aSelection CMsvEntrySelection, which contains the TMsvIds of the entry passed.
* @return TInt System wide error code.
*/
TInt CMsvIndexAdapter::ExpandSelectionRecursively(CMsvEntrySelection& aSelection)
{
TInt error = KErrNone;
iOrigEntryPos=0;
iRecursionSelection=&aSelection;
TInt count = aSelection.Count();
while (iOrigEntryPos < count)
{
TMsvId entryId = aSelection.At(iOrigEntryPos);
if( !EntryExists(entryId) )
{
error = KErrNotFound;
aSelection.Delete(iOrigEntryPos);
}
else
{
// if the entry has children
CMsvEntrySelection* children = NULL;
TRAPD(err, children = new (ELeave)CMsvEntrySelection);
if(KErrNone != err)
{
return err;
}
TInt leave = KErrNone;
TRAP(leave, GetChildrenIdL(entryId, *children));
if(KErrNone != leave)
{
delete children;
return leave;
}
TInt count = children->Count();
for(TInt index=0; index<count; ++index)
{
iRecursionIndex = iOrigEntryPos;
TRAP(leave, DoExpandSelectionRecursivelyL(children->At(index)));
if(KErrNone != leave)
{
delete children;
return leave;
}
}
delete children;
++iOrigEntryPos;
}
}
return error;
}
/**
* DoExpandSelectionRecursivelyL
* Expands a selection to include all the descendents of the entry with TMsvId aId.
*
* @param aId TMsvId of the entire
* @return void
*/
void CMsvIndexAdapter::DoExpandSelectionRecursivelyL(TMsvId aId)
{
// include this entry
iRecursionSelection->InsertL(iRecursionIndex,aId);
iOrigEntryPos++;
// if aEntry has children
CMsvEntrySelection* children = new (ELeave)CMsvEntrySelection;
CleanupStack::PushL(children);
GetChildrenIdL(aId, *children);
TInt count = children->Count();
for(TInt index=0; index<count; ++index)
{
DoExpandSelectionRecursivelyL(children->At(index));
}
CleanupStack::PopAndDestroy(1, children); //children
}
/**
* GetChildrenId()
* @param TMsvId: Id os the parent.
* @param CMsvEntrySelection: List of child Ids.
*
* This function should be used only by ExpandSelectionRecursively()
* and DoExpandSelectionRecursivelyL().
* --- 557 ---
* If aId is an standard id ensure that it is masked. If the aId is
* not masked the function will return children from current drive.
*/
void CMsvIndexAdapter::GetChildrenIdL(TMsvId aId, CMsvEntrySelection& aSelection)
{
CMsvCacheVisibleFolder* folderNode = NULL;
TDblQueIter<CMsvCacheVisibleFolder> dqIter(iFolderListHeader);
#if (defined SYMBIAN_MSGS_ENHANCED_REMOVABLE_MEDIA_SUPPORT)
if(IsStandardId(aId))
{
TUint driveId = GetDriveId(aId);
TMsvId unmaskedId = UnmaskTMsvId(aId);
dqIter.SetToFirst();
// Look for the entry in cache.
if(!iFolderListHeader.IsEmpty())
{
while(NULL != (folderNode = dqIter++))
{
if((unmaskedId == folderNode->GetFolderId()) && (driveId == folderNode->GetDrive()))
{
if(!folderNode->GetChildrenIdL(aSelection))
{
if(iDbAdapter)
{
iDbAdapter->GetChildrenIdL(aId, aSelection);
}
else
{
User::Leave(ErrorState());
}
}
return;
}
}
}
if(iDbAdapter)
{
iDbAdapter->GetChildrenIdL(aId, aSelection);
}
else
{
User::Leave(ErrorState());
}
return;
}
#endif // #if (defined SYMBIAN_MSGS_ENHANCED_REMOVABLE_MEDIA_SUPPORT)
dqIter.SetToFirst();
// Look for the entry in cache.
if(!iFolderListHeader.IsEmpty())
{
// Check if aId is one of the visible folder,
// already present in the cache.
while(NULL != (folderNode = dqIter++))
{
if(aId == folderNode->GetFolderId())
{
// Fetch children. Returns EFalse if unable
// to get them.
if(!folderNode->GetChildrenIdL(aSelection))
{
if(iDbAdapter)
{
// Childrens not fetched. Get them from DB.
iDbAdapter->GetChildrenIdL(aId, aSelection);
}
else
{
User::Leave(ErrorState());
}
}
return;
}
}
// Entry is not present in the visibleFolder list.
CMsvCacheEntry* entry = NULL;
if(FindEntryInCache(aId, entry))
{
if(NULL != entry->ChildIdArray())
{
RArray<TMsvId>* childId = entry->ChildIdArray();
TInt size = childId->Count();
TInt index = 0;
while(index < size)
{
aSelection.AppendL((*childId)[index++]);
}
return;
}
else
{
if(iDbAdapter)
{
iDbAdapter->GetChildrenIdL(aId, aSelection);
}
else
{
User::Leave(ErrorState());
}
RArray<TMsvId>* childId = new(ELeave) RArray<TMsvId>;
TInt size = aSelection.Count();
TInt index = 0;
while(index < size)
{
TInt err = childId->Append(aSelection[index++]);
if(KErrNone != err)
{
childId->Reset();
childId->Close();
delete childId;
User::Leave(err);
}
}
entry->SetChildIdArray(childId);
return;
}
}
}
// Could not find parent entry in cache.
// Check in DB now.
if(iDbAdapter)
{
iDbAdapter->GetChildrenIdL(aId, aSelection);
}
else
{
User::Leave(ErrorState());
}
}
/**
* ChangeEntry
* Change the contents of a TMsvEntry.
*
*/
TInt CMsvIndexAdapter::ChangeEntry(const TMsvEntry& aEntry, TSecureId aOwnerId, TBool aForcedUpdate)
{
TRAPD(err, DoChangeEntryL(aEntry, aOwnerId, EFalse, aForcedUpdate, ETrue));
return err;
}
/**
* ChangeEntryNoCommit
*/
TInt CMsvIndexAdapter::ChangeEntryNoCommit(const TMsvEntry& aEntry, TSecureId aOwnerId, TBool aForcedUpdate)
{
if(!iDbAdapter)
{
return ErrorState();
}
TRAP_IGNORE(iDbAdapter->BeginTransactionL());
TRAPD(err, DoChangeEntryL(aEntry, aOwnerId, EFalse, aForcedUpdate, EFalse));
if(err)
{
RollbackChanges();
}
return err;
}
/**
* ChangeEntryInternal()
*/
TInt CMsvIndexAdapter::ChangeEntryInternal(const TMsvEntry& aEntry, TSecureId aOwnerId)
{
TRAPD(err, DoChangeEntryL(aEntry, aOwnerId, ETrue, EFalse, ETrue));
return err;
}
/**
* DoChangeEntryL
*/
void CMsvIndexAdapter::DoChangeEntryL(const TMsvEntry& aNewEntryContents, TSecureId aOwnerId, TBool aChangeStandardFolder, TBool aForcedUpdate, TBool aCommitToFile)
{
// Leave if the message store is not currently available
User::LeaveIfError(iErrorState);
// 1. Handle root entry differently.
if(aNewEntryContents.Id() == KMsvRootIndexEntryId)
{
TMsvEntry& entry = const_cast<TMsvEntry&> (aNewEntryContents);
iDbAdapter->UpdateEntryL(entry, KMsvRootIndexEntryId, EFalse);
iRootEntry->Entry() = entry;
return;
}
// 2. Find entry in cache.
CMsvCacheEntry* oldEntry = NULL;
CMsvCacheVisibleFolder* oldFolderNode = NULL;
GetVisibleFolderDetailsL(aNewEntryContents.Id(), oldEntry, oldFolderNode);
// 3. Validate entry.
// If the entry is not present in cache, it means it is not locked.
// Can only change a locked entry and not one marked as a standard
// folder unless aChangeStandardFolder is true.
if( (!oldEntry->IsEntryLocked()) || (aChangeStandardFolder==EFalse && oldEntry->Entry().StandardFolder()) )
{
User::Leave(KErrAccessDenied);
}
// Reserving space in changed entry list array.
if(!aCommitToFile)
{
iNonCommittedChangedEntryList.ReserveL(iNonCommittedChangedEntryList.Count()+1);
}
#if (defined SYMBIAN_MSGS_ENHANCED_REMOVABLE_MEDIA_SUPPORT)
// We do not support movement of entry to a folder present in a different drive.
if( oldEntry->Entry().Parent() != KErrNotFound && //-1 will appear to be masked. disregard this.
GetDriveId(aNewEntryContents.Id()) != GetDriveId(oldEntry->Entry().Parent()))
{
User::Leave(KErrNotSupported);
}
#endif
TMsvId oldParentId = oldEntry->Entry().Parent();
TMsvId oldVisibleFolderId = oldFolderNode->GetFolderId();
// Check if the parent id is also changed.
CMsvCacheEntry* oldParentEntry = NULL;
CMsvCacheEntry* newParentEntry = NULL;
TBool resetOldParentOwnerFlag = EFalse;
CMsvEntrySelection* descendentList = NULL;
TMsvId newParentId = aNewEntryContents.Parent();
CMsvCacheVisibleFolder* newVisibleFolderNode = NULL;
if(oldParentId != newParentId)
{
// YES, PARENT ID IS CHANGED.
// These steps are similar to MoveEntry()
newVisibleFolderNode = DoChangeEntryPreambleL(oldEntry,
newParentId,
oldParentEntry,
newParentEntry,
oldVisibleFolderId,
resetOldParentOwnerFlag,
descendentList
);
} // if(oldParentId != newParentId)
TMsvId newVisibleFolderId = newVisibleFolderNode ? newVisibleFolderNode->GetFolderId() : oldVisibleFolderId;
// Update entry in cache.
TBool changedPrivateInfo;
CMsvCacheEntry* bkpEntry = NULL;
TRAPD(err, bkpEntry = iFreePoolInstance->EntryL();
bkpEntry->SetEntryL(oldEntry->Entry());
bkpEntry->SetEntryOwnerId(oldEntry->EntryOwnerId());
oldEntry->CopyEntryL(aNewEntryContents, changedPrivateInfo);
);
if(err)
{
iFreePoolInstance->ReleaseEntryWithoutTransaction(bkpEntry);
if(newVisibleFolderNode)
newVisibleFolderNode->DeleteEntryL(oldEntry->GetId());
User::Leave(err);
}
UpdateDates(*oldEntry, EFalse);
if(aNewEntryContents.Connected())
{
oldEntry->Entry().SetConnected(EFalse);
}
if(aForcedUpdate || changedPrivateInfo && aOwnerId != KMsvServerId )
{
oldEntry->SetEntryOwnerId(aOwnerId);
}
TRAP(err,
if(aCommitToFile)
iDbAdapter->BeginTransactionL();
// Update the actual entry
if(oldParentId != newParentId)
{
iDbAdapter->UpdateEntryL(oldEntry->Entry(), newVisibleFolderId);
}
else
{
iDbAdapter->UpdateEntryL(oldEntry->Entry(), newVisibleFolderId, EFalse);
}
// Reset old parent owner flag.
if(resetOldParentOwnerFlag)
iDbAdapter->UpdateOwnerStatusL(oldParentId, oldParentEntry->Entry(), EFalse);
// Set new parent owner flag.
if(newParentEntry && (!newParentEntry->Entry().Owner()))
iDbAdapter->UpdateOwnerStatusL(newParentId, newParentEntry->Entry(), ETrue);
// Update the child entries visibleFolderId in DB.
if(descendentList)
iDbAdapter->UpdateVisibleFolderL(descendentList, newVisibleFolderId);
if(aCommitToFile)
iDbAdapter->CommitTransactionL();
);
if(err)
{
if(aCommitToFile)
TRAP_IGNORE(iDbAdapter->RollbackTransactionL());
oldEntry->SetEntryOwnerId(bkpEntry->EntryOwnerId());
oldEntry->CopyEntryL(bkpEntry->Entry(), changedPrivateInfo);
iFreePoolInstance->ReleaseEntryWithoutTransaction(bkpEntry);
if(newVisibleFolderNode)
newVisibleFolderNode->DeleteEntryL(oldEntry->GetId());
User::Leave(err);
}
// Nothing should fail after this. Ensure that
// all leaving function after this does not leave.
if(aCommitToFile)
{
iFreePoolInstance->ReleaseEntryWithoutTransaction(bkpEntry);
DoChangeEntryPostamble(oldFolderNode, newVisibleFolderId, oldEntry->GetId(), descendentList, newParentEntry, oldParentEntry, resetOldParentOwnerFlag);
if(descendentList)
{
CleanupStack::PopAndDestroy(descendentList);
}
} // if(aCommitToFile)
else
{
// Store relevant data for later commit.
TNonCommittedChangedEntries entryDetails(oldFolderNode,
newVisibleFolderNode,
oldEntry,
bkpEntry,
oldParentEntry,
newParentEntry,
descendentList,
resetOldParentOwnerFlag);
iNonCommittedChangedEntryList.Append(entryDetails);
if(descendentList)
CleanupStack::Pop(descendentList);
}
// Update entry in SearchSort delta cache.
if(aNewEntryContents.iType.iUid == KUidMsvMessageEntryValue && CMSvSearchSortCacheManager::Instance()->iManagerEntry != NULL)
{
TMsgType aType(EUpdatedMsg);
if(CMSvSearchSortCacheManager::Instance()->iManagerEntry->Count()>0)
{
CMsvSearchSortDeltaCache::Instance()->EntryInDeltaCache(aNewEntryContents.Id(), aType);
}
}
}
// Used only by DoChangeEntryL()
CMsvCacheVisibleFolder* CMsvIndexAdapter::DoChangeEntryPreambleL(CMsvCacheEntry*& aOldEntry, TMsvId aNewParentId, CMsvCacheEntry*& aOldParentEntry, CMsvCacheEntry*& aNewParentEntry, TMsvId aOldVisibleFolderId, TBool& aResetOldParentOwnerFlag, CMsvEntrySelection*& aDescendentList)
{
// YES, PARENT ID IS CHANGED.
// These steps are similar to MoveEntry()
TBool des;
IsADescendent(aOldEntry->Entry().Id(), aNewParentId, des);
if (des)
{
User::Leave(KErrArgument);
}
// Fetch the parent entries.
FindEntryL(aOldEntry->Entry().Parent(), aOldParentEntry);
FindEntryL(aNewParentId, aNewParentEntry);
// Check if the owner flag of the old parent needs to be reset.
CMsvEntrySelection* children = new(ELeave)CMsvEntrySelection;
CleanupStack::PushL(children);
GetChildrenIdL(aOldEntry->Entry().Parent(), *children);
if(1 == children->Count() && aOldParentEntry->Entry().Owner())
{
aResetOldParentOwnerFlag = ETrue;
}
CleanupStack::PopAndDestroy(); //children
// If required, pre-allocate memory for child array of newParentEntry.
if(aNewParentEntry->ChildIdArray())
{
// This to ensure that future append does not fail.
aNewParentEntry->ChildIdArray()->ReserveL(aNewParentEntry->ChildIdArray()->Count() + 1);
}
// Find the visibleFolder of new parent.
TMsvId newVisibleFolderId = NULL;
if(!GetVisibleFolderId(aNewParentId, newVisibleFolderId))
{
User::Leave(KErrNotFound);
}
// Check if the visible folders are different.
CMsvCacheVisibleFolder* newVisibleFolderNode = NULL;
if(aOldVisibleFolderId != newVisibleFolderId)
{
// If the visible folders are different, check if
// child entries also needs to be moved.
if(!aOldEntry->Entry().VisibleFolderFlag())
{
// If yes, create a descendent list of the entry.
aDescendentList = new(ELeave) CMsvEntrySelection;
CleanupStack::PushL(aDescendentList);
aDescendentList->AppendL(aOldEntry->GetId());
User::LeaveIfError(ExpandSelectionRecursively(*aDescendentList));
}
// Add duplicate entry under new visible folder. Removing
// entry from old visible folder is done later.
CMsvCacheEntry* newCacheEntry = iFreePoolInstance->EntryL();
TRAPD(err, newCacheEntry->DupNDestroyL(aOldEntry);
newVisibleFolderNode = AddEntryToCacheL(newVisibleFolderId, newCacheEntry);
);
if(err)
{
iFreePoolInstance->ReleaseEntryWithoutTransaction(newCacheEntry);
User::Leave(err);
}
aOldEntry = newCacheEntry;
} // if(oldVisibleFolderId != newVisibleFolderId) }
return newVisibleFolderNode;
}
// Used only by DoChangeEntryL()
void CMsvIndexAdapter::DoChangeEntryPostamble(CMsvCacheVisibleFolder* aOldFolderNode, TMsvId aNewVisibleFolderId, TMsvId aEntryId, CMsvEntrySelection* aDescendentList, CMsvCacheEntry* aNewParentEntry, CMsvCacheEntry* aOldParentEntry, TBool aResetOldParentOwnerFlag)
{
// Removing entry from old visible folder.
if(aOldFolderNode->GetFolderId() != aNewVisibleFolderId)
{
// Will never leave.
//aOldFolderNode->DeleteEntryL(oldEntry->GetId());
TRAP_IGNORE(aOldFolderNode->DeleteEntryL(aEntryId));
}
// Removing child entries from old visible folder.
if(aDescendentList)
{
for(TInt index=(aDescendentList->Count()-2); index>=0; index--)
{
// Can leave with KErrNotFound.
TRAP_IGNORE(aOldFolderNode->DeleteEntryL(aDescendentList->At(index)));
}
}
// Add child id to new parent child array.
if(aNewParentEntry && aNewParentEntry->ChildIdArray())
{
// This will not leave, as memory is already reserved.
TRAP_IGNORE(aNewParentEntry->ChildIdArray()->AppendL(aEntryId));
}
// Remove child from old parent's child array.
if(aOldParentEntry)
{
RArray<TMsvId>* oldParentChildArr = aOldParentEntry->ChildIdArray();
if(oldParentChildArr)
{
TInt pos = oldParentChildArr->Find(aEntryId);
if(pos != KErrNotFound)
{
oldParentChildArr->Remove(pos);
}
}
}
// Update owner flag of parent entries.
if(aResetOldParentOwnerFlag)
{
aOldParentEntry->Entry().SetOwner(EFalse);
}
if(aNewParentEntry)
{
aNewParentEntry->Entry().SetOwner(ETrue);
}
}
/**
* GetEntry()
*/
TInt CMsvIndexAdapter::GetEntry(TMsvId aId, TMsvEntry*& aEntry)
{
TSecureId dummy;
return GetEntry(aId, aEntry, dummy);
}
/**
* GetEntryNoCache()
* If aAddToCache is EFalse, entry will not be added to cache,
* if the entry is fetched from DB. The user of this function
* should ensure that it releses the memory occupied by entry
* to the freepool.
*/
TInt CMsvIndexAdapter::GetEntryNoCache(TMsvId aId, TMsvEntry* aEntry)
{
if(KMsvRootIndexEntryId == aId)
{
*aEntry = iRootEntry->Entry();
return KErrNone;
}
TBool aIsDanglingEntry = EFalse;
CMsvCacheEntry* serverEntry=NULL;
TRAPD(err, aIsDanglingEntry = FindEntryL(aId, serverEntry, EFalse));
if (err == KErrNone)
{
*aEntry = serverEntry->Entry();
// If entry is not present in cache,
// it should be handled carefully.
if(aIsDanglingEntry)
{
// Release CMsvCacheEntry to freepool.
iFreePoolInstance->ReleaseEntry(serverEntry, ETrue);
}
return KErrNone;
}
else
{
return KErrNotFound;
}
}
/**
* GetEntry()
*/
TInt CMsvIndexAdapter::GetEntry(TMsvId aId, TMsvEntry*& aEntry, TSecureId& aOwnerId)
{
if(KMsvRootIndexEntryId == aId)
{
aEntry = &iRootEntry->Entry();
aOwnerId = 0;
return KErrNone;
}
CMsvCacheEntry* serverEntry=NULL;
TRAPD(err, FindEntryL(aId, serverEntry));
if (err == KErrNone)
{
aEntry = &serverEntry->Entry();
aOwnerId = serverEntry->EntryOwnerId();
return KErrNone;
}
else
{
return KErrNotFound;
}
}
/**
* FindEntryInCache()
* @param aId: Entry Id.
* @return aEntry: CMsvCacheEntry
* @return bool: ETrue if entry found, otherwise EFalse.
*
* Find an entry only in cache and return the entry.
*/
TBool CMsvIndexAdapter::FindEntryInCache(TMsvId aId, CMsvCacheEntry*& aEntry)
{
if(KMsvRootIndexEntryId == aId)
{
aEntry = iRootEntry;
return ETrue;
}
// If cache is not empty.
if(!iFolderListHeader.IsEmpty())
{
CMsvCacheVisibleFolder* folderNode = NULL;
TDblQueIter<CMsvCacheVisibleFolder> dqIter(iFolderListHeader);
dqIter.SetToFirst();
while((folderNode = dqIter++)!=NULL)
{
if(folderNode->GetEntry(aId, aEntry))
{
return ETrue;
}
}
}
aEntry = NULL;
return EFalse;
}
/**
* FindEntryL()
*
* WARNING:
* Returns CMsvCacheEntry which is still owned by Cache. so aEntry should
* never be deleted by the caller.
*
* If the entry is fetched from DB and not added to cache, the function
* returns ETrue, indicating that aEntry should be release to the freepool
* by the caller.
*/
TBool CMsvIndexAdapter::FindEntryL(TMsvId aId, CMsvCacheEntry*& aEntry, TBool aAddToCache /*DEFAULT=ETrue */)
{
// First search the entry in cache,
if(FindEntryInCache(aId, aEntry))
{
return EFalse;
}
// Entry cannot be found in cache,
// Now search in DB.
TMsvId visibleEntryId;
if(iDbAdapter)
{
iDbAdapter->GetEntryL(aId, aEntry, visibleEntryId);
}
else
{
User::Leave(ErrorState());
}
if(aAddToCache)
{
// Add entry to cache.
AddEntryToCacheL(visibleEntryId, aEntry);
}
else
{
// If the entry is not added to cache, we need not start
// the background operation. We can return ETrue saying
// entry is dangling and needs to be freed.
return ETrue;
}
if (!iIdle->IsActive())
{
iIdle->Start(TCallBack(BackGroundOperations, this));
}
return EFalse;
}
/**
* AddEntryNoVisibleL()
*/
void CMsvIndexAdapter::AddEntryNoVisibleL(CMsvCacheEntry* aEntry)
{
TMsvId visibleFolder;
if(GetVisibleFolderId(aEntry->Entry().Parent(), visibleFolder))
{
AddEntryToCacheL(visibleFolder, aEntry);
}
}
/**
* AddEntryToCacheL()
*/
CMsvCacheVisibleFolder* CMsvIndexAdapter::AddEntryToCacheL(TMsvId aVisibleEntryId, CMsvCacheEntry* aEntry)
{
#if (defined SYMBIAN_MSGS_ENHANCED_REMOVABLE_MEDIA_SUPPORT)
TUint driveId = GetDriveId(aEntry->GetId());
if(IsStandardId(aEntry->GetId()))
{
aEntry->Entry().SetId(UnmaskTMsvId(aEntry->GetId()));
}
#endif
// First search the visibleEntry in cache,
// if cache is not empty...
if(!iFolderListHeader.IsEmpty())
{
CMsvCacheVisibleFolder* folderNode = NULL;
TDblQueIter<CMsvCacheVisibleFolder> dqIter(iFolderListHeader);
dqIter.SetToFirst();
while((folderNode = dqIter++)!=NULL)
{
TMsvId folderId = folderNode->GetFolderId();
if(aVisibleEntryId == folderId)
{
// Visible folder exists.
#if (defined SYMBIAN_MSGS_ENHANCED_REMOVABLE_MEDIA_SUPPORT)
// For standard entries also check for driveId.
if(IsStandardId(aVisibleEntryId))
{
if(driveId == folderNode->GetDrive())
{
folderNode->AddEntryL(aEntry);
return folderNode;
}
}
else
#endif
{
// Add entry to this folder.
folderNode->AddEntryL(aEntry);
return folderNode;
}
}
}
}
// Visible folder not found in the folder list.
// Create the visible folder and add it to the
// beginning of the list.
#if (defined SYMBIAN_MSGS_ENHANCED_REMOVABLE_MEDIA_SUPPORT)
CMsvCacheVisibleFolder* newFolder = CMsvCacheVisibleFolder::NewL(MaskTMsvId(GetDriveId(aEntry->GetId()), aVisibleEntryId));
#else
CMsvCacheVisibleFolder* newFolder = CMsvCacheVisibleFolder::NewL(aVisibleEntryId);
#endif
iFolderListHeader.AddFirst(*newFolder);
// Now add entry to the visible folder
newFolder->AddEntryL(aEntry);
return newFolder;
}
/**
* GetChildrenL()
*/
void CMsvIndexAdapter::GetChildrenL(TMsvId aId, CArrayPtr<const TMsvEntry>& aSelection, const TMsvSelectionOrdering& aOrdering, TUid aMtm)
{
TSecureId dummy = KMsvServerId.iId;
GetChildrenL(aId, aSelection, aOrdering, aMtm, EFalse, dummy);
}
/**
* GetChildrenL()
*/
void CMsvIndexAdapter::GetChildrenL(TMsvId aId, CArrayPtr<const TMsvEntry>& aSelection, const TMsvSelectionOrdering& aOrdering, TUid aMtm, TBool aFilterByOwnerId, TSecureId aOwnerId)
{
CMsvEntryFilter* filter = CMsvEntryFilter::NewLC();
filter->SetOrder(aOrdering);
filter->SetSortMtm(aMtm);
CMsvEntryArray* entries = DoGetChildrenL(aId, *filter, aFilterByOwnerId, aOwnerId);
CleanupStack::PushL(entries);
TInt count = entries->Count();
for (TInt ii=0; ii<count; ii++)
{
aSelection.AppendL(entries->At(ii));
}
CleanupStack::PopAndDestroy(2, filter); // filter and entries
}
/**
* DoGetChildrenL
*/
CMsvEntryArray* CMsvIndexAdapter::DoGetChildrenL(TMsvId aId, const CMsvEntryFilter& aFilter, TBool aFilterByOwnerId, TSecureId aOwnerId)
{
// Check if entry for aId exists...
CMsvCacheEntry* entry=NULL;
#if (defined SYMBIAN_MSGS_ENHANCED_REMOVABLE_MEDIA_SUPPORT)
TUint driveId = GetDriveId(aId);
TMsvId unmaskedId = UnmaskTMsvId(aId);
if(IsStandardId(aId))
{
FindEntryL(unmaskedId, entry);
}
else
#endif
{
FindEntryL(aId, entry);
}
TBool isParentAVisibleFolder = EFalse;
if(entry->Entry().VisibleFolderFlag())
{
isParentAVisibleFolder = ETrue;
}
CArrayFixFlat<TUid>* mtmList = new(ELeave) CArrayFixFlat<TUid>(KMsvMtmListGranularity);
CleanupStack::PushL(mtmList);
if (KUidMsvNullEntry != aFilter.SortMtm())
{
mtmList->AppendL(aFilter.SortMtm());
}
RPointerArray<CMsvCacheEntry> selection;
CleanupClosePushL(selection);
CMsvCacheVisibleFolder* folderNode = NULL;
CMsvEntryArray* entries = CMsvEntryArray::NewLC(*mtmList);
TDblQueIter<CMsvCacheVisibleFolder> dqIter(iFolderListHeader);
#if (defined SYMBIAN_MSGS_ENHANCED_REMOVABLE_MEDIA_SUPPORT)
if(IsStandardId(aId))
{
// Look for the entry in cache.
if(!iFolderListHeader.IsEmpty())
{
dqIter.SetToFirst();
// Check if aId is one of the visible folder,
// already present in the cache.
while(NULL != (folderNode = dqIter++))
{
if((folderNode->GetFolderId() == unmaskedId) && (folderNode->GetDrive() == driveId))
{
folderNode->GetChildrenL(aId, iDbAdapter, selection);
FilterChildrenListL(aId, selection, *entries, aFilter, aFilterByOwnerId, aOwnerId);
CleanupStack::Pop(entries); // Fetch entries from stack. It will be destroyed by caller.
CleanupStack::PopAndDestroy(2);
return entries;
}
}
}
}
else
{
#endif // #if (defined SYMBIAN_MSGS_ENHANCED_REMOVABLE_MEDIA_SUPPORT)
// Look for the entry in cache.
if(!iFolderListHeader.IsEmpty())
{
dqIter.SetToFirst();
// Check if aId is one of the visible folder,
// already present in the cache.
while(NULL != (folderNode = dqIter++))
{
if(aId == folderNode->GetFolderId())
{
if(iDbAdapter == NULL)
{
User::Leave(ErrorState());
}
folderNode->GetChildrenL(aId, iDbAdapter, selection);
FilterChildrenListL(aId, selection, *entries, aFilter, aFilterByOwnerId, aOwnerId);
CleanupStack::Pop(entries); // Fetch entries from stack. It will be destroyed by caller.
CleanupStack::PopAndDestroy(2);
return entries;
}
}
// If the parent is not a visible folder
// check if it is child of one of the visible folder.
if(EFalse == isParentAVisibleFolder)
{
dqIter.SetToFirst();
while(NULL != (folderNode = dqIter++))
{
TBool retVal=EFalse;
TRAPD(err, retVal = folderNode->GetChildrenL(aId, iDbAdapter, selection))
// aId not found under current visibleFolder.
if(KErrNotFound == err)
{
continue;
}
// aId found under current visibleFolder.
if(retVal)
{
// Children is fetched succesfully.
FilterChildrenListL(aId, selection, *entries, aFilter, aFilterByOwnerId, aOwnerId);
CleanupStack::Pop(entries); // Fetch entries from stack. It will be destroyed by caller.
CleanupStack::PopAndDestroy(2);
return entries;
}
else
{
// aId itself is a visibleFolder.
isParentAVisibleFolder = ETrue;
break;
}
}
}
}
#if (defined SYMBIAN_MSGS_ENHANCED_REMOVABLE_MEDIA_SUPPORT)
}
#endif // #if (defined SYMBIAN_MSGS_ENHANCED_REMOVABLE_MEDIA_SUPPORT)
// Could not find parent entry in cache.
// Check in DB now.
TInt err = KErrNone;
if(iDbAdapter)
{
TRAP(err, iDbAdapter->GetChildrenL(aId, selection));
}
else
{
User::Leave(ErrorState());
}
if(err)
{
User::Leave(KErrNotFound);
}
FilterChildrenListL(aId, selection, *entries, aFilter, aFilterByOwnerId, aOwnerId);
// Check if the entire entry list can be added to cache.
// This is important in a situation when all childs of a
// folder cannot be accomodated in cache. In such scenario
// only entries with higher TMsvId will be added to cache.
TInt excessEntries = iFreePoolInstance->ExcessMemoryAllocated();
TBool isCompleteChildren = ETrue;
if(excessEntries)
{
isCompleteChildren = EFalse;
}
while(excessEntries)
{
if(selection.Count())
{
iFreePoolInstance->RecordExcessMemoryL(selection[0]);
selection.Remove(0);
}
--excessEntries;
}
// Add children to cache.
TMsvId visibleFolderId;
if(!isParentAVisibleFolder)
{
if(!GetVisibleFolderId(aId, visibleFolderId))
{
User::Leave(KErrNotFound);
}
}
else
{
visibleFolderId = aId;
}
TBool isEntryAdded = EFalse;
dqIter.SetToFirst();
while((folderNode = dqIter++)!=NULL)
{
if(visibleFolderId == folderNode->GetFolderId())
{
#if (defined SYMBIAN_MSGS_ENHANCED_REMOVABLE_MEDIA_SUPPORT)
if(IsStandardId(visibleFolderId))
{
if(GetDriveId(aId) == folderNode->GetDrive())
{
isEntryAdded=ETrue;
folderNode->AddEntryListL(selection, (isCompleteChildren && isParentAVisibleFolder));
}
}
else
#endif
{
isEntryAdded=ETrue;
folderNode->AddEntryListL(selection, (isCompleteChildren && isParentAVisibleFolder));
}
}
}
if(!isEntryAdded)
{
// If aId is the visibleFolder itself then pass
// aIsCompleteChildOfFolder as ETrue.
#if (defined SYMBIAN_MSGS_ENHANCED_REMOVABLE_MEDIA_SUPPORT)
// Standard Id can be unmasked.
CMsvCacheVisibleFolder* newFolder = CMsvCacheVisibleFolder::NewL(MaskTMsvId(GetDriveId(aId), visibleFolderId));
#else
CMsvCacheVisibleFolder* newFolder = CMsvCacheVisibleFolder::NewL(visibleFolderId);
#endif
iFolderListHeader.AddFirst(*newFolder);
newFolder->AddEntryListL(selection, (isCompleteChildren && isParentAVisibleFolder));
}
CleanupStack::Pop(entries); // Fetch entries from stack. It will be destroyed by caller.
CleanupStack::PopAndDestroy(2);
//Start the background operations, if not running.
if (!iIdle->IsActive())
{
iIdle->Start(TCallBack(BackGroundOperations, this));
}
return entries;
}
/**
* GetChildrenId()
*/
TInt CMsvIndexAdapter::GetChildrenId(TMsvId aId, const CMsvEntryFilter& aFilter, CMsvEntrySelection& aSelection)
{
TSecureId dummy = KMsvServerId.iId;
return GetChildrenId(aId, aFilter, aSelection, EFalse, dummy);
}
/**
* GetChildrenId()
*/
TInt CMsvIndexAdapter::GetChildrenId(TMsvId aId, const CMsvEntryFilter& aFilter, CMsvEntrySelection& aSelection, TBool aFilterByOwnerId, TSecureId aOwnerId)
{
CMsvEntryArray* entries = NULL;
TInt err = KErrNone;
TRAP(err, entries=DoGetChildrenL(aId, aFilter, aFilterByOwnerId, aOwnerId));
if(err)
{
delete entries;
return err;
}
TInt count = entries->Count();
for(TInt i=0; i<count; ++i)
{
TRAP(err, aSelection.AppendL(entries->At(i)->Id()));
}
delete entries;
return err;
}
/**
* FilterChildrenListL
*/
void CMsvIndexAdapter::FilterChildrenListL(TMsvId aId, RPointerArray<CMsvCacheEntry> aChacheEntry, CMsvEntryArray& aEntryArray, const CMsvEntryFilter& aFilter, TBool aFilterByOwnerId, TSecureId aOwnerId)
{
TBool getInvisibleEntries = aFilter.Order().ShowInvisibleEntries();
TInt count = aChacheEntry.Count();
for(TInt i=0; i<count; ++i)
{
TMsvEntry entry = aChacheEntry[i]->Entry();
TBool vis = aChacheEntry[i]->Entry().Visible();
if ((aChacheEntry[i]->Entry().Visible() || getInvisibleEntries) &&
(aFilter.Service() == KMsvNullIndexEntryId || aChacheEntry[i]->Entry().iServiceId == aFilter.Service()) &&
(aFilter.Mtm() == KNullUid || aChacheEntry[i]->Entry().iMtm == aFilter.Mtm()) &&
(aFilter.Type() == KNullUid || aChacheEntry[i]->Entry().iType == aFilter.Type()) &&
(aFilter.LastChangeDate().Int64() == 0 || aChacheEntry[i]->LastChangeDate() >= aFilter.LastChangeDate()))
{
// Add the entry if -
// 1. Not filtering by owner ID OR
// 2. Filtering by owner ID, but entry owner ID matches OR
// 3. Entry is a standard folder OR
// 4. Entrt is a service entry.
if( !aFilterByOwnerId ||
aChacheEntry[i]->EntryOwnerId() == aOwnerId ||
aChacheEntry[i]->Entry().StandardFolder() ||
aChacheEntry[i]->Entry().iType == KUidMsvServiceEntry )
{
aEntryArray.AppendL(&aChacheEntry[i]->Entry());
}
}
}
if (aEntryArray.Count())
{
if (aId==KMsvRootIndexEntryId)
{
aEntryArray.SortL(TMsvSelectionOrdering(KMsvNoGrouping, aFilter.Order().Sorting()));
}
else
{
aEntryArray.SortL(aFilter.Order());
}
}
}
/**
* LockEntry()
*/
TInt CMsvIndexAdapter::LockEntry(TMsvId aId)
{
//__DEBUG_INVARIANT_ONEXIT;
CMsvCacheEntry* entry = NULL;
TRAPD(err, FindEntryL(aId, entry))
if (err)
{
return KErrNotFound;
}
return entry->LockEntry();
}
/**
* ReleaseEntry()
*/
TInt CMsvIndexAdapter::ReleaseEntry(TMsvId aId)
{
//__DEBUG_INVARIANT_ONEXIT;
CMsvCacheEntry* entry = NULL;
if(FindEntryInCache(aId, entry))
{
entry->ReleaseEntry();
return KErrNone;
}
else
{
return KErrNotFound;
}
}
/**
* IsEntryLocked()
*/
TInt CMsvIndexAdapter::IsEntryLocked(TMsvId aId, TBool& aLocked)
{
CMsvCacheEntry* entry = NULL;
TRAPD(err, FindEntryL(aId, entry));
if(KErrNone != err)
{
aLocked = EFalse;
return err;
}
aLocked = entry->IsEntryLocked();
return KErrNone;
}
/**
* LockStore()
*/
TInt CMsvIndexAdapter::LockStore(TMsvId aId)
{
//__DEBUG_INVARIANT_ONEXIT;
CMsvCacheEntry* entry = NULL;
TRAPD(err, FindEntryL(aId, entry));
if (err)
{
return KErrNotFound;
}
return entry->LockStore();
}
/**
* ReleaseStore()
*/
TInt CMsvIndexAdapter::ReleaseStore(TMsvId aId)
{
//__DEBUG_INVARIANT_ONEXIT;
CMsvCacheEntry* entry = NULL;
if(FindEntryInCache(aId, entry))
{
entry->ReleaseStore();
return KErrNone;
}
else
{
return KErrNotFound;
}
}
/**
* IsStoreLocked()
*/
TInt CMsvIndexAdapter::IsStoreLocked(TMsvId aId, TBool& aLocked)
{
CMsvCacheEntry* entry = NULL;
TRAPD(err, FindEntryL(aId, entry));
if(KErrNone != err)
{
aLocked = EFalse;
return err;
}
aLocked = entry->IsStoreLocked();
return KErrNone;
}
/**
* IsEntryAndStoreLocked()
*/
TInt CMsvIndexAdapter::IsEntryOrStoreLocked(TMsvId aId)
{
CMsvCacheEntry* entry = NULL;
TBool locked = EFalse;
if(FindEntryInCache(aId, entry))
{
locked = entry->IsEntryOrStoreLocked();
}
if(locked)
{
return KErrLocked;
}
else
{
return KErrNone;
}
}
/**
* LockEntryAndStore()
*/
TInt CMsvIndexAdapter::LockEntryAndStore(TMsvId aId)
{
//__DEBUG_INVARIANT_ONEXIT;
CMsvCacheEntry* entry = NULL;
TRAPD(err, FindEntryL(aId, entry));
if (err)
{
return KErrNotFound;
}
return entry->LockEntryAndStore();
}
/**
* ReleaseEntryAndStore()
*/
TInt CMsvIndexAdapter::ReleaseEntryAndStore(TMsvId aId)
{
//__DEBUG_INVARIANT_ONEXIT;
CMsvCacheEntry* entry = NULL;
if(FindEntryInCache(aId, entry))
{
entry->ReleaseEntryAndStore();
return KErrNone;
}
else
{
return KErrNotFound;
}
}
/**
* IsEntryOrStoreLocked()
*/
TInt CMsvIndexAdapter::IsEntryOrStoreLocked(TMsvId aId, TBool& aLocked)
{
CMsvCacheEntry* entry = NULL;
if(FindEntryInCache(aId, entry))
{
aLocked = entry->IsEntryOrStoreLocked();
}
else
{
aLocked = EFalse;
}
return KErrNone;
}
/**
* IsStoreReadingLocked()
*/
TInt CMsvIndexAdapter::IsStoreReadingLocked(TMsvId aId, TBool& aLocked)
{
CMsvCacheEntry* entry = NULL;
if(FindEntryInCache(aId, entry))
{
aLocked = entry->IsStoreReadingLocked();
}
else
{
aLocked = EFalse;
}
return KErrNone;
}
/**
* IncStoreReaderCount()
*/
TInt CMsvIndexAdapter::IncStoreReaderCount(TMsvId aId)
{
CMsvCacheEntry* entry = NULL;
TRAPD(err, FindEntryL(aId, entry));
if (err)
{
return KErrNotFound;
}
entry->IncStoreReaderCount();
return KErrNone;
}
/**
* DecStoreReaderCount()
*/
TInt CMsvIndexAdapter::DecStoreReaderCount(TMsvId aId)
{
CMsvCacheEntry* entry = NULL;
if(FindEntryInCache(aId, entry))
{
entry->DecStoreReaderCount();
}
return KErrNone;
}
/**
* OwningService()
*/
TInt CMsvIndexAdapter::OwningService(TMsvId aId, TMsvId& aService)
{
aService = aId;
if (aId!=KMsvRootIndexEntryId)
{
FOREVER
{
CMsvCacheEntry* entry = NULL;
TRAPD(err, FindEntryL(aService, entry));
if (err)
{
return KErrNotFound;
}
if (KUidMsvServiceEntry==entry->Entry().iType)
{
break;
}
aService = entry->Entry().Parent();
}
}
return KErrNone;
}
/**
* IsLocal()
*/
TInt CMsvIndexAdapter::IsLocal(TMsvId aId, TBool& aLocal)
{
TInt errVal = KErrNone;
aLocal = ETrue;
if (aId!=KMsvRootIndexEntryId)
{
TMsvId service;
errVal = OwningService(aId, service);
aLocal = (KMsvLocalServiceIndexEntryId==service);
}
return errVal;
}
/**
* MoveEntry
* Moves a entry with TMsvId as aId to a folder with target id as aTarget
*
* @param aId TMsvId of the entry to be moved.
* @param aTarget TMsvId of the parent folder.
* @param aDescendents List of descendents id, if the caller also wants to move descendents
* in cache and DB. The last id in the descendent list must be the entry id itself.
* @return TInt System-wide error code.
*/
TInt CMsvIndexAdapter::MoveEntry(TMsvId aId, TMsvId aTarget, CMsvEntrySelection* aDescendents /* DEFAULT=NULL */)
{
TRAPD(err, DoMoveEntryL(aId, aTarget, aDescendents));
return err;
}
/**
* DoMoveEntryL()
* Called from MoveEntry()
* */
void CMsvIndexAdapter::DoMoveEntryL(TMsvId aId, TMsvId aTarget, CMsvEntrySelection* aDescendents /* DEFAULT=NULL */)
{
// Leave if the message store is not currently available.
User::LeaveIfError(iErrorState);
CMsvCacheEntry* entry = NULL;
CMsvCacheVisibleFolder* oldFolderNode = NULL;
TMsvId oldVisibleFolderId = KMsvNullIndexEntryIdValue;
// 1. Find the entry in cache.
GetVisibleFolderDetailsL(aId, entry, oldFolderNode);
// 2. Validate entry.
TBool des;
__ASSERT_DEBUG(entry->IsEntryAndStoreLocked(), PanicServer(EMsvMovingUnlockedEntry));
__ASSERT_DEBUG(entry->Entry().Parent() != aTarget, PanicServer(EMsvMovingWithinSameEntry));
IsADescendent(entry->Entry().Id(), aTarget, des);
if (des)
{// we need to put changes in another method.
User::Leave(KErrArgument);
}
// 3. Fetch the parent entries.
CMsvCacheEntry* oldParentEntry = NULL;
CMsvCacheEntry* newParentEntry = NULL;
TMsvId oldParentId = entry->Entry().Parent();
FindEntryL(oldParentId, oldParentEntry);
FindEntryL(aTarget, newParentEntry);
// 4. Check if the owner flag of the old parent needs to be reset.
TBool resetOldParentOwnerFlag = EFalse;
CMsvEntrySelection* children = new(ELeave)CMsvEntrySelection;
CleanupStack::PushL(children);
GetChildrenIdL(oldParentId, *children);
if(1 == children->Count() && oldParentEntry->Entry().Owner())
{
// Just set this flag, this is updated in step 14.
resetOldParentOwnerFlag = ETrue;
}
CleanupStack::PopAndDestroy(); //children
// 5. If required, pre-allocate memory for child array of newParentEntry.
// This is later updated in step 12.
RArray<TMsvId>* newParentChildArr = newParentEntry->ChildIdArray();
if(newParentChildArr)
{
// This to ensure that future append does not fail.
newParentChildArr->ReserveL(newParentChildArr->Count() + 1);
}
// Step 6 & 7 is performed in this function.
TBool isChildEntriesNeedsUpdation = EFalse;
CMsvCacheVisibleFolder* newVisibleFolderNode = UpdateCacheForMoveEntryL(aTarget,
entry,
oldFolderNode,
aDescendents,
isChildEntriesNeedsUpdation);
TMsvId newVisibleFolderId = newVisibleFolderNode? newVisibleFolderNode->GetFolderId(): oldFolderNode->GetFolderId();
// 8. Update the entry.
UpdateDates(*entry, EFalse);
entry->Entry().SetParent(aTarget);
// 9. Update the DB.
TRAPD(err,
iDbAdapter->BeginTransactionL();
// Update the actual entry
iDbAdapter->UpdateEntryL(entry->Entry(), newVisibleFolderId);
// Reset old parent owner flag.
if(resetOldParentOwnerFlag)
iDbAdapter->UpdateOwnerStatusL(oldParentId, oldParentEntry->Entry(), EFalse);
// Set new parent owner flag.
if(!newParentEntry->Entry().Owner())
iDbAdapter->UpdateOwnerStatusL(aTarget, newParentEntry->Entry(), ETrue);
// Update the child entries visibleFolderId in DB.
if(isChildEntriesNeedsUpdation)
iDbAdapter->UpdateVisibleFolderL(aDescendents, newVisibleFolderId);
iDbAdapter->CommitTransactionL();
);
if(err)
{
TRAP_IGNORE(iDbAdapter->RollbackTransactionL());
// Undo step 8.
entry->Entry().SetParent(oldParentId);
// Undo step 7.
if(newVisibleFolderNode)
newVisibleFolderNode->DeleteEntryL(aId);
if(isChildEntriesNeedsUpdation)
{
for(TInt index=(aDescendents->Count()-2); index>=0; index--)
{
newVisibleFolderNode->DeleteEntryL(aDescendents->At(index));
}
}
User::Leave(err);
}
// Nothing should fail after this. Ensure that
// all leaving function after this does not leave.
// 10. Continuing step 7.1.
// Removing entry from old visible folder.
if(oldVisibleFolderId != newVisibleFolderId)
{
oldFolderNode->DeleteEntryL(aId);
}
// 11. Continuing step 7.2.
// Removing child entries from old visible folder.
if(isChildEntriesNeedsUpdation)
{
for(TInt index=(aDescendents->Count()-2); index>=0; index--)
{
oldFolderNode->DeleteEntryL(aDescendents->At(index));
}
}
// 12. Add child id to new parent child array.
if(newParentChildArr)
{
// This will not leave, as memory is already reserved.
newParentChildArr->AppendL(aId);
}
// 13. Remove child from old parent's child array.
RArray<TMsvId>* oldParentChildArr = oldParentEntry->ChildIdArray();
if(oldParentChildArr)
{
TInt pos = oldParentChildArr->Find(aId);
if(pos != KErrNotFound)
{
oldParentChildArr->Remove(pos);
}
}
// 14. Update owner flag of parent entries.
if(resetOldParentOwnerFlag)
{
oldParentEntry->Entry().SetOwner(EFalse);
}
newParentEntry->Entry().SetOwner(ETrue);
}
void CMsvIndexAdapter::GetVisibleFolderDetailsL(TMsvId aEntryId, CMsvCacheEntry*& aEntry, CMsvCacheVisibleFolder*& aVisibleFolder)
{
TDblQueIter<CMsvCacheVisibleFolder> dqIter(iFolderListHeader);
dqIter.SetToFirst();
while((aVisibleFolder = dqIter++)!=NULL)
{
if(aVisibleFolder->GetEntry(aEntryId, aEntry))
{
break;
}
}
if(aEntry==NULL)
{
User::Leave(KErrNotFound);
}
}
CMsvCacheVisibleFolder* CMsvIndexAdapter::UpdateCacheForMoveEntryL(TMsvId aNewParentId,
CMsvCacheEntry*& aOrigEntry,
CMsvCacheVisibleFolder* aOldVisibleFolderNode,
CMsvEntrySelection* aDescendents,
TBool& aIsChildEntriesNeedsUpdation)
{
// 6. Find the visible folder of new parent.
TMsvId newVisibleFolderId = aOldVisibleFolderNode->GetFolderId();
if(!GetVisibleFolderId(aNewParentId, newVisibleFolderId))
{
User::Leave(KErrNotFound);
}
// 7. Check if the visible folders are different.
CMsvCacheVisibleFolder* newVisibleFolderNode = NULL;
if(aOldVisibleFolderNode->GetFolderId() != newVisibleFolderId)
{
// 7.1. If the visible folders are different, add duplicate
// entry under new visible folder. Removing entry from old
// visible folder is done in step 10 of DoMoveEntryL().
CMsvCacheEntry* newCacheEntry = iFreePoolInstance->EntryL();
TRAPD(err, newCacheEntry->DupNDestroyL(aOrigEntry);
newVisibleFolderNode = AddEntryToCacheL(newVisibleFolderId, newCacheEntry);
);
if(err)
{
iFreePoolInstance->ReleaseEntryWithoutTransaction(newCacheEntry);
User::Leave(err);
}
// 7.2. Check if child entries also needs to be moved.
aOrigEntry = newCacheEntry;
if(aDescendents && !aOrigEntry->Entry().VisibleFolderFlag())
{
aIsChildEntriesNeedsUpdation = ETrue;
// Add duplicate child entry under new visible folder.
// Removing child entries from old visible folder is done in step 11.
CMsvCacheEntry* childEntry = NULL;
CMsvCacheEntry* dupChildEntry = NULL;
for(TInt index=(aDescendents->Count()-2); index>=0; index--)
{
if(aOldVisibleFolderNode->GetEntry(aDescendents->At(index), childEntry))
{
dupChildEntry = NULL;
// Create the duplicate of original entry.
TRAP(err, dupChildEntry = iFreePoolInstance->EntryL();
dupChildEntry->DupNDestroyL(childEntry);
// Add under new visible folder node.
newVisibleFolderNode->AddEntryL(dupChildEntry);
);
if(err)
{
// Delete should not be leaving...
newVisibleFolderNode->DeleteEntryL(aOrigEntry->GetId()); // Undo step 7.1
iFreePoolInstance->ReleaseEntryWithoutTransaction(dupChildEntry); // Undo current entry
// Undo remaining entries.
for(TInt numEntries=index+1; numEntries<=(aDescendents->Count()-2); numEntries++)
{
newVisibleFolderNode->DeleteEntryL(aDescendents->At(numEntries));
}
User::Leave(err);
}
} // if(oldFolderNode->GetEntry(aDescendents->At(index), childEntry))
} // for()
} // if(aDescendents && !entry->Entry().VisibleFolderFlag())
}
return newVisibleFolderNode;
}
/**
* IsADescendent()
*/
TInt CMsvIndexAdapter::IsADescendent(TMsvId aAscendentId, TMsvId aDescendentId, TBool& aDescendent)
{
__ASSERT_DEBUG((KMsvRootIndexEntryId != aAscendentId) && (KMsvRootIndexEntryId != aDescendentId), PanicServer(EMsvDescendentArgumentsRoot));
__ASSERT_DEBUG(aAscendentId!=aDescendentId, PanicServer(EMsvDescendentArgumentsEqual));
aDescendent=EFalse;
while (KMsvRootIndexEntryId != aDescendentId)
{
CMsvCacheEntry* entry;
TRAPD(err, FindEntryL(aDescendentId, entry));
if (err)
{
return KErrNotFound;
}
if (aDescendentId==aAscendentId)
{
aDescendent=ETrue;
break;
}
aDescendentId=entry->Entry().Parent();
}
return KErrNone;
}
/**
* EntryExists()
*/
TBool CMsvIndexAdapter::EntryExists(TMsvId aId)
{
// First search the entry in cache,
// if cache is not empty...
#if (defined SYMBIAN_MSGS_ENHANCED_REMOVABLE_MEDIA_SUPPORT)
if(KMsvRootIndexEntryId == aId)
{
return true;
}
#endif
if(!iFolderListHeader.IsEmpty())
{
CMsvCacheVisibleFolder* folderNode = NULL;
TDblQueIter<CMsvCacheVisibleFolder> dqIter(iFolderListHeader);
dqIter.SetToFirst();
while((folderNode = dqIter++)!=NULL)
{
if(aId == folderNode->GetFolderId())
{
return ETrue;
}
if(folderNode->EntryExists(aId))
{
return ETrue;
}
}
}
TBool entryExists = EFalse;
if(iDbAdapter)
{
TRAP_IGNORE(entryExists = iDbAdapter->EntryExistsL(aId));
}
else
{
return EFalse;
}
if(!entryExists)
{
return EFalse;
}
return ETrue;
}
/**
* ChangeTemporaryData()
*/
TInt CMsvIndexAdapter::ChangeTemporaryData(const TMsvEntry& aNewEntryContents)
{
//__DEBUG_INVARIANT_ONEXIT;
CMsvCacheEntry* entry;
// find the entry in the index
TRAPD(err, FindEntryL(aNewEntryContents.Id(), entry));
if (err)
{
return KErrNotFound;
}
// Can only change a locked entry and not one marked as read only
if (!entry->IsEntryLocked() || entry->Entry().StandardFolder())
{
return KErrAccessDenied;
}
// Check if just changing temporary flags (ie those not stored on file)
entry->Entry().iData=aNewEntryContents.iData;
return KErrNone;
}
/**
* ChangeAttributes()
*/
TInt CMsvIndexAdapter::ChangeAttributes(CMsvEntrySelection& aSelection, TUint aSetAttributes, TUint aClearAttributes)
{
TRAPD(leave, DoChangeAttributesL(aSelection, aSetAttributes, aClearAttributes));
return leave;
}
void CMsvIndexAdapter::DoChangeAttributesL(CMsvEntrySelection& aSelection, TUint aSetAttributes, TUint aClearAttributes)
{
// Leave if the message store is not currently available
User::LeaveIfError(iErrorState);
CArrayFixSeg<TInt32>* newData = new(ELeave)CArrayFixSeg<TInt32>(KMsvChangeAttributesListGranularity);
CleanupStack::PushL(newData);
CArrayFixSeg<TInt32>* newPcSyncCount = new(ELeave)CArrayFixSeg<TInt32>(KMsvChangeAttributesListGranularity);
CleanupStack::PushL(newPcSyncCount);
newData->ResizeL(aSelection.Count(), 0);
newPcSyncCount->ResizeL(aSelection.Count(), 0);
TInt count=aSelection.Count();
TDblQueIter<CMsvCacheVisibleFolder> dqIter(iFolderListHeader);
CMsvCacheEntry* entry = NULL;
CMsvCacheVisibleFolder* folderNode = NULL;
TMsvId newVisibleFolderId = NULL;
if(iDbAdapter)
{
iDbAdapter->BeginTransactionL();
}
else
{
User::Leave(ErrorState());
}
while(count--)
{
// find the entry in the index
dqIter.SetToFirst();
while((folderNode = dqIter++)!=NULL)
{
if(folderNode->GetEntry(aSelection.At(count), entry))
{
newVisibleFolderId = folderNode->GetFolderId();
break;
}
}
if (entry == NULL)
{
User::Leave(KErrNotFound);
}
// can only change a locked entry and not one marked as read only
if (!entry->IsEntryLocked() || entry->Entry().StandardFolder())
{
User::Leave(KErrAccessDenied);
}
// work out the new attributes
TBool persistedFlagsChanged;
if (!NewAttributes(entry->Entry(), newData->At(count), newPcSyncCount->At(count), aSetAttributes, aClearAttributes, persistedFlagsChanged))
{
// attributes don't need changing
aSelection.Delete(count);
newData->Delete(count);
newPcSyncCount->Delete(count);
continue;
}
if(persistedFlagsChanged)
{
TMsvEntry tEntry = entry->Entry();
tEntry.iData = newData->At(count);
tEntry.iPcSyncCount = newPcSyncCount->At(count);
TInt err = KErrNone;
if(iDbAdapter)
{
TRAP(err, iDbAdapter->UpdateEntryL(tEntry, newVisibleFolderId, EFalse));
}
else
{
User::Leave(ErrorState());
}
if(err)
{
iDbAdapter->RollbackTransactionL();
User::Leave(err);
}
}
}
if(iDbAdapter)
{
iDbAdapter->CommitTransactionL();
}
else
{
User::Leave(ErrorState());
}
count=aSelection.Count();
while(count--)
{
// find the entry in the index
CMsvCacheEntry* entryPtr = NULL;
dqIter.SetToFirst();
while((folderNode = dqIter++)!=NULL)
{
if(folderNode->GetEntry(aSelection.At(count), entryPtr))
{
break;
}
}
#if defined(_DEBUG)
TBool found = (NULL != entryPtr);
__ASSERT_DEBUG(found, PanicServer(EMsvChangeAttEmtryNotFound));
#endif
entryPtr->Entry().iData=newData->At(count);
entryPtr->Entry().iPcSyncCount=newPcSyncCount->At(count);
}
CleanupStack::PopAndDestroy(2, newData);
if (!iIdle->IsActive())
{
iIdle->Start(TCallBack(BackGroundOperations, this));
}
}
/**
* NewAttributes
*/
TBool CMsvIndexAdapter::NewAttributes(const TMsvEntry& aEntry, TInt32& aNewData, TInt32& aNewPcSyncCount, TUint aSetAttributes, TUint aClearAttributes, TBool& aPersistedFlagsChanged)
{
aNewData = aEntry.iData;
aNewPcSyncCount = aEntry.iPcSyncCount;
// KMsvPcSyncCountAttribute is a dummy flag that is used to increment/decrement the PC Sync Count
// The visibility attribute is actually recorded as an 'invisible' flag in the index
if (aSetAttributes)
{
if (aSetAttributes & KMsvPcSyncCountAttribute)
{
aNewPcSyncCount++;
aSetAttributes &= ~KMsvPcSyncCountAttribute;
}
const TInt32 mask = KMsvSendStateMax << KMsvSendingStateShift;
if (aSetAttributes & mask)
{
aNewData &= ~mask;
}
aNewData |= aSetAttributes;
if (aSetAttributes & KMsvVisibilityAttribute)
{
aNewData &= ~TMsvEntry::KMsvEntryInvisibleFlag;
}
if (aSetAttributes & KMsvPendingDeleteAttribute)
{
aNewData |= TMsvEntry::KMsvEntryPendingDeleteFlag;
}
}
if (aClearAttributes)
{
if (aClearAttributes & KMsvPcSyncCountAttribute)
{
aNewPcSyncCount--;
aClearAttributes &= ~KMsvPcSyncCountAttribute;
}
aNewData &= ~aClearAttributes;
if (aClearAttributes&KMsvVisibilityAttribute)
{
aNewData |= TMsvEntry::KMsvEntryInvisibleFlag;
}
if (aClearAttributes&KMsvPendingDeleteAttribute)
{
aNewData &= ~TMsvEntry::KMsvEntryPendingDeleteFlag;
}
}
aPersistedFlagsChanged = (aNewData&TMsvEntry::KMsvEntryPersistedFlags)!=(aEntry.iData&TMsvEntry::KMsvEntryPersistedFlags) ||
aNewPcSyncCount != aEntry.iPcSyncCount;
return aNewData != aEntry.iData || aNewPcSyncCount != aEntry.iPcSyncCount;
}
/**
* UpdateDates
*/
void CMsvIndexAdapter::UpdateDates(CMsvCacheEntry& aEntry, TBool aSetCreatedDate)
{
TTime now;
now.UniversalTime();
TMsvTime time;
time.SetTime(now);
aEntry.SetLastChangeDate(time);
if (aSetCreatedDate)
{
aEntry.SetCreatedDate(time);
}
}
/**
* GetInternalEntry()
*/
TInt CMsvIndexAdapter::GetInternalEntry(TMsvId aId, CMsvCacheEntry*& aEntry)
{
CMsvCacheEntry* entry=NULL;
TRAPD(err, FindEntryL(aId, entry));
if(err!=KErrNone)
{
return err;
}
if(entry==NULL)
{
return KErrNotFound;
}
aEntry=entry;
return KErrNone;
}
/**
* Progress()
*/
TMsvIndexProgress& CMsvIndexAdapter::Progress()
{
return iProgress;
}
/**
* EntryTreeInfo()
*/
TInt CMsvIndexAdapter::EntryTreeInfo(TMsvId aId, TMsvServerEntryInfo& aEntryInfo)
{
aEntryInfo.Reset();
aEntryInfo.iId = aId;
if( aId != KMsvRootIndexEntryId )
{
// Search up the tree to find which service the specified entry is under.
// Also, record the highest owning folder for the entry.
TMsvId id = aId;
CMsvCacheEntry* entry =NULL;
TRAPD(err, FindEntryL(id, entry));
if(err)
return KErrNotFound;
id = entry->Entry().iId; //Id may be masked in cache
aEntryInfo.iId = id;
// Get info for the entry.
aEntryInfo.iEntryOwnerId = entry->EntryOwnerId();
aEntryInfo.iMtm = entry->Entry().iMtm;
aEntryInfo.iType = entry->Entry().iType;
if( aEntryInfo.iType == KUidMsvFolderEntry )
{
// This is a folder - record as this may be needed.
aEntryInfo.iTopFolder = id;
}
TBool found = (aEntryInfo.iType == KUidMsvServiceEntry);
while( !found )
{
// Move up the tree and get the next entry...
id = entry->Entry().Parent();
TRAPD(err, FindEntryL(id, entry));
if(err)
return KErrNotFound;
TUid type = entry->Entry().iType;
if( type == KUidMsvServiceEntry )
{
// Ok, we've found the owning service - stop looking.
found = ETrue;
}
else if( type == KUidMsvFolderEntry )
{
// This is a folder - record as this may be needed.
aEntryInfo.iTopFolder = id;
}
else
{
// This is a message or attachment entry - this implies that the
// original entry was part of an existing message. Set the owner
// ID to the highest parent.
aEntryInfo.iPartOfMessage = ETrue;
aEntryInfo.iParentOwnerId = entry->EntryOwnerId();
}
}
// Record the service and MTM...
aEntryInfo.iService = id;
aEntryInfo.iServiceMtm = entry->Entry().iMtm;
}
else
{
// This is the root entry! Mark the service as being the local service.
aEntryInfo.iType = KUidMsvRootEntry;
aEntryInfo.iMtm = KUidMsvLocalServiceMtm;
aEntryInfo.iService = KMsvLocalServiceIndexEntryId;
aEntryInfo.iServiceMtm = KUidMsvLocalServiceMtm;
aEntryInfo.iTopFolder = KMsvRootIndexEntryId;
}
return KErrNone;
}
/**
* CommitNonCommitedEntries()
*/
void CMsvIndexAdapter::CommitNonCommitedEntries()
{
// 1. Commit transaction in DB.
if(iDbAdapter)
{
TRAPD(err, iDbAdapter->CommitTransactionL());
if(err)
{
RollbackAdditions();
RollbackChanges();
return;
}
}
// 2. Update changed entries completely.
TInt count = iNonCommittedChangedEntryList.Count();
while(count)
{
TNonCommittedChangedEntries changedEntry = iNonCommittedChangedEntryList[--count];
CMsvCacheVisibleFolder* oldVisibleFolderNode = changedEntry.iOldVisibleFolderNode;
// Removing entry from old visible folder.
if(changedEntry.iNewVisibleFolderNode &&
oldVisibleFolderNode->GetFolderId() != changedEntry.iNewVisibleFolderNode->GetFolderId())
{
// Will never leave.
TRAP_IGNORE(changedEntry.iOldVisibleFolderNode->DeleteEntryL(changedEntry.iEntry->GetId()));
}
// Removing child entries from old visible folder.
if(changedEntry.iDescendentList)
{
for(TInt index=(changedEntry.iDescendentList->Count()-2); index>=0; index--)
{
// Can leave with KErrNotFound.
TRAP_IGNORE(oldVisibleFolderNode->DeleteEntryL(changedEntry.iDescendentList->At(index)));
}
delete changedEntry.iDescendentList;
}
// Add child id to new parent child array.
if(changedEntry.iNewParentEntry && (changedEntry.iNewParentEntry->ChildIdArray()))
{
// This will not leave, as memory is already reserved.
TRAP_IGNORE(changedEntry.iNewParentEntry->ChildIdArray()->AppendL(changedEntry.iEntry->GetId()));
}
// Remove child from old parent's child array.
if(changedEntry.iOldParentEntry)
{
RArray<TMsvId>* oldParentChildArr = changedEntry.iOldParentEntry->ChildIdArray();
if(oldParentChildArr)
{
TInt pos = oldParentChildArr->Find(changedEntry.iEntry->GetId());
if(pos != KErrNotFound)
{
oldParentChildArr->Remove(pos);
}
}
}
// Update owner flag of parent entries.
if(changedEntry.iResetOldParentOwnerFlag)
{
changedEntry.iOldParentEntry->Entry().SetOwner(EFalse);
}
if(changedEntry.iNewParentEntry)
{
changedEntry.iNewParentEntry->Entry().SetOwner(ETrue);
}
}
// 3. Reset non-committed entry list.
iNonCommittedAddedEntryList.ResetAndDestroy();
iNonCommittedChangedEntryList.Reset();
}
/**
* RollbackAdditions()
*/
void CMsvIndexAdapter::RollbackAdditions()
{
// 1. Rollback transaction in DB.
if(iDbAdapter)
{
TRAP_IGNORE(iDbAdapter->RollbackTransactionL());
}
// 2. Delete added entry from cache.
CMsvCacheVisibleFolder* folderNode = NULL;
TInt count = iNonCommittedAddedEntryList.Count();
TDblQueIter<CMsvCacheVisibleFolder> dqIter(iFolderListHeader);
while(count)
{
--count;
dqIter.SetToFirst();
while ((folderNode = dqIter++) != NULL)
{
if(iNonCommittedAddedEntryList[count]->iVisibleFolder == folderNode->GetFolderId())
{
iNonCommittedAddedEntryList[count]->entry->LockEntry();
// Error can cause if the entry does not exist. Ignore.
TRAP_IGNORE(folderNode->DeleteEntryL(iNonCommittedAddedEntryList[count]->entry->GetId()));
break;
}
};
}
// 3. Reset non-committed entry list.
iNonCommittedAddedEntryList.ResetAndDestroy();
}
/**
* RollbackChanges()
*/
void CMsvIndexAdapter::RollbackChanges()
{
// 1. Rollback transaction in DB.
if(iDbAdapter)
{
TRAP_IGNORE(iDbAdapter->RollbackTransactionL());
}
// 2. Undo changed entry in cache.
TInt count = iNonCommittedChangedEntryList.Count();
while(count)
{
TNonCommittedChangedEntries changedEntry = iNonCommittedChangedEntryList[--count];
CMsvCacheEntry* oldEntry = changedEntry.iEntry;
// If the visible folder Id of the entry is changed,
// oldEntry represents the entry under new visible folder node.
// Get the actual old entry from old visible folder node.
if(changedEntry.iNewVisibleFolderNode)
{
// This will be non leaving as entry is already present.
if(changedEntry.iOldVisibleFolderNode->GetEntry(oldEntry->GetId(), oldEntry))
{
// Remove added entry from new visible node.
// This function will never leave in the current
// scenario, hence ignoring the error.
TRAP_IGNORE(changedEntry.iNewVisibleFolderNode->DeleteEntryL(oldEntry->GetId()));
}
}
oldEntry->SetEntryOwnerId(changedEntry.iBkpEntry->EntryOwnerId());
oldEntry->RollBackCopyEntry(changedEntry.iBkpEntry->Entry());
iFreePoolInstance->ReleaseEntryWithoutTransaction(changedEntry.iBkpEntry);
if(changedEntry.iDescendentList)
{
delete changedEntry.iDescendentList;
}
}
// 3. Reset non-committed entry list.
iNonCommittedChangedEntryList.Reset();
}
CMsvIndexAdapter::TMsvServerEntryInfo::TMsvServerEntryInfo()
{
iId = KMsvNullIndexEntryIdValue;
iTopFolder = KMsvNullIndexEntryIdValue;
iService = KMsvNullIndexEntryIdValue;
iMtm = KUidMsvNullEntry;
iType = KUidMsvNullEntry;
iServiceMtm = KUidMsvNullEntry;
iEntryOwnerId = KMsvServerId.iId;
iParentOwnerId = KMsvServerId.iId;
iPartOfMessage = EFalse;
}
void CMsvIndexAdapter::TMsvServerEntryInfo::Reset()
{
iId = KMsvNullIndexEntryIdValue;
iTopFolder = KMsvNullIndexEntryIdValue;
iService = KMsvNullIndexEntryIdValue;
iMtm = KUidMsvNullEntry;
iType = KUidMsvNullEntry;
iServiceMtm = KUidMsvNullEntry;
iEntryOwnerId = KMsvServerId.iId;
iParentOwnerId = KMsvServerId.iId;
iPartOfMessage = EFalse;
}
/**
Need to see the posibilty of getting this first from cache
*/
TBool CMsvIndexAdapter::GetNextSiblingL(TMsvId aId,TMsvId aParentId,TMsvId& aNextSiblingId)
{
TBool flag = EFalse;
if(iDbAdapter)
{
flag = iDbAdapter->GetNextSiblingL(aId, aParentId, aNextSiblingId);
}
else
{
User::Leave(ErrorState());
}
return flag;
}
/**
Need to see the posibilty of getting this first from cache
*/
TBool CMsvIndexAdapter::GetFirstChildIdL(TMsvId aParentId,TMsvId& aFirstChild)
{
TBool flag = EFalse;
if(iDbAdapter)
{
flag = iDbAdapter->GetFirstChildIdL(aParentId, aFirstChild);
}
else
{
User::Leave(ErrorState());
}
return flag;
}
/**
* SetLocalServiceComplete
* @param TUint: Drive Id of the local service folder node.
*
* Code changes for PREQ 557.
* Sets the localservice visiblefolder.
*/
void CMsvIndexAdapter::SetLocalServiceComplete()
{
TDblQueIter<CMsvCacheVisibleFolder> dqIter(iFolderListHeader);
dqIter.SetToFirst();
CMsvCacheVisibleFolder* folderNode = NULL;
while((folderNode = dqIter++)!=NULL)
{
if(KMsvLocalServiceIndexEntryIdValue == folderNode->GetFolderId())
{
folderNode->SetComplete(ETrue);
break;
}
}
}
/**
*BackGroundOperations()
*@param aPtr A TAny*
*@return TBool ETrue if the background operation needs to be rescheduled.
* EFalse if the background operation need not be rescheduled.
*/
TBool CMsvIndexAdapter::BackGroundOperations(TAny* aPtr)
{
return ((CMsvIndexAdapter*)aPtr)->DoBackGroundOperations();
}
/**
*DoBackGroundOperations()
*@param None
*@return TBool ETrue if the background operation needs to be rescheduled.
* EFalse if the background operation need not be rescheduled.
*/
TBool CMsvIndexAdapter::DoBackGroundOperations()
{
TInt stopBackgroundOperation = EFalse;
TRAPD(err, DoBackGroundOperationsL(stopBackgroundOperation));
if(KErrNone == err && !stopBackgroundOperation)
{
return ETrue;
}
return EFalse;
}
/**
*DoBackGroundOperations()
*This is the background operation state machine, this will perform a set
*of operations in each of the iteration.
*
*@param None
*@return void
*/
void CMsvIndexAdapter::DoBackGroundOperationsL(TBool& aStopBackgroundOperation)
{
iFreePoolInstance->RoutineFreePoolCleanUpL();
switch(iBackgroundOperationState)
{
case ERemoveDeletedEntries:
{
iBackgroundOperationState = EAllocateMemoryOperation;
DoRemoveDeletedEntriesL();
break;
}
case EAllocateMemoryOperation:
{
iBackgroundOperationState = ECheckBlockSize;
if(CheckAndAllocateMemoryL())
{
iBackgroundOperationPerformed = 0;
break;
}
iBackgroundOperationPerformed++;
}
case ECheckBlockSize:
{
iBackgroundOperationState = ENoOperation;
SplitBlockL();
break;
}
case ENoOperation:
default:
{
iBackgroundOperationState = ERemoveDeletedEntries;
// The background operation has run multiple times, without doing
// much work, so it can be stopped temporarily.
if(iBackgroundOperationPerformed >= 5)
{
iBackgroundOperationPerformed = 0;
aStopBackgroundOperation = ETrue;
}
break;
}
}
}
/**
*CheckAndAllocateMemory
*Will check the state of the free pool and allocate memory if required.
*Allocation will be done if 70% of the CMsvCacheEntries created are used.
*
*@return TBool ETrue, if allocation has been done.
* EFalse, if no allocation is done.
*/
TBool CMsvIndexAdapter::CheckAndAllocateMemoryL()
{
if(iFreePoolInstance->IsAllocationRequiredL())
{
iFreePoolInstance->AllocateMemoryL();
return ETrue;
}
return EFalse;
}
/**
*SplitBlockL
*Will sort and split the blocks of CMsvCacheEntries.
*
*@return void
*/
void CMsvIndexAdapter::SplitBlockL()
{
TDblQueIter<CMsvCacheVisibleFolder> dqIter(iFolderListHeader);
dqIter.SetToFirst();
CMsvCacheVisibleFolder* folderNode = NULL;
while((folderNode = dqIter++)!=NULL)
{
folderNode->SplitBlockL();
}
}
/**
*ForceDeleteEntry
*Deletes the TMsvEntry even if it is not in the cache.This does not require
*the entry to be locked.
*
*@param aId TMsvId, id ofthe entry to be deleted.
*@return TInt Any of the system wide error codes.
*/
TInt CMsvIndexAdapter::ForceDeleteEntry(TMsvId aId)
{
TRAPD(leave, DoForceDeleteEntryL(aId));
return leave;
}
/**
*DoForceDeleteEntryL
*Called from ForceDeleteEntry
*
*@param aId TMsvId, id ofthe entry to be deleted.
*@return void
*/
void CMsvIndexAdapter::DoForceDeleteEntryL(TMsvId aId)
{
User::LeaveIfError(iErrorState);
CMsvCacheVisibleFolder* folderNode = NULL;
TDblQueIter<CMsvCacheVisibleFolder> dqIter(iFolderListHeader);
if(!iDbAdapter)
{
User::Leave(ErrorState());
}
CMsvCacheEntry* entry = NULL;
TBool releaseEntry = FindEntryL(aId, entry, EFalse);
TMsvId parentId = entry->Entry().Parent();
TRAPD(err, iDbAdapter->DeleteEntryL(aId));
// If the entry does not exists in cache,
// we need to release the entry explicitly.
if(releaseEntry)
{
iFreePoolInstance->ReleaseEntry(entry, ETrue);
User::LeaveIfError(err);
}
else
{
User::LeaveIfError(err);
// Otherwise remove the entry from cache.
dqIter.SetToFirst();
while((folderNode = dqIter++)!=NULL)
{
TRAPD(err, folderNode->DeleteEntryL(aId, ETrue));
if(KErrNone == err)
{
break;
}
}
}
//Find the parent entry in cache and check for any children.
//If there are no more children under the parent, then
//reset the owner flag in cache and database.
CMsvEntrySelection* children = new(ELeave)CMsvEntrySelection;
CleanupStack::PushL(children);
releaseEntry = FindEntryL(parentId, entry, EFalse);
TRAP(err,
GetChildrenIdL(parentId, *children);
if(0 == children->Count() && entry->Entry().Owner())
{
iDbAdapter->UpdateOwnerStatusL(parentId, entry->Entry(), EFalse);
entry->Entry().SetOwner(EFalse);
}
);
if(releaseEntry)
{
iFreePoolInstance->ReleaseEntry(entry, ETrue);
}
User::LeaveIfError(err);
CleanupStack::PopAndDestroy(); //children
}
/**
*DeleteSelectionUsingTransaction
*Deletes a selection of TMsvEntries.This will also delete enteries that are not locked.
*
*@param aSelection CMsvEntrySelection&, selection of the TMsvEntries.
@param TInt Any of the system wide error codes.
*/
TInt CMsvIndexAdapter::DeleteSelectionUsingTransaction(const CMsvEntrySelection& aSelection)
{
TRAPD(leave, DoDeleteSelectionUsingTransactionL(aSelection));
return leave;
}
/**
*DoDeleteSelectionUsingTransactionL
*Called from DeleteSelectionUsingTransaction.
*
*@param aSelection CMsvEntrySelection&, selection of the TMsvEntries.
@param void
*/
void CMsvIndexAdapter::DoDeleteSelectionUsingTransactionL(const CMsvEntrySelection& aSelection)
{
User::LeaveIfError(iErrorState);
if(!iDbAdapter)
{
User::Leave(iErrorState);
}
CMsvCacheVisibleFolder* folderNode = NULL;
TDblQueIter<CMsvCacheVisibleFolder> dqIter(iFolderListHeader);
const TInt count = aSelection.Count();
if(!count)
{
return;
}
CMsvEntrySelection* children = new(ELeave)CMsvEntrySelection;
CleanupStack::PushL(children);
CMsvCacheEntry* entry = NULL;
TBool releaseEntry = FindEntryL(aSelection.At(0), entry, EFalse);
TMsvId parentId = entry->Entry().Parent();
if(releaseEntry)
{
iFreePoolInstance->ReleaseEntry(entry, ETrue);
}
// Find the parent in the cache - we may need to update its owner status flag both in cache and DB.
TBool updateTheParent = ETrue;
TRAPD(err, releaseEntry = FindEntryL(parentId, entry, EFalse)); // can't let it leave - need to delete the child in any case.
if(err == KErrNotFound)
{
updateTheParent = EFalse;
if(releaseEntry)
{
iFreePoolInstance->ReleaseEntry(entry, ETrue);
}
}
else
{
// Get parent's children.
GetChildrenIdL(parentId, *children); // can leave - we'll let it leave as there's nothing to be rolled back now.
if(children->Count() > 1)
updateTheParent = EFalse;
}
// Perform DB operations:
// - Delete the entry.
// - Update owner status flag of the parent.
iDbAdapter->DeleteEntryL(aSelection); // can leave - we'll let it leave as there's nothing to be rolled back now.
if(updateTheParent)
{
TRAP(err, iDbAdapter->UpdateOwnerStatusL(parentId, entry->Entry(), EFalse));
}
// Leave if DB operations fail. Cache is not touched.
if(err)
{
TRAP_IGNORE(iDbAdapter->RollbackTransactionL()); //
User::Leave(err);
}
else
{
if(updateTheParent)
{
entry->Entry().SetOwner(EFalse);
}
// Do the cache operations. These don't require any memory allocation and cannot fail.
for (TInt index=0; index<count; ++index)
{
// If the entry is a visible folder, then delete the node.
dqIter.SetToFirst();
while((folderNode = dqIter++)!=NULL)
{
if(folderNode->GetFolderId() == aSelection.At(index))
{
folderNode->iDlink.Deque();
delete folderNode;
break;
}
}
// Delete the entry from under its parent visible folder.
dqIter.SetToFirst();
while((folderNode = dqIter++)!=NULL)
{
TRAPD(err, folderNode->DeleteEntryL(aSelection.At(index), ETrue));
if(KErrNone == err)
{
break;
}
}
}
}
// Update SearchSort delta cache.
TMsgType aType(EDeletedMsg);
if(CMSvSearchSortCacheManager::Instance()->iManagerEntry != NULL)
{
if(CMSvSearchSortCacheManager::Instance()->iManagerEntry->Count()>0)
{
for(TInt ii =0; ii < aSelection.Count() ; ii ++)
{
CMsvSearchSortDeltaCache::Instance()->EntryInDeltaCache(aSelection[ii],aType);
}
}
}
CleanupStack::PopAndDestroy(); //children
}
/**
* BeginTransaction()
*
* Starts a new DB transaction.
*/
void CMsvIndexAdapter::BeginTransaction()
{
if(iDbAdapter)
{
TRAP_IGNORE(iDbAdapter->BeginTransactionL());
}
}
/**
* CommitTransaction()
*
* Commits an already opened transaction.
*/
void CMsvIndexAdapter::CommitTransaction()
{
if(iDbAdapter)
{
TRAP_IGNORE(iDbAdapter->CommitTransactionL());
}
}
/**
* DoRemoveDeletedEntriesL()
*
* Deletes all the entries whose PcSyncCount is 0 and delete flag is set.
*/
TBool CMsvIndexAdapter::DoRemoveDeletedEntriesL()
{
CMsvCacheEntry* parent = NULL;
CMsvCacheEntry* entry = NULL;
CMsvCacheEntry* siblingEntry = NULL;
TMsvId siblingId = KMsvNullIndexEntryId;
TMsvId visibleFolder = KMsvNullIndexEntryId;
TMsvId firstchildId = KMsvNullIndexEntryId;
TInt commitCount = 1;
// Find deleted folder
FindEntryL(KMsvDeletedEntryFolderEntryId, parent, ETrue);
CMsvEntrySelection* selection = new(ELeave)CMsvEntrySelection;
CleanupStack::PushL(selection);
// All deleted entries are a child of deleted folder
if(iDbAdapter->GetFirstChildIdL(parent->Entry().Id(),firstchildId))
{
iDbAdapter->GetEntryL(firstchildId, entry, visibleFolder);
}
while(entry && commitCount > 0)
{
if (entry->Entry().PcSyncCount() <= 0)
{
// Add entry to list of things to be deleted
__ASSERT_DEBUG(entry->Entry().Deleted(), PanicServer(EMsvDeletedFlagNotSet));
User::LeaveIfError(LockEntryAndStore(entry->Entry().Id()));
selection->AppendL(entry->Entry().Id());
// Will the next entry be deleted?
if(iDbAdapter->GetNextSiblingL(entry->Entry().Id(), parent->Entry().Id(), siblingId))
{
iDbAdapter->GetEntryL(siblingId, siblingEntry, visibleFolder);
if(siblingEntry->Entry().PcSyncCount() > 0)
{
commitCount--;
}
}
else
break;
}
iFreePoolInstance->ReleaseEntryWithoutTransaction(entry);
entry = siblingEntry;
}
if (selection->Count() > 0)
{
User::LeaveIfError(DeleteSelection(*selection));
}
CleanupStack::PopAndDestroy(); // selection
// Possibly more to delete if we did not get to the end of the child list
TBool retVal = entry != NULL;
iFreePoolInstance->ReleaseEntryWithoutTransaction(entry);
return retVal;
}
#if (defined SYMBIAN_MSGS_ENHANCED_REMOVABLE_MEDIA_SUPPORT)
/**
* GetChildrenAllL()
*
* Code added for PREQ 557.
*
* The function is similar to function GetChildrenL(), but
* additionally it fetches entries from all the drive in the
* preferred drive list.
*/
void CMsvIndexAdapter::GetChildrenAllL(TMsvId aId, CArrayPtr<const TMsvEntry>& aSelection, const TMsvSelectionOrdering& aOrdering, TUid aMtm, TBool aFilterByOwnerId, TSecureId aOwnerId)
{
TMsvPreferredDrive driveEntry;
CMsvPreferredDriveList *driveList = CMsvPreferredDriveList::GetDriveList();
for(TInt index=0; index<driveList->Count(); ++index)
{
driveEntry = (*driveList)[index];
if(EMsvMessageStoreAvailableStatus == driveEntry.status)
{
GetChildrenL(MaskTMsvId(driveEntry.driveId, aId), aSelection, aOrdering, aMtm, aFilterByOwnerId, aOwnerId);
}
}
}
/**
* GetChildrenIdAll()
*
* Code added for PREQ 557.
*
* The function is similar to function GetChildrenId(), but
* additionally it fetches entries from all the drive in the
* preferred drive list.
*/
TInt CMsvIndexAdapter::GetChildrenIdAll(TMsvId aId, const CMsvEntryFilter& aFilter, CMsvEntrySelection& aSelection)
{
TSecureId dummy = KMsvServerId.iId;
return GetChildrenIdAll(aId, aFilter, aSelection, EFalse, dummy);
}
// Overloaded.
TInt CMsvIndexAdapter::GetChildrenIdAll(TMsvId aId, const CMsvEntryFilter& aFilter, CMsvEntrySelection& aSelection, TBool aFilterByOwnerId, TSecureId aOwnerId)
{
TInt err = KErrNone;
TMsvPreferredDrive driveEntry;
CMsvPreferredDriveList *driveList = CMsvPreferredDriveList::GetDriveList();
for(TInt index=0; index<driveList->Count(); ++index)
{
driveEntry = (*driveList)[index];
if(EMsvMessageStoreAvailableStatus == driveEntry.status)
{
err = GetChildrenId(MaskTMsvId(driveEntry.driveId, aId), aFilter, aSelection, aFilterByOwnerId, aOwnerId);
if(KErrNone != err)
{
return err;
}
}
}
return KErrNone;
}
/**
* RemoveDriveL()
*
* This function is added in PREQ 557.
* @param TUint: The drive priority, which is also the index of drive
* entry in message server data structure.
* @param TBool: If the drive still remains in the preferred drive list.
* @return None.
*
*/
void CMsvIndexAdapter::RemoveDriveL(TUint aDriveId, TUint aDriveIndex, TBool aIsStdFolderVisible /*=ETrue */)
{
CMsvCacheVisibleFolder* folderNode = NULL;
TDblQueIter<CMsvCacheVisibleFolder> dqIter(iFolderListHeader);
// Browse through the folder list.
dqIter.SetToFirst();
while ((folderNode = dqIter++) != NULL)
{
// If the folder is from the same drive.
// Caution: If the current drive is removed,
// this will also include root and localService.
if(folderNode->GetDrive() == aDriveId)
{
folderNode->iDlink.Deque();
delete folderNode;
}
} // while ((folderNode = dqIter++) != NULL)
// If current drive is removed,
// Remove localService entry as well.
// Detach the database and remove maxId entry.
if(!aIsStdFolderVisible)
{
iDbAdapter->DetachDBL(aDriveId);
iMaxMsvIdList[aDriveId] = NULL;
CMsvPreferredDriveList::GetDriveList()->UpdateDriveIdL(aDriveIndex, KMsvInvalidDriveId);
CMsvPreferredDriveList::GetDriveList()->UpdateDriveStatusL(aDriveIndex, EMsvDriveDiskNotAvailableStatus);
}
}
/**
* AddDriveL()
*
* This function is added in PREQ 557.
* @param TUint: The drive priority, which is also the index of drive
* entry in message server data structure.
*
*/
void CMsvIndexAdapter::AddDriveL(TUint aDrivePriority)
{
// Assign a new drive id.
TUint driveId = GetNextAvailableDriveId();
CMsvPreferredDriveList::GetDriveList()->UpdateDriveIdL(aDrivePriority, driveId);
// Attach the database.
TMsvId maxId;
TMsvPreferredDrive driveEntry;
CMsvPreferredDriveList::GetDriveList()->DriveInfoL(aDrivePriority, driveEntry);
#if (defined SYMBIAN_MESSAGESTORE_HEADER_BODY_USING_SQLDB)
TRAPD(err, iDbAdapter->AttachDBL(driveEntry.driveNum, driveId, maxId, &iServer.MessageDBAdapter()));
#else
TRAPD(err, iDbAdapter->AttachDBL(driveEntry.driveNum, driveId, maxId));
#endif
WrapDBErrorL(err);
// Insert the max Id in the list.
iMaxMsvIdList[driveId] = ((maxId >= MaskTMsvId(driveId, KFirstFreeEntryId))? (maxId+1) : MaskTMsvId(driveId, KFirstFreeEntryId));
}
/**
* ChangeDriveL()
*
* This function is added in PREQ 557.
* @param TUint: The drive priority, which is also the index of drive
* entry in message server data structure.
* @param TBool: If the previous drive still remains in the preferred drive list.
* @return None.
*
*/
void CMsvIndexAdapter::ChangeDriveL(TUint aNewDriveIndex, TBool aIsStdFolderVisible /*= ETrue*/)
{
// Rule1: Remove all non-standard folder node of current drive.
TInt oldCurrentDriveIndex = CMsvPreferredDriveList::GetDriveList()->CurrentDriveIndex();
RemoveDriveL(KCurrentDriveId, oldCurrentDriveIndex, EFalse);
// If old drive should be visible, re-attach the drive.
// This assigns a new drive id to the old current drive.
if(aIsStdFolderVisible)
{
CMsvPreferredDriveList::GetDriveList()->UpdateDriveStatusL(oldCurrentDriveIndex, EMsvMessageStoreAvailableStatus);
AddDriveL(oldCurrentDriveIndex);
}
// Clear search sort cache db table
TRAPD(err, GetDbAdapter()->ClearDBContentsL());
WrapDBErrorL(err);
// 3. Flush up entries from new current drive,
// if it is already attached.
TMsvPreferredDrive driveEntry;
CMsvPreferredDriveList::GetDriveList()->DriveInfoL(aNewDriveIndex, driveEntry);
if(KMsvInvalidDriveId != driveEntry.driveId)
{
RemoveDriveL(driveEntry.driveId, aNewDriveIndex, EFalse);
}
// Assign a new drive Id to the drive.
CMsvPreferredDriveList::GetDriveList()->UpdateDriveIdL(aNewDriveIndex, KCurrentDriveId);
CMsvPreferredDriveList::GetDriveList()->UpdateDriveStatusL(aNewDriveIndex, EMsvMessageStoreAvailableStatus);
// Assign a new drive Id to the drive.
CMsvPreferredDriveList::GetDriveList()->UpdateDriveIdL(aNewDriveIndex, KCurrentDriveId);
CMsvPreferredDriveList::GetDriveList()->UpdateDriveStatusL(aNewDriveIndex, EMsvMessageStoreAvailableStatus);
// --------- Attach the database and update MaxId.
TMsvId maxId;
#if (defined SYMBIAN_MESSAGESTORE_HEADER_BODY_USING_SQLDB)
TRAP(err, iDbAdapter->AttachDBL(driveEntry.driveNum, KCurrentDriveId, maxId, &iServer.MessageDBAdapter()));
#else
TRAP(err, iDbAdapter->AttachDBL(driveEntry.driveNum, KCurrentDriveId, maxId));
#endif
WrapDBErrorL(err);
// Insert the max Id in the list.
iMaxMsvIdList[KCurrentDriveId] = ((maxId >= MaskTMsvId(KCurrentDriveId, KFirstFreeEntryId))? (maxId+1) : MaskTMsvId(KCurrentDriveId, KFirstFreeEntryId));
// ------- Create a root node and its children.
TMsvId folderId = MaskTMsvId(KCurrentDriveId, KMsvRootIndexEntryId);
// Add local service folder node in the folder list.
iRootNode = CMsvCacheVisibleFolder::NewL(folderId);
iFolderListHeader.AddFirst(*iRootNode);
// Fetch children of root folder to cache.
RPointerArray<CMsvCacheEntry> childEntryList;
CleanupClosePushL(childEntryList);
TRAP(err, iDbAdapter->GetChildrenL(folderId, childEntryList));
WrapDBErrorL(err);
iRootNode->AddEntryListL(childEntryList, ETrue);
childEntryList.Reset();
// ------- Create a local service node and its children.
folderId = MaskTMsvId(KCurrentDriveId, KMsvLocalServiceIndexEntryId);
// Add local service folder node in the folder list.
CMsvCacheVisibleFolder* localServiceFolder = CMsvCacheVisibleFolder::NewL(folderId);
iFolderListHeader.AddFirst(*localServiceFolder);
TRAP(err, iDbAdapter->GetChildrenL(folderId, childEntryList));
WrapDBErrorL(err);
localServiceFolder->AddEntryListL(childEntryList, ETrue);
CleanupStack::PopAndDestroy(); // childEntryList.
}
/**
* GetNextAvailableDriveId()
*
* This function is added in PREQ 557.
* @param None:
* @return TUint: The next available drive Id.
*
*/
TUint CMsvIndexAdapter::GetNextAvailableDriveId()
{
TMsvPreferredDrive driveEntry;
TUint driveId = iFirstFreeDriveId;
for(TUint index=0; index<8; index++)
{
TBool driveIdFound = EFalse;
CMsvPreferredDriveList *driveList = CMsvPreferredDriveList::GetDriveList();
for(TInt index=0; index<driveList->Count(); ++index)
{
driveEntry = (*driveList)[index];
if(driveId == driveEntry.driveId)
{
driveIdFound = ETrue;
break;
}
}
if(EFalse == driveIdFound)
{
iFirstFreeDriveId = ((driveId)%7 ? (driveId+1) : 2);
return driveId;
}
driveId = ((driveId)%7 ? (driveId+1) : 2);
}
return KErrNone;
}
/**
* ReloadCacheL()
*
* This function is used by backup and restore functionality
* when it finds that DB being restored is changed. The function
* destroys the cache of the current drive and recreates it.
*/
void CMsvIndexAdapter::ReloadCacheL()
{
TUint currDriveIndex = CMsvPreferredDriveList::GetDriveList()->CurrentDriveIndex();
TDriveNumber currDriveNum = CMsvPreferredDriveList::GetDriveList()->CurrentDriveNumber();
// Attach the database.
TMsvId maxId;
#if (defined SYMBIAN_MESSAGESTORE_HEADER_BODY_USING_SQLDB)
TRAPD(err, iDbAdapter->AttachDBL(currDriveNum, KCurrentDriveId, maxId, &iServer.MessageDBAdapter()));
#else
TRAPD(err, iDbAdapter->AttachDBL(currDriveNum, KCurrentDriveId, maxId));
#endif
WrapDBErrorL(err);
// Update the entry in drive list.
CMsvPreferredDriveList::GetDriveList()->UpdateDriveStatusL(currDriveIndex, EMsvMessageStoreAvailableStatus);
// Insert the max Id in the list.
iMaxMsvIdList[KCurrentDriveId] = ((maxId >= MaskTMsvId(KCurrentDriveId, KFirstFreeEntryId))? (maxId+1) : MaskTMsvId(KCurrentDriveId, KFirstFreeEntryId));
TMsvId folderId = MaskTMsvId(KCurrentDriveId, KMsvRootIndexEntryId);
// Add local service folder node in the folder list.
iRootNode = CMsvCacheVisibleFolder::NewL(folderId);
iFolderListHeader.AddFirst(*iRootNode);
// Fetch children of root folder to cache.
RPointerArray<CMsvCacheEntry> childEntryList;
CleanupClosePushL(childEntryList);
// Updating children under root node.
TRAP(err, iDbAdapter->GetChildrenL(folderId, childEntryList));
WrapDBErrorL(err);
iRootNode->AddEntryListL(childEntryList, ETrue);
childEntryList.Reset();
// Create a local service node and its children.
folderId = MaskTMsvId(KCurrentDriveId, KMsvLocalServiceIndexEntryId);
// Add local service folder node in the folder list.
CMsvCacheVisibleFolder* localServiceFolder = CMsvCacheVisibleFolder::NewL(folderId);
iFolderListHeader.AddFirst(*localServiceFolder);
// Fetch children of localService folder to cache.
TRAP(err, iDbAdapter->GetChildrenL(folderId, childEntryList));
WrapDBErrorL(err);
localServiceFolder->AddEntryListL(childEntryList, ETrue);
CleanupStack::PopAndDestroy(); // childEntryList.
}
#endif // #if (defined SYMBIAN_MSGS_ENHANCED_REMOVABLE_MEDIA_SUPPORT)
#if (defined SYMBIAN_MESSAGESTORE_UNIT_TESTCODE)
#ifdef _DEBUG
void CMsvIndexAdapter::PrintL()
{
_LIT8(KFolderId, "FOLDER ID: ");
_LIT8(KDriveId, " DRIVE ID: ");
_LIT8(KComplete, " COMPLETE FLAG: ");
_LIT8(KFalse, "FALSE.");
_LIT8(KTrue, "TRUE.");
RFileLogger logger;
CleanupClosePushL(logger);
if (logger.Connect() == KErrNone)
{
logger.CreateLog(_L("msgs"), _L("Cache.txt"), EFileLoggingModeAppend);
logger.SetDateAndTime(EFalse, EFalse);
logger.Write(_L(" Message Index Cache Structure."));
logger.Write(_L("--------------------------------"));
logger.Write(_L(""));
}
CMsvCacheVisibleFolder* folderNode = NULL;
TDblQueIter<CMsvCacheVisibleFolder> dqIter(iFolderListHeader);
dqIter.SetToFirst();
while((folderNode = dqIter++)!=NULL)
{
RBuf8 text;
CleanupClosePushL(text);
text.CreateL(100);
text.Append(KFolderId);
text.AppendNum(folderNode->GetFolderId());
#if (defined SYMBIAN_MSGS_ENHANCED_REMOVABLE_MEDIA_SUPPORT)
text.Append(KDriveId);
text.AppendNum(folderNode->GetDrive());
#endif
text.Append(KComplete);
if(folderNode->IsComplete())
{
text.Append(KTrue);
}
else
{
text.Append(KFalse);
}
logger.Write(text);
CleanupStack::PopAndDestroy(); // text
folderNode->Print(logger);
logger.Write(_L(""));
}
logger.CloseLog();
CleanupStack::PopAndDestroy(); // logger
}
#endif // #ifdef _DEBUG
#endif // #if (defined SYMBIAN_MESSAGESTORE_UNIT_TESTCODE)