omads/omadsextensions/dsutils/emailxmlutils/src/NSmlEmailParser.cpp
author Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
Mon, 23 Nov 2009 14:46:41 +0200
changeset 0 dab8a81a92de
permissions -rw-r--r--
Revision: 200948

/*
* Copyright (c) 2004 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:  Sources
*
*/



// INCLUDE FILES
#include <s32buf.h>
#include <s32file.h>

#include "nsmlxmlparser.h"


// ============================ MEMBER FUNCTIONS ===============================

// -----------------------------------------------------------------------------
// CNSmlEmailParser::NewL
// Two-phased constructor.
// -----------------------------------------------------------------------------
//
EXPORT_C CNSmlEmailParser* CNSmlEmailParser::NewL()
	{
	CNSmlEmailParser* self = CNSmlEmailParser::NewLC();
	CleanupStack::Pop();

	return self;
	}

// -----------------------------------------------------------------------------
// CNSmlEmailParser::NewLC
// Two-phased constructor.
// -----------------------------------------------------------------------------
//
EXPORT_C CNSmlEmailParser* CNSmlEmailParser::NewLC()
	{
	CNSmlEmailParser* self = new (ELeave) CNSmlEmailParser();
	CleanupStack::PushL(self);
	self->ConstructL();

	return self;
	}

// -----------------------------------------------------------------------------
// CNSmlEmailParser::~CNSmlEmailParser
// Destructor
// -----------------------------------------------------------------------------
//
EXPORT_C CNSmlEmailParser::~CNSmlEmailParser()
	{
	if( iTruncated )
		delete iTruncated;

	if( iExt ) 
		{
		iExt->ResetAndDestroy();
		delete iExt;
		}

	if( iEmailItem ) delete iEmailItem;

	if( iTruncType ) delete iTruncType;
	if( iTruncName ) delete iTruncName;
	if( iExtData ) delete iExtData;

	}

// -----------------------------------------------------------------------------
// CNSmlEmailParser::ParseXml
// Parses through the given XML and puts the data it contains 
// to member variables. 
// Note: The string-values in CNSmlEmailParser are NOT copies, but 
// references to aXml. The method does not check, if member variables 
// already contain data, but just writes it (if some of them are already 
// instantiated dynamically, they should be deleted before this method is called).
// -----------------------------------------------------------------------------
//
EXPORT_C TNSmlParserGeneratorError CNSmlEmailParser::ParseXml( HBufC8* aXml )
	{

	// pre-process the data (remove cdata over <Data>)
	TRAPD( cdataErr, PreProcessL(aXml));
	if (cdataErr != EErrorNone)
		return (TNSmlParserGeneratorError)cdataErr;

	// reset state
	iSetValues.Reset();
	iCurrentState = ENone;
	iLastState = ENone;
	TPtrC8 ptr(*aXml);

	// parse
	TRAPD(err, ParseL( ptr ));

	// return error, if in wrong state
	if( err == EErrorNone && iCurrentState != ENone )
		return EInvalidXmlError;

	return CheckError(err);
	}

// -----------------------------------------------------------------------------
// CNSmlEmailParser::GenerateXml
// Generates Email-XML using the data in member variables. aXml contains the 
// generated xml when the function returns successfully. Note: caller should not 
// instantiate the HBufC8, since this method first counts the size of the xml to
// be generated and then instatiates the HBufC8 with a correct maximum size. If
// the given buffer is not NULL, it is destroyed and a new one is instantiated.
// However, the caller gets the control of the HBufC8 when the function returns.
// -----------------------------------------------------------------------------
//
EXPORT_C TNSmlParserGeneratorError CNSmlEmailParser::GenerateXml( HBufC8*& aXml ) 
	{

	TRAPD( err, GenerateEmailXmlL( aXml ) );

	return CheckError(err);
	}

