mirror of
https://github.com/edubart/otclient.git
synced 2025-12-20 23:47:12 +01:00
merge total remake
This commit is contained in:
@@ -1,8 +1,8 @@
|
||||
#ifndef OTML_H
|
||||
#define OTML_H
|
||||
|
||||
#include "otmldocument.h"
|
||||
#include "otmlnode.h"
|
||||
#include "otmlemitter.h"
|
||||
#include "otmlparser.h"
|
||||
#include "otmlnodeext.h"
|
||||
|
||||
#endif // OTML_H
|
||||
#endif
|
||||
|
||||
16
src/framework/otml/otmldeclarations.h
Normal file
16
src/framework/otml/otmldeclarations.h
Normal file
@@ -0,0 +1,16 @@
|
||||
#ifndef OTMLDECLARATIONS_H
|
||||
#define OTMLDECLARATIONS_H
|
||||
|
||||
#include <global.h>
|
||||
|
||||
class OTMLNode;
|
||||
class OTMLDocument;
|
||||
class OTMLParser;
|
||||
class OTMLEmitter;
|
||||
|
||||
typedef std::shared_ptr<OTMLNode> OTMLNodePtr;
|
||||
typedef std::shared_ptr<OTMLDocument> OTMLDocumentPtr;
|
||||
typedef std::weak_ptr<OTMLNode> OTMLNodeWeakPtr;
|
||||
typedef std::vector<OTMLNodePtr> OTMLNodeList;
|
||||
|
||||
#endif
|
||||
41
src/framework/otml/otmldocument.cpp
Normal file
41
src/framework/otml/otmldocument.cpp
Normal file
@@ -0,0 +1,41 @@
|
||||
#include "otmldocument.h"
|
||||
#include "otmlparser.h"
|
||||
#include "otmlemitter.h"
|
||||
|
||||
#include <core/resourcemanager.h>
|
||||
|
||||
OTMLDocumentPtr OTMLDocument::create()
|
||||
{
|
||||
OTMLDocumentPtr doc(new OTMLDocument);
|
||||
doc->setTag("doc");
|
||||
return doc;
|
||||
}
|
||||
|
||||
OTMLDocumentPtr OTMLDocument::parse(const std::string& fileName)
|
||||
{
|
||||
std::stringstream fin;
|
||||
g_resources.loadFile(fileName, fin);
|
||||
return parse(fin, fileName);
|
||||
}
|
||||
|
||||
OTMLDocumentPtr OTMLDocument::parse(std::istream& in, const std::string& source)
|
||||
{
|
||||
OTMLDocumentPtr doc(new OTMLDocument);
|
||||
doc->setSource(source);
|
||||
OTMLParser parser(doc, in);
|
||||
parser.parse();
|
||||
return doc;
|
||||
}
|
||||
|
||||
std::string OTMLDocument::emit()
|
||||
{
|
||||
return OTMLEmitter::emitNode(shared_from_this());
|
||||
}
|
||||
|
||||
bool OTMLDocument::save(const std::string& fileName)
|
||||
{
|
||||
setSource(fileName);
|
||||
|
||||
return g_resources.saveFile(fileName, emit());
|
||||
}
|
||||
|
||||
29
src/framework/otml/otmldocument.h
Normal file
29
src/framework/otml/otmldocument.h
Normal file
@@ -0,0 +1,29 @@
|
||||
#ifndef OTMLDOCUMENT_H
|
||||
#define OTMLDOCUMENT_H
|
||||
|
||||
#include "otmlnode.h"
|
||||
|
||||
class OTMLDocument : public OTMLNode
|
||||
{
|
||||
public:
|
||||
OTMLDocument() { }
|
||||
virtual ~OTMLDocument() { }
|
||||
|
||||
/// Create a new OTML document for filling it with nodes
|
||||
static OTMLDocumentPtr create();
|
||||
|
||||
/// Parse OTML from a file
|
||||
static OTMLDocumentPtr parse(const std::string& fileName);
|
||||
|
||||
/// Parse OTML from input stream
|
||||
/// @param source is the file name that will be used to show errors messages
|
||||
static OTMLDocumentPtr parse(std::istream& in, const std::string& source);
|
||||
|
||||
/// Emits this document and all it's children to a std::string
|
||||
std::string emit();
|
||||
|
||||
/// Save this document to a file
|
||||
bool save(const std::string& fileName);
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -1,63 +1,68 @@
|
||||
#include "otmlemitter.h"
|
||||
#include "otmlnode.h"
|
||||
#include "otmldocument.h"
|
||||
|
||||
#include <boost/algorithm/string.hpp>
|
||||
|
||||
OTMLEmitter::OTMLEmitter() :
|
||||
m_rootNode(0)
|
||||
{
|
||||
}
|
||||
|
||||
OTMLEmitter::~OTMLEmitter()
|
||||
{
|
||||
if(m_rootNode)
|
||||
delete m_rootNode;
|
||||
}
|
||||
|
||||
OTMLNode* OTMLEmitter::createDocument()
|
||||
{
|
||||
m_rootNode = new OTMLNode;
|
||||
return m_rootNode;
|
||||
}
|
||||
|
||||
std::string OTMLEmitter::emitDocument()
|
||||
{
|
||||
if(m_rootNode)
|
||||
return emitNode(m_rootNode, 0);
|
||||
return std::string();
|
||||
}
|
||||
|
||||
std::string OTMLEmitter::emitNodeValue(OTMLNode* node)
|
||||
{
|
||||
std::string value = node->value();
|
||||
if(!value.empty() && (value[0] == '"' || *value.rbegin() == '"'||
|
||||
value[0] == ' ' || *value.rbegin() == ' '||
|
||||
value[0] == '-' || value[0] == '{' || value[0] == '[' || value[0] == '|' ||
|
||||
value.find("\n") != std::string::npos)) {
|
||||
boost::replace_all(value, "\\", "\\\\");
|
||||
boost::replace_all(value, "\"", "\\\"");
|
||||
boost::replace_all(value, "\n", "\\n");
|
||||
value.append("\"");
|
||||
value.insert(0, "\"");
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
std::string OTMLEmitter::emitNode(OTMLNode* node, int currentDepth)
|
||||
std::string OTMLEmitter::emitNode(const OTMLNodePtr& node, int currentDepth)
|
||||
{
|
||||
std::stringstream ss;
|
||||
for(int i=1;i<currentDepth;++i)
|
||||
ss << " ";
|
||||
if(currentDepth > 0) {
|
||||
if(node->hasTag())
|
||||
|
||||
// emit nodes
|
||||
if(currentDepth >= 0) {
|
||||
// fill spaces for current depth
|
||||
for(int i=0;i<currentDepth;++i)
|
||||
ss << " ";
|
||||
|
||||
// emit node tag
|
||||
if(node->hasTag()) {
|
||||
ss << node->tag();
|
||||
if(node->hasValue())
|
||||
ss << (node->hasTag() ? ": " : "- ") << emitNodeValue(node);
|
||||
if(!node->hasTag() && !node->hasValue())
|
||||
|
||||
// add ':' to if the node is unique or has value
|
||||
if(node->hasValue() || node->isUnique())
|
||||
ss << ":";
|
||||
} else
|
||||
ss << "-";
|
||||
ss << std::endl;
|
||||
|
||||
// emit node value
|
||||
if(node->hasValue()) {
|
||||
ss << " ";
|
||||
|
||||
std::string value = node->value();
|
||||
|
||||
// emit multiline values
|
||||
if(value.find("\n") != std::string::npos) {
|
||||
if(value[value.length()-1] == '\n' && value[value.length()-2] == '\n')
|
||||
ss << "|+";
|
||||
else if(value[value.length()-1] == '\n')
|
||||
ss << "|";
|
||||
else
|
||||
ss << "|-";
|
||||
|
||||
// multilines
|
||||
for(std::size_t pos = 0; pos < value.length(); ++pos) {
|
||||
ss << "\n";
|
||||
|
||||
// fill spaces for multiline depth
|
||||
for(int i=0;i<currentDepth+1;++i)
|
||||
ss << " ";
|
||||
|
||||
// fill until a new line
|
||||
while(pos < value.length()) {
|
||||
if(value[pos] == '\n')
|
||||
break;
|
||||
ss << value[pos++];
|
||||
}
|
||||
}
|
||||
// emit inline values
|
||||
} else
|
||||
ss << value;
|
||||
}
|
||||
}
|
||||
for(int i=0;i<node->size();++i)
|
||||
|
||||
// emit children
|
||||
for(int i=0;i<node->size();++i) {
|
||||
if(currentDepth >= 0 || i != 0)
|
||||
ss << "\n";
|
||||
ss << emitNode(node->at(i), currentDepth+1);
|
||||
}
|
||||
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
@@ -1,24 +1,13 @@
|
||||
#ifndef OTMLEMITTER_H
|
||||
#define OTMLEMITTER_H
|
||||
|
||||
#include <string>
|
||||
|
||||
class OTMLNode;
|
||||
#include "otmldeclarations.h"
|
||||
|
||||
class OTMLEmitter
|
||||
{
|
||||
public:
|
||||
OTMLEmitter();
|
||||
~OTMLEmitter();
|
||||
|
||||
OTMLNode* createDocument();
|
||||
std::string emitDocument();
|
||||
|
||||
static std::string emitNodeValue(OTMLNode* node);
|
||||
static std::string emitNode(OTMLNode* node, int currentDepth = 0);
|
||||
|
||||
private:
|
||||
OTMLNode* m_rootNode;
|
||||
/// Emits a node and it's children to a std::string
|
||||
static std::string emitNode(const OTMLNodePtr& node, int currentDepth = -1);
|
||||
};
|
||||
|
||||
#endif // OTMLEMITTER_H
|
||||
#endif
|
||||
|
||||
25
src/framework/otml/otmlexception.cpp
Normal file
25
src/framework/otml/otmlexception.cpp
Normal file
@@ -0,0 +1,25 @@
|
||||
#include "otmlexception.h"
|
||||
#include "otmldocument.h"
|
||||
|
||||
OTMLException::OTMLException(const OTMLNodePtr& node, const std::string& error)
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << "OTML error";
|
||||
if(!node->source().empty())
|
||||
ss << " in '" << node->source() << "'";
|
||||
ss << ": " << error;
|
||||
m_what = ss.str();
|
||||
}
|
||||
|
||||
OTMLException::OTMLException(const OTMLDocumentPtr& doc, const std::string& error, int line)
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << "OTML error";
|
||||
if(doc && !doc->source().empty()) {
|
||||
ss << " in '" << doc->source() << "'";
|
||||
if(line >= 0)
|
||||
ss << " at line " << line;
|
||||
}
|
||||
ss << ": " << error;
|
||||
m_what = ss.str();
|
||||
}
|
||||
23
src/framework/otml/otmlexception.h
Normal file
23
src/framework/otml/otmlexception.h
Normal file
@@ -0,0 +1,23 @@
|
||||
#ifndef OTMLEXCEPTION_H
|
||||
#define OTMLEXCEPTION_H
|
||||
|
||||
#include "otmldeclarations.h"
|
||||
|
||||
/// All OTML errors throw this exception
|
||||
class OTMLException : public std::exception
|
||||
{
|
||||
public:
|
||||
OTMLException(const OTMLNodePtr& node, const std::string& error);
|
||||
OTMLException(const OTMLDocumentPtr& doc, const std::string& error, int line = -1);
|
||||
virtual ~OTMLException() throw() { };
|
||||
|
||||
virtual const char* what() const throw() { return m_what.c_str(); }
|
||||
|
||||
protected:
|
||||
OTMLException() { }
|
||||
void generateErrorMessage(const OTMLDocumentPtr& doc, const std::string& error, int line);
|
||||
void generateErrorMessage(const OTMLNodePtr& node, const std::string& error);
|
||||
std::string m_what;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -1,93 +1,225 @@
|
||||
#include "otmlnode.h"
|
||||
#include "otmlemitter.h"
|
||||
#include "otmldocument.h"
|
||||
|
||||
#include <boost/algorithm/string.hpp>
|
||||
|
||||
OTMLNode::OTMLNode(std::string what) :
|
||||
m_parent(0), m_line(0), m_what(what)
|
||||
OTMLNode::OTMLNode()
|
||||
{
|
||||
m_unique = false;
|
||||
}
|
||||
|
||||
OTMLNode::~OTMLNode()
|
||||
std::string OTMLNode::tag() const
|
||||
{
|
||||
for(int i=0;i<size();++i)
|
||||
delete at(i);
|
||||
return m_tag;
|
||||
}
|
||||
|
||||
OTMLNode* OTMLNode::at(const std::string& childTag) const {
|
||||
int i=0;
|
||||
while(i<size() && at(i)->tag()!=childTag)
|
||||
++i;
|
||||
return at(i);
|
||||
std::string OTMLNode::value() const
|
||||
{
|
||||
// ~ is an alias for no value
|
||||
if(m_value == "~")
|
||||
return aux::empty_string;
|
||||
return m_value;
|
||||
}
|
||||
|
||||
OTMLNode* OTMLNode::at(int pos) const
|
||||
int OTMLNode::size() const
|
||||
{
|
||||
if(pos < (int)m_children.size() && pos >= 0)
|
||||
return m_children[pos];
|
||||
return 0;
|
||||
return m_childNodes.size();
|
||||
}
|
||||
|
||||
OTMLNode* OTMLNode::atPath(const std::string& path) const
|
||||
OTMLNodePtr OTMLNode::parent() const
|
||||
{
|
||||
std::vector<std::string> nodeTags;
|
||||
OTMLNode* node = const_cast<OTMLNode*>(this);
|
||||
std::string shortcutKey;
|
||||
return m_parent.lock();
|
||||
}
|
||||
|
||||
boost::split(nodeTags, path, boost::is_any_of(std::string("/")));
|
||||
foreach(std::string& stag, nodeTags) {
|
||||
if(!shortcutKey.empty())
|
||||
shortcutKey += '.';
|
||||
shortcutKey += stag;
|
||||
if(node)
|
||||
node = node->at(stag);
|
||||
const OTMLNodeList& OTMLNode::childNodes() const
|
||||
{
|
||||
return m_childNodes;
|
||||
}
|
||||
|
||||
std::string OTMLNode::source() const
|
||||
{
|
||||
return m_source;
|
||||
}
|
||||
|
||||
bool OTMLNode::hasTag() const
|
||||
{
|
||||
return !m_tag.empty();
|
||||
}
|
||||
|
||||
bool OTMLNode::hasValue() const
|
||||
{
|
||||
return (!m_value.empty() && m_value != "~");
|
||||
}
|
||||
|
||||
bool OTMLNode::hasChildNodes() const
|
||||
{
|
||||
return size() > 0;
|
||||
}
|
||||
|
||||
bool OTMLNode::hasChild(const std::string& childTag) const
|
||||
{
|
||||
return !!get(childTag);
|
||||
}
|
||||
|
||||
bool OTMLNode::hasChild(int index) const
|
||||
{
|
||||
return !!get(index);
|
||||
}
|
||||
|
||||
bool OTMLNode::isUnique() const
|
||||
{
|
||||
return m_unique;
|
||||
}
|
||||
|
||||
void OTMLNode::setTag(std::string tag)
|
||||
{
|
||||
m_tag = tag;
|
||||
|
||||
// valued nodes that has tags are always unique
|
||||
if(!m_value.empty() && hasTag())
|
||||
setUnique();
|
||||
}
|
||||
|
||||
void OTMLNode::setValue(const std::string& value)
|
||||
{
|
||||
m_value = value;
|
||||
|
||||
// valued nodes that has tags are always unique
|
||||
if(!m_value.empty() && hasTag())
|
||||
setUnique();
|
||||
}
|
||||
|
||||
void OTMLNode::setParent(const OTMLNodePtr& parent)
|
||||
{
|
||||
m_parent = parent;
|
||||
}
|
||||
|
||||
void OTMLNode::setUnique(bool unique)
|
||||
{
|
||||
m_unique = unique;
|
||||
}
|
||||
|
||||
void OTMLNode::setSource(const std::string& source)
|
||||
{
|
||||
m_source = source;
|
||||
}
|
||||
|
||||
OTMLNodePtr OTMLNode::at(const std::string& childTag)
|
||||
{
|
||||
for(const OTMLNodePtr& child : m_childNodes) {
|
||||
if(child->tag() == childTag)
|
||||
return child;
|
||||
}
|
||||
throw OTMLException(shared_from_this(), aux::make_string("child node with tag '", childTag, "' not found"));
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
OTMLNodePtr OTMLNode::at(int childIndex)
|
||||
{
|
||||
if(childIndex < size() && childIndex >= 0)
|
||||
return m_childNodes[childIndex];
|
||||
throw OTMLException(shared_from_this(), aux::make_string("child node at index '", childIndex, "' not found"));
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
OTMLNodePtr OTMLNode::get(const std::string& childTag) const
|
||||
{
|
||||
for(const OTMLNodePtr& child : m_childNodes) {
|
||||
if(child->tag() == childTag)
|
||||
return child;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
OTMLNodePtr OTMLNode::get(int childIndex) const
|
||||
{
|
||||
if(childIndex < size() && childIndex >= 0)
|
||||
return m_childNodes[childIndex];
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void OTMLNode::addChild(const OTMLNodePtr& newChild)
|
||||
{
|
||||
// replace is needed when the tag is marked as unique
|
||||
if(newChild->hasTag()) {
|
||||
for(OTMLNodePtr node : m_childNodes) {
|
||||
if(node->tag() == newChild->tag() && (node->isUnique() || newChild->isUnique())) {
|
||||
newChild->setUnique();
|
||||
replaceChild(node, newChild);
|
||||
|
||||
// remove any other child with the same tag
|
||||
auto it = m_childNodes.begin();
|
||||
while(it != m_childNodes.end()) {
|
||||
OTMLNodePtr node = (*it);
|
||||
if(node != newChild && node->tag() == newChild->tag()) {
|
||||
node->setParent(nullptr);
|
||||
it = m_childNodes.erase(it);
|
||||
} else
|
||||
++it;
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(node)
|
||||
return node;
|
||||
else
|
||||
return at(shortcutKey);
|
||||
m_childNodes.push_back(newChild);
|
||||
newChild->setParent(shared_from_this());
|
||||
}
|
||||
|
||||
OTMLNode* OTMLNode::createNode(std::string tag)
|
||||
bool OTMLNode::removeChild(const OTMLNodePtr& oldChild)
|
||||
{
|
||||
OTMLNode* node = new OTMLNode;
|
||||
node->setTag(tag);
|
||||
addNode(node);
|
||||
return node;
|
||||
}
|
||||
|
||||
void OTMLNode::addNode(OTMLNode* node) {
|
||||
if(node->hasTag() && node->hasValue())
|
||||
if(OTMLNode* other = at(node->tag()))
|
||||
removeNode(other);
|
||||
m_children.push_back(node);
|
||||
node->setParent(this);
|
||||
}
|
||||
|
||||
bool OTMLNode::removeNode(OTMLNode* node) {
|
||||
for(NodeList::iterator it = m_children.begin(); it != m_children.end(); ++it) {
|
||||
if((*it) == node) {
|
||||
m_children.erase(it);
|
||||
for(auto it = m_childNodes.begin(); it != m_childNodes.end(); ++it) {
|
||||
if((*it) == oldChild) {
|
||||
m_childNodes.erase(it);
|
||||
oldChild->setParent(nullptr);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string OTMLNode::generateErrorMessage(const std::string& message) const {
|
||||
std::stringstream ss;
|
||||
ss << "OTML error";
|
||||
if(!what().empty())
|
||||
ss << " in '" << what() << "'";
|
||||
if(m_line > 0)
|
||||
ss << " at line " << m_line;
|
||||
if(m_line > 0 && hasTag())
|
||||
ss << ", in node '" << tag() << "'";
|
||||
ss << ": " << message;
|
||||
return ss.str();
|
||||
bool OTMLNode::replaceChild(const OTMLNodePtr& oldChild, const OTMLNodePtr& newChild)
|
||||
{
|
||||
for(auto it = m_childNodes.begin(); it != m_childNodes.end(); ++it) {
|
||||
if((*it) == oldChild) {
|
||||
oldChild->setParent(nullptr);
|
||||
newChild->setParent(shared_from_this());
|
||||
it = m_childNodes.erase(it);
|
||||
m_childNodes.insert(it, newChild);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void OTMLNode::throwError(const std::string& message) const
|
||||
void OTMLNode::clear()
|
||||
{
|
||||
throw OTMLException(generateErrorMessage(message));
|
||||
m_childNodes.clear();
|
||||
}
|
||||
|
||||
void OTMLNode::merge(const OTMLNodePtr& node)
|
||||
{
|
||||
for(const OTMLNodePtr& child : node->childNodes()) {
|
||||
OTMLNodePtr newNode(new OTMLNode);
|
||||
newNode->setUnique(child->isUnique());
|
||||
newNode->setTag(child->tag());
|
||||
newNode->setValue(child->value());
|
||||
addChild(newNode);
|
||||
newNode->merge(child);
|
||||
}
|
||||
}
|
||||
|
||||
OTMLNodePtr OTMLNode::clone() const
|
||||
{
|
||||
OTMLNodePtr myClone(new OTMLNode);
|
||||
myClone->setTag(tag());
|
||||
myClone->setValue(value());
|
||||
myClone->setUnique(isUnique());
|
||||
for(OTMLNodePtr child : childNodes())
|
||||
myClone->addChild(child->clone());
|
||||
return myClone;
|
||||
}
|
||||
|
||||
std::string OTMLNode::emit()
|
||||
{
|
||||
return OTMLEmitter::emitNode(shared_from_this(), 0);
|
||||
}
|
||||
|
||||
@@ -1,260 +1,215 @@
|
||||
#ifndef OTMLNODE_H
|
||||
#define OTMLNODE_H
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <list>
|
||||
#include <map>
|
||||
#include <stdexcept>
|
||||
#include <typeinfo>
|
||||
#include "otmldeclarations.h"
|
||||
#include "otmlexception.h"
|
||||
|
||||
#include <util/foreach.h>
|
||||
#include <util/makestring.h>
|
||||
#include <util/convert.h>
|
||||
|
||||
class OTMLException : public std::runtime_error {
|
||||
public:
|
||||
OTMLException(const std::string& what) : std::runtime_error(what) {}
|
||||
};
|
||||
|
||||
class OTMLNode
|
||||
class OTMLNode : public std::enable_shared_from_this<OTMLNode>
|
||||
{
|
||||
public:
|
||||
typedef std::vector<OTMLNode*> NodeList;
|
||||
typedef NodeList::iterator iterator;
|
||||
typedef NodeList::const_iterator const_iterator;
|
||||
OTMLNode();
|
||||
virtual ~OTMLNode() { }
|
||||
|
||||
OTMLNode(std::string what = "");
|
||||
~OTMLNode();
|
||||
std::string value() const;
|
||||
std::string tag() const;
|
||||
int size() const;
|
||||
OTMLNodePtr parent() const;
|
||||
const OTMLNodeList& childNodes() const;
|
||||
std::string source() const;
|
||||
|
||||
bool hasTag() const { return !m_tag.empty(); }
|
||||
bool hasChildren() const { return size() > 0; }
|
||||
bool hasValue() const { return !m_value.empty(); }
|
||||
bool hasChild(const std::string ctag) const { return at(ctag) != 0; }
|
||||
bool hasTag() const;
|
||||
bool hasValue() const;
|
||||
bool hasChildNodes() const;
|
||||
bool hasChild(const std::string& childTag) const;
|
||||
bool hasChild(int index) const;
|
||||
bool isUnique() const;
|
||||
|
||||
void setTag(std::string tag) { m_tag = tag; }
|
||||
void setLine(int line) { m_line = line; }
|
||||
void setValue(const std::string& value) { m_value = value; }
|
||||
void setParent(OTMLNode* parent) { m_parent = parent; }
|
||||
void setTag(std::string tag);
|
||||
void setValue(const std::string& value);
|
||||
void setParent(const OTMLNodePtr& parent);
|
||||
void setUnique(bool unique = true);
|
||||
void setSource(const std::string& source);
|
||||
|
||||
std::string tag() const { return m_tag; }
|
||||
int line() const { return m_line; }
|
||||
int size() const { return m_children.size(); }
|
||||
OTMLNode* parent() { return m_parent; }
|
||||
std::string what() const { return (m_parent ? m_parent->what() : m_what); }
|
||||
/// Same as get but if the child node doesn't exist throws an OTMLException
|
||||
OTMLNodePtr at(const std::string& childTag);
|
||||
OTMLNodePtr at(int childIndex);
|
||||
|
||||
iterator begin() { return m_children.begin(); }
|
||||
iterator end() { return m_children.end(); }
|
||||
const_iterator begin() const { return m_children.begin(); }
|
||||
const_iterator end() const { return m_children.end(); }
|
||||
/// Get a child node, if doesn't exists returns nullptr
|
||||
OTMLNodePtr get(const std::string& childTag) const;
|
||||
OTMLNodePtr get(int childIndex) const;
|
||||
|
||||
OTMLNode* front() const { return at(0); }
|
||||
OTMLNode* back() const { return at(size()-1); }
|
||||
void addChild(const OTMLNodePtr& newChild);
|
||||
bool removeChild(const OTMLNodePtr& oldChild);
|
||||
bool replaceChild(const OTMLNodePtr& oldChild, const OTMLNodePtr& newChild);
|
||||
|
||||
OTMLNode* at(const std::string& ctag) const;
|
||||
OTMLNode* at(int pos) const;
|
||||
OTMLNode *atPath(const std::string& path) const;
|
||||
/// Remove all children
|
||||
void clear();
|
||||
|
||||
OTMLNode* createNode(std::string tag = "");
|
||||
void addNode(OTMLNode* node);
|
||||
bool removeNode(OTMLNode* node);
|
||||
/// Recursively copy children from another node to this node
|
||||
void merge(const OTMLNodePtr& node);
|
||||
|
||||
std::string generateErrorMessage(const std::string& message) const;
|
||||
void throwError(const std::string& message) const;
|
||||
/// Recursively clone this node into a new one
|
||||
OTMLNodePtr clone() const;
|
||||
|
||||
std::string value(const std::string& def = "") const { return (m_value.empty() ? def : m_value); }
|
||||
std::string valueAt(const std::string ctag, const std::string& def = "") const {
|
||||
OTMLNode* c = at(ctag);
|
||||
return (c ? c->value() : def);
|
||||
}
|
||||
std::string valueAt(int pos, const std::string& def = "") const {
|
||||
OTMLNode* n = at(pos);
|
||||
return (n ? n->value() : def);
|
||||
}
|
||||
std::string valueAtPath(const std::string path, const std::string& def = "") const {
|
||||
OTMLNode* c = atPath(path);
|
||||
return (c ? c->value() : def);
|
||||
}
|
||||
/// Emits this node to a std::string
|
||||
virtual std::string emit();
|
||||
|
||||
// read into memory
|
||||
template <typename T>
|
||||
void read(T* v) const {
|
||||
if(!(*this >> *v))
|
||||
throwError(make_string("failed to cast node value to type ", std::string(typeid(T).name())));
|
||||
}
|
||||
template<typename T>
|
||||
T read();
|
||||
|
||||
template <typename T>
|
||||
bool readAt(const std::string& ctag, T* v) const {
|
||||
if(OTMLNode* node = at(ctag)) {
|
||||
node->read<T>(v);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
template<typename T>
|
||||
T read(const T& def);
|
||||
|
||||
template <typename T>
|
||||
bool readAt(int pos, T* v) const {
|
||||
if(OTMLNode* node = at(pos)) {
|
||||
node->read<T>(v);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
template<typename T, typename U>
|
||||
T readAt(const U& childIdentifier);
|
||||
|
||||
template <typename T>
|
||||
bool readAtPath(const std::string& ctag, T* v) const {
|
||||
if(OTMLNode* node = atPath(ctag)) {
|
||||
node->read<T>(v);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
template<typename T, typename U>
|
||||
T readAt(const U& childIdentifier, const T& def);
|
||||
|
||||
// read returning the result
|
||||
template <typename T>
|
||||
T read() const { T v; read<T>(&v); return v;}
|
||||
template<typename T>
|
||||
void write(const T& v);
|
||||
|
||||
template <typename T>
|
||||
T readAt(const std::string& ctag) const {
|
||||
T v;
|
||||
if(!readAt(ctag, &v))
|
||||
throwError(make_string("child node \'", ctag, "\' not found"));
|
||||
return v;
|
||||
}
|
||||
template<typename T>
|
||||
void writeAt(const std::string& childTag, const T& v);
|
||||
|
||||
template <typename T>
|
||||
T readAt(int pos) const {
|
||||
T v;
|
||||
if(!readAt(pos, &v))
|
||||
throwError(make_string("child node at pos ", pos, " not found"));
|
||||
return v;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T readAtPath(const std::string& ctag) const {
|
||||
T v;
|
||||
if(!readAtPath(ctag, &v))
|
||||
throwError(make_string("child node in path \'", ctag, "\' not found"));
|
||||
return v;
|
||||
}
|
||||
|
||||
// read with default supplied
|
||||
template <typename T>
|
||||
T readAt(const std::string& ctag, const T& def) const {
|
||||
OTMLNode* c = at(ctag);
|
||||
return (c ? c->read<T>() : def);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T readAt(int pos, const T& def) const {
|
||||
OTMLNode* c = at(pos);
|
||||
return (c ? c->read<T>() : def);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T readAtPath(const std::string& path, const T& def) const {
|
||||
OTMLNode* c = atPath(path);
|
||||
return (c ? c->read<T>() : def);
|
||||
}
|
||||
|
||||
// writing
|
||||
template <typename T>
|
||||
void write(T v) {
|
||||
if(!(*this << v))
|
||||
throwError(make_string("failed to cast to string node value of type ", typeid(T).name()));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void writeIn(int pos, T v) {
|
||||
OTMLNode* c;
|
||||
while(!at(pos))
|
||||
c = createNode();
|
||||
c->write<T>(v);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void writeIn(const std::string& ctag, T v) {
|
||||
OTMLNode* c = at(ctag);
|
||||
if(!c)
|
||||
c = createNode(ctag);
|
||||
c->write<T>(v);
|
||||
}
|
||||
template<typename T>
|
||||
void writeIn(const T& v);
|
||||
|
||||
private:
|
||||
OTMLNode* m_parent;
|
||||
int m_line;
|
||||
std::string m_what;
|
||||
NodeList m_children;
|
||||
std::string m_tag;
|
||||
std::string m_value;
|
||||
std::string m_source;
|
||||
bool m_unique;
|
||||
OTMLNodeList m_childNodes;
|
||||
OTMLNodeWeakPtr m_parent;
|
||||
};
|
||||
|
||||
// read operators
|
||||
template <typename T>
|
||||
bool operator >> (const OTMLNode& node, T& v) { return safe_convert(node.value(), v); }
|
||||
// templates for reading values
|
||||
template<typename T>
|
||||
T OTMLNode::read() {
|
||||
T v;
|
||||
if(!from_otmlnode(shared_from_this(), v))
|
||||
throw OTMLException(shared_from_this(),
|
||||
aux::make_string("failed to cast node value to type '", aux::demangle_type<T>(), "'"));
|
||||
return v;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
bool operator >> (const OTMLNode& node, std::vector<T>& v) {
|
||||
v.resize(node.size());
|
||||
for(unsigned i=0;i<node.size();++i)
|
||||
v[i] = node.readAt<T>(i);
|
||||
template<typename T>
|
||||
T OTMLNode::read(const T& def) {
|
||||
if(hasValue())
|
||||
return read<T>();
|
||||
return def;
|
||||
}
|
||||
|
||||
template<typename T, typename U>
|
||||
T OTMLNode::readAt(const U& childIdentifier) {
|
||||
OTMLNodePtr child = at(childIdentifier);
|
||||
return child->read<T>();
|
||||
}
|
||||
|
||||
template<typename T, typename U>
|
||||
T OTMLNode::readAt(const U& childIdentifier, const T& def) {
|
||||
OTMLNodePtr child = get(childIdentifier);
|
||||
if(!child)
|
||||
return def;
|
||||
return child->read<T>(def);
|
||||
}
|
||||
|
||||
|
||||
// templates for writing values
|
||||
template<typename T>
|
||||
void OTMLNode::write(const T& v) {
|
||||
to_otmlnode(shared_from_this(), v);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void OTMLNode::writeAt(const std::string& childTag, const T& v) {
|
||||
OTMLNodePtr child = get(childTag);
|
||||
bool created = false;
|
||||
if(!child) {
|
||||
child = OTMLNodePtr(new OTMLNode);
|
||||
child->setTag(childTag);
|
||||
child->setUnique();
|
||||
created = true;
|
||||
}
|
||||
child->write<T>(v);
|
||||
if(created)
|
||||
addChild(child);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void OTMLNode::writeIn(const T& v) {
|
||||
OTMLNodePtr child = OTMLNodePtr(new OTMLNode);
|
||||
child->write<T>(v);
|
||||
addChild(child);
|
||||
}
|
||||
|
||||
// templates for casting a node to another type
|
||||
template<typename T>
|
||||
bool from_otmlnode(OTMLNodePtr node, T& v) {
|
||||
return aux::cast(node->value(), v);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
bool from_otmlnode(OTMLNodePtr node, std::vector<T>& v) {
|
||||
v.resize(node->size());
|
||||
for(unsigned i=0;i<node->size();++i)
|
||||
v[i] = node->readAt<T>(i);
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
bool operator >> (const OTMLNode& node, std::list<T>& v) {
|
||||
for(unsigned i=0;i<node.size();++i)
|
||||
v.push_back(node.readAt<T>(i));
|
||||
template<typename T>
|
||||
bool from_otmlnode(OTMLNodePtr node, std::list<T>& v) {
|
||||
for(unsigned i=0;i<node->size();++i)
|
||||
v.push_back(node->readAt<T>(i));
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename K, typename T>
|
||||
bool operator >> (const OTMLNode& node, std::map<K, T>& m) {
|
||||
for(int i=0;i<node.size();++i) {
|
||||
bool from_otmlnode(OTMLNodePtr node, std::map<K, T>& m) {
|
||||
for(int i=0;i<node->size();++i) {
|
||||
K k;
|
||||
if(!safe_convert(node.at(i)->tag(), k))
|
||||
if(!aux::cast(node->at(i)->tag(), k))
|
||||
return false;
|
||||
m[k] = node.at(i)->read<T>();
|
||||
m[k] = node->at(i)->read<T>();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// write operators
|
||||
template <typename T>
|
||||
bool operator << (OTMLNode& node, const T& v) {
|
||||
std::string out;
|
||||
if(!safe_convert(v, out))
|
||||
return false;
|
||||
node.setValue(out);
|
||||
return true;
|
||||
// templates for casting a type to a node
|
||||
template<typename T>
|
||||
void to_otmlnode(OTMLNodePtr node, const T& v) {
|
||||
node->setValue(aux::unsafe_cast<std::string>(v));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
bool operator << (OTMLNode& node, const std::vector<T>& v) {
|
||||
for(unsigned i=0;i<v.size();++i)
|
||||
node.createNode()->write(v[i]);
|
||||
return true;
|
||||
template<typename T>
|
||||
void to_otmlnode(OTMLNodePtr node, const std::vector<T>& v) {
|
||||
for(unsigned i=0;i<v.size();++i) {
|
||||
OTMLNodePtr newNode(new OTMLNode);
|
||||
newNode->write(v[i]);
|
||||
node->addChild(newNode);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
bool operator << (OTMLNode& node, const std::list<T>& v) {
|
||||
for(unsigned i=0;i<v.size();++i)
|
||||
node.createNode()->write(v[i]);
|
||||
return true;
|
||||
template<typename T>
|
||||
void to_otmlnode(OTMLNodePtr node, const std::list<T>& v) {
|
||||
for(unsigned i=0;i<v.size();++i) {
|
||||
OTMLNodePtr newNode(new OTMLNode);
|
||||
newNode->write(v[i]);
|
||||
node->addChild(newNode);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename K, typename T>
|
||||
bool operator << (OTMLNode& node, const std::map<K, T>& m) {
|
||||
typename std::map<K, T>::const_iterator it;
|
||||
for(it = m.begin(); it != m.end(); ++it) {
|
||||
std::string k;
|
||||
if(!safe_convert(it->first, k))
|
||||
return false;
|
||||
node.createNode(k)->write(it->second);
|
||||
void to_otmlnode(OTMLNodePtr node, const std::map<K, T>& m) {
|
||||
for(auto it = m.begin(); it != m.end(); ++it) {
|
||||
std::string k = aux::unsafe_cast<std::string>(it->first);
|
||||
OTMLNodePtr newNode(new OTMLNode);
|
||||
newNode->setTag(k);
|
||||
newNode->setUnique();
|
||||
newNode->write(it->second);
|
||||
node->addChild(newNode);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
#include "otmlnodeext.h"
|
||||
|
||||
#endif // OTMLNODE_H
|
||||
#endif
|
||||
|
||||
@@ -1,27 +1,3 @@
|
||||
/* The MIT License
|
||||
*
|
||||
* Copyright (c) 2010 OTClient, https://github.com/edubart/otclient
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef OTMLNODEEXT_H
|
||||
#define OTMLNODEEXT_H
|
||||
|
||||
@@ -30,55 +6,41 @@
|
||||
#include <util/rect.h>
|
||||
#include <util/size.h>
|
||||
|
||||
inline bool operator>>(const OTMLNode& node, Color& color)
|
||||
{
|
||||
int r, g, b, a;
|
||||
if(node.readAt(0, &r) &&
|
||||
node.readAt(1, &g) &&
|
||||
node.readAt(2, &b)) {
|
||||
a = 255;
|
||||
node.readAt(3, &a);
|
||||
color.setRGBA(r,g,b,a);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
//inline bool from_otmlnode(const OTMLNodePtr& node, Color& color)
|
||||
//{
|
||||
//int r, g, b, a;
|
||||
//r = node->readAt<int>(0);
|
||||
//g = node->readAt<int>(1);
|
||||
//b = node->readAt<int>(2);
|
||||
//a = 255;
|
||||
//if(node->hasChild(3))
|
||||
//a = node->readAt<int>(3);
|
||||
//return true;
|
||||
//}
|
||||
|
||||
template <class T>
|
||||
inline bool operator>>(const OTMLNode& node, TPoint<T>& point)
|
||||
{
|
||||
T x, y;
|
||||
if(node.readAt(0, &x) && node.readAt(1, &y)) {
|
||||
point.x = x;
|
||||
point.y = y;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
//template <class T>
|
||||
//bool from_otmlnode(const OTMLNodePtr& node, TPoint<T>& point)
|
||||
//{
|
||||
//point.x = node->readAt<T>(0);
|
||||
//point.y = node->readAt<T>(1);
|
||||
//return true;
|
||||
//}
|
||||
|
||||
template <class T>
|
||||
inline bool operator>>(const OTMLNode& node, TSize<T>& size)
|
||||
{
|
||||
T w, h;
|
||||
if(node.readAt(0, &w) && node.readAt(1, &h)) {
|
||||
size.setSize(w, h);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
//template <class T>
|
||||
//bool from_otmlnode(const OTMLNodePtr& node, TSize<T>& size)
|
||||
//{
|
||||
//size.setSize(node->readAt<T>(0), node->readAt<T>(1));
|
||||
//return true;
|
||||
//}
|
||||
|
||||
template <class T>
|
||||
inline bool operator>>(const OTMLNode& node, TRect<T>& rect)
|
||||
{
|
||||
T x, y, width, height;
|
||||
if(node.readAt(0, &x) &&
|
||||
node.readAt(1, &y) &&
|
||||
node.readAt(2, &width) &&
|
||||
node.readAt(3, &height)) {
|
||||
rect.setRect(x, y, width, height);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
//template <class T>
|
||||
//bool from_otmlnode(const OTMLNodePtr& node, TRect<T>& rect)
|
||||
//{
|
||||
//rect.setRect(node->readAt<int>(0),
|
||||
//node->readAt<int>(1),
|
||||
//node->readAt<int>(2),
|
||||
//node->readAt<int>(3));
|
||||
//return true;
|
||||
//}
|
||||
|
||||
#endif // OTMLNODEEXT_H
|
||||
#endif
|
||||
|
||||
@@ -1,259 +1,160 @@
|
||||
#include "otmlparser.h"
|
||||
#include "otmlnode.h"
|
||||
#include "otmldocument.h"
|
||||
|
||||
#include <boost/algorithm/string.hpp>
|
||||
|
||||
OTMLParser::OTMLParser(std::istream& in, std::string what) :
|
||||
m_currentDepth(0), m_currentLine(0),
|
||||
m_rootNode(new OTMLNode(what)), m_currentParent(m_rootNode), m_previousNode(0),
|
||||
m_in(in)
|
||||
OTMLParser::OTMLParser(OTMLDocumentPtr doc, std::istream& in) :
|
||||
currentDepth(0), currentLine(0),
|
||||
doc(doc), currentParent(doc), previousNode(0),
|
||||
in(in)
|
||||
{
|
||||
parse();
|
||||
}
|
||||
|
||||
OTMLParser::~OTMLParser()
|
||||
{
|
||||
delete m_rootNode;
|
||||
}
|
||||
|
||||
void OTMLParser::throwError(const std::string& message, int line)
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << "OTML syntax error";
|
||||
if(!what().empty())
|
||||
ss << " in '" << what() << "'";
|
||||
if(line > 0)
|
||||
ss << " at line " << line;
|
||||
ss << ": " << message;
|
||||
throw OTMLException(ss.str());
|
||||
}
|
||||
|
||||
void OTMLParser::parse()
|
||||
{
|
||||
m_rootNode->setTag("document");
|
||||
if(!in.good())
|
||||
throw OTMLException(doc, "cannot read from input stream");
|
||||
|
||||
while(m_in.good() && !m_in.eof()) {
|
||||
m_currentLine++;
|
||||
std::string line;
|
||||
std::getline(m_in, line);
|
||||
parseLine(line);
|
||||
while(!in.eof())
|
||||
parseLine(getNextLine());
|
||||
}
|
||||
|
||||
std::string OTMLParser::getNextLine()
|
||||
{
|
||||
currentLine++;
|
||||
std::string line;
|
||||
std::getline(in, line);
|
||||
return line;
|
||||
}
|
||||
|
||||
int OTMLParser::getLineDepth(const std::string& line, bool multilining)
|
||||
{
|
||||
// count number of spaces at the line beginning
|
||||
std::size_t spaces = 0;
|
||||
while(line[spaces] == ' ')
|
||||
spaces++;
|
||||
|
||||
// pre calculate depth
|
||||
int depth = spaces / 2;
|
||||
|
||||
if(!multilining || depth <= currentDepth) {
|
||||
// check the next character is a tab
|
||||
if(line[spaces] == '\t')
|
||||
throw OTMLException(doc, "indentation with tabs are not allowed", currentLine);
|
||||
|
||||
// must indent every 2 spaces
|
||||
if(spaces % 2 != 0)
|
||||
throw OTMLException(doc, "must indent every 2 spaces", currentLine);
|
||||
}
|
||||
|
||||
return depth;
|
||||
}
|
||||
|
||||
void OTMLParser::parseLine(std::string line)
|
||||
{
|
||||
// calculate depth
|
||||
std::size_t numSpaces = line.find_first_not_of(' ');
|
||||
int depth = getLineDepth(line);
|
||||
|
||||
// trim left whitespaces
|
||||
boost::trim_left(line);
|
||||
// remove line sides spaces
|
||||
boost::trim(line);
|
||||
|
||||
// skip comment or empty lines
|
||||
if(line[0] == '#' || line.empty())
|
||||
// skip empty lines
|
||||
if(line.empty())
|
||||
return;
|
||||
|
||||
// calculate depth
|
||||
int depth = 0;
|
||||
if(numSpaces != std::string::npos)
|
||||
depth = numSpaces / 2;
|
||||
|
||||
// check for syntax error
|
||||
if(numSpaces != std::string::npos && numSpaces % 2 != 0)
|
||||
throwError("file must be idented every 2 whitespaces", m_currentLine);
|
||||
// skip comments
|
||||
if(boost::starts_with(line, "//"))
|
||||
return;
|
||||
|
||||
// a depth above, change current parent to the previous added node
|
||||
if(depth == m_currentDepth+1) {
|
||||
m_currentParent = m_previousNode;
|
||||
// a depth below, change parent to previus parent
|
||||
} else if(depth < m_currentDepth) {
|
||||
for(int i=0;i<m_currentDepth-depth;++i)
|
||||
m_currentParent = m_currentParent->parent();
|
||||
// else if it isn't the current depth it's a syntax error
|
||||
} else if(depth != m_currentDepth) {
|
||||
throwError("invalid indentation level", m_currentLine);
|
||||
}
|
||||
if(depth == currentDepth+1) {
|
||||
currentParent = previousNode;
|
||||
// a depth below, change parent to previous parent
|
||||
} else if(depth < currentDepth) {
|
||||
for(int i=0;i<currentDepth-depth;++i)
|
||||
currentParent = currentParent->parent();
|
||||
// if it isn't the current depth, it's a syntax error
|
||||
} else if(depth != currentDepth)
|
||||
throw OTMLException(doc, "invalid indentation depth, are you indenting correctly?", currentLine);
|
||||
|
||||
// update current depth
|
||||
m_currentDepth = depth;
|
||||
// sets current depth
|
||||
currentDepth = depth;
|
||||
|
||||
// add node
|
||||
OTMLNode* node = m_currentParent->createNode();
|
||||
m_previousNode = node;
|
||||
parseNode(node, line);
|
||||
// alright, new depth is set, the line is not empty and it isn't a comment
|
||||
// then it must be a node, so we parse it
|
||||
parseNode(line);
|
||||
}
|
||||
|
||||
void OTMLParser::parseNode(OTMLNode* node, std::string data)
|
||||
void OTMLParser::parseNode(const std::string& data)
|
||||
{
|
||||
std::string tag;
|
||||
std::string value;
|
||||
std::size_t dotsPos = data.find_first_of(':');
|
||||
|
||||
// its a node that has a value but no tag
|
||||
// node that has no tag and may have a value
|
||||
if(!data.empty() && data[0] == '-') {
|
||||
value = data.substr(1);
|
||||
boost::trim(value);
|
||||
|
||||
// check if it's value is shortcut for adding a child node
|
||||
if(dotsPos != std::string::npos && !value.empty() && value[0] != '"') {
|
||||
OTMLNode* child = node->createNode();
|
||||
parseNode(child, value);
|
||||
value.clear();
|
||||
}
|
||||
}
|
||||
// its a node that has tag and possible a value
|
||||
else if(dotsPos != std::string::npos) {
|
||||
// node that has tag and possible a value
|
||||
} else if(dotsPos != std::string::npos) {
|
||||
tag = data.substr(0, dotsPos);
|
||||
value = data.substr(dotsPos+1);
|
||||
}
|
||||
// its a node that has only a tag
|
||||
else {
|
||||
if(data.size() > dotsPos+1)
|
||||
value = data.substr(dotsPos+1);
|
||||
// node that has only a tag
|
||||
} else {
|
||||
tag = data;
|
||||
}
|
||||
|
||||
// set node tag
|
||||
boost::trim(tag);
|
||||
node->setTag(tag);
|
||||
|
||||
// set node line
|
||||
node->setLine(m_currentLine);
|
||||
|
||||
// process node value
|
||||
parseNodeValue(node, value);
|
||||
}
|
||||
|
||||
void OTMLParser::parseNodeValue(OTMLNode* node, std::string value)
|
||||
{
|
||||
boost::trim(value);
|
||||
if(value.empty())
|
||||
return;
|
||||
|
||||
// multiline text scalar
|
||||
if(value[0] == '|') {
|
||||
// process multitine values
|
||||
if(value == "|" || value == "|-" || value == "|+") {
|
||||
// reads next lines until we can a value below the same depth
|
||||
std::string multiLineData;
|
||||
do {
|
||||
std::string line;
|
||||
size_t lastPos = m_in.tellg();
|
||||
std::getline(m_in, line);
|
||||
|
||||
// calculate numspaces
|
||||
std::size_t numSpaces = line.find_first_not_of(' ');
|
||||
size_t lastPos = in.tellg();
|
||||
std::string line = getNextLine();
|
||||
int depth = getLineDepth(line, true);
|
||||
|
||||
// depth above current depth, add the text to the multiline
|
||||
if(numSpaces != std::string::npos && (int)numSpaces >= (m_currentDepth+1)*2) {
|
||||
if(depth > currentDepth) {
|
||||
multiLineData += line.substr((currentDepth+1)*2);
|
||||
// it has contents below the current depth
|
||||
} else {
|
||||
// if not empty, its a node
|
||||
boost::trim(line);
|
||||
parseTextValue(line);
|
||||
multiLineData += line + "\n";
|
||||
}
|
||||
// if has contents below the current depth, its a node
|
||||
else if(numSpaces != std::string::npos) {
|
||||
m_in.seekg(lastPos, std::ios::beg);
|
||||
break;
|
||||
}
|
||||
// else its just a new line
|
||||
else {
|
||||
multiLineData += "\n";
|
||||
}
|
||||
} while(!m_in.eof());
|
||||
|
||||
// determine how to treat last new lines
|
||||
if(value.length() == 1 || (value.length() == 2 && value[1] == '-')) {
|
||||
// remove all new lines at the end
|
||||
while(*multiLineData.rbegin() == '\n')
|
||||
multiLineData.erase(multiLineData.length()-1, 1);
|
||||
// keep just one extra line
|
||||
if(value[0] == '-')
|
||||
multiLineData.append("\n");
|
||||
} else if(value.length() > 2 || value[1] != '+')
|
||||
throwError("invalid multiline identifier", m_currentLine);
|
||||
|
||||
node->setValue(multiLineData);
|
||||
}
|
||||
// sequence
|
||||
else if(value[0] == '[') {
|
||||
std::vector<std::string> tokens;
|
||||
parseTokens(value.substr(1), tokens);
|
||||
foreach(std::string& token, tokens) {
|
||||
OTMLNode* child = node->createNode();
|
||||
child->setLine(m_currentLine);
|
||||
parseNodeValue(child, token);
|
||||
}
|
||||
}
|
||||
// inline map
|
||||
else if(value[0] == '{') {
|
||||
std::vector<std::string> tokens;
|
||||
parseTokens(value.substr(1), tokens);
|
||||
foreach(std::string& token, tokens) {
|
||||
OTMLNode* child = node->createNode();
|
||||
parseNode(child, token);
|
||||
}
|
||||
}
|
||||
// text scalar
|
||||
else {
|
||||
parseTextValue(value);
|
||||
node->setValue(value);
|
||||
}
|
||||
}
|
||||
|
||||
void OTMLParser::parseTextValue(std::string& value)
|
||||
{
|
||||
if(value[0] == '"' && value[value.length()-1] == '"') {
|
||||
value = value.substr(1, value.length()-2);
|
||||
// escape characters
|
||||
boost::replace_all(value, "\\\\", "\\");
|
||||
boost::replace_all(value, "\\\"", "\"");
|
||||
boost::replace_all(value, "\\n", "\n");
|
||||
}
|
||||
}
|
||||
|
||||
void OTMLParser::parseTokens(std::string data, std::vector<std::string>& out)
|
||||
{
|
||||
bool inQuote = false;
|
||||
int brackets = 1;
|
||||
|
||||
std::string tmp;
|
||||
uint i = 0;
|
||||
do {
|
||||
if(i<data.length()) {
|
||||
char c = data[i];
|
||||
tmp += c;
|
||||
|
||||
if(c == '"') {
|
||||
if(!inQuote)
|
||||
inQuote = true;
|
||||
else if(data[i-1] != '\\')
|
||||
inQuote = false;
|
||||
} else if(!inQuote) {
|
||||
if(c == '{' || c == '[')
|
||||
brackets++;
|
||||
else if(c == '}' || c == ']')
|
||||
brackets--;
|
||||
else if(c == ',' && brackets == 1) {
|
||||
tmp.erase(tmp.length()-1);
|
||||
boost::trim(tmp);
|
||||
if(!tmp.empty())
|
||||
out.push_back(tmp);
|
||||
tmp.clear();
|
||||
if(!line.empty()) {
|
||||
// rewind and break
|
||||
in.seekg(lastPos, std::ios::beg);
|
||||
currentLine--;
|
||||
break;
|
||||
}
|
||||
}
|
||||
multiLineData += "\n";
|
||||
} while(!in.eof());
|
||||
|
||||
if(brackets == 0) {
|
||||
tmp.erase(tmp.length()-1);
|
||||
boost::trim(tmp);
|
||||
if(!tmp.empty())
|
||||
out.push_back(tmp);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(i+1 >= data.length() && !m_in.eof()) {
|
||||
std::string line;
|
||||
std::getline(m_in, line);
|
||||
boost::trim(line);
|
||||
data += " ";
|
||||
data += line;
|
||||
}
|
||||
++i;
|
||||
} while(i<data.length());
|
||||
/* determine how to treat new lines at the end
|
||||
* | strip all new lines at the end and add just a new one
|
||||
* |- strip all new lines at the end
|
||||
* |+ keep all the new lines at the end (the new lines until next node)
|
||||
*/
|
||||
if(value == "|" || value == "|-") {
|
||||
// remove all new lines at the end
|
||||
int lastPos = multiLineData.length();
|
||||
while(multiLineData[--lastPos] == '\n')
|
||||
multiLineData.erase(lastPos, 1);
|
||||
|
||||
if(brackets != 0)
|
||||
throwError("no matching bracket while parsing, did you forget to close one?", m_currentLine);
|
||||
if(value == "|")
|
||||
multiLineData.append("\n");
|
||||
} // else it's |+
|
||||
|
||||
value = multiLineData;
|
||||
}
|
||||
|
||||
// create the node
|
||||
OTMLNodePtr node(new OTMLNode);
|
||||
node->setUnique(dotsPos != std::string::npos);
|
||||
node->setTag(tag);
|
||||
node->setValue(value);
|
||||
node->setSource(doc->source() + ":" + aux::safe_cast<std::string>(currentLine));
|
||||
currentParent->addChild(node);
|
||||
previousNode = node;
|
||||
}
|
||||
|
||||
@@ -1,37 +1,33 @@
|
||||
#ifndef OTMLPARSER_H
|
||||
#define OTMLPARSER_H
|
||||
|
||||
#include <otml/otmlnode.h>
|
||||
|
||||
#include <istream>
|
||||
#include "otmldeclarations.h"
|
||||
|
||||
class OTMLParser
|
||||
{
|
||||
public:
|
||||
OTMLParser(std::istream& in, std::string what = "");
|
||||
~OTMLParser();
|
||||
OTMLParser(OTMLDocumentPtr doc, std::istream& in);
|
||||
|
||||
OTMLNode* getDocument() const { return m_rootNode; }
|
||||
std::string what() { return m_rootNode->what(); }
|
||||
|
||||
void throwError(const std::string& message, int line);
|
||||
|
||||
protected:
|
||||
/// Parse the entire document
|
||||
void parse();
|
||||
void parseLine(std::string line);
|
||||
void parseNode(OTMLNode* node, std::string data);
|
||||
void parseNodeValue(OTMLNode* node, std::string value);
|
||||
void parseTextValue(std::string& value);
|
||||
|
||||
void parseTokens(std::string data, std::vector<std::string>& out);
|
||||
|
||||
private:
|
||||
int m_currentDepth;
|
||||
int m_currentLine;
|
||||
OTMLNode* m_rootNode;
|
||||
OTMLNode* m_currentParent;
|
||||
OTMLNode* m_previousNode;
|
||||
std::istream& m_in;
|
||||
/// Retrieve next line from the input stream
|
||||
std::string getNextLine();
|
||||
/// Counts depth of a line (every 2 spaces increments one depth)
|
||||
int getLineDepth(const std::string& line, bool multilining = false);
|
||||
|
||||
/// Parse each line of the input stream
|
||||
void parseLine(std::string line);
|
||||
/// Parse nodes tag and value
|
||||
void parseNode(const std::string& data);
|
||||
|
||||
int currentDepth;
|
||||
int currentLine;
|
||||
OTMLDocumentPtr doc;
|
||||
OTMLNodePtr currentParent;
|
||||
OTMLNodePtr previousNode;
|
||||
std::istream& in;
|
||||
};
|
||||
|
||||
#endif // OTMLPARSER_H
|
||||
#endif
|
||||
|
||||
Reference in New Issue
Block a user