pimappservices/calendarvcalplugin/src/agnversit.cpp
author Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
Tue, 02 Feb 2010 10:12:19 +0200
changeset 0 f979ecb2b13e
permissions -rw-r--r--
Revision: 201003 Kit: 201005

// 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 "agnversit.h"
#include <vtzrules.h>
#include <ecom/implementationproxy.h>
#include <calrrule.h>
#include <calentry.h>
#include <caldataformat.h>

/**
@internalComponent
*/
template<class T> void CalCleanup<T>::PushResetAndDestroyL(T& aRef)
	{
	CleanupStack::PushL(TCleanupItem(ResetAndDestroy, &aRef));
	}
	
/**
@internalComponent
*/
template<class T> void CalCleanup<T>::PushResetL(T& aRef)
	{
	CleanupStack::PushL(TCleanupItem(Reset, &aRef));
	}

/**
@internalComponent
*/	
template <class T> void CalCleanup<T>::ResetAndDestroy(TAny* aPtr)
	{
	T* self = static_cast<T*>(aPtr);
	if (self)
		{
		self->ResetAndDestroy();
		}
	}
	
/**
@internalComponent
*/	
template <class T> void CalCleanup<T>::Reset(TAny* aPtr)
	{
	T self = static_cast<T>(aPtr);
	if (self)
		{
		self->Reset();
		}
	}
	
/** Global function for ease of use of CalCleanup template class
@internalComponent
*/
template<class T> void CleanupResetAndDestroyPushL(T& aRef)
	{
	CalCleanup<T>::PushResetAndDestroyL(aRef);
	}
	
/** Global function for ease of use of CalCleanup template class
@internalComponent
*/
template<class T> void CleanupResetPushL(T& aRef)
	{
	CalCleanup<T>::PushResetL(aRef);
	}


/**
 * Imports a <code>vCalendar</code> consisting of one or more
 * <code>vCalendar</code> entities.
 *
 * The <code>vCalendar</code> is read from the read stream specified and
 * its entities are converted into EPOC agenda entries and returned in
 * the array <code>aEntryArray</code>.
 *
 * @param     "RReadStream& aReadStream"
 *            The read stream which contains the
 *            <code>vCalendar</code>.
 * @param     "CArrayPtr<CCalEntry>* aEntryArray"
 *            On return, a list of the agenda entries which were
 *            imported from the <code>vCalendar</code>. The list
 *            contains the <code>vEvents</code> followed by the
 *            <code>vTodos</code>.
 */
void CAgnVCalConverter::ImportVCalL(TInt aFlags, RReadStream& aReadStream, RPointerArray<CCalEntry>& aEntryArray)

	{
	// Create a new converter object
	CVCalToAgendaEntryConverter* converter = new(ELeave)CVCalToAgendaEntryConverter();
	CleanupStack::PushL(converter);

	// Create a parser to internalize the entries
	CParserVCal* vCal = CParserVCal::NewL();
	CleanupStack::PushL(vCal);

	// Japan uses SHIFT-JIS as the default charset contrary to 
	// the vCalendar Specification which says that the default 
	// charset should be US-ASCII. 
	// In order to provide interoperability with this market
	// segment, allow a flag to override the default charset in versit.
	if(aFlags & KCalDataExchangeDefaultShiftJIS)
	    {
	    vCal->SetDefaultCharSet(Versit::EShiftJISCharSet);
	    vCal->SetFlags(CVersitParser::EUseDefaultCharSetForAllProperties);
	    }
	vCal->InternalizeL(aReadStream);

	
	// Set the time zone converter.  iTzConverter may be NULL if the optional
	// time zone conversion service is not available.  In this case all imported times 
	// will be exported as local time.
	if (iTzConnected)
		{
		converter->SetTzConverter(&iTzServer);
		}
	
	if (aFlags & KCalDataExchangeImportStatusAsVCalendar)
		{
		converter->SetImportVCalendarValues(ETrue);
		}
	else
		{
		converter->SetImportVCalendarValues(ETrue);
		}

	converter->SetTzRules(ImportTzRulesL(*vCal)); // converter takes ownership of rules collection.
	
	CArrayPtr<CVersitParser>* entities = vCal->ArrayOfEntities(EFalse); //doesn't take ownership
	if (entities)
		{
		TInt count = entities->Count();
		for (TInt i=0; i<count; i++)
			{
			CVersitParser& parser = *((*entities)[i]);			
			converter->ImportVCalL(parser, aEntryArray);
			} 
		}

	CleanupStack::PopAndDestroy(vCal);
	CleanupStack::PopAndDestroy(converter);
	}


/**
 * Exports a list of agenda entries as multiple vCalendar entities to the
 * write stream specified.
 *
 * The entries are exported either as vTodos, if they are to-dos,
 * otherwise as vEvents
 *
 * @param     "RWriteStream& aWriteStream"
 *            The stream to which the agenda entries should be
 *            externalised as vCalendar entities.
 * @param     "CArrayPtr<CCalEntry>* aEntryArray"
 *            Pointer to the array of agenda entries.
 * @param     "const Versit::TVersitCharSet aCharSet"
 *             The character encoding type
 */
void CAgnVCalConverter::ExportVCalL(RWriteStream& aWriteStream, const RPointerArray<CCalEntry>& aEntryArray, const Versit::TVersitCharSet aCharSet)

	{
	if(aEntryArray.Count()>0)
		{
		iCharSet = aCharSet;
		// Create a new converter object
		if(!iExportConverter)
			{
			iExportConverter = new(ELeave)CAgendaEntryToVCalConverter;
			}
		iEntryArray = const_cast<RPointerArray<CCalEntry>*>(&aEntryArray);
		// Set the time zone converter.  iTzConverter may be NULL if the optional
		// time zone conversion service is not available.  In this case all times will 
		// be exported as local time.
		if (iTzConnected)
			{
			iExportConverter->SetTzConverter(&iTzServer);
			}
			
		ExportEntryBySameTzL(aWriteStream);
		}
	}
	
void CAgnVCalConverter::ExportEntryBySameTzL(RWriteStream& aWriteStream)
	{//To export entries into a serveral vCal according to their tz rules
	if(!iSametzEntries)
		{
		iSametzEntries = new (ELeave) RPointerArray<CCalEntry>(4);
		}
		
	iSametzEntries->Reset();
	InitialiseSameTzEntryArrayL();
	//Find entries which belong to the first tz
	FindSameTzEntryL(*iSametzEntries);
	CParserVCal* vCal = NULL;
	//Expoet entries by grouping them into different tz rules 
	while(iSametzEntries->Count()>0)
		{//Pull entries with the same time zone in the same array and export them
		vCal = CParserVCal::NewL(); // takes ownership of entrylist
		CleanupStack::PushL(vCal);
		vCal->SetDefaultCharSet(iCharSet);
		ConvertEntryToVCalAndExportL(vCal, *iSametzEntries, aWriteStream);
		CleanupStack::PopAndDestroy(vCal);
		iSametzEntries->Reset();
		FindSameTzEntryL(*iSametzEntries);//this result in fill in iSametzEntries with same tz rule and remove the their indexes from iEntryIndexToExport
		}
	
	if(iEntryIndexToExport.Count()>0)
		{
		//Export the rest of entryies(those are entries without tz informations)
		ExportEntryWithoutTzRuleL(aWriteStream);
		}
	}
		
void CAgnVCalConverter::ExportEntryWithoutTzRuleL(RWriteStream& aWriteStream)
	{//This function only should be called when all entries in iEntryIndexToExport are either no-repeating or floating entry
	CParserVCal* vCal = CParserVCal::NewL(); // takes ownership of entrylist
	CleanupStack::PushL(vCal);
	vCal->SetDefaultCharSet(iCharSet);
	const TInt KCount = iEntryIndexToExport.Count();
	for (TInt ii = 0; ii < KCount; ++ii)
		{
		CCalEntry* entry = (*iEntryArray)[iEntryIndexToExport[ii]];
		if (entry)
			{
			TCalRRule rule;
			__ASSERT_DEBUG(!(entry->GetRRuleL(rule)) || entry->StartTimeL().TimeMode() == TCalTime::EFloating, User::Invariant());
			iExportConverter->ExportEntryL(entry, *vCal);
			}
		}
	vCal->ExternalizeL(aWriteStream);
	CleanupStack::PopAndDestroy(vCal);
	}

void CAgnVCalConverter::ConvertEntryToVCalAndExportL(CParserVCal* aVcal, const RPointerArray<CCalEntry>& aEntryArray, RWriteStream& aWriteStream)
	{
	iExportConverter->NextVCalendarL();
	TInt count = aEntryArray.Count();

	for (TInt ii = 0; ii < count; ii++)
		{
		CCalEntry* entry = aEntryArray[ii];
		if (entry)
			{
			iExportConverter->AddTimeZonePropertiesL(*aVcal, entry);
			iExportConverter->ExportEntryL(entry, *aVcal);
			}
		}
	
	aVcal->ExternalizeL(aWriteStream);
	}
	
