Click here to Skip to main content
15,895,667 members
Articles / Desktop Programming / MFC

A simple XML Parser

Rate me:
Please Sign up or sign in to vote.
4.69/5 (13 votes)
28 Jan 2002CPOL 362.7K   5.8K   70  
A class to read and write non validated XML files
// XML.cpp: implementation of the CXML class.
//
//////////////////////////////////////////////////////////////////////
/*
// History
29 Mar 2001 - Fixed error allowing xml declaration to be read as root element
		Fixed several memory leaks.
25 Aug 2000  - renamed helper class from pre XML notation of Param to XML Attribute convention
25 Aug 2000  - Added special funcitons to support standard names of ID and NAME.
20 July 2000 - Add Serialization to class to support embedding in binary applications.
20 July 2000 - Added copy functions
14 July 2000 - Modified read buffer to support large blocks of text and parameter fields.

*/

#include "stdafx.h"
#include "XML.h"

#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[]=__FILE__;
#define new DEBUG_NEW
#endif

// Implementation of the list of child Tagged Blocks 
//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////


CAttributeList::CAttributeList()
{

}

CAttributeList::~CAttributeList()
{
	CAttribute *d;
	while(this->GetCount()>0)
	{
		d=GetTail();
		RemoveTail();
		delete d;
	}
}

CAttribute::CAttribute()
{

}

CAttribute::~CAttribute()
{

}

CMabString CAttribute::GetAttributeText()
{
	return m_AttributeValue;
}

int CAttribute::GetAttributeInt()
{
	return m_AttributeValue.GetInt();
}

long CAttribute::GetAttributeLong()
{
	return m_AttributeValue.GetLong();
}

float CAttribute::GetAttributeFloat()
{
	return m_AttributeValue.GetFloat();
}

double CAttribute::GetAttributeDouble()
{
	return m_AttributeValue.GetDouble();
}

void CAttribute::SetAttribute(LPCTSTR Value)
{
	m_AttributeValue = Value;
}

void CAttribute::SetAttribute(int Value)
{
	CMabString sVal;
	sVal.PutInt(Value);
	SetAttribute(sVal);
}

void CAttribute::SetAttribute(long Value)
{
	CMabString sVal;
	sVal.PutLong(Value);
	SetAttribute(sVal);
}

void CAttribute::SetAttribute(float Value)
{
	CMabString sVal;
	sVal.PutFloat(Value);
	SetAttribute(sVal);
}

void CAttribute::SetAttribute(double Value)
{
	CMabString sVal;
	sVal.PutDouble(Value);
	SetAttribute(sVal);
}

CMabString CAttribute::GetField(char Delim, int FieldNum)
{
	CMabString sf;
	sf = m_AttributeValue.GetField(Delim,FieldNum);
	return sf;
}
CMabString CAttribute::StepField(char Delim)
{
	CMabString sf;
	sf = m_AttributeValue.StepField(Delim);
	return sf;
}
CMabString CAttribute::NextField(char Delim)
{
	CMabString sf;
	sf = m_AttributeValue.NextField(Delim);
	return sf;
}

int CAttribute::GetFieldCount(char Delim)
{
	return m_AttributeValue.GetFieldCount(Delim);
}

CAttribute* CAttribute::Copy()
{
	CAttribute* copy;
	copy = new CAttribute;
	copy->m_AttributeName = m_AttributeName;
	copy->m_AttributeValue = m_AttributeValue;
	return copy;
}

// Added Serialization to support imbedded application data. 20 July 2000
IMPLEMENT_SERIAL(CAttribute, CObject, 0);
void CAttribute::Serialize(CArchive &ar)
{
	if(ar.IsStoring())
	{
//		ar << m_AttributeName << m_AttributeValue;
	}
	else
	{
//		ar >> m_AttributeName >> m_AttributeValue;
	}
}
//&&&&&&&&&&&&&%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

// Implementation of the list of child Tagged Blocks 
//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////

CXMLList::CXMLList()
{

}

CXMLList::~CXMLList()
{

}

//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////

CXML::CXML()
{
	m_EOF=FALSE;
	m_AttributePos=NULL;
	m_ChildPos=NULL;
	m_TextBlock="";
	m_TagName = "";
	m_TextBlock = "";
	m_FileName = "";
}

