merge total remake

This commit is contained in:
Eduardo Bart
2011-08-13 23:09:11 -03:00
parent 0af7856475
commit 55862b07ad
253 changed files with 6777 additions and 8463 deletions

View File

@@ -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

View 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

View 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());
}

View 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

View File

@@ -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();
}

View File

@@ -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

View 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();
}

View 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

View File

@@ -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);
}

View File

@@ -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

View File

@@ -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

View File

@@ -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;
}

View File

@@ -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