/* xml++.h
 * libxml++ and this file are copyright (C) 2000 by Ari Johnson, and
 * are covered by the GNU Lesser General Public License, which should be
 * included with libxml++ as the file COPYING.
 */


#include <string>
#include <list>

#if __GNUC__==2
#include <hash_map>
#else
#include <ext/hash_map>
using namespace __gnu_cxx;
#endif

#include <libxml/parser.h>
#include <libxml/tree.h>
#include <stdarg.h>
#include <stdio.h>

using namespace std;

#ifndef __XML_H
#define __XML_H

#if __GNUC__==3 && __GNUC_MINOR__ >=1
namespace __gnu_cxx
#else
namespace std
#endif
{
	struct hash<string> 
	{
		size_t operator()(const string &s) const 
		{
			return __stl_hash_string(s.c_str());
		};
	};
}
class XMLTree;
class XMLNode;
typedef list<XMLNode *> XMLNodeList;
typedef XMLNodeList::iterator XMLNodeIterator;
typedef XMLNodeList::const_iterator XMLNodeConstIterator;
class XMLProperty;
typedef list<XMLProperty *> XMLPropertyList;
typedef XMLPropertyList::iterator XMLPropertyIterator;
typedef XMLPropertyList::const_iterator XMLPropertyConstIterator;
typedef hash_map<string, XMLProperty *> XMLPropertyHash;

class XMLParserCallback {
public:
  virtual void start_document(void) { };
  virtual void end_document(void) { };
  virtual void start_element(const string &n, const XMLPropertyHash &p) { };
  virtual void end_element(const string &n) { };
  virtual void characters(const string &s) { };
  virtual void comment(const string &s) { };
  virtual void warning(const string &s) { };
  virtual void error(const string &s) { };
  virtual void fatal_error(const string &s) { };
};

template <class ParserCallback = XMLParserCallback>
class XMLParser {
private:
  XMLParserCallback *_parser_callback;
  xmlParserCtxtPtr _context;

  static xmlEntityPtr _get_entity(void *_parser, const xmlChar *n) {
    return xmlGetPredefinedEntity(n);
  };

  static void _start_document(void *_parser) {
    XMLParser *parser;

    parser = (XMLParser *) _parser;
    parser->start_document();
  };

  static void _end_document(void *_parser) {
    XMLParser *parser;

    parser = (XMLParser *) _parser;
    parser->end_document();
  };

  static void _start_element(void *_parser, const xmlChar *n,
				const xmlChar **p) {
    XMLParser *parser;
    XMLPropertyHash properties;

    if(p) {
      const xmlChar **cur;

      for(cur = p; cur && *cur; cur++) {
        string name, value;

        name = string((const char *) *cur++);
        value = string((const char *) *cur);

        properties[name] = new XMLProperty(name, value);
      }
    }

    parser = (XMLParser *) _parser;
    parser->start_element(string((const char *) n), properties);
  };

  static void _end_element(void *_parser, const xmlChar *n) {
    XMLParser *parser;

    parser = (XMLParser *) _parser;
    parser->end_element(string((const char *) n));
  };

  static void _characters(void *_parser, const xmlChar *s, int len) {
    XMLParser *parser;

    parser = (XMLParser *) _parser;
    parser->characters(string((const char *) s, len));
  };

  static void _comment(void *_parser, const xmlChar *s) {
    XMLParser *parser;

    parser = (XMLParser *) _parser;
    parser->comment(string((const char *) s));
  };

  static void _warning(void *_parser, const char *fmt, ...) {
    XMLParser *parser;
    va_list arg;
    char buff[1024];

    va_start(arg, fmt);
    vsprintf(buff, fmt, arg);
    va_end(arg);

    parser = (XMLParser *) _parser;
    parser->warning(string(buff));
  };

  static void _error(void *_parser, const char *fmt, ...) {
    XMLParser *parser;
    va_list arg;
    char buff[1024];

    va_start(arg, fmt);
    vsprintf(buff, fmt, arg);
    va_end(arg);

    parser = (XMLParser *) _parser;
    parser->error(string(buff));
  };

  static void _fatal_error(void *_parser, const char *fmt, ...) {
    XMLParser *parser;
    va_list arg;
    char buff[1024];

    va_start(arg, fmt);
    vsprintf(buff, fmt, arg);
    va_end(arg);

    parser = (XMLParser *) _parser;
    parser->fatal_error(string(buff));
  };

  void start_document(void) {
    if(_parser_callback)
      _parser_callback->start_document();
  };

  void end_document(void) {
    if(_parser_callback)
      _parser_callback->end_document();
  };