CXML::~CXML()
{
	ResetContents();
}

void CXML::AddChild(CXML *Child)
{
	if(Child==NULL) return;
	if(Child->m_TagName.GetLength()<1)
		{
		AfxMessageBox("No Valid Tag Name");
		}
	m_ChildList.AddTail(Child);
}

void CXML::AddAttr(CAttribute *Attribute)
{
	POSITION pos;
	CMabString NewName = Attribute->m_AttributeName;
	CAttribute *OldAttribute;
	if(m_AttributeList.GetCount()<1)
	{
		m_AttributeList.AddHead(Attribute);
		return;
	}
	pos = m_AttributeList.GetHeadPosition();
	while(pos!=NULL)
	{
		OldAttribute = m_AttributeList.GetAt(pos);
		if(NewName.CompareNoCase(OldAttribute->m_AttributeName)==0)
		{
			OldAttribute->m_AttributeValue = Attribute->m_AttributeValue;
			delete Attribute;
			return;
		}
		else if(NewName<=OldAttribute->m_AttributeName)
		{
			m_AttributeList.InsertBefore(pos,Attribute);
			return;
		}
		m_AttributeList.GetNext(pos);
	}
	m_AttributeList.AddTail(Attribute);
	return;
}

void CXML::AddToText(CString Text)
{
	m_TextBlock+=Text;
}

// Added Copy ability to support versioning - 20 July 2000
CXML* CXML::Copy()
{
	CXML* copy;
	copy = new CXML;
	copy->m_FileName = m_FileName;
	copy->m_TagName = m_TagName;
	copy->m_TextBlock = m_TextBlock;

	CAttribute *p,*np;
	p = GetFirstAttribute();
	while(p!=NULL)
		{
		np=p->Copy();
		copy->m_AttributeList.AddTail(np);
		p = GetNextAttribute();
		}
	CXML *x,*nx;
	x = GetFirstChild();
	while(x!=NULL)
		{
		nx = x->Copy();
		copy->m_ChildList.AddTail(nx);
		x=GetNextChild();
		}
	return copy;
}

CXML* CXML::GetChildAt(int index)
{
	m_ChildPos = m_ChildList.FindIndex(index);
	if(m_ChildPos==NULL) return NULL;
	return m_ChildList.GetAt(m_ChildPos);
}

long CXML::GetChildCount()
{
	return m_ChildList.GetCount();
}

CXML* CXML::GetCurrentChild()
{
	if(m_ChildPos==NULL) return NULL;
	return m_ChildList.GetAt(m_ChildPos);
}

CAttribute* CXML::GetCurrentAttribute()
{
	if(m_AttributePos==NULL) return NULL;
	return m_AttributeList.GetAt(m_AttributePos);
}

CMabString CXML::GetField(char Delim, int FieldNum)
{
	CMabString sf;
	sf = m_TextBlock.GetField(Delim,FieldNum);
	return sf;
}

int CXML::GetFieldCount(char Delim)
{
	return m_TextBlock.GetFieldCount(Delim);
}

