Moving string in/out into XMLUtil. Using that across the API. Supporting text queries of primitive types.

This commit is contained in:
Lee Thomason 2012-07-15 17:27:22 -07:00
parent 78a773ddd9
commit 21be882810
6 changed files with 387 additions and 48 deletions

View File

@ -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")

2
dox
View File

@ -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

View File

@ -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 )
{

View File

@ -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
<foo>This is text</foo>
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
<foo><b>This is text</b></foo>
<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>
<foo>This is <b>text</b></foo>
@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
<point>
<x>1</x>
<y>1.4</y>
</point>
@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, // <foo>
@ -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 );

View File

@ -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 =
"<information>"
" <attributeApproach v='2' />"
" <textApproach>"
" <v>2</v>"
" </textApproach>"
"</information>";
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("<x> ");
XMLTest("Missing end tag with trailing whitespace", xml.Error(), true);
xml.Parse("<x></y>");
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 = "<ipxml ws='1'><info bla=' /></ipxml>";
XMLDocument doc;
doc.Parse( xml );
XMLTest( "Ill formed XML", true, doc.Error() );
}
// QueryXYZText
{
const char* xml = "<point> <x>1.2</x> <y>1</y> <z>38</z> <valid>true</valid> </point>";
XMLDocument doc;
doc.Parse( xml );
const XMLElement* pointElement = doc.RootElement();
int intValue = 0;
unsigned unsignedValue = 0;
float floatValue = 0;
double doubleValue = 0;
bool boolValue = false;
pointElement->FirstChildElement( "y" )->QueryIntText( &intValue );
pointElement->FirstChildElement( "y" )->QueryUnsignedText( &unsignedValue );
pointElement->FirstChildElement( "x" )->QueryFloatText( &floatValue );
pointElement->FirstChildElement( "x" )->QueryDoubleText( &doubleValue );
pointElement->FirstChildElement( "valid" )->QueryBoolText( &boolValue );
XMLTest( "QueryIntText", intValue, 1, false );
XMLTest( "QueryUnsignedText", unsignedValue, (unsigned)1, false );
XMLTest( "QueryFloatText", floatValue, 1.2f, false );
XMLTest( "QueryDoubleText", doubleValue, 1.2, false );
XMLTest( "QueryBoolText", boolValue, true, false );
}
// ----------- Performance tracking --------------

View File

@ -92,3 +92,38 @@
looking for XMLText, not an element, and ToText()
is a cast from a Node to a XMLText.
*/
/** @page Example-4 Read attributes and text information.
@dontinclude ./xmltest.cpp
There are fundamentally 2 ways of writing a key-value
pair into an XML file. (Something that's always annoyed
me about XML.) Either by using attributes, or by writing
the key name into an element and the value into
the text node wrapped by the element. Both approaches
are illustrated in this example, which shows two ways
to encode the value "2" into the key "v":
@skip example_4
@until "</information>";
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 );
*/