diff -r 000000000000 -r f979ecb2b13e pimappservices/calendar/server/src/agsasyncdelete.cpp --- /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(At(aLeft))->StreamId().Value(); + TUint32 right = static_cast(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(KTidyArrayGranularity); + iTidyDeleteArray = new(ELeave)CArrayFixFlat(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 tidyEndDateTimeBuf; + AgmDebug::TTimeStrL(iTidyEndDate, tidyEndDateTimeBuf); + TBuf 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& 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; + }