// 14 July 2000 - Modified line buffer to enhance larger blocks of data.
CMabString CXML::GetNextLine(CFile* pFile)
{
	CMabString Line="";
	CString ReadLine;
	LPSTR LineBuffer;
	long NRead,pos1,pos2,pos3,TestChar;
	DWORD fpos;
	unsigned char lastchar = ' ';
	BOOL isEOF=FALSE;
	BOOL InQuote=FALSE;
	// 
	// We are inside of the XML block
	// we first need to find the next block to handle.
	// If there is text data we need to append it to the text data buffer.
	// for this element
	while(!isEOF && lastchar!='<')
	{
		fpos = pFile->GetPosition();
		ReadLine.Empty();
		LineBuffer = ReadLine.GetBuffer(1000);
		NRead = pFile->Read(LineBuffer,sizeof(char)*1000);
		ReadLine.ReleaseBuffer();
		if(NRead<1)
		{
			m_EOF=TRUE;
			return Line;
		}
		pos1 = ReadLine.Find('<',0);
		pos2 = ReadLine.Find('"',0);
		pos3 = ReadLine.Find('>',0);
		if(InQuote)
		{
			if(pos2>=0)
			{
				m_TextBlock+= ReadLine.Left(pos2+1);
				pFile->Seek(fpos+pos2+1,CFile::begin);
				InQuote=FALSE;
			}
			m_TextBlock+=ReadLine;
		}
		else
		{
			TestChar = ReadLine.GetAt(0);
			if(pos1==0)
			{
				lastchar='<';
				pFile->Seek(fpos+1,CFile::begin);
			}
			else if(pos1==1 && TestChar<33)
			{
				if(pos3<pos2 && pos3>2)
				{
					pFile->Seek(fpos+pos3+1,CFile::begin);
					Line = ReadLine.Mid(pos1,pos3);
					return Line;
				}
				lastchar='<';
				pFile->Seek(fpos+2,CFile::begin);
			}
			else if(pos2==0)
			{
				InQuote=TRUE;
				m_TextBlock+='"';
				pFile->Seek(fpos+1,CFile::begin);
			}
			else if(pos2>=0 && (pos2<pos1 || pos1<0) )
			{
				m_TextBlock+= ReadLine.Left(pos2+1);
				pFile->Seek(fpos+pos2+1,CFile::begin);
				InQuote=TRUE;
			}
			else if(pos1>=0)
			{
				m_TextBlock+= ReadLine.Left(pos1);
				pFile->Seek(fpos+pos1,CFile::begin);
			}
			else
			{
				if(TestChar==10)
					m_TextBlock+= ReadLine.Right(ReadLine.GetLength()-1);
				else
					m_TextBlock+= ReadLine;
			}
		}
	}
	// We are now inside of the block to read.
	// find the end.
	Line ="<";
	int InQuote2=1;
	while(!isEOF)
	{
		fpos = pFile->GetPosition();
		ReadLine.Empty();
		LineBuffer = ReadLine.GetBuffer(1000);
		NRead = pFile->Read(LineBuffer,sizeof(char)*1000);
		ReadLine.ReleaseBuffer();
		if(NRead<1)
		{
			m_EOF=TRUE;
			return Line;
		}
		pos1 = ReadLine.Find('<',0);
		pos2 = ReadLine.Find('"',0);
		pos3 = ReadLine.Find('>',0);
		if(InQuote2<0)
		{
			if(pos2<0)
			{
				Line+=ReadLine;
			}
			else
			{
				InQuote2*=-1;
				Line+=ReadLine.Left(pos2+1);
				pFile->Seek(fpos+pos2+1,CFile::begin);
			}
		}
		else
		{
			if(pos2>-1 && pos3>-1)
			{
				if(pos3<pos2)
				{
					Line+=ReadLine.Left(pos3+1);
					pFile->Seek(fpos+pos3+1,CFile::begin);
					return Line;
				}
				InQuote2*=-1;
				Line+=ReadLine.Left(pos2+1);
				pFile->Seek(fpos+pos2+1,CFile::begin);
			}
			else if(pos3>-1)
			{
					Line+=ReadLine.Left(pos3+1);
					pFile->Seek(fpos+pos3+1,CFile::begin);
					return Line;
			}
			else if(pos2>-1)
			{
				InQuote2*=-1;
				Line+=ReadLine.Left(pos2+1);
				pFile->Seek(fpos+pos2+1,CFile::begin);
			}
			else
			{
				Line+=ReadLine;
			}

		}
	}
	if(isEOF)
		m_EOF=TRUE;
	else
		Line+=">";
	return Line;
}

// Attributeeter functions
CMabString CXML::GetAttrText(LPCTSTR Name)
{
	CMabString TestName = Name;
	POSITION pos;
	CAttribute *Attribute;
	if(m_AttributeList.GetCount()<1) return "";
	pos = m_AttributeList.GetHeadPosition();
	while(pos!=NULL)
	{
		Attribute = m_AttributeList.GetNext(pos);
		if(TestName==Attribute->m_AttributeName)
		{
			TestName = Attribute->m_AttributeValue;
			return TestName;
		}
	}
	TestName="";
	return TestName;
}

