pimappservices/calendar/server/src/agsentrymanager.cpp
changeset 0 f979ecb2b13e
child 18 c198609911f9
child 19 1984aceb8774
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/pimappservices/calendar/server/src/agsentrymanager.cpp	Tue Feb 02 10:12:19 2010 +0200
@@ -0,0 +1,993 @@
+// Copyright (c) 1997-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 "agsentrymanager.h"
+#include "agmutil.h"
+#include "agmentry.h"
+#include "agmdebug.h"
+
+#include <s32stor.h>
+
+CAgnEntryManager::CAgnEntryManager()
+//
+// Constructor
+//
+	:iNextAvailableGeneralStreamId(KNullStreamId),
+	iNextAvailableTodoStreamId(KNullStreamId),
+	iNextAvailableRptStreamId(KNullStreamId),
+	iCurrentGeneralStreamId(KNullStreamId),
+	iCurrentTodoStreamId(KNullStreamId),
+	iCurrentRptStreamId(KNullStreamId),
+	iGeneralBufferHasBeenStored(EFalse),
+	iTodoBufferHasBeenStored(EFalse),
+	iRptBufferHasBeenStored(EFalse),
+	iBufferHasBeenStored(EFalse),
+	iBufferedDeleting(EFalse)
+	{ 
+	}
+
+
+CAgnEntryManager* CAgnEntryManager::NewL()
+//
+// Create a CAgnEntryManager object
+//
+	{
+
+	CAgnEntryManager* Self=new(ELeave)CAgnEntryManager();
+	CleanupStack::PushL(Self);
+	Self->iGeneralBuffer=new(ELeave) CBuffer(TAgnEntryId::EMaxNumEntriesPerStream);
+	Self->iTodoBuffer=new(ELeave) CBuffer(TAgnEntryId::EMaxNumEntriesPerStream);
+	Self->iRptBuffer=new(ELeave) CBuffer(TAgnEntryId::EMaxNumEntriesPerStream);
+	Self->iIteratorBuffer=new(ELeave) CBuffer(TAgnEntryId::EMaxNumEntriesPerStream);
+	CleanupStack::Pop();
+	return (Self);
+	}
+
+
+CAgnEntryManager::~CAgnEntryManager()
+//
+// Destructor 
+//
+	{
+
+	if (iGeneralBuffer)
+		{
+		FreeBuffer(EGeneral);
+		delete iGeneralBuffer;
+		}
+	if (iTodoBuffer)
+		{
+		FreeBuffer(ETodo);
+		delete iTodoBuffer;
+		}
+	if (iRptBuffer)
+		{
+		FreeBuffer(ERpt);
+		delete iRptBuffer;
+		}
+	if (iIteratorBuffer)
+		{
+		FreeBuffer(EIterator);
+		delete iIteratorBuffer;
+		}
+	}
+
+TStreamId CAgnEntryManager::DoAddEntryL(CAgnEntry& aEntry, TStreamId& aCurrentStreamId,
+										TStreamId& aNextAvailableStreamId, CBuffer* aBuffer, TBufferType aBufferType)
+//
+// Add an entry by either adding it to an existing stream or creating a new stream if necessary
+//
+	{
+	if (aNextAvailableStreamId==KNullStreamId)  // create a new stream
+		{
+		if (aBuffer->Count() > 0) // clear out any previous entries loaded in memory
+			FreeBuffer(aBufferType); 
+
+		if (aEntry.EntryId().IsNullId()) // the entry might have already been assigned an id (via NextAvailableIdL)	
+			aEntry.SetEntryId(NextAvailableIdL(aBufferType)); // this will create a new stream
+
+		aNextAvailableStreamId = aEntry.EntryId().StreamId();
+   		
+		CAgnEntry* entry = aEntry.CloneL();
+		CleanupStack::PushL(entry);
+ 		aBuffer->AppendL(TBufItem(entry));
+ 		CleanupStack::Pop(entry);
+ 
+		__ASSERT_DEBUG(aBuffer->Count()==1,Panic(EAgmErrOnlyOneEntryAllowedInBuffer));
+		TRAPD(ret,StoreBufferL(aNextAvailableStreamId,aBuffer,aBufferType));		
+		if (ret!=KErrNone)
+			{
+			FreeBuffer(aBufferType); 
+			User::Leave(ret);
+			}
+
+		aCurrentStreamId = aNextAvailableStreamId;
+		
+		return (aNextAvailableStreamId);
+		}
+
+// else - the entry is to be added to an existing stream:
+
+	TAgnEntryId entryId = NextAvailableIdL(aBufferType);	
+	aEntry.SetEntryId(entryId);
+
+	CAgnEntry* entry = aEntry.CloneL();
+	CleanupStack::PushL(entry);
+	aBuffer->AppendL(TBufItem(entry));
+	CleanupStack::Pop(entry);
+
+	TInt ret;
+	TRAP(ret,StoreBufferL(entryId.StreamId(),aBuffer,aBufferType));
+	if (ret!=KErrNone)
+		{
+		if (ret!=KErrNoMemory) 
+			FreeBuffer(aBufferType);
+		else
+			{
+			aBuffer->Delete(aBuffer->Count()-1); //!!! any reason why not resetanddestroy?
+			delete entry;
+			}
+		User::Leave(ret);
+		}
+
+	if (aBuffer->Count()==TAgnEntryId::EMaxNumEntriesPerStream)
+		aNextAvailableStreamId = KNullStreamId;
+
+	return (KNullStreamId);	// indicate a new stream wasn't created during the addition 
+	}
+
+
+TStreamId CAgnEntryManager::AddEntryL(CAgnEntry& aEntry)
+//
+// Stores aEntry into a stream in iStore. If iNextAvailableStreamId is null then this indicates that the entry
+// is to be inserted into a brand new stream, otherwise iNextAvailableStreamId should identify an existing stream
+// which may contain other entries - if so then they are loaded into memory so that a unique id can be
+// assigned to the new entry, the existing entries are then re-written back to the stream along with
+// the new entry. The entries' details are kept loaded in memory. 
+//
+// An entry's id is a TStreamId which contains 28 bits for the stream id and 4 spare bits, it is necessary
+// to look at any other entries' 4 bits so that a unique number can be assigned to the new entry.
+//
+	{
+
+	__ASSERT_DEBUG(iStore,Panic(EAgmErrStreamStoreIsNull));
+
+	if (aEntry.Type()==CCalEntry::ETodo)
+		{
+		return (DoAddEntryL(aEntry,iCurrentTodoStreamId,iNextAvailableTodoStreamId,iTodoBuffer,ETodo));
+		}		
+	else
+		{
+		if (aEntry.RptDef())
+			{
+			return (DoAddEntryL(aEntry,iCurrentRptStreamId,iNextAvailableRptStreamId,iRptBuffer,ERpt));
+			}			
+		}
+		
+	return (DoAddEntryL(aEntry,iCurrentGeneralStreamId,iNextAvailableGeneralStreamId,iGeneralBuffer,EGeneral));
+	}
+
+
+/**
+ Update the details of the existing entry aEntry in the store
+
+ Call SetHowRichTextIsStored() IF its loaded in memory (rich text object will panic otherwise)
+ Notes:
+ 1) Was inline and still is -> simply rewrite to stream
+ 2) Was outline and still is
+		a) its not loaded - can't have changed -> do nothing
+		b) its loaded and has changed ->
+			i ) delete the store in the child stream
+			ii) store components as if it were a new entry
+ 3) Was inline and now is outline	 -> call StoreComponents
+ 4) Was outline and now is inline
+		a) delete the store
+		b) delete the child stream
+		c) save as normal
+
+If an entry moves from one stream to another and it was the last entry in the stream then
+the id of that stream is returned otherwise KNullStreamId is returned. If when an entry moves
+to a new stream the stream was not previoulsy existing then its id is returned via aNewStreamId
+which is otherwise KNullStreamId;
+
+@capability ReadUserData
+*/
+TStreamId CAgnEntryManager::UpdateEntryL(CAgnEntry& aEntry, TStreamId& aNewStreamId)
+	{
+
+	__ASSERT_DEBUG(iStore,Panic(EAgmErrStreamStoreIsNull));
+	aNewStreamId = KNullStreamId;
+
+	TAgnEntryId entryId = aEntry.EntryId();
+
+	// If the entry isn't already loaded in memory then do so
+	TInt pos;
+	CAgnEntry* originalEntry=SearchBuffers(entryId,pos);
+	if (!originalEntry)
+		{	
+		RestoreBufferL(entryId.StreamId());
+		originalEntry=SearchBuffers(entryId,pos);
+		}
+		
+	__ASSERT_ALWAYS(originalEntry,User::Leave(KErrNotFound));
+	TBufferType type = EGeneral;
+	if (originalEntry->Type() == CCalEntry::ETodo)
+		type = ETodo;
+	else
+		{
+		if (originalEntry->RptDef())
+			type = ERpt;
+		}		
+
+
+	//delete original entry from buffer and add new one then save buffer
+ 	CAgnEntry* entry = NULL;
+ 
+	switch (type)
+		{
+		case EGeneral:
+			{
+			delete (((*iGeneralBuffer)[pos]).Entry());
+			iGeneralBuffer->Delete(pos);
+			if (aEntry.RptDef())
+				{	// move the entry to the repeat buffer (thus generating a new id)
+				TAgnEntryId nullId;
+				aEntry.SetEntryId(nullId);
+				aNewStreamId = AddEntryL(aEntry);  
+				}
+			else
+				{
+				entry = aEntry.CloneL();
+				CleanupStack::PushL(entry);
+				iGeneralBuffer->AppendL(TBufItem(entry));
+  				CleanupStack::Pop();
+				}
+			if (iGeneralBuffer->Count())
+				StoreBufferL(iCurrentGeneralStreamId,iGeneralBuffer,EGeneral);
+			else
+				{
+				iNextAvailableGeneralStreamId = KNullStreamId;
+				return (iCurrentGeneralStreamId);
+				}
+			break;
+			}
+		case ETodo:
+			{
+			entry = aEntry.CloneL();
+			delete (((*iTodoBuffer)[pos]).Entry());
+			(*iTodoBuffer)[pos] = TBufItem(entry);
+			StoreBufferL(iCurrentTodoStreamId,iTodoBuffer,ETodo);
+			break;
+			}
+		case ERpt:
+			{
+			delete (((*iRptBuffer)[pos]).Entry());
+			iRptBuffer->Delete(pos);
+			if (aEntry.RptDef())
+				{
+  				entry = aEntry.CloneL();
+  				CleanupStack::PushL(entry);
+  				iRptBuffer->AppendL(TBufItem(entry));
+  				CleanupStack::Pop();
+				}
+			else
+				{ // move the entry to the general buffer (thus generating a new id)
+				TAgnEntryId nullId2;
+				aEntry.SetEntryId(nullId2);
+				aNewStreamId = AddEntryL(aEntry);
+				}
+			if (iRptBuffer->Count())
+				StoreBufferL(iCurrentRptStreamId,iRptBuffer,ERpt);
+			else
+				{
+				iNextAvailableRptStreamId = KNullStreamId;
+				return (iCurrentRptStreamId);
+				}
+			break;
+			}
+		}
+	return (KNullStreamId);
+	}
+
+
+TStreamId CAgnEntryManager::DeleteEntryL(const TAgnEntryId& aEntryId)
+//
+// Delete the entry identified by aEntryId from the store. If the entry was the last entry in the
+// stream then the stream's id is returned otherwise KNullStreamId is returned
+//
+	{
+
+	__ASSERT_DEBUG(iStore,Panic(EAgmErrStreamStoreIsNull));
+
+	// If the entry isn't already loaded in memory then do so
+	TInt pos;
+	CAgnEntry* pEntry=SearchBuffers(aEntryId,pos);
+	if (!pEntry)
+		{
+		if (BufferedDeleting())
+			{
+			if (!iGeneralBufferHasBeenStored && iGeneralBuffer->Count())
+				{
+				DeleteEntriesInBufferFromStoreL(iGeneralBuffer,iNumGeneralEntriesToDelete);
+				__ASSERT_DEBUG(iCurrentGeneralStreamId!=KNullStreamId,Panic(EAgmErrStreamIdIsNull));
+				__ASSERT_DEBUG(iGeneralBuffer->Count(),Panic(EAgmErrExpectedEntriesInStore)); // There should have been at least 1 entry left over from the previous deletion
+				StoreBufferL(iCurrentGeneralStreamId,iGeneralBuffer,EGeneral);
+				SetBufferHasBeenStored(ETrue,EGeneral);
+				}
+			if (!iTodoBufferHasBeenStored && iTodoBuffer->Count())
+				{
+				DeleteEntriesInBufferFromStoreL(iTodoBuffer,iNumTodoEntriesToDelete);
+				__ASSERT_DEBUG(iCurrentTodoStreamId!=KNullStreamId,Panic(EAgmErrStreamIdIsNull));
+				__ASSERT_DEBUG(iTodoBuffer->Count(),Panic(EAgmErrExpectedEntriesInStore)); // There should have been at least 1 entry left over from the previous deletion
+				StoreBufferL(iCurrentTodoStreamId,iTodoBuffer,ETodo);
+				SetBufferHasBeenStored(ETrue,ETodo);
+				}
+			if (!iRptBufferHasBeenStored && iRptBuffer->Count())
+				{
+				DeleteEntriesInBufferFromStoreL(iRptBuffer,iNumRptEntriesToDelete);
+				__ASSERT_DEBUG(iCurrentRptStreamId!=KNullStreamId,Panic(EAgmErrStreamIdIsNull));
+				__ASSERT_DEBUG(iRptBuffer->Count(),Panic(EAgmErrExpectedEntriesInStore)); // There should have been at least 1 entry left over from the previous deletion
+				StoreBufferL(iCurrentRptStreamId,iRptBuffer,ERpt);
+				SetBufferHasBeenStored(ETrue,ERpt);
+				}
+			}
+		RestoreBufferL(aEntryId.StreamId());
+		pEntry=SearchBuffers(aEntryId,pos);
+		}
+	__ASSERT_ALWAYS(pEntry,User::Leave(KErrNotFound));
+
+	if (pEntry->Type()==CCalEntry::ETodo)
+		return (DoDeleteEntryL(pEntry,iTodoBuffer,iNumTodoEntriesToDelete,iNextAvailableTodoStreamId,iCurrentTodoStreamId,ETodo,pos));
+	else
+		{
+		if (pEntry->RptDef())
+			return (DoDeleteEntryL(pEntry,iRptBuffer,iNumRptEntriesToDelete,iNextAvailableRptStreamId,iCurrentRptStreamId,ERpt,pos));
+		}
+	return (DoDeleteEntryL(pEntry,iGeneralBuffer,iNumGeneralEntriesToDelete,iNextAvailableGeneralStreamId,iCurrentGeneralStreamId,EGeneral,pos));
+	}
+
+TStreamId CAgnEntryManager::DoDeleteEntryL(CAgnEntry* aEntry, CBuffer* aBuffer, TInt& aNumEntriesToDelete,
+											TStreamId& aNextAvailableStreamId, const TStreamId& aCurrentStreamId,
+											TBufferType aBufferType, TInt aPos)
+//
+// Delete the entry. 
+// If the entry was the last entry in a stream then the stream's id is returned otherwise KNullStreamId
+// is returned.
+//
+	{
+	TStreamId streamId = aEntry->EntryId().StreamId();
+	 
+	if (BufferedDeleting())
+		{
+		((*aBuffer)[aPos]).SetToBeDeleted(ETrue);
+		if (++aNumEntriesToDelete == aBuffer->Count())
+			{
+			__ASSERT_DEBUG(aCurrentStreamId!=KNullStreamId,Panic(EAgmErrStreamIdIsNull));
+			DeleteEntriesInBufferFromStoreL(aBuffer,aNumEntriesToDelete);
+			__ASSERT_DEBUG(aBuffer->Count()==0,Panic(EAgmErrExpectedEntriesInStore)); // All the entries should have been marked for deletion
+			SetBufferHasBeenStored(ETrue,aBufferType); // the removal of a stream is semantically the same as rewriting it
+										// with a lesser number of entries - hence the call to SetBufferHasBeenStored
+			aNextAvailableStreamId = KNullStreamId;
+			return (aCurrentStreamId); // indicate the stream is empty
+			}
+		else
+			SetBufferHasBeenStored(EFalse,aBufferType);
+		}
+	else
+		{
+		__ASSERT_DEBUG(aCurrentStreamId!=KNullStreamId,Panic(EAgmErrStreamIdIsNull));
+		delete aEntry;
+		aBuffer->Delete(aPos);
+		if (aBuffer->Count()) // only write back to the stream if it contains entries
+			StoreBufferL(aCurrentStreamId,aBuffer,aBufferType);
+		else
+			{
+			SetBufferHasBeenStored(ETrue,aBufferType);
+			aNextAvailableStreamId = KNullStreamId;
+			return (streamId); // indicate the stream is empty
+			}
+		}
+	return (KNullStreamId);
+	}
+
+
+CAgnEntry* CAgnEntryManager::FetchEntryL(const TAgnEntryId& aEntryId)
+//
+// Create, restore and return the entry identified by aEntryId. If the entry cannot be found or a problem
+// is encountered reading the stream then the function leaves (with KErrNotFound in the former case).
+//
+	{
+	__ASSERT_DEBUG(iStore, Panic(EAgmErrStreamStoreIsNull));
+
+	TInt pos;
+	TBool createdEntryFromStream(EFalse);
+
+	CAgnEntry* pEntry = SearchBuffers(aEntryId, pos);
+	
+	if ( ! pEntry )
+		{
+		if ( BufferedDeleting() ) 
+		// don't want to restore into the buffer during i.e. merging as this will slow things down dramatically 
+			{			
+			RStoreReadStream stream;
+			stream.OpenLC(*iStore, aEntryId.StreamId());
+
+			TBufferType type = TBufferType(stream.ReadInt8L());
+			__ASSERT_ALWAYS(type == EGeneral || type == ETodo || type == ERpt, User::Leave(KErrCorrupt));
+
+			TUint8 entryCount = stream.ReadUint8L();
+			__ASSERT_ALWAYS(entryCount <= TAgnEntryId::EMaxNumEntriesPerStream, User::Leave(KErrCorrupt));
+
+			for ( TInt ii = 0; ii < entryCount; ++ii )
+				{
+				pEntry = CAgnEntry::NewL(stream);
+
+				if ( pEntry->EntryId() == aEntryId )
+					{
+					createdEntryFromStream = ETrue;
+					break;
+					}
+
+				delete pEntry;
+				pEntry = NULL;
+				}
+
+			CleanupStack::PopAndDestroy();
+			
+			if (createdEntryFromStream)
+				{
+				CleanupStack::PushL(pEntry);
+				}
+			}
+		else
+			{
+		   	RestoreBufferL(aEntryId.StreamId());
+			pEntry = SearchBuffers(aEntryId, pos);
+			}
+		}
+
+	// sanity check, if no entry found leave
+
+	__ASSERT_ALWAYS(pEntry, User::Leave(KErrNotFound));
+
+	// allocate a new entry, copy from the found one and return
+	
+	CAgnEntry* entry = pEntry->CloneL();
+	if (createdEntryFromStream)
+		{	
+		CleanupStack::PopAndDestroy(pEntry);
+		}
+	
+	return (entry);
+	}
+
+
+TInt CAgnEntryManager::RestoreBufferL(const TStreamId& aStreamId)
+//
+// Continuously reads entries from aStream until there are none left, and stores the
+// details to an internal data structure.
+//
+	{
+
+	__ASSERT_DEBUG(iStore,Panic(EAgmErrStreamStoreIsNull));
+	RStoreReadStream stream;
+	stream.OpenLC(*iStore,aStreamId);
+
+	TBufferType type;
+	if(iUseIteratorBuffer)
+		{
+		//need to read the type from the stream, but ignore it
+		stream.ReadInt8L();
+		type = EIterator;
+		}
+	else
+		{
+		type = TBufferType(stream.ReadInt8L());
+		__ASSERT_ALWAYS(type == EGeneral || type == ETodo || type == ERpt,
+						User::Leave(KErrCorrupt));
+		}
+	
+	CBuffer* buffer = NULL;
+	switch (type)
+		{
+		case EGeneral:	iCurrentGeneralStreamId = KNullStreamId;
+						buffer = iGeneralBuffer;
+						break;
+		case ETodo:		iCurrentTodoStreamId = KNullStreamId; 
+						buffer = iTodoBuffer;
+						break;
+		case ERpt:		iCurrentRptStreamId = KNullStreamId; 
+						buffer = iRptBuffer;
+						break;
+		case EIterator:	buffer = iIteratorBuffer;
+						break;
+		default: 
+			{
+			_DBGLOG_ENTRY(AgmDebug::DebugLog("Entry Manager: CAgnEntryManager::RestoreBufferL Panic: Invalid Buffer Type - Unknown type");)
+			DBG_PANIC(EAgmErrInvalidBufferType);
+			}
+		}
+
+	TUint8 count = stream.ReadUint8L();
+	__ASSERT_ALWAYS(count <= TAgnEntryId::EMaxNumEntriesPerStream,User::Leave(KErrCorrupt));
+
+	FreeBuffer(type);
+
+	for (TInt ii=0; ii < count; ++ii)
+		{
+		CAgnEntry* entry = NULL;
+		TRAPD(ret, entry = CAgnEntry::NewL(stream));
+		if (ret!=KErrNone)
+			{
+			if (ret == KErrCorrupt || ret == KErrEof) // skip any corrupted entries 
+				{ 
+				_DBGLOG_ENTRY(AgmDebug::DebugLog("Entry Manager: RestoreBufferL Panic - Failed to restore buffer");)
+				DBG_PANIC(EAgmErrFailedToRestoreBuffer);
+				continue; 
+				}
+			else  //i.e. run out of memory or file read error
+				{
+				FreeBuffer(type);
+				User::Leave(ret);			
+				}
+			}
+	
+		TRAP(ret,buffer->AppendL(TBufItem(entry)));
+		if (ret!=KErrNone)
+			{
+			FreeBuffer(type);
+			delete entry;
+			User::Leave(ret);
+			}
+		}
+		
+	__ASSERT_DEBUG(count == buffer->Count(), Panic(EAgmErrFailedToRestoreBuffer));
+	switch (type)
+		{
+		case EGeneral:	iCurrentGeneralStreamId = aStreamId; 
+						iNumGeneralEntriesToDelete = 0;
+						break;
+		case ETodo:		iCurrentTodoStreamId = aStreamId; 
+						iNumTodoEntriesToDelete = 0;
+						break;
+		case ERpt:		iCurrentRptStreamId = aStreamId; 
+						iNumRptEntriesToDelete = 0;
+						break;
+		case EIterator:	break;
+		default: 
+			{
+			_DBGLOG_ENTRY(AgmDebug::DebugLog("Entry Manager: RestoreBufferL Panic - Invalid Buffer Type - Type unknown");)
+			DBG_PANIC(EAgmErrInvalidBufferType);
+			}
+		}
+	CleanupStack::PopAndDestroy();
+	iLastRestored = type;
+	return (count);
+	}
+
+CAgnEntry* CAgnEntryManager::SearchBuffers(const TAgnEntryId& aEntryId, TInt& aPos)
+//
+// Search the entries currently loaded in memory looking for one with an id equal to aEntryId 
+// and return a pointer to it if found also set aPos to its position within the array, 
+// return NULL if the entry can't be found
+//	
+	{
+
+	if (iGeneralBuffer->Count() !=0 && ((*iGeneralBuffer)[0]).Entry()->EntryId().StreamId() == aEntryId.StreamId())
+		{
+		for (TInt ii=iGeneralBuffer->Count(); ii > 0; --ii)
+			{
+			if (((*iGeneralBuffer)[ii-1]).Entry()->EntryId()==aEntryId)
+				{
+				aPos=ii-1;
+				return (((*iGeneralBuffer)[ii-1]).Entry());			
+				}
+			}
+		}
+
+	if (iTodoBuffer->Count() !=0 && ((*iTodoBuffer)[0]).Entry()->EntryId().StreamId() == aEntryId.StreamId())
+		{
+		for (TInt ii=iTodoBuffer->Count(); ii > 0; --ii)
+			{
+			if (((*iTodoBuffer)[ii-1]).Entry()->EntryId()==aEntryId)
+				{
+				aPos=ii-1;
+				return (((*iTodoBuffer)[ii-1]).Entry());			
+				}
+			}
+		}
+
+	if (iRptBuffer->Count() !=0 && ((*iRptBuffer)[0]).Entry()->EntryId().StreamId() == aEntryId.StreamId())
+		{
+		for (TInt ii=iRptBuffer->Count(); ii > 0; --ii)
+			{
+			if (((*iRptBuffer)[ii-1]).Entry()->EntryId()==aEntryId)
+				{
+				aPos=ii-1;
+				return (((*iRptBuffer)[ii-1]).Entry());			
+				}
+			}
+		}
+
+	aPos=-1;
+	return (NULL);
+	}
+
+const CAgnEntry* CAgnEntryManager::operator[](TInt aIndex) const
+//
+// Return a pointer to the aIndex'th entry currently loaded in memory
+//
+	{ 
+	if(iUseIteratorBuffer)
+		{
+		__ASSERT_DEBUG(aIndex >= 0 && aIndex < iIteratorBuffer->Count(),Panic(EAgmErrInvalidEntryManagerIndex));
+		return(((*iIteratorBuffer)[aIndex]).Entry());
+		}
+	else
+		{
+		switch (iLastRestored)
+			{
+			case EGeneral:	__ASSERT_DEBUG(aIndex >= 0 && aIndex < iGeneralBuffer->Count(),Panic(EAgmErrInvalidEntryManagerIndex));
+							return(((*iGeneralBuffer)[aIndex]).Entry()); 
+			case ETodo:		__ASSERT_DEBUG(aIndex >= 0 && aIndex < iTodoBuffer->Count(),Panic(EAgmErrInvalidEntryManagerIndex));
+							return(((*iTodoBuffer)[aIndex]).Entry()); 
+			case ERpt:		__ASSERT_DEBUG(aIndex >= 0 && aIndex < iRptBuffer->Count(),Panic(EAgmErrInvalidEntryManagerIndex));
+							return(((*iRptBuffer)[aIndex]).Entry());
+		default: 
+				{
+				_DBGLOG_ENTRY(AgmDebug::DebugLog("Entry Manager: CAgnEntryManager::operator[] Panic - Invalid Buffer Type");)
+				DBG_PANIC(EAgmErrInvalidBufferType);
+				}
+			}
+		}
+	return (NULL);
+	}
+
+
+
+void CAgnEntryManager::StoreBufferL(const TStreamId& aStreamId,
+									const CBuffer* aBuffer, TBufferType aType)
+//
+// Write the entries in the buffer to the stream identified by aStreamId
+//
+	{
+
+	__ASSERT_DEBUG(aStreamId!=KNullStreamId,Panic(EAgmErrStreamIdIsNull));
+	__ASSERT_DEBUG(iStore,Panic(EAgmErrStreamStoreIsNull));
+	__ASSERT_DEBUG(aBuffer->Count() <= TAgnEntryId::EMaxNumEntriesPerStream,Panic(EAgmErrEntryBufferExceedsMaxStreamSize));
+
+	if (aBuffer->Count()==0)
+		return;
+
+	RStoreWriteStream out;
+	out.ReplaceLC(*iStore,aStreamId);
+
+	TInt count = aBuffer->Count();
+	__ASSERT_ALWAYS(count <= TAgnEntryId::EMaxNumEntriesPerStream,Panic(EAgnErrEntryManagerInternalError));
+	out.WriteInt8L(aType);
+	out.WriteUint8L(count);
+	// Externalize the BC version of the entry (this is the bit only old Agenda's will see)
+	TInt jj;
+	for (jj = 0; jj < count; ++jj)
+		{
+		CAgnEntry* entry = ((*aBuffer)[jj]).Entry();
+		__ASSERT_ALWAYS(entry->Type()==CCalEntry::EAppt || entry->Type()==CCalEntry::EEvent ||
+					entry->Type()==CCalEntry::EAnniv || entry->Type()==CCalEntry::ETodo || 
+					entry->Type()==CCalEntry::EReminder, Panic(EAgmErrBadTypeEntry));
+
+		out.WriteUint8L(entry->Type());
+		entry->ExternalizeL(out);
+		}
+
+	out.CommitL();
+	CleanupStack::PopAndDestroy();	
+	SetBufferHasBeenStored(ETrue,aType);
+	}
+
+
+void CAgnEntryManager::SetBufferHasBeenStored(TBool aSetting,TBufferType aType)
+	{
+	switch (aType)
+		{
+		case EGeneral:	iGeneralBufferHasBeenStored = aSetting; break;
+		case ETodo:		iTodoBufferHasBeenStored = aSetting; break;
+		case ERpt:		iRptBufferHasBeenStored = aSetting; break;
+		default: 
+			{
+			_DBGLOG_ENTRY(AgmDebug::DebugLog("Entry Manager: CAgnEntryManager::SetBufferHasBeenStored Panic - Invalid Buffer Type");)
+			DBG_PANIC(EAgmErrInvalidBufferType);
+			}
+		}
+	iBufferHasBeenStored = aSetting;
+	}
+
+void CAgnEntryManager::StoreBuffersL()
+//
+// Store all unstored buffers but don't free them afterwards
+//
+	{
+	
+	StoreAndMaybeFreeBuffersL(EFalse);
+	}
+
+
+void CAgnEntryManager::FlushBuffersL()
+//
+// During a prolonged activity such as merging,tidying then entries are not updated in the store
+// until the buffer is full, if one of these activities finishes before the buffer has had a change
+// to automaticaly update its contents in the store then this function must be called to make it do so.
+//
+	{
+
+	StoreAndMaybeFreeBuffersL(ETrue);
+	}
+
+
+void CAgnEntryManager::StoreAndMaybeFreeBuffersL(TBool aFreeBuffers)
+	{
+
+	__ASSERT_DEBUG(iStore,Panic(EAgmErrStreamStoreIsNull));
+	if (!iGeneralBufferHasBeenStored) 
+		{
+		if (BufferedDeleting())
+			DeleteEntriesInBufferFromStoreL(iGeneralBuffer,iNumGeneralEntriesToDelete);
+		if (iCurrentGeneralStreamId != KNullStreamId)
+			StoreBufferL(iCurrentGeneralStreamId,iGeneralBuffer,EGeneral);
+		SetBufferHasBeenStored(ETrue,EGeneral);
+		}
+	if (aFreeBuffers)
+		FreeBuffer(EGeneral);
+
+	if (!iTodoBufferHasBeenStored) 
+		{
+		if (BufferedDeleting())
+			DeleteEntriesInBufferFromStoreL(iTodoBuffer,iNumTodoEntriesToDelete);
+		if (iCurrentTodoStreamId != KNullStreamId)
+			StoreBufferL(iCurrentTodoStreamId,iTodoBuffer,ETodo);
+		SetBufferHasBeenStored(ETrue,ETodo);
+		}
+	if (aFreeBuffers)
+		FreeBuffer(ETodo);
+
+	if (!iRptBufferHasBeenStored) 
+		{
+		if (BufferedDeleting())
+			DeleteEntriesInBufferFromStoreL(iRptBuffer,iNumRptEntriesToDelete);
+		if (iCurrentRptStreamId != KNullStreamId)
+			StoreBufferL(iCurrentRptStreamId,iRptBuffer,ERpt);
+		SetBufferHasBeenStored(ETrue,ERpt);
+		}
+	if (aFreeBuffers)
+		FreeBuffer(ERpt);
+	}
+
+
+TAgnEntryId CAgnEntryManager::NextAvailableIdL(TBufferType aBufferType)
+//
+// Find the next available id for an entry. If iNextAvailableStreamId is null then it indicates that
+// the next entry to be added will go into a new stream in which case the stream must be created
+// to determine its stream id. If iNextAvailableStreamId is not null then the entries which currently exist
+// in the stream identifed by aStreamId must be loaded into memory to find the next partial id.
+//
+// An entry's id consists of 28 bits corresponsing to the stream id of the stream in which it
+// is stored and 4 bits giving it a unique number within that stream.
+//
+	{
+
+	__ASSERT_DEBUG(iStore,Panic(EAgmErrStreamStoreIsNull));
+
+	CBuffer* buffer = NULL;
+	TStreamId nextAvailableStreamId=KNullStreamId;
+	switch (aBufferType)
+		{
+		case EGeneral:	buffer = iGeneralBuffer; 
+						nextAvailableStreamId = iNextAvailableGeneralStreamId;
+						break; 
+		case ETodo:		buffer = iTodoBuffer; 
+						nextAvailableStreamId = iNextAvailableTodoStreamId;
+						break; 
+		case ERpt:		buffer = iRptBuffer;
+						nextAvailableStreamId = iNextAvailableRptStreamId;	 
+						break; 
+		default: 
+			{
+			_DBGLOG_ENTRY(AgmDebug::DebugLog("Entry Manager: CAgnEntryManager::NextAvailableIdL Panic - Invalid Buffer Type");)
+			DBG_PANIC(EAgmErrInvalidBufferType);
+			}
+		}
+
+	if (nextAvailableStreamId==KNullStreamId)
+		{
+		TStreamId newStreamId = iStore->ExtendL();
+		return (TAgnEntryId(newStreamId,0)); // entries partial ids start numbering from 0
+		}
+	else
+		{
+		// If the entries in the stream to which the new entry will be added are not already
+		// loaded in memory then do so
+		if (buffer->Count()==0 || (buffer->Count() > 0 && (((*buffer)[0]).Entry()->EntryId().StreamId() != nextAvailableStreamId)))
+			RestoreBufferL(nextAvailableStreamId);
+
+		TUint uniqueIdPortion = FindUniqueIdPortion(buffer);
+
+#if defined(_DEBUG)
+TInt pos;
+if (SearchBuffers(TAgnEntryId(nextAvailableStreamId,uniqueIdPortion),pos) != NULL)
+	{
+	Panic(EAgmErrEntryIdNotUnique);
+	}
+#endif
+		
+		return (TAgnEntryId(nextAvailableStreamId,uniqueIdPortion));
+		}
+	}
+
+
+TUint CAgnEntryManager::FindUniqueIdPortion(const CBuffer* aBuffer)
+//
+// Finds and returns an entry id portion that is not being used by any other entry.
+// Where an id portion is a number in the range 0 to 15.
+//
+	{
+	__ASSERT_DEBUG(aBuffer->Count() <=16, Panic(EAgmErrEntryBufferExceedsMaxStreamSize));
+	static const TUint mapToBits[16] = 
+		{
+		EBit1,EBit2,EBit3,EBit4,EBit5,EBit6,EBit7,EBit8,EBit9,
+		EBit10,EBit11,EBit12,EBit13,EBit14,EBit15,EBit16
+		};
+
+	TUint usedPortions=0;
+	for (TInt ii=aBuffer->Count(); ii > 0; --ii)
+		{
+		TUint id = ((*aBuffer)[ii-1]).Entry()->EntryId().PartialId();
+		__ASSERT_DEBUG(id <= 15,Panic(EAgmErrInvalidPartialId));
+		usedPortions |= mapToBits[id];
+		}
+
+	__ASSERT_DEBUG(usedPortions <= 0xFFFF,Panic(EAgmErrNoMorePartialIdsAvailable));
+
+	TUint id=0;
+	TUint num=1;
+	while (usedPortions & num)
+		{
+		num <<= 1;
+		++id;
+		}
+
+	__ASSERT_DEBUG(id<=15,Panic(EAgmErrPartialIdOverFlow));
+	return (id);
+	}
+
+
+void CAgnEntryManager::DeleteEntriesInBufferFromStoreL(CBuffer* aBuffer,TInt& aNumEntriesToDelete)
+//
+// For each entry in the buffer marked as being deleted, delete any child streams within the
+// store owned by the entry.
+//
+	{
+
+	__ASSERT_DEBUG(iStore,Panic(EAgmErrStreamStoreIsNull));
+	TBool found = EFalse;
+	FOREVER
+		{
+		found = EFalse;
+		for (TInt ii=0,n=aBuffer->Count();ii<n;++ii)
+			{
+			if (((*aBuffer)[ii]).ToBeDeleted())
+				{
+				delete ((*aBuffer)[ii]).Entry();
+				aBuffer->Delete(ii);		
+				found = ETrue;
+				break;
+				}
+			}
+		if (!found)
+			break;
+		}		
+	aNumEntriesToDelete = 0;
+	}
+
+
+void CAgnEntryManager::SetBufferedDeleting(TBool aSetting)
+//
+// When this is set to true, whenever an entry is deleted it is deleted from the buffer but
+// but not from the store until	the buffer is empty (in which case the entire stream in which
+// the entry/s reside is deleted) or another action is performed such as fetching an entry.
+//
+	{ 
+	iBufferedDeleting = aSetting; 
+	SetBufferHasBeenStored(ETrue,EGeneral);
+	SetBufferHasBeenStored(ETrue,ETodo);
+	SetBufferHasBeenStored(ETrue,ERpt);
+	}
+
+
+void CAgnEntryManager::FreeBuffers()
+	{
+	FreeBuffer(EGeneral);
+	FreeBuffer(ETodo);
+	FreeBuffer(ERpt);
+	}
+
+
+void CAgnEntryManager::FreeBuffer(TBufferType aBufferType)
+	{ 
+
+	CBuffer* buffer=NULL;
+	switch (aBufferType)
+		{
+		case EGeneral:	buffer = iGeneralBuffer; break;
+		case ETodo:		buffer = iTodoBuffer; break;	
+		case ERpt:		buffer = iRptBuffer; break;
+		case EIterator:	buffer = iIteratorBuffer; break;
+		default: 
+			{
+			_DBGLOG_ENTRY(AgmDebug::DebugLog("Entry Manager: CAgnEntryManager::FreeBuffer Panic - Invalid Buffer Type");)
+			DBG_PANIC(EAgmErrInvalidBufferType);
+			}
+		}
+
+	TInt n=buffer->Count();
+	for (TInt ii=0;ii<n;++ii)
+		delete ((*buffer)[ii]).Entry();
+	buffer->Delete(0,n); // no point in compressing 
+	}
+
+
+void CAgnEntryManager::Reset()
+	{
+	FreeBuffers(); 
+	iNumGeneralEntriesToDelete = 0;
+	iNumTodoEntriesToDelete = 0;
+	iNumRptEntriesToDelete = 0;
+	iBufferedDeleting = EFalse;
+	iGeneralBufferHasBeenStored = EFalse;
+	iTodoBufferHasBeenStored = EFalse;
+	iRptBufferHasBeenStored = EFalse;
+	iNextAvailableGeneralStreamId = KNullStreamId;
+	iNextAvailableTodoStreamId = KNullStreamId;
+	iNextAvailableRptStreamId = KNullStreamId;
+	iCurrentGeneralStreamId = KNullStreamId;
+	iCurrentTodoStreamId = KNullStreamId;
+	iCurrentRptStreamId = KNullStreamId;
+	}
+
+
+void CAgnEntryManager::InternalizeL(RReadStream& aStream)
+	{
+
+	aStream >> iNextAvailableGeneralStreamId;
+	aStream >> iNextAvailableTodoStreamId;
+	aStream >> iNextAvailableRptStreamId;
+	}
+
+
+void CAgnEntryManager::ExternalizeL(RWriteStream& aStream) const
+	{
+
+	aStream << iNextAvailableGeneralStreamId;
+	aStream << iNextAvailableTodoStreamId;
+	aStream << iNextAvailableRptStreamId;
+	}
+
+void CAgnEntryManager::CopyStreamIds(const CAgnEntryManager& aOriginalManager)
+	{
+	iNextAvailableGeneralStreamId = aOriginalManager.iNextAvailableGeneralStreamId;
+	iNextAvailableTodoStreamId = aOriginalManager.iNextAvailableTodoStreamId;
+	iNextAvailableRptStreamId = aOriginalManager.iNextAvailableRptStreamId;
+	}
+
+
+