void CAgnVCalConverter::ImportVCalAsyncL(TInt aFlags, RReadStream& aReadStream, 
		RPointerArray<CCalEntry>& aEntryArray, MCalDataExchangeCallBack& aObserver)
	{
	// Create a new converter object
	if(iAsyncState!=EIdle)
		{
		User::Leave(KErrInUse);
		}
		
	iImportConverter = new (ELeave) CVCalToAgendaEntryConverter();
	iVCalArray = new (ELeave) RPointerArray<CParserVCal>();

	// Read all the entries in the file. The parser will only read one.
	iTotalNumEntry = 0;
	TInt errcode = KErrNone;
	while (errcode != KErrEof)
   	 	{
 		// Create a parser to internalize the entries
		CParserVCal* vCal = CParserVCal::NewL();
		CleanupStack::PushL(vCal);
		// Japan uses SHIFT-JIS as the default charset contrary to 
		// the vCalendar Specification which says that the default 
		// charset should be US-ASCII. 
		// In order to provide interoperability with this market
		// segment, allow a flag to override the default charset in versit.
		if(aFlags & KCalDataExchangeDefaultShiftJIS)
		    {
		    vCal->SetDefaultCharSet(Versit::EShiftJISCharSet);
		    vCal->SetFlags(CVersitParser::EUseDefaultCharSetForAllProperties);
		    }
  	 	TRAP( errcode, vCal->InternalizeL(aReadStream) );
  	 	if (errcode ==KErrNone)
  	 		{
  	 		CArrayPtr<CVersitParser>* entities = vCal->ArrayOfEntities(EFalse); 
  	 		TInt numEntry = entities->Count();
	 		if(numEntry>0)
	 			{
	 			iVCalArray->AppendL(vCal);
	 			CleanupStack::Pop(vCal);
	 			iTotalNumEntry += numEntry;
	 			}
  	 		}
  	 	else if (errcode ==KErrEof)
  	 		{
  	 		CleanupStack::PopAndDestroy(vCal);
  	 		}
	  	else
	  	 	{
	  	 	User::LeaveIfError( errcode );
	  	 	}
     	}


	// Set the time zone converter.  iTzConverter may be NULL if the optional
	// time zone conversion service is not available.  In this case all imported times 
	// will be exported as local time.
	if (iTzConnected)
		{
		iImportConverter->SetTzConverter(&iTzServer);
		}

	iEntryArray = &aEntryArray;
	iTotalCounter = 0;
	iSameTzCounter = 0;
	iObserver = &aObserver;
	iAsyncState = EImporting;
	iMaxNumEntryInStep = iObserver->NumberOfEntriesToHandleAtOnce();
	if(iTotalNumEntry > 0)
		{
		CTzRules* tzRule = ImportTzRulesL(*((*iVCalArray)[0]));
		iImportConverter->SetTzRules(tzRule); // converter takes ownership of rules collection.
		}
	iImportExportActive->Start();
	}
	
void CAgnVCalConverter::FindSameTzEntryL(RPointerArray<CCalEntry>& aSameTzEntry)
	{
	//Fill in aSameTzEntry with those entries with the same tz rule in iEntryArray
	//Remove the index of the entry from iEntryIndexToExport
	//Note that the order of the entries being exported has to be maintained 
	CCalEntry* firstEntrywithTz = NULL;
	for (TInt ii = 0; ii < iEntryIndexToExport.Count();  ++ii)
		{
		TCalRRule repeatrule;//temp veraible
		CCalEntry* entry = (*iEntryArray)[iEntryIndexToExport[ii]];
		if(entry->GetRRuleL(repeatrule) && entry->StartTimeL().TimeMode() != TCalTime::EFloating)
			{//Otherwise time zone info is not needed to export
			if(firstEntrywithTz)
				{
				CTzRules* rules1 = entry->TzRulesL();
				CleanupStack::PushL(rules1);

				CTzRules* rules2 = firstEntrywithTz->TzRulesL();
				CleanupStack::PushL(rules2);
				
				if (rules1 && rules2 && rules1->IsEqualTo(*rules2))
					{
					//compare the time zone to see if it is the same as the "firstEntrywithTz"
					aSameTzEntry.AppendL(entry);
					iEntryIndexToExport.Remove(ii);
					--ii;
					}
				CleanupStack::PopAndDestroy(rules2);
				CleanupStack::PopAndDestroy(rules1);
				}
			else
				{
				firstEntrywithTz = entry;
				aSameTzEntry.AppendL(entry);
				iEntryIndexToExport.Remove(ii);
				--ii;
				}
			}
		}
	}
	
