pimprotocols/pbap/server/pbapfoldernodepb.cpp
author Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
Tue, 02 Feb 2010 10:12:17 +0200
changeset 0 e686773b3f54
permissions -rw-r--r--
Revision: 201003 Kit: 201005

// Copyright (c) 2006-2009 Nokia Corporation and/or its subsidiary(-ies).
// All rights reserved.
// This component and the accompanying materials are made available
// under the terms of "Eclipse Public License v1.0"
// which accompanies this distribution, and is available
// at the URL "http://www.eclipse.org/legal/epl-v10.html".
//
// Initial Contributors:
// Nokia Corporation - initial contribution.
//
// Contributors:
//
// Description:
//

#include <cntdb.h>

#include "pbapfoldernodepb.h"
#include "pbaprequest.h"
#include "pbapfolderclient.h"
#include "pbapexporter.h"
#include "pbaperrorreporter.h"
#include "pbapserver.h"
#include "pbapappheader.h"

#include "btaccesshostlog.h"

// constants
_LIT(KFolderPb, "pb");


/*static*/ CFolderNodePb* CFolderNodePb::NewL(MVirtualFolderClient& aClient)
	{
	LOG_STATIC_FUNC
	CFolderNodePb* self = new (ELeave) CFolderNodePb(aClient);
	CleanupStack::PushL(self);
	self->ConstructL();
	CleanupStack::Pop(self);
	return self;
	}


CFolderNodePb::CFolderNodePb(MVirtualFolderClient& aClient)
: CFolderNode(aClient, KFolderPb())
	{
	LOG_FUNC
	}

		
void CFolderNodePb::ConstructL()
	{
	LOG_FUNC
	iHandleCache = CPbapPbHandleCache::NewL(iClient.ContactDB(), *this);
	}


CFolderNodePb::~CFolderNodePb()
	{
	LOG_FUNC

	delete iHandleCache;
	delete iAsyncExporter;		
	delete iSearchResults;
	iCacheIndexesToExport.Close();
	}
	
	
void CFolderNodePb::CancelGet()
	{
	LOG_FUNC
	// cancel asynchronous searching and sorting of contacts
	iClient.ContactDbViews().CancelSearchAndSortRequest();
	
	// cancel any asynchronous export operations and avoid iAsyncExporter is deleted
	// by itself
	CPbapPbExporter* tmpExporter = iAsyncExporter;
	iAsyncExporter = NULL;
	delete tmpExporter;
	
	// reset for next get request
	Reset();
	}

void CFolderNodePb::GetComplete()
	{
	LOG_FUNC
	
	if (iAsyncExporter)
		{
		delete iAsyncExporter;
		iAsyncExporter = NULL;
		}
	}


 void CFolderNodePb::Reset()
 	{
 	LOG_FUNC
	
 	delete 	iSearchResults;
	iSearchResults = NULL;
 
	delete iAppHeader;
	iAppHeader = NULL;
	
	iCacheIndexesToExport.Reset();	
 	}
 
 				
TInt CFolderNodePb::DoGetItem(TInt aHandle)
	{
	LOG_FUNC
	// check that state has been reset after previous get request
	if(iAsyncExporter)
		{
		__ASSERT_DEBUG(EFalse, Panic(EVirtualFolderPbExportAlreadyExists));
		return KErrAlreadyExists;
		}

	TInt error = KErrNone;
	TInt index = 0;
				
	if (iPhonebookChanged)
		{
		// the Pb phonebook has been modified since the last get request
		// report precondition failed error to obex until next listing
		// request completes (or new Pbap session initiated)
		iClient.ErrorReporter().SendPreconditionFailedError();
		// error already reported above so don't update error
		
		delete iAppHeader;
		iAppHeader = NULL;
		}
	// search for the requested handle in the cache
	else if ((index = iHandleCache->FindHandle(aHandle)) != KErrNotFound)
		{
		// export the contact associated with this handle
		TRAP(error, iAsyncExporter = CPbapPbItemExporter::NewL(iClient, *this, *iHandleCache, index, 1, iAppHeader->VCardVersion(), iAppHeader->Filter()));
		}
	else 
		{
		// the handle does not exist must report not found error to Obex
		iClient.ErrorReporter().SendNotFoundError();
		// error already reported above so don't update error
		
		delete iAppHeader;
		iAppHeader = NULL;
		}
	return error;
	}


TInt CFolderNodePb::DoGetListing()
	{
	LOG_FUNC
	
	TInt error = KErrNone;	
	if (iAppHeader->SearchValue().Length() || iAppHeader->Order() != SymbianPBAP::EIndexed)
		{
		// search or non-indexed sort requested
		TRAP(error, SearchAndSortItemsL());
		}
	else
		{
		// the order is indexed and no search was requested so export listing now
		TRAP(error, ExportIndexSortedListingL());
		}
	
	if (error != KErrNone)
		{
		// error occurred so reset state ready for next get request
		Reset();
		}
	return error;	
	}


