Просмотр исходного кода

element and attribute code compiling

Lee Thomason 14 лет назад
Родитель
Сommit
8a5dfee8ce
4 измененных файлов с 357 добавлено и 116 удалено
  1. 265 94
      tinyxml2.cpp
  2. 80 20
      tinyxml2.h
  3. BIN
      tinyxml2.suo
  4. 12 2
      xmltest.cpp

+ 265 - 94
tinyxml2.cpp

@@ -31,6 +31,137 @@ static const char CR = CARRIAGE_RETURN;
 }
 
 
+// --------- XMLBase ----------- //
+const char* XMLBase::ParseText( char* p, const char* endTag, char** next )
+{
+	TIXMLASSERT( endTag && *endTag );
+
+	char* start = p;
+	char* q = p;		// q (target) <= p (src) in same buffer.
+	char  endChar = *endTag;
+	int   length = strlen( endTag );	
+	char* nextTag = 0;
+	*next = 0;
+
+	// Inner loop of text parsing.
+	while ( *p ) {
+		if ( *p == endChar && strncmp( p, endTag, length ) == 0 ) {
+			*q = 0;
+			nextTag = p + length;
+			break;
+		}
+		else if ( *p == CR ) {
+			// CR-LF pair becomes LF
+			// CR alone becomes LF
+			// LF-CR becomes LF
+			if ( *(p+1) == LF ) {
+				p += 2;
+			}
+			else {
+				++p;
+			}
+			*q = LF;
+		}
+		else if ( *p == LF ) {
+			if ( *(p+1) == CR ) {
+				p += 2;
+			}
+			else {
+				++p;
+			}
+			*q = LF;
+		}
+		else {
+			*q = *p;
+			++p;
+		}
+		++q;
+	}	
+
+	// Error? If we don't have a text tag, something went wrong. (Although 
+	// what the nextTag points at may be null.)
+	if ( nextTag == 0 ) {
+		return 0;
+	}
+	*next = nextTag;
+	return start;
+}
+
+
+const char* XMLBase::ParseName( char* p, char** next )
+{
+	char* start = p;
+	char* nextTag = 0;
+	*next = 0;
+
+	start = p;
+	if ( !start || !(*start) ) {
+		return 0;
+	}
+
+	if ( !IsAlpha( *p ) ) {
+		return 0;
+	}
+
+	while( *p && (
+			   IsAlphaNum( (unsigned char) *p ) 
+			|| *p == '_'
+			|| *p == '-'
+			|| *p == '.'
+			|| *p == ':' ))
+	{
+		++p;
+	}
+	*p = 0;
+
+	if ( p > start ) {
+		*next = p;
+		return start;
+	}
+	return p+1;
+}
+
+
+char* XMLBase::Identify( XMLDocument* document, char* p, XMLNode** node ) 
+{
+	XMLNode* returnNode = 0;
+
+	p = XMLNode::SkipWhiteSpace( p );
+	if( !p || !*p || *p != '<' )
+	{
+		return 0;
+	}
+
+	// What is this thing? 
+	// - Elements start with a letter or underscore, but xml is reserved.
+	// - Comments: <!--
+	// - Decleration: <?xml
+	// - Everthing else is unknown to tinyxml.
+	//
+
+	static const char* xmlHeader		= { "<?xml" };
+	static const char* commentHeader	= { "<!--" };
+	static const char* dtdHeader		= { "<!" };
+	static const char* cdataHeader		= { "<![CDATA[" };
+
+	static const int xmlHeaderLen		= 5;
+	static const int commentHeaderLen	= 4;
+	static const int dtdHeaderLen		= 2;
+	static const int cdataHeaderLen		= 9;
+
+	if ( XMLNode::StringEqual( p, commentHeader, commentHeaderLen ) ) {
+		returnNode = new XMLComment( document );
+		p += commentHeaderLen;
+	}
+	else {
+		TIXMLASSERT( 0 );
+	}
+
+	*node = returnNode;
+	return p;
+}
+
+
 // --------- XMLNode ----------- //
 
 XMLNode::XMLNode( XMLDocument* doc ) :
