xml/legacyminidomparser/xmlparser/src/gmxmlparser.cpp
changeset 34 c7e9f1c97567
parent 0 e35f40988205
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/xml/legacyminidomparser/xmlparser/src/gmxmlparser.cpp	Mon Sep 13 13:16:40 2010 +0530
@@ -0,0 +1,1962 @@
+// Copyright (c) 2001-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:
+// @file
+// This file contains the definition of the generic CMDXMLParser class
+// which is responsible for creating a DOM structure
+// from a given XML file.
+// 
+//
+
+#include <f32file.h>
+#include <utf.h>
+#include <bautils.h>
+
+#include <gmxmlconstants.h>
+#include <gmxmlcomposer.h>
+#include <gmxmlentityconverter.h>
+#include <gmxmlnode.h>
+#include <gmxmldocument.h>
+#include <gmxmlelement.h>
+#include <gmxmlparser.h>
+#include <gmxmlprocessinginstruction.h>
+#include <gmxmlcomment.h>
+#include <gmxmlcdatasection.h>
+#include <gmxmltext.h>
+
+#include "GMXMLFileDataSource.h"
+#ifdef SYMBIAN_ENABLE_SPLIT_HEADERS 
+#include "gmxmldummydtd.h"
+#endif
+
+const TInt KGMXMLDefaultTextBufferSize = 1024;
+const TInt KUTF8EdgeBufferLen = 6;
+
+//
+// Global functions					//
+//
+
+
+//==================================================================================
+
+//
+//CMDXParser						//
+//
+//==================================================================================
+
+void CMDXMLParser::Panic(TPanicCode aReason) const
+	{
+	_LIT(KClassName, "CMDXMLParser");
+	User::Panic(KClassName, aReason);
+	}
+
+EXPORT_C CMDXMLParser* CMDXMLParser::NewL(MMDXMLParserObserver* aParserObserver)
+//
+// Two phase static factory function constructor
+// @return Created CMDXMLParser
+// @leave can Leave due to OOM
+//
+	{
+	CMDXMLParser* self = NewLC(aParserObserver);
+	CleanupStack::Pop();
+	return self;
+	}
+
+EXPORT_C CMDXMLParser* CMDXMLParser::NewL(MMDXMLParserObserver* aParserObserver, MXMLDtd* aDtdRepresentation)
+//
+// Two phase static factory function constructor
+// @return Created CMDXMLParser
+// @param aDtdRepresentation specid DTD represention class to be used for validation
+// @leave can Leave due to OOM
+//
+	{
+	CMDXMLParser* self = NewLC(aParserObserver, aDtdRepresentation);
+	CleanupStack::Pop();
+	return self;
+	}
+
+
+//==================================================================================
+
+EXPORT_C CMDXMLParser* CMDXMLParser::NewLC(MMDXMLParserObserver* aParserObserver)
+//
+// Two phase static factory function constructor
+// @return Created CMDXMLParser
+// @leave can Leave due to OOM
+//
+	{
+	CMDXMLParser* self = new (ELeave) CMDXMLParser(aParserObserver);
+	CleanupStack::PushL(self);
+	// This overload of NewLC doesn't take a MXMLDtd*, but we need to provide one to
+	// ConstructL where ownership is taken if we do have one.  Just pass NULL.
+	self->ConstructL(NULL);
+	return self;
+	}
+
+EXPORT_C CMDXMLParser* CMDXMLParser::NewLC(MMDXMLParserObserver* aParserObserver, MXMLDtd* aDtdRepresentation)
+//
+// Two phase static factory function constructor
+// @return Created CMDXMLParser
+// @param aDtdRepresentation specid DTD represention class to be used for validation
+// @leave can Leave due to OOM
+//
+	{
+	CMDXMLParser* self = new (ELeave) CMDXMLParser(aParserObserver);
+	CleanupStack::PushL(self);
+	self->ConstructL(aDtdRepresentation);
+	return self;
+	}
+
+
+//==================================================================================
+
+void CMDXMLParser::ConstructL(MXMLDtd* aDtdRepresentation)
+//
+// Second stage constructor
+// @param aDtdRepresentation The DTD to be used for validation
+// @leave can Leave due to OOM
+//
+	{
+	CMDXMLEntityConverter* entityConverter = new(ELeave) CMDXMLEntityConverter();
+	SetEntityConverter(entityConverter);
+
+	// This doesn't leave, but if CMDXMLParser::NewLC() Leaves after taking ownership
+	// of this we'll get a double deletion as the caller will have pushed 
+	// aDtdRepresentation onto the CleanupStack.  As such we can only take ownership
+	// once we are sure we aren't going to leave.
+	iDtdRepresentation = aDtdRepresentation;
+	}
+
+CMDXMLParser::CMDXMLParser(MMDXMLParserObserver* aParserObserver)
+	: CActive(EPriorityNormal)
+//
+// Constructor
+//
+	{
+	iParserObserver = aParserObserver;
+	iStoreInvalid = ETrue;
+	CActiveScheduler::Add(this);
+	}
+
+//==================================================================================
+
+EXPORT_C CMDXMLParser::~CMDXMLParser()
+	{
+	Cancel();
+	delete iBomBuffer;
+	if( iFileSource == NULL )
+		{
+		// iFileSource has not been allocated yet, so the file path or file
+		// handle are still owned
+		if( iFileToParse!=NULL )
+			delete iFileToParse;
+		else
+			iFileHandleToParse.Close();
+		}
+	else
+		{
+		delete iFileSource;
+		}
+	delete iUTF8EdgeBuffer;
+	delete iXMLDoc;
+	delete iEntityConverter;
+	delete iElementTag;
+	delete iDtdRepresentation;
+	delete iText;
+	}
+
+//==================================================================================
+//Defect fix for INC036136- Enable the use of custom entity converters in GMXML
+EXPORT_C void CMDXMLParser::SetEntityConverter(CMDXMLEntityConverter* aEntityConverter)
+/*
+	 * Sets the entity converter to be used
+	 * and  take ownership of the passed entity converter
+	 * @param aEntityConverter The entity converter to be used
+	 */
+	{
+	delete iEntityConverter;
+	iEntityConverter = aEntityConverter;
+	}
+//End Defect fix for INC036136
+//==================================================================================
+
+EXPORT_C void CMDXMLParser::SetStoreInvalid(TBool aStoreInvalid)
+	{
+	iStoreInvalid = aStoreInvalid;
+	}
+	
+//==================================================================================
+// Defect fix for INC105134 - GmXML consumes whitespace characters 
+//==================================================================================	
+EXPORT_C void CMDXMLParser::SetWhiteSpaceHandlingMode(TBool aPreserve)
+    {
+    iPreserve = aPreserve;
+    }
+
+// End Defect fix for INC105134
+//==================================================================================
+EXPORT_C CMDXMLDocument* CMDXMLParser::DetachXMLDoc()
+//
+// @return CMDXMLDocument* to the created DOM, should be called after the
+// conclusion of the parser process.  Note that internal variable pointing to
+// the document is set to NULL so this function can only be called once per file
+// parse.  Client application must take ownership of document for cleanup purposes.
+//
+	{
+	CMDXMLDocument* returnDoc = iXMLDoc;
+	iXMLDoc = NULL;
+	return returnDoc;
+	}
+
+//==================================================================================
+
+CMDXMLEntityConverter* CMDXMLParser::EntityConverter()
+//
+// @return the CMDXMLEntityConverter for use in converting built in entity
+// and character entities back to their original format
+//
+	{
+	return iEntityConverter;
+	}
+
+//==================================================================================
+
+EXPORT_C TInt CMDXMLParser::ParseFile(RFs aRFs, const TDesC& aFileToParse)
+//
+// ParseFile opens a file ready for parsing
+// @param aRFs a resource file session used for file I/O
+// @param aFileToParse the file name to parse
+// @return KErrNone if all OK or file read error code
+//
+	{
+	//Find out whether the file exists. If not dont start the active object
+	if(!BaflUtils::FileExists(aRFs,aFileToParse))
+		{
+		return KErrNotFound;
+		}
+	else
+		{	
+		//Check whether the file is locked by any other process				
+		RFile tempFile;
+		TInt err=tempFile.Open(aRFs, aFileToParse, EFileRead | EFileShareReadersOnly);		
+		if(err!=KErrNone)
+			{
+			return err;					
+			}
+		tempFile.Close();	
+		}	
+	Cancel();
+	iSuspiciousCharacter = KErrNotFound;
+	iError = KErrNone;
+	iSeverity = EXMLNone;
+	iDocTypeSet = EFalse;
+	iVersionSet = EFalse;
+
+	/* We need to open our file in a leave-safe place as it involves
+	   a heap alloc, and so we'll set up the AO to do that when it runs next.
+	*/
+	delete iFileToParse;
+	iFileToParse = aFileToParse.Alloc();
+	if(iFileToParse == NULL)
+		{
+		return KErrNoMemory;
+		}
+
+	iRFs = aRFs;
+	iState = KInitFromFile;
+
+	SetActive();
+	TRequestStatus* s=&iStatus;
+	User::RequestComplete(s, KErrNone);
+
+	return KErrNone;
+	}
+	
+/** 
+Parses a specified XML file into a DOM object tree.
+
+Parses a specified XML file into a DOM object tree using an open file handle. The 
+parser takes ownership of the open file handle and will close handle when completed.
+
+@param aFileHandleToParse An open file handle for the file to parse. Ownership of the
+	   file handle is passed.
+@return KErrNone if successful.
+*/
+EXPORT_C TInt CMDXMLParser::ParseFile(RFile& aFileHandleToParse)
+	{	
+	iFileHandleToParse = aFileHandleToParse;
+
+	Cancel();
+	iSuspiciousCharacter = KErrNotFound;
+	iError = KErrNone;
+	iSeverity = EXMLNone;
+	iDocTypeSet = EFalse;
+	iVersionSet = EFalse;
+
+	iState = KInitFromFile;
+
+	iStatus = KRequestPending;
+	SetActive();
+	TRequestStatus* s=&iStatus;
+	User::RequestComplete(s, KErrNone);
+
+	return KErrNone;
+	}
+//==================================================================================
+
+EXPORT_C void CMDXMLParser::ParseSourceL(MMDXMLParserDataProvider *aSource)
+	{
+	iSuspiciousCharacter = KErrNotFound;
+	iError = KErrNone;
+	iSeverity = EXMLNone;
+	iDocTypeSet = EFalse;
+	iVersionSet = EFalse;
+
+	iDataSource = aSource;
+	PrepareForReuseL();
+	GetMoreData();
+	}
+
+//==================================================================================
+
+void CMDXMLParser::PrepareForReuseL()
+	{
+	Cancel();
+
+	iError = KErrNone;
+	iSeverity = EXMLNone;
+	iDocTypeSet = EFalse;
+	iVersionSet = EFalse;
+	iBytesPerChar = 0;
+	iNextChar = 0;
+	iInputBytesRemaining = 0;
+	iOpened = EFalse;
+	iClosed = EFalse;
+	iNewElement = NULL;
+	iParentElement = NULL;
+	delete iUTF8EdgeBuffer;
+	iUTF8EdgeBuffer = NULL;
+	delete iBomBuffer;
+	iBomBuffer = NULL;
+
+	CreateDocumentL();
+	iState = KDetermineCharset;
+	}
+
+//==================================================================================
+TBool CMDXMLParser::DetectFileType()
+//
+// Detects the type of a data source - can be Unicode, UTF-8 or ASCII (because ASCII is
+// a subset of UTF-8).
+// If the file is empty it is assumed to be Utf-8.
+	{
+	TBuf8<3> bom;
+
+	// Read the first 3 bytes of the file.  These contain any BOM present.  If it turns out
+	// there's not a bom we leave the pointer untouched so we can parse these bytes
+	// as usual.  
+	if(iInputBufferPtr.Length() < 3)
+		return EFalse;
+	else
+		bom.Copy(iInputBufferPtr.Left(3));
+
+	TInt hichar = (CEditableText::EByteOrderMark & 0xFF00)>>8;
+	TInt lochar = CEditableText::EByteOrderMark & 0xFF;
+
+	if((bom[0] == 0xEF) && (bom[1] == 0xBB) && (bom[2] == 0xBF))
+		{
+		// Utf-8 with a bom.  We don't want to parse the bom, so add 3 bytes offset to the read pos.
+		iBytesPerChar = 1;
+		iNextChar = 3;
+		}
+	else
+		{
+		if((bom[0] == lochar) && (bom[1] == hichar))
+			{
+			// Little Endian Unicode.  Move the read position on 2 bytes to ignore the bom.
+			iBytesPerChar = 2;
+			// would normally skip first 2 characters
+			iNextChar = 2;
+			}
+		else if((bom[0] == hichar) && (bom[1] == lochar))
+			{
+			// We have a bom, but it indicates endianess opposite to that of the platform.  We
+			// don't currently support this so set an error.
+			SetError(KErrNotSupported, EXMLFatal);
+			}
+		else
+			{
+			// Default to Utf-8
+			iBytesPerChar = 1;
+			}
+		}
+	return ETrue;
+	}
+
+//==================================================================================
+
+EXPORT_C void CMDXMLParser::DoCancel()
+//
+// DoCancel function inherited from CActive base class
+//
+	{
+	if (iDataSource)
+	  {
+	  iDataSource->Disconnect();
+	  iDataSource = NULL;
+	  }
+	}
+
+//==================================================================================
+
+EXPORT_C TInt CMDXMLParser::RunError(TInt aError)
+//
+// RunError function inherited from CActive base class - intercepts any Leave from
+// the RunL() function, sets an appropriate errorcode and calls ParseFileCompleteL
+//
+	{
+	if(iDataSource)
+		{
+		iDataSource->Disconnect();
+		iDataSource = NULL;
+		}
+
+	iSeverity = EXMLFatal;
+	iError = aError;
+	if (iFileToParse)
+		{
+		delete iFileToParse;
+		iFileToParse = NULL;
+		}
+	else if( iFileSource==NULL )
+		{
+		// iFileSource is not set so the ownership of the file handle has not been passed
+		iFileHandleToParse.Close();
+		}
+
+	__ASSERT_DEBUG(iParserObserver != NULL, Panic(ENullMemVarParserObserver));
+	TRAPD(err, iParserObserver->ParseFileCompleteL());
+	return err;
+	}
+
+//==================================================================================
+
+TBool CMDXMLParser::DoParseLoopL()
+//
+// RunL function inherited from CActive base class - carries out the actual parsing.
+// @leave can Leave due to OOM
+//
+	{
+	TBuf<1> singleChar;
+	TGetCharReturn getCharReturn = KError;
+	TInt error;
+
+	while(iSeverity != EXMLFatal && (getCharReturn = GetChar(singleChar), getCharReturn == KCharReturned) )
+		{
+
+#if 0	// THIS NEEDS TO BE REMOVED - IT'S A VERY HANDY DEBUGGING TOOL WHEN YOU'RE MANUALLY DOING 
+		// BYTE ORDERING ON MISALIGNED STREAMS THOUGH...
+		{
+#define DES_AS_8_BIT(str) (TPtrC8((TText8*)((str).Ptr()), (str).Size()))
+
+		RFs aRFs;
+		RFile aFile;
+		_LIT(KFileName, "c:\\documents\\SMIL_Test_Files\\echoOutput.txt");
+		TPtrC file;
+		TInt err;
+
+		file.Set(KFileName);
+
+		aRFs.Connect();
+
+		err = aFile.Open(aRFs, file, EFileWrite);
+		if(err != KErrNone)
+			err = aFile.Create(aRFs, file, EFileWrite);
+
+		err = 0;
+		aFile.Seek(ESeekEnd, err);
+		aFile.Write(DES_AS_8_BIT(singleChar));
+		aFile.Close();
+		aRFs.Close();
+		}
+#endif
+		if((!iOpened) && singleChar != KXMLStartTag)
+			{
+			HandleTextL(singleChar);
+			}
+
+		if((iOpened) || (singleChar == KXMLStartTag))			
+			{
+			if(singleChar == KXMLStartTag)
+				{
+				if(iOpened)
+					{
+					if((iSuspiciousCharacter == KErrNotFound))
+						{
+						iSuspiciousCharacter = iElementTag->Length();
+						}
+					}
+				else
+					{
+					AddTextL(iParentElement);
+					iOpened = ETrue;
+					}
+				}
+			else if(singleChar == KXMLEndTag)
+				{
+				if(iSuspiciousCharacter != KErrNotFound)
+					{
+					TPtrC suspiciousSection = iElementTag->Mid(iSuspiciousCharacter);
+					if( CheckForStartCData(suspiciousSection) == 0 )
+						{
+						TInt endCDataLen = TPtrC(KXMLEndCDataSection).Length();
+						// The suspicious character begins a CDataSection.  Check if
+						// this End Tag is closing it.
+						if( suspiciousSection.Right(endCDataLen - 1) 
+							== TPtrC(KXMLEndCDataSection).Left(endCDataLen - 1) )
+							{
+							// Any dodgy characters began the CDataSection or were in it
+							iSuspiciousCharacter = KErrNotFound;
+							}
+						}
+					else if( suspiciousSection.Find(KXMLStartComment) == 0 )
+						{
+						// The suspicious character begins a comment.  Check if
+						// this End Tag is closing it.
+						TInt endCommentLen = TPtrC(KXMLEndComment).Length();
+						if( suspiciousSection.Right(endCommentLen - 1) 
+							== TPtrC(KXMLEndComment).Left(endCommentLen - 1) )
+							{
+							// Any dodgy characters began the comment or were in it
+							iSuspiciousCharacter = KErrNotFound;
+							}
+						}
+					else if((CheckForStartCData(*iElementTag) == 0) || (iElementTag->Find(KXMLStartComment) == 0))
+						{
+						// this tag is a CDataSection or comment, we're allowed <
+						iSuspiciousCharacter = KErrNotFound;
+						iClosed = ETrue;
+						}
+					else
+						{
+						// The < was spurious, set an error and close the tag as normal
+						SetError(KErrXMLIllegalCharacter, EXMLWorkable);
+						iClosed = ETrue;
+						iSuspiciousCharacter = KErrNotFound;
+						}
+					}
+				else
+					{
+					iClosed = ETrue;
+					}
+				}
+
+			// ensure descriptor doesn't overflow end and panics
+			if(iElementTag->Length() == iElementTag->Des().MaxLength())
+				{
+				iElementTag = iElementTag->ReAllocL(iElementTag->Length() + KNominalTagLength);
+				}
+
+			iElementTag->Des().Append(singleChar);
+
+			// if tag is complete and needs adding to the DOM?
+			if(iClosed)
+				{
+				if(		!CommentL(iParentElement)
+					&&	!CDataSectionL(iParentElement)
+					&&	!VersionIDL()
+					&&	!DocTypeL()
+					&&	!ProcessingInstructionL(iParentElement) )
+					{
+					// is this a regular closing tag
+					if
+						(iElementTag->Left(2) == KXMLStartEndTag)
+						{
+						error = ParseElementEndTag(*iParentElement, iElementTag->Des());
+						if(error == KErrNone)
+							{
+							if(iParentElement->ParentNode() == NULL)
+								{
+								SetError(KErrXMLBadNesting, EXMLIndeterminate);
+								}
+							else
+								{
+								iParentElement = (CMDXMLElement*) iParentElement->ParentNode();
+								}
+							}
+						else if(error == KErrNotFound)
+							{
+							CMDXMLElement* tempElement = (CMDXMLElement*) iParentElement->ParentNode();
+							TInt searchResult = KErrNotFound;
+
+							while(tempElement != NULL &&
+									searchResult == KErrNotFound &&
+									tempElement->NodeName() != KXMLDocumentElementNodeName)
+								{
+								searchResult = ParseElementEndTag(*tempElement,iElementTag->Des());	
+								if(searchResult == KErrNone)
+									{
+									iParentElement = tempElement;
+									SetError(KErrXMLBadNesting, EXMLIndeterminate);
+									}
+								else
+									{
+									tempElement = (CMDXMLElement*) tempElement->ParentNode();
+									}
+								}
+							if(searchResult != KErrNone)
+								{
+								SetError(KErrXMLBadNesting, EXMLIndeterminate);
+								}
+							}
+						else
+							{
+							SetError(error, EXMLIndeterminate);
+							}
+						}
+					// if a new element start tag or start/end tag
+					else
+						{
+						// NOTE ParseStartTagL destroys iElementTag
+						// so following check must be done first
+
+						// if not single tag with close
+						if(!(iElementTag->Right(2) == KXMLEndStartTag))
+							{
+							iNewElement = ParseStartTagL();
+							CleanupStack::PushL(iNewElement);
+							error = iParentElement->AppendChild(iNewElement);
+
+							if(error == KErrNone)
+								{
+								CleanupStack::Pop(); // iNewElement
+								iParentElement = (CMDXMLElement*) iParentElement->LastChild();
+								}
+							else
+								{
+								SetError(error, EXMLWorkable);
+								CleanupStack::PopAndDestroy(iNewElement); // iNewElement
+								}
+							}
+						else
+							{
+							iNewElement = ParseStartTagL();
+							CleanupStack::PushL(iNewElement);
+							error = iParentElement->AppendChild(iNewElement);
+
+							if(error == KErrNone)
+								{
+								CleanupStack::Pop(iNewElement); // iNewElement
+								}
+							else
+								{
+								SetError(error, EXMLWorkable);
+								CleanupStack::PopAndDestroy(iNewElement); // iNewElement
+								}
+							}
+						}
+						iEndOfTag = ETrue;
+					}
+
+				if(iEndOfTag)
+					{
+					iEndOfTag = EFalse;
+					iOpened = iClosed = EFalse;
+					iElementTag->Des().Zero();
+
+					// reduce size of ElementTag if increased beyond normal limits on last pass
+					if(iElementTag->Des().MaxLength() > KNominalTagLength)
+						{
+						iElementTag = iElementTag->ReAllocL(KNominalTagLength);
+						}
+					}
+				}
+			}
+		}	// END OF WHILE LOOP
+
+	if(getCharReturn == KError)
+		{
+		return EFalse;
+		}
+	else
+		{
+		//	GetChar returned KWEaitForChar
+		// GetChar handles pushing the state and requesting more data for us, so we just go active.
+		return ETrue;
+		}
+	}
+
+
+
+
+//==================================================================================
+
+void CMDXMLParser::RunL()
+	{
+	TRequestStatus* s=&iStatus;
+	TInt err = s->Int();
+
+	switch(iState)
+		{
+	case KInitFromFile:
+		delete iFileSource;
+		iFileSource = NULL;
+		if( iFileToParse == NULL )
+			{
+			// iFileToParse is not set, file was passed by open file handle.
+			iFileSource = CMDXMLParserFileDataSource::NewL(iFileHandleToParse);			
+			}
+		else
+			{
+			iFileSource = CMDXMLParserFileDataSource::NewL(iRFs, *iFileToParse);
+
+			delete iFileToParse;
+			iFileToParse = NULL;
+			}
+
+		ParseSource(iFileSource);	// will go active itself 
+		break;
+
+	case KDetermineCharset:
+		if(!iBytesPerChar)
+			{
+			if(DetectFileType())
+				{
+				iState = KParseData;
+				SetActive();
+				User::RequestComplete(s, KErrNone);
+				}
+			else
+				{
+				if(!iBomBuffer)
+					{
+					iBomBuffer = HBufC8::NewL(KUTF8EdgeBufferLen);
+					}
+
+				TPtr8 bomDes(iBomBuffer->Des());
+				TInt newLength = bomDes.Length() + iCurrentInputBufferLen;
+				iBomBuffer->ReAlloc(newLength);
+				
+				bomDes.Append(iInputBufferPtr);
+				if(iBomBuffer->Length() >=3)
+					{
+					iInputBufferPtr.Set(bomDes);
+					iCurrentInputBufferLen = newLength;
+
+					SetActive();
+					User::RequestComplete(s, KErrNone);
+					}
+				else
+					{
+					GetMoreData();
+					}
+				}
+			}
+		else
+			{
+			iState = KParseData;
+			SetActive();
+			User::RequestComplete(s, KErrNone);
+			}
+		break;
+
+	case KWaitingForData:
+		switch(err)
+			{
+		case MMDXMLParserDataProvider::KMoreData:
+			// We got more data this time, make sure all the parameters are correct.
+			iCurrentInputBufferLen = iInputBufferPtr.Length();				
+
+			iState = iPreviousState;
+			SetActive();
+			User::RequestComplete(s, KErrNone);
+			break;
+
+		case MMDXMLParserDataProvider::KDataStreamEnd:
+			iState = KFinished;
+			SetActive();
+			User::RequestComplete(s, KErrNone);
+			break;
+
+		default:
+		case MMDXMLParserDataProvider::KDataStreamError:
+			User::Leave(KErrCorrupt);
+			break;
+			}
+
+		break;
+
+	case KParseData:
+		if(!iParentElement)	// initialise the parsing
+			{
+			iOpened = EFalse;
+			iClosed = EFalse;
+			// If we're going through the tag and find a < we don't know whether
+			// it will be valid (eg it starts a CDataSection) or whether it's an
+			// illegal character.  Store its index so that when we're a bit further
+			// along in the string we can check whether it was allowed and if not 
+			// set an Illegal Character error.
+			delete iNewElement;
+			iNewElement = NULL;
+			
+			__ASSERT_DEBUG(iXMLDoc != NULL, Panic(ENullMemVarXMLDoc));
+			iParentElement = iXMLDoc->DocumentElement();
+
+			delete iElementTag;
+			iElementTag = NULL;
+			iElementTag = HBufC::NewL(KNominalTagLength);
+			}
+
+		if(!iBytesPerChar)
+			{
+			// assume ascii/UTF8
+			iBytesPerChar = 1;
+			}
+
+		if(!DoParseLoopL())
+			iState = KFinished;
+
+			break;
+
+	case KFinished:
+		// Check for any errors that we can pick up now, like missing doctype, or incomplete content
+		CheckForErrors();
+
+		// we want to leave this instance in a safe state where it can be restarted again with a single call to ParseSource.
+		// cannot delete this element, as it belongs to the document...
+		iParentElement = NULL;
+		iState = KDetermineCharset;
+
+		iDataSource->Disconnect();
+		iDataSource=NULL;
+		__ASSERT_DEBUG(iParserObserver != NULL, Panic(ENullMemVarParserObserver));
+		iParserObserver->ParseFileCompleteL();
+		break;
+
+	default:
+		User::Leave(KErrUnknown);
+		break;
+		}
+	}
+
+void CMDXMLParser::CheckForErrors()
+	{
+	if(iError == KErrNone)
+		{
+		if(iParentElement)
+			{
+			// if iParentElement is not pointing to dummy root node, there has been a problem
+			if( (iParentElement == NULL) || (iParentElement->NodeName() != KXMLDocumentElementNodeName) )
+				{
+				SetError(KErrXMLIncomplete, EXMLWorkable);
+				}
+			else if(!iParentElement->CheckChildren())
+				{
+				SetError(KErrXMLInvalidChild, EXMLWorkable);
+				}
+			else if(iParentElement->FirstChild() != NULL)
+				{
+				// multiple real (not dummy) root elements
+				TInt count = 0;
+				CMDXMLNode* iterator = iParentElement->FirstChild();
+				do
+					{
+					if(iterator->NodeType() == CMDXMLNode::EElementNode)
+						{
+						count++;
+						}
+					iterator = iterator->NextSibling();
+					}
+				while(iterator != NULL);
+
+				if(count != 1)
+					{
+					SetError(KErrXMLMultipleRootElements, EXMLWorkable);
+					}
+				}
+			}
+		}
+	if(iError == KErrNone && !iDocTypeSet)
+		{
+		SetError(KErrXMLMissingDocTypeTag, EXMLWorkable);
+		}
+
+	if(iError == KErrNone && !iVersionSet)
+		{
+		SetError(KErrXMLMissingVersionTag, EXMLWorkable);
+		}
+	}
+
+
+void CMDXMLParser::CreateDocumentL()
+//
+// Creates a generic or DTD-specific document object
+// @leave can Leave due to OOM
+//
+	{
+	delete iXMLDoc;
+	iXMLDoc = NULL;
+
+	if (iDtdRepresentation != NULL)
+		iXMLDoc = CMDXMLDocument::NewL(*iDtdRepresentation);
+	else
+		iXMLDoc = CMDXMLDocument::NewL();
+	}
+
+
+//==================================================================================
+
+TBool CMDXMLParser::DocTypeL()
+//
+// @return Returns true if the current tag is a doctype tag and sets the
+// Document DocType member accordingly on the first pass of this function.
+//
+	{
+	TBool returnValue = EFalse;
+	TInt tagIdLen = TPtrC(KXMLDocumentTypes).Length();
+
+	__ASSERT_DEBUG(iElementTag != NULL, Panic(ENullMemVarElementTag));
+
+	if(iElementTag->Length() > tagIdLen
+			&& iElementTag->Left(tagIdLen) == KXMLDocumentTypes)
+		{
+		if(iDocTypeSet)
+			{
+			SetError(KErrXMLDuplicateDocTypeTags, EXMLWorkable);
+			}
+		else
+			{
+			iXMLDoc->SetDocTypeTagL(iElementTag->Des());
+			iDocTypeSet = ETrue;
+			}
+		returnValue = ETrue;
+		iEndOfTag = ETrue;
+		}
+
+	return returnValue;
+	}
+
+//==================================================================================
+
+TBool CMDXMLParser::ProcessingInstructionL(CMDXMLElement* aParentElement)
+//
+// creates a new processing instruction if necessary and adds to document
+// @return Returns true if the current tag is a processing instruction
+//
+	{
+	TBool returnValue = EFalse;
+	TInt startPILen = TPtrC(KXMLStartProcessingInstruction).Length();
+	TInt endPILen = TPtrC(KXMLEndProcessingInstruction).Length();
+
+	__ASSERT_DEBUG(iElementTag != NULL, Panic(ENullMemVarElementTag));
+
+	if((iElementTag->Left(startPILen) == KXMLStartProcessingInstruction) &&
+		(iElementTag->Right(endPILen) == KXMLEndProcessingInstruction))
+		{
+		if(aParentElement != NULL)
+			{
+			CMDXMLProcessingInstruction* inst = CMDXMLProcessingInstruction::NewLC(iXMLDoc);
+
+			TPtrC instStr = iElementTag->Des().Mid(startPILen,
+				iElementTag->Length() - (startPILen + endPILen));
+			inst->SetDataL(instStr);
+
+			__ASSERT_DEBUG(aParentElement != NULL, Panic(ENullParameterParentElement));
+			TInt error = aParentElement->AppendChild(inst);
+			CleanupStack::Pop(inst);
+
+			if(error != KErrNone)
+				{
+				SetError(error, EXMLWorkable);
+				}
+			}
+
+		returnValue = ETrue;
+		iEndOfTag = ETrue;
+		}
+
+	return returnValue;
+	}
+
+
+//==================================================================================
+
+TBool CMDXMLParser::CDataSectionL(CMDXMLElement* aParentElement)
+//
+// creates a new CDataSection if necessary and adds to document
+// @return Returns true if the current tag is a CDataSection
+//
+	{
+	TBool returnValue = EFalse;
+	TInt instLen = TPtrC(KXMLStartCDataSection).Length();
+	TInt endCDataLen = TPtrC(KXMLEndCDataSection).Length();
+
+	__ASSERT_DEBUG(iElementTag != NULL, Panic(ENullMemVarElementTag));
+
+	if (iElementTag->Left(instLen) == KXMLStartCDataSection) 
+		{
+		returnValue = ETrue;
+		if ((iElementTag->Right(endCDataLen) == KXMLEndCDataSection) && (aParentElement != NULL))
+			{
+			CMDXMLCDATASection* inst = CMDXMLCDATASection::NewLC(iXMLDoc);
+
+			TPtrC instStr = iElementTag->Des().Mid(instLen,
+				iElementTag->Length() - (instLen + endCDataLen));
+			inst->SetDataL(instStr);
+
+			__ASSERT_DEBUG(aParentElement != NULL, Panic(ENullParameterParentElement));
+			TInt error = aParentElement->AppendChild(inst);
+			CleanupStack::Pop(); // inst
+
+			if(error != KErrNone)
+				{
+				SetError(error, EXMLWorkable);
+				}
+			}
+		iEndOfTag = ETrue;
+		}
+
+	return returnValue;
+	}
+
+
+//==================================================================================
+
+TBool CMDXMLParser::VersionIDL()
+//
+// @return returns true if the current tag is a version id tag and sets the
+// Document Version member accordingly on the first pass of this function.
+//
+	{
+	TBool returnValue = EFalse;
+	TInt tagIdLen = TPtrC(KXMLVersion).Length();
+
+	__ASSERT_DEBUG(iElementTag != NULL, Panic(ENullMemVarElementTag));
+
+	if(iElementTag->Length() > tagIdLen
+			&& iElementTag->Left(tagIdLen) == KXMLVersion)
+		{
+		if(iVersionSet)
+			{
+			SetError(KErrXMLDuplicateVersionTags, EXMLWorkable);
+			}
+		else
+			{
+			iXMLDoc->SetVersionTagL(iElementTag->Des());
+			iVersionSet = ETrue;
+			}
+		returnValue = ETrue;
+		iEndOfTag = ETrue;
+		}
+
+	return returnValue;
+	}
+
+//==================================================================================
+
+TBool CMDXMLParser::CommentL(CMDXMLElement* aParentElement)
+//
+// creates a new comment if necessary and adds to document
+// @return returns true if the current tag is a comment tag
+//
+	{
+	TBool returnValue = EFalse;
+	TInt commentLen = TPtrC(KXMLStartComment).Length();
+	TInt endCommentLen = TPtrC(KXMLEndComment).Length();
+
+	__ASSERT_DEBUG(iElementTag != NULL, Panic(ENullMemVarElementTag));
+
+	if (iElementTag->Left(commentLen) == KXMLStartComment) 
+		{
+		returnValue = ETrue;
+		if ((aParentElement != NULL) && (iElementTag->Right(endCommentLen) == KXMLEndComment))
+			{
+			CMDXMLComment* comment = CMDXMLComment::NewLC(iXMLDoc);
+
+			TPtrC commentStr = iElementTag->Des().Mid(commentLen,
+				iElementTag->Length() - (commentLen + endCommentLen));
+			comment->SetDataL(commentStr);
+
+			__ASSERT_DEBUG(aParentElement != NULL, Panic(ENullParameterParentElement));
+			TInt error  = aParentElement->AppendChild(comment);
+			CleanupStack::Pop(); // comment
+
+			if(error != KErrNone)
+				{
+				SetError(error, EXMLWorkable);
+				}
+			iEndOfTag = ETrue;
+			}
+		}
+
+	return returnValue;
+	}
+
+//==================================================================================
+
+EXPORT_C void CMDXMLParser::SetSourceCharacterWidth(TMDXMLParserInputCharWidth aWidth)
+	{
+	iBytesPerChar = aWidth;
+	}
+
+//==================================================================================
+
+void CMDXMLParser::GetMoreData()
+	{
+
+	// Prepare these variables.
+	iNextChar = 0;					// reading from the start of the buffer
+	iCurrentInputBufferLen = 0;		// we have no characters in the buffer
+	iUnicodeConversion.Zero();		// we have no characters in our UTF8->Unicode Conversion buffer.
+	iUnicodeConversionLen = 0;
+	iUnicodeReadPos = 0;
+
+	// Request more data from the data provider
+	__ASSERT_DEBUG(iDataSource != NULL, Panic(ENullMemVarDataSource));
+	iDataSource->GetData(iInputBufferPtr, iStatus);
+	SetActive();
+	iPreviousState = iState;
+	iState = KWaitingForData;
+	}
+
+//==================================================================================
+
+CMDXMLParser::TGetCharReturn CMDXMLParser::GetDoubleByteChar(TDes& aChar)
+	// when inputing we have a pointer to an 8 bit buffer (iInputBufferPtr), for unicode
+	// input we point a 16 bit descriptor (tempUnicodeInput) at the 8 bit buffer to
+	// enable us to read the 2 x 8 bit chars as a single 16 bit char.
+	// However it isn't always this simple as the data provider interface makes no guarantees
+	// on the alignment of this data. It's perfectly possible for it to end up with a unicode
+	// character where the high byte comes from the previous buffer and the low byte comes from
+	// the current one.  This will put the rest of the current buffer out of line, and also all
+	// subsequent buffers unless an odd length buffer is provided.  Hopfully this won't happen often.
+	{
+	aChar.Zero();
+
+	if(iUnicodeInputMisaligned)
+		{
+		TUint16 tempOut;
+		TUint8* tempRead;
+		tempRead = (TUint8*)(iInputBufferPtr.Ptr()) + iNextChar;
+
+		if(iCurrentInputBufferLen - iNextChar >=1)
+		{
+			// if we saved a byte last time, lets use that first
+			if(iSpareChar.Length())
+				{
+				tempOut = iSpareChar[0];
+				iSpareChar.Zero();
+				}
+			else if(iCurrentInputBufferLen - iNextChar >=2)
+				{
+				// we didn't save a byte, so we read from the stream.
+				tempOut = *tempRead;
+				tempRead++;
+				iNextChar++;
+				}
+			else
+				{
+				// our input stream must have been an odd length - this might cause alignment problems, 
+				// so we need to start reading bytewise for a while
+				iUnicodeInputMisaligned = ETrue;
+
+				TUint8* tempRead = (TUint8*)(iInputBufferPtr.Ptr()) + iNextChar;
+				TUint16 tempVal = (TUint16)(*tempRead << 8);
+				iSpareChar.Copy(&tempVal,1);
+
+				GetMoreData();
+				return KWaitForChar;
+
+				}
+
+			// second byte (high byte) of our output comes from the input stream in all cases.
+			tempOut |= ((*tempRead & 0xFF) << 8);
+			iNextChar++;
+
+			TPtrC16 readDes(&tempOut, 1);
+			aChar = readDes.Left(1);
+			}
+		}
+	else if(iCurrentInputBufferLen - iNextChar >= 2)
+		{
+		// we may be in a position where we don't know we're going to lose a byte
+		// so we'll test for that ahead of time, and then handle that in the normal way
+
+		// if we execute this, it means that we have two bytes available to read.
+		const TUint16* word = reinterpret_cast<const TUint16*>((iInputBufferPtr.Ptr() + iNextChar));
+		TPtrC16 tempUnicodeInput(word, 2);
+		aChar = tempUnicodeInput.Left(1);
+		iNextChar+=2;
+		return KCharReturned;
+		}
+	
+	TInt bytesRemaining = iCurrentInputBufferLen - iNextChar;
+
+	switch(bytesRemaining)
+		{
+		case 1:
+			{
+			// our input stream must have been an odd length - this might cause alignment problems, 
+			// so we need to start reading bytewise for a while
+
+			TUint8* tempRead = (TUint8*)(iInputBufferPtr.Ptr()) + iNextChar;
+			TUint16 tempVal = *tempRead;
+			iSpareChar.Copy(&tempVal,1);
+			iUnicodeInputMisaligned = ETrue;
+			iNextChar++;
+			if(!aChar.Length())
+				{
+				GetMoreData();
+				return KWaitForChar;
+				}
+			}
+			break;
+		case 0:
+			{
+			// we're at the end of this block, and it's turned out to be re-aligned.
+			// we can read subsequent blocks in 16-bit chunks.
+			iUnicodeInputMisaligned = EFalse;
+			}
+			break;
+		}
+
+	return KCharReturned;
+	}
+	
+//==================================================================================
+
+CMDXMLParser::TGetCharReturn CMDXMLParser::GetSingleByteChar(TDes& aChar)
+	{
+	// We have UTF8/ASCII Source, and we must need to convert some more if we got here.
+	iUnicodeConversion.Zero();
+	iUnicodeConversionLen = 0;
+	iUnicodeReadPos = 0;
+
+	// if we are not operating out of the edge buffer yet, work on the real one
+	if(!iUTF8EdgeBuffer)
+		{
+		// This is an 8 bit encoding, probably UTF-8, but could be ASCII.  Because
+		// ASCII is valid UTF-8 we can just convert what we have to Unicode.
+		// We're going to convert a number of characters at a time here.
+		TInt inputBytesRemaining = iCurrentInputBufferLen - iNextChar;
+		TInt convResult;
+		TPtrC8 tempPtr( (TUint8*)(iInputBufferPtr.Ptr()) + iNextChar, inputBytesRemaining );
+
+		convResult = CnvUtfConverter::ConvertToUnicodeFromUtf8(iUnicodeConversion, tempPtr);
+		if((convResult >= 0) || (convResult == KErrCorrupt))
+			{
+			// Sometimes the UTF8 decoder might return corrupt if it only gets a single character
+			// in this case we ignore the error and report that we have converted 0 characters
+			if (convResult == KErrCorrupt)
+				convResult = tempPtr.Length();
+
+			// This is the number of bytes converted.
+			// Keep an eye out in case there is no change in the character consumed count.
+			TInt bytesConverted = inputBytesRemaining - convResult;
+
+			// We consumed characters.  Make our input buffer read position correct.
+			iNextChar += bytesConverted;
+
+			// Make our intermediate buffering correct and return the first character out of our buffer
+			// subsequent calls will just return characters from this buffer.
+			iUnicodeConversionLen = iUnicodeConversion.Length();
+			aChar = iUnicodeConversion.Left(1);
+			iUnicodeReadPos = 1;
+
+			if(convResult && convResult < KUTF8EdgeBufferLen)
+				{
+				TUint8* multiByteCheck = (TUint8*)(iInputBufferPtr.Ptr()) + iNextChar;
+
+				// There is a possibility that we've got an edge case here
+				//check if our left over character is in fact UTF8.
+				if((0x80 & *multiByteCheck) != 0)
+					{
+					// Shift 'convResult' characters off into the edge buffer.
+					delete iUTF8EdgeBuffer;
+					iUTF8EdgeBuffer = HBufC8::New(KUTF8EdgeBufferLen);
+					*iUTF8EdgeBuffer = iInputBufferPtr.Right(convResult);
+
+					TUint8 bitMask = 0x80;
+					TInt   byteCount = 0;
+					while(bitMask && (bitMask & (iUTF8EdgeBuffer->Des()[0])) != 0)
+						{
+						bitMask >>= 1;
+						byteCount++;
+						};
+
+					if(!bitMask)
+						{
+						// the utf8 stream appears to be corrupt.
+						SetError(KError, EXMLFatal);
+						return KError;
+						}
+					// we need to find byteCount characters to make up the character currently stored in the edge
+					// buffer.
+					iRequiredUTF8Bytes = byteCount - iUTF8EdgeBuffer->Length();
+
+					// set the variables up so that we return any converted characters, and then begin work
+					// on the edge buffer (where we've already cached the remaining bytes if any)
+					// NOTE: We will return all the characters which we preconverted into iUnicodeConversion *before*
+					// we begin dealing with the edge buffer, because of the structure of this function.
+					iNextChar = iCurrentInputBufferLen;
+
+					if (bytesConverted == 0)
+						// If no bytes were converted then there is nothing to return,
+						// we need to wait for more data and then conbine it with what we have
+						// just put on the edge buffer.
+						{
+						// need more bytes to finish this character.
+						GetMoreData();
+						return KWaitForChar;			
+						}
+					}
+				}
+			}
+
+		else
+			{
+			return KError;	// something failed in the UTF8 Converter.
+			}
+		}
+	else
+		{
+		// We are converting the UTF8 Edge Buffer.  We know that in it's current state, it 
+		// can't be converted to Unicode.
+		// Decide if we have enough characters in our current input stream to convert to an
+		// output character (or two) yet.
+		
+		if(iUTF8EdgeBuffer->Length() >= KUTF8EdgeBufferLen)
+			{
+			// our edge buffer reached the maximum length for a UTF8 character
+			// and we haven't managed to convert a unicode output.
+			// this means that the input stream is corrupt.
+			delete iUTF8EdgeBuffer;
+			iUTF8EdgeBuffer = NULL;
+
+			// Report a fatal error.
+			SetError(KError, EXMLFatal);
+			return KError;
+			}
+		else
+			{
+			TInt convResult;
+
+			// we know how many bytes are required in order to complete the utf8 buffer
+			TInt bytesAvailable = iCurrentInputBufferLen - iNextChar;
+
+			if(bytesAvailable >= iRequiredUTF8Bytes)
+				{
+				// we have enough bytes to complete this character.
+				// Go ahead and convert then return it.
+				iUTF8EdgeBuffer->Des().Append(iInputBufferPtr.Mid(iNextChar, iRequiredUTF8Bytes));
+				iUnicodeConversion.Zero();
+				convResult = CnvUtfConverter::ConvertToUnicodeFromUtf8(iUnicodeConversion, iUTF8EdgeBuffer->Des());
+				
+				// regardless if we managed to convert this buffer or not, we don't need it any more.
+				delete iUTF8EdgeBuffer;
+				iUTF8EdgeBuffer = NULL;
+
+				// make sure we report any error in the conversion
+				if(convResult != 0)
+				{
+					// we either incorrectly calculated the required number of bytes or the
+					// stream is corrupt.  Either way we have a fatal error.
+					SetError(KError, EXMLFatal);
+					return KError;
+				}
+
+				// Make our intermediate buffering correct and return the first character out of our buffer
+				// subsequent calls will just return characters from this buffer.
+				iUnicodeConversionLen = iUnicodeConversion.Length();
+				aChar = iUnicodeConversion.Left(1);
+				iUnicodeReadPos = 1;
+
+				// set up the main input buffers so that the next char comes from the input stream.
+				iNextChar += iRequiredUTF8Bytes;
+				iRequiredUTF8Bytes = 0;
+				}
+			else
+				{
+				// we haven't got enough bytes to complete this character, store the 
+				// available byte(s) and request more data.
+				iUTF8EdgeBuffer->Des().Append(iInputBufferPtr.Mid(iNextChar, bytesAvailable));
+
+				// Move the next character index on for as many bytes as we have just added to the edge buffer
+				iNextChar += bytesAvailable;
+				// We can reduce the number of bytes require by the number of bytes added to the edge buffer
+				iRequiredUTF8Bytes -= bytesAvailable;
+
+				// need more bytes to finish this character.
+				GetMoreData();
+				return KWaitForChar;			
+				}
+			}
+		}
+
+	return KCharReturned;
+	}
+
+//==================================================================================
+
+CMDXMLParser::TGetCharReturn CMDXMLParser::GetChar(TDes& aChar)
+//
+// Fetch one character from the input file
+// @param aChar the returned character.
+// @return returns true if a character returned or false if the file is finished
+//
+	{
+	// first test - see if we're providing preconverted characters.
+	if(iUnicodeConversionLen && iUnicodeReadPos < iUnicodeConversionLen)
+		{
+		// return one of the preconverted chars.
+		aChar = iUnicodeConversion.Mid(iUnicodeReadPos, 1);
+		iUnicodeReadPos++;
+
+		return KCharReturned;
+		}
+	
+	// Second test - see if we require more data.  If we have converted data and we require
+	// more data, this code is not intelligent enough to request data from the provider
+	// early, but that's ok.
+
+	// Buffer length held as a member variable for performance reasons
+	// this function will be accessed thousands of times
+	if(iCurrentInputBufferLen <= iNextChar)
+		{
+		GetMoreData();
+		return KWaitForChar;	
+		}
+
+	// return the character, handling any of the buffer shuffling we need to do.
+	if(iBytesPerChar == 2)
+		return GetDoubleByteChar(aChar);
+	else	
+		return GetSingleByteChar(aChar);
+	}
+
+//==================================================================================
+
+CMDXMLElement* CMDXMLParser::ParseStartTagL()
+//
+// Parse a start of element tag and create an element with attributes set.
+// @return Returns a pointer to the created element
+// @leave can Leave due to OOM
+//
+	{
+	__ASSERT_DEBUG(iElementTag != NULL, Panic(ENullMemVarElementTag));
+
+	// there must be at least two angle brackets and a single character to be meaningful
+	if(iElementTag->Length() < 3)
+		return NULL;
+
+	CMDXMLElement* newElement = NULL;
+	TPtr elementTagPtr = iElementTag->Des();
+
+	// remove the angle brackets and trim white space
+	if(iElementTag->Right(TPtrC(KXMLEndStartTag).Length()) == KXMLEndStartTag)
+		elementTagPtr = iElementTag->Left(iElementTag->Length() - TPtrC(KXMLEndStartTag).Length());
+	else
+		elementTagPtr = iElementTag->Left(iElementTag->Length() - TPtrC(KXMLEndTag).Length());
+
+	elementTagPtr = iElementTag->Right(iElementTag->Length() - TPtrC(KXMLStartTag).Length());
+	elementTagPtr.Trim();
+
+	// find out where the name ends and the attributes begin
+	TLex16 element(elementTagPtr);
+	element.SkipCharacters();
+	TInt endOfName = element.Offset();
+
+	// separate out the name from the attributes
+	HBufC* elementName = (iElementTag->Left(endOfName)).AllocLC();
+	TPtr elementNamePtr = elementName->Des();
+	elementNamePtr.TrimRight();
+
+	TInt error = KErrNone;
+
+	__ASSERT_DEBUG(iXMLDoc != NULL, Panic(ENullMemVarXMLDoc));
+	
+	TBool validElement = iXMLDoc->ValidElementNameL(elementNamePtr);
+	if(validElement || iStoreInvalid)
+		{
+		// remove the actual name from the tag so we only pass on the attributes
+		elementTagPtr = iElementTag->Right(iElementTag->Length() - endOfName);
+		elementTagPtr.TrimLeft();
+		newElement = CMDXMLElement::NewLC(iXMLDoc->CanElementHaveChildren(elementNamePtr), iXMLDoc, elementNamePtr);
+
+		error = ParseElementAttributesL(*newElement, elementTagPtr);
+		CleanupStack::Pop();
+		}
+	if(!validElement)
+		{
+		error = KErrXMLInvalidElement;
+		}
+
+	CleanupStack::PopAndDestroy(elementName); // elementName
+
+	if(error != KErrNone)
+		{
+		SetError(error, EXMLWorkable);
+		}
+
+	return newElement;
+
+	}
+
+
+TInt CMDXMLParser::ParseElementAttributesL(CMDXMLElement& aElement, TDes& aTagToParse)
+//
+// This function is used to parse the attributes.
+// @param aElement The element to which the attributes belong
+// @param aTagToParse The tag to be parsed
+// @return Returns KErrNone if both attribute name & value are valid 
+// KErrXMLBadAttributeName if attribute name is invalid or KErrXMLBadAttributeValue is invalid
+// @leave can Leave due to OOM
+//
+	{
+	TInt error = KErrNone;
+	TInt attributeError = KErrNone;
+	HBufC* attributeName = NULL;
+	HBufC* attributeValue = NULL;
+	TBuf<1> attributeDelimiter; // may be " or '
+	TInt offset = KErrNone;
+
+	offset = aTagToParse.Find(KEqualSign);
+	while(offset != KErrNotFound)
+		{
+		attributeName = TPtrC(aTagToParse.Left(offset)).AllocLC();
+		TPtr attributeNamePtr = attributeName->Des();
+		attributeNamePtr.TrimRight(); // remove white space that existed between name and equal sign
+
+		// remove current attribute name and equal sign from string
+		aTagToParse = aTagToParse.Right(aTagToParse.Length() - (offset + 1));
+		aTagToParse.TrimLeft(); // remove white space that existed between equal sign and delimiter
+
+		if(error == KErrNone && aTagToParse.Length() < 2) // a name must be at followed by at least 2 delimiters
+			{
+			error = KErrXMLBadAttributeName;
+			// In this case, there is insufficient tag left to contain any more attributes
+			}
+		else if(error == KErrNone && aElement.IsAttributeSpecified(*attributeName))
+			{
+			error = KErrXMLDuplicateAttributeName;
+			// We need to remove the attribute value from the tag string or it will be
+			// picked up as part of the next attribute name.
+			attributeDelimiter = aTagToParse.Left(1);
+
+			// Check in case we've got a missing " at the beginning of the attribute.  If
+			// we do, we need to try a different strategy to find the end of this attribute
+			if (attributeDelimiter != KQuotation && attributeDelimiter != KApostrophe)
+				{
+				offset = LocateNextAttribute(aTagToParse);
+				}
+			else
+				{
+				// remove start delimiter then search for next one (end delimiter)
+				aTagToParse = aTagToParse.Right(aTagToParse.Length() - 1);
+				offset = FindDelimiter(aTagToParse, attributeDelimiter);
+				}
+
+			if(offset != KErrNotFound)
+				{
+				// remove current attribute value and delimiter
+				aTagToParse = aTagToParse.Right(aTagToParse.Length() - (offset + 1));
+				aTagToParse.TrimLeft(); // remove white space that existed between delimiter and next name
+				}
+			}
+		else
+			{
+			attributeDelimiter = aTagToParse.Left(1);
+
+			if (attributeDelimiter != KQuotation && attributeDelimiter != KApostrophe)
+				{
+				// This attribute doesn't have a valid delimiter.  Try and find the beginning of the next
+				// attribute and just cut this one.
+				TInt nextAttribute = LocateNextAttribute(aTagToParse);
+				if(nextAttribute > 0)
+					{
+					// Add one to next attribute because the offset includes the whitespace before it
+					aTagToParse = aTagToParse.Right(aTagToParse.Length() - (nextAttribute + 1));
+					}
+
+				if (error == KErrNone)
+					{
+					error = KErrXMLBadAttributeValue;
+					}
+				}
+			else
+				{
+				// remove start delimiter then search for next one (end delimiter)
+				aTagToParse = aTagToParse.Right(aTagToParse.Length() - 1);
+
+				offset = FindDelimiter(aTagToParse, attributeDelimiter);
+				if(offset != KErrNotFound)
+					{
+					attributeValue = TPtrC(aTagToParse.Left(offset)).AllocLC();
+					TPtr attributeValuePtr = attributeValue->Des();
+					attributeValuePtr.TrimRight(); // remove white space that existed between value and delimiter
+
+					// remove current attribute value and delimiter
+					aTagToParse = aTagToParse.Right(aTagToParse.Length() - (offset + 1));
+					aTagToParse.TrimLeft(); // remove white space that existed between delimiter and next name
+
+					// Entity convert this attribute
+					attributeError = ParseSingleAttributeL(attributeValuePtr);
+					if( attributeError != KErrNone && error == KErrNone)
+						{
+						error = attributeError;
+						}
+
+					attributeError = aElement.SetAttributeL(*attributeName, *attributeValue, iStoreInvalid);
+					if( attributeError != KErrNone && error == KErrNone)
+						{
+						error = KErrXMLInvalidAttribute;
+						}
+					CleanupStack::PopAndDestroy(attributeValue); //attributeValue
+					}
+				else if(error == KErrNone)
+					{
+					error = KErrXMLBadAttributeValue;
+					}
+				}
+			}
+
+		// next attribute pair
+		offset = aTagToParse.Find(KEqualSign);
+		CleanupStack::PopAndDestroy(attributeName); //attributeName
+		}
+
+	if(error == KErrNone && aTagToParse.Length() != 0)
+		{
+		error = KErrXMLBadAttributeValue;
+		}
+
+	return error;
+	}
+
+TInt CMDXMLParser::LocateNextAttribute(const TDesC& aTagToParse)
+	{
+	// Find the next attribute by looking for an = then search back for a ' '.
+	// This is useful when you've hit rubbish parsing the content of a start tag
+	// and are looking for somewhere sensible to start.
+	TInt nextAttribute = KErrNotFound;
+	TInt offset = aTagToParse.Find(KEqualSign);
+
+	// If the = is the first character then there isn't space for a ' ' so
+	// don't bother looking
+	if(offset > 0)
+		{
+		TPtrC invalidText = aTagToParse.Left(offset);
+		nextAttribute = invalidText.LocateReverse(' ');
+		}
+
+	return nextAttribute;
+	}
+
+TInt CMDXMLParser::ParseElementEndTag(CMDXMLElement& aElement, const TDesC& aTagToParse)
+//
+// Parses an end tag.  In fact, at this point the end tag must match
+// the tag name of the start tag.  
+// @param aTagToParse Text of the end tag.
+// @return Returns KErrNone if the end tag matches the start tag or KErrNotFound if there is a mismatch.
+//
+	{
+	// The tag should be of the form '</tag>' where tag is the name of this element so we will
+	// check and strip off the surrounding </  > and then compare the remains with this #
+	// node name.
+	TInt retVal = KErrNone;
+	if( aTagToParse.Length() != (aElement.NodeName().Length()+3))
+		{
+		retVal = KErrNotFound;
+		}
+	else
+		{
+		TInt startEndTagLen = TPtrC(KXMLStartEndTag).Length();
+		TInt endTagLen = TPtrC(KXMLEndTag).Length();
+
+		if((aTagToParse.Left(startEndTagLen).Compare(KXMLStartEndTag) == 0) &&
+				(aTagToParse.Right(endTagLen).Compare(KXMLEndTag) == 0))
+			{
+			if(aElement.NodeName().Compare(aTagToParse.Mid(2,
+						aTagToParse.Length() - (startEndTagLen + endTagLen))) != 0)
+				{
+				retVal = KErrNotFound;
+				}
+			}
+		}
+	return retVal;
+	}
+
+
+//==================================================================================
+
+EXPORT_C void CMDXMLParser::SetError(const TInt aErrorCode, const TXMLErrorCodeSeverity aSeverity)
+//
+// Sets iError to new errorcode if more serious than any error so far encountered
+//
+	{
+	if(iSeverity > aSeverity)
+		{
+		iSeverity = aSeverity;
+		iError = aErrorCode;
+		}
+	}
+
+//==================================================================================
+
+EXPORT_C TInt CMDXMLParser::Error() const
+	{
+	return iError;
+	}
+
+//==================================================================================
+
+EXPORT_C TXMLErrorCodeSeverity CMDXMLParser::ErrorSeverity() const
+	{
+	return iSeverity;
+	}
+
+//==================================================================================
+
+void CMDXMLParser::HandleTextL(TDes& aChar)
+//
+// Called when a character is read in and found to bo outside of an element tag
+//
+	{
+	// Save the text in a buffer.
+	// This text will get added as as a child element when the next tag is encounted.
+	if (iText == NULL)
+		iText = HBufC::NewL(KGMXMLDefaultTextBufferSize);
+	
+	if (iText->Length() == iText->Des().MaxLength())
+		// The buffer will overflow if we add another character.
+		// Need to reallocate.
+		{
+		iText = iText->ReAllocL(iText->Des().MaxLength() + KGMXMLDefaultTextBufferSize);
+		}
+
+	iText->Des().Append(aChar);
+	}
+
+TBool CMDXMLParser::EndOfCDataSection()
+	{
+	TBool endOfCData = EFalse;
+	TPtrC cdataEndSection(KXMLEndCDataSection);
+	TInt instLen = TPtrC(KXMLEndCDataSection).Length()-1;
+
+	__ASSERT_DEBUG(iElementTag != NULL, Panic(ENullMemVarElementTag));
+	if(iElementTag->Right(instLen) == cdataEndSection.Left(instLen))
+		{
+		if(iElementTag->Left(instLen) == KXMLStartCDataSection)
+			endOfCData = ETrue;
+		}
+
+	return endOfCData;
+	}
+
+TInt CMDXMLParser::CheckForStartCData(const TDesC& aTextToCheck)
+	{
+	TInt index;
+	index = aTextToCheck.Find(KXMLStartCDataSection);
+	return index;
+	}
+
+TInt CMDXMLParser::FindDelimiter(TDesC& aDataToSearch, TDesC& aDelimiterToFind)
+	{
+	TInt currentOffset = 0;
+	TInt nextDelimiter = KErrNotFound;
+	TBool valid = EFalse;
+	TPtrC unsearchedData(aDataToSearch);
+
+	while (!valid && ((nextDelimiter = unsearchedData.Find(aDelimiterToFind)) != KErrNotFound))
+		{
+		// If this isn't the first time round the loop (When currentOffset == 0) we're moved
+		// our attention to the character after the delimiter we found, so add one to currentOffset
+		if(currentOffset != 0)
+			{
+			currentOffset += 1;
+			}
+		// We have a delimiter, add the position of this to currentOffset
+		currentOffset += nextDelimiter;
+
+		// Check whether this delimiter is in a CDataSection, it's valid if it isn't
+		TPtrC delimiterToCheck = aDataToSearch.Left(currentOffset);
+		valid = !InCDataSection(delimiterToCheck);
+
+		// Move on to the next section of text in case this one wasn't valid
+		unsearchedData.Set(aDataToSearch.Mid(currentOffset + 1));
+		}
+
+	if ((nextDelimiter == KErrNotFound) && (!valid))
+		{
+		return KErrNotFound;
+		}
+	else
+		{
+		return currentOffset;
+		}
+	}
+
+void CMDXMLParser::AddTextL(CMDXMLElement* aParentElement)
+	{
+	if ((aParentElement != NULL) && (iText != NULL))
+	// Add any buffered text to the parent element unless it contains only whitespace
+		{
+		// Strip off any leading whitespace
+		TInt stripCounter = 0;
+		
+		if (!iPreserve) // GmXML consumes whitespace characters 
+			{
+			TBool endOfWhitespace = EFalse;
+			while ((stripCounter < iText->Length()) && (!endOfWhitespace))
+				{
+				// If character is not 0x20 (space) and not between 0x09 and 0x0d 
+				// it isn't whitespace 
+				if( ((*iText)[stripCounter] != 0x20) &&
+					!((*iText)[stripCounter] >= 0x09 && (*iText)[stripCounter] <= 0x0d))
+					{
+					endOfWhitespace = ETrue;
+					}
+				else
+					{
+					stripCounter++;
+					}
+				}
+			}
+
+		HBufC* strippedText = TPtrC(iText->Right(iText->Length() - stripCounter)).AllocLC();
+
+		if (strippedText->Length() > 0)
+			// If there is anything left of the stripped text then entity convert and add it.
+			{
+			TPtr toConvert = strippedText->Des();
+			TInt error = iEntityConverter->EntityToTextL(toConvert);
+			if( error != KErrNone )
+				{
+				SetError(error, EXMLIndeterminate);
+				}
+			CMDXMLText* textElement = CMDXMLText::NewLC(iXMLDoc);
+			textElement->SetDataL(*strippedText);
+			CleanupStack::Pop(textElement);
+			TInt err = aParentElement->AppendChild(textElement);
+			if(err != KErrNone)
+				{
+				SetError(err, EXMLWorkable);
+				}			
+			}
+
+		CleanupStack::PopAndDestroy(strippedText);
+		iText->Des().Zero();
+		}
+	}
+
+TBool CMDXMLParser::InCDataSection(TDesC& aDataToSearch)
+	{
+	TBool inCDataSection = EFalse;
+	TInt startCData = CheckForStartCData(aDataToSearch);
+	TInt endCData = 0;
+
+	while ((startCData != KErrNotFound) && !inCDataSection)
+		{
+		// We only want to look for the end of the CDataSection in the part of
+		// aDataToSearch after the start of the CDataSection.  We know that the 
+		// first (TPtrC)KXMLStartCDataSection.Length() of the data we're looking
+		// at won't match because it's the start tag, but it's probably more 
+		// efficient to check the extra few characters than to work out the 
+		// length of the tag so we can ignore them.
+		startCData += endCData;
+		TPtrC afterStart = aDataToSearch.Mid(startCData);
+		endCData = afterStart.Find(KXMLEndCDataSection);
+		if (endCData == KErrNotFound)
+			{
+			// We haven't found a match for the start of the CDataSection so
+			// we must still be in it -> "<" is valid.
+			inCDataSection = ETrue;
+			}
+		else 
+			{
+			// We found a match for the start of the CDataSection.  Check to
+			// see if another one has started since then.
+			endCData += startCData;
+			TPtrC afterEnd = aDataToSearch.Mid(endCData);
+			startCData = CheckForStartCData(afterEnd);
+			}
+		}
+
+	return inCDataSection;
+	}
+
+TInt CMDXMLParser::ParseSingleAttributeL(TDes& aAttributeValue)
+	{
+	TInt error = KErrNone;
+	TInt beginSection = 0;
+	TInt endSection = aAttributeValue.Find(KXMLStartCDataSection);
+	
+	// We've found at least one CDataSection
+	while(endSection != KErrNotFound)
+		{
+		// Entity convert this plain text section
+		HBufC* textToConvert = TPtrC(aAttributeValue.Mid(beginSection, endSection)).AllocLC();
+		TPtr toConvert = textToConvert->Des();
+		error = iEntityConverter->EntityToTextL(toConvert);
+		aAttributeValue.Replace(beginSection, endSection, *textToConvert);
+		CleanupStack::PopAndDestroy(textToConvert);
+
+		// Move on our markers.  We start the new section at the end of the old one.
+		beginSection += endSection;
+		// The end of this new section is the end of the CDataSection
+		endSection = TPtrC(aAttributeValue.Mid(beginSection)).Find(KXMLEndCDataSection);
+
+		if(endSection != KErrNotFound)
+			{
+			// Now move on our markers again.  Start at the end of the CDataSection, 
+			// plus the length of the end tag, and continue to the beginning of the next one.
+			beginSection += endSection + TPtrC(KXMLEndCDataSection).Length();
+			endSection = TPtrC(aAttributeValue.Mid(beginSection)).Find(KXMLStartCDataSection);
+			}
+		else
+			{
+			// There's an unterminated CDataSection in our attribute
+			error = KErrXMLBadAttributeValue;
+			}
+		}
+
+	// There are no more CDataSections, entity convert the rest of the string
+	if(!error)
+		{
+		HBufC* textToConvert = TPtrC(aAttributeValue.Mid(beginSection)).AllocLC();
+		TPtr toConvert = textToConvert->Des();
+		error = iEntityConverter->EntityToTextL(toConvert);
+		aAttributeValue.Replace(beginSection, (aAttributeValue.Length()-beginSection), *textToConvert);
+		CleanupStack::PopAndDestroy(textToConvert);
+		}
+
+	return error;
+	}
+
+
+EXPORT_C void CMDXMLParser::PlaceholderForRemovedExport1(MMDXMLParserObserver* /*aParserObserver*/)
+	{
+	User::Panic(KLDRIMPORT, KLdrImportedOrdinalDoesNotExist);
+	}
+
+EXPORT_C void CMDXMLParser::PlaceholderForRemovedExport2(MMDXMLParserObserver* /*aParserObserver*/, MXMLDtd* /*aDtdRepresentation*/)
+	{
+	User::Panic(KLDRIMPORT, KLdrImportedOrdinalDoesNotExist);
+	}
+
+EXPORT_C void CMDXMLParser::PlaceholderForRemovedExport3()
+	{
+	User::Panic(KLDRIMPORT, KLdrImportedOrdinalDoesNotExist);
+	}
+
+// End of File