pimappservices/calendar/server/src/agsasyncdelete.cpp
changeset 0 f979ecb2b13e
child 12 38571fd2a704
child 45 b6db4fd4947b
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/pimappservices/calendar/server/src/agsasyncdelete.cpp	Tue Feb 02 10:12:19 2010 +0200
@@ -0,0 +1,359 @@
+// 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 "agsasyncdelete.h"
+
+#include "agsfilemanager.h"
+#include "agmentry.h"
+#include "agsentrymodel.h"
+#include "agmrptdef.h"
+#include "agmutil.h"
+#include "agssess.h"
+#include "agmdebug.h"
+
+const TUint KTidyCallbackRate = 24;
+const TInt KTidyArrayGranularity = 16;
+
+/**
+Used to sort a list of TAgnEntryIds by their stream id component - used when tidying
+*/
+TInt CCalAsyncDelete::TStreamIdKey::Compare(TInt aLeft,TInt aRight) const
+	{
+	TUint32 left = static_cast<TAgnEntryId*>(At(aLeft))->StreamId().Value();
+	TUint32 right = static_cast<TAgnEntryId*>(At(aRight))->StreamId().Value();
+	TInt r = -1;
+	if (left == right)
+		{
+		r=0;
+		}
+	else if (left > right)
+		{
+		r=1;
+		}
+	return (r);
+	}
+
+CCalAsyncDelete::CCalAsyncDelete(CAgnEntryModel& aModel, TAgnChangeFilter& aChangeFilter, CAgnSimpleEntryTable& aEntryTable)
+	:iModel(aModel), iChangeFilter(aChangeFilter), iEntryTable(aEntryTable)
+	{
+	}
+
+void CCalAsyncDelete::ConstructL()
+	{
+	iAlreadyTidied = new(ELeave)CArrayFixFlat<TAgnEntryId>(KTidyArrayGranularity);
+	iTidyDeleteArray = new(ELeave)CArrayFixFlat<TAgnEntryId>(KTidyArrayGranularity);
+	}
+
+CCalAsyncDelete* CCalAsyncDelete::NewL(CAgnEntryModel& aModel, TAgnChangeFilter& aChangeFilter, CAgnSimpleEntryTable& aEntryTable)
+	{
+	CCalAsyncDelete* self = new(ELeave) CCalAsyncDelete(aModel, aChangeFilter, aEntryTable);
+	CleanupStack::PushL(self);
+	self->ConstructL();
+	CleanupStack::Pop(self);
+	return self;
+	}
+
+CCalAsyncDelete::~CCalAsyncDelete()
+	{
+	delete iTidyIter;
+	delete iAlreadyTidied;
+	delete iTidyDeleteArray;
+	}
+
+/**
+Determines if an instance of an entry falls within the tidy range.
+*/
+TBool CCalAsyncDelete::IsEntryValidToTidyL(const CAgnSimpleEntry& aSimpleEntry)
+	{
+	// non-repeating entries must fall in the time range because otherwise the entry iterator wouldn't find them
+	if (aSimpleEntry.RptDef())
+		{
+		TTime instance;
+		if ( ! aSimpleEntry.RptDef()->NudgeNextInstanceL(iTidyStartDate, instance, ETrue))
+		    {
+            // If the parent is repeating but all its instances are excepted it doesn't mean 
+            // that it cannot be deleted. It can if its time range is falling 
+            // within the time filter, meaning the parent is safe to tidy as 
+            // the child entries are already in the tidy up list.
+            if (aSimpleEntry.RptDef()->FirstInstanceL().LocalL() < iTidyStartDate || 
+                aSimpleEntry.RptDef()->LastInstanceL().LocalL() > iTidyEndDate)
+                {
+                return (EFalse);
+                }
+            else
+                {
+                return (ETrue);
+                }
+            }
+		
+        if (instance < iTidyStartDate || instance > iTidyEndDate)
+            {
+            return (EFalse);
+            }
+        }
+    return (ETrue);
+    }
+
+
+/**
+Calculate the number of entries to be tidied (so the percentage tidied so far
+can be determined) and create and required objects.
+@capability WriteUserData
+@capability ReadUserData
+*/
+TBool CCalAsyncDelete::SetUpDeleteL(const TAgnFilter& aFilter, const TTime& aTodaysDate, const TTime& aStartDate, const TTime& aEndDate)
+	{
+	iTidyStartDate = aStartDate;
+	iTidyEndDate = aEndDate;
+	iTidyDate = iTidyStartDate;
+
+	#if defined (__CAL_ASYNC_LOGGING__) || (__CAL_VERBOSE_LOGGING__)
+		{
+		TBuf<KMinTTimeStrLength> tidyEndDateTimeBuf;
+		AgmDebug::TTimeStrL(iTidyEndDate, tidyEndDateTimeBuf);
+		TBuf<KMinTTimeStrLength> tidyStartDateTimeBuf;
+		AgmDebug::TTimeStrL(iTidyStartDate, tidyStartDateTimeBuf);
+		AgmDebug::DebugLog("Async Delete: Range From %S To %S", &tidyStartDateTimeBuf, &tidyEndDateTimeBuf);
+		}
+	#endif
+		
+	iNumEntriesToProcess = 0;
+	delete iTidyIter;
+	iTidyIter = NULL;
+	
+	iTidyIter = new(ELeave) TAgnEntryDateIterator(iEntryTable, aTodaysDate, aFilter);
+	
+	for (iTidyIter->GotoL(iTidyStartDate); !iTidyIter->AtEnd() && iTidyIter->CurrentKey() <= iTidyEndDate; iTidyIter->NextL())
+		{
+		if (IsEntryValidToTidyL(iTidyIter->CurrentElement()))
+			{
+			++iNumEntriesToProcess;
+			}
+		}
+	
+	if (!iNumEntriesToProcess)
+		{ // there are no entries to tidy that match the date/filter criteria
+		return (EFalse); 
+		}
+
+	iNumEntriesProcessed = 0;
+	return (ETrue);
+	}
+
+TBool CCalAsyncDelete::CheckDateIteratorEnd() const
+	{
+	return ( ! iTidyIter->AtEnd() && iTidyIter->CurrentKey() <= iTidyEndDate);
+	}
+
+/**
+Tidy the agenda model and return the progress as a percentage of the number of entries tidied so far.
+Returns 0 when the tidy has completed.
+The callback frequency and percentage completed are based on the number of entries tidied so far out
+of the total number of entreis that match the tidy filter and date range. But as the iterator may be
+position half-way through the entries for a day when a callback is due to be made then its necessary
+to continue processing till the end of that day before the callback is made - this is so that next time
+round the same entries aren't processed twice.
+
+@capability WriteUserData
+@capability ReadUserData
+*/
+TInt CCalAsyncDelete::DoDeleteStepL()
+	{
+	iTidyIter->GotoL(iTidyDate);	
+
+	TInt pos = 0;
+	TKeyArrayFix alreadyTidiedKey(0, ECmpTUint32);
+	while(CheckDateIteratorEnd())
+		{
+		const CAgnSimpleEntry& simpleEntry = iTidyIter->CurrentElement();
+
+		if (IsEntryValidToTidyL(simpleEntry))
+			{
+			TBool alreadyTidied = EFalse;
+			
+			// If the entry is repeating or if it spans more than one instant then the iterator may return the
+			// same entry more than once (due to the fact that the call backs interrupt the sequence and the
+			// iterator is reset on each chunk of processing) so add then to an array to prevent this
+			if ( simpleEntry.RptDef() || simpleEntry.StartTime() != simpleEntry.EndTime())
+				{
+				if (iAlreadyTidied->Find(simpleEntry.EntryId(), alreadyTidiedKey, pos) == 0)
+					{
+					alreadyTidied = ETrue;
+					}
+				else
+					{
+					iAlreadyTidied->AppendL(simpleEntry.EntryId());
+					}
+				}
+			
+			if ( ! alreadyTidied)
+				{
+				TidyEntryL(simpleEntry);
+				
+				if ((++iNumEntriesProcessed % KTidyCallbackRate)==0)
+					{
+					DeleteTidiedEntriesL();
+					
+					TInt percentage = (iNumEntriesProcessed * KAgnPercentageComplete) / iNumEntriesToProcess;
+					return (percentage < 1 ? 1 : percentage);
+					}
+
+				__ASSERT_DEBUG(iNumEntriesProcessed <= iNumEntriesToProcess, Panic(EAgmErrCorruptDelete));
+				}
+			}
+
+		iTidyIter->NextL();	
+		}
+	
+	DeleteTidiedEntriesL();
+	return KAgnPercentageComplete;
+	}
+
+/**
+Delete entries whose id's have been placed in iTidyDeleteArray during the tidying process
+@capability WriteUserData
+@capability ReadUserData
+*/
+void CCalAsyncDelete::DeleteTidiedEntriesL()
+	{
+	if (iTidyDeleteArray && iTidyDeleteArray->Count())
+		{
+		// sort the entries in the tidy array by their entry ID (and therefore by their stream)
+		// this ensures entries in the same stream will be deleted together
+		TStreamIdKey key;
+		iTidyDeleteArray->Sort(key);
+		
+		const TInt KDeleteArrayCount = iTidyDeleteArray->Count();
+		for (TInt ii = 0; ii < KDeleteArrayCount; ++ii)
+			{
+			CAgnEntry* entry = iModel.FetchEntryL((*iTidyDeleteArray)[ii]);
+			if (entry)
+				{
+				CleanupStack::PushL(entry);
+				iModel.DeleteEntryL(*entry, ETrue, NULL);
+				CleanupStack::PopAndDestroy(entry);
+				}
+			}
+			
+		iTidyDeleteArray->Reset();
+		iModel.FlushL();
+		}
+	}
+
+/**
+Tidies (i.e. deletes) the entry identified by aSimpleEntry from the agenda if its eligible according to 
+its dates. 
+If it is non-repeating then for it to be tidied its instance date must fall within iTidyStartDate and
+iTidyEndDate. 
+If it is repeating then both its repeat start and until dates must fall within the range. 
+If it is an undated todo then it would not have been selected by the iterator if today does not fall within the tidy
+range hence it can be added without further checks.
+*/
+void CCalAsyncDelete::TidyEntryL(const CAgnSimpleEntry& aSimpleEntry)
+	{
+	CAgnEntry* entry = iModel.FetchEntryL(aSimpleEntry.EntryId());
+	if (entry == NULL)
+		{
+		// already deleted
+		return;
+		}
+	
+	CleanupStack::PushL(entry);
+	
+	TBool todelete = EFalse;
+	if(entry->GsDataType() == CGsData::EParent)
+		{
+		// Only parents need to be tidied
+		todelete = FallInTimeRangeL(aSimpleEntry);
+		// If this Entry is a parent and it has children
+		if (todelete)
+			{
+			//Find out whether all children fall in the time range to tidy
+			const RArray<TGsChildRefData>& KChildIds = entry->ChildIds();
+		   
+			const TInt KCount = KChildIds.Count();
+			// Check each child to see if they are all fall in the date range
+			for (TInt i = 0; i < KCount && todelete; ++i)
+				{
+				const CAgnSimpleEntry* KChildEntry = iModel.GetSimpleEntryFromIndexes(KChildIds[i].ChildId());
+				todelete = FallInTimeRangeL(*KChildEntry);
+				}
+			}
+
+	#if defined (__CAL_ENTRY_LOGGING__) || (__CAL_VERBOSE_LOGGING__)
+		if(todelete)
+			{
+			AgmDebug::DebugLog("TidyEntryL: Async - Tidying - Deleting an entry");
+			AgmDebug::DebugLogEntryL(*entry,EDumpEntryIDs);
+			}
+	#endif
+		}
+	CleanupStack::PopAndDestroy(entry);
+	
+	if(todelete)
+		{
+		iTidyDeleteArray->AppendL(aSimpleEntry.EntryId());
+		if(aSimpleEntry.Type() == CCalEntry::ETodo)
+			{
+			iChangeFilter.SetPubSubChange(TAgnChangeFilter::ETodoChanged);
+			}
+		else
+			{
+			iChangeFilter.SetPubSubChange(TAgnChangeFilter::EEventChanged);
+			}
+		}
+	}
+
+TBool CCalAsyncDelete::FallInTimeRangeL(const CAgnSimpleEntry& aSimpleEntry)
+	{
+	if (aSimpleEntry.RptDef())
+		{ 
+		TTime firstUnexceptedInstanceDate;
+		TTime lastUnexceptedInstanceDate;
+		if(!aSimpleEntry.RptDef()->NudgeNextInstanceL(aSimpleEntry.RptDef()->FirstInstanceL().LocalL(), firstUnexceptedInstanceDate, ETrue))
+			{
+			firstUnexceptedInstanceDate = aSimpleEntry.RptDef()->FirstInstanceL().LocalL(); //we don't want the last date - we want the first date
+			}
+		if(!aSimpleEntry.RptDef()->NudgePreviousUnexceptedInstanceL(aSimpleEntry.RptDef()->LastInstanceL().LocalL(), lastUnexceptedInstanceDate))
+			{
+			lastUnexceptedInstanceDate = aSimpleEntry.RptDef()->LastInstanceL().LocalL(); // we don't want the first date - we want the last date
+			}
+		if ((firstUnexceptedInstanceDate >= iTidyStartDate) && (lastUnexceptedInstanceDate <= iTidyEndDate))
+
+			{
+			return ETrue;
+			}
+		}
+	else
+		{
+		if (aSimpleEntry.Type()==CCalEntry::ETodo)
+			{
+			// check due date only for Todos
+			if (aSimpleEntry.EndTime().LocalL() <= iTidyEndDate && aSimpleEntry.EndTime().LocalL() >= iTidyStartDate)
+				{
+				return ETrue;
+				}
+			}
+		else
+			{
+			if (aSimpleEntry.EndTime().LocalL() <= iTidyEndDate && aSimpleEntry.StartTime().LocalL() >= iTidyStartDate)
+				{
+				return ETrue;
+				}
+			}
+		}
+
+	return EFalse;
+	}