// -----------------------------------------------------------------------------
// CNSmlEmailParser::GenerateEmailXmlL
// Private method that does the actual email xml generation
// -----------------------------------------------------------------------------
//
void CNSmlEmailParser::GenerateEmailXmlL( HBufC8*& aXml )
	{
	ConvertIntoEntitiesL();

	// count the size of the xml
	TInt size = CountXmlSizeL();

	// create buffer for the xml

	if ( aXml )
		{
		delete aXml;
		aXml = NULL;
		}

	aXml = HBufC8::NewLC(size);
	TPtr8 ptr = aXml->Des();

	// append data to buffer
	AppendElement(ptr, KEmailElement());

	if ( iRead )
		{
		AppendElement(ptr, KEmailReadElement(), BooleanToString( iRead ));
		}

	if ( iForwarded )
		{
		AppendElement(ptr, KEmailForwardedElement(), BooleanToString( iForwarded ));
		}

	if ( iReplied )
		{
		AppendElement(ptr, KEmailRepliedElement(), BooleanToString( iReplied ));
		}

	if (iReceived != Time::NullTTime())
		{
		AppendElement(ptr, KEmailReceivedElement(), DateTimeToStringL(iReceived));
		}

	if (iCreated != Time::NullTTime())
		{
		AppendElement(ptr, KEmailCreatedElement(), DateTimeToStringL(iCreated));
		}

	if (iModified != Time::NullTTime())
		{
		AppendElement(ptr, KEmailModifiedElement(), DateTimeToStringL(iModified));
		}

	if ( iDeleted )
		{
		AppendElement(ptr, KEmailDeletedElement(), BooleanToString( iDeleted ));
		}

	if ( iFlagged )
		{
		AppendElement(ptr, KEmailFlaggedElement(), BooleanToString( iFlagged ));
		}

	if ( iEmailItem )
		{
		AppendElement(ptr, KEmailItemElement());
		ptr.Append(KCDataStart);
		ptr.Append( *iEmailItem );
		ptr.Append(KCDataEnd);
		AppendEndElement(ptr, KEmailItemElement());
		}

	if ( iTruncated )
		{
		iTruncated->GenerateXmlL(ptr, this);
		}

	if ( iExt )
		{
		for (TInt i=0; i < iExt->Count(); ++i)
			{
			iExt->At(i)->GenerateXmlL(ptr, this);
			}

		}


	AppendEndElement(ptr, KEmailElement());

	CleanupStack::Pop(); // aXml

	}

// -----------------------------------------------------------------------------
// CNSmlEmailParser::CountXmlSizeL
// Counts how long the xml string will be if it was generated using 
// the values currently in member variables.
// -----------------------------------------------------------------------------
//
EXPORT_C TInt CNSmlEmailParser::CountXmlSizeL()
	{
	TInt size = 0;

	size += KCDataStart().Length() + KCDataEnd().Length();
	size += SizeOfElements( KEmailElement() );

	if ( iRead )
		{
		size += SizeOfBoolean( iRead, KEmailReadElement() );
		}

	if ( iForwarded )
		{
		size += SizeOfBoolean( iForwarded, KEmailForwardedElement() );
		}

	if ( iReplied )
		{
		size += SizeOfBoolean( iReplied, KEmailRepliedElement() );
		}

	if (iReceived != Time::NullTTime())
		{
		size += SizeOfDatetime( KEmailReceivedElement() );
		}

	if (iCreated != Time::NullTTime())
		{
		size += SizeOfDatetime( KEmailCreatedElement() );
		}

	if (iModified != Time::NullTTime())
		{
		size += SizeOfDatetime( KEmailModifiedElement() );
		}

	if ( iDeleted )
		{
		size += SizeOfBoolean( iDeleted, KEmailDeletedElement() );
		}

	if ( iFlagged )
		{
		size += SizeOfBoolean( iFlagged, KEmailFlaggedElement() );
		}

	if ( iEmailItem )
		{
		size += SizeOfString( iEmailItem, KEmailItemElement() );
		size += KCDataStart().Length() + KCDataInnerEnd().Length();
		}

	if ( iTruncated )
		{
		size += iTruncated->CountXmlSizeL( this );
		}

	if ( iExt )
		{
		for (TInt i=0; i < iExt->Count(); ++i)
			{
			size += iExt->At(i)->CountXmlSize( this );
			}
		}

	return size;
	}

// -----------------------------------------------------------------------------
// CNSmlEmailParser::AddExtL
// Adds the given extension to iExt
// -----------------------------------------------------------------------------
//
EXPORT_C void CNSmlEmailParser::AddExtL( CNSmlExtData* aExt )
	{
	iExt->AppendL(aExt);
	}