/**
 Use database views to search and sort contacts
*/
void CFolderNodePb::SearchAndSortItemsL()
	{
	LOG_FUNC
	if(iSearchResults)
		{
		__ASSERT_DEBUG(EFalse, Panic(EVirtualFolderSearchInProgress));
		User::Leave(KErrAlreadyExists);
		}
	
	// The views only support alphabetical and phonetic sort orders so for index ordered
	// searches use alphabetical order and then resort the results when search complete			
	SymbianPBAP::TOrder order = (iAppHeader->Order() == SymbianPBAP::EIndexed ? SymbianPBAP::EAlphabetical : iAppHeader->Order());

	// create empty array to store results
	iSearchResults = CContactIdArray::NewL();
				
	// start the asynchronous search and sort				
	iClient.ContactDbViews().GetContactIdsMatchingCriteriaL(order, iAppHeader->SearchAttribute(), iAppHeader->SearchValue(), *iSearchResults, *this);
	}


void CFolderNodePb::ExportIndexSortedListingL()
	{
	LOG_FUNC
	// check that state has been reset after previous get request
	if(iAsyncExporter)
		{
		__ASSERT_DEBUG(EFalse, Panic(EVirtualFolderPbExportAlreadyExists));
		User::Leave(KErrAlreadyExists);
		}
	
	if(iCacheIndexesToExport.Count() != 0)
		{
		__ASSERT_DEBUG(EFalse, Panic(EVirtualFolderCachedIndexes));
		User::Leave(KErrAlreadyExists);
		}
	
	// build an array of indexes to cache items (handle/contact Id pairs) to export.
	// Since the cache is ordered on handle the indexes match the ordering of the cache

	// determine the last handle to export and then start the append loop from iListStartOffset
	TInt maxHandle = Min(iAppHeader->MaxListCount() + iAppHeader->ListStartOffset(), HandleCount());
	for (TInt index = iAppHeader->ListStartOffset(); index < maxHandle; ++index)
		{
		iCacheIndexesToExport.AppendL(index);
		}
			
	// export array of cache items as listing
	iAsyncExporter = CPbapPbListingExporter::NewL(iClient, *this, *iHandleCache, iCacheIndexesToExport);
	}


TInt CFolderNodePb::DoGetCount()
	{
	LOG_FUNC
	// check that state has been reset correctly
	if(iAsyncExporter)
		{
		__ASSERT_DEBUG(EFalse, Panic(EVirtualFolderPbExportAlreadyExists));
		return KErrAlreadyExists;
		}
	
	// export the Pb phonebook size. This is equal to the number of handles in the
	// cache (assuming that 0.vcf is always counted even if no owner card is set)
	TRAPD(error, iAsyncExporter = CPbapPbCountExporter::NewL(iClient, *this, HandleCount()));
	return error;
	}


TInt CFolderNodePb::DoGetFolder()
	{
	LOG_FUNC
	// check that state has been reset correctly
	if(iAsyncExporter)
		{
		__ASSERT_DEBUG(EFalse, Panic(EVirtualFolderPbExportAlreadyExists));
		return KErrAlreadyExists;
		}
	
	// setup range of items to export
	TInt count = 0;
	if (iAppHeader->ListStartOffset() < HandleCount())
		{
		// limit the number of items to export to the number of handles available from
		// the offset
		count = Min(iAppHeader->MaxListCount(), HandleCount() - iAppHeader->ListStartOffset());
		}
				
	// export the contacts associated with range of handle cache indexes (these
	// are aleady sorted in increasing handle order)
	TRAPD(error, iAsyncExporter = CPbapPbItemExporter::NewL(iClient, *this, *iHandleCache, iAppHeader->ListStartOffset(), count, iAppHeader->VCardVersion(), iAppHeader->Filter()));
	return error;
	}


void CFolderNodePb::HandleCacheChanged()
	{
	LOG_FUNC
		
	// the contact item a handle refers to has changed. All PullvCard requests will
	// now be blocked until the next listing request (or session close)
	iPhonebookChanged = ETrue;
	// check to see if there is an export in progress
	if (iSearchResults || iAsyncExporter)
	
		{
		// cancel the export and report error according to the error reporting
		// scheme for modified/deleted handles
		CancelGet();
		}
	}


void CFolderNodePb::HandleSearchAndSortComplete(TInt aError)
	{
	LOG_FUNC
	
	if (aError == KErrNone)
		{
		// sorting and searching completed successfully
		TRAP(aError, ExportSearchAndSortResultsL());
		}
	
	if (aError != KErrNone)
		{
		// get request failed report error to obex and reset
		iClient.ErrorReporter().SendServiceUnavailableError();
		Reset();
		}
	}


