pimprotocols/pbap/server/pbapfoldernodepb.cpp
changeset 0 e686773b3f54
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/pimprotocols/pbap/server/pbapfoldernodepb.cpp	Tue Feb 02 10:12:17 2010 +0200
@@ -0,0 +1,444 @@
+// 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();
+ 	}
+