// -----------------------------------------------------------------------------
// CNSmlEmailParser::NextElementL
// Base class' ParseL method calls this method, when a new element is found. aElement 
// is the name of that element (string inside '<' and '>' characters).
// -----------------------------------------------------------------------------
//
void CNSmlEmailParser::NextElementL( TPtrC8 aElement )
	{
	if (aElement.Length() == 0)
		User::Leave(EInvalidXmlError);

	TBool startElement = ETrue;

	if(aElement[0] == KCharacterSlash)
		{
		// take off the '/' character from the beginning
		aElement.Set(aElement.Right(aElement.Length()-1));
		// end element
		startElement = EFalse;
		}
#ifndef __NO_EMPTY_ELEMENTS_
	else if (aElement[aElement.Length()-1] == KCharacterSlash)
		{
		// take off the '/' character from the end
		aElement.Set(aElement.Left(aElement.Length()-1));

		// empty element == start element, empty data, end element
		NextElementL( aElement ); // simulates start element
		NextDataL( _L8("") ); // simulates empty data

		// create the end element
		HBufC8* end = HBufC8::NewLC(aElement.Length()+1);
		TPtr8 ptr = end->Des();
		ptr.Append( _L8("/") );
		ptr.Append( aElement );

		NextElementL( ptr ); // simulates end element

		CleanupStack::PopAndDestroy(); // end

		return;
		}
#endif

	// variables used for state, init to <Email> and not set
	TNSmlCurrentEmailElement inner = EEmail;
	TNSmlCurrentEmailElement outer = EEmail;
	TBool isSet = EFalse;

	// check which element we've got
	if( aElement == KEmailElement )
		{
		outer = ENone;
		isSet = iSetValues.iEmail;
		if ( startElement ) iSetValues.iEmail = ETrue;
		}
	else if( aElement == KEmailReadElement )
		{
		inner = ERead;
		isSet = iSetValues.iRead;
		}
	else if( aElement == KEmailForwardedElement )
		{
		inner = EForwarded;
		isSet = iSetValues.iForwarded;
		}
	else if( aElement == KEmailRepliedElement )
		{
		inner = EReplied;
		isSet = iSetValues.iReplied;
		}
	else if( aElement == KEmailReceivedElement )
		{
		inner = EReceived;
		isSet = iSetValues.iReceived;
		}
	else if( aElement == KEmailCreatedElement )
		{
		inner = ECreated;
		isSet = iSetValues.iCreated;
		}
	else if( aElement == KEmailModifiedElement )
		{
		inner = EModified;
		isSet = iSetValues.iModified;
		}
	else if( aElement == KEmailDeletedElement )
		{
		inner = EDeleted;
		isSet = iSetValues.iDeleted;
		}
	else if( aElement == KEmailFlaggedElement )
		{
		inner = EFlagged;
		isSet = iSetValues.iFlagged;
		}
	else if( aElement == KEmailItemElement )
		{
		inner = EEmailItem;
		isSet = iSetValues.iEmailItem;
		if ( startElement ) 
			HandleNextEmailItemStartL();
		else
			HandleNextEmailItemEndL();
		}
	else if( aElement == KTruncatedElement )
		{
		inner = ETrunc;
		isSet = iSetValues.iTrunc;

		if ( startElement )
			{
			if ( !iTruncated )
				iTruncated = CNSmlTruncatedData::NewL();
			iSetValues.iTrunc = ETrue;
			}
		}
	else if( aElement == KTruncatedBodyElement )
		{
		inner = ETruncBody;
		outer = ETrunc;
		if ( startElement )
			{
			if( iTruncType )
				{
				delete iTruncType;
				iTruncType = NULL;
				}
			iTruncSize = KErrNotFound;

			iSetValues.iTruncBodySize = EFalse;
			iSetValues.iTruncBodyType = EFalse;
			}
		else
			{
			iTruncated->AddBodyL(iTruncSize, iTruncType);
			}
		}
	else if( aElement == KTruncatedAttachElement )
		{
		inner = ETruncAttach;
		outer = ETrunc;
		if ( startElement )
			{
			if( iTruncType )
				{
				delete iTruncType;
				iTruncType = NULL;
				}
			if( iTruncName )
				{
				delete iTruncName;
				iTruncName = NULL;
				}
			iTruncSize = KErrNotFound;

			iSetValues.iTruncAttachName = EFalse;
			iSetValues.iTruncAttachType = EFalse;
			iSetValues.iTruncAttachSize = EFalse;
			}
		else
			{
			iTruncated->AddAttachmentL(iTruncName, iTruncSize, iTruncType);
			}
		}
	else if( aElement == KTruncatedSizeElement )
		{
		if ( (startElement && iCurrentState == ETruncBody) 
				|| (!startElement && iCurrentState == ETruncBodySize) )
			{
			inner = ETruncBodySize;
			outer = ETruncBody;
			isSet = iSetValues.iTruncBodySize;
			}
		else if ( (startElement && iCurrentState == ETruncAttach) 
				|| (!startElement && iCurrentState == ETruncAttachSize) )
			{
			inner = ETruncAttachSize;
			outer = ETruncAttach;
			isSet = iSetValues.iTruncAttachSize;
			}
		else
			{
			User::Leave(EInvalidXmlError); // wrong state
			}
		}
	else if( aElement == KTruncatedTypeElement )
		{
		if ( (startElement && iCurrentState == ETruncBody) 
			|| (!startElement && iCurrentState == ETruncBodyType) )
			{
			inner = ETruncBodyType;
			outer = ETruncBody;
			isSet = iSetValues.iTruncBodyType;
			}
		else if ( (startElement && iCurrentState == ETruncAttach)
				|| (!startElement && iCurrentState == ETruncAttachType) )
			{
			inner = ETruncAttachType;
			outer = ETruncAttach;
			isSet = iSetValues.iTruncAttachType;
			}
		else
			{
			User::Leave(EInvalidXmlError); // wrong state
			}
		}
	else if( aElement == KTruncatedNameElement )
		{
		inner = ETruncAttachName;
		outer = ETruncAttach;
		isSet = iSetValues.iTruncAttachName;
		}
	else if( aElement == KExtElement )
		{
		inner = EExt;
		if ( startElement )
			{
			if( iExtData )
				{
				delete iExtData;
				iExtData = NULL;
				}
			iExtData = CNSmlExtData::NewL();
			iSetValues.iXNam = EFalse;
			}
		else
			{
			if( !iSetValues.iXNam ) User::Leave(EMandatoryFieldNotFound); // xnam not set
			iExt->AppendL(iExtData);
			iExtData = NULL;
			}
		}
	else if( aElement == KExtXNamElement )
		{
		inner = EExtXNam;
		outer = EExt;
		isSet = iSetValues.iXNam;
		}
	else if( aElement == KExtXValElement )
		{
		inner = EExtXVal;
		outer = EExt;
		}
	else
		{ // unknown element
		User::Leave(EInvalidXmlError);
		}


	// finally, change state depending on was the element start or end element
	if ( startElement )
		{
		StartElementStateChangeL( outer, inner, isSet );
		}
	else
		{
		EndElementStateChangeL( inner, outer );
		}


	}