@@ -51,6 +182,12 @@ XMLNode::~XMLNode()
 		delete node;
 		node = temp;
 	}
+	if ( prev ) {
+		prev->next = next;
+	}
+	if ( next ) {
+		next->prev = prev;
+	}
 }
 
 
@@ -93,71 +230,17 @@ void XMLNode::PrintSpace( FILE* fp, int depth )
 }
 
 
-const char* XMLNode::ParseText( char* p, const char* endTag, char** next )
-{
-	TIXMLASSERT( endTag && *endTag );
-
-	char* start = p;
-	char* q = p;		// q (target) <= p (src) in same buffer.
-	char  endChar = *endTag;
-	int   length = strlen( endTag );	
-	char* nextTag = 0;
-
-	// Inner loop of text parsing.
-	while ( *p ) {
-		if ( *p == endChar && strncmp( p, endTag, length ) == 0 ) {
-			*q = 0;
-			nextTag = p + length;
-			break;
-		}
-		else if ( *p == CR ) {
-			// CR-LF pair becomes LF
-			// CR alone becomes LF
-			// LF-CR becomes LF
-			if ( *(p+1) == LF ) {
-				p += 2;
-			}
-			else {
-				++p;
-			}
-			*q = LF;
-		}
-		else if ( *p == LF ) {
-			if ( *(p+1) == CR ) {
-				p += 2;
-			}
-			else {
-				++p;
-			}
-			*q = LF;
-		}
-		else {
-			*q = *p;
-			++p;
-		}
-		++q;
-	}	
-
-	// Error? If we don't have a text tag, something went wrong. (Although 
-	// what the nextTag points at may be null.)
-	if ( nextTag == 0 ) {
-		return 0;
-	}
-	*next = nextTag;
-	return start;
-}
 
 
 // --------- XMLComment ---------- //
 
-XMLComment::XMLComment( XMLDocument* doc ) : XMLNode( doc )
+XMLComment::XMLComment( XMLDocument* doc ) : XMLNode( doc ), value( 0 )
 {
 }
 
 
 XMLComment::~XMLComment()
 {
-
 }
 
 
@@ -176,6 +259,132 @@ char* XMLComment::ParseDeep( char* p )
 }
 
 
+// --------- XMLAttribute ---------- //
+char* XMLAttribute::ParseDeep( char* p )
+{
+	char endTag[2] = { *p, 0 };
+	++p;
+	value = ParseText( p, endTag, &p );
+	if ( !value ) return 0;
+	return p;
+}
+
+
+// --------- XMLElement ---------- //
+XMLElement::XMLElement( XMLDocument* doc ) : XMLNode( doc ),
+	name( 0 ),
+	closing( false ),
+	rootAttribute( 0 ),
+	lastAttribute( 0 )
+{
+}
+
+
+XMLElement::~XMLElement()
+{
+	XMLAttribute* attribute = rootAttribute;
+	while( attribute ) {
+		XMLAttribute* next = attribute->next;
+		delete attribute;
+		attribute = next;
+	}
+
+	XMLNode* child = firstChild;
+	while( child ) {
+		XMLNode* next = child->next;
+		delete child;
+		child = next;
+	}
+}
+
+
+char* XMLElement::ParseDeep( char* p )
+{
+	// Read the element name.
+	p = SkipWhiteSpace( p );
+	if ( !p ) return 0;
+	const char* start = p;
+
+	// 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;
+	}
+
+	name = ParseName( p, &p );
+	if ( !name ) return 0;
+
+	// Read the attributes.
+	while( p ) {
+		p = SkipWhiteSpace( p );
+		if ( !p || !(*p) ) {
+			document->SetError( XMLDocument::ERROR_PARSING_ELEMENT, start, name );
+			return 0;
+		}
+		const char* saveP = p;
+
+		// attribute.
+		if ( *p == '\'' || *p == '\"' ) {
+			XMLAttribute* attrib = new XMLAttribute( this );
+			p = attrib->ParseDeep( p );
+			if ( !p ) {
+				delete attrib;
+				document->SetError( XMLDocument::ERROR_PARSING_ATTRIBUTE, start, saveP );
+				return 0;
+			}
+			if ( rootAttribute ) {
+				TIXMLASSERT( lastAttribute );
+				lastAttribute->next = attrib;
+				lastAttribute = attrib;
+			}
+			else {
+				rootAttribute = lastAttribute = attrib;
+			}
+		}
+		else if ( *p == '/' && *(p+1) == '>' ) {
+			// end tag.
+			if ( closing ) {
+				document->SetError( XMLDocument::ERROR_PARSING_ELEMENT, start, p );
+				return 0;
+			}
+			return p+2;	// done; sealed element.
+		}
+		else if ( *p == '>' ) {
+			++p;
+			break;
+		}
+	}
+
+	while( p && *p ) {
+		XMLNode* node = 0;
+		p = Identify( document, p, &node );
+		if ( p && node ) {
+			node->ParseDeep( p );
+
+			XMLElement* element = node->ToElement();
+			if ( element && element->Closing() ) {
+				if ( StringEqual( element->Name(), this->Name() ) ) {
+					// All good, this is closing tag.
+					delete node;
+					p = 0;
+				}
+				else {
+					document->SetError( XMLDocument::ERROR_PARSING_ELEMENT, start, p );
+					delete node;
+				}
+				return p;
+			}
+			else {
+				this->InsertEndChild( node );
+			}
+		}
+	}
+	return 0;
+}
+
+
 // --------- XMLDocument ----------- //
 XMLDocument::XMLDocument() : 
 	charBuffer( 0 )