void CAgnVCalConverter::FindEntryWithoutTzRuleL(RPointerArray<CCalEntry>& aSameTzEntry)
	{
	// This functions should be only used for asynchronous exporting (use ExportEntryWithoutTzRuleL if it is for synchronous exporting)
	for (TInt ii = 0; ii< iEntryIndexToExport.Count();  ++ii)
		{//Pull those entries which do not need to export tz together (either non-repeating or floating entry)
		TCalRRule rule;
		CCalEntry* entry = (*iEntryArray)[iEntryIndexToExport[ii]];
		if(!entry->GetRRuleL(rule) || entry->StartTimeL().TimeMode() == TCalTime::EFloating)
			{
			aSameTzEntry.AppendL((*iEntryArray)[iEntryIndexToExport[ii]]);
			iEntryIndexToExport.Remove(ii);
			--ii;
			}
		}
	}

void CAgnVCalConverter::ExportVCalAsyncL(RWriteStream& aWriteStream, RPointerArray<CCalEntry>& aEntryArray, MCalDataExchangeCallBack& aObserver, const Versit::TVersitCharSet aCharSet)
	{
	if(iAsyncState!=EIdle)
		{
		User::Leave(KErrInUse);
		}

	iEntryArray = &aEntryArray;
		
	// Create a new converter object
	if(!iExportConverter)
		{
		iExportConverter = new(ELeave)CAgendaEntryToVCalConverter;
		}

	// Set the time zone converter.  iTzConverter may be NULL if the optional
	// time zone conversion service is not available.  In this case all times will 
	// be exported as local time.
	if (iTzConnected)
		{
		iExportConverter->SetTzConverter(&iTzServer);
		}
	
	if(!iSametzEntries)
		{
		iSametzEntries = new(ELeave) RPointerArray<CCalEntry>;
		}
	iCharSet = aCharSet;
	iSametzEntries->Reset();
	InitialiseSameTzEntryArrayL();

	iVCal = CParserVCal::NewL();
	iVCal->SetDefaultCharSet(iCharSet);
	
	iTotalNumEntry = iEntryArray->Count();
	iTotalCounter = 0;
	iSameTzCounter = 0;
	iObserver = &aObserver;
	iAsyncState = EExporting;
	iWriteStream = &aWriteStream;
	iMaxNumEntryInStep = iObserver->NumberOfEntriesToHandleAtOnce();
	
	FindSameTzEntryL(*iSametzEntries);
	if(iSametzEntries->Count()==0)
		{
		FindEntryWithoutTzRuleL(*iSametzEntries);
		__ASSERT_DEBUG(iEntryIndexToExport.Count()==0, User::Invariant());
		}
	iExportConverter->NextVCalendarL();
	iImportExportActive->Start();
	}
	
void CAgnVCalConverter::InitialiseSameTzEntryArrayL()
	{// Initialise the the array with the indexes of each element in iEntryArray
	iEntryIndexToExport.Reset();
	const TInt KCount = iEntryArray->Count();
	for (TInt ii = 0; ii<KCount; ++ii)
		{
		iEntryIndexToExport.AppendL(ii);
		}
	}
	
void CCalImportExportActive::RunL()
	{
	switch (iConverter.AsyncState())
		{
		case CAgnVCalConverter::EImporting:
			iConverter.NextImportStepL();
			break;
		case CAgnVCalConverter::EExporting:
			iConverter.NextExportStepL();
			break;
		default:
			case CAgnVCalConverter::EIdle:
			Panic(EAgnVersitPanicImportExportFailure);
			break;
		}
	}
	
CAgnVCalConverter::TState CAgnVCalConverter::AsyncState() const 
	{
	return iAsyncState;
	}
	
void CAgnVCalConverter::CheckCompletion()
	{
	if (iTotalCounter == iTotalNumEntry)
		{
		iObserver->Completed();
		CancelImportExport();
		}
	else
		{
		iObserver->Progress((iTotalCounter * 100) / iTotalNumEntry);
		iImportExportActive->Start();
		}
	}
	
void CAgnVCalConverter::NextImportStepL()
	{
	if(iTotalNumEntry>0)
		{
		CArrayPtr<CVersitParser>* entities = (*iVCalArray)[0]->ArrayOfEntities(EFalse); //doesn't take ownership
		TInt numSameTzEntry = entities->Count();
		TInt count = ((numSameTzEntry - iSameTzCounter) > iMaxNumEntryInStep)?iMaxNumEntryInStep:(numSameTzEntry - iSameTzCounter); 
		RPointerArray<CCalEntry> importedEntries;
		CleanupResetAndDestroyPushL(importedEntries);

		for (TInt i = 0; i < count; i++)
			{
			CVersitParser& parser = *((*entities)[i + iSameTzCounter ]);
			iImportConverter->ImportVCalL(parser, importedEntries);
			for (TInt j = 0; j < importedEntries.Count(); j++)
				{
				iEntryArray->AppendL((importedEntries)[j]);
				}
			importedEntries.Reset();
			}
			
		CleanupStack::PopAndDestroy();//importedEntries
		iTotalCounter  += count;
		iSameTzCounter += count;

		if(iSameTzCounter == numSameTzEntry)
			{//Convert the next vCal into entries
			delete (*iVCalArray)[0];
			iVCalArray->Remove(0);
		
			if(iVCalArray->Count()>0)
				{
				CTzRules* tzRule = ImportTzRulesL(*((*iVCalArray)[0]));
				iImportConverter->SetTzRules(tzRule); // converter takes ownership of rules collection.
				iSameTzCounter = 0;
				}
			}
		}
		
	CheckCompletion();
	}
	