CMabString CXML::GetAttrTextUS(LPCTSTR Name)
{
	CMabString TestName = Name;
	POSITION pos;
	CAttribute *Attribute;
	if(m_AttributeList.GetCount()<1) return "";
	pos = m_AttributeList.GetHeadPosition();
	while(pos!=NULL)
	{
		Attribute = m_AttributeList.GetNext(pos);
		if(TestName==Attribute->m_AttributeName)
		{
			TestName = Attribute->m_AttributeValue;
			if(TestName.GetLength()<1) TestName="0.00";
			return TestName;
		}
	}
	TestName="0.00";
	return TestName;
}


int CXML::GetAttrInt(LPCTSTR Name)
{
	CMabString sVal = GetAttrText(Name);
	return sVal.GetInt();
}

long CXML::GetAttrLong(LPCTSTR Name)
{
	CMabString sVal = GetAttrText(Name);
	return sVal.GetLong();
}

float CXML::GetAttrFloat(LPCTSTR Name)
{
	CMabString sVal = GetAttrText(Name);
	return sVal.GetFloat();
}

double CXML::GetAttrDouble(LPCTSTR Name)
{
	CMabString sVal = GetAttrText(Name);
	return sVal.GetDouble();
}

CAttribute* CXML::GetFirstAttribute()
{
	if(m_AttributeList.GetCount()<1) 
	{
		m_AttributePos=NULL;
		return NULL;
	}
	m_AttributePos = m_AttributeList.GetHeadPosition();
	return m_AttributeList.GetAt(m_AttributePos);
}

CAttribute* CXML::GetNextAttribute()
{
	if(m_AttributePos==NULL) return NULL;
	m_AttributeList.GetNext(m_AttributePos);
	if(m_AttributePos==NULL) return NULL;
	return m_AttributeList.GetAt(m_AttributePos);
}

CAttribute* CXML::GetPrevAttribute()
{
	if(m_AttributePos==NULL) return NULL;
	m_AttributeList.GetPrev(m_AttributePos);
	if(m_AttributePos==NULL) return NULL;
	return m_AttributeList.GetAt(m_AttributePos);
}


CAttribute* CXML::GetLastAttribute()
{
	if(m_AttributeList.GetCount()<1) 
	{
		m_AttributePos=NULL;
		return NULL;
	}
	m_AttributePos = m_AttributeList.GetTailPosition();
	return m_AttributeList.GetAt(m_AttributePos);
}


void CXML::SetCurrentAttr(CAttribute *Attribute)
{
	if(m_AttributePos==NULL) return;
	m_AttributeList.SetAt(m_AttributePos,Attribute);
}

BOOL CXML::IsStartBlock(CMabString Line, CMabString TagName)
{
	int endpos;
	if(Line[0]!=60) return FALSE;
	if(TagName.GetLength()<1) return TRUE;
	endpos = Line.Find(' ');
	CMabString LineName;
	if(endpos<0)
	{
		endpos = Line.GetLength();
		if(Line[endpos-1]!='>') return FALSE;
		if(Line[endpos-2]=='/') endpos--;
		LineName = Line.Mid(1,endpos-2);
	}
	else
		LineName = Line.Mid(1,endpos-1);
	if(LineName!=TagName) return FALSE;
	return TRUE;
}

