new element parsing. reasonable set of test cases.

This commit is contained in:
Lee Thomason 2012-01-24 16:01:51 -08:00
parent 5492a1c705
commit 67d6131d74
4 changed files with 98 additions and 72 deletions

View File

@ -186,6 +186,7 @@ char* XMLBase::Identify( XMLDocument* document, char* p, XMLNode** node )
XMLNode::XMLNode( XMLDocument* doc ) : XMLNode::XMLNode( XMLDocument* doc ) :
document( doc ), document( doc ),
parent( 0 ), parent( 0 ),
isTextParent( false ),
firstChild( 0 ), lastChild( 0 ), firstChild( 0 ), lastChild( 0 ),
prev( 0 ), next( 0 ) prev( 0 ), next( 0 )
{ {
@ -242,6 +243,9 @@ XMLNode* XMLNode::InsertEndChild( XMLNode* addThis )
addThis->prev = 0; addThis->prev = 0;
addThis->next = 0; addThis->next = 0;
} }
if ( addThis->ToText() ) {
SetTextParent();
}
return addThis; return addThis;
} }
@ -254,6 +258,25 @@ void XMLNode::Print( FILE* fp, int depth )
} }
char* XMLNode::ParseDeep( char* p )
{
while( p && *p ) {
XMLNode* node = 0;
p = Identify( document, p, &node );
if ( p && node ) {
p = node->ParseDeep( p );
// FIXME: is it the correct closing element?
if ( node->IsClosingElement() ) {
delete node;
return p;
}
this->InsertEndChild( node );
}
}
return 0;
}
void XMLNode::PrintSpace( FILE* fp, int depth ) void XMLNode::PrintSpace( FILE* fp, int depth )
{ {
for( int i=0; i<depth; ++i ) { for( int i=0; i<depth; ++i ) {
@ -276,7 +299,8 @@ char* XMLText::ParseDeep( char* p )
void XMLText::Print( FILE* cfile, int depth ) void XMLText::Print( FILE* cfile, int depth )
{ {
fprintf( cfile, value.GetStr() ); const char* v = value.GetStr();
fprintf( cfile, v );
} }
@ -350,23 +374,10 @@ XMLElement::~XMLElement()
} }
char* XMLElement::ParseDeep( char* p ) char* XMLElement::ParseAttributes( char* p, bool* closedElement )
{ {
// Read the element name.
p = SkipWhiteSpace( p );
if ( !p ) return 0;
const char* start = p; const char* start = p;
*closedElement = false;
// The closing element is the </element> form. It is
// parsed just like a regular element then deleted from
// the DOM.
if ( *p == '/' ) {
closing = true;
++p;
}
p = ParseName( p, &name );
if ( name.Empty() ) return 0;
// Read the attributes. // Read the attributes.
while( p ) { while( p ) {
@ -400,6 +411,7 @@ char* XMLElement::ParseDeep( char* p )
document->SetError( XMLDocument::ERROR_PARSING_ELEMENT, start, p ); document->SetError( XMLDocument::ERROR_PARSING_ELEMENT, start, p );
return 0; return 0;
} }
*closedElement = true;
return p+2; // done; sealed element. return p+2; // done; sealed element.
} }
// end of the tag // end of the tag
@ -412,38 +424,47 @@ char* XMLElement::ParseDeep( char* p )
return 0; return 0;
} }
} }
return p;
}
while( p && *p ) {
XMLNode* node = 0;
p = Identify( document, p, &node );
if ( p && node ) {
p = node->ParseDeep( p );
XMLElement* element = node->ToElement(); //
if ( element && element->Closing() ) { // <ele></ele>
if ( StringEqual( element->Name(), this->Name() ) ) { // <ele>foo<b>bar</b></ele>
// All good, this is closing tag. //
delete node; char* XMLElement::ParseDeep( char* p )
} {
else { // Read the element name.
document->SetError( XMLDocument::ERROR_PARSING_ELEMENT, start, p ); p = SkipWhiteSpace( p );
delete node; if ( !p ) return 0;
p = 0; const char* start = p;
}
return p; // The closing element is the </element> form. It is
} // parsed just like a regular element then deleted from
else { // the DOM.
this->InsertEndChild( node ); if ( *p == '/' ) {
} closing = true;
} ++p;
} }
return 0;
p = ParseName( p, &name );
if ( name.Empty() ) return 0;
bool elementClosed=false;
p = ParseAttributes( p, &elementClosed );
if ( !p || !*p || elementClosed || closing )
return p;
p = XMLNode::ParseDeep( p );
return p;
} }
void XMLElement::Print( FILE* cfile, int depth ) void XMLElement::Print( FILE* cfile, int depth )
{ {
PrintSpace( cfile, depth ); if ( !parent || !parent->IsTextParent() ) {
PrintSpace( cfile, depth );
}
fprintf( cfile, "<%s", Name() ); fprintf( cfile, "<%s", Name() );
for( XMLAttribute* attrib=rootAttribute; attrib; attrib=attrib->next ) { for( XMLAttribute* attrib=rootAttribute; attrib; attrib=attrib->next ) {
@ -452,38 +473,39 @@ void XMLElement::Print( FILE* cfile, int depth )
} }
if ( firstChild ) { if ( firstChild ) {
// fixme: once text is on, it should stay on, and not use newlines.
bool useNewline = firstChild->ToText() == 0;
fprintf( cfile, ">", Name() ); fprintf( cfile, ">", Name() );
if ( useNewline ) fprintf( cfile, "\n" ); if ( !IsTextParent() ) {
fprintf( cfile, "\n" );
}
for( XMLNode* node=firstChild; node; node=node->next ) { for( XMLNode* node=firstChild; node; node=node->next ) {
node->Print( cfile, depth+1 ); node->Print( cfile, depth+1 );
} }
fprintf( cfile, "</%s>\n", Name() ); fprintf( cfile, "</%s>", Name() );
// fixme: see note above if ( !IsTextParent() ) {
//if ( useNewline ) fprintf( cfile, "\n" ); fprintf( cfile, "\n" );
}
} }
else { else {
fprintf( cfile, "/>\n" ); fprintf( cfile, "/>" );
if ( !IsTextParent() ) {
fprintf( cfile, "\n" );
}
} }
} }
// --------- XMLDocument ----------- // // --------- XMLDocument ----------- //
XMLDocument::XMLDocument() : XMLDocument::XMLDocument() :
XMLNode( this ),
charBuffer( 0 ) charBuffer( 0 )
{ {
root = new XMLNode( this );
} }
XMLDocument::~XMLDocument() XMLDocument::~XMLDocument()
{ {
delete root;
delete charBuffer;
} }
@ -493,25 +515,21 @@ bool XMLDocument::Parse( const char* p )
charBuffer = CharBuffer::Construct( p ); charBuffer = CharBuffer::Construct( p );
XMLNode* node = 0; XMLNode* node = 0;
// fixme: clean up char* q = ParseDeep( charBuffer->mem );
char* q = Identify( this, charBuffer->mem, &node ); return true;
while ( node ) {
root->InsertEndChild( node );
q = node->ParseDeep( q );
node = 0;
if ( q && *q ) {
q = Identify( this, q, &node );
}
}
return false;
} }
void XMLDocument::Print( FILE* fp, int depth ) void XMLDocument::Print( FILE* fp, int depth )
{ {
for( XMLNode* node = root->firstChild; node; node=node->next ) { for( XMLNode* node = firstChild; node; node=node->next ) {
node->Print( fp, depth ); node->Print( fp, depth );
} }
} }
void XMLDocument::SetError( int error, const char* str1, const char* str2 )
{
printf( "ERROR: id=%d '%s' '%s'\n", error, str1, str2 );
}

