Breaking up xml parsing into xml namespace and classes
This commit is contained in:
		
							parent
							
								
									8641748274
								
							
						
					
					
						commit
						283a5648a8
					
				
					 4 changed files with 439 additions and 0 deletions
				
			
		
							
								
								
									
										47
									
								
								lib/core/include/core/xml/Document.hh
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										47
									
								
								lib/core/include/core/xml/Document.hh
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,47 @@ | |||
| /* XMLDocument.hh
 | ||||
|  * vim: set tw=80: | ||||
|  * Eryn Wells <eryn@erynwells.me> | ||||
|  */ | ||||
| /**
 | ||||
|  * An XML document and related structures. | ||||
|  */ | ||||
| 
 | ||||
| #pragma once | ||||
| 
 | ||||
| #include <map> | ||||
| #include <memory> | ||||
| #include <vector> | ||||
| 
 | ||||
| #include "core/File.hh" | ||||
| #include "core/String.hh" | ||||
| 
 | ||||
| 
 | ||||
| namespace erw { | ||||
| namespace xml { | ||||
| 
 | ||||
| struct Document; | ||||
| 
 | ||||
| 
 | ||||
| /** An XML document. */ | ||||
| struct Document | ||||
| { | ||||
|     /**
 | ||||
|      * Constructor. Parse an XML document out of the given file. Doing so takes | ||||
|      * ownership of the file. | ||||
|      */ | ||||
|     Document(InFile&& file); | ||||
| 
 | ||||
|     /** Constructor. Parse an XML document from the given string. */ | ||||
|     Document(const String& string); | ||||
| 
 | ||||
|     ~Document(); | ||||
| 
 | ||||
|     const Node& root() const noexcept; | ||||
| 
 | ||||
| protected: | ||||
|     /** The root of the XML tree. The document owns its root. */ | ||||
|     Node mRoot; | ||||
| }; | ||||
| 
 | ||||
| } /* namespace xml */ | ||||
| } /* namespace erw */ | ||||
							
								
								
									
										41
									
								
								lib/core/include/core/xml/Node.hh
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										41
									
								
								lib/core/include/core/xml/Node.hh
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,41 @@ | |||
| /* Node.hh
 | ||||
|  * vim: set tw=80: | ||||
|  * Eryn Wells <eryn@erynwells.me> | ||||
|  */ | ||||
| /**
 | ||||
|  * An XML node. | ||||
|  */ | ||||
| 
 | ||||
| #pragma once | ||||
| 
 | ||||
| #include <map> | ||||
| #include <vector> | ||||
| 
 | ||||
| #include "String.hh" | ||||
| 
 | ||||
| namespace erw { | ||||
| namespace xml { | ||||
| 
 | ||||
| /** A node in an XML tree. */ | ||||
| struct Node | ||||
| { | ||||
|     typedef std::vector<Node> List; | ||||
|     typedef std::map<String, String> AttributeMap; | ||||
| 
 | ||||
|     Node(); | ||||
|     Node(const String& name, const List& children); | ||||
|     Node(const Node& other); | ||||
|     ~Node(); | ||||
| 
 | ||||
|     String name() const noexcept; | ||||
|     List children() const noexcept; | ||||
| 
 | ||||
| protected: | ||||
|     /** The name of the node. */ | ||||
|     String mName; | ||||
|     /** Children of this node. The node owns its children. */ | ||||
|     List mChildren; | ||||
| }; | ||||
| 
 | ||||
| } /* namespace xml */ | ||||
| } /* namespace erw */ | ||||
							
								
								
									
										275
									
								
								lib/core/src/xml/Document.cc
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										275
									
								
								lib/core/src/xml/Document.cc
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,275 @@ | |||
| /* XMLParser.cc
 | ||||
|  * vim: set tw=80: | ||||
|  * Eryn Wells <eryn@erynwells.me> | ||||
|  */ | ||||
| /**
 | ||||
|  * Implementation of an XML parser. | ||||
|  */ | ||||
| 
 | ||||