void CXML::ParseBlock(CMabString Line, CFile *pFile)
{
	int LinePos,EndPos;
	ResetContents();
//	m_ChildList.RemoveAll();
//	m_AttributeList.RemoveAll();
	if(Line.GetLength()<1) Line=" ";//return;// mab 12 May 2000
	if(Line[0]!=60) return;
	LinePos = Line.Find(' ');
	if(LinePos<0)
	{
		LinePos = Line.GetLength()-1;
	}
	m_TagName = Line.Mid(1,LinePos-1);
	if(m_TagName[0]=='/') AfxMessageBox("Warning Element Terminator exists without matching initial Block "+m_TagName);
	EndPos = Line.GetLength();
	if(Line[EndPos-1]!='>') AfxMessageBox("Warning Tag Block is Incomplete");
	Line.SetAt(EndPos-1,' ');
	BOOL Children=TRUE;
	if(Line[EndPos-2]=='/')
	{
		Children=FALSE;
		Line.SetAt(EndPos-2,' ');
	}
	Line = Line.Right(Line.GetLength() - m_TagName.GetLength()-1);
	Line.Trim();
	CMabString pname,pval;
	while(Line.GetLength()>1)
	{
		LinePos = Line.Find('=');
		if(LinePos<1) break;
		pname = Line.Left(LinePos);
		pname.Trim();
		LinePos = Line.Find('"');
		Line = Line.Right(Line.GetLength()-LinePos-1);
		LinePos = Line.Find('"');
		pval = Line.Left(LinePos);
		SetAttr((LPCTSTR)pname,(LPCTSTR)pval);
		Line = Line.Right(Line.GetLength()-LinePos-1);
		Line.TrimLeft();
	}
	if(!Children) return;
	Line.Empty();
	Line = GetNextLine(pFile);
	CMabString LastLine;
	LastLine = "</";
	LastLine += m_TagName;
	LastLine += '>';
	CXML *child;
	while(Line!=LastLine && !m_EOF)
	{
		if(Line[1]!='!' && Line[1]!='?')
		{
		child = new CXML;
		child->ParseBlock(Line,pFile);
		if(child->m_TagName.GetLength()>1) AddChild(child);
		else delete child;
		}
		Line = GetNextLine(pFile);
	}
	// Test TextBlock for extra line feed.
	int TestChar,lenpos;
	lenpos = m_TextBlock.GetLength()-1;
	if(lenpos>=0)
		{
		TestChar = m_TextBlock.GetAt(lenpos);
		if(TestChar==10) m_TextBlock.SetAt(lenpos,(char)32);
		}
}

void CXML::RemoveCurrentAttr()
{
	if(m_AttributePos==NULL) return;
	CAttribute *Attr;
	POSITION pos = m_AttributePos;
	Attr = m_AttributeList.GetAt(m_AttributePos);
	m_AttributeList.GetNext(pos);
	if(pos==NULL)
	{
		m_AttributeList.RemoveAt(m_AttributePos);
		GetLastAttribute();
	}
	else
	{
		m_AttributeList.RemoveAt(m_AttributePos);
		m_AttributePos = pos;
	}
	if(Attr!=NULL) delete Attr; // mab 23 mar 2001
}

void CXML::SetAttr(LPCTSTR Name, LPCTSTR Value)
{
	POSITION pos;
	CMabString NewName = Name;
	CAttribute *Attribute,*OldAttribute;
	Attribute = new CAttribute;
	Attribute->m_AttributeName = Name;
	Attribute->m_AttributeValue = Value;
	if(m_AttributeList.GetCount()<1)
	{
		m_AttributeList.AddHead(Attribute);
		return;
	}
	pos = m_AttributeList.GetHeadPosition();
	while(pos!=NULL)
	{
		OldAttribute = m_AttributeList.GetAt(pos);
		if(NewName==OldAttribute->m_AttributeName)
		{
			OldAttribute->m_AttributeValue = Value;
			delete Attribute;
			return;
		}
		else if(NewName<=OldAttribute->m_AttributeName)
		{
			m_AttributeList.InsertBefore(pos,Attribute);
			return;
		}
		m_AttributeList.GetNext(pos);
	}
	m_AttributeList.AddTail(Attribute);
	return;
}

void CXML::SetAttr(LPCTSTR Name, int Value)
{
	CMabString sVal;
	sVal.PutInt(Value);
	SetAttr(Name,sVal);
}

void CXML::SetAttr(LPCTSTR Name, long Value)
{
	CMabString sVal;
	sVal.PutLong(Value);
	SetAttr(Name,sVal);
}

void CXML::SetAttr(LPCTSTR Name, float Value)
{
	CMabString sVal;
	sVal.PutFloat(Value);
	SetAttr(Name,sVal);
}

void CXML::SetAttr(LPCTSTR Name, double Value)
{
	CMabString sVal;
	sVal.PutDouble(Value);
	SetAttr(Name,sVal);
}

// Child functions
CXML* CXML::GetFirstChild()
{
	if(m_ChildList.GetCount()<1)
	{
		m_ChildPos=NULL;
		return NULL;
	}
	m_ChildPos = m_ChildList.GetHeadPosition();
	if(m_ChildPos==NULL) return NULL;
	return m_ChildList.GetAt(m_ChildPos);
}