void CAgnVCalConverter::NextExportStepL()
	{
	if(iTotalNumEntry>0)
		{
		TInt numSameTzEntry = iSametzEntries->Count();
		//decide how many entries to export in this step
		TInt count = ((numSameTzEntry - iSameTzCounter) > iMaxNumEntryInStep)?iMaxNumEntryInStep:(numSameTzEntry - iSameTzCounter); 
		for (TInt i = 0; i < count; i++)
			{
			CCalEntry* entry = (*iSametzEntries)[i + iSameTzCounter];
			if (entry)
				{
				iExportConverter->AddTimeZonePropertiesL(*iVCal, entry);
				iExportConverter->ExportEntryL(entry, *iVCal);
				}
			}
			
		iTotalCounter += count;
		iSameTzCounter += count;

		if (iSameTzCounter == numSameTzEntry)
			{//Add time zone property 
			iExportConverter->NextVCalendarL();
			//calling AddTimeZone Properties for all entries
			//iExportConverter->AddTimeZonePropertiesL(*iVCal, (*iSametzEntries)[0]);
			iVCal->ExternalizeL(*iWriteStream);
			delete iVCal;
			iVCal = NULL;
			//Find out next group entries which are in the same time zone
			if(iEntryIndexToExport.Count()>0)
				{
				iVCal = CParserVCal::NewL();
				iVCal->SetDefaultCharSet(iCharSet);
				iSametzEntries->Reset();
				FindSameTzEntryL(*iSametzEntries);
				if(iSametzEntries->Count()==0)
					{
					FindEntryWithoutTzRuleL(*iSametzEntries);
					}
				iSameTzCounter = 0;
				}
			}
		}
	CheckCompletion();
	}

void CAgnVCalConverter::CancelImportExport(TInt aError)
/**
 * This function destroys import/export convertor objects and resets async state.
 * @param "TInt aError"
 *        Error during operation passed to the observer.
 */
	{
	// Propagate in case of error
	if(aError != KErrNone)
		{
		iObserver->Progress(aError);
		iObserver->Completed();
		}

	delete iImportConverter;
	iImportConverter = NULL;
	delete iExportConverter;
	iExportConverter = NULL;
	delete iVCal;
	iVCal = NULL;
	if(iVCalArray)
		{
		iVCalArray->ResetAndDestroy();
		}
	delete iVCalArray;
	iVCalArray = NULL;
	iAsyncState = EIdle;
	}
/**
 * Provides indication of validity of Daylight property during import
 *
 * At the moment, this means the following are all true:
 *    1) start year is not earlier than 1900
 *    2) end year is not later than 2100
 *    3) end year is not earlier than start year
 *
 * @param aDaylightProperty The property to be validated.
 *
 * @return ETrue if aDaylightProperty satisfies above conditions. EFalse otherwise.
 */
TBool CAgnVCalConverter::ValidDaylightProperty(CParserProperty& aDaylightProperty) const
    {		
    CParserPropertyValue* propertyValue = aDaylightProperty.Value();
 
    if (propertyValue)
       {
       CVersitDaylight* checkProperty = static_cast<CParserPropertyValueDaylight*>(propertyValue)->Value();

       if (checkProperty && checkProperty->iSavings) 
          {
          TInt startYear = checkProperty->iStartTime->iDateTime.Year();
          TInt endYear = checkProperty->iEndTime->iDateTime.Year();
          TTime minDateTime = TCalTime::MinTime();
          TTime maxDateTime = TCalTime::MaxTime();
          
          return ( (startYear	>= minDateTime.DateTime().Year()) &&
                   (endYear 	<= maxDateTime.DateTime().Year()) &&
                   (endYear 	>= startYear) );
          }
       }
 
    return EFalse;
    }