void CFolderNodePb::ExportSearchAndSortResultsL()
	{
	LOG_FUNC	
	
	// check that state has been reset after previous get request
	if(iAsyncExporter)
		{
		__ASSERT_DEBUG(EFalse, Panic(EVirtualFolderPbExportAlreadyExists));
		User::Leave(KErrAlreadyExists);
		}
	if(iCacheIndexesToExport.Count() != 0)
		{
		__ASSERT_DEBUG(EFalse, Panic(EVirtualFolderCachedIndexes));
		User::Leave(KErrAlreadyExists);
		}

	TInt count = iSearchResults->Count();
	
	// if the order is indexed then to get here means that a search value has been specified
	if (iAppHeader->Order() == SymbianPBAP::EIndexed)
		{
		// sort the search result into index order
		RArray<TInt> sortedIndexes;
		CleanupClosePushL(sortedIndexes);
		for (TInt ii = 0; ii < count; ++ii)
			{
			TContactItemId contactId = (*iSearchResults)[ii];
			TInt index = iHandleCache->FindContactId(contactId);
			if (index != KErrNotFound)
				{
				// insert index in order to match handle ordering of cache
				sortedIndexes.InsertInOrderL(index);
				}
			}
		
		// results array no longer required
		delete iSearchResults;
		iSearchResults = NULL;
		
		// extract range of cache indexes to export
		count = Min(iAppHeader->MaxListCount() + iAppHeader->ListStartOffset(), sortedIndexes.Count());		
		for (TInt jj = iAppHeader->ListStartOffset(); jj < count; ++jj)
			{
			iCacheIndexesToExport.AppendL(sortedIndexes[jj]);
			}
		CleanupStack::PopAndDestroy(); // sortedIndexes	
		}
	// to get here means it's a non-index order sort or search
	else
		{
		// the search results array is sorted in the correct (alphabetic or
		// phonetic) order. Build array of indexes of cache items which match the
		// contact ids 
		TInt maxAndOffset = iAppHeader->MaxListCount() + iAppHeader->ListStartOffset();
		count = Min(count, maxAndOffset);
		
		// decide whether there is a OwnCard
		TBool hasOwnCard = ETrue;
		TInt ownCardItemIndex = iHandleCache->FindHandle(KOwnCardHandle); // always present
		TContactItemId contactId = iHandleCache->ContactIdAtL(ownCardItemIndex);
		if(contactId == KNullContactId)
			{
			hasOwnCard = EFalse;
			}

		// fill the array iCacheIndexsToExport
		for (TInt ii = iAppHeader->ListStartOffset(); ii < count; ++ii)
			{
			TContactItemId contactId = (*iSearchResults)[ii];
			TInt index = iHandleCache->FindContactId(contactId);
			if (index!=KErrNotFound)
				{
				iCacheIndexesToExport.AppendL(index);
				}
			}

		// append an empty own card (0.vcf) when doing sorting on alphabetic or phonetic order
		// search value should be empty when doing sorting
		if(iAppHeader->SearchValue().Size() == 0 && !hasOwnCard)
			{
			// "count < maxAndOffset" means that 
			// "iSearchResults->Count()" < "iAppHeader->MaxListCount() + iAppHeader->ListStartOffset()" 
			// in which case, we need to append an empty OwnCard entry to iCacheIndexesToExport.
			if(count < maxAndOffset)
				{
				iCacheIndexesToExport.AppendL(KOwnCardHandle);
				}
			}
		// results array no longer required
		delete iSearchResults;
		iSearchResults = NULL;			
		}
		
	// export items associated with list of handle cache indexes
	iAsyncExporter = CPbapPbListingExporter::NewL(iClient, *this, *iHandleCache, iCacheIndexesToExport);
	}


 void CFolderNodePb::HandleExportComplete(TInt aError)
 	{
 	LOG_FUNC
 		
 	if (aError)
 		{
 		if(iPhonebookChanged)
 			{
	 		iClient.ErrorReporter().SendPreconditionFailedError();
 			}
 		else
 			{
	 		// error occured during export report service unavailable error to obex 
	 		iClient.ErrorReporter().SendServiceUnavailableError();
 			}
 		}
 	
 	// a successful listing request allows the pb phonebook to be read from again
 	// after it has been modified
 	if (aError == KErrNone && iAppHeader->Operation() == EPullVCardListing)
 		{
 		iPhonebookChanged = EFalse;
 		}
 		
 	// get request complete	so reset state ready for next one
 	Reset();
 	}


 TInt CFolderNodePb::HandleCount()
 	{
 	return iHandleCache->Count();
 	}