@@ -197,7 +406,7 @@ bool XMLDocument::Parse( const char* p )
 	charBuffer = CharBuffer::Construct( p );
 	XMLNode* node = 0;
 	
-	char* q = Identify( charBuffer->mem, &node );
+	char* q = Identify( this, charBuffer->mem, &node );
 	root->InsertEndChild( node );
 	node->ParseDeep( q );
 
@@ -213,41 +422,3 @@ void XMLDocument::Print( FILE* fp, int depth )
 }
 
 
-char* XMLDocument::Identify( char* p, XMLNode** node ) 
-{
-	XMLNode* returnNode = 0;
-
-	p = XMLNode::SkipWhiteSpace( p );
-	if( !p || !*p || *p != '<' )
-	{
-		return 0;
-	}
-
-	// What is this thing? 
-	// - Elements start with a letter or underscore, but xml is reserved.
-	// - Comments: <!--
-	// - Decleration: <?xml
-	// - Everthing else is unknown to tinyxml.
-	//
-
-	static const char* xmlHeader = { "<?xml" };
-	static const char* commentHeader = { "<!--" };
-	static const char* dtdHeader = { "<!" };
-	static const char* cdataHeader = { "<![CDATA[" };
-
-	static const int xmlHeaderLen = 5;
-	static const int commentHeaderLen = 4;
-	static const int dtdHeaderLen = 2;
-	static const int cdataHeaderLen = 9;
-
-	if ( XMLNode::StringEqual( p, commentHeader, commentHeaderLen ) ) {
-		returnNode = new XMLComment( this );
-		p += commentHeaderLen;
-	}
-	else {
-		TIXMLASSERT( 0 );
-	}
-
-	*node = returnNode;
-	return p;
-}

+ 80 - 20
tinyxml2.h

@@ -30,6 +30,10 @@
 namespace tinyxml2
 {
 class XMLDocument;
+class XMLElement;
+class XMLAttribute;
+class XMLComment;
+class XMLNode;
 
 // internal - move to separate namespace
 struct CharBuffer
@@ -42,19 +46,13 @@ struct CharBuffer
 };
 
 
-class XMLNode
+class XMLBase
 {
-	friend class XMLDocument;
 public:
-
-	XMLNode* InsertEndChild( XMLNode* addThis );
-	virtual void Print( FILE* cfile, int depth );
+	XMLBase() {}
+	virtual ~XMLBase() {}
 
 protected:
-	XMLNode( XMLDocument* );
-	virtual ~XMLNode();
-
-	// Utility
 	static const char* SkipWhiteSpace( const char* p )	{ while( isspace( *p ) ) { ++p; } return p; }
 	static char* SkipWhiteSpace( char* p )				{ while( isspace( *p ) ) { ++p; } return p; }
 
@@ -68,18 +66,33 @@ protected:
 		}
 		return false;
 	}