/**
 * Creates a collection of rules that describes a set UTC offsets and when they 
 * should be applied to local times within entities observed by the "home" 
 * system that created the vCalendar object.
 *
 * @param aCalParser A handle to the vCalendar parser.
 *
 * @return The time zone rules collection, NULL if no rules can be created.
 */
CTzRules* CAgnVCalConverter::ImportTzRulesL(CParserVCal& aCalParser) const
	{
    TBool acceptable = EFalse;
	TTimeIntervalMinutes stdOffset = StandardTimeOffsetL(aCalParser);

	// access properties but don't take ownership
	CArrayPtr<CParserProperty>* daylightProperties = 
    aCalParser.PropertyL(KVersitTokenDAYLIGHT, TUid::Uid(KVersitPropertyDaylightUid), EFalse);

    if (daylightProperties)
       {
       // check for an acceptable daylight property 
       // if there are none, treat this vCal as if it has no daylight properties at all
       const TInt KCount = daylightProperties->Count();
       for (TInt i = 0; i < KCount && ! acceptable; i++) 
           {	
           if (ValidDaylightProperty(*daylightProperties->At(i)))	
       		  {
              acceptable = ETrue;
              }
           }

       delete daylightProperties; 
       daylightProperties = NULL;
       }
        
    // Need either a TZ property or at least one acceptable DAYLIGHT property         
    // before TZ rules can be generated

	if ((stdOffset.Int() == KMaxTInt) && acceptable) 
	   {
       // no TZ property supplied, but a DAYLIGHT property is useable   
       // set initial TZ offset to 0
       stdOffset = 0;
       }

	CTzRules* rules = NULL;
	// if TZ offset is set, we have something to process
	if (stdOffset.Int() != KMaxTInt)
		{
		rules = DaylightRulesL(aCalParser, stdOffset);
		rules->SetInitialStdTimeOffset(stdOffset.Int());
		}
	return rules;
	}

static TInt CompareDaylightProperties(const CParserProperty& aLeftProperty, const CParserProperty& aRightProperty)
	{
	CParserPropertyValueDaylight* leftValue = static_cast<CParserPropertyValueDaylight*>(aLeftProperty.Value());
	CParserPropertyValueDaylight* rightValue = static_cast<CParserPropertyValueDaylight*>(aRightProperty.Value());

	if (leftValue->Value()->iStartTimeSortKey >= rightValue->Value()->iStartTimeSortKey)
		return 1;
	return -1;
	}

/**
 * Creates a collection of rules that describes a set of UTC offsets and when they 
 * should be applied to local times within entities observed by the "home" 
 * system that created the vCalendar object.
 *
 * @param aCalParser A handle to the vCalendar parser.
 * @param aStdOffset The standard time UTC offset.  Specify TTimeIntervalMinutes(KMaxTInt) if not known.
 * @return The time zone rules collection, NULL if no rules can be created.
 */
