added a bunch of comments in
diff --git a/tinyxml2.h b/tinyxml2.h
index 8136d82..b5a908e 100644
--- a/tinyxml2.h
+++ b/tinyxml2.h
@@ -1,36 +1,16 @@
#ifndef TINYXML_INCLUDED
#define TINYXML2_INCLUDED
-/*
- TODO
- X const and non-const versions of API
- X memory pool the class construction
- X attribute accessors
- X node navigation
- - handles
- X visit pattern - change streamer?
- X make constructors protected
- X hide copy constructor
- X hide = operator
- X UTF8 support: isAlpha, etc.
- X string buffer for sets. (Grr.)
- - MS BOM
- X print to memory buffer
- - tests from xml1
- - xml1 tests especially UTF-8
- - perf test: xml1
- - perf test: xenowar
- - test: load(char*)
- - test: load(FILE*)
- - rename declaration
- - rename streamer
-*/
#include <limits.h>
#include <ctype.h>
#include <stdio.h>
#include <memory.h>
+/* TODO: create main page description.
+ TODO: add 'lastAttribute' for faster parsing.
+*/
+
#if defined( _DEBUG ) || defined( DEBUG ) || defined (__DEBUG__)
#ifndef DEBUG
#define DEBUG
@@ -89,12 +69,12 @@
class XMLDeclaration;
class XMLUnknown;
-class XMLStreamer;
+class XMLPrinter;
/*
A class that wraps strings. Normally stores the start and end
pointers into the XML file itself, and will apply normalization
- and entity transalion if actually read. Can also store (and memory
+ and entity translation if actually read. Can also store (and memory
manage) a traditional char[]
*/
class StrPair
@@ -380,20 +360,48 @@
};
+/** XMLNode is a base class for every object that is in the
+ XML Document Object Model (DOM), except XMLAttributes.
+ Nodes have siblings, a parent, and children which can
+ be navigated. A node is always in a XMLDocument.
+ The type of a TiXmlNode can be queried, and it can
+ be cast to its more defined type.
+
+ An XMLDocument allocates memory for all its Nodes.
+ When the XMLDocument gets deleted, all its Nodes
+ will also be deleted.
+
+ @verbatim
+ A Document can contain: Element (container or leaf)
+ Comment (leaf)
+ Unknown (leaf)
+ Declaration( leaf )
+
+ An Element can contain: Element (container or leaf)
+ Text (leaf)
+ Attributes (not on tree)
+ Comment (leaf)
+ Unknown (leaf)
+
+ @endverbatim
+*/
class XMLNode
{
friend class XMLDocument;
friend class XMLElement;
public:
+
+ /// Get the XMLDocument that owns this XMLNode.
const XMLDocument* GetDocument() const { return document; }
+ /// Get the XMLDocument that owns this XMLNode.
XMLDocument* GetDocument() { return document; }
- virtual XMLElement* ToElement() { return 0; }
- virtual XMLText* ToText() { return 0; }
- virtual XMLComment* ToComment() { return 0; }
- virtual XMLDocument* ToDocument() { return 0; }
- virtual XMLDeclaration* ToDeclaration() { return 0; }
- virtual XMLUnknown* ToUnknown() { return 0; }
+ virtual XMLElement* ToElement() { return 0; } ///< Safely cast to an Element, or null.
+ virtual XMLText* ToText() { return 0; } ///< Safely cast to Text, or null.
+ virtual XMLComment* ToComment() { return 0; } ///< Safely cast to a Comment, or null.
+ virtual XMLDocument* ToDocument() { return 0; } ///< Safely cast to a Document, or null.
+ virtual XMLDeclaration* ToDeclaration() { return 0; } ///< Safely cast to a Declaration, or null.
+ virtual XMLUnknown* ToUnknown() { return 0; } ///< Safely cast to an Unknown, or null.
virtual const XMLElement* ToElement() const { return 0; }
virtual const XMLText* ToText() const { return 0; }
@@ -402,66 +410,111 @@
virtual const XMLDeclaration* ToDeclaration() const { return 0; }
virtual const XMLUnknown* ToUnknown() const { return 0; }
+ /** The meaning of 'value' changes for the specific type.
+ @verbatim
+ Document: empy
+ Element: name of the element
+ Comment: the comment text
+ Unknown: the tag contents
+ Text: the text string
+ @endverbatim
+ */
const char* Value() const { return value.GetStr(); }
+ /** Set the Value of an XML node.
+ @sa Value()
+ */
void SetValue( const char* val, bool staticMem=false );
+ /// Get the parent of this node on the DOM.
const XMLNode* Parent() const { return parent; }
XMLNode* Parent() { return parent; }
/// Returns true if this node has no children.
bool NoChildren() const { return !firstChild; }
+ /// Get the first child node, or null if none exists.
const XMLNode* FirstChild() const { return firstChild; }
XMLNode* FirstChild() { return firstChild; }
+ /** Get the first child element, or optionally the first child
+ element with the specified name.
+ */
const XMLElement* FirstChildElement( const char* value=0 ) const;
XMLElement* FirstChildElement( const char* value=0 ) { return const_cast<XMLElement*>(const_cast<const XMLNode*>(this)->FirstChildElement( value )); }
+ /// Get the last child node, or null if none exists.
const XMLNode* LastChild() const { return lastChild; }
XMLNode* LastChild() { return const_cast<XMLNode*>(const_cast<const XMLNode*>(this)->LastChild() ); }
+ /** Get the last child element or optionally the last child
+ element with the specified name.
+ */
const XMLElement* LastChildElement( const char* value=0 ) const;
XMLElement* LastChildElement( const char* value=0 ) { return const_cast<XMLElement*>(const_cast<const XMLNode*>(this)->LastChildElement(value) ); }
+ /// Get the previous (left) sibling node of this node.
const XMLNode* PreviousSibling() const { return prev; }
XMLNode* PreviousSibling() { return prev; }
+ /// Get the previous (left) sibling element of this node, with an opitionally supplied name.
const XMLNode* PreviousSiblingElement( const char* value=0 ) const ;
XMLNode* PreviousSiblingElement( const char* value=0 ) { return const_cast<XMLNode*>(const_cast<const XMLNode*>(this)->PreviousSiblingElement( value ) ); }
+ /// Get the next (right) sibling node of this node.
const XMLNode* NextSibling() const { return next; }
XMLNode* NextSibling() { return next; }
+ /// Get the next (right) sibling element of this node, with an opitionally supplied name.
const XMLNode* NextSiblingElement( const char* value=0 ) const;
XMLNode* NextSiblingElement( const char* value=0 ) { return const_cast<XMLNode*>(const_cast<const XMLNode*>(this)->NextSiblingElement( value ) ); }
/**
-
- Tests: Programmatic DOM
+ Add a child node as the last (right) child.
*/
XMLNode* InsertEndChild( XMLNode* addThis );
/**
-
- Tests: Programmatic DOM
+ Add a child node as the first (left) child.
*/
XMLNode* InsertFirstChild( XMLNode* addThis );
/**
-
- Tests: Programmatic DOM
+ Add a node after the specified child node.
*/
XMLNode* InsertAfterChild( XMLNode* afterThis, XMLNode* addThis );
/**
- Tests: All (used by destructor)
+ Delete all the children of this node.
*/
- void ClearChildren();
+ void DeleteChildren();
/**
- Tests: Progammatic DOM
+ Delete a child of this node.
*/
void DeleteChild( XMLNode* node );
+ /** Accept a hierchical visit the nodes in the TinyXML DOM. Every node in the
+ XML tree will be conditionally visited and the host will be called back
+ via the TiXmlVisitor interface.
+
+ This is essentially a SAX interface for TinyXML. (Note however it doesn't re-parse
+ the XML for the callbacks, so the performance of TinyXML is unchanged by using this
+ interface versus any other.)
+
+ The interface has been based on ideas from:
+
+ - http://www.saxproject.org/
+ - http://c2.com/cgi/wiki?HierarchicalVisitorPattern
+
+ Which are both good references for "visiting".
+
+ An example of using Accept():
+ @verbatim
+ TiXmlPrinter printer;
+ tinyxmlDoc.Accept( &printer );
+ const char* xmlcstr = printer.CStr();
+ @endverbatim
+ */
virtual bool Accept( XMLVisitor* visitor ) const = 0;
+ // internal
virtual char* ParseDeep( char*, StrPair* );
protected:
@@ -486,6 +539,18 @@
};
+/** XML text.
+
+ Note that a text node can have child element nodes, for example:
+ @verbatim
+ <root>This is <b>bold</b></root>
+ @endverbatim
+
+ A text node can have 2 ways to output the next. "normal" output
+ and CDATA. It will default to the mode it was parsed from the XML file and
+ you generally want to leave it alone, but you can change the output mode with
+ SetCDATA() and query it with CDATA().
+*/
class XMLText : public XMLNode
{
friend class XMLBase;
@@ -496,7 +561,9 @@
virtual XMLText* ToText() { return this; }
virtual const XMLText* ToText() const { return this; }
- void SetCData( bool value ) { isCData = true; }
+ /// Declare whether this should be CDATA or standard text.
+ void SetCData( bool isCData ) { this->isCData = isCData; }
+ /// Returns true if this is a CDATA text element.
bool CData() const { return isCData; }
char* ParseDeep( char*, StrPair* endTag );
@@ -512,6 +579,7 @@
};
+/** An XML Comment. */
class XMLComment : public XMLNode
{
friend class XMLDocument;
@@ -533,6 +601,17 @@
};
+/** In correct XML the declaration is the first entry in the file.
+ @verbatim
+ <?xml version="1.0" standalone="yes"?>
+ @endverbatim
+
+ TinyXML2 will happily read or write files without a declaration,
+ however.
+
+ The text of the declaration isn't interpreted. It is parsed
+ and written as a string.
+*/
class XMLDeclaration : public XMLNode
{
friend class XMLDocument;
@@ -552,6 +631,13 @@
};
+/** Any tag that tinyXml doesn't recognize is saved as an
+ unknown. It is a tag of text, but should not be modified.
+ It will be written back to the XML, unchanged, when the file
+ is saved.
+
+ DTD tags get thrown into TiXmlUnknowns.
+*/
class XMLUnknown : public XMLNode
{
friend class XMLDocument;
@@ -593,31 +679,59 @@
};
+/** An attribute is a name-value pair. Elements have an arbitrary
+ number of attributes, each with a unique name.
+
+ @note The attributes are not XMLNodes. You may only query the
+ Next() attribute in a list.
+*/
class XMLAttribute
{
friend class XMLElement;
public:
- const char* Name() const { return name.GetStr(); }
- const char* Value() const { return value.GetStr(); }
- const XMLAttribute* Next() const { return next; }
+ const char* Name() const { return name.GetStr(); } ///< The name of the attribute.
+ const char* Value() const { return value.GetStr(); } ///< The value of the attribute.
+ const XMLAttribute* Next() const { return next; } ///< The next attribute in the list.
+ /** IntAttribute interprets the attribute as an integer, and returns the value.
+ If the value isn't an integer, 0 will be returned. There is no error checking;
+ use QueryIntAttribute() if you need error checking.
+ */
int IntAttribute( const char* name ) const { int i=0; QueryIntAttribute( &i ); return i; }
+ /// Query as an unsigned integer. See IntAttribute()
unsigned UnsignedAttribute( const char* name ) const{ unsigned i=0; QueryUnsignedAttribute( &i ); return i; }
+ /// Query as a boolean. See IntAttribute()
bool BoolAttribute( const char* name ) const { bool b=false; QueryBoolAttribute( &b ); return b; }
+ /// Query as a double. See IntAttribute()
double DoubleAttribute( const char* name ) const { double d=0; QueryDoubleAttribute( &d ); return d; }
+ /// Query as a float. See IntAttribute()
float FloatAttribute( const char* name ) const { float f=0; QueryFloatAttribute( &f ); return f; }
+ /** QueryIntAttribute interprets the attribute as an integer, and returns the value
+ in the provided paremeter. The function will return XML_NO_ERROR on success,
+ and WRONG_ATTRIBUTE_TYPE if the conversion is not successful.
+ */
int QueryIntAttribute( int* value ) const;
+ /// See QueryIntAttribute
int QueryUnsignedAttribute( unsigned int* value ) const;
+ /// See QueryIntAttribute
int QueryBoolAttribute( bool* value ) const;
+ /// See QueryIntAttribute
int QueryDoubleAttribute( double* value ) const;
+ /// See QueryIntAttribute
int QueryFloatAttribute( float* value ) const;
+ /// Set the attribute to a string value.
void SetAttribute( const char* value );
+ /// Set the attribute to value.
void SetAttribute( int value );
+ /// Set the attribute to value.
void SetAttribute( unsigned value );
+ /// Set the attribute to value.
void SetAttribute( bool value );
+ /// Set the attribute to value.
void SetAttribute( double value );
+ /// Set the attribute to value.
void SetAttribute( float value );
private:
@@ -638,46 +752,116 @@
};
+/** The element is a container class. It has a value, the element name,
+ and can contain other elements, text, comments, and unknowns.
+ Elements also contain an arbitrary number of attributes.
+*/
class XMLElement : public XMLNode
{
friend class XMLBase;
friend class XMLDocument;
public:
+ /// Get the name of an element (which is the Value() of the node.)
const char* Name() const { return Value(); }
+ /// Set the name of the element.
void SetName( const char* str, bool staticMem=false ) { SetValue( str, staticMem ); }
virtual XMLElement* ToElement() { return this; }
virtual const XMLElement* ToElement() const { return this; }
virtual bool Accept( XMLVisitor* visitor ) const;
+ /** Given an attribute name, Attribute() returns the value
+ for the attribute of that name, or null if none exists.
+ */
const char* Attribute( const char* name ) const { const XMLAttribute* a = FindAttribute( name ); if ( !a ) return 0; return a->Value(); }
+ /** Given an attribute name, IntAttribute() returns the value
+ of the attribute interpreted as an integer. 0 will be
+ returned if there is an error. For a method with error
+ checking, see QueryIntAttribute()
+ */
int IntAttribute( const char* name ) const { int i=0; QueryIntAttribute( name, &i ); return i; }
+ /// See IntAttribute()
unsigned UnsignedAttribute( const char* name ) const{ unsigned i=0; QueryUnsignedAttribute( name, &i ); return i; }
+ /// See IntAttribute()
bool BoolAttribute( const char* name ) const { bool b=false; QueryBoolAttribute( name, &b ); return b; }
+ /// See IntAttribute()
double DoubleAttribute( const char* name ) const { double d=0; QueryDoubleAttribute( name, &d ); return d; }
+ /// See IntAttribute()
float FloatAttribute( const char* name ) const { float f=0; QueryFloatAttribute( name, &f ); return f; }
+ /** Given an attribute name, QueryIntAttribute() returns
+ XML_NO_ERROR, WRONG_ATTRIBUTE_TYPE if the conversion
+ can't be performed, or NO_ATTRIBUTE if the attribute
+ doesn't exist. If successful, the result of the conversion
+ will be written to 'value'. If not successful, nothing will
+ be written to 'value'. This allows you to provide default
+ value:
+
+ @verbatim
+ int value = 10;
+ QueryIntAttribute( "foo", &value ); // if "foo" isn't found, value will still be 10
+ @endverbatim
+ */
int QueryIntAttribute( const char* name, int* value ) const { const XMLAttribute* a = FindAttribute( name ); if ( !a ) return NO_ATTRIBUTE; return a->QueryIntAttribute( value ); }
+ /// See QueryIntAttribute()
int QueryUnsignedAttribute( const char* name, unsigned int* value ) const { const XMLAttribute* a = FindAttribute( name ); if ( !a ) return NO_ATTRIBUTE; return a->QueryUnsignedAttribute( value ); }
+ /// See QueryIntAttribute()
int QueryBoolAttribute( const char* name, bool* value ) const { const XMLAttribute* a = FindAttribute( name ); if ( !a ) return NO_ATTRIBUTE; return a->QueryBoolAttribute( value ); }
+ /// See QueryIntAttribute()
int QueryDoubleAttribute( const char* name, double* value ) const { const XMLAttribute* a = FindAttribute( name ); if ( !a ) return NO_ATTRIBUTE; return a->QueryDoubleAttribute( value ); }
+ /// See QueryIntAttribute()
int QueryFloatAttribute( const char* name, float* value ) const { const XMLAttribute* a = FindAttribute( name ); if ( !a ) return NO_ATTRIBUTE; return a->QueryFloatAttribute( value ); }
+ /// Sets the named attribute to value.
void SetAttribute( const char* name, const char* value ) { XMLAttribute* a = FindOrCreateAttribute( name ); a->SetAttribute( value ); }
+ /// Sets the named attribute to value.
void SetAttribute( const char* name, int value ) { XMLAttribute* a = FindOrCreateAttribute( name ); a->SetAttribute( value ); }
+ /// Sets the named attribute to value.
void SetAttribute( const char* name, unsigned value ) { XMLAttribute* a = FindOrCreateAttribute( name ); a->SetAttribute( value ); }
+ /// Sets the named attribute to value.
void SetAttribute( const char* name, bool value ) { XMLAttribute* a = FindOrCreateAttribute( name ); a->SetAttribute( value ); }
+ /// Sets the named attribute to value.
void SetAttribute( const char* name, double value ) { XMLAttribute* a = FindOrCreateAttribute( name ); a->SetAttribute( value ); }
/**
- Tests: Programmatic DOM
+ Delete an attribute.
*/
void DeleteAttribute( const char* name );
+ /// Return the first attribute in the list.
const XMLAttribute* FirstAttribute() const { return rootAttribute; }
+ /// Query a specific attribute in the list.
const XMLAttribute* FindAttribute( const char* name ) const;
+ /** Convenience function for easy access to the text inside an element. Although easy
+ and concise, GetText() is limited compared to getting the TiXmlText child
+ and accessing it directly.
+
+ If the first child of 'this' is a TiXmlText, the GetText()
+ returns the character string of the Text node, else null is returned.
+
+ This is a convenient method for getting the text of simple contained text:
+ @verbatim
+ <foo>This is text</foo>
+ const char* str = fooElement->GetText();
+ @endverbatim
+
+ 'str' will be a pointer to "This is text".
+
+ Note that this function can be misleading. If the element foo was created from
+ this XML:
+ @verbatim
+ <foo><b>This is text</b></foo>
+ @endverbatim
+
+ then the value of str would be null. The first child node isn't a text node, it is
+ another element. From this XML:
+ @verbatim
+ <foo>This is <b>text</b></foo>
+ @endverbatim
+ GetText() will return "This is ".
+ */
const char* GetText() const;
// internal:
@@ -705,54 +889,111 @@
};
+/** A document binds together all the functionality.
+ It can be saved, loaded, and printed to the screen.
+ All Nodes are connected and allocated to a Document.
+ If the Document is deleted, all its Nodes are also deleted.
+*/
class XMLDocument : public XMLNode
{
friend class XMLElement;
public:
+ /// constructor
XMLDocument();
~XMLDocument();
virtual XMLDocument* ToDocument() { return this; }
virtual const XMLDocument* ToDocument() const { return this; }
+ /**
+ Parse an XML file from a character string.
+ Returns XML_NO_ERROR (0) on success, or
+ an errorID.
+ */
int Parse( const char* xml );
+ /**
+ Load an XML file from disk.
+ Returns XML_NO_ERROR (0) on success, or
+ an errorID.
+ */
int LoadFile( const char* filename );
+ /**
+ Load an XML file from disk. You are responsible
+ for providing and closing the FILE*.
+
+ Returns XML_NO_ERROR (0) on success, or
+ an errorID.
+ */
int LoadFile( FILE* );
+ /**
+ Save the XML file to disk.
+ */
void SaveFile( const char* filename );
bool HasBOM() const { return writeBOM; }
+
+ /** Return the root element of DOM. Equivalent to FirstChildElement().
+ To get the first node, use FirstChild().
+ */
XMLElement* RootElement() { return FirstChildElement(); }
const XMLElement* RootElement() const { return FirstChildElement(); }
- void Print( XMLStreamer* streamer=0 );
+ /** Print the Document. If the Printer is not provided, it will
+ print to stdout. If you provide Printer, this can print to a file:
+ @verbatim
+ XMLPrinter printer( fp );
+ doc.Print( &printer );
+ @endverbatim
+
+ Or you can use a printer to print to memory:
+ @verbatim
+ XMLPrinter printer;
+ doc->Print( &printer );
+ SomeFunctior( printer.CStr() );
+ @endverbatim
+ */
+ void Print( XMLPrinter* streamer=0 );
virtual bool Accept( XMLVisitor* visitor ) const;
/**
- Tests: Programmatic DOM
+ Create a new Element associated with
+ this Document. The memory for the Element
+ is managed by the Document.
*/
XMLElement* NewElement( const char* name );
/**
- Tests: Programmatic DOM
+ Create a new Comment associated with
+ this Document. The memory for the Comment
+ is managed by the Document.
*/
XMLComment* NewComment( const char* comment );
/**
- Tests: Programmatic DOM
+ Create a new Text associated with
+ this Document. The memory for the Text
+ is managed by the Document.
*/
XMLText* NewText( const char* text );
/**
- Tests: Programmatic DOM
+ Delete a node associated with this documented.
+ It will be unlinked from the DOM.
*/
void DeleteNode( XMLNode* node ) { node->parent->DeleteChild( node ); }
void SetError( int error, const char* str1, const char* str2 );
+ /// Return true if there was an error parsing the document.
bool Error() const { return errorID != XML_NO_ERROR; }
+ /// Return the errorID.
int ErrorID() const { return errorID; }
+ /// Return a possibly helpful diagnostic location or string.
const char* GetErrorStr1() const { return errorStr1; }
+ /// Return possibly helpful secondary diagnostic location or string.
const char* GetErrorStr2() const { return errorStr2; }
+ /// If there is an error, print it to stdout
void PrintError() const;
+ // internal
char* Identify( char* p, XMLNode** node );
private:
@@ -773,19 +1014,75 @@
};
-class XMLStreamer : public XMLVisitor
+
+/**
+ Printing functionality. The XMLPrinter gives you more
+ options than the XMLDocument::Print() method.
+
+ It can:
+ -# Print to memory.
+ -# Print to a file you provide
+ -# Print XML without a XMLDocument.
+
+ Print to Memory
+
+ @verbatim
+ XMLPrinter printer;
+ doc->Print( &printer );
+ SomeFunctior( printer.CStr() );
+ @endverbatim
+
+ Print to a File
+
+ You provide the file pointer.
+ @verbatim
+ XMLPrinter printer( fp );
+ doc.Print( &printer );
+ @endverbatim
+
+ Print without a XMLDocument
+
+ When loading, an XML parser is very useful. However, sometimes
+ when saving, it just gets in the way. The code is often set up
+ for streaming, and constructing the DOM is just overhead.
+
+ The Printer supports the streaming case. The following code
+ prints out a trivially simple XML file without ever creating
+ an XML document.
+
+ @verbatim
+ XMLPrinter printer( fp );
+ printer.OpenElement( "foo" );
+ printer.PushAttribute( "foo", "bar" );
+ printer.CloseElement();
+ @endverbatim
+*/
+class XMLPrinter : public XMLVisitor
{
public:
- XMLStreamer( FILE* file=0 );
- ~XMLStreamer() {}
+ /** Construct the printer. If the FILE* is specified,
+ this will print to the FILE. Else it will print
+ to memory, and the result is available in CStr()
+ */
+ XMLPrinter( FILE* file=0 );
+ ~XMLPrinter() {}
+ /** If streaming, write the BOM and declaration. */
void PushHeader( bool writeBOM, bool writeDeclaration );
+ /** If streaming, start writing an element.
+ The element must be closed with CloseElement()
+ */
void OpenElement( const char* name );
+ /// If streaming, add an attribute to an open element.
void PushAttribute( const char* name, const char* value );
+ /// If streaming, close the Element.
void CloseElement();
+ /// Add a text node.
void PushText( const char* text, bool cdata=false );
+ /// Add a comment
void PushComment( const char* comment );
+
void PushDeclaration( const char* value );
void PushUnknown( const char* value );
@@ -800,6 +1097,10 @@
virtual bool Visit( const XMLDeclaration& declaration );
virtual bool Visit( const XMLUnknown& unknown );
+ /**
+ If in print to memory mode, return a pointer to
+ the XML file in memory.
+ */
const char* CStr() const { return buffer.Mem(); }
private: