summaryrefslogtreecommitdiff
path: root/xml.h
diff options
context:
space:
mode:
Diffstat (limited to 'xml.h')
-rw-r--r--xml.h329
1 files changed, 308 insertions, 21 deletions
diff --git a/xml.h b/xml.h
index 13bf317c..2ddb247a 100644
--- a/xml.h
+++ b/xml.h
@@ -1,46 +1,333 @@
#ifndef _XML_H
#define _XML_H
-#include "parser.h"
-#include "format.h"
+#include "value.h"
+#include "debug.h"
+
+extern "C" {
+#if defined(HAVE_EXPAT)
+#include <expat.h> // expat XML parser
+#elif defined(HAVE_XMLPARSE)
+#include <xmlparse.h> // expat XML parser
+#endif
+}
namespace ledger {
-#if defined(HAVE_EXPAT) || defined(HAVE_XMLPARSE)
+class transaction_t;
+class entry_t;
+class account_t;
+class journal_t;
-class xml_parser_t : public parser_t
+namespace xml {
+
+class node_t;
+
+class document_t
{
+ const char ** builtins;
+ const int builtins_size;
+
+ typedef std::deque<std::string> names_array;
+
+ names_array names;
+
+ typedef std::map<std::string, int> names_map;
+ typedef std::pair<std::string, int> names_pair;
+
+ names_map names_index;
+
public:
- virtual bool test(std::istream& in) const;
+ node_t * top;
+
+ // Ids 0-9 are reserved. 10-999 are for "builtin" names. 1000+ are
+ // for dynamically registered names.
+ enum special_names_t {
+ CURRENT, PARENT, ROOT, ALL
+ };
+
+ document_t(node_t * _top = NULL, const char ** _builtins = NULL,
+ const int _builtins_size = 0);
- virtual unsigned int parse(std::istream& in,
- config_t& config,
- journal_t * journal,
- account_t * master = NULL,
- const std::string * original_file = NULL);
+ int register_name(const std::string& name);
+ int lookup_name_id(const std::string& name) const;
+ const char * lookup_name(int id) const;
+
+ void write(std::ostream& out) const;
};
+#define XML_NODE_IS_PARENT 0x1
+
+class parent_node_t;
+
+class node_t
+{
+public:
+ int name_id;
+#ifdef THREADSAFE
+ document_t * document;
+#else
+ static document_t * document;
#endif
+ parent_node_t * parent;
+ node_t * next;
+ node_t * prev;
+ unsigned int flags;
+ void * info;
+
+ typedef std::map<std::string, std::string> attrs_map;
+ typedef std::pair<std::string, std::string> attrs_pair;
+
+ attrs_map * attrs;
+
+ node_t(document_t * _document, parent_node_t * _parent = NULL,
+ unsigned int _flags = 0);
+
+ virtual ~node_t() {
+ TRACE_DTOR("node_t");
+ if (parent) extract();
+ if (attrs) delete attrs;
+ }
+
+ void extract(); // extract this node from its parent's child list
+
+ virtual const char * text() const {
+ assert(0);
+ }
-class format_xml_entries : public format_entries
+ const char * name() const {
+ return document->lookup_name(name_id);
+ }
+ int set_name(const char * _name) {
+ name_id = document->register_name(_name);
+ return name_id;
+ }
+ int set_name(int _name_id) {
+ name_id = _name_id;
+ return name_id;
+ }
+
+ void set_attr(const char * n, const char * v) {
+ if (! attrs)
+ attrs = new attrs_map;
+ std::pair<attrs_map::iterator, bool> result =
+ attrs->insert(attrs_pair(n, v));
+ assert(result.second);
+ }
+ const char * get_attr(const char * n) {
+ if (attrs) {
+ attrs_map::iterator i = attrs->find(n);
+ if (i != attrs->end())
+ return (*i).second.c_str();
+ }
+ return NULL;
+ }
+
+ virtual void write(std::ostream& out, int depth = 0) const = 0;
+
+private:
+ node_t(const node_t&);
+ node_t& operator=(const node_t&);
+};
+
+class parent_node_t : public node_t
+{
+public:
+ mutable node_t * _children;
+ mutable node_t * _last_child;
+
+ parent_node_t(document_t * _document, parent_node_t * _parent = NULL)
+ : node_t(_document, _parent, XML_NODE_IS_PARENT),
+ _children(NULL), _last_child(NULL)
+ {
+ TRACE_CTOR("parent_node_t(document_t *, parent_node_t *)");
+ }
+ virtual ~parent_node_t() {
+ TRACE_DTOR("parent_node_t");
+ if (_children) clear();
+ }
+
+ virtual void clear(); // clear out all child nodes
+ virtual node_t * children() const {
+ return _children;
+ }
+ virtual node_t * last_child() {
+ if (! _children)
+ children();
+ return _last_child;
+ }
+ virtual void add_child(node_t * node);
+
+ void write(std::ostream& out, int depth = 0) const;
+
+private:
+ parent_node_t(const parent_node_t&);
+ parent_node_t& operator=(const parent_node_t&);
+};
+
+class terminal_node_t : public node_t
{
- bool show_totals;
+ std::string data;
+
+public:
+ terminal_node_t(document_t * _document, parent_node_t * _parent = NULL)
+ : node_t(_document, _parent)
+ {
+ TRACE_CTOR("terminal_node_t(document_t *, parent_node_t *)");
+ }
+
+ virtual const char * text() const {
+ return data.c_str();
+ }
+ virtual void set_text(const char * _data) {
+ data = _data;
+ }
+ virtual void set_text(const std::string& _data) {
+ data = _data;
+ }
+
+ void write(std::ostream& out, int depth = 0) const;
+
+private:
+ terminal_node_t(const node_t&);
+ terminal_node_t& operator=(const node_t&);
+};
+
+#if defined(HAVE_EXPAT) || defined(HAVE_XMLPARSE)
+
+class parser_t
+{
+ public:
+ document_t * document;
+ XML_Parser parser;
+ std::string have_error;
+ const char * pending;
+ node_t::attrs_map * pending_attrs;
+ bool handled_data;
+
+ std::list<parent_node_t *> node_stack;
+
+ parser_t() : document(NULL), pending(NULL), pending_attrs(NULL),
+ handled_data(false) {}
+
+ virtual bool test(std::istream& in) const;
+ virtual document_t * parse(std::istream& in,
+ const char ** builtins = NULL,
+ const int builtins_size = 0);
+};
+
+class parse_error : public error {
public:
- format_xml_entries(std::ostream& output_stream,
- const bool _show_totals = false)
- : format_entries(output_stream, ""), show_totals(_show_totals) {
- output_stream << "<?xml version=\"1.0\"?>\n"
- << "<ledger version=\"2.5\">\n";
+ parse_error(const std::string& reason, error_context * ctxt = NULL) throw()
+ : error(reason, ctxt) {}
+ virtual ~parse_error() throw() {}
+};
+
+#endif
+
+class transaction_node_t : public parent_node_t
+{
+ transaction_t * transaction;
+
+public:
+ transaction_node_t(document_t * document, transaction_t * _transaction,
+ parent_node_t * parent = NULL)
+ : parent_node_t(document, parent), transaction(_transaction) {
+ TRACE_CTOR("transaction_node_t(document_t *, transaction_t *, parent_node_t *)");
+ set_name("transaction");
+ }
+ virtual ~transaction_node_t() {
+ TRACE_DTOR("transaction_node_t");
}
- virtual void flush() {
- format_entries::flush();
- output_stream << "</ledger>" << std::endl;
+ virtual node_t * children() const;
+};
+
+class entry_node_t : public parent_node_t
+{
+ entry_t * entry;
+
+public:
+ entry_node_t(document_t * document, entry_t * _entry,
+ parent_node_t * parent = NULL)
+ : parent_node_t(document, parent), entry(_entry) {
+ TRACE_CTOR("entry_node_t(document_t *, entry_t *, parent_node_t *)");
+ set_name("entry");
+ }
+ virtual ~entry_node_t() {
+ TRACE_DTOR("entry_node_t");
+ }
+
+ virtual node_t * children() const;
+};
+
+class account_node_t : public parent_node_t
+{
+ account_t * account;
+
+public:
+ account_node_t(document_t * document, account_t * _account,
+ parent_node_t * parent = NULL)
+ : parent_node_t(document, parent), account(_account) {
+ TRACE_CTOR("account_node_t(document_t *, account_t *, parent_node_t *)");
+ set_name("account");
+ }
+ virtual ~account_node_t() {
+ TRACE_DTOR("account_node_t");
}
- virtual void format_last_entry();
+ virtual node_t * children() const;
};
+class journal_node_t : public parent_node_t
+{
+ journal_t * journal;
+
+public:
+ journal_node_t(document_t * document, journal_t * _journal,
+ parent_node_t * parent = NULL)
+ : parent_node_t(document, parent), journal(_journal) {
+ TRACE_CTOR("journal_node_t(document_t *, journal_t *, parent_node_t *)");
+ set_name("journal");
+ }
+ virtual ~journal_node_t() {
+ TRACE_DTOR("journal_node_t");
+ }
+
+ virtual node_t * children() const;
+};
+
+template <typename T>
+inline parent_node_t * wrap_node(document_t * doc, T * item,
+ void * parent_node = NULL) {
+ assert(0);
+}
+
+template <>
+inline parent_node_t * wrap_node(document_t * doc, transaction_t * xact,
+ void * parent_node) {
+ return new transaction_node_t(doc, xact, (parent_node_t *)parent_node);
+}
+
+template <>
+inline parent_node_t * wrap_node(document_t * doc, entry_t * entry,
+ void * parent_node) {
+ return new entry_node_t(doc, entry, (parent_node_t *)parent_node);
+}
+
+template <>
+inline parent_node_t * wrap_node(document_t * doc, account_t * account,
+ void * parent_node) {
+ return new account_node_t(doc, account, (parent_node_t *)parent_node);
+}
+
+template <>
+inline parent_node_t * wrap_node(document_t * doc, journal_t * journal,
+ void * parent_node) {
+ return new journal_node_t(doc, journal, (parent_node_t *)parent_node);
+}
+
+} // namespace xml
} // namespace ledger
#endif // _XML_H