CXML* CXML::GetNextChild()
{
	if(m_ChildPos==NULL) return NULL;
	m_ChildList.GetNext(m_ChildPos);
	if(m_ChildPos==NULL) return NULL;
	return m_ChildList.GetAt(m_ChildPos);
}

CXML* CXML::GetPrevChild()
{
	if(m_ChildPos==NULL) return NULL;
	m_ChildList.GetPrev(m_ChildPos);
	if(m_ChildPos==NULL) return NULL;
	return m_ChildList.GetAt(m_ChildPos);
}


CXML* CXML::GetLastChild()
{
	if(m_ChildList.GetCount()<1)
	{
		m_ChildPos=NULL;
		return NULL;
	}
	m_ChildPos = m_ChildList.GetTailPosition();
	return m_ChildList.GetAt(m_ChildPos);
}


void CXML::SetCurrentChild(CXML *Child)
{
	if(m_ChildPos==NULL) return;
	m_ChildList.SetAt(m_ChildPos,Child);
}

void CXML::RemoveCurrentChild()
{
	if(m_ChildPos==NULL) return;
	CXML *x;
	x = m_ChildList.GetAt(m_ChildPos);
	POSITION pos = m_ChildPos;
	m_ChildList.GetNext(pos);
	if(pos==NULL)
	{
		m_ChildList.RemoveAt(m_ChildPos);
		GetLastChild();
	}
	else
	{
		m_ChildList.RemoveAt(m_ChildPos);
		m_ChildPos = pos;
	}
	delete x;
}

void CXML::ResetContents()
{
	CAttribute *p;
	while(m_AttributeList.GetCount()>0)
		{
		p=m_AttributeList.GetTail();
		m_AttributeList.RemoveTail();
		delete p;
		}
	CXML *x;
	while(m_ChildList.GetCount()>0)
		{
		x=m_ChildList.GetTail();
		m_ChildList.RemoveTail();
		delete x;
		}
	m_TextBlock.Empty();
}

long CXML::GetAttrCount()
{
	return m_AttributeList.GetCount();
}

void CXML::XMLWrite(CFile *pFile)
{
	CMabString Line;
	CMabString Addition;
	Line = "<";
	Line += m_TagName;
	CAttribute *Attribute;
	Attribute = GetFirstAttribute();
	while(Attribute!=NULL)
	{
		Addition = " ";
		Addition += Attribute->m_AttributeName;
		Addition += "=\"";
		Attribute->m_AttributeValue.Trim();
		Addition += Attribute->m_AttributeValue;
		Addition += "\"";
		Line += Addition;
		Attribute = GetNextAttribute();
	}
	if((m_ChildList.GetCount()<1) && (m_TextBlock.GetLength()<1))
	{
		Addition = "/>\n";
		Line += Addition;
		pFile->Write(Line,Line.GetLength());
		return;
	}
	Addition = ">\n";
	Line += Addition;
	pFile->Write(Line,Line.GetLength());
	// Now the children
	if(m_TextBlock.GetLength()>0)
		{
		m_TextBlock+='\n';
		pFile->Write(m_TextBlock,m_TextBlock.GetLength());
		m_TextBlock = m_TextBlock.Left(m_TextBlock.GetLength()-1);
		}
	CXML *child;
	child = GetFirstChild();
	while(child!=NULL)
	{
		child->XMLWrite(pFile);
		child = GetNextChild();
	}
	// and the terminator
	Line = "</";
	Line += m_TagName;
	Line += ">\n";
	pFile->Write(Line,Line.GetLength());
	return;
}

BOOL CXML::Open(LPCTSTR InFilter, LPCTSTR TagName)
{
	CFile in;
	CMabString defext;
	CMabString filter = InFilter;
	int pos1 = filter.Find('(');
	int pos2 = filter.Find(')');
	defext = filter.Mid(pos1+1,pos2-pos1-1);
	CFileDialog myfile(TRUE,NULL,defext,NULL,filter,NULL);
	if(myfile.DoModal()==IDOK)
	{
		in.Open(myfile.GetPathName(),CFile::modeRead);
		XMLRead(&in,TagName);
		in.Close();
		m_FileName = myfile.GetPathName();
		return TRUE;
	}
	return FALSE;
}