// -----------------------------------------------------------------------------
// CNSmlEmailParser::NextDataL
// Base class' ParseL method calls this method, when a data is read from xml, but that 
// data is not element name (data inside or between elements).
// -----------------------------------------------------------------------------
//
void CNSmlEmailParser::NextDataL( TPtrC8 aData )
	{
	switch(iCurrentState)
		{
		case ENone:
		case EEmail:
		case ETrunc:
		case ETruncBody:
		case ETruncAttach:
		case EExt:
			LeaveIfNotWhiteSpaceL( aData );
			break;
		case ERead:
			iRead = StringToBooleanL(aData);
			iSetValues.iRead = ETrue;
			break;
		case EForwarded:
			iForwarded = StringToBooleanL(aData);
			iSetValues.iForwarded = ETrue;
			break;
		case EReplied:
			iReplied = StringToBooleanL(aData);
			iSetValues.iReplied = ETrue;
			break;
		case EDeleted:
			iDeleted = StringToBooleanL(aData);
			iSetValues.iDeleted = ETrue;
			break;
		case EFlagged:
			iFlagged = StringToBooleanL(aData);
			iSetValues.iFlagged = ETrue;
			break;
		case EReceived:
			iReceived = StringToTTimeL(aData);
			iSetValues.iReceived = ETrue;
			break;
		case ECreated:
			iCreated = StringToTTimeL(aData);
			iSetValues.iCreated = ETrue;
			break;
		case EModified:
			iModified = StringToTTimeL(aData);
			iSetValues.iModified = ETrue;
			break;
		case EEmailItem:
			HandleNextEmailItemDataL( aData ); // implementation dependent behavior
			iSetValues.iEmailItem = ETrue;
			break;
		case ETruncBodySize:
			iTruncSize = StringToIntegerL(aData);
			iSetValues.iTruncBodySize = ETrue;
			break;
		case ETruncBodyType:
			iTruncType = aData.AllocL();
			iSetValues.iTruncBodyType = ETrue;
			break;
		case ETruncAttachSize:
			iTruncSize = StringToIntegerL(aData);
			iSetValues.iTruncAttachSize = ETrue;
			break;
		case ETruncAttachName:
			iTruncName = aData.AllocL();
			iSetValues.iTruncAttachName = ETrue;
			break;
		case ETruncAttachType:
			iTruncType = aData.AllocL();
			iSetValues.iTruncAttachType = ETrue;
			break;
		case EExtXNam:
			iExtData->iXNam = aData.AllocL();
			iSetValues.iXNam = ETrue;
			break;
		case EExtXVal:
			iExtData->AddXValL(aData.AllocL());
			break;
		default:
			User::Leave(EUnknownError); // should never happen
			break;
		}
	}