-	inline static int IsUTF8Continuation( char p ) { return p & 0x80; }
-
-	/* Parses text. (Not a text node.)
-	   - [ ] EOL normalization.
-	   - [X] Do not trim leading whitespace
-	   - [X] Do not trim trailing whitespace.
-	   - [X] Leaves inner whitespace
-	*/
+	inline static int IsUTF8Continuation( unsigned char p ) { return p & 0x80; }
+	inline static int IsAlphaNum( unsigned char anyByte )	{ return ( anyByte <= 127 ) ? isalnum( anyByte ) : 1; }
+	inline static int IsAlpha( unsigned char anyByte )		{ return ( anyByte <= 127 ) ? isalpha( anyByte ) : 1; }
+
 	const char* ParseText( char* in, const char* endTag, char** next );
+	const char* ParseName( char* in, char** next );
+	char* Identify( XMLDocument* document, char* p, XMLNode** node );
+};
+
+
+class XMLNode : public XMLBase
+{
+	friend class XMLDocument;
+	friend class XMLElement;
+public:
+	virtual ~XMLNode();
+
+	XMLNode* InsertEndChild( XMLNode* addThis );
+	virtual void Print( FILE* cfile, int depth );
+
+	virtual XMLElement* ToElement() { return 0; }
 
 	virtual char* ParseDeep( char* )	{ TIXMLASSERT( 0 ); }
 
+protected:
+	XMLNode( XMLDocument* );
+
 	XMLDocument*	document;
 	XMLNode*		parent;
 
@@ -103,15 +116,56 @@ public:
 
 	virtual void Print( FILE* cfile, int depth );
 
-protected:
+	const char* Value() const { return value; }
+
 	char* ParseDeep( char* );
 
+protected:
+
+private:
+	const char* value;
+};
+
+
+class XMLAttribute : public XMLBase
+{
+	friend class XMLElement;
+public:
+	XMLAttribute( XMLElement* element ) : value( 0 ), next( 0 ) {}
+	virtual ~XMLAttribute()	{}
+
 private:
+	char* ParseDeep( char* p );
+
 	const char* value;
+	XMLAttribute* next;
+};
+
+
+class XMLElement : public XMLNode
+{
+public:
+	XMLElement( XMLDocument* doc );
+	virtual ~XMLElement();
+
+	const char* Name() const { return name; }
+
+	virtual XMLElement* ToElement() { return this; }
+	bool Closing() const			{ return closing; }
+
+	char* ParseDeep( char* p );
+
+protected:
+
+private:
+	const char* name;
+	bool closing;
+	XMLAttribute* rootAttribute;
+	XMLAttribute* lastAttribute;
 };
 
 
-class XMLDocument
+class XMLDocument : public XMLBase
 {
 public:
 	XMLDocument();
@@ -123,9 +177,15 @@ public:
 	XMLNode* Root()				{ return root; }
 	XMLNode* RootElement();
 
+	enum {
+		ERROR_ELEMENT_MISMATCH,
+		ERROR_PARSING_ELEMENT,
+		ERROR_PARSING_ATTRIBUTE
+	};
+	void SetError( int error, const char* str1, const char* str2 )	{}
+
 private:
 	XMLDocument( const XMLDocument& );	// intentionally not implemented
-	char* Identify( char* p, XMLNode** node );
 
 	XMLNode*	root;
 	CharBuffer* charBuffer;


+ 12 - 2
xmltest.cpp

@@ -15,10 +15,20 @@ int main( int argc, const char* argv )
 		doc.Parse( test );
 		doc.Print( stdout );
 	}
-#endif
 	{
 		static const char* test = "<!--hello world\n"
-			                      "          line 2\r-->";
+			                      "          line 2\r"
+			                      "          line 3\r\n"
+			                      "          line 4\n\r"
+			                      "          line 5\r-->";
+
+		XMLDocument doc;
+		doc.Parse( test );
+		doc.Print( stdout );
+	}
+#endif
+	{
+		static const char* test = "<element />";
 
 		XMLDocument doc;
 		doc.Parse( test );