View File

@ -106,7 +106,6 @@ protected:
char* Identify( XMLDocument* document, char* p, XMLNode** node ); char* Identify( XMLDocument* document, char* p, XMLNode** node );
}; };
class XMLNode : public XMLBase class XMLNode : public XMLBase
{ {
friend class XMLDocument; friend class XMLDocument;
@ -121,7 +120,12 @@ public:
virtual XMLText* ToText() { return 0; } virtual XMLText* ToText() { return 0; }
virtual XMLComment* ToComment() { return 0; } virtual XMLComment* ToComment() { return 0; }
virtual char* ParseDeep( char* ) { TIXMLASSERT( 0 ); } // fixme: guarentee null terminator to avoid internal checks
virtual char* ParseDeep( char* );
void SetTextParent() { isTextParent = true; }
bool IsTextParent() const { return isTextParent; }
virtual bool IsClosingElement() const { return false; }
protected: protected:
XMLNode( XMLDocument* ); XMLNode( XMLDocument* );
@ -129,6 +133,7 @@ protected:
XMLDocument* document; XMLDocument* document;
XMLNode* parent; XMLNode* parent;
bool isTextParent;
XMLNode* firstChild; XMLNode* firstChild;
XMLNode* lastChild; XMLNode* lastChild;
@ -208,13 +213,15 @@ public:
virtual void Print( FILE* cfile, int depth ); virtual void Print( FILE* cfile, int depth );
virtual XMLElement* ToElement() { return this; } virtual XMLElement* ToElement() { return this; }
bool Closing() const { return closing; } virtual bool IsClosingElement() const { return closing; }
char* ParseDeep( char* p ); char* ParseDeep( char* p );
protected: protected:
private: private:
char* ParseAttributes( char* p, bool *closedElement );
StrPair name; StrPair name;
bool closing; bool closing;
XMLAttribute* rootAttribute; XMLAttribute* rootAttribute;
@ -222,7 +229,7 @@ private:
}; };
class XMLDocument : public XMLBase class XMLDocument : public XMLNode
{ {
public: public:
XMLDocument(); XMLDocument();
@ -231,20 +238,19 @@ public:
bool Parse( const char* ); bool Parse( const char* );
void Print( FILE* cfile=stdout, int depth=0 ); void Print( FILE* cfile=stdout, int depth=0 );
/*
XMLNode* Root() { return root; } XMLNode* Root() { return root; }
XMLNode* RootElement(); XMLNode* RootElement();
*/
enum { enum {
ERROR_ELEMENT_MISMATCH, ERROR_ELEMENT_MISMATCH,
ERROR_PARSING_ELEMENT, ERROR_PARSING_ELEMENT,
ERROR_PARSING_ATTRIBUTE ERROR_PARSING_ATTRIBUTE
}; };
void SetError( int error, const char* str1, const char* str2 ) {} void SetError( int error, const char* str1, const char* str2 );
private: private:
XMLDocument( const XMLDocument& ); // intentionally not implemented XMLDocument( const XMLDocument& ); // intentionally not implemented
XMLNode* root;
CharBuffer* charBuffer; CharBuffer* charBuffer;
}; };

Binary file not shown.

View File

@ -39,6 +39,8 @@ int main( int argc, const char* argv )
"<element attrib1='foo' attrib2=\"bar\" ></element>", "<element attrib1='foo' attrib2=\"bar\" ></element>",
"<element attrib1='foo' attrib2=\"bar\" ><subelement attrib3='yeehaa' /></element>", "<element attrib1='foo' attrib2=\"bar\" ><subelement attrib3='yeehaa' /></element>",
"<element>Text inside element.</element>", "<element>Text inside element.</element>",
"<element><b></b></element>",
"<element>Text inside and <b>bolded</b> in the element.</element>",
0 0
}; };
for( int i=0; test[i]; ++i ) { for( int i=0; test[i]; ++i ) {