Mercurial > code
changeset 306:4fac25f2b251
Xml:
* Rename XmlParser.(h|cpp) to Xml.$1
* Class XmlParser renamed to XmlReader
* Use callbacks instead of virtual functions
Task: #311
author | David Demelier <markand@malikania.fr> |
---|---|
date | Mon, 17 Nov 2014 20:29:09 +0100 |
parents | ddd704ac6e21 |
children | e2a8cbf2dd79 |
files | C++/Tests/Xml/CMakeLists.txt C++/Tests/Xml/data/simple.xml C++/Tests/Xml/main.cpp C++/Tests/Zip/CMakeLists.txt C++/Xml.cpp C++/Xml.h C++/XmlParser.cpp C++/XmlParser.h CMakeLists.txt |
diffstat | 9 files changed, 427 insertions(+), 170 deletions(-) [+] |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/C++/Tests/Xml/CMakeLists.txt Mon Nov 17 20:29:09 2014 +0100 @@ -0,0 +1,35 @@ +# +# CMakeLists.txt -- tests for Xml +# +# Copyright (c) 2013, 2014 David Demelier <markand@malikania.fr> +# +# Permission to use, copy, modify, and/or distribute this software for any +# purpose with or without fee is hereby granted, provided that the above +# copyright notice and this permission notice appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +# + +project(xml) + +find_package(EXPAT REQUIRED) + +set( + SOURCES + ${code_SOURCE_DIR}/C++/Xml.cpp + ${code_SOURCE_DIR}/C++/Xml.h + ${xml_SOURCE_DIR}/data/simple.xml + main.cpp +) + +define_test(xml "${SOURCES}") + +target_include_directories(xml PRIVATE ${EXPAT_INCLUDE_DIRS}) +target_link_libraries(xml ${EXPAT_LIBRARIES}) +target_compile_definitions(xml PRIVATE "SOURCE=\"${xml_SOURCE_DIR}\"")
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/C++/Tests/Xml/data/simple.xml Mon Nov 17 20:29:09 2014 +0100 @@ -0,0 +1,4 @@ +<xml> + <firstName>David</firstName> + <lastName>Demelier</lastName> +</xml>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/C++/Tests/Xml/main.cpp Mon Nov 17 20:29:09 2014 +0100 @@ -0,0 +1,99 @@ +/* + * main.cpp -- main test file for Xml + * + * Copyright (c) 2013, 2014 David Demelier <markand@malikania.fr> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <gtest/gtest.h> + +#include <Xml.h> + +TEST(File, simple) +{ + XmlReader reader; + std::string firstName; + std::string lastName; + std::string element; + + reader.setOnElement([&] (const auto &name, const auto &) { + element = name; + }); + reader.setOnEndElement([&] (const auto &) { + element = ""; + }); + reader.setOnContent([&] (const auto &data) { + if (element == "firstName") + firstName = data; + else if (element == "lastName") + lastName = data; + }); + + reader.open(SOURCE "/data/simple.xml"); + + ASSERT_EQ("David", firstName); + ASSERT_EQ("Demelier", lastName); +} + +TEST(String, simple) +{ + XmlReader reader; + std::string element; + std::string name; + + reader.setOnElement([&] (const auto &name, const auto &) { + element = name; + }); + reader.setOnEndElement([&] (const auto &) { + element = ""; + }); + reader.setOnContent([&] (const auto &data) { + if (element == "name") + name = data; + }); + + reader.parse("<root><name>David</name></root>", true); + + ASSERT_EQ("David", name); +} + +TEST(String, split) +{ + XmlReader reader; + std::string element; + std::string name; + + reader.setOnElement([&] (const auto &name, const auto &) { + element = name; + }); + reader.setOnEndElement([&] (const auto &) { + element = ""; + }); + reader.setOnContent([&] (const auto &data) { + if (element == "name") + name = data; + }); + + reader.parse("<root><name>David", false); + reader.parse("</name></root>", true); + + ASSERT_EQ("David", name); +} + +int main(int argc, char **argv) +{ + testing::InitGoogleTest(&argc, argv); + + return RUN_ALL_TESTS(); +}
--- a/C++/Tests/Zip/CMakeLists.txt Sat Nov 15 13:29:30 2014 +0100 +++ b/C++/Tests/Zip/CMakeLists.txt Mon Nov 17 20:29:09 2014 +0100 @@ -24,6 +24,7 @@ SOURCES ${code_SOURCE_DIR}/C++/ZipArchive.cpp ${code_SOURCE_DIR}/C++/ZipArchive.h + ${zip_SOURCE_DIR}/data/data.txt main.cpp )
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/C++/Xml.cpp Mon Nov 17 20:29:09 2014 +0100 @@ -0,0 +1,91 @@ +/* + * Xml.h -- C++ wrappers around libexpat + * + * Copyright (c) 2013, 2014 David Demelier <markand@malikania.fr> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <fstream> + +#include "Xml.h" + +namespace { + +void xmlStartElementHandler(XmlReader *p, const XML_Char *name, const XML_Char **attrs) +{ + XmlReader::Attrs attributes; + + for (const XML_Char **p = attrs; *p != nullptr; p += 2) + attributes[p[0]] = p[1]; + + p->onElement()(name, attributes); +} + +void xmlEndElementHandler(XmlReader *p, const XML_Char *name) +{ + p->onEndElement()(name); +} + +void xmlCharacterDataHandler(XmlReader *p, const XML_Char *data, int length) +{ + p->onContent()({data, static_cast<size_t>(length)}); +} + +void xmlStartCdataSectionHandler(XmlReader *p) +{ + p->onCData()(); +} + +void xmlEndCdataSectionHandler(XmlReader *p) +{ + p->onEndCData()(); +} + +} // !namespace + +XmlReader::XmlReader() + : m_handle{nullptr, nullptr} +{ + auto p = XML_ParserCreate(nullptr); + + XML_SetUserData(p, this); + XML_SetElementHandler(p, + reinterpret_cast<XML_StartElementHandler>(xmlStartElementHandler), + reinterpret_cast<XML_EndElementHandler>(xmlEndElementHandler)); + XML_SetCharacterDataHandler(p, + reinterpret_cast<XML_CharacterDataHandler>(xmlCharacterDataHandler)); + XML_SetCdataSectionHandler(p, + reinterpret_cast<XML_StartCdataSectionHandler>(xmlStartCdataSectionHandler), + reinterpret_cast<XML_EndCdataSectionHandler>(xmlEndCdataSectionHandler)); + + m_handle = Handle(p, XML_ParserFree); +} + +void XmlReader::open(const std::string &path) +{ + std::string line; + std::ifstream ifile{path}; + + if (ifile.is_open()) { + while (std::getline(ifile, line)) + XML_Parse(m_handle.get(), line.c_str(), line.length(), false); + + XML_Parse(m_handle.get(), "", 0, true); + } +} + +void XmlReader::parse(const std::string &data, bool isfinal) +{ + XML_Parse(m_handle.get(), data.c_str(), data.length(), isfinal); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/C++/Xml.h Mon Nov 17 20:29:09 2014 +0100 @@ -0,0 +1,192 @@ +/* + * Xml.h -- C++ wrappers around libexpat + * + * Copyright (c) 2013, 2014 David Demelier <markand@malikania.fr> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _XML_WRAPPERS_H_ +#define _XML_WRAPPERS_H_ + +#include <functional> +#include <memory> +#include <string> +#include <type_traits> +#include <unordered_map> + +#include <expat.h> + +/** + * @class XmlReader + * @brief Wrapper around libexpat for reading + * + * As a wrapper on libexpat, this class works exclusively with callbacks, which + * makes it performant on large files. + */ +class XmlReader { +public: + using Attrs = std::unordered_map<std::string, std::string>; + using OnElement = std::function<void (const std::string &, const Attrs &)>; + using OnEndElement = std::function<void (const std::string &)>; + using OnContent = std::function<void (const std::string &)>; + using OnCData = std::function<void ()>; + using OnEndCData = std::function<void ()>; + using ParserType = std::remove_pointer<XML_Parser>::type; + + static inline void defOnElement(const std::string &, const Attrs &) {} + static inline void defOnEndElement(const std::string &) {} + static inline void defOnContent(const std::string &) {} + static inline void defOnCData() {} + static inline void defOnEndCData() {} + +private: + using Handle = std::unique_ptr<ParserType, void (*)(XML_Parser)>; + + Handle m_handle; + OnElement m_onElement{defOnElement}; + OnEndElement m_onEndElement{defOnEndElement}; + OnContent m_onContent{defOnContent}; + OnCData m_onCData{defOnCData}; + OnEndCData m_onEndCData{defOnEndCData}; + +public: + /** + * Default constructor. + */ + XmlReader(); + + /** + * Read the file. The file will be parsed line per line so passing + * a large file is efficient. + * + * @param path the data + */ + void open(const std::string &path); + + /** + * Parse XML data from a buffer. + * + * @param data the data + * @param isfinal tells if the buffer is finished (no more data is available) + */ + void parse(const std::string &data, bool isfinal = false); + + /** + * Get the element handler. + * + * @return element handler + */ + inline OnElement &onElement() + { + return m_onElement; + } + + /** + * Set the element handler. + * + * @param func the handler + */ + template <typename Func> + inline void setOnElement(Func &&func) + { + m_onElement = std::forward<Func>(func); + } + + /** + * Get the end element handler. + * + * @return element handler + */ + inline OnEndElement &onEndElement() + { + return m_onEndElement; + } + + /** + * Set the end element handler. + * + * @param func the handler + */ + template <typename Func> + inline void setOnEndElement(Func &&func) + { + m_onEndElement = std::forward<Func>(func); + } + + /** + * Get the content element handler. + * + * @return element handler + */ + inline OnContent &onContent() + { + return m_onContent; + } + + /** + * Set the content element handler. + * + * @param func the handler + */ + template <typename Func> + inline void setOnContent(Func &&func) + { + m_onContent = std::forward<Func>(func); + } + + /** + * Get the begin cdata element handler. + * + * @return element handler + */ + inline OnCData &onCData() + { + return m_onCData; + } + + /** + * Set the begin cdata element handler. + * + * @param func the handler + */ + template <typename Func> + inline void setOnCData(Func &&func) + { + m_onCData = std::forward<Func>(func); + } + + /** + * Get the end cdata element handler. + * + * @return element handler + */ + inline OnEndCData &onEndCData() + { + return m_onEndCData; + } + + /** + * Set the end cdata element handler. + * + * @param func the handler + */ + template <typename Func> + inline void setOnEndCData(Func &&func) + { + m_onEndCData = std::forward<Func>(func); + } +}; + + +#endif // !_XML_WRAPPERS_H_
--- a/C++/XmlParser.cpp Sat Nov 15 13:29:30 2014 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,79 +0,0 @@ -/* - * XmlParser.h -- C++ wrapper around libexpat - * - * Copyright (c) 2013 David Demelier <markand@malikania.fr> - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include <fstream> - -#include "XmlParser.h" - -namespace { - -void xmlStartElementHandler(XmlParser *p, const XML_Char *name, const XML_Char **attrs) -{ - XmlParser::Attrs attributes; - - for (const XML_Char **p = attrs; *p != NULL; p += 2) - attributes[p[0]] = p[1]; - - p->startElementHandler(name, attributes); -} - -void xmlEndElementHandler(XmlParser *p, const XML_Char *name) -{ - p->endElementHandler(name); -} - -void xmlCharacterDataHandler(XmlParser *p, const XML_Char *data, int length) -{ - std::string str; - - str.reserve(length); - str.insert(0, data, length); - - p->characterDataHandler(str); -} - -} - -XmlParser::XmlParser(const std::string &path) - : m_path(path) -{ - auto p = XML_ParserCreate(nullptr); - - XML_SetUserData(p, this); - XML_SetElementHandler(p, - reinterpret_cast<XML_StartElementHandler>(xmlStartElementHandler), - reinterpret_cast<XML_EndElementHandler>(xmlEndElementHandler)); - XML_SetCharacterDataHandler(p, - reinterpret_cast<XML_CharacterDataHandler>(xmlCharacterDataHandler)); - - m_handle = Ptr(new XML_Parser(p)); -} - -void XmlParser::open() -{ - std::string line; - std::ifstream ifile(m_path); - int done = 0; - - if (ifile.is_open() && !done) { - while (std::getline(ifile, line)) { - done = ifile.eof(); - XML_Parse(*m_handle.get(), line.c_str(), line.length(), done); - } - } -}
--- a/C++/XmlParser.h Sat Nov 15 13:29:30 2014 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,90 +0,0 @@ -/* - * XmlParser.h -- C++ wrapper around libexpat - * - * Copyright (c) 2013 David Demelier <markand@malikania.fr> - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#ifndef _XML_PARSER_H_ -#define _XML_PARSER_H_ - -#include <memory> -#include <string> -#include <unordered_map> - -#include <expat.h> - -/** - * @class XmlParser - * @brief Wrapper around libexpat - */ -class XmlParser { -public: - using Attrs = std::unordered_map<std::string, std::string>; - -private: - struct Deleter { - void operator()(XML_Parser *p) - { - XML_ParserFree(*p); - - delete p; - } - }; - - using Ptr = std::unique_ptr<XML_Parser, Deleter>; - - Ptr m_handle; - std::string m_path; - -public: - /** - * Constructor and open a file. - * - * @param path the path - */ - XmlParser(const std::string &path); - - /** - * Read the file. - */ - void open(); - - /** - * Handler when a tag occurs. - * - * @param name the tag name - * @param attributes the optional attributes - */ - virtual void startElementHandler(const std::string &name, - const Attrs &attributes) { } - - /** - * Handler when a tag ends. - * - * @param name the tag name - */ - virtual void endElementHandler(const std::string &name) { } - - /** - * Handler when data occurs. The data may have leading and - * trailing spaces. - * - * @param data the data - */ - virtual void characterDataHandler(const std::string &data) { } -}; - - -#endif // !_XML_PARSER_H_
--- a/CMakeLists.txt Sat Nov 15 13:29:30 2014 +0100 +++ b/CMakeLists.txt Mon Nov 17 20:29:09 2014 +0100 @@ -58,7 +58,7 @@ option(WITH_SOCKETS "Enable sockets tests" On) option(WITH_TREENODE "Enable treenode tests" On) option(WITH_UTF8 "Enable Utf8 functions tests" On) -option(WITH_XMLPARSER "Enable XML tests" On) +option(WITH_XML "Enable XML tests" On) option(WITH_ZIP "Enable ZipArchive tests" On) if (UNIX) @@ -109,6 +109,10 @@ add_subdirectory(C++/Tests/Xdg) endif () +if (WITH_XML) + add_subdirectory(C++/Tests/Xml) +endif () + if (WITH_ZIP) add_subdirectory(C++/Tests/Zip) endif ()