CTzRules* CAgnVCalConverter::DaylightRulesL(CParserVCal& aCalParser, TTimeIntervalMinutes aStdOffset) const
	{
	__ASSERT_DEBUG(aStdOffset.Int() != KMaxTInt, User::Invariant());
	
	CTzRules* rules = CTzRules::NewL(0, KMaxTUint);
	CleanupStack::PushL(rules);

	// get properties but don't take ownership of the elements of the array
	CArrayPtr<CParserProperty>* daylightProperties = aCalParser.PropertyL(KVersitTokenDAYLIGHT, TUid::Uid(KVersitPropertyDaylightUid), EFalse);
	
	TTime nextRuleStart = Time::MinTTime();	// start of the next std rule
	TTzTimeReference nextRuleTimeRef = ETzUtcTimeReference;

	TInt  prevRuleOffset = aStdOffset.Int();

	if (daylightProperties != NULL)
		{
		CleanupStack::PushL(daylightProperties);
		// re-order the daylight properties
		TLinearOrder<CParserProperty> daylightPropertySort(CompareDaylightProperties);
		RPointerArray<CParserProperty> newDaylightPropArray;
		TInt i;
		for (i = 0; i < daylightProperties->Count(); i++)
			{
            // insert only valid imported properties 
            if (ValidDaylightProperty(*daylightProperties->At(i)))
                {
  			    TInt err = newDaylightPropArray.InsertInOrder(daylightProperties->At(i), daylightPropertySort);
  			    if (err != KErrAlreadyExists)
  			    	{
  			    	User::LeaveIfError(err);
  			    	}
                }
			}
		CleanupStack::PopAndDestroy(daylightProperties);

		CVersitDaylight* daylightProperty = NULL;
		TInt n = newDaylightPropArray.Count();
		
		// For each DAYLIGHT property, create a standard time rule prior to the
		// daylight saving time period, and then create the daylight savings rule
		// following on from the standard time period.
		for (i = 0; i < n; ++i)	
			{
			daylightProperty = static_cast<CParserPropertyValueDaylight*>(newDaylightPropArray[i]->Value())->Value();
			
			if (daylightProperty && daylightProperty->iSavings)
				{
				TTime dstStartTime(daylightProperty->iStartTime->iDateTime);
				TTime dstEndTime(daylightProperty->iEndTime->iDateTime);
				TInt  dstOffset = daylightProperty->iOffset.Int() / 60; // Convert seconds to minutes.
				
				// standard time rule (STD)
				TTzRule stdRule = TzRuleL(prevRuleOffset, 	// old offset, minutes
							    		  aStdOffset, 		// new offset (=STD offset), minutes
										  nextRuleStart,	// from the end of prev rule
								dstStartTime-TTimeIntervalSeconds(1));	// to DST start time less 1sec
																		// (e.g. 01:59:59)
				// daylight saving time rule (DST)
				TTzRule dstRule = TzRuleL(aStdOffset,		// old offset (=STD offset), minutes
							    		  dstOffset,		// new offset (=DST offset), minutes
							    		  dstStartTime, 	// start
										  dstEndTime); 		// end time
				
				prevRuleOffset = dstOffset;				

				// 01:59:00->02:00:00; 01:59:59->02:00:00; 02:00:00->02:00:00; 01:29:59->01:30:00
				nextRuleStart = dstEndTime;
				nextRuleStart.RoundUpToNextMinute();
				if (nextRuleStart.DateTime().Minute()%10==9) nextRuleStart+=TTimeIntervalMinutes(1);

				// time references
				stdRule.iFrom.iTimeReference = nextRuleTimeRef;
				stdRule.iTimeReference = nextRuleTimeRef;
				if (daylightProperty->iStartTime->iRelativeTime!=TVersitDateTime::EIsUTC)
					{
					stdRule.iTo.iTimeReference = ETzWallTimeReference;
					dstRule.iFrom.iTimeReference = ETzWallTimeReference;
					dstRule.iTimeReference = ETzWallTimeReference;
					}
				if (daylightProperty->iEndTime->iRelativeTime!=TVersitDateTime::EIsUTC)
					{
					nextRuleTimeRef = ETzWallTimeReference;
					dstRule.iTo.iTimeReference = ETzWallTimeReference;
					}
				else
					{
					nextRuleTimeRef = ETzUtcTimeReference;
					}
			
				rules->AddRuleL(stdRule);	// add STD rule
				rules->AddRuleL(dstRule);	// add DST rule
				}
			}
		newDaylightPropArray.Reset();
		}
	
	// Create a standard time rule after the last daylight saving period.
	TTzRule lastStdRule = TzRuleL(prevRuleOffset, aStdOffset, nextRuleStart, Time::MaxTTime());
	lastStdRule.iFrom.iTimeReference = nextRuleTimeRef;
	lastStdRule.iTimeReference = nextRuleTimeRef;	
	rules->AddRuleL(lastStdRule);
	CleanupStack::Pop(rules);

	return rules;
	}
	

/**
 * Creates a rule that stores the UTC offset and when it should be
 * applied to local times within entities specified by the "home" 
 * system that created the vCalendar object.
 *
 * @param aOldTzOffset The UTC offset (minutes since midnight) before the rule applies.
 * @param aNewTzOffset The UTC offset (minutes since midnight) when the rule applies.
 * @param aStartTime   The time from when the rule applies.
 * @param aEndTime     The time to when the rule applies.
 */
TTzRule CAgnVCalConverter::TzRuleL(TTimeIntervalMinutes aOldTzOffset, TTimeIntervalMinutes aNewTzOffset, TTime aStartTime, TTime aEndTime) const
	{
	TTzRule rule;

	// There needs to be better boundary checking when converting from TTime to TUInt...
	// Ideally, the rule should accept TTimes.
	
	if (aStartTime != Time::MinTTime())
		{
		rule.iFrom.iTime = aStartTime;
		// rule.iFrom.iTimeReference defaults to ETzUtcTimeReference	
		rule.iMonth = aStartTime.DateTime().Month();
		rule.iDayOfMonth = aStartTime.DateTime().Day();
		rule.iTimeOfChange = aStartTime.DateTime().Hour() * 60 + aStartTime.DateTime().Minute();
		}
	
	if (aEndTime == Time::MaxTTime())
		{
		rule.iTo = TTimeWithReference::Max();
		// rule.iTo.iTimeReference defaults to ETzUtcTimeReference
		}
	else
		{
		rule.iTo = aEndTime;
		// rule.iTo.iTimeReference defaults to ETzUtcTimeReference
		}

	rule.iOldLocalTimeOffset = aOldTzOffset.Int();
	rule.iNewLocalTimeOffset = aNewTzOffset.Int();
	rule.iDayRule = ETzFixedDate;
	rule.iTimeReference = ETzUtcTimeReference;

	return rule;
	}


