From 21be8828102e7bf13360f4451827c7572870ea4f Mon Sep 17 00:00:00 2001 From: Lee Thomason Date: Sun, 15 Jul 2012 17:27:22 -0700 Subject: [PATCH] Moving string in/out into XMLUtil. Using that across the API. Supporting text queries of primitive types. --- CMakeLists.txt | 2 +- dox | 2 +- tinyxml2.cpp | 235 +++++++++++++++++++++++++++++++++++++++++++------ tinyxml2.h | 76 ++++++++++++++-- xmltest.cpp | 85 +++++++++++++++--- xmltest.h | 35 ++++++++ 6 files changed, 387 insertions(+), 48 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 53f09c2..4b60d7c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -10,7 +10,7 @@ include(GNUInstallDirs) ################################ # set lib version here -set(GENERIC_LIB_VERSION "1.0.5") +set(GENERIC_LIB_VERSION "1.0.6") set(GENERIC_LIB_SOVERSION "1") diff --git a/dox b/dox index 5a9e9d3..c435bbf 100755 --- a/dox +++ b/dox @@ -32,7 +32,7 @@ PROJECT_NAME = "TinyXML-2" # This could be handy for archiving the generated documentation or # if some version control system is used. -PROJECT_NUMBER = 1.0.5 +PROJECT_NUMBER = 1.0.6 # Using the PROJECT_BRIEF tag one can provide an optional one line description # for a project that appears at the top of each page and should give viewer diff --git a/tinyxml2.cpp b/tinyxml2.cpp index 08917e1..b7dd909 100644 --- a/tinyxml2.cpp +++ b/tinyxml2.cpp @@ -369,6 +369,86 @@ const char* XMLUtil::GetCharacterRef( const char* p, char* value, int* length ) } +void XMLUtil::ToStr( int v, char* buffer, int bufferSize ) +{ + TIXML_SNPRINTF( buffer, bufferSize, "%d", v ); +} + + +void XMLUtil::ToStr( unsigned v, char* buffer, int bufferSize ) +{ + TIXML_SNPRINTF( buffer, bufferSize, "%u", v ); +} + + +void XMLUtil::ToStr( bool v, char* buffer, int bufferSize ) +{ + TIXML_SNPRINTF( buffer, bufferSize, "%d", v ? 1 : 0 ); +} + + +void XMLUtil::ToStr( float v, char* buffer, int bufferSize ) +{ + TIXML_SNPRINTF( buffer, bufferSize, "%f", v ); +} + + +void XMLUtil::ToStr( double v, char* buffer, int bufferSize ) +{ + TIXML_SNPRINTF( buffer, bufferSize, "%f", v ); +} + + +bool XMLUtil::ToInt( const char* str, int* value ) +{ + if ( TIXML_SSCANF( str, "%d", value ) == 1 ) + return true; + return false; +} + +bool XMLUtil::ToUnsigned( const char* str, unsigned *value ) +{ + if ( TIXML_SSCANF( str, "%u", value ) == 1 ) + return true; + return false; +} + +bool XMLUtil::ToBool( const char* str, bool* value ) +{ + int ival = 0; + if ( ToInt( str, &ival )) { + *value = (ival==0) ? false : true; + return true; + } + if ( StringEqual( str, "true" ) ) { + *value = true; + return true; + } + else if ( StringEqual( str, "false" ) ) { + *value = false; + return true; + } + return false; +} + + +bool XMLUtil::ToFloat( const char* str, float* value ) +{ + if ( TIXML_SSCANF( str, "%f", value ) == 1 ) { + return true; + } + return false; +} + +bool XMLUtil::ToDouble( const char* str, double* value ) +{ + if ( TIXML_SSCANF( str, "%lf", value ) == 1 ) { + return true; + } + return false; +} + + char* XMLDocument::Identify( char* p, XMLNode** node ) { XMLNode* returnNode = 0; @@ -942,7 +1022,7 @@ void XMLAttribute::SetName( const char* n ) int XMLAttribute::QueryIntValue( int* value ) const { - if ( TIXML_SSCANF( Value(), "%d", value ) == 1 ) + if ( XMLUtil::ToInt( Value(), value )) return XML_NO_ERROR; return XML_WRONG_ATTRIBUTE_TYPE; } @@ -950,7 +1030,7 @@ int XMLAttribute::QueryIntValue( int* value ) const int XMLAttribute::QueryUnsignedValue( unsigned int* value ) const { - if ( TIXML_SSCANF( Value(), "%u", value ) == 1 ) + if ( XMLUtil::ToUnsigned( Value(), value )) return XML_NO_ERROR; return XML_WRONG_ATTRIBUTE_TYPE; } @@ -958,32 +1038,24 @@ int XMLAttribute::QueryUnsignedValue( unsigned int* value ) const int XMLAttribute::QueryBoolValue( bool* value ) const { - int ival = -1; - QueryIntValue( &ival ); - - if ( ival > 0 || XMLUtil::StringEqual( Value(), "true" ) ) { - *value = true; + if ( XMLUtil::ToBool( Value(), value )) { return XML_NO_ERROR; } - else if ( ival == 0 || XMLUtil::StringEqual( Value(), "false" ) ) { - *value = false; - return XML_NO_ERROR; - } - return XML_WRONG_ATTRIBUTE_TYPE; -} - - -int XMLAttribute::QueryDoubleValue( double* value ) const -{ - if ( TIXML_SSCANF( Value(), "%lf", value ) == 1 ) - return XML_NO_ERROR; return XML_WRONG_ATTRIBUTE_TYPE; } int XMLAttribute::QueryFloatValue( float* value ) const { - if ( TIXML_SSCANF( Value(), "%f", value ) == 1 ) + if ( XMLUtil::ToFloat( Value(), value )) + return XML_NO_ERROR; + return XML_WRONG_ATTRIBUTE_TYPE; +} + + +int XMLAttribute::QueryDoubleValue( double* value ) const +{ + if ( XMLUtil::ToDouble( Value(), value )) return XML_NO_ERROR; return XML_WRONG_ATTRIBUTE_TYPE; } @@ -998,7 +1070,7 @@ void XMLAttribute::SetAttribute( const char* v ) void XMLAttribute::SetAttribute( int v ) { char buf[BUF_SIZE]; - TIXML_SNPRINTF( buf, BUF_SIZE, "%d", v ); + XMLUtil::ToStr( v, buf, BUF_SIZE ); value.SetStr( buf ); } @@ -1006,7 +1078,7 @@ void XMLAttribute::SetAttribute( int v ) void XMLAttribute::SetAttribute( unsigned v ) { char buf[BUF_SIZE]; - TIXML_SNPRINTF( buf, BUF_SIZE, "%u", v ); + XMLUtil::ToStr( v, buf, BUF_SIZE ); value.SetStr( buf ); } @@ -1014,21 +1086,21 @@ void XMLAttribute::SetAttribute( unsigned v ) void XMLAttribute::SetAttribute( bool v ) { char buf[BUF_SIZE]; - TIXML_SNPRINTF( buf, BUF_SIZE, "%d", v ? 1 : 0 ); + XMLUtil::ToStr( v, buf, BUF_SIZE ); value.SetStr( buf ); } void XMLAttribute::SetAttribute( double v ) { char buf[BUF_SIZE]; - TIXML_SNPRINTF( buf, BUF_SIZE, "%f", v ); + XMLUtil::ToStr( v, buf, BUF_SIZE ); value.SetStr( buf ); } void XMLAttribute::SetAttribute( float v ) { char buf[BUF_SIZE]; - TIXML_SNPRINTF( buf, BUF_SIZE, "%f", v ); + XMLUtil::ToStr( v, buf, BUF_SIZE ); value.SetStr( buf ); } @@ -1093,6 +1165,72 @@ const char* XMLElement::GetText() const } +int XMLElement::QueryIntText( int* _value ) const +{ + if ( FirstChild() && FirstChild()->ToText() ) { + const char* t = FirstChild()->ToText()->Value(); + if ( XMLUtil::ToInt( t, _value ) ) { + return XML_SUCCESS; + } + return XML_CAN_NOT_CONVERT_TEXT; + } + return XML_NO_TEXT_NODE; +} + + +int XMLElement::QueryUnsignedText( unsigned* _value ) const +{ + if ( FirstChild() && FirstChild()->ToText() ) { + const char* t = FirstChild()->ToText()->Value(); + if ( XMLUtil::ToUnsigned( t, _value ) ) { + return XML_SUCCESS; + } + return XML_CAN_NOT_CONVERT_TEXT; + } + return XML_NO_TEXT_NODE; +} + + +int XMLElement::QueryBoolText( bool* _value ) const +{ + if ( FirstChild() && FirstChild()->ToText() ) { + const char* t = FirstChild()->ToText()->Value(); + if ( XMLUtil::ToBool( t, _value ) ) { + return XML_SUCCESS; + } + return XML_CAN_NOT_CONVERT_TEXT; + } + return XML_NO_TEXT_NODE; +} + + +int XMLElement::QueryDoubleText( double* _value ) const +{ + if ( FirstChild() && FirstChild()->ToText() ) { + const char* t = FirstChild()->ToText()->Value(); + if ( XMLUtil::ToDouble( t, _value ) ) { + return XML_SUCCESS; + } + return XML_CAN_NOT_CONVERT_TEXT; + } + return XML_NO_TEXT_NODE; +} + + +int XMLElement::QueryFloatText( float* _value ) const +{ + if ( FirstChild() && FirstChild()->ToText() ) { + const char* t = FirstChild()->ToText()->Value(); + if ( XMLUtil::ToFloat( t, _value ) ) { + return XML_SUCCESS; + } + return XML_CAN_NOT_CONVERT_TEXT; + } + return XML_NO_TEXT_NODE; +} + + + XMLAttribute* XMLElement::FindOrCreateAttribute( const char* name ) { XMLAttribute* last = 0; @@ -1668,7 +1806,7 @@ void XMLPrinter::PushAttribute( const char* name, const char* value ) void XMLPrinter::PushAttribute( const char* name, int v ) { char buf[BUF_SIZE]; - TIXML_SNPRINTF( buf, BUF_SIZE, "%d", v ); + XMLUtil::ToStr( v, buf, BUF_SIZE ); PushAttribute( name, buf ); } @@ -1676,7 +1814,7 @@ void XMLPrinter::PushAttribute( const char* name, int v ) void XMLPrinter::PushAttribute( const char* name, unsigned v ) { char buf[BUF_SIZE]; - TIXML_SNPRINTF( buf, BUF_SIZE, "%u", v ); + XMLUtil::ToStr( v, buf, BUF_SIZE ); PushAttribute( name, buf ); } @@ -1684,7 +1822,7 @@ void XMLPrinter::PushAttribute( const char* name, unsigned v ) void XMLPrinter::PushAttribute( const char* name, bool v ) { char buf[BUF_SIZE]; - TIXML_SNPRINTF( buf, BUF_SIZE, "%d", v ? 1 : 0 ); + XMLUtil::ToStr( v, buf, BUF_SIZE ); PushAttribute( name, buf ); } @@ -1692,7 +1830,7 @@ void XMLPrinter::PushAttribute( const char* name, bool v ) void XMLPrinter::PushAttribute( const char* name, double v ) { char buf[BUF_SIZE]; - TIXML_SNPRINTF( buf, BUF_SIZE, "%f", v ); + XMLUtil::ToStr( v, buf, BUF_SIZE ); PushAttribute( name, buf ); } @@ -1745,6 +1883,45 @@ void XMLPrinter::PushText( const char* text, bool cdata ) } } +void XMLPrinter::PushText( int value ) +{ + char buf[BUF_SIZE]; + XMLUtil::ToStr( value, buf, BUF_SIZE ); + PushText( buf, false ); +} + + +void XMLPrinter::PushText( unsigned value ) +{ + char buf[BUF_SIZE]; + XMLUtil::ToStr( value, buf, BUF_SIZE ); + PushText( buf, false ); +} + + +void XMLPrinter::PushText( bool value ) +{ + char buf[BUF_SIZE]; + XMLUtil::ToStr( value, buf, BUF_SIZE ); + PushText( buf, false ); +} + + +void XMLPrinter::PushText( float value ) +{ + char buf[BUF_SIZE]; + XMLUtil::ToStr( value, buf, BUF_SIZE ); + PushText( buf, false ); +} + + +void XMLPrinter::PushText( double value ) +{ + char buf[BUF_SIZE]; + XMLUtil::ToStr( value, buf, BUF_SIZE ); + PushText( buf, false ); +} + void XMLPrinter::PushComment( const char* comment ) { diff --git a/tinyxml2.h b/tinyxml2.h index a47cc62..13629f4 100644 --- a/tinyxml2.h +++ b/tinyxml2.h @@ -85,7 +85,7 @@ distribution. static const int TIXML2_MAJOR_VERSION = 1; static const int TIXML2_MINOR_VERSION = 0; -static const int TIXML2_PATCH_VERSION = 5; +static const int TIXML2_PATCH_VERSION = 6; namespace tinyxml2 { @@ -388,6 +388,20 @@ public: // the UTF-8 value of the entity will be placed in value, and length filled in. static const char* GetCharacterRef( const char* p, char* value, int* length ); static void ConvertUTF32ToUTF8( unsigned long input, char* output, int* length ); + + // converts primitive types to strings + static void ToStr( int v, char* buffer, int bufferSize ); + static void ToStr( unsigned v, char* buffer, int bufferSize ); + static void ToStr( bool v, char* buffer, int bufferSize ); + static void ToStr( float v, char* buffer, int bufferSize ); + static void ToStr( double v, char* buffer, int bufferSize ); + + // converts strings to primitive types + static bool ToInt( const char* str, int* value ); + static bool ToUnsigned( const char* str, unsigned* value ); + static bool ToBool( const char* str, bool* value ); + static bool ToFloat( const char* str, float* value ); + static bool ToDouble( const char* str, double* value ); }; @@ -739,7 +753,10 @@ enum { XML_ERROR_PARSING_UNKNOWN, XML_ERROR_EMPTY_DOCUMENT, XML_ERROR_MISMATCHED_ELEMENT, - XML_ERROR_PARSING + XML_ERROR_PARSING, + + XML_CAN_NOT_CONVERT_TEXT, + XML_NO_TEXT_NODE }; @@ -928,7 +945,7 @@ public: This is a convenient method for getting the text of simple contained text: @verbatim This is text - const char* str = fooElement->GetText(); + const char* str = fooElement->GetText(); @endverbatim 'str' will be a pointer to "This is text". @@ -936,18 +953,54 @@ public: Note that this function can be misleading. If the element foo was created from this XML: @verbatim - This is text + This is text @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 - This is text + This is text @endverbatim GetText() will return "This is ". */ const char* GetText() const; + /** + Convenience method to query the value of a child text node. This is probably best + shown by example. Given you have a document is this form: + @verbatim + + 1 + 1.4 + + @endverbatim + + The QueryIntText() and similar functions provide a safe and easier way to get to the + "value" of x and y. + + @verbatim + int x = 0; + float y = 0; // types of x and y are contrived for example + const XMLElement* xElement = pointElement->FirstChildElement( "x" ); + const XMLElement* yElement = pointElement->FirstChildElement( "y" ); + xElement->QueryIntText( &x ); + yElement->QueryFloatText( &y ); + @endverbatim + + @returns XML_SUCCESS (0) on success, XML_CAN_NOT_CONVERT_TEXT if the text cannot be converted + to the requested type, and XML_NO_TEXT_NODE if there is no child text to query. + + */ + int QueryIntText( int* _value ) const; + /// See QueryIntText() + int QueryUnsignedText( unsigned* _value ) const; + /// See QueryIntText() + int QueryBoolText( bool* _value ) const; + /// See QueryIntText() + int QueryDoubleText( double* _value ) const; + /// See QueryIntText() + int QueryFloatText( float* _value ) const; + // internal: enum { OPEN, // @@ -1352,7 +1405,18 @@ public: /// Add a text node. void PushText( const char* text, bool cdata=false ); - /// Add a comment. + /// Add a text node from an integer. + void PushText( int value ); + /// Add a text node from an unsigned. + void PushText( unsigned value ); + /// Add a text node from a bool. + void PushText( bool value ); + /// Add a text node from a float. + void PushText( float value ); + /// Add a text node from a double. + void PushText( double value ); + + /// Add a comment void PushComment( const char* comment ); void PushDeclaration( const char* value ); diff --git a/xmltest.cpp b/xmltest.cpp index 760d0e2..7916a05 100644 --- a/xmltest.cpp +++ b/xmltest.cpp @@ -38,7 +38,7 @@ bool XMLTest (const char* testString, const char* expected, const char* found, b } -bool XMLTest( const char* testString, int expected, int found, bool echo=true ) +template< class T > bool XMLTest( const char* testString, T expected, T found, bool echo=true ) { bool pass = ( expected == found ); if ( pass ) @@ -116,6 +116,34 @@ int example_3() } +bool example_4() +{ + static const char* xml = + "" + " " + " " + " 2" + " " + ""; + + XMLDocument doc; + doc.Parse( xml ); + + int v0 = 0; + int v1 = 0; + + XMLElement* attributeApproachElement = doc.FirstChildElement()->FirstChildElement( "attributeApproach" ); + attributeApproachElement->QueryIntAttribute( "v", &v0 ); + + XMLElement* textApproachElement = doc.FirstChildElement()->FirstChildElement( "textApproach" ); + textApproachElement->FirstChildElement( "v" )->QueryIntText( &v1 ); + + printf( "Both values are the same: %d and %d\n", v0, v1 ); + + return !doc.Error() && ( v0 == v1 ); +} + + int main( int /*argc*/, const char ** /*argv*/ ) { #if defined( _MSC_VER ) && defined( DEBUG ) @@ -148,6 +176,7 @@ int main( int /*argc*/, const char ** /*argv*/ ) XMLTest( "Example-1", 0, example_1() ); XMLTest( "Example-2", 0, example_2() ); XMLTest( "Example-3", 0, example_3() ); + XMLTest( "Example-4", true, example_4() ); /* ------ Example 2: Lookup information. ---- */ @@ -243,7 +272,7 @@ int main( int /*argc*/, const char ** /*argv*/ ) XMLTest( "Programmatic DOM", true, doc->FirstChildElement()->FirstChildElement()->BoolAttribute( "attrib" ) ); int value = 10; int result = doc->FirstChildElement()->LastChildElement()->QueryIntAttribute( "attrib", &value ); - XMLTest( "Programmatic DOM", result, XML_NO_ATTRIBUTE ); + XMLTest( "Programmatic DOM", result, (int)XML_NO_ATTRIBUTE ); XMLTest( "Programmatic DOM", value, 10 ); doc->Print(); @@ -303,7 +332,7 @@ int main( int /*argc*/, const char ** /*argv*/ ) XMLDocument doc; doc.Parse( error ); - XMLTest( "Bad XML", doc.ErrorID(), XML_ERROR_PARSING_ATTRIBUTE ); + XMLTest( "Bad XML", doc.ErrorID(), (int)XML_ERROR_PARSING_ATTRIBUTE ); } { @@ -318,17 +347,17 @@ int main( int /*argc*/, const char ** /*argv*/ ) double dVal; result = ele->QueryDoubleAttribute( "attr0", &dVal ); - XMLTest( "Query attribute: int as double", result, XML_NO_ERROR ); + XMLTest( "Query attribute: int as double", result, (int)XML_NO_ERROR ); XMLTest( "Query attribute: int as double", (int)dVal, 1 ); result = ele->QueryDoubleAttribute( "attr1", &dVal ); XMLTest( "Query attribute: double as double", (int)dVal, 2 ); result = ele->QueryIntAttribute( "attr1", &iVal ); - XMLTest( "Query attribute: double as int", result, XML_NO_ERROR ); + XMLTest( "Query attribute: double as int", result, (int)XML_NO_ERROR ); XMLTest( "Query attribute: double as int", iVal, 2 ); result = ele->QueryIntAttribute( "attr2", &iVal ); - XMLTest( "Query attribute: not a number", result, XML_WRONG_ATTRIBUTE_TYPE ); + XMLTest( "Query attribute: not a number", result, (int)XML_WRONG_ATTRIBUTE_TYPE ); result = ele->QueryIntAttribute( "bar", &iVal ); - XMLTest( "Query attribute: does not exist", result, XML_NO_ATTRIBUTE ); + XMLTest( "Query attribute: does not exist", result, (int)XML_NO_ATTRIBUTE ); } { @@ -566,7 +595,7 @@ int main( int /*argc*/, const char ** /*argv*/ ) XMLDocument doc; doc.Parse( test ); - XMLTest( "dot in names", doc.Error(), 0); + XMLTest( "dot in names", doc.Error(), false ); XMLTest( "dot in names", doc.FirstChildElement()->Name(), "a.elem" ); XMLTest( "dot in names", doc.FirstChildElement()->Attribute( "xmi.version" ), "2.0" ); } @@ -623,7 +652,7 @@ int main( int /*argc*/, const char ** /*argv*/ ) XMLDocument doc; doc.Parse( doctype ); - XMLTest( "Parsing repeated attributes.", XML_ERROR_PARSING_ATTRIBUTE, doc.ErrorID() ); // is an error to tinyxml (didn't use to be, but caused issues) + XMLTest( "Parsing repeated attributes.", (int)XML_ERROR_PARSING_ATTRIBUTE, doc.ErrorID() ); // is an error to tinyxml (didn't use to be, but caused issues) doc.PrintError(); } @@ -641,7 +670,7 @@ int main( int /*argc*/, const char ** /*argv*/ ) const char* str = " "; XMLDocument doc; doc.Parse( str ); - XMLTest( "Empty document error", XML_ERROR_EMPTY_DOCUMENT, doc.ErrorID() ); + XMLTest( "Empty document error", (int)XML_ERROR_EMPTY_DOCUMENT, doc.ErrorID() ); } { @@ -668,7 +697,7 @@ int main( int /*argc*/, const char ** /*argv*/ ) xml.Parse(" "); XMLTest("Missing end tag with trailing whitespace", xml.Error(), true); xml.Parse(""); - XMLTest("Mismatched tags", xml.ErrorID(), XML_ERROR_MISMATCHED_ELEMENT); + XMLTest("Mismatched tags", xml.ErrorID(), (int)XML_ERROR_MISMATCHED_ELEMENT); } @@ -865,6 +894,40 @@ int main( int /*argc*/, const char ** /*argv*/ ) XMLTest( "BOM and default declaration", printer.CStr(), result, false ); XMLTest( "CStrSize", printer.CStrSize(), 42, false ); } + { + const char* xml = ""; + + TinyXML-2 has accessors for both approaches. + + When using an attribute, you navigate to the XMLElement + with that attribute and use the QueryIntAttribute() + group of methods. (Also QueryFloatAttribute(), etc.) + + @skip XMLElement* attributeApproachElement + @until &v0 ); + + When using the text approach, you need to navigate + down one more step to the XMLElement that contains + the text. Note the extra FirstChildElement( "v" ) + in the code below. The value of the text can then + be safely queried with the QueryIntText() group + of methods. (Also QueryFloatText(), etc.) + + @skip XMLElement* textApproachElement + @until &v1 ); +*/ +