// -----------------------------------------------------------------------------
// CNSmlEmailParser::HandleNextEmailItemDataL
// Next emailitem data
// -----------------------------------------------------------------------------
//
void CNSmlEmailParser::HandleNextEmailItemDataL( TPtrC8 aData )
	{
	iEmailItem = aData.AllocL();
	}

// -----------------------------------------------------------------------------
// CNSmlEmailParser::StartElementStateChangeL
// Checks that the state change (new start element) is legal (right order of 
// elements and element not already set) and changes the state.
// -----------------------------------------------------------------------------
//
void CNSmlEmailParser::StartElementStateChangeL(TNSmlCurrentEmailElement aCurrentState, 
											    TNSmlCurrentEmailElement aNextState, 
											    TBool aIsSet)
	{
	if( aIsSet || iCurrentState != aCurrentState || aNextState < iLastState )
		{
		User::Leave(EInvalidXmlError);
		}

	iLastState = iCurrentState;
	iCurrentState = aNextState;
	}

// -----------------------------------------------------------------------------
// CNSmlEmailParser::EndElementStateChangeL
// Checks that the state change (new end element) is legal (right order of 
// elements and element not already set) and changes the state.
// -----------------------------------------------------------------------------
//
void CNSmlEmailParser::EndElementStateChangeL(TNSmlCurrentEmailElement aCurrentState, 
											  TNSmlCurrentEmailElement aNextState )
	{
	if( iCurrentState != aCurrentState )
		{
		User::Leave(EInvalidXmlError);
		}

	iLastState = iCurrentState;
	iCurrentState = aNextState;
	}

// -----------------------------------------------------------------------------
// CNSmlEmailParser::ConvertIntoEntitiesL
// Converts special characters of this email-dataobject to corresponding 
// characters. Emailitem needs not to be changed, since it is always 
// placed inside cdata (when generating xml).
// -----------------------------------------------------------------------------
//
void CNSmlEmailParser::ConvertIntoEntitiesL()
	{

	// truncate info
	if ( iTruncated )
		{
		iTruncated->ConvertIntoEntitiesL(this);
		}

	// extensions
	if ( iExt )
		{
		for (TInt i=0; i < iExt->Count(); ++i)
			{
			iExt->At(i)->ConvertIntoEntitiesL(this);
			}
		}

	}

// -----------------------------------------------------------------------------
// CNSmlEmailParser::CNSmlEmailParser
// Private constructor
// -----------------------------------------------------------------------------
//
CNSmlEmailParser::CNSmlEmailParser()
	: iReceived(Time::NullTTime()), iCreated(Time::NullTTime()), iModified(Time::NullTTime()),
	iTruncSize(KErrNotFound)
	{
	}

// -----------------------------------------------------------------------------
// CNSmlEmailParser::ConstructL
// Second phase construction
// -----------------------------------------------------------------------------
//
void CNSmlEmailParser::ConstructL()
	{
	iExt = new (ELeave) CNSmlExtDataArray(3);
	}

// -----------------------------------------------------------------------------
// TNSmlSetEmailValues::TNSmlSetEmailValues
// -----------------------------------------------------------------------------
//
CNSmlEmailParser::TNSmlSetEmailValues::TNSmlSetEmailValues()
	{
	Reset();
	}


// -----------------------------------------------------------------------------
// TNSmlSetEmailValues::Reset
// -----------------------------------------------------------------------------
//
void CNSmlEmailParser::TNSmlSetEmailValues::Reset()
	{
	iEmail = EFalse;
	iRead = EFalse;
	iForwarded = EFalse;
	iReplied = EFalse;
	iDeleted = EFalse;
	iFlagged = EFalse;
	iReceived = EFalse;
	iCreated = EFalse;
	iModified = EFalse;
	iEmailItem = EFalse;
	iTrunc = EFalse;
	iTruncBodySize = EFalse;
	iTruncBodyType = EFalse;
	iXNam = EFalse;
	iTruncAttachSize = EFalse;
	iTruncAttachType = EFalse;
	iTruncAttachName = EFalse;
	}

//  End of File