diff -r 000000000000 -r f979ecb2b13e pimappservices/calendar/server/src/agsentrymanager.cpp --- /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 + +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();iiDelete(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;iiDelete(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; + } + + +