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