BOOL CXML::SaveAs(LPCTSTR InFilter)
{
	CFile in;
	CMabString defext;
	CMabString filter = InFilter;
	int pos1 = filter.Find('(');
	int pos2 = filter.Find(')');
	defext = filter.Mid(pos1+1,pos2-pos1-1);
	CFileDialog myfile(FALSE,NULL,defext,NULL,filter,NULL);
	if(myfile.DoModal()==IDOK)
	{
		in.Open(myfile.GetPathName(),CFile::modeCreate|CFile::modeWrite);
		XMLWrite(&in);
		in.Close();
		m_FileName = myfile.GetPathName();
		return TRUE;
	}
	m_FileName = "";
	return FALSE;
}

BOOL CXML::Save(LPCTSTR InFilter)
{
	if(m_FileName.GetLength()<1)
	{
		return SaveAs(InFilter);
	}
	CFile in;
	in.Open(m_FileName,CFile::modeCreate|CFile::modeWrite);
	XMLWrite(&in);
	in.Close();
	return TRUE;
}

void CXML::SetTagName(LPCTSTR NewName)
{
//	CString line;
//	line.Format("Setting tag from %s to %s",(LPCTSTR)m_TagName,NewName);
//	AfxMessageBox(line);
	m_TagName = NewName;
}

LPCTSTR CXML::GetTagName()
{
	return (LPCTSTR)m_TagName;
}

CMabString CAttribute::GetAttributeName()
{
	return m_AttributeName;
}

void CXML::SetText(LPCTSTR NewText)
{
	m_TextBlock = NewText;
}

LPCTSTR CXML::GetText()
{
	return (LPCTSTR)m_TextBlock;
}


void CXML::InsertChild(CXML *Child)
{
	if(m_ChildPos==NULL) 
		m_ChildList.AddTail(Child);
	else
		m_ChildList.InsertBefore(m_ChildPos,Child);
	return;
}

CMabString CXML::GetTag()
{
	return m_TagName;
}

CMabString CXML::GetAttr(LPCTSTR Name)
{
	CMabString TestName = Name;
	POSITION pos;
	CAttribute *Attribute;
	if( (m_AttributeList.GetCount()) < 1) return "";
	pos = m_AttributeList.GetHeadPosition();
	while(pos!=NULL)
	{
		Attribute = m_AttributeList.GetNext(pos);
		if(TestName==Attribute->m_AttributeName) return Attribute->m_AttributeValue;
	}
	return "";
}

void CXML::SetAttrUS(LPCSTR Name, double value)
{
	CString str;
	str.Format("%.2f",value+0.001);
	SetAttr(Name,str);
}

void CXML::RemoveAttr(LPCSTR Name)
{
	CMabString TestName = Name;
	CAttribute *Attribute;
	if( (m_AttributeList.GetCount()) < 1) return;
	Attribute = GetFirstAttribute();
	while(Attribute!=NULL)
	{
		if(TestName==Attribute->m_AttributeName)
		{
			RemoveCurrentAttr();
			return;
		}
		Attribute = GetNextAttribute();
	}
}

BOOL CXML::IsAlpha(char c)
{
	if(c>64 && c<91) return TRUE;
	if(c>96 && c<123) return TRUE;
	return FALSE;
}

CMabString CXML::StepField(char Delim)
{
	CMabString sf;
	sf = m_TextBlock.StepField(Delim);
	return sf;
}

CMabString CXML::FirstField(char Delim)
{
	CMabString sf;
	sf = m_TextBlock.FirstField(Delim);
	return sf;
}

CMabString CXML::NextField(char Delim)
{
	CMabString sf;
	sf = m_TextBlock.NextField(Delim);
	return sf;
}


BOOL CXML::XMLRead(CFile *pFile, LPCTSTR TagName)
{
	m_TagName = TagName;
	CMabString NextLine;
	NextLine = GetNextLine(pFile);
	// Skip header
	while(NextLine[1]==63) NextLine=GetNextLine(pFile);
	while (!m_EOF && !IsStartBlock(NextLine,TagName))
	{
		NextLine = GetNextLine(pFile);
	}
	m_TextBlock.Empty();
	if(m_EOF) return FALSE;
	else ParseBlock(NextLine,pFile);
	return TRUE;
}

CAttribute* CXML::GetAttributePointer(LPCTSTR Name)
{
	CMabString TestName = Name;
	POSITION pos;
	CAttribute *Attribute;
	if( (m_AttributeList.GetCount()) < 1) return NULL;
	pos = m_AttributeList.GetHeadPosition();
	while(pos!=NULL)
	{
		Attribute = m_AttributeList.GetNext(pos);
		if(TestName==Attribute->m_AttributeName) return Attribute;
	}
	return NULL;
}

IMPLEMENT_SERIAL(CXML, CObject, 0);
void CXML::Serialize(CArchive &ar)
{
	if(ar.IsStoring())
	{
//		ar << m_TagName << m_TextBlock;
	}
	else
	{
//		ar >> m_TagName >> m_TextBlock;
	}
	m_AttributeList.Serialize(ar);
	m_ChildList.Serialize(ar);
}


CXML* CXML::RemoveAt(int position)
{
	CXML* child;
	child = GetChildAt(position);
	if(child!=NULL) RemoveCurrentChild();
	return child;
}

void CXML::InsertChildBefore(int ZeroBasedPos, CXML *Child)
{
	m_ChildList.InsertBefore(m_ChildList.FindIndex(ZeroBasedPos),Child);
}

void CAttribute::AddToText(CString text)
{
	m_AttributeValue+=text;
}


// Added 25 Aug 2000 to handle the special case of the ID and Name attributes

CXML* CXML::FindByID(long id)
{
	CXML *child,*grand;
	child=GetFirstChild();
	while(child!=NULL)
	{
		if(child->GetAttrLong("ID")==id) return child;
		grand=child->FindByID(id);
		if(grand!=NULL) return grand;
		child=GetNextChild();
	}
//	AfxMessageBox("XML ID NOT FOUND");
	return child;
}

CXML* CXML::FindByName(CString name)
{
	CXML *child,*grand;
	child=GetFirstChild();
	while(child!=NULL)
	{
		if(name.CompareNoCase(child->GetAttrText("NAME"))==0) return child;
		grand=child->FindByName(name);
		if(grand!=NULL) return grand;
		child=GetNextChild();
	}
	return child;
}

void CXML::SortByName()
{
	CXML *c1,*c2;
	CString n1;
restart:
	c1=GetFirstChild();
	c2=GetNextChild();
	while(c2!=NULL)
	{
		n1=c1->GetAttrText("NAME");
		if(n1.CompareNoCase(c2->GetAttrText("NAME"))>0)
		{
			RemoveCurrentChild();
			AddChildByName(c2);
			goto restart;
		};
		c1=c2;
		c2=GetNextChild();
	}
	return;

}

void CXML::SortById()
{
	CXML *c1,*c2;
	long n1;
restart:
	c1=GetFirstChild();
	c2=GetNextChild();
	while(c2!=NULL)
	{
		n1=c1->GetAttrLong("ID");
		if(n1>c2->GetAttrLong("ID"))
		{
			RemoveCurrentChild();
			AddChildById(c2);
			goto restart;
		};
		c1=c2;
		c2=GetNextChild();
	}
	return;

}

void CXML::AddChildByName(CXML *newchild)
{
	CXML *child;
	CString n1;
	child=GetFirstChild();
	while(child!=NULL)
	{
		n1=child->GetAttrText("NAME");
		if(n1.CompareNoCase(newchild->GetAttrText("NAME"))>0)
		{
			InsertChild(newchild);
			return;
		}
		child=GetNextChild();
	}
	AddChild(newchild);
	return;
}

void CXML::AddChildById(CXML *newchild)
{
	CXML *child;
	long id;
	id=newchild->GetAttrLong("ID");
	child=GetFirstChild();
	while(child!=NULL)
	{
		if(id<child->GetAttrLong("ID"))
		{
			InsertChild(newchild);
			return;
		}
		child=GetNextChild();
	}
	AddChild(newchild);
	return;
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Retired
United States United States
Began programming in 1968 on a Wang 720. Move to Fortran and began developing FEM (finite element model) applications on an IBM 360 in 1973. Developed custom FEM editors for most of my career until 1995.

Comments and Discussions