resources/out/
tinyxml2/tinyxml2-cbp/bin/
tinyxml2/tinyxml2-cbp/obj/
+tinyxml2/bin/
+tinyxml2/temp/
*.sdf
*.suo
*.opensdf
*.depend
*.layout
*.o
+*.vc.db
+*.vc.opendb
\ No newline at end of file
ENDIF(BIICODE)\r
cmake_minimum_required(VERSION 2.6 FATAL_ERROR)\r
cmake_policy(VERSION 2.6)\r
+if(POLICY CMP0063)\r
+ cmake_policy(SET CMP0063 OLD)\r
+endif()\r
\r
project(tinyxml2)\r
include(GNUInstallDirs)\r
################################\r
# set lib version here\r
\r
-set(GENERIC_LIB_VERSION "4.0.1")\r
-set(GENERIC_LIB_SOVERSION "4")\r
-\r
-\r
-################################\r
-# Add common source\r
-\r
-include_directories("${CMAKE_CURRENT_SOURCE_DIR}/.")\r
-\r
-################################\r
-# Add custom target to copy all data\r
-\r
-set(TARGET_DATA_COPY DATA_COPY)\r
-set(DATA_COPY_FILES)\r
-if(NOT ${CMAKE_CURRENT_SOURCE_DIR} STREQUAL ${CMAKE_CURRENT_BINARY_DIR})\r
- foreach(data dream.xml empty.xml utf8test.xml utf8testverify.xml)\r
- set(DATA_COPY_SRC ${CMAKE_CURRENT_SOURCE_DIR}/resources/${data})\r
- set(DATA_COPY_DEST ${CMAKE_CURRENT_BINARY_DIR}/resources/${data})\r
- add_custom_command(\r
- OUTPUT ${DATA_COPY_DEST}\r
- COMMAND ${CMAKE_COMMAND}\r
- ARGS -E copy ${DATA_COPY_SRC} ${DATA_COPY_DEST}\r
- DEPENDS ${DATA_COPY_SRC})\r
- list(APPEND DATA_COPY_FILES ${DATA_COPY_DEST})\r
- endforeach(data)\r
-endif(NOT ${CMAKE_CURRENT_SOURCE_DIR} STREQUAL ${CMAKE_CURRENT_BINARY_DIR})\r
-add_custom_target(${TARGET_DATA_COPY} DEPENDS ${DATA_COPY_FILES})\r
+set(GENERIC_LIB_VERSION "5.0.0")\r
+set(GENERIC_LIB_SOVERSION "5")\r
\r
################################\r
# Add definitions\r
\r
-if(MSVC)\r
- add_definitions(-D_CRT_SECURE_NO_WARNINGS)\r
-endif(MSVC)\r
-\r
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -DDEBUG")\r
\r
################################\r
# Add targets\r
# By Default shared libray is being built\r
# To build static libs also - Do cmake . -DBUILD_STATIC_LIBS:BOOL=ON\r
-# User can choose not to build shared library by using cmake -BUILD_SHARED_LIBS:BOOL:OFF\r
+# User can choose not to build shared library by using cmake -DBUILD_SHARED_LIBS:BOOL=OFF\r
# To build only static libs use cmake . -DBUILD_SHARED_LIBS:BOOL=OFF -DBUILD_STATIC_LIBS:BOOL=ON\r
+# To build the tests, use cmake . -DBUILD_TESTS:BOOL=ON\r
+# To disable the building of the tests, use cmake . -DBUILD_TESTS:BOOL=OFF\r
\r
option(BUILD_SHARED_LIBS "build as shared library" ON)\r
option(BUILD_STATIC_LIBS "build as static library" OFF)\r
+option(BUILD_TESTS "build xmltest" ON)\r
+\r
+set(CMAKE_CXX_VISIBILITY_PRESET hidden)\r
+set(CMAKE_VISIBILITY_INLINES_HIDDEN 1)\r
+\r
+# to distinguish between debug and release lib\r
+set(CMAKE_DEBUG_POSTFIX "d")\r
\r
if(BUILD_SHARED_LIBS)\r
add_library(tinyxml2 SHARED tinyxml2.cpp tinyxml2.h)\r
VERSION "${GENERIC_LIB_VERSION}"\r
SOVERSION "${GENERIC_LIB_SOVERSION}")\r
\r
+\r
if(DEFINED CMAKE_VERSION AND NOT "${CMAKE_VERSION}" VERSION_LESS "2.8.11")\r
- target_include_directories(tinyxml2 INTERFACE "${CMAKE_CURRENT_SOURCE_DIR}/.")\r
+ target_include_directories(tinyxml2 PUBLIC \r
+ $<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}>\r
+ $<INSTALL_INTERFACE:${CMAKE_INSTALL_PREFIX}/include>)\r
+\r
+ if(MSVC)\r
+ target_compile_definitions(tinyxml2 PUBLIC -D_CRT_SECURE_NO_WARNINGS)\r
+ endif(MSVC)\r
+else()\r
+ include_directories(${PROJECT_SOURCE_DIR})\r
+\r
+ if(MSVC)\r
+ add_definitions(-D_CRT_SECURE_NO_WARNINGS)\r
+ endif(MSVC)\r
endif()\r
\r
+# export targets for find_package config mode\r
+export(TARGETS tinyxml2\r
+ FILE ${CMAKE_BINARY_DIR}/${CMAKE_PROJECT_NAME}Targets.cmake)\r
+\r
install(TARGETS tinyxml2\r
+ EXPORT ${CMAKE_PROJECT_NAME}Targets\r
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}\r
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}\r
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR})\r
SOVERSION "${GENERIC_LIB_SOVERSION}")\r
set_target_properties( tinyxml2_static PROPERTIES OUTPUT_NAME tinyxml2 )\r
\r
+target_compile_definitions(tinyxml2 PUBLIC -D_CRT_SECURE_NO_WARNINGS)\r
+\r
if(DEFINED CMAKE_VERSION AND NOT "${CMAKE_VERSION}" VERSION_LESS "2.8.11")\r
- target_include_directories(tinyxml2_static INTERFACE "${CMAKE_CURRENT_SOURCE_DIR}/.")\r
+ target_include_directories(tinyxml2_static PUBLIC \r
+ $<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}>\r
+ $<INSTALL_INTERFACE:${CMAKE_INSTALL_PREFIX}/include>)\r
+\r
+ if(MSVC)\r
+ target_compile_definitions(tinyxml2 PUBLIC -D_CRT_SECURE_NO_WARNINGS)\r
+ endif(MSVC)\r
+else()\r
+ include_directories(${PROJECT_SOURCE_DIR})\r
+\r
+ if(MSVC)\r
+ add_definitions(-D_CRT_SECURE_NO_WARNINGS)\r
+ endif(MSVC)\r
endif()\r
\r
+# export targets for find_package config mode\r
+export(TARGETS tinyxml2_static\r
+ FILE ${CMAKE_BINARY_DIR}/${CMAKE_PROJECT_NAME}Targets.cmake)\r
+\r
install(TARGETS tinyxml2_static\r
+ EXPORT ${CMAKE_PROJECT_NAME}Targets\r
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}\r
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}\r
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR})\r
endif()\r
\r
-add_executable(xmltest xmltest.cpp)\r
-if(BUILD_SHARED_LIBS)\r
- add_dependencies(xmltest tinyxml2)\r
- add_dependencies(xmltest ${TARGET_DATA_COPY})\r
- target_link_libraries(xmltest tinyxml2)\r
-else(BUILD_STATIC_LIBS)\r
- add_dependencies(xmltest tinyxml2_static)\r
- add_dependencies(xmltest ${TARGET_DATA_COPY})\r
- target_link_libraries(xmltest tinyxml2_static)\r
+if(BUILD_TESTS)\r
+ add_executable(xmltest xmltest.cpp)\r
+ if(BUILD_SHARED_LIBS)\r
+ add_dependencies(xmltest tinyxml2)\r
+ target_link_libraries(xmltest tinyxml2)\r
+ else(BUILD_STATIC_LIBS)\r
+ add_dependencies(xmltest tinyxml2_static)\r
+ target_link_libraries(xmltest tinyxml2_static)\r
+ endif()\r
+\r
+ # Copy test resources and create test output directory\r
+ add_custom_command(TARGET xmltest POST_BUILD\r
+ COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_SOURCE_DIR}/resources $<TARGET_FILE_DIR:xmltest>/resources\r
+ COMMAND ${CMAKE_COMMAND} -E make_directory $<TARGET_FILE_DIR:xmltest>/resources/out\r
+ COMMENT "Configuring xmltest resources directory: ${CMAKE_BINARY_DIR}/resources"\r
+ )\r
endif()\r
-install(TARGETS DESTINATION ${CMAKE_INSTALL_BINDIR})\r
\r
install(FILES tinyxml2.h DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})\r
\r
configure_file(tinyxml2.pc.in tinyxml2.pc @ONLY)\r
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/tinyxml2.pc DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig)\r
\r
-#add_test(xmltest ${SAMPLE_NAME} COMMAND $<TARGET_FILE:${SAMPLE_NAME}>)\r
-\r
# uninstall target\r
configure_file(\r
"${CMAKE_CURRENT_SOURCE_DIR}/cmake_uninstall.cmake.in"\r
\r
add_custom_target(uninstall\r
COMMAND ${CMAKE_COMMAND} -P ${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake)\r
+\r
+file(WRITE\r
+ ${CMAKE_BINARY_DIR}/${CMAKE_PROJECT_NAME}Config.cmake\r
+ "include(\${CMAKE_CURRENT_LIST_DIR}/${CMAKE_PROJECT_NAME}Targets.cmake)\n")\r
+\r
+install(FILES\r
+ ${CMAKE_BINARY_DIR}/${CMAKE_PROJECT_NAME}Config.cmake\r
+ DESTINATION lib/cmake/${CMAKE_PROJECT_NAME})\r
+\r
+install(EXPORT ${CMAKE_PROJECT_NAME}Targets\r
+ DESTINATION lib/cmake/${CMAKE_PROJECT_NAME})
\ No newline at end of file
build_script:
- msbuild tinyxml2.sln /m /p:Configuration=Release /t:ALL_BUILD
- - copy Release\xmltest.exe .\ && copy Release\tinyxml2.dll .\
+ - cd Release
- xmltest.exe
# could be handy for archiving the generated documentation or if some version\r
# control system is used.\r
\r
-PROJECT_NUMBER = 4.0.1\r
+PROJECT_NUMBER = 5.0.0\r
\r
# Using the PROJECT_BRIEF tag one can provide an optional one line description\r
# for a project that appears at the top of each page and should give viewer a\r
Advantages of TinyXML-1
-1. Can report the location of parsing errors.
-2. Support for some C++ STL conventions: streams and strings
-3. Very mature and well debugged code base.
+1. Support for some C++ STL conventions: streams and strings
+2. Very mature and well debugged code base.
Features
--------
Microsoft has an excellent article on white space: http://msdn.microsoft.com/en-us/library/ms256097.aspx
-By default, TinyXML-2 preserves white space in a (hopefully) sane way that is almost complient with the
+By default, TinyXML-2 preserves white space in a (hopefully) sane way that is almost compliant with the
spec. (TinyXML-1 used a completely different model, much more similar to 'collapse', below.)
As a first step, all newlines / carriage-returns / line-feeds are normalized to a
Note that (currently) there is a performance impact for using COLLAPSE_WHITESPACE.
It essentially causes the XML to be parsed twice.
+#### Error Reporting
+
+TinyXML-2 reports the line number of any errors in an XML document that
+cannot be parsed correctly. In addition, all nodes (elements, declarations,
+text, comments etc.) and attributes have a line number recorded as they are parsed.
+This allows an application that performs additional validation of the parsed
+XML document (e.g. application-implemented DTD validation) to report
+line number information in it's errors.
+
### Entities
TinyXML-2 recognizes the pre-defined "character entities", meaning special
// This in effect implements the assignment operator by "moving"\r
// ownership (as in auto_ptr).\r
\r
+ TIXMLASSERT( other != 0 );\r
TIXMLASSERT( other->_flags == 0 );\r
TIXMLASSERT( other->_start == 0 );\r
TIXMLASSERT( other->_end == 0 );\r
}\r
\r
\r
-char* StrPair::ParseText( char* p, const char* endTag, int strFlags )\r
+char* StrPair::ParseText( char* p, const char* endTag, int strFlags, int* curLineNumPtr )\r
{\r
+ TIXMLASSERT( p );\r
TIXMLASSERT( endTag && *endTag );\r
+ TIXMLASSERT(curLineNumPtr);\r
\r
char* start = p;\r
char endChar = *endTag;\r
if ( *p == endChar && strncmp( p, endTag, length ) == 0 ) {\r
Set( start, p, strFlags );\r
return p + length;\r
+ } else if (*p == '\n') {\r
+ ++(*curLineNumPtr);\r
}\r
++p;\r
+ TIXMLASSERT( p );\r
}\r
return 0;\r
}\r
// Adjusting _start would cause undefined behavior on delete[]\r
TIXMLASSERT( ( _flags & NEEDS_DELETE ) == 0 );\r
// Trim leading space.\r
- _start = XMLUtil::SkipWhiteSpace( _start );\r
+ _start = XMLUtil::SkipWhiteSpace( _start, 0 );\r
\r
if ( *_start ) {\r
- char* p = _start; // the read pointer\r
+ const char* p = _start; // the read pointer\r
char* q = _start; // the write pointer\r
\r
while( *p ) {\r
if ( XMLUtil::IsWhiteSpace( *p )) {\r
- p = XMLUtil::SkipWhiteSpace( p );\r
+ p = XMLUtil::SkipWhiteSpace( p, 0 );\r
if ( *p == 0 ) {\r
break; // don't write to q; this trims the trailing space.\r
}\r
_flags ^= NEEDS_FLUSH;\r
\r
if ( _flags ) {\r
- char* p = _start; // the read pointer\r
+ const char* p = _start; // the read pointer\r
char* q = _start; // the write pointer\r
\r
while( p < _end ) {\r
else {\r
++p;\r
}\r
- *q++ = LF;\r
+ *q = LF;\r
+ ++q;\r
}\r
else if ( (_flags & NEEDS_NEWLINE_NORMALIZATION) && *p == LF ) {\r
if ( *(p+1) == CR ) {\r
else {\r
++p;\r
}\r
- *q++ = LF;\r
+ *q = LF;\r
+ ++q;\r
}\r
else if ( (_flags & NEEDS_ENTITY_PROCESSING) && *p == '&' ) {\r
// Entities handled by tinyXML2:\r
\r
// --------- XMLUtil ----------- //\r
\r
+const char* XMLUtil::writeBoolTrue = "true";\r
+const char* XMLUtil::writeBoolFalse = "false";\r
+\r
+void XMLUtil::SetBoolSerialization(const char* writeTrue, const char* writeFalse)\r
+{\r
+ static const char* defTrue = "true";\r
+ static const char* defFalse = "false";\r
+\r
+ writeBoolTrue = (writeTrue) ? writeTrue : defTrue;\r
+ writeBoolFalse = (writeFalse) ? writeFalse : defFalse;\r
+}\r
+\r
+\r
const char* XMLUtil::ReadBOM( const char* p, bool* bom )\r
{\r
TIXMLASSERT( p );\r
\r
void XMLUtil::ToStr( bool v, char* buffer, int bufferSize )\r
{\r
- TIXML_SNPRINTF( buffer, bufferSize, "%d", v ? 1 : 0 );\r
+ TIXML_SNPRINTF( buffer, bufferSize, "%s", v ? writeBoolTrue : writeBoolFalse);\r
}\r
\r
/*\r
TIXMLASSERT( node );\r
TIXMLASSERT( p );\r
char* const start = p;\r
- p = XMLUtil::SkipWhiteSpace( p );\r
+ int const startLine = _parseCurLineNum;\r
+ p = XMLUtil::SkipWhiteSpace( p, &_parseCurLineNum );\r
if( !*p ) {\r
*node = 0;\r
TIXMLASSERT( p );\r
TIXMLASSERT( sizeof( XMLComment ) == sizeof( XMLDeclaration ) ); // use same memory pool\r
XMLNode* returnNode = 0;\r
if ( XMLUtil::StringEqual( p, xmlHeader, xmlHeaderLen ) ) {\r
- TIXMLASSERT( sizeof( XMLDeclaration ) == _commentPool.ItemSize() );\r
- returnNode = new (_commentPool.Alloc()) XMLDeclaration( this );\r
- returnNode->_memPool = &_commentPool;\r
+ returnNode = CreateUnlinkedNode<XMLDeclaration>( _commentPool );\r
+ returnNode->_parseLineNum = _parseCurLineNum;\r
p += xmlHeaderLen;\r
}\r
else if ( XMLUtil::StringEqual( p, commentHeader, commentHeaderLen ) ) {\r
- TIXMLASSERT( sizeof( XMLComment ) == _commentPool.ItemSize() );\r
- returnNode = new (_commentPool.Alloc()) XMLComment( this );\r
- returnNode->_memPool = &_commentPool;\r
+ returnNode = CreateUnlinkedNode<XMLComment>( _commentPool );\r
+ returnNode->_parseLineNum = _parseCurLineNum;\r
p += commentHeaderLen;\r
}\r
else if ( XMLUtil::StringEqual( p, cdataHeader, cdataHeaderLen ) ) {\r
- TIXMLASSERT( sizeof( XMLText ) == _textPool.ItemSize() );\r
- XMLText* text = new (_textPool.Alloc()) XMLText( this );\r
+ XMLText* text = CreateUnlinkedNode<XMLText>( _textPool );\r
returnNode = text;\r
- returnNode->_memPool = &_textPool;\r
+ returnNode->_parseLineNum = _parseCurLineNum;\r
p += cdataHeaderLen;\r
text->SetCData( true );\r
}\r
else if ( XMLUtil::StringEqual( p, dtdHeader, dtdHeaderLen ) ) {\r
- TIXMLASSERT( sizeof( XMLUnknown ) == _commentPool.ItemSize() );\r
- returnNode = new (_commentPool.Alloc()) XMLUnknown( this );\r
- returnNode->_memPool = &_commentPool;\r
+ returnNode = CreateUnlinkedNode<XMLUnknown>( _commentPool );\r
+ returnNode->_parseLineNum = _parseCurLineNum;\r
p += dtdHeaderLen;\r
}\r
else if ( XMLUtil::StringEqual( p, elementHeader, elementHeaderLen ) ) {\r
- TIXMLASSERT( sizeof( XMLElement ) == _elementPool.ItemSize() );\r
- returnNode = new (_elementPool.Alloc()) XMLElement( this );\r
- returnNode->_memPool = &_elementPool;\r
+ returnNode = CreateUnlinkedNode<XMLElement>( _elementPool );\r
+ returnNode->_parseLineNum = _parseCurLineNum;\r
p += elementHeaderLen;\r
}\r
else {\r
- TIXMLASSERT( sizeof( XMLText ) == _textPool.ItemSize() );\r
- returnNode = new (_textPool.Alloc()) XMLText( this );\r
- returnNode->_memPool = &_textPool;\r
+ returnNode = CreateUnlinkedNode<XMLText>( _textPool );\r
+ returnNode->_parseLineNum = _parseCurLineNum; // Report line of first non-whitespace character\r
p = start; // Back it up, all the text counts.\r
+ _parseCurLineNum = startLine;\r
}\r
\r
TIXMLASSERT( returnNode );\r
XMLNode::XMLNode( XMLDocument* doc ) :\r
_document( doc ),\r
_parent( 0 ),\r
+ _parseLineNum( 0 ),\r
_firstChild( 0 ), _lastChild( 0 ),\r
_prev( 0 ), _next( 0 ),\r
_userData( 0 ),\r
\r
const char* XMLNode::Value() const \r
{\r
- // Catch an edge case: XMLDocuments don't have a a Value. Carefully return nullptr.\r
+ // Edge case: XMLDocuments don't have a Value. Return null.\r
if ( this->ToDocument() )\r
return 0;\r
return _value.GetStr();\r
}\r
}\r
\r
+XMLNode* XMLNode::DeepClone(XMLDocument* target) const\r
+{\r
+ XMLNode* clone = this->ShallowClone(target);\r
+ if (!clone) return 0;\r
+\r
+ for (const XMLNode* child = this->FirstChild(); child; child = child->NextSibling()) {\r
+ XMLNode* childClone = child->DeepClone(target);\r
+ TIXMLASSERT(childClone);\r
+ clone->InsertEndChild(childClone);\r
+ }\r
+ return clone;\r
+}\r
\r
void XMLNode::DeleteChildren()\r
{\r
while( _firstChild ) {\r
TIXMLASSERT( _lastChild );\r
- TIXMLASSERT( _firstChild->_document == _document );\r
- XMLNode* node = _firstChild;\r
- Unlink( node );\r
-\r
- DeleteNode( node );\r
+ DeleteChild( _firstChild );\r
}\r
_firstChild = _lastChild = 0;\r
}\r
if ( child->_next ) {\r
child->_next->_prev = child->_prev;\r
}\r
+ child->_next = 0;\r
+ child->_prev = 0;\r
child->_parent = 0;\r
}\r
\r
TIXMLASSERT( node->_document == _document );\r
TIXMLASSERT( node->_parent == this );\r
Unlink( node );\r
+ TIXMLASSERT(node->_prev == 0);\r
+ TIXMLASSERT(node->_next == 0);\r
+ TIXMLASSERT(node->_parent == 0);\r
DeleteNode( node );\r
}\r
\r
const XMLElement* XMLNode::FirstChildElement( const char* name ) const\r
{\r
for( const XMLNode* node = _firstChild; node; node = node->_next ) {\r
- const XMLElement* element = node->ToElement();\r
+ const XMLElement* element = node->ToElementWithName( name );\r
if ( element ) {\r
- if ( !name || XMLUtil::StringEqual( element->Name(), name ) ) {\r
- return element;\r
- }\r
+ return element;\r
}\r
}\r
return 0;\r
const XMLElement* XMLNode::LastChildElement( const char* name ) const\r
{\r
for( const XMLNode* node = _lastChild; node; node = node->_prev ) {\r
- const XMLElement* element = node->ToElement();\r
+ const XMLElement* element = node->ToElementWithName( name );\r
if ( element ) {\r
- if ( !name || XMLUtil::StringEqual( element->Name(), name ) ) {\r
- return element;\r
- }\r
+ return element;\r
}\r
}\r
return 0;\r
const XMLElement* XMLNode::NextSiblingElement( const char* name ) const\r
{\r
for( const XMLNode* node = _next; node; node = node->_next ) {\r
- const XMLElement* element = node->ToElement();\r
- if ( element\r
- && (!name || XMLUtil::StringEqual( name, element->Name() ))) {\r
+ const XMLElement* element = node->ToElementWithName( name );\r
+ if ( element ) {\r
return element;\r
}\r
}\r
const XMLElement* XMLNode::PreviousSiblingElement( const char* name ) const\r
{\r
for( const XMLNode* node = _prev; node; node = node->_prev ) {\r
- const XMLElement* element = node->ToElement();\r
- if ( element\r
- && (!name || XMLUtil::StringEqual( name, element->Name() ))) {\r
+ const XMLElement* element = node->ToElementWithName( name );\r
+ if ( element ) {\r
return element;\r
}\r
}\r
}\r
\r
\r
-char* XMLNode::ParseDeep( char* p, StrPair* parentEnd )\r
+char* XMLNode::ParseDeep( char* p, StrPair* parentEndTag, int* curLineNumPtr )\r
{\r
// This is a recursive method, but thinking about it "at the current level"\r
// it is a pretty simple flat list:\r
XMLNode* node = 0;\r
\r
p = _document->Identify( p, &node );\r
+ TIXMLASSERT( p );\r
if ( node == 0 ) {\r
break;\r
}\r
\r
+ int initialLineNum = node->_parseLineNum;\r
+\r
StrPair endTag;\r
- p = node->ParseDeep( p, &endTag );\r
+ p = node->ParseDeep( p, &endTag, curLineNumPtr );\r
if ( !p ) {\r
DeleteNode( node );\r
if ( !_document->Error() ) {\r
- _document->SetError( XML_ERROR_PARSING, 0, 0 );\r
+ _document->SetError( XML_ERROR_PARSING, 0, 0, initialLineNum);\r
}\r
break;\r
}\r
\r
XMLDeclaration* decl = node->ToDeclaration();\r
if ( decl ) {\r
- // A declaration can only be the first child of a document.\r
- // Set error, if document already has children.\r
- if ( !_document->NoChildren() ) {\r
- _document->SetError( XML_ERROR_PARSING_DECLARATION, decl->Value(), 0);\r
- DeleteNode( decl );\r
+ // Declarations are only allowed at document level\r
+ bool wellLocated = ( ToDocument() != 0 );\r
+ if ( wellLocated ) {\r
+ // Multiple declarations are allowed but all declarations\r
+ // must occur before anything else\r
+ for ( const XMLNode* existingNode = _document->FirstChild(); existingNode; existingNode = existingNode->NextSibling() ) {\r
+ if ( !existingNode->ToDeclaration() ) {\r
+ wellLocated = false;\r
break;\r
+ }\r
}\r
+ }\r
+ if ( !wellLocated ) {\r
+ _document->SetError( XML_ERROR_PARSING_DECLARATION, decl->Value(), 0, initialLineNum);\r
+ DeleteNode( node );\r
+ break;\r
+ }\r
}\r
\r
XMLElement* ele = node->ToElement();\r
if ( ele ) {\r
// We read the end tag. Return it to the parent.\r
if ( ele->ClosingType() == XMLElement::CLOSING ) {\r
- if ( parentEnd ) {\r
- ele->_value.TransferTo( parentEnd );\r
+ if ( parentEndTag ) {\r
+ ele->_value.TransferTo( parentEndTag );\r
}\r
node->_memPool->SetTracked(); // created and then immediately deleted.\r
DeleteNode( node );\r
}\r
}\r
if ( mismatch ) {\r
- _document->SetError( XML_ERROR_MISMATCHED_ELEMENT, ele->Name(), 0 );\r
+ _document->SetError( XML_ERROR_MISMATCHED_ELEMENT, ele->Name(), 0, initialLineNum);\r
DeleteNode( node );\r
break;\r
}\r
return 0;\r
}\r
\r
-void XMLNode::DeleteNode( XMLNode* node )\r
+/*static*/ void XMLNode::DeleteNode( XMLNode* node )\r
{\r
if ( node == 0 ) {\r
return;\r
}\r
+ TIXMLASSERT(node->_document);\r
+ if (!node->ToDocument()) {\r
+ node->_document->MarkInUse(node);\r
+ }\r
+\r
MemPool* pool = node->_memPool;\r
node->~XMLNode();\r
pool->Free( node );\r
TIXMLASSERT( insertThis );\r
TIXMLASSERT( insertThis->_document == _document );\r
\r
- if ( insertThis->_parent )\r
+ if (insertThis->_parent) {\r
insertThis->_parent->Unlink( insertThis );\r
- else\r
+ }\r
+ else {\r
+ insertThis->_document->MarkInUse(insertThis);\r
insertThis->_memPool->SetTracked();\r
+ }\r
+}\r
+\r
+const XMLElement* XMLNode::ToElementWithName( const char* name ) const\r
+{\r
+ const XMLElement* element = this->ToElement();\r
+ if ( element == 0 ) {\r
+ return 0;\r
+ }\r
+ if ( name == 0 ) {\r
+ return element;\r
+ }\r
+ if ( XMLUtil::StringEqual( element->Name(), name ) ) {\r
+ return element;\r
+ }\r
+ return 0;\r
}\r
\r
// --------- XMLText ---------- //\r
-char* XMLText::ParseDeep( char* p, StrPair* )\r
+char* XMLText::ParseDeep( char* p, StrPair*, int* curLineNumPtr )\r
{\r
const char* start = p;\r
if ( this->CData() ) {\r
- p = _value.ParseText( p, "]]>", StrPair::NEEDS_NEWLINE_NORMALIZATION );\r
+ p = _value.ParseText( p, "]]>", StrPair::NEEDS_NEWLINE_NORMALIZATION, curLineNumPtr );\r
if ( !p ) {\r
- _document->SetError( XML_ERROR_PARSING_CDATA, start, 0 );\r
+ _document->SetError( XML_ERROR_PARSING_CDATA, start, 0, _parseLineNum );\r
}\r
return p;\r
}\r
flags |= StrPair::NEEDS_WHITESPACE_COLLAPSING;\r
}\r
\r
- p = _value.ParseText( p, "<", flags );\r
+ p = _value.ParseText( p, "<", flags, curLineNumPtr );\r
if ( p && *p ) {\r
return p-1;\r
}\r
if ( !p ) {\r
- _document->SetError( XML_ERROR_PARSING_TEXT, start, 0 );\r
+ _document->SetError( XML_ERROR_PARSING_TEXT, start, 0, _parseLineNum );\r
}\r
}\r
return 0;\r
\r
bool XMLText::ShallowEqual( const XMLNode* compare ) const\r
{\r
+ TIXMLASSERT( compare );\r
const XMLText* text = compare->ToText();\r
return ( text && XMLUtil::StringEqual( text->Value(), Value() ) );\r
}\r
}\r
\r
\r
-char* XMLComment::ParseDeep( char* p, StrPair* )\r
+char* XMLComment::ParseDeep( char* p, StrPair*, int* curLineNumPtr )\r
{\r
// Comment parses as text.\r
const char* start = p;\r
- p = _value.ParseText( p, "-->", StrPair::COMMENT );\r
+ p = _value.ParseText( p, "-->", StrPair::COMMENT, curLineNumPtr );\r
if ( p == 0 ) {\r
- _document->SetError( XML_ERROR_PARSING_COMMENT, start, 0 );\r
+ _document->SetError( XML_ERROR_PARSING_COMMENT, start, 0, _parseLineNum );\r
}\r
return p;\r
}\r
}\r
\r
\r
-char* XMLDeclaration::ParseDeep( char* p, StrPair* )\r
+char* XMLDeclaration::ParseDeep( char* p, StrPair*, int* curLineNumPtr )\r
{\r
// Declaration parses as text.\r
const char* start = p;\r
- p = _value.ParseText( p, "?>", StrPair::NEEDS_NEWLINE_NORMALIZATION );\r
+ p = _value.ParseText( p, "?>", StrPair::NEEDS_NEWLINE_NORMALIZATION, curLineNumPtr );\r
if ( p == 0 ) {\r
- _document->SetError( XML_ERROR_PARSING_DECLARATION, start, 0 );\r
+ _document->SetError( XML_ERROR_PARSING_DECLARATION, start, 0, _parseLineNum );\r
}\r
return p;\r
}\r
}\r
\r
\r
-char* XMLUnknown::ParseDeep( char* p, StrPair* )\r
+char* XMLUnknown::ParseDeep( char* p, StrPair*, int* curLineNumPtr )\r
{\r
// Unknown parses as text.\r
const char* start = p;\r
\r
- p = _value.ParseText( p, ">", StrPair::NEEDS_NEWLINE_NORMALIZATION );\r
+ p = _value.ParseText( p, ">", StrPair::NEEDS_NEWLINE_NORMALIZATION, curLineNumPtr );\r
if ( !p ) {\r
- _document->SetError( XML_ERROR_PARSING_UNKNOWN, start, 0 );\r
+ _document->SetError( XML_ERROR_PARSING_UNKNOWN, start, 0, _parseLineNum );\r
}\r
return p;\r
}\r
return _value.GetStr();\r
}\r
\r
-char* XMLAttribute::ParseDeep( char* p, bool processEntities )\r
+char* XMLAttribute::ParseDeep( char* p, bool processEntities, int* curLineNumPtr )\r
{\r
// Parse using the name rules: bug fix, was using ParseText before\r
p = _name.ParseName( p );\r
}\r
\r
// Skip white space before =\r
- p = XMLUtil::SkipWhiteSpace( p );\r
+ p = XMLUtil::SkipWhiteSpace( p, curLineNumPtr );\r
if ( *p != '=' ) {\r
return 0;\r
}\r
\r
++p; // move up to opening quote\r
- p = XMLUtil::SkipWhiteSpace( p );\r
+ p = XMLUtil::SkipWhiteSpace( p, curLineNumPtr );\r
if ( *p != '\"' && *p != '\'' ) {\r
return 0;\r
}\r
char endTag[2] = { *p, 0 };\r
++p; // move past opening quote\r
\r
- p = _value.ParseText( p, endTag, processEntities ? StrPair::ATTRIBUTE_VALUE : StrPair::ATTRIBUTE_VALUE_LEAVE_ENTITIES );\r
+ p = _value.ParseText( p, endTag, processEntities ? StrPair::ATTRIBUTE_VALUE : StrPair::ATTRIBUTE_VALUE_LEAVE_ENTITIES, curLineNumPtr );\r
return p;\r
}\r
\r
\r
// --------- XMLElement ---------- //\r
XMLElement::XMLElement( XMLDocument* doc ) : XMLNode( doc ),\r
- _closingType( 0 ),\r
+ _closingType( OPEN ),\r
_rootAttribute( 0 )\r
{\r
}\r
return 0;\r
}\r
\r
+int XMLElement::IntAttribute(const char* name, int defaultValue) const \r
+{\r
+ int i = defaultValue;\r
+ QueryIntAttribute(name, &i);\r
+ return i;\r
+}\r
+\r
+unsigned XMLElement::UnsignedAttribute(const char* name, unsigned defaultValue) const \r
+{\r
+ unsigned i = defaultValue;\r
+ QueryUnsignedAttribute(name, &i);\r
+ return i;\r
+}\r
+\r
+int64_t XMLElement::Int64Attribute(const char* name, int64_t defaultValue) const \r
+{\r
+ int64_t i = defaultValue;\r
+ QueryInt64Attribute(name, &i);\r
+ return i;\r
+}\r
+\r
+bool XMLElement::BoolAttribute(const char* name, bool defaultValue) const \r
+{\r
+ bool b = defaultValue;\r
+ QueryBoolAttribute(name, &b);\r
+ return b;\r
+}\r
+\r
+double XMLElement::DoubleAttribute(const char* name, double defaultValue) const \r
+{\r
+ double d = defaultValue;\r
+ QueryDoubleAttribute(name, &d);\r
+ return d;\r
+}\r
+\r
+float XMLElement::FloatAttribute(const char* name, float defaultValue) const \r
+{\r
+ float f = defaultValue;\r
+ QueryFloatAttribute(name, &f);\r
+ return f;\r
+}\r
\r
const char* XMLElement::GetText() const\r
{\r
return XML_NO_TEXT_NODE;\r
}\r
\r
+int XMLElement::IntText(int defaultValue) const\r
+{\r
+ int i = defaultValue;\r
+ QueryIntText(&i);\r
+ return i;\r
+}\r
+\r
+unsigned XMLElement::UnsignedText(unsigned defaultValue) const\r
+{\r
+ unsigned i = defaultValue;\r
+ QueryUnsignedText(&i);\r
+ return i;\r
+}\r
+\r
+int64_t XMLElement::Int64Text(int64_t defaultValue) const\r
+{\r
+ int64_t i = defaultValue;\r
+ QueryInt64Text(&i);\r
+ return i;\r
+}\r
+\r
+bool XMLElement::BoolText(bool defaultValue) const\r
+{\r
+ bool b = defaultValue;\r
+ QueryBoolText(&b);\r
+ return b;\r
+}\r
+\r
+double XMLElement::DoubleText(double defaultValue) const\r
+{\r
+ double d = defaultValue;\r
+ QueryDoubleText(&d);\r
+ return d;\r
+}\r
+\r
+float XMLElement::FloatText(float defaultValue) const\r
+{\r
+ float f = defaultValue;\r
+ QueryFloatText(&f);\r
+ return f;\r
+}\r
\r
\r
XMLAttribute* XMLElement::FindOrCreateAttribute( const char* name )\r
}\r
}\r
if ( !attrib ) {\r
- TIXMLASSERT( sizeof( XMLAttribute ) == _document->_attributePool.ItemSize() );\r
- attrib = new (_document->_attributePool.Alloc() ) XMLAttribute();\r
- attrib->_memPool = &_document->_attributePool;\r
+ attrib = CreateAttribute();\r
+ TIXMLASSERT( attrib );\r
if ( last ) {\r
+ TIXMLASSERT( last->_next == 0 );\r
last->_next = attrib;\r
}\r
else {\r
+ TIXMLASSERT( _rootAttribute == 0 );\r
_rootAttribute = attrib;\r
}\r
attrib->SetName( name );\r
- attrib->_memPool->SetTracked(); // always created and linked.\r
}\r
return attrib;\r
}\r
}\r
\r
\r
-char* XMLElement::ParseAttributes( char* p )\r
+char* XMLElement::ParseAttributes( char* p, int* curLineNumPtr )\r
{\r
const char* start = p;\r
XMLAttribute* prevAttribute = 0;\r
\r
// Read the attributes.\r
while( p ) {\r
- p = XMLUtil::SkipWhiteSpace( p );\r
+ p = XMLUtil::SkipWhiteSpace( p, curLineNumPtr );\r
if ( !(*p) ) {\r
- _document->SetError( XML_ERROR_PARSING_ELEMENT, start, Name() );\r
+ _document->SetError( XML_ERROR_PARSING_ELEMENT, start, Name(), _parseLineNum );\r
return 0;\r
}\r
\r
// attribute.\r
if (XMLUtil::IsNameStartChar( *p ) ) {\r
- TIXMLASSERT( sizeof( XMLAttribute ) == _document->_attributePool.ItemSize() );\r
- XMLAttribute* attrib = new (_document->_attributePool.Alloc() ) XMLAttribute();\r
- attrib->_memPool = &_document->_attributePool;\r
- attrib->_memPool->SetTracked();\r
+ XMLAttribute* attrib = CreateAttribute();\r
+ TIXMLASSERT( attrib );\r
+ attrib->_parseLineNum = _document->_parseCurLineNum;\r
+\r
+ int attrLineNum = attrib->_parseLineNum;\r
\r
- p = attrib->ParseDeep( p, _document->ProcessEntities() );\r
+ p = attrib->ParseDeep( p, _document->ProcessEntities(), curLineNumPtr );\r
if ( !p || Attribute( attrib->Name() ) ) {\r
DeleteAttribute( attrib );\r
- _document->SetError( XML_ERROR_PARSING_ATTRIBUTE, start, p );\r
+ _document->SetError( XML_ERROR_PARSING_ATTRIBUTE, start, p, attrLineNum );\r
return 0;\r
}\r
// There is a minor bug here: if the attribute in the source xml\r
// avoids re-scanning the attribute list. Preferring performance for\r
// now, may reconsider in the future.\r
if ( prevAttribute ) {\r
+ TIXMLASSERT( prevAttribute->_next == 0 );\r
prevAttribute->_next = attrib;\r
}\r
else {\r
+ TIXMLASSERT( _rootAttribute == 0 );\r
_rootAttribute = attrib;\r
}\r
prevAttribute = attrib;\r
return p+2; // done; sealed element.\r
}\r
else {\r
- _document->SetError( XML_ERROR_PARSING_ELEMENT, start, p );\r
+ _document->SetError( XML_ERROR_PARSING_ELEMENT, start, p, _parseLineNum );\r
return 0;\r
}\r
}\r
pool->Free( attribute );\r
}\r
\r
+XMLAttribute* XMLElement::CreateAttribute()\r
+{\r
+ TIXMLASSERT( sizeof( XMLAttribute ) == _document->_attributePool.ItemSize() );\r
+ XMLAttribute* attrib = new (_document->_attributePool.Alloc() ) XMLAttribute();\r
+ TIXMLASSERT( attrib );\r
+ attrib->_memPool = &_document->_attributePool;\r
+ attrib->_memPool->SetTracked();\r
+ return attrib;\r
+}\r
+\r
//\r
// <ele></ele>\r
// <ele>foo<b>bar</b></ele>\r
//\r
-char* XMLElement::ParseDeep( char* p, StrPair* strPair )\r
+char* XMLElement::ParseDeep( char* p, StrPair* parentEndTag, int* curLineNumPtr )\r
{\r
// Read the element name.\r
- p = XMLUtil::SkipWhiteSpace( p );\r
+ p = XMLUtil::SkipWhiteSpace( p, curLineNumPtr );\r
\r
// The closing element is the </element> form. It is\r
// parsed just like a regular element then deleted from\r
return 0;\r
}\r
\r
- p = ParseAttributes( p );\r
- if ( !p || !*p || _closingType ) {\r
+ p = ParseAttributes( p, curLineNumPtr );\r
+ if ( !p || !*p || _closingType != OPEN ) {\r
return p;\r
}\r
\r
- p = XMLNode::ParseDeep( p, strPair );\r
+ p = XMLNode::ParseDeep( p, parentEndTag, curLineNumPtr );\r
return p;\r
}\r
\r
};\r
\r
\r
-XMLDocument::XMLDocument( bool processEntities, Whitespace whitespace ) :\r
+XMLDocument::XMLDocument( bool processEntities, Whitespace whitespaceMode ) :\r
XMLNode( 0 ),\r
_writeBOM( false ),\r
_processEntities( processEntities ),\r
_errorID(XML_SUCCESS),\r
- _whitespace( whitespace ),\r
- _errorStr1( 0 ),\r
- _errorStr2( 0 ),\r
- _charBuffer( 0 )\r
+ _whitespaceMode( whitespaceMode ),\r
+ _errorLineNum( 0 ),\r
+ _charBuffer( 0 ),\r
+ _parseCurLineNum( 0 )\r
{\r
// avoid VC++ C4355 warning about 'this' in initializer list (C4355 is off by default in VS2012+)\r
_document = this;\r
}\r
\r
\r
+void XMLDocument::MarkInUse(XMLNode* node)\r
+{\r
+ TIXMLASSERT(node);\r
+ TIXMLASSERT(node->_parent == 0);\r
+\r
+ for (int i = 0; i < _unlinked.Size(); ++i) {\r
+ if (node == _unlinked[i]) {\r
+ _unlinked.SwapRemove(i);\r
+ break;\r
+ }\r
+ }\r
+}\r
+\r
void XMLDocument::Clear()\r
{\r
DeleteChildren();\r
+ while( _unlinked.Size()) {\r
+ DeleteNode(_unlinked[0]); // Will remove from _unlinked as part of delete.\r
+ }\r
\r
#ifdef DEBUG\r
const bool hadError = Error();\r
#endif\r
- _errorID = XML_SUCCESS;\r
- _errorStr1 = 0;\r
- _errorStr2 = 0;\r
+ ClearError();\r
\r
delete [] _charBuffer;\r
_charBuffer = 0;\r
}\r
\r
\r
+void XMLDocument::DeepCopy(XMLDocument* target)\r
+{\r
+ TIXMLASSERT(target);\r
+ if (target == this) {\r
+ return; // technically success - a no-op.\r
+ }\r
+\r
+ target->Clear();\r
+ for (const XMLNode* node = this->FirstChild(); node; node = node->NextSibling()) {\r
+ target->InsertEndChild(node->DeepClone(target));\r
+ }\r
+}\r
+\r
XMLElement* XMLDocument::NewElement( const char* name )\r
{\r
- TIXMLASSERT( sizeof( XMLElement ) == _elementPool.ItemSize() );\r
- XMLElement* ele = new (_elementPool.Alloc()) XMLElement( this );\r
- ele->_memPool = &_elementPool;\r
+ XMLElement* ele = CreateUnlinkedNode<XMLElement>( _elementPool );\r
ele->SetName( name );\r
return ele;\r
}\r
\r
XMLComment* XMLDocument::NewComment( const char* str )\r
{\r
- TIXMLASSERT( sizeof( XMLComment ) == _commentPool.ItemSize() );\r
- XMLComment* comment = new (_commentPool.Alloc()) XMLComment( this );\r
- comment->_memPool = &_commentPool;\r
+ XMLComment* comment = CreateUnlinkedNode<XMLComment>( _commentPool );\r
comment->SetValue( str );\r
return comment;\r
}\r
\r
XMLText* XMLDocument::NewText( const char* str )\r
{\r
- TIXMLASSERT( sizeof( XMLText ) == _textPool.ItemSize() );\r
- XMLText* text = new (_textPool.Alloc()) XMLText( this );\r
- text->_memPool = &_textPool;\r
+ XMLText* text = CreateUnlinkedNode<XMLText>( _textPool );\r
text->SetValue( str );\r
return text;\r
}\r
\r
XMLDeclaration* XMLDocument::NewDeclaration( const char* str )\r
{\r
- TIXMLASSERT( sizeof( XMLDeclaration ) == _commentPool.ItemSize() );\r
- XMLDeclaration* dec = new (_commentPool.Alloc()) XMLDeclaration( this );\r
- dec->_memPool = &_commentPool;\r
+ XMLDeclaration* dec = CreateUnlinkedNode<XMLDeclaration>( _commentPool );\r
dec->SetValue( str ? str : "xml version=\"1.0\" encoding=\"UTF-8\"" );\r
return dec;\r
}\r
\r
XMLUnknown* XMLDocument::NewUnknown( const char* str )\r
{\r
- TIXMLASSERT( sizeof( XMLUnknown ) == _commentPool.ItemSize() );\r
- XMLUnknown* unk = new (_commentPool.Alloc()) XMLUnknown( this );\r
- unk->_memPool = &_commentPool;\r
+ XMLUnknown* unk = CreateUnlinkedNode<XMLUnknown>( _commentPool );\r
unk->SetValue( str );\r
return unk;\r
}\r
Clear();\r
FILE* fp = callfopen( filename, "rb" );\r
if ( !fp ) {\r
- SetError( XML_ERROR_FILE_NOT_FOUND, filename, 0 );\r
+ SetError( XML_ERROR_FILE_NOT_FOUND, filename, 0, 0 );\r
return _errorID;\r
}\r
LoadFile( fp );\r
\r
fseek( fp, 0, SEEK_SET );\r
if ( fgetc( fp ) == EOF && ferror( fp ) != 0 ) {\r
- SetError( XML_ERROR_FILE_READ_ERROR, 0, 0 );\r
+ SetError( XML_ERROR_FILE_READ_ERROR, 0, 0, 0 );\r
return _errorID;\r
}\r
\r
const long filelength = ftell( fp );\r
fseek( fp, 0, SEEK_SET );\r
if ( filelength == -1L ) {\r
- SetError( XML_ERROR_FILE_READ_ERROR, 0, 0 );\r
+ SetError( XML_ERROR_FILE_READ_ERROR, 0, 0, 0 );\r
return _errorID;\r
}\r
TIXMLASSERT( filelength >= 0 );\r
\r
if ( !LongFitsIntoSizeTMinusOne<>::Fits( filelength ) ) {\r
// Cannot handle files which won't fit in buffer together with null terminator\r
- SetError( XML_ERROR_FILE_READ_ERROR, 0, 0 );\r
+ SetError( XML_ERROR_FILE_READ_ERROR, 0, 0, 0 );\r
return _errorID;\r
}\r
\r
if ( filelength == 0 ) {\r
- SetError( XML_ERROR_EMPTY_DOCUMENT, 0, 0 );\r
+ SetError( XML_ERROR_EMPTY_DOCUMENT, 0, 0, 0 );\r
return _errorID;\r
}\r
\r
_charBuffer = new char[size+1];\r
size_t read = fread( _charBuffer, 1, size, fp );\r
if ( read != size ) {\r
- SetError( XML_ERROR_FILE_READ_ERROR, 0, 0 );\r
+ SetError( XML_ERROR_FILE_READ_ERROR, 0, 0, 0 );\r
return _errorID;\r
}\r
\r
{\r
FILE* fp = callfopen( filename, "w" );\r
if ( !fp ) {\r
- SetError( XML_ERROR_FILE_COULD_NOT_BE_OPENED, filename, 0 );\r
+ SetError( XML_ERROR_FILE_COULD_NOT_BE_OPENED, filename, 0, 0 );\r
return _errorID;\r
}\r
SaveFile(fp, compact);\r
{\r
// Clear any error from the last save, otherwise it will get reported\r
// for *this* call.\r
- SetError(XML_SUCCESS, 0, 0);\r
+ ClearError();\r
XMLPrinter stream( fp, compact );\r
Print( &stream );\r
return _errorID;\r
Clear();\r
\r
if ( len == 0 || !p || !*p ) {\r
- SetError( XML_ERROR_EMPTY_DOCUMENT, 0, 0 );\r
+ SetError( XML_ERROR_EMPTY_DOCUMENT, 0, 0, 0 );\r
return _errorID;\r
}\r
if ( len == (size_t)(-1) ) {\r
}\r
\r
\r
-void XMLDocument::SetError( XMLError error, const char* str1, const char* str2 )\r
+void XMLDocument::SetError( XMLError error, const char* str1, const char* str2, int lineNum )\r
{\r
TIXMLASSERT( error >= 0 && error < XML_ERROR_COUNT );\r
_errorID = error;\r
- _errorStr1 = str1;\r
- _errorStr2 = str2;\r
+ \r
+ _errorStr1.Reset();\r
+ _errorStr2.Reset();\r
+ _errorLineNum = lineNum;\r
+\r
+ if (str1)\r
+ _errorStr1.SetStr(str1);\r
+ if (str2)\r
+ _errorStr2.SetStr(str2);\r
}\r
\r
-const char* XMLDocument::ErrorName() const\r
+/*static*/ const char* XMLDocument::ErrorIDToName(XMLError errorID)\r
{\r
- TIXMLASSERT( _errorID >= 0 && _errorID < XML_ERROR_COUNT );\r
- const char* errorName = _errorNames[_errorID];\r
+ TIXMLASSERT( errorID >= 0 && errorID < XML_ERROR_COUNT );\r
+ const char* errorName = _errorNames[errorID];\r
TIXMLASSERT( errorName && errorName[0] );\r
return errorName;\r
}\r
\r
+const char* XMLDocument::ErrorName() const\r
+{\r
+ return ErrorIDToName(_errorID);\r
+}\r
+\r
void XMLDocument::PrintError() const\r
{\r
if ( Error() ) {\r
char buf1[LEN] = { 0 };\r
char buf2[LEN] = { 0 };\r
\r
- if ( _errorStr1 ) {\r
- TIXML_SNPRINTF( buf1, LEN, "%s", _errorStr1 );\r
+ if ( !_errorStr1.Empty() ) {\r
+ TIXML_SNPRINTF( buf1, LEN, "%s", _errorStr1.GetStr() );\r
}\r
- if ( _errorStr2 ) {\r
- TIXML_SNPRINTF( buf2, LEN, "%s", _errorStr2 );\r
+ if ( !_errorStr2.Empty() ) {\r
+ TIXML_SNPRINTF( buf2, LEN, "%s", _errorStr2.GetStr() );\r
}\r
\r
// Should check INT_MIN <= _errorID && _errorId <= INT_MAX, but that\r
// causes a clang "always true" -Wtautological-constant-out-of-range-compare warning\r
TIXMLASSERT( 0 <= _errorID && XML_ERROR_COUNT - 1 <= INT_MAX );\r
- printf( "XMLDocument error id=%d '%s' str1=%s str2=%s\n",\r
- static_cast<int>( _errorID ), ErrorName(), buf1, buf2 );\r
+ printf( "XMLDocument error id=%d '%s' str1=%s str2=%s line=%d\n",\r
+ static_cast<int>( _errorID ), ErrorName(), buf1, buf2, _errorLineNum );\r
}\r
}\r
\r
{\r
TIXMLASSERT( NoChildren() ); // Clear() must have been called previously\r
TIXMLASSERT( _charBuffer );\r
+ _parseCurLineNum = 1;\r
+ _parseLineNum = 1;\r
char* p = _charBuffer;\r
- p = XMLUtil::SkipWhiteSpace( p );\r
+ p = XMLUtil::SkipWhiteSpace( p, &_parseCurLineNum );\r
p = const_cast<char*>( XMLUtil::ReadBOM( p, &_writeBOM ) );\r
if ( !*p ) {\r
- SetError( XML_ERROR_EMPTY_DOCUMENT, 0, 0 );\r
+ SetError( XML_ERROR_EMPTY_DOCUMENT, 0, 0, 0 );\r
return;\r
}\r
- ParseDeep(p, 0 );\r
+ ParseDeep(p, 0, &_parseCurLineNum );\r
}\r
\r
XMLPrinter::XMLPrinter( FILE* file, bool compact, int depth ) :\r
}\r
for( int i=0; i<NUM_ENTITIES; ++i ) {\r
const char entityValue = entities[i].value;\r
- TIXMLASSERT( 0 <= entityValue && entityValue < ENTITY_RANGE );\r
+ TIXMLASSERT( ((unsigned char)entityValue) < ENTITY_RANGE );\r
_entityFlag[ (unsigned char)entityValue ] = true;\r
}\r
_restrictedEntityFlag[(unsigned char)'&'] = true;\r
}\r
}\r
\r
+void XMLPrinter::PushText( int64_t value )\r
+{\r
+ char buf[BUF_SIZE];\r
+ XMLUtil::ToStr( value, buf, BUF_SIZE );\r
+ PushText( buf, false );\r
+}\r
+\r
void XMLPrinter::PushText( int value )\r
{\r
char buf[BUF_SIZE];\r
AStyle.exe --style=1tbs --indent-switches --break-closing-brackets --indent-preprocessor tinyxml2.cpp tinyxml2.h\r
*/\r
\r
-#if defined( _DEBUG ) || defined( DEBUG ) || defined (__DEBUG__)\r
+#if defined( _DEBUG ) || defined (__DEBUG__)\r
# ifndef DEBUG\r
# define DEBUG\r
# endif\r
# else\r
# define TINYXML2_LIB\r
# endif\r
+#elif __GNUC__ >= 4\r
+# define TINYXML2_LIB __attribute__((visibility("default")))\r
#else\r
# define TINYXML2_LIB\r
#endif\r
/* Versioning, past 1.0.14:\r
http://semver.org/\r
*/\r
-static const int TIXML2_MAJOR_VERSION = 4;\r
+static const int TIXML2_MAJOR_VERSION = 5;\r
static const int TIXML2_MINOR_VERSION = 0;\r
-static const int TIXML2_PATCH_VERSION = 1;\r
+static const int TIXML2_PATCH_VERSION = 0;\r
\r
namespace tinyxml2\r
{\r
NEEDS_NEWLINE_NORMALIZATION = 0x02,\r
NEEDS_WHITESPACE_COLLAPSING = 0x04,\r
\r
- TEXT_ELEMENT = NEEDS_ENTITY_PROCESSING | NEEDS_NEWLINE_NORMALIZATION,\r
+ TEXT_ELEMENT = NEEDS_ENTITY_PROCESSING | NEEDS_NEWLINE_NORMALIZATION,\r
TEXT_ELEMENT_LEAVE_ENTITIES = NEEDS_NEWLINE_NORMALIZATION,\r
- ATTRIBUTE_NAME = 0,\r
- ATTRIBUTE_VALUE = NEEDS_ENTITY_PROCESSING | NEEDS_NEWLINE_NORMALIZATION,\r
- ATTRIBUTE_VALUE_LEAVE_ENTITIES = NEEDS_NEWLINE_NORMALIZATION,\r
- COMMENT = NEEDS_NEWLINE_NORMALIZATION\r
+ ATTRIBUTE_NAME = 0,\r
+ ATTRIBUTE_VALUE = NEEDS_ENTITY_PROCESSING | NEEDS_NEWLINE_NORMALIZATION,\r
+ ATTRIBUTE_VALUE_LEAVE_ENTITIES = NEEDS_NEWLINE_NORMALIZATION,\r
+ COMMENT = NEEDS_NEWLINE_NORMALIZATION\r
};\r
\r
StrPair() : _flags( 0 ), _start( 0 ), _end( 0 ) {}\r
~StrPair();\r
\r
void Set( char* start, char* end, int flags ) {\r
+ TIXMLASSERT( start );\r
+ TIXMLASSERT( end );\r
Reset();\r
_start = start;\r
_end = end;\r
\r
void SetStr( const char* str, int flags=0 );\r
\r
- char* ParseText( char* in, const char* endTag, int strFlags );\r
+ char* ParseText( char* in, const char* endTag, int strFlags, int* curLineNumPtr );\r
char* ParseName( char* in );\r
\r
void TransferTo( StrPair* other );\r
+ void Reset();\r
\r
private:\r
- void Reset();\r
void CollapseWhitespace();\r
\r
enum {\r
void Push( T t ) {\r
TIXMLASSERT( _size < INT_MAX );\r
EnsureCapacity( _size+1 );\r
- _mem[_size++] = t;\r
+ _mem[_size] = t;\r
+ ++_size;\r
}\r
\r
T* PushArr( int count ) {\r
\r
T Pop() {\r
TIXMLASSERT( _size > 0 );\r
- return _mem[--_size];\r
+ --_size;\r
+ return _mem[_size];\r
}\r
\r
void PopArr( int count ) {\r
return _allocated;\r
}\r
\r
+ void SwapRemove(int i) {\r
+ TIXMLASSERT(i >= 0 && i < _size);\r
+ TIXMLASSERT(_size > 0);\r
+ _mem[i] = _mem[_size - 1];\r
+ --_size;\r
+ }\r
+\r
const T* Mem() const {\r
TIXMLASSERT( _mem );\r
return _mem;\r
TIXMLASSERT( cap <= INT_MAX / 2 );\r
int newAllocated = cap * 2;\r
T* newMem = new T[newAllocated];\r
+ TIXMLASSERT( newAllocated >= _size );\r
memcpy( newMem, _mem, sizeof(T)*_size ); // warning: not using constructors, only works for PODs\r
if ( _mem != _pool ) {\r
delete [] _mem;\r
/*\r
Template child class to create pools of the correct type.\r
*/\r
-template< int SIZE >\r
+template< int ITEM_SIZE >\r
class MemPoolT : public MemPool\r
{\r
public:\r
}\r
\r
virtual int ItemSize() const {\r
- return SIZE;\r
+ return ITEM_SIZE;\r
}\r
int CurrentAllocs() const {\r
return _currentAllocs;\r
Block* block = new Block();\r
_blockPtrs.Push( block );\r
\r
- for( int i=0; i<COUNT-1; ++i ) {\r
- block->chunk[i].next = &block->chunk[i+1];\r
+ Item* blockItems = block->items;\r
+ for( int i = 0; i < ITEMS_PER_BLOCK - 1; ++i ) {\r
+ blockItems[i].next = &(blockItems[i + 1]);\r
}\r
- block->chunk[COUNT-1].next = 0;\r
- _root = block->chunk;\r
+ blockItems[ITEMS_PER_BLOCK - 1].next = 0;\r
+ _root = blockItems;\r
}\r
- void* result = _root;\r
+ Item* const result = _root;\r
+ TIXMLASSERT( result != 0 );\r
_root = _root->next;\r
\r
++_currentAllocs;\r
if ( _currentAllocs > _maxAllocs ) {\r
_maxAllocs = _currentAllocs;\r
}\r
- _nAllocs++;\r
- _nUntracked++;\r
+ ++_nAllocs;\r
+ ++_nUntracked;\r
return result;\r
}\r
\r
return;\r
}\r
--_currentAllocs;\r
- Chunk* chunk = static_cast<Chunk*>( mem );\r
+ Item* item = static_cast<Item*>( mem );\r
#ifdef DEBUG\r
- memset( chunk, 0xfe, sizeof(Chunk) );\r
+ memset( item, 0xfe, sizeof( *item ) );\r
#endif\r
- chunk->next = _root;\r
- _root = chunk;\r
+ item->next = _root;\r
+ _root = item;\r
}\r
void Trace( const char* name ) {\r
printf( "Mempool %s watermark=%d [%dk] current=%d size=%d nAlloc=%d blocks=%d\n",\r
- name, _maxAllocs, _maxAllocs*SIZE/1024, _currentAllocs, SIZE, _nAllocs, _blockPtrs.Size() );\r
+ name, _maxAllocs, _maxAllocs * ITEM_SIZE / 1024, _currentAllocs,\r
+ ITEM_SIZE, _nAllocs, _blockPtrs.Size() );\r
}\r
\r
void SetTracked() {\r
- _nUntracked--;\r
+ --_nUntracked;\r
}\r
\r
int Untracked() const {\r
// 16k: 5200\r
// 32k: 4300\r
// 64k: 4000 21000\r
- enum { COUNT = (4*1024)/SIZE }; // Some compilers do not accept to use COUNT in private part if COUNT is private\r
+ // Declared public because some compilers do not accept to use ITEMS_PER_BLOCK\r
+ // in private part if ITEMS_PER_BLOCK is private\r
+ enum { ITEMS_PER_BLOCK = (4 * 1024) / ITEM_SIZE };\r
\r
private:\r
MemPoolT( const MemPoolT& ); // not supported\r
void operator=( const MemPoolT& ); // not supported\r
\r
- union Chunk {\r
- Chunk* next;\r
- char mem[SIZE];\r
+ union Item {\r
+ Item* next;\r
+ char itemData[ITEM_SIZE];\r
};\r
struct Block {\r
- Chunk chunk[COUNT];\r
+ Item items[ITEMS_PER_BLOCK];\r
};\r
DynArray< Block*, 10 > _blockPtrs;\r
- Chunk* _root;\r
+ Item* _root;\r
\r
int _currentAllocs;\r
int _nAllocs;\r
/*\r
Utility functionality.\r
*/\r
-class XMLUtil\r
+class TINYXML2_LIB XMLUtil\r
{\r
public:\r
- static const char* SkipWhiteSpace( const char* p ) {\r
+ static const char* SkipWhiteSpace( const char* p, int* curLineNumPtr ) {\r
TIXMLASSERT( p );\r
+\r
while( IsWhiteSpace(*p) ) {\r
+ if (curLineNumPtr && *p == '\n') {\r
+ ++(*curLineNumPtr);\r
+ }\r
++p;\r
}\r
TIXMLASSERT( p );\r
return p;\r
}\r
- static char* SkipWhiteSpace( char* p ) {\r
- return const_cast<char*>( SkipWhiteSpace( const_cast<const char*>(p) ) );\r
+ static char* SkipWhiteSpace( char* p, int* curLineNumPtr ) {\r
+ return const_cast<char*>( SkipWhiteSpace( const_cast<const char*>(p), curLineNumPtr ) );\r
}\r
\r
// Anything in the high order range of UTF-8 is assumed to not be whitespace. This isn't\r
if ( p == q ) {\r
return true;\r
}\r
+ TIXMLASSERT( p );\r
+ TIXMLASSERT( q );\r
+ TIXMLASSERT( nChar >= 0 );\r
return strncmp( p, q, nChar ) == 0;\r
}\r
\r
static bool ToFloat( const char* str, float* value );\r
static bool ToDouble( const char* str, double* value );\r
static bool ToInt64(const char* str, int64_t* value);\r
+\r
+ // Changes what is serialized for a boolean value.\r
+ // Default to "true" and "false". Shouldn't be changed\r
+ // unless you have a special testing or compatibility need.\r
+ // Be careful: static, global, & not thread safe.\r
+ // Be sure to set static const memory as parameters.\r
+ static void SetBoolSerialization(const char* writeTrue, const char* writeFalse);\r
+\r
+private:\r
+ static const char* writeBoolTrue;\r
+ static const char* writeBoolFalse;\r
};\r
\r
\r
*/\r
void SetValue( const char* val, bool staticMem=false );\r
\r
+ /// Gets the line number the node is in, if the document was parsed from a file.\r
+ int GetLineNum() const { return _parseLineNum; }\r
+\r
/// Get the parent of this node on the DOM.\r
const XMLNode* Parent() const {\r
return _parent;\r
*/\r
virtual XMLNode* ShallowClone( XMLDocument* document ) const = 0;\r
\r
+ /**\r
+ Make a copy of this node and all its children.\r
+\r
+ If the 'target' is null, then the nodes will\r
+ be allocated in the current document. If 'target' \r
+ is specified, the memory will be allocated is the \r
+ specified XMLDocument.\r
+\r
+ NOTE: This is probably not the correct tool to \r
+ copy a document, since XMLDocuments can have multiple\r
+ top level XMLNodes. You probably want to use\r
+ XMLDocument::DeepCopy()\r
+ */\r
+ XMLNode* DeepClone( XMLDocument* target ) const;\r
+\r
/**\r
Test if 2 nodes are the same, but don't test children.\r
The 2 nodes do not need to be in the same Document.\r
XMLNode( XMLDocument* );\r
virtual ~XMLNode();\r
\r
- virtual char* ParseDeep( char*, StrPair* );\r
+ virtual char* ParseDeep( char* p, StrPair* parentEndTag, int* curLineNumPtr);\r
\r
XMLDocument* _document;\r
XMLNode* _parent;\r
mutable StrPair _value;\r
+ int _parseLineNum;\r
\r
XMLNode* _firstChild;\r
XMLNode* _lastChild;\r
void Unlink( XMLNode* child );\r
static void DeleteNode( XMLNode* node );\r
void InsertChildPreamble( XMLNode* insertThis ) const;\r
+ const XMLElement* ToElementWithName( const char* name ) const;\r
\r
XMLNode( const XMLNode& ); // not supported\r
XMLNode& operator=( const XMLNode& ); // not supported\r
XMLText( XMLDocument* doc ) : XMLNode( doc ), _isCData( false ) {}\r
virtual ~XMLText() {}\r
\r
- char* ParseDeep( char*, StrPair* endTag );\r
+ char* ParseDeep( char* p, StrPair* parentEndTag, int* curLineNumPtr );\r
\r
private:\r
bool _isCData;\r
XMLComment( XMLDocument* doc );\r
virtual ~XMLComment();\r
\r
- char* ParseDeep( char*, StrPair* endTag );\r
+ char* ParseDeep( char* p, StrPair* parentEndTag, int* curLineNumPtr);\r
\r
private:\r
XMLComment( const XMLComment& ); // not supported\r
XMLDeclaration( XMLDocument* doc );\r
virtual ~XMLDeclaration();\r
\r
- char* ParseDeep( char*, StrPair* endTag );\r
+ char* ParseDeep( char* p, StrPair* parentEndTag, int* curLineNumPtr );\r
\r
private:\r
XMLDeclaration( const XMLDeclaration& ); // not supported\r
XMLUnknown( XMLDocument* doc );\r
virtual ~XMLUnknown();\r
\r
- char* ParseDeep( char*, StrPair* endTag );\r
+ char* ParseDeep( char* p, StrPair* parentEndTag, int* curLineNumPtr );\r
\r
private:\r
XMLUnknown( const XMLUnknown& ); // not supported\r
/// The value of the attribute.\r
const char* Value() const;\r
\r
+ /// Gets the line number the attribute is in, if the document was parsed from a file.\r
+ int GetLineNum() const { return _parseLineNum; }\r
+\r
/// The next attribute in the list.\r
const XMLAttribute* Next() const {\r
return _next;\r
}\r
\r
/** QueryIntValue interprets the attribute as an integer, and returns the value\r
- in the provided parameter. The function will return XML_NO_ERROR on success,\r
+ in the provided parameter. The function will return XML_SUCCESS on success,\r
and XML_WRONG_ATTRIBUTE_TYPE if the conversion is not successful.\r
*/\r
XMLError QueryIntValue( int* value ) const;\r
private:\r
enum { BUF_SIZE = 200 };\r
\r
- XMLAttribute() : _next( 0 ), _memPool( 0 ) {}\r
+ XMLAttribute() : _parseLineNum( 0 ), _next( 0 ), _memPool( 0 ) {}\r
virtual ~XMLAttribute() {}\r
\r
XMLAttribute( const XMLAttribute& ); // not supported\r
void operator=( const XMLAttribute& ); // not supported\r
void SetName( const char* name );\r
\r
- char* ParseDeep( char* p, bool processEntities );\r
+ char* ParseDeep( char* p, bool processEntities, int* curLineNumPtr );\r
\r
mutable StrPair _name;\r
mutable StrPair _value;\r
+ int _parseLineNum;\r
XMLAttribute* _next;\r
MemPool* _memPool;\r
};\r
const char* Attribute( const char* name, const char* value=0 ) const;\r
\r
/** Given an attribute name, IntAttribute() returns the value\r
- of the attribute interpreted as an integer. 0 will be\r
- returned if there is an error. For a method with error\r
- checking, see QueryIntAttribute()\r
+ of the attribute interpreted as an integer. The default\r
+ value will be returned if the attribute isn't present,\r
+ or if there is an error. (For a method with error\r
+ checking, see QueryIntAttribute()).\r
*/\r
- int IntAttribute( const char* name ) const {\r
- int i=0;\r
- QueryIntAttribute( name, &i );\r
- return i;\r
- }\r
-\r
+ int IntAttribute(const char* name, int defaultValue = 0) const;\r
/// See IntAttribute()\r
- unsigned UnsignedAttribute( const char* name ) const {\r
- unsigned i=0;\r
- QueryUnsignedAttribute( name, &i );\r
- return i;\r
- }\r
-\r
+ unsigned UnsignedAttribute(const char* name, unsigned defaultValue = 0) const;\r
/// See IntAttribute()\r
- int64_t Int64Attribute(const char* name) const {\r
- int64_t i = 0;\r
- QueryInt64Attribute(name, &i);\r
- return i;\r
- }\r
-\r
+ int64_t Int64Attribute(const char* name, int64_t defaultValue = 0) const;\r
/// See IntAttribute()\r
- bool BoolAttribute( const char* name ) const {\r
- bool b=false;\r
- QueryBoolAttribute( name, &b );\r
- return b;\r
- }\r
+ bool BoolAttribute(const char* name, bool defaultValue = false) const;\r
/// See IntAttribute()\r
- double DoubleAttribute( const char* name ) const {\r
- double d=0;\r
- QueryDoubleAttribute( name, &d );\r
- return d;\r
- }\r
+ double DoubleAttribute(const char* name, double defaultValue = 0) const;\r
/// See IntAttribute()\r
- float FloatAttribute( const char* name ) const {\r
- float f=0;\r
- QueryFloatAttribute( name, &f );\r
- return f;\r
- }\r
+ float FloatAttribute(const char* name, float defaultValue = 0) const;\r
\r
/** Given an attribute name, QueryIntAttribute() returns\r
- XML_NO_ERROR, XML_WRONG_ATTRIBUTE_TYPE if the conversion\r
+ XML_SUCCESS, XML_WRONG_ATTRIBUTE_TYPE if the conversion\r
can't be performed, or XML_NO_ATTRIBUTE if the attribute\r
doesn't exist. If successful, the result of the conversion\r
will be written to 'value'. If not successful, nothing will\r
\r
\r
/** Given an attribute name, QueryAttribute() returns\r
- XML_NO_ERROR, XML_WRONG_ATTRIBUTE_TYPE if the conversion\r
+ XML_SUCCESS, XML_WRONG_ATTRIBUTE_TYPE if the conversion\r
can't be performed, or XML_NO_ATTRIBUTE if the attribute\r
doesn't exist. It is overloaded for the primitive types,\r
and is a generally more convenient replacement of\r
/// See QueryIntText()\r
XMLError QueryFloatText( float* fval ) const;\r
\r
+ int IntText(int defaultValue = 0) const;\r
+\r
+ /// See QueryIntText()\r
+ unsigned UnsignedText(unsigned defaultValue = 0) const;\r
+ /// See QueryIntText()\r
+ int64_t Int64Text(int64_t defaultValue = 0) const;\r
+ /// See QueryIntText()\r
+ bool BoolText(bool defaultValue = false) const;\r
+ /// See QueryIntText()\r
+ double DoubleText(double defaultValue = 0) const;\r
+ /// See QueryIntText()\r
+ float FloatText(float defaultValue = 0) const;\r
+\r
// internal:\r
- enum {\r
+ enum ElementClosingType {\r
OPEN, // <foo>\r
CLOSED, // <foo/>\r
CLOSING // </foo>\r
};\r
- int ClosingType() const {\r
+ ElementClosingType ClosingType() const {\r
return _closingType;\r
}\r
virtual XMLNode* ShallowClone( XMLDocument* document ) const;\r
virtual bool ShallowEqual( const XMLNode* compare ) const;\r
\r
protected:\r
- char* ParseDeep( char* p, StrPair* endTag );\r
+ char* ParseDeep( char* p, StrPair* parentEndTag, int* curLineNumPtr );\r
\r
private:\r
XMLElement( XMLDocument* doc );\r
}\r
XMLAttribute* FindOrCreateAttribute( const char* name );\r
//void LinkAttribute( XMLAttribute* attrib );\r
- char* ParseAttributes( char* p );\r
+ char* ParseAttributes( char* p, int* curLineNumPtr );\r
static void DeleteAttribute( XMLAttribute* attribute );\r
+ XMLAttribute* CreateAttribute();\r
\r
enum { BUF_SIZE = 200 };\r
- int _closingType;\r
+ ElementClosingType _closingType;\r
// The attribute list is ordered; there is no 'lastAttribute'\r
// because the list needs to be scanned for dupes before adding\r
// a new attribute.\r
friend class XMLElement;\r
public:\r
/// constructor\r
- XMLDocument( bool processEntities = true, Whitespace = PRESERVE_WHITESPACE );\r
+ XMLDocument( bool processEntities = true, Whitespace whitespaceMode = PRESERVE_WHITESPACE );\r
~XMLDocument();\r
\r
virtual XMLDocument* ToDocument() {\r
\r
/**\r
Parse an XML file from a character string.\r
- Returns XML_NO_ERROR (0) on success, or\r
+ Returns XML_SUCCESS (0) on success, or\r
an errorID.\r
\r
You may optionally pass in the 'nBytes', which is\r
\r
/**\r
Load an XML file from disk.\r
- Returns XML_NO_ERROR (0) on success, or\r
+ Returns XML_SUCCESS (0) on success, or\r
an errorID.\r
*/\r
XMLError LoadFile( const char* filename );\r
not text in order for TinyXML-2 to correctly\r
do newline normalization.\r
\r
- Returns XML_NO_ERROR (0) on success, or\r
+ Returns XML_SUCCESS (0) on success, or\r
an errorID.\r
*/\r
XMLError LoadFile( FILE* );\r
\r
/**\r
Save the XML file to disk.\r
- Returns XML_NO_ERROR (0) on success, or\r
+ Returns XML_SUCCESS (0) on success, or\r
an errorID.\r
*/\r
XMLError SaveFile( const char* filename, bool compact = false );\r
Save the XML file to disk. You are responsible\r
for providing and closing the FILE*.\r
\r
- Returns XML_NO_ERROR (0) on success, or\r
+ Returns XML_SUCCESS (0) on success, or\r
an errorID.\r
*/\r
XMLError SaveFile( FILE* fp, bool compact = false );\r
return _processEntities;\r
}\r
Whitespace WhitespaceMode() const {\r
- return _whitespace;\r
+ return _whitespaceMode;\r
}\r
\r
/**\r
*/\r
void DeleteNode( XMLNode* node );\r
\r
- void SetError( XMLError error, const char* str1, const char* str2 );\r
+ void SetError( XMLError error, const char* str1, const char* str2, int lineNum );\r
+\r
+ void ClearError() {\r
+ SetError(XML_SUCCESS, 0, 0, 0);\r
+ }\r
\r
/// Return true if there was an error parsing the document.\r
bool Error() const {\r
return _errorID;\r
}\r
const char* ErrorName() const;\r
+ static const char* ErrorIDToName(XMLError errorID);\r
\r
/// Return a possibly helpful diagnostic location or string.\r
const char* GetErrorStr1() const {\r
- return _errorStr1;\r
+ return _errorStr1.GetStr();\r
}\r
/// Return a possibly helpful secondary diagnostic location or string.\r
const char* GetErrorStr2() const {\r
- return _errorStr2;\r
+ return _errorStr2.GetStr();\r
+ }\r
+ /// Return the line where the error occured, or zero if unknown.\r
+ int GetErrorLineNum() const\r
+ {\r
+ return _errorLineNum;\r
}\r
/// If there is an error, print it to stdout.\r
void PrintError() const;\r
/// Clear the document, resetting it to the initial state.\r
void Clear();\r
\r
- // internal\r
+ /**\r
+ Copies this document to a target document.\r
+ The target will be completely cleared before the copy.\r
+ If you want to copy a sub-tree, see XMLNode::DeepClone().\r
+\r
+ NOTE: that the 'target' must be non-null.\r
+ */\r
+ void DeepCopy(XMLDocument* target);\r
+\r
+ // internal\r
char* Identify( char* p, XMLNode** node );\r
\r
+ // internal\r
+ void MarkInUse(XMLNode*);\r
+\r
virtual XMLNode* ShallowClone( XMLDocument* /*document*/ ) const {\r
return 0;\r
}\r
XMLDocument( const XMLDocument& ); // not supported\r
void operator=( const XMLDocument& ); // not supported\r
\r
- bool _writeBOM;\r
- bool _processEntities;\r
- XMLError _errorID;\r
- Whitespace _whitespace;\r
- const char* _errorStr1;\r
- const char* _errorStr2;\r
- char* _charBuffer;\r
+ bool _writeBOM;\r
+ bool _processEntities;\r
+ XMLError _errorID;\r
+ Whitespace _whitespaceMode;\r
+ mutable StrPair _errorStr1;\r
+ mutable StrPair _errorStr2;\r
+ int _errorLineNum;\r
+ char* _charBuffer;\r
+ int _parseCurLineNum;\r
+ // Memory tracking does add some overhead.\r
+ // However, the code assumes that you don't\r
+ // have a bunch of unlinked nodes around.\r
+ // Therefore it takes less memory to track\r
+ // in the document vs. a linked list in the XMLNode,\r
+ // and the performance is the same.\r
+ DynArray<XMLNode*, 10> _unlinked;\r
\r
MemPoolT< sizeof(XMLElement) > _elementPool;\r
MemPoolT< sizeof(XMLAttribute) > _attributePool;\r
static const char* _errorNames[XML_ERROR_COUNT];\r
\r
void Parse();\r
+\r
+ template<class NodeType, int PoolElementSize>\r
+ NodeType* CreateUnlinkedNode( MemPoolT<PoolElementSize>& pool );\r
};\r
\r
+template<class NodeType, int PoolElementSize>\r
+inline NodeType* XMLDocument::CreateUnlinkedNode( MemPoolT<PoolElementSize>& pool )\r
+{\r
+ TIXMLASSERT( sizeof( NodeType ) == PoolElementSize );\r
+ TIXMLASSERT( sizeof( NodeType ) == pool.ItemSize() );\r
+ NodeType* returnNode = new (pool.Alloc()) NodeType( this );\r
+ TIXMLASSERT( returnNode );\r
+ returnNode->_memPool = &pool;\r
+\r
+ _unlinked.Push(returnNode);\r
+ return returnNode;\r
+}\r
\r
/**\r
A XMLHandle is a class that wraps a node pointer with null checks; this is\r
}\r
/// Safe cast to XMLElement. This can return null.\r
XMLElement* ToElement() {\r
- return ( ( _node == 0 ) ? 0 : _node->ToElement() );\r
+ return ( _node ? _node->ToElement() : 0 );\r
}\r
/// Safe cast to XMLText. This can return null.\r
XMLText* ToText() {\r
- return ( ( _node == 0 ) ? 0 : _node->ToText() );\r
+ return ( _node ? _node->ToText() : 0 );\r
}\r
/// Safe cast to XMLUnknown. This can return null.\r
XMLUnknown* ToUnknown() {\r
- return ( ( _node == 0 ) ? 0 : _node->ToUnknown() );\r
+ return ( _node ? _node->ToUnknown() : 0 );\r
}\r
/// Safe cast to XMLDeclaration. This can return null.\r
XMLDeclaration* ToDeclaration() {\r
- return ( ( _node == 0 ) ? 0 : _node->ToDeclaration() );\r
+ return ( _node ? _node->ToDeclaration() : 0 );\r
}\r
\r
private:\r
return _node;\r
}\r
const XMLElement* ToElement() const {\r
- return ( ( _node == 0 ) ? 0 : _node->ToElement() );\r
+ return ( _node ? _node->ToElement() : 0 );\r
}\r
const XMLText* ToText() const {\r
- return ( ( _node == 0 ) ? 0 : _node->ToText() );\r
+ return ( _node ? _node->ToText() : 0 );\r
}\r
const XMLUnknown* ToUnknown() const {\r
- return ( ( _node == 0 ) ? 0 : _node->ToUnknown() );\r
+ return ( _node ? _node->ToUnknown() : 0 );\r
}\r
const XMLDeclaration* ToDeclaration() const {\r
- return ( ( _node == 0 ) ? 0 : _node->ToDeclaration() );\r
+ return ( _node ? _node->ToDeclaration() : 0 );\r
}\r
\r
private:\r
void ClearBuffer() {\r
_buffer.Clear();\r
_buffer.Push(0);\r
+ _firstElement = true;\r
}\r
\r
protected:\r
<PropertyGroup Label="UserMacros" />\r
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug-Dll|Win32'" />\r
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug-Dll|Win32'">\r
- <IntDir>$(SolutionDir)$(Configuration)\</IntDir>\r
+ <IntDir>$(SolutionDir)temp\$(ProjectName)\$(Platform)-$(Configuration)\</IntDir>\r
<OutDir>$(SolutionDir)$(Configuration)\</OutDir>\r
</PropertyGroup>\r
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug-Lib|Win32'" />\r
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug-Lib|Win32'">\r
- <IntDir>$(SolutionDir)$(Configuration)\</IntDir>\r
+ <IntDir>$(SolutionDir)temp\$(ProjectName)\$(Platform)-$(Configuration)\</IntDir>\r
<OutDir>$(SolutionDir)$(Configuration)\</OutDir>\r
</PropertyGroup>\r
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release-Dll|Win32'" />\r
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release-Dll|Win32'">\r
- <IntDir>$(SolutionDir)$(Configuration)\</IntDir>\r
+ <IntDir>$(SolutionDir)temp\$(ProjectName)\$(Platform)-$(Configuration)\</IntDir>\r
<OutDir>$(SolutionDir)$(Configuration)\</OutDir>\r
</PropertyGroup>\r
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release-Lib|Win32'" />\r
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release-Lib|Win32'">\r
- <IntDir>$(SolutionDir)$(Configuration)\</IntDir>\r
+ <IntDir>$(SolutionDir)temp\$(ProjectName)\$(Platform)-$(Configuration)\</IntDir>\r
<OutDir>$(SolutionDir)$(Configuration)\</OutDir>\r
</PropertyGroup>\r
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug-Dll|x64'">\r
- <OutDir>$(SolutionDir)bin\$(Platform)-$(Configuration)\</OutDir>\r
+ <OutDir>$(SolutionDir)bin\$(ProjectName)\$(Platform)-$(Configuration)\</OutDir>\r
</PropertyGroup>\r
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug-Dll|x64'">\r
- <IntDir>$(SolutionDir)temp\$(Platform)-$(Configuration)\</IntDir>\r
+ <IntDir>$(SolutionDir)temp\$(ProjectName)\$(Platform)-$(Configuration)\</IntDir>\r
</PropertyGroup>\r
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug-Lib|x64'">\r
- <OutDir>$(SolutionDir)bin\$(Platform)-$(Configuration)\</OutDir>\r
+ <OutDir>$(SolutionDir)bin\$(ProjectName)\$(Platform)-$(Configuration)\</OutDir>\r
</PropertyGroup>\r
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug-Lib|x64'">\r
- <IntDir>$(SolutionDir)temp\$(Platform)-$(Configuration)\</IntDir>\r
+ <IntDir>$(SolutionDir)temp\$(ProjectName)\$(Platform)-$(Configuration)\</IntDir>\r
</PropertyGroup>\r
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release-Dll|x64'">\r
- <OutDir>$(SolutionDir)bin\$(Platform)-$(Configuration)\</OutDir>\r
+ <OutDir>$(SolutionDir)bin\$(ProjectName)\$(Platform)-$(Configuration)\</OutDir>\r
</PropertyGroup>\r
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release-Dll|x64'">\r
- <IntDir>$(SolutionDir)temp\$(Platform)-$(Configuration)\</IntDir>\r
+ <IntDir>$(SolutionDir)temp\$(ProjectName)\$(Platform)-$(Configuration)\</IntDir>\r
</PropertyGroup>\r
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release-Lib|x64'">\r
- <OutDir>$(SolutionDir)bin\$(Platform)-$(Configuration)\</OutDir>\r
+ <OutDir>$(SolutionDir)bin\$(ProjectName)\$(Platform)-$(Configuration)\</OutDir>\r
</PropertyGroup>\r
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release-Lib|x64'">\r
- <IntDir>$(SolutionDir)temp\$(Platform)-$(Configuration)\</IntDir>\r
+ <IntDir>$(SolutionDir)temp\$(ProjectName)\$(Platform)-$(Configuration)\</IntDir>\r
</PropertyGroup>\r
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug-Lib|Win32'">\r
<ClCompile>\r
<PropertyGroup Label="UserMacros" />\r
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug-Lib|Win32'">\r
<LinkIncremental>true</LinkIncremental>\r
+ <OutDir>$(SolutionDir)$(Configuration)\</OutDir>\r
+ <IntDir>$(SolutionDir)temp\$(ProjectName)\$(Platform)-$(Configuration)\</IntDir>\r
</PropertyGroup>\r
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug-Dll|Win32'">\r
<LinkIncremental>true</LinkIncremental>\r
+ <OutDir>$(SolutionDir)$(Configuration)\</OutDir>\r
+ <IntDir>$(SolutionDir)temp\$(ProjectName)\$(Platform)-$(Configuration)\</IntDir>\r
</PropertyGroup>\r
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug-Lib|x64'">\r
<LinkIncremental>true</LinkIncremental>\r
- <OutDir>$(SolutionDir)bin\$(Platform)-$(Configuration)\</OutDir>\r
- <IntDir>$(SolutionDir)temp\$(Platform)-$(Configuration)\</IntDir>\r
+ <OutDir>$(SolutionDir)bin\$(ProjectName)\$(Platform)-$(Configuration)\</OutDir>\r
+ <IntDir>$(SolutionDir)temp\$(ProjectName)\$(Platform)-$(Configuration)\</IntDir>\r
</PropertyGroup>\r
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug-Dll|x64'">\r
<LinkIncremental>true</LinkIncremental>\r
- <OutDir>$(SolutionDir)bin\$(Platform)-$(Configuration)\</OutDir>\r
- <IntDir>$(SolutionDir)temp\$(Platform)-$(Configuration)\</IntDir>\r
+ <OutDir>$(SolutionDir)bin\$(ProjectName)\$(Platform)-$(Configuration)\</OutDir>\r
+ <IntDir>$(SolutionDir)temp\$(ProjectName)\$(Platform)-$(Configuration)\</IntDir>\r
</PropertyGroup>\r
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release-Lib|Win32'">\r
<LinkIncremental>false</LinkIncremental>\r
+ <OutDir>$(SolutionDir)$(Configuration)\</OutDir>\r
+ <IntDir>$(SolutionDir)temp\$(ProjectName)\$(Platform)-$(Configuration)\</IntDir>\r
</PropertyGroup>\r
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release-Dll|Win32'">\r
<LinkIncremental>false</LinkIncremental>\r
+ <OutDir>$(SolutionDir)$(Configuration)\</OutDir>\r
+ <IntDir>$(SolutionDir)temp\$(ProjectName)\$(Platform)-$(Configuration)\</IntDir>\r
</PropertyGroup>\r
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release-Lib|x64'">\r
<LinkIncremental>false</LinkIncremental>\r
- <OutDir>$(SolutionDir)bin\$(Platform)-$(Configuration)\</OutDir>\r
- <IntDir>$(SolutionDir)temp\$(Platform)-$(Configuration)\</IntDir>\r
+ <OutDir>$(SolutionDir)bin\$(ProjectName)\$(Platform)-$(Configuration)\</OutDir>\r
+ <IntDir>$(SolutionDir)temp\$(ProjectName)\$(Platform)-$(Configuration)\</IntDir>\r
</PropertyGroup>\r
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release-Dll|x64'">\r
<LinkIncremental>false</LinkIncremental>\r
- <OutDir>$(SolutionDir)bin\$(Platform)-$(Configuration)\</OutDir>\r
- <IntDir>$(SolutionDir)temp\$(Platform)-$(Configuration)\</IntDir>\r
+ <OutDir>$(SolutionDir)bin\$(ProjectName)\$(Platform)-$(Configuration)\</OutDir>\r
+ <IntDir>$(SolutionDir)temp\$(ProjectName)\$(Platform)-$(Configuration)\</IntDir>\r
</PropertyGroup>\r
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug-Lib|Win32'">\r
<ClCompile>\r
#include <ctime>\r
\r
#if defined( _MSC_VER )\r
- #include <direct.h> // _mkdir\r
#include <crtdbg.h>\r
#define WIN32_LEAN_AND_MEAN\r
#include <windows.h>\r
_CrtMemState startMemState;\r
_CrtMemState endMemState;\r
-#elif defined(MINGW32) || defined(__MINGW32__)\r
- #include <io.h> // mkdir\r
-#else\r
- #include <sys/stat.h> // mkdir\r
#endif\r
\r
using namespace tinyxml2;\r
return pass;\r
}\r
\r
+bool XMLTest(const char* testString, XMLError expected, XMLError found, bool echo = true, bool extraNL = false)\r
+{\r
+ return XMLTest(testString, XMLDocument::ErrorIDToName(expected), XMLDocument::ErrorIDToName(found), echo, extraNL);\r
+}\r
+\r
+bool XMLTest(const char* testString, bool expected, bool found, bool echo = true, bool extraNL = false)\r
+{\r
+ return XMLTest(testString, expected ? "true" : "false", found ? "true" : "false", echo, extraNL);\r
+}\r
\r
template< class T > bool XMLTest( const char* testString, T expected, T found, bool echo=true )\r
{\r
_CrtMemCheckpoint( &startMemState );\r
// Enable MS Visual C++ debug heap memory leaks dump on exit\r
_CrtSetDbgFlag(_CrtSetDbgFlag(_CRTDBG_REPORT_FLAG) | _CRTDBG_LEAK_CHECK_DF);\r
- #endif\r
-\r
- #if defined(_MSC_VER) || defined(MINGW32) || defined(__MINGW32__)\r
- #if defined __MINGW64_VERSION_MAJOR && defined __MINGW64_VERSION_MINOR\r
- //MINGW64: both 32 and 64-bit\r
- mkdir( "resources/out/" );\r
- #else\r
- _mkdir( "resources/out/" );\r
- #endif\r
- #else\r
- mkdir( "resources/out/", S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);\r
+ {\r
+ int leaksOnStart = _CrtDumpMemoryLeaks();\r
+ XMLTest( "No leaks on start?", FALSE, leaksOnStart );\r
+ }\r
#endif\r
\r
{\r
element->LastChildElement()->DeleteAttribute( "attrib" );\r
\r
XMLTest( "Programmatic DOM", true, doc->FirstChildElement()->FirstChildElement()->BoolAttribute( "attrib" ) );\r
- int value = 10;\r
- int result = doc->FirstChildElement()->LastChildElement()->QueryIntAttribute( "attrib", &value );\r
+ int value1 = 10;\r
+ int value2 = doc->FirstChildElement()->LastChildElement()->IntAttribute( "attrib", 10 );\r
+ int result = doc->FirstChildElement()->LastChildElement()->QueryIntAttribute( "attrib", &value1 );\r
XMLTest( "Programmatic DOM", result, (int)XML_NO_ATTRIBUTE );\r
- XMLTest( "Programmatic DOM", value, 10 );\r
+ XMLTest( "Programmatic DOM", value1, 10 );\r
+ XMLTest( "Programmatic DOM", value2, 10 );\r
\r
doc->Print();\r
\r
{\r
XMLPrinter streamer( 0, true );\r
doc->Print( &streamer );\r
- XMLTest( "Compact mode", "<element><sub attrib=\"1\"/><sub/></element>", streamer.CStr(), false );\r
+ XMLTest( "Compact mode", "<element><sub attrib=\"true\"/><sub/></element>", streamer.CStr(), false );\r
}\r
doc->SaveFile( "./resources/out/pretty.xml" );\r
doc->SaveFile( "./resources/out/compact.xml", true );\r
result = ele->QueryDoubleAttribute( "attr0", &dVal );\r
XMLTest( "Query attribute: int as double", result, (int)XML_SUCCESS);\r
XMLTest( "Query attribute: int as double", (int)dVal, 1 );\r
+ XMLTest( "Query attribute: int as double", (int)ele->DoubleAttribute("attr0"), 1);\r
+\r
result = ele->QueryDoubleAttribute( "attr1", &dVal );\r
XMLTest( "Query attribute: double as double", result, (int)XML_SUCCESS);\r
- XMLTest( "Query attribute: double as double", (int)dVal, 2 );\r
+ XMLTest( "Query attribute: double as double", dVal, 2.0 );\r
+ XMLTest( "Query attribute: double as double", ele->DoubleAttribute("attr1"), 2.0 );\r
+\r
result = ele->QueryIntAttribute( "attr1", &iVal );\r
XMLTest( "Query attribute: double as int", result, (int)XML_SUCCESS);\r
XMLTest( "Query attribute: double as int", iVal, 2 );\r
+\r
result = ele->QueryIntAttribute( "attr2", &iVal );\r
XMLTest( "Query attribute: not a number", result, (int)XML_WRONG_ATTRIBUTE_TYPE );\r
+ XMLTest( "Query attribute: not a number", ele->DoubleAttribute("attr2", 4.0), 4.0 );\r
+\r
result = ele->QueryIntAttribute( "bar", &iVal );\r
XMLTest( "Query attribute: does not exist", result, (int)XML_NO_ATTRIBUTE );\r
+ XMLTest( "Query attribute: does not exist", ele->BoolAttribute("bar", true), true );\r
}\r
\r
{\r
XMLTest( "Attribute round trip. double.", -1, (int)dVal );\r
XMLTest( "Alternate query", true, iVal == iVal2 );\r
XMLTest( "Alternate query", true, dVal == dVal2 );\r
+ XMLTest( "Alternate query", true, iVal == ele->IntAttribute("int") );\r
+ XMLTest( "Alternate query", true, dVal == ele->DoubleAttribute("double") ); \r
}\r
\r
{\r
XMLTest( "SetText types", "1", element->GetText() );\r
\r
element->SetText( true );\r
- XMLTest( "SetText types", "1", element->GetText() ); // TODO: should be 'true'?\r
+ XMLTest( "SetText types", "true", element->GetText() );\r
\r
element->SetText( 1.5f );\r
XMLTest( "SetText types", "1.5", element->GetText() );\r
XMLTest("Attribute: int", -100, v, true);\r
element->QueryAttribute("attrib", &v);\r
XMLTest("Attribute: int", -100, v, true);\r
+ XMLTest("Attribute: int", -100, element->IntAttribute("attrib"), true);\r
}\r
{\r
element->SetAttribute("attrib", unsigned(100));\r
XMLTest("Attribute: unsigned", unsigned(100), v, true);\r
element->QueryAttribute("attrib", &v);\r
XMLTest("Attribute: unsigned", unsigned(100), v, true);\r
+ XMLTest("Attribute: unsigned", unsigned(100), element->UnsignedAttribute("attrib"), true);\r
}\r
{\r
element->SetAttribute("attrib", BIG);\r
XMLTest("Attribute: int64_t", BIG, v, true);\r
element->QueryAttribute("attrib", &v);\r
XMLTest("Attribute: int64_t", BIG, v, true);\r
+ XMLTest("Attribute: int64_t", BIG, element->Int64Attribute("attrib"), true);\r
}\r
{\r
element->SetAttribute("attrib", true);\r
XMLTest("Attribute: bool", true, v, true);\r
element->QueryAttribute("attrib", &v);\r
XMLTest("Attribute: bool", true, v, true);\r
+ XMLTest("Attribute: bool", true, element->BoolAttribute("attrib"), true);\r
+ }\r
+ {\r
+ element->SetAttribute("attrib", true);\r
+ const char* result = element->Attribute("attrib");\r
+ XMLTest("Bool true is 'true'", "true", result);\r
+\r
+ XMLUtil::SetBoolSerialization("1", "0");\r
+ element->SetAttribute("attrib", true);\r
+ result = element->Attribute("attrib");\r
+ XMLTest("Bool true is '1'", "1", result);\r
+\r
+ XMLUtil::SetBoolSerialization(0, 0);\r
}\r
{\r
element->SetAttribute("attrib", 100.0);\r
XMLTest("Attribute: double", 100.0, v, true);\r
element->QueryAttribute("attrib", &v);\r
XMLTest("Attribute: double", 100.0, v, true);\r
+ XMLTest("Attribute: double", 100.0, element->DoubleAttribute("attrib"), true);\r
}\r
{\r
element->SetAttribute("attrib", 100.0f);\r
XMLTest("Attribute: float", 100.0f, v, true);\r
element->QueryAttribute("attrib", &v);\r
XMLTest("Attribute: float", 100.0f, v, true);\r
+ XMLTest("Attribute: float", 100.0f, element->FloatAttribute("attrib"), true);\r
}\r
{\r
element->SetText(BIG);\r
}\r
\r
{\r
+ // Deep Cloning of root element.\r
+ XMLDocument doc2;\r
+ XMLPrinter printer1;\r
+ {\r
+ // Make sure doc1 is deleted before we test doc2\r
+ const char* xml =\r
+ "<root>"\r
+ " <child1 foo='bar'/>"\r
+ " <!-- comment thing -->"\r
+ " <child2 val='1'>Text</child2>"\r
+ "</root>";\r
+ XMLDocument doc;\r
+ doc.Parse(xml);\r
+\r
+ doc.Print(&printer1);\r
+ XMLNode* root = doc.RootElement()->DeepClone(&doc2);\r
+ doc2.InsertFirstChild(root);\r
+ }\r
+ XMLPrinter printer2;\r
+ doc2.Print(&printer2);\r
+\r
+ XMLTest("Deep clone of element.", printer1.CStr(), printer2.CStr(), true);\r
+ }\r
+\r
+ {\r
+ // Deep Cloning of sub element.\r
+ XMLDocument doc2;\r
+ XMLPrinter printer1;\r
+ {\r
+ // Make sure doc1 is deleted before we test doc2\r
+ const char* xml =\r
+ "<?xml version ='1.0'?>"\r
+ "<root>"\r
+ " <child1 foo='bar'/>"\r
+ " <!-- comment thing -->"\r
+ " <child2 val='1'>Text</child2>"\r
+ "</root>";\r
+ XMLDocument doc;\r
+ doc.Parse(xml);\r
+\r
+ const XMLElement* subElement = doc.FirstChildElement("root")->FirstChildElement("child2");\r
+ subElement->Accept(&printer1);\r
+\r
+ XMLNode* clonedSubElement = subElement->DeepClone(&doc2);\r
+ doc2.InsertFirstChild(clonedSubElement);\r
+ }\r
+ XMLPrinter printer2;\r
+ doc2.Print(&printer2);\r
+\r
+ XMLTest("Deep clone of sub-element.", printer1.CStr(), printer2.CStr(), true);\r
+ }\r
+\r
+ {\r
+ // Deep cloning of document.\r
+ XMLDocument doc2;\r
+ XMLPrinter printer1;\r
+ {\r
+ // Make sure doc1 is deleted before we test doc2\r
+ const char* xml =\r
+ "<?xml version ='1.0'?>"\r
+ "<!-- Top level comment. -->"\r
+ "<root>"\r
+ " <child1 foo='bar'/>"\r
+ " <!-- comment thing -->"\r
+ " <child2 val='1'>Text</child2>"\r
+ "</root>";\r
+ XMLDocument doc;\r
+ doc.Parse(xml);\r
+ doc.Print(&printer1);\r
+\r
+ doc.DeepCopy(&doc2);\r
+ }\r
+ XMLPrinter printer2;\r
+ doc2.Print(&printer2);\r
+\r
+ XMLTest("DeepCopy of document.", printer1.CStr(), printer2.CStr(), true);\r
+ }\r
+\r
+\r
+ {\r
// This shouldn't crash.\r
XMLDocument doc;\r
if(XML_SUCCESS != doc.LoadFile( "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" ))\r
}\r
\r
{\r
- // Check that declarations are parsed only as the FirstChild\r
+ // Check that declarations are allowed only at beginning of document\r
const char* xml0 = "<?xml version=\"1.0\" ?>"\r
" <!-- xml version=\"1.1\" -->"\r
"<first />";\r
const char* xml1 = "<?xml version=\"1.0\" ?>"\r
- " <?xml version=\"1.1\" ?>"\r
+ "<?xml-stylesheet type=\"text/xsl\" href=\"Anything.xsl\"?>"\r
"<first />";\r
const char* xml2 = "<first />"\r
"<?xml version=\"1.0\" ?>";\r
+ const char* xml3 = "<first></first>"\r
+ "<?xml version=\"1.0\" ?>";\r
+\r
+ const char* xml4 = "<first><?xml version=\"1.0\" ?></first>";\r
+\r
XMLDocument doc;\r
doc.Parse(xml0);\r
XMLTest("Test that the code changes do not affect normal parsing", doc.Error(), false);\r
doc.Parse(xml1);\r
- XMLTest("Test that the second declaration throws an error", doc.ErrorID(), XML_ERROR_PARSING_DECLARATION);\r
+ XMLTest("Test that the second declaration is allowed", doc.Error(), false);\r
doc.Parse(xml2);\r
- XMLTest("Test that declaration after a child throws an error", doc.ErrorID(), XML_ERROR_PARSING_DECLARATION);\r
+ XMLTest("Test that declaration after a child is not allowed", doc.ErrorID(), XML_ERROR_PARSING_DECLARATION);\r
+ doc.Parse(xml3);\r
+ XMLTest("Test that declaration after a child is not allowed", doc.ErrorID(), XML_ERROR_PARSING_DECLARATION);\r
+ doc.Parse(xml4);\r
+ XMLTest("Test that declaration inside a child is not allowed", doc.ErrorID(), XML_ERROR_PARSING_DECLARATION);\r
}\r
\r
{\r
{\r
XMLDocument doc;\r
for( int i = 0; i < XML_ERROR_COUNT; i++ ) {\r
- doc.SetError( (XMLError)i, 0, 0 );\r
+ doc.SetError( (XMLError)i, 0, 0, 0 );\r
doc.ErrorName();\r
}\r
}\r
\r
+ {\r
+ // Evil memory leaks. \r
+ // If an XMLElement (etc) is allocated via NewElement() (etc.)\r
+ // and NOT added to the XMLDocument, what happens?\r
+ //\r
+ // Previously (buggy):\r
+ // The memory would be free'd when the XMLDocument is\r
+ // destructed. But the destructor wasn't called, so that\r
+ // memory allocated by the XMLElement would not be free'd.\r
+ // In practice this meant strings allocated by the XMLElement\r
+ // would leak. An edge case, but annoying.\r
+ // Now:\r
+ // The destructor is called. But the list of unlinked nodes\r
+ // has to be tracked. This has a minor performance impact\r
+ // that can become significant if you have a lot. (But why\r
+ // would you do that?)\r
+ // The only way to see this bug is in a leak tracker. This\r
+ // is compiled in by default on Windows Debug.\r
+ {\r
+ XMLDocument doc;\r
+ doc.NewElement("LEAK 1");\r
+ }\r
+ {\r
+ XMLDocument doc;\r
+ XMLElement* ele = doc.NewElement("LEAK 2");\r
+ doc.DeleteNode(ele);\r
+ }\r
+ }\r
+\r
+ {\r
+ // Crashing reported via email.\r
+ const char* xml =\r
+ "<playlist id='playlist1'>"\r
+ "<property name='track_name'>voice</property>"\r
+ "<property name='audio_track'>1</property>"\r
+ "<entry out = '604' producer = '4_playlist1' in = '0' />"\r
+ "<blank length = '1' />"\r
+ "<entry out = '1625' producer = '3_playlist' in = '0' />"\r
+ "<blank length = '2' />"\r
+ "<entry out = '946' producer = '2_playlist1' in = '0' />"\r
+ "<blank length = '1' />"\r
+ "<entry out = '128' producer = '1_playlist1' in = '0' />"\r
+ "</playlist>";\r
+\r
+ // It's not a good idea to delete elements as you walk the\r
+ // list. I'm not sure this technically should work; but it's\r
+ // an interesting test case.\r
+ XMLDocument doc;\r
+ XMLError err = doc.Parse(xml);\r
+ XMLElement* playlist = doc.FirstChildElement("playlist");\r
+\r
+ XMLTest("Crash bug parsing", err, XML_SUCCESS);\r
+ XMLTest("Crash bug parsing", true, playlist != 0);\r
+\r
+ tinyxml2::XMLElement* entry = playlist->FirstChildElement("entry");\r
+ XMLTest("Crash bug parsing", true, entry != 0);\r
+ while (entry) {\r
+ tinyxml2::XMLElement* todelete = entry;\r
+ entry = entry->NextSiblingElement("entry");\r
+ playlist->DeleteChild(todelete);\r
+ };\r
+ tinyxml2::XMLElement* blank = playlist->FirstChildElement("blank");\r
+ while (blank) {\r
+ tinyxml2::XMLElement* todelete = blank;\r
+ blank = blank->NextSiblingElement("blank");\r
+ playlist->DeleteChild(todelete);\r
+ };\r
+\r
+ tinyxml2::XMLPrinter printer;\r
+ playlist->Accept(&printer);\r
+ printf("%s\n", printer.CStr());\r
+\r
+ // No test; it only need to not crash. \r
+ // Still, wrap it up with a sanity check\r
+ int nProperty = 0;\r
+ for (const XMLElement* p = playlist->FirstChildElement("property"); p; p = p->NextSiblingElement("property")) {\r
+ nProperty++;\r
+ }\r
+ XMLTest("Crash bug parsing", nProperty, 2);\r
+ }\r
+\r
+ // ----------- Line Number Tracking --------------\r
+ {\r
+ struct TestUtil: XMLVisitor\r
+ {\r
+ void TestParseError(const char *testString, const char *docStr, XMLError expected_error, int expectedLine)\r
+ {\r
+ XMLDocument doc;\r
+ XMLError err = doc.Parse(docStr);\r
+\r
+ XMLTest(testString, true, doc.Error());\r
+ XMLTest(testString, expected_error, err);\r
+ XMLTest(testString, expectedLine, doc.GetErrorLineNum());\r
+ };\r
+\r
+ void TestStringLines(const char *testString, const char *docStr, const char *expectedLines)\r
+ {\r
+ XMLDocument doc;\r
+ doc.Parse(docStr);\r
+ XMLTest(testString, false, doc.Error());\r
+ TestDocLines(testString, doc, expectedLines);\r
+ }\r
+\r
+ void TestFileLines(const char *testString, const char *file_name, const char *expectedLines)\r
+ {\r
+ XMLDocument doc;\r
+ doc.LoadFile(file_name);\r
+ XMLTest(testString, false, doc.Error());\r
+ TestDocLines(testString, doc, expectedLines);\r
+ }\r
+\r
+ private:\r
+ DynArray<char, 10> str;\r
+\r
+ void Push(char type, int lineNum)\r
+ {\r
+ str.Push(type);\r
+ str.Push(char('0' + (lineNum / 10)));\r
+ str.Push(char('0' + (lineNum % 10)));\r
+ }\r
+\r
+ bool VisitEnter(const XMLDocument& doc)\r
+ {\r
+ Push('D', doc.GetLineNum());\r
+ return true;\r
+ }\r
+ bool VisitEnter(const XMLElement& element, const XMLAttribute* firstAttribute)\r
+ {\r
+ Push('E', element.GetLineNum());\r
+ for (const XMLAttribute *attr = firstAttribute; attr != 0; attr = attr->Next())\r
+ Push('A', attr->GetLineNum());\r
+ return true;\r
+ }\r
+ bool Visit(const XMLDeclaration& declaration)\r
+ {\r
+ Push('L', declaration.GetLineNum());\r
+ return true;\r
+ }\r
+ bool Visit(const XMLText& text)\r
+ {\r
+ Push('T', text.GetLineNum());\r
+ return true;\r
+ }\r
+ bool Visit(const XMLComment& comment)\r
+ {\r
+ Push('C', comment.GetLineNum());\r
+ return true;\r
+ }\r
+ bool Visit(const XMLUnknown& unknown)\r
+ {\r
+ Push('U', unknown.GetLineNum());\r
+ return true;\r
+ }\r
+\r
+ void TestDocLines(const char *testString, XMLDocument &doc, const char *expectedLines)\r
+ {\r
+ str.Clear();\r
+ doc.Accept(this);\r
+ str.Push(0);\r
+ XMLTest(testString, expectedLines, str.Mem());\r
+ }\r
+ } tester;\r
+\r
+ tester.TestParseError("ErrorLine-Parsing", "\n<root>\n foo \n<unclosed/>", XML_ERROR_PARSING, 2);\r
+ tester.TestParseError("ErrorLine-Declaration", "<root>\n<?xml version=\"1.0\"?>", XML_ERROR_PARSING_DECLARATION, 2);\r
+ tester.TestParseError("ErrorLine-Mismatch", "\n<root>\n</mismatch>", XML_ERROR_MISMATCHED_ELEMENT, 2);\r
+ tester.TestParseError("ErrorLine-CData", "\n<root><![CDATA[ \n foo bar \n", XML_ERROR_PARSING_CDATA, 2);\r
+ tester.TestParseError("ErrorLine-Text", "\n<root>\n foo bar \n", XML_ERROR_PARSING_TEXT, 3);\r
+ tester.TestParseError("ErrorLine-Comment", "\n<root>\n<!-- >\n", XML_ERROR_PARSING_COMMENT, 3);\r
+ tester.TestParseError("ErrorLine-Declaration", "\n<root>\n<? >\n", XML_ERROR_PARSING_DECLARATION, 3);\r
+ tester.TestParseError("ErrorLine-Unknown", "\n<root>\n<! \n", XML_ERROR_PARSING_UNKNOWN, 3);\r
+ tester.TestParseError("ErrorLine-Element", "\n<root>\n<unclosed \n", XML_ERROR_PARSING_ELEMENT, 3);\r
+ tester.TestParseError("ErrorLine-Attribute", "\n<root>\n<unclosed \n att\n", XML_ERROR_PARSING_ATTRIBUTE, 4);\r
+ tester.TestParseError("ErrorLine-ElementClose", "\n<root>\n<unclosed \n/unexpected", XML_ERROR_PARSING_ELEMENT, 3);\r
+\r
+ tester.TestStringLines(\r
+ "LineNumbers-String",\r
+\r
+ "<?xml version=\"1.0\"?>\n" // 1 Doc, DecL\r
+ "<root a='b' \n" // 2 Element Attribute\r
+ "c='d'> d <blah/> \n" // 3 Attribute Text Element\r
+ "newline in text \n" // 4 Text\r
+ "and second <zxcv/><![CDATA[\n" // 5 Element Text\r
+ " cdata test ]]><!-- comment -->\n" // 6 Comment\r
+ "<! unknown></root>", // 7 Unknown\r
+\r
+ "D01L01E02A02A03T03E03T04E05T05C06U07");\r
+\r
+ tester.TestStringLines(\r
+ "LineNumbers-CRLF",\r
+\r
+ "\r\n" // 1 Doc (arguably should be line 2)\r
+ "<?xml version=\"1.0\"?>\n" // 2 DecL\r
+ "<root>\r\n" // 3 Element\r
+ "\n" // 4\r
+ "text contining new line \n" // 5 Text\r
+ " and also containing crlf \r\n" // 6\r
+ "<sub><![CDATA[\n" // 7 Element Text\r
+ "cdata containing new line \n" // 8\r
+ " and also containing cflr\r\n" // 9\r
+ "]]></sub><sub2/></root>", // 10 Element\r
+\r
+ "D01L02E03T05E07T07E10");\r
+\r
+ tester.TestFileLines(\r
+ "LineNumbers-File",\r
+ "resources/utf8test.xml",\r
+ "D01L01E02E03A03A03T03E04A04A04T04E05A05A05T05E06A06A06T06E07A07A07T07E08A08A08T08E09T09E10T10");\r
+ }\r
+\r
// ----------- Performance tracking --------------\r
{\r
#if defined( _MSC_VER )\r
\r
FILE* perfFP = fopen("resources/dream.xml", "r");\r
fseek(perfFP, 0, SEEK_END);\r
- long size = ftell(fp);\r
+ long size = ftell(perfFP);\r
fseek(perfFP, 0, SEEK_SET);\r
\r
char* mem = new char[size + 1];\r
_CrtMemState diffMemState;\r
_CrtMemDifference( &diffMemState, &startMemState, &endMemState );\r
_CrtMemDumpStatistics( &diffMemState );\r
+\r
+ {\r
+ int leaksBeforeExit = _CrtDumpMemoryLeaks();\r
+ XMLTest( "No leaks before exit?", FALSE, leaksBeforeExit );\r
+ }\r
#endif\r
\r
printf ("\nPass %d, Fail %d\n", gPass, gFail);\r