  void start_element(const string &n, const XMLPropertyHash &p) {
    if(_parser_callback)
      _parser_callback->start_element(n, p);
  };

  void end_element(const string &n) {
    if(_parser_callback)
      _parser_callback->end_element(n);
  };

  void characters(const string &s) {
    if(_parser_callback)
      _parser_callback->characters(s);
  };

  void comment(const string &s) {
    if(_parser_callback)
      _parser_callback->comment(s);
  };

  void warning(const string &s) {
    if(_parser_callback)
      _parser_callback->warning(s);
  };

  void error(const string &s) {
    if(_parser_callback)
      _parser_callback->error(s);
  };

  void fatal_error(const string &s) {
    if(_parser_callback)
      _parser_callback->fatal_error(s);
  };
public:
  XMLParser() {
    xmlSAXHandler sax_handler = {
      NULL,		// internalSubset
      NULL,		// isStandalone
      NULL,		// hasInternalSubset
      NULL,		// hasExternalSubset
      NULL,		// resolveEntity
      _get_entity,	// getEntity
      NULL,		// entityDecl
      NULL,		// notationDecl
      NULL,		// attributeDecl
      NULL,		// elementDecl
      NULL,		// unparsedEntityDecl
      NULL,		// setDocumentLocator
      _start_document,	// startDocument
      _end_document,	// endDocument
      _start_element,	// startElement
      _end_element,	// endElement
      NULL,		// reference
      _characters,	// characters
      NULL,		// ignorableWhitespace
      NULL,		// processingInstruction
      _comment,		// comment
      _warning,		// warning
      _error,		// error
      _fatal_error,	// fatalError
      NULL,		// getParameterEntity
      NULL,		// cdataBlock
      NULL		// externalSubset
    };

    _parser_callback = new ParserCallback;
    _context = xmlCreatePushParserCtxt(&sax_handler, this, NULL, 0, NULL);
  };

  ~XMLParser() {
    if(_parser_callback)
      delete _parser_callback;

    if(_context)
      xmlFreeParserCtxt(_context);
  };

  void parse_chunk(const string &s) {
    xmlParseChunk(_context, s.c_str(), s.length(), 0);
  };

  void finish(void) {
    xmlParseChunk(_context, NULL, 0, 1);
  };
};

class XMLTree {
private:
  bool _initialized;
  string _filename;
  XMLNode *_root;
  int _compression;
public:
  XMLTree() : _filename(), _root(NULL), _compression(0) { };
  XMLTree(const string &fn)
	: _filename(fn), _root(NULL), _compression(0) { read(); };
  ~XMLTree();
  bool initialized(void) const { return _initialized; };
  XMLNode *root(void) const { return _root; };
  XMLNode *set_root(XMLNode *n) { return _root = n; };
  const string & filename(void) const { return _filename; };
  const string & set_filename(const string &fn) { return _filename = fn; };
  int compression(void) const { return _compression; };
  int set_compression(int);
  bool read(void);
  bool read(const string &fn) { set_filename(fn); return read(); };
  bool read_buffer(const string &);
  bool write(void) const;
  bool write(const string &fn) { set_filename(fn); return write(); };
  const string & write_buffer(void) const;
};

class XMLNode {
private:
  bool _initialized;
  string _name;
  bool _is_content;
  string _content;
  XMLNodeList _children;
  XMLPropertyList _proplist;
  XMLPropertyHash _prophash;
public:
  XMLNode(const string &);
  XMLNode(const string &, const string &);
  ~XMLNode();
  bool initialized(void) const { return _initialized; };
  const string name(void) const { return _name; };
  bool is_content(void) const { return _is_content; };
  const string & content(void) const { return _content; };
  const string & set_content(const string &);
  const XMLNodeList & children(const string & = string()) const;
  XMLNode *add_child(const string &);
  XMLNode *add_child(XMLNode *);
  XMLNode *add_content(const string & = string());
  void remove_child(XMLNode *);
  const XMLPropertyList & properties(void) const { return _proplist; };
  XMLProperty *property(const string &);
  const XMLProperty *property(const string &n) const
	{ return ((XMLNode *) this)->property(n); };
  XMLProperty *add_property(const string &, const string & = string());
  void remove_property(const string &);
};

class XMLProperty {
private:
  string _name;
  string _value;
public:
  XMLProperty(const string &n, const string &v = string())
	: _name(n), _value(v) { };
  const string & name(void) const { return _name; };
  const string & value(void) const { return _value; };
  const string & set_value(const string &v) { return _value = v; };
};


#endif /* __XML_H */