/**
 * The TZ property represents the standard time zone of the 
 * "home" system that created the vCalendar object.  If there are
 * more than one TZ properties, then only the first TZ property value
 * is returned.
 * 
 * @param  aCalParser A handle to the vCalendar parser.
 * @return TTimeIntervalMinutes(KMaxTInt) if there is no TZ property, TZ property value otherwise.
 */
TTimeIntervalMinutes CAgnVCalConverter::StandardTimeOffsetL(CParserVCal& aCalParser) const
	{
	// get properties but don't take ownership of the elements of the array
	CArrayPtr<CParserProperty>* tzCollection = aCalParser.PropertyL(KVersitTokenTZ, TUid::Uid(KVersitPropertyTimeZoneUid), EFalse);

	if (tzCollection == NULL)
		return TTimeIntervalMinutes(KMaxTInt);
	
	CleanupStack::PushL(tzCollection);
	TTimeIntervalSeconds stdTime = static_cast<CParserPropertyValueTimeZone*>((*tzCollection)[0]->Value())->Value();
	CleanupStack::PopAndDestroy(tzCollection);

	return TTimeIntervalMinutes(stdTime.Int() / 60);
	}
	
/**  
 * This function destroys the TAgnVCalConverter object created by the gate function,
 * and the CTzConverter object if it exists
 */
CAgnVCalConverter::~CAgnVCalConverter()
	{	
	delete iImportConverter;
	
	if (iVCalArray)
		{
		iVCalArray->ResetAndDestroy();
		}
	delete iVCalArray;
	
	delete iImportExportActive;
	delete iExportConverter;

	iTzServer.Close();
	if(iSametzEntries)
		{
		iSametzEntries->Close();
		delete iSametzEntries;
		}
		
	iEntryIndexToExport.Close();
	}

/**
 * Standard 2-phase construction
 */
CAgnVCalConverter* CAgnVCalConverter::NewL()
   	{
   	CAgnVCalConverter* self = new (ELeave) CAgnVCalConverter;
   	CleanupStack::PushL(self);
   	self->ConstructL();
   	CleanupStack::Pop(self);
   	return self;
   	}
  


// Export the implementation collection function
const TImplementationProxy ImplementationTable[] = 
    {
    IMPLEMENTATION_PROXY_ENTRY(0x102035F6, CAgnVCalConverter::NewL)
    };

EXPORT_C const TImplementationProxy* ImplementationGroupProxy(TInt& aTableCount)
    {
    aTableCount = sizeof(ImplementationTable) / sizeof(TImplementationProxy);
    return ImplementationTable;
    }
  


void CAgnVCalConverter::ConstructL()
	{
	iTzConnected = EFalse;
	// Timezone conversion library is not guaranteed to be available.
	// If it is not available, then AgnVersit will ignore DST rules
	// and default to its previous behaviour
	const TInt result = iTzServer.Connect();
	if (result == KErrNone)
		{
		iTzConnected = ETrue;
		}
	else if (result != KErrNotFound)
		{
		User::Leave(result);
		}
	iImportExportActive = new (ELeave) CCalImportExportActive(*this);
	iAsyncState = EIdle;
	}
	
/**
 * Panic the thread with AGNVERSIT as the category
 * 
 * @param aPanic Panic number
 */
void Panic(TAgnVersitPanic aPanic)
	{
	_LIT(KAgnVersitPanicCategory,"AGNVERSIT");
	User::Panic(KAgnVersitPanicCategory,aPanic);
	}

/**
 * Standard Epoc32 Dll Entry point
 */


// Active object to control asynchronous import / export
CCalImportExportActive::CCalImportExportActive(CAgnVCalConverter& aConverter) :
	CActive(EPriorityLow), iConverter(aConverter)
	{
	CActiveScheduler::Add(this);
	}

CCalImportExportActive::~CCalImportExportActive()
	{
	Cancel();
	}
	
void CCalImportExportActive::Start()
	{
	if (!IsActive())
		{
		TRequestStatus* status = &iStatus;
		User::RequestComplete(status, KErrNone);
		SetActive();
		}
	}


void CCalImportExportActive::DoCancel()
	{
	iConverter.CancelImportExport();
	}

TInt CCalImportExportActive::RunError(TInt aError)
	{
	// Propogate error		
	iConverter.CancelImportExport(aError);
	return KErrNone;
	}