| #include <cstddef> | ||||
| #include <mutex> | ||||
| 
 | ||||
| #include <libxml/parser.h> | ||||
| #include <libxml/tree.h> | ||||
| 
 | ||||
| #include "XMLParser.hh" | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| namespace { | ||||
| 
 | ||||
| /*
 | ||||
|  * initLibrary -- | ||||
|  */ | ||||
| void | ||||
| initLibrary() | ||||
| { | ||||
|     static std::once_flag once; | ||||
|     std::call_once(once, []() { | ||||
|         xmlInitParser(); | ||||
|     }); | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| /*
 | ||||
|  * parseFile -- | ||||
|  */ | ||||
| XMLNode | ||||
| parseFile(File&& file) | ||||
| { | ||||
|     const size_t kInitialBufferSize = 16; | ||||
|     const size_t kBufferSize = 1024; | ||||
| 
 | ||||
|     static_assert(kInitialBufferSize < kBufferSize, | ||||
|                   "XML parser initial buffer size must be smaller than the " | ||||
|                   "total buffer size"); | ||||
| 
 | ||||
|     initLibrary(); | ||||
| 
 | ||||
|     char buffer[kBufferSize]; | ||||
|     ssize_t bytesRead = 0; | ||||
| 
 | ||||
|     bytesRead = file.read(buffer, kInitialBufferSize); | ||||
|     xmlParserCtxtPtr context = xmlCreatePushParserCtxt(nullptr, nullptr, | ||||
|                                                        buffer, bytesRead, | ||||
|                                                        file.path().c_str()); | ||||
|     if (!context) { | ||||
|         // TODO: Throw an appropriate error...
 | ||||
|         throw 42; | ||||
|     } | ||||
| 
 | ||||
|     /*
 | ||||
|      * Read chunks until we're done. Once all data has been read, indicate that | ||||
|      * the parser should terminate by calling xmlParseChunk() with a last | ||||
|      * argument of 1 rather than 0. | ||||
|      */ | ||||
|     while ((bytesRead = file.read(buffer, kBufferSize)) > 0) { | ||||
|         xmlParseChunk(context, buffer, bytesRead, 0); | ||||
|     } | ||||
|     xmlParseChunk(context, buffer, 0, 1); | ||||
| 
 | ||||
|     bool succeeded = bool(context->wellFormed); | ||||
|     xmlDocPtr document = context->myDoc; | ||||
| 
 | ||||
|     xmlFreeParserCtxt(context); | ||||
| 
 | ||||
|     return succeeded ? XMLDocument::UCPtr(new XML2Document(document)) : nullptr; | ||||
| } | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| namespace erw { | ||||
| 
 | ||||
| struct XML2Node | ||||
|     : public XMLNode | ||||
| { | ||||
|     XML2Node(xmlNodePtr node); | ||||
| 
 | ||||
|     virtual ~XML2Node(); | ||||
| 
 | ||||
|     virtual String name() const noexcept override; | ||||
|     virtual String content() const noexcept override; | ||||
|     virtual XMLNode::AttributeMap attributes() const noexcept override; | ||||
| 
 | ||||
| private: | ||||
|     xmlNodePtr mNode; | ||||
| 
 | ||||
|     /** Make a list of children of the node, excluding text nodes. */ | ||||
|     XMLNode::List childrenOfXML2Node(xmlNodePtr node) const noexcept; | ||||
| }; | ||||
| 
 | ||||
| 
 | ||||
| /** An XML parser that uses libxml2. */ | ||||
| struct XML2Document | ||||
|     : public XMLDocument | ||||
| { | ||||
|     /**
 | ||||
|      * Initialize the xml2 library, if needed. This function may be called more | ||||
|      * than once with no adverse affects. | ||||
|      */ | ||||
|     static void initLibrary(); | ||||
| 
 | ||||
|     /**
 | ||||
|      * Parse a file into a libxml2 document object. Note: because of move | ||||
|      * semantics related to the file UPtr, the file will be closed after this | ||||
|      * method completes. | ||||
|      * | ||||
|      * @param [in] file     The file to parse. | ||||
|      * @return A libxml2 document, or nullptr if the parse fails. | ||||
|      */ | ||||
|     static XMLDocument::UCPtr parseFile(File::UPtr file); | ||||
| 
 | ||||
|     static XMLDocument::UCPtr parseString(const String& string); | ||||
| 
 | ||||
|     XML2Document(xmlDocPtr document); | ||||
| 
 | ||||
|     virtual ~XML2Document(); | ||||
| 
 | ||||
|     virtual XMLNode::WCPtr root() const noexcept override; | ||||
| 
 | ||||
| private: | ||||
|     xmlDocPtr mDocument; | ||||
| }; | ||||
| 
 | ||||
| #pragma mark - erw::XMLDocument | ||||
| 
 | ||||
| /* static */ XMLDocument::UCPtr | ||||
| XMLDocument::parseFile(File::UPtr file) | ||||
| { | ||||
|     return XML2Document::parseFile(std::move(file)); | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| /* static */ XMLDocument::UCPtr | ||||
| XMLDocument::parseString(const String& string) | ||||
| { | ||||
|     return XML2Document::parseString(string); | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| XMLDocument(InFile&& file) | ||||
|     : mRoot() | ||||
| { } | ||||
| 
 | ||||
| 
 | ||||
| XMLDocument::~XMLDocument() | ||||
| { } | ||||
| 
 | ||||
| #pragma mark - erw::XML2Document | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| /* static */ XMLDocument::UCPtr | ||||
| XML2Document::parseString(const String& string) | ||||
| { | ||||
|     initLibrary(); | ||||
|     xmlDocPtr document = xmlReadMemory(string.c_str(), string.size(), "memory.xml", NULL, 0); | ||||
|     return document ? XMLDocument::UCPtr(new XML2Document(document)) : nullptr; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| XML2Document::XML2Document(xmlDocPtr document) | ||||
|     : XMLDocument(XMLNode::Ptr(new XML2Node(xmlDocGetRootElement(document)))), | ||||
|       mDocument(document) | ||||
| { } | ||||
| 
 | ||||
| 
 | ||||
| XML2Document::~XML2Document() | ||||
| { | ||||
|     if (mDocument) { | ||||
|         xmlFreeDoc(mDocument); | ||||
|         mDocument = nullptr; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| XMLNode::WCPtr | ||||
| XML2Document::root() | ||||
|     const noexcept | ||||
| { | ||||
|     return mRoot; | ||||
| } | ||||
| 
 | ||||
| #pragma mark - XMLNode | ||||
| 
 | ||||
| XMLNode::~XMLNode() | ||||
| { } | ||||
| 
 | ||||
| 
 | ||||
| XMLNode::XMLNode(XMLNode::List&& children) | ||||
|     : mChildren(children) | ||||
| { } | ||||
| 
 | ||||
| 
 | ||||
| XMLNode::WCList | ||||
| XMLNode::children() | ||||
|     const noexcept | ||||
| { | ||||
|     WCList weakChildren; | ||||
|     for (Ptr child : mChildren) { | ||||
|         weakChildren.push_back(WCPtr(child)); | ||||
|     } | ||||
|     return weakChildren; | ||||
| } | ||||
| 
 | ||||
| #pragma mark - XML2Node | ||||
| 
 | ||||
| XML2Node::XML2Node(xmlNodePtr node) | ||||
|     : XMLNode(childrenOfXML2Node(node)), | ||||
|       mNode(node) | ||||
| { } | ||||
| 
 | ||||
| 
 | ||||
| XML2Node::~XML2Node() | ||||
| { } | ||||
| 
 | ||||
| 
 | ||||
| String | ||||
| XML2Node::name() | ||||
|     const noexcept | ||||
| { | ||||
|     return (const char *)mNode->name; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| String | ||||
| XML2Node::content() | ||||
|     const noexcept | ||||
| { | ||||
|     xmlChar *content = xmlNodeGetContent(mNode); | ||||
|     String contentString((const char *)content); | ||||
|     xmlFree(content); | ||||
|     return contentString; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| XMLNode::AttributeMap | ||||
| XML2Node::attributes() | ||||
|     const noexcept | ||||
| { | ||||
|     AttributeMap attrs; | ||||
|     for (xmlAttrPtr attr = mNode->properties; attr && attr->name && attr->children; attr = attr->next) { | ||||
|         xmlChar *value = xmlNodeListGetString(mNode->doc, attr->children, 1); | ||||
|         attrs[(const char *)attr->name] = (const char *)value; | ||||
|         xmlFree(value); | ||||
|     } | ||||
|     return attrs; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| XMLNode::List | ||||
| XML2Node::childrenOfXML2Node(xmlNodePtr node) | ||||
|     const noexcept | ||||
| { | ||||
|     XMLNode::List children; | ||||
|     for (xmlNodePtr c = node->children; c != nullptr; c = c->next) { | ||||
|         if (c->type != XML_ELEMENT_NODE) { | ||||
|             continue; | ||||
|         } | ||||
|         children.push_back(XMLNode::Ptr(new XML2Node(c))); | ||||
|     } | ||||
|     return children; | ||||
| } | ||||
| 
 | ||||
| } /* namespace erw */ | ||||
							
								
								
									
										76
									
								
								lib/core/src/xml/Node.cc
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										76
									
								
								lib/core/src/xml/Node.cc
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,76 @@ | |||
| /* XMLNode.cc
 | ||||
|  * vim: set tw=80: | ||||
|  * Eryn Wells <eryn@erynwells.me> | ||||
|  */ | ||||
| /**
 | ||||
|  * Implementation of a node in an XML tree. | ||||
|  */ | ||||
| 
 | ||||
| #include "core/XMLDocument.hh" | ||||
| 
 | ||||
| #include <libxml/parser.h> | ||||
| #include <libxml/tree.h> | ||||
| 
 | ||||
| 
 | ||||
| namespace erw { | ||||
| namespace xml { | ||||
| 
 | ||||
| /*
 | ||||
|  * Node::Node -- | ||||
|  */ | ||||
| Node::Node() | ||||
|     : mName(), | ||||
|       mChildren() | ||||
| { } | ||||
| 
 | ||||
| 
 | ||||
| /*
 | ||||
|  * Node::Node -- | ||||
|  */ | ||||
| Node::Node(const String& name, | ||||
|            const List& children) | ||||
|     : mName(name), | ||||
|       mChildren(children) | ||||
| { } | ||||
| 
 | ||||
| 
 | ||||
| /*
 | ||||
|  * Node::Node -- | ||||
|  */ | ||||
| Node::Node(const Node& other) | ||||
|     : mName(other.name), | ||||
|       mChildren(other.children) | ||||
| { } | ||||
| 
 | ||||
| 
 | ||||
| /*
 | ||||
|  * Node::~Node -- | ||||
|  */ | ||||
| Node::~Node() | ||||
| { } | ||||
| 
 | ||||
| #pragma mark Properties | ||||
| 
 | ||||
| /*
 | ||||
|  * Node::name -- | ||||
|  */ | ||||
| String | ||||
| Node::name() | ||||
|     const | ||||
| { | ||||
|     return mName; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| /*
 | ||||
|  * Node::children -- | ||||
|  */ | ||||
| List | ||||
| Node::children() | ||||
|     const | ||||
| { | ||||
|     return mChildren; | ||||
| } | ||||
| 
 | ||||
| } /* namespace xml */ | ||||
| } /* namespace erw */ | ||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue