วันจันทร์ที่ 25 ตุลาคม พ.ศ. 2553

xmlconfig - parsing xml-config

xmlconfig is used to parse a simple xml file as a configuration file.


<!-- this is an example xml configuration file -->
<?xml version="1.0" encoding="ISO-8859-1"?>

<Interday>
  <Cache use="YES">
    <Log>
      <Name>../log/interday.log</Name>
      <!-- minsize = 0, maxsize = 3.99G -->
      <!-- K = 2^10 -->
      <!-- M = 2^20 -->
      <!-- G = 2^30 -->
      <Size>3.99G</Size>
      <Level>System</Level>
    </Log>
  </Cache>
</Interday>



//////////////////////////////////////////////////////////////////////////////////////////////
// xmlcnf.h
// Author: Seree Rakwong
// Date: 25-OCT-10
// Purpose: Read a simple XML file
#ifndef xmlcnf_h
#define xmlcnf_h

#include <fstream>
#include <vector>
#include <stack>
#include <map>
#include <iostream>

typedef bool   xmlbool;
typedef char   xmlint8;
typedef short  xmlint16;
typedef int    xmlint32;
typedef long   xmlint64;
typedef float  xmlfloat32;
typedef double xmlfloat64;
typedef unsigned char  xmluint8;
typedef unsigned short xmluint16;
typedef unsigned int   xmluint32;
typedef unsigned long  xmluint64;

//////////////////////////////////////////////////////////////////////////////////////////////////////////
// xmlstring
std::string trim_left(const std::string& str);
std::string trim_right(const std::string& str);
std::string trim(const std::string& str);

//////////////////////////////////////////////////////////////////////////////////////////////////////////
// xmlcnf_interface
class xmlcnf_parser;
class xmlcnf_interface {
  friend class xmlcnf_parser;
protected:
  virtual void start_doc()=0;
  virtual void end_doc()=0;
  virtual void found_error(const std::string& filename, xmluint32 line_no, xmlint32 errcode)=0;
  virtual void found_xml_element(const std::string& element)=0;
  virtual void found_start_element(const std::string& element)=0;
  virtual void found_end_element(const std::string& element)=0;
  virtual void found_comment(const std::string& comment)=0;
  virtual void found_text(const std::string& element, const std::string& text)=0;
  virtual void found_attribute(const std::string& element, const std::string& attribute, const std::string& value)=0;
};

//////////////////////////////////////////////////////////////////////////////////////////////////////////
// xmlcnf_attribute
class xmlcnf_attribute {
public:
  xmlcnf_attribute();
  xmlcnf_attribute(const std::string& attibute, const std::string& value);

public:
  const std::string attribute() const;
  const std::string value() const;
  std::string& attribute();
  std::string& value();

private:
  std::string attribute_;
  std::string value_;
};

//////////////////////////////////////////////////////////////////////////////////////////////////////////
// xmlcnf_element
class xmlcnf_element {
public:
  xmlcnf_element();
  xmlcnf_element(const std::string& name, const std::string& text = "");

public:
  const std::string name() const;
  const std::string text() const;
  std::string& name();
  std::string& text();

private:
  std::vector<xmlcnf_attribute> attributes_;
  std::string name_;
  std::string text_;
};

//////////////////////////////////////////////////////////////////////////////////////////////////////////
// xmlcnf_parser
class xmlcnf_parser : public std::ifstream {
private:
  std::stack<xmlcnf_element> elements_;
  std::string                filename_;

public:
  xmlcnf_parser();
  xmlcnf_parser(const char *filename, std::ios_base::openmode mode = ios_base::in);
  xmlcnf_parser(const std::string& filename);

public:
  void open(const char *filename, std::ios_base::openmode mode = ios_base::in);
  void open(const std::string& filename);
  int  parse(xmlcnf_interface *xmlcnf_ptr);
#ifdef XML_DEBUG
  void dump_error(const std::string& filename, xmluint32 line_no, const std::string& funcname);
#endif

protected:
  int  read_element(xmlcnf_interface *xmlcnf_ptr, xmluint32 *line_ptr, char *curchar_ptr);

private:
  int  parse_xml_element(xmlcnf_interface *xmlcnf_ptr, xmluint32 *line_ptr, const std::string& element);
  int  parse_xml_comment(xmlcnf_interface *xmlcnf_ptr, xmluint32 *line_ptr, const std::string& element);
  int  parse_xml_start_element(xmlcnf_interface *xmlcnf_ptr, xmluint32 *line_ptr, const std::string& element);
  int  parse_xml_end_element(xmlcnf_interface *xmlcnf_ptr, xmluint32 *line_ptr, const std::string& element);
  int  parse_xml_attribute(xmlcnf_interface *xmlcnf_ptr, xmluint32 *line_ptr, const std::string& element);
  char skip_whitespaces(xmluint32 *line_ptr);
  bool is_whitespaces(char ch);
  bool is_valid_char(char ch);
  char skip_if_not(xmlcnf_interface *xmlcnf_ptr, xmluint32 *line_ptr, char stop_char);
  bool is_valid_name(const std::string& name);

public:
  enum xmlcnf_error {
    error_none = 0,
    // xml continue reading
    start_of_element    = 1,
    end_of_element      = 2,
    // basic file error
    interface_object_is_null = -1,
    file_is_not_opened       = -2,
    file_is_bad              = -3,
    file_not_complete        = -4,
    // parsing error
    element_is_not_correct    = 1000,
    found_new_start_element   = 1001,
    element_is_reserved       = 1002,
    attribute_is_not_complete = 1003,
    missing_quote             = 1004,
    end_element_not_matched   = 1005,
    comment_not_complete      = 1006,
    invalid_xml_element       = 1007,
    invalid_attribute_name    = 1008
  };

public:
  std::string errtext(xmlint32 errcode) const;
};

//////////////////////////////////////////////////////////////////////////////////////////////////////////
//
class xmlcnf : public xmlcnf_interface,
               public xmlcnf_parser {
protected:
  void start_doc();
  void end_doc();
  void found_error(const std::string& filename, xmluint32 line_no, xmlint32 errcode);
  void found_xml_element(const std::string& element);
  void found_start_element(const std::string& element);
  void found_end_element(const std::string& element);
  void found_comment(const std::string& comment);
  void found_text(const std::string& element, const std::string& text);
  void found_attribute(const std::string& element, const std::string& attribute, const std::string& value);

public:
  int parse();
#ifdef XML_DEBUG
  void dump_keys();
#endif
  std::string get_string(const std::string& key, const std::string& def_value = "");
  xmlint64    get_int(const std::string& key, xmlint64 def_value = 0);
  xmluint64   get_uint(const std::string& key, xmlint64 def_value = 0);
  xmlfloat64  get_float(const std::string& key, xmlfloat64 def_value = 0.0);
  xmlbool     get_bool(const std::string& key, const std::string& true_value = "YES");

private:
  std::map<std::string, std::string> map_keys_;
  std::vector<std::string>           vector_keys_;
};


#endif //xmlcnf_h


//////////////////////////////////////////////////////////////////////////////////////////////
// xmlcnf.cpp
// Author: Seree Rakwong
// Date: 25-OCT-10
// Purpose: Read a simple XML file
#include "xmlcnf.h"
#ifdef XML_DEBUG
#include <iostream>
using std::cerr;
using std::cout;
using std::endl;
#endif

using std::string;
using std::vector;
using std::stack;
using std::ifstream;
using std::map;

//////////////////////////////////////////////////////////////////////////////////////////////////////////
// xmlcnf_attribute
xmlcnf_attribute::xmlcnf_attribute() 
: attribute_(),
  value_() {
}

xmlcnf_attribute::xmlcnf_attribute(const string& attribute, const string& value)
: attribute_(attribute),
  value_(value) {
}

const string xmlcnf_attribute::attribute() const {
  return attribute_;
}

const string xmlcnf_attribute::value() const {
  return value_;
}

string& xmlcnf_attribute::attribute() {
  return attribute_;
}

string& xmlcnf_attribute::value() {
  return value_;
}

//////////////////////////////////////////////////////////////////////////////////////////////////////////
// xmlcnf_element
xmlcnf_element::xmlcnf_element() 
: name_(),
  text_() {
}

xmlcnf_element::xmlcnf_element(const string& name, const string& text)
: name_(name),
  text_(text) {
}

const string xmlcnf_element::name() const {
  return name_;
}

const string xmlcnf_element::text() const {
  return text_;
}

string& xmlcnf_element::name() {
  return name_;
}

string& xmlcnf_element::text() {
  return text_;
}

//////////////////////////////////////////////////////////////////////////////////////////////////////////
// xmlcnf_parser
xmlcnf_parser::xmlcnf_parser()
: filename_("") {
}

xmlcnf_parser::xmlcnf_parser(const char *filename, ios_base::openmode mode)
: ifstream(filename, mode),
  filename_(filename) {
}

xmlcnf_parser::xmlcnf_parser(const string& filename)
: ifstream(filename.c_str()),
  filename_(filename) {
}

void xmlcnf_parser::open(const char *filename, ios_base::openmode mode) {
  ifstream::open(filename, mode);
  filename_ = filename; 
}

void xmlcnf_parser::open(const string& filename) {
  ifstream::open(filename.c_str());
  filename_ = filename;
}

#ifdef XML_DEBUG
void xmlcnf_parser::dump_error(const string& filename, xmluint32 line_no, const string& funcname) {
  cerr << "\nFilename: " << filename
       << "\tLine: "     << line_no
       << "\tFunction: " << funcname
       << endl;
}
#endif

char xmlcnf_parser::skip_whitespaces(xmluint32 *line_ptr) {
  char ch = 0;
  while(good()) {
    ch = get();
    if(is_whitespaces(ch)) {
      if(ch == '\n') (*line_ptr)++;
    }
    else return ch;
  }
  return ch;
}

char xmlcnf_parser::skip_if_not(xmlcnf_interface *xmlcnf_ptr, xmluint32 *line_ptr, char stop_char) {
  char ch = 0;
  string text;
  while(good()) {
    ch = get();
    if(ch == stop_char) {
      if(!elements_.empty()) {
        if(elements_.top().text().empty()) {
          string trim_text = trim(text);
          if(trim_text.length() > 0) {
            elements_.top().text() = trim_text;
            xmlcnf_ptr->found_text(elements_.top().name(), trim_text);
            //elements_.top().text() = text;
            //xmlcnf_ptr->found_text(elements_.top().name(), text);
          }
        }
      }
      return stop_char;
    }
    else if(ch == '\n') (*line_ptr)++;
    text += ch;
  }
  return ch;
}

bool xmlcnf_parser::is_whitespaces(char ch) {
  if(ch == ' ' || ch == '\t' || ch == '\r' || ch == '\n')
    return true;
  return false;
}

int xmlcnf_parser::parse(xmlcnf_interface *xmlcnf_ptr) {
  if(!xmlcnf_ptr) return interface_object_is_null;
  if(!is_open())  return file_is_not_opened;
  if(!good())     return file_is_bad;

  // start reading
  xmlcnf_ptr->start_doc();

  xmluint32 line_no  = 1;
  char      cur_char = 0;
  int       rc       = error_none;

  if(good()) cur_char = get(); // first read
  while(good()) {
    // always read an element first
    rc = read_element(xmlcnf_ptr, &line_no, &cur_char);
    if(rc != error_none) {
      if(rc != end_of_element) break;
    }
    cur_char = skip_if_not(xmlcnf_ptr, &line_no, '<');
  }

  // end reading
  xmlcnf_ptr->end_doc();
  return rc;
}

int xmlcnf_parser::read_element(xmlcnf_interface *xmlcnf_ptr, xmluint32 *line_ptr, char *curchar_ptr) {
  if(is_whitespaces(*curchar_ptr)) {
    *curchar_ptr = skip_whitespaces(line_ptr);
  }
  if(*curchar_ptr == '>') { //read next char
    if(good()) *curchar_ptr = get();
  }
  // start
  string element("<");
  char ch = get();
  while(good()) {
    if(ch == '\n') { // xml stores a new line as LF
      (*line_ptr)++;
    }
    else if(ch == '<') {
      if(element.length() > 4 && element.substr(0, 4) != "<!--") {
        xmlcnf_ptr->found_error(filename_, *line_ptr, found_new_start_element);
#ifdef XML_DEBUG
        dump_error(__FILE__, __LINE__, "xmlcnf_parser::read_element::found_new_start_element");
#endif
        return found_new_start_element;
      }
    }
    else if(ch == '>') {
      size_t len = element.length();
      if(len >= 4 && element.substr(0, 4) != "<!--") {
        //end-of-element
        break;
      }
      else {
        if(len >= 6 && element.substr(len-2, 2) == "--") {
          // end-of-comment
          break;
        }
      }
    }
    element += ch;
    ch = get();
  }
  if(!good()) {
    xmlcnf_ptr->found_error(filename_, *line_ptr, file_not_complete);
#ifdef XML_DEBUG
    dump_error(__FILE__, __LINE__, "xmlcnf_parser::read_element::file_not_complete");
#endif
    return file_not_complete;
  }
  // the last
  element += ch; // should be <element_name ...> or <?xml ... ?> or <!-- ... --> or </element_name>

  // begin-of-element the length has 3 characters at least, but
  // if it the end-of-element has 4 characters at least
  size_t len = element.length();
  // check if the length is less than 3
  if(len < 3) {
    xmlcnf_ptr->found_error(filename_, *line_ptr, element_is_not_correct);
#ifdef XML_DEBUG
    dump_error(__FILE__, __LINE__, "xmlcnf_parser::read_element::element_is_not_correct");
#endif
    return element_is_not_correct;
  }
  // check if it is end-of-element
  if(element[1] == '/' && len < 4) {
    xmlcnf_ptr->found_error(filename_, *line_ptr, element_is_not_correct);
#ifdef XML_DEBUG
    dump_error(__FILE__, __LINE__, "xmlcnf_parser::read_element::element_is_not_correct");
#endif
    return element_is_not_correct;
  }
  // parsing element
  int rc = error_none;
  switch(element[1]) {
    case '?': {
      // xml declaration
      rc = parse_xml_element(xmlcnf_ptr, line_ptr, element);
      break;
    }
    case '/': {
      // end-of-element
      rc = parse_xml_end_element(xmlcnf_ptr, line_ptr, element);
      if(rc == end_of_element) {
        *curchar_ptr = ch;
        return rc;
      }
      break;
    }
    case '!': {
      // comment
      rc = parse_xml_comment(xmlcnf_ptr, line_ptr, element);
      break;
    }
    default: {
      // begin-of-element
      rc = parse_xml_start_element(xmlcnf_ptr, line_ptr, element);
      break;
    }
  }
  if(rc != error_none) return rc;

  // next char
  *curchar_ptr = ch;
  return error_none;
}

int xmlcnf_parser::parse_xml_element(xmlcnf_interface *xmlcnf_ptr, xmluint32 *line_ptr, const string& element) {
  if(element.length() < 7) {
    xmlcnf_ptr->found_error(filename_, *line_ptr, invalid_xml_element);
#ifdef XML_DEBUG
    dump_error(__FILE__, __LINE__, "xmlcnf_parser::parse_xml_element::invalid_xml_element");
#endif
    return invalid_xml_element;
  }
  if(element.substr(0, 5) != "<?xml") {
    xmlcnf_ptr->found_error(filename_, *line_ptr, invalid_xml_element);
#ifdef XML_DEBUG
    dump_error(__FILE__, __LINE__, "xmlcnf_parser::parse_xml_element::invalid_xml_element");
#endif
    return invalid_xml_element;
  }
  if(element.substr(element.length()-2, 2) != "?>") {
    xmlcnf_ptr->found_error(filename_, *line_ptr, invalid_xml_element);
#ifdef XML_DEBUG
    dump_error(__FILE__, __LINE__, "xmlcnf_parser::parse_xml_element::invalid_xml_element");
#endif
    return invalid_xml_element;
  }
  elements_.push(xmlcnf_element("xml", ""));
  xmlcnf_ptr->found_xml_element(element);

  size_t pos = element.find_first_not_of(" \t\r", 5);
  string xml_attribute = element.substr(pos, element.length()-pos-2);
  xml_attribute = trim(xml_attribute);

  int rc = parse_xml_attribute(xmlcnf_ptr, line_ptr, xml_attribute);
  return rc;
}

int xmlcnf_parser::parse_xml_comment(xmlcnf_interface *xmlcnf_ptr, xmluint32 *line_ptr, const string& element) {
  if(element.length() < 7) {
    // this is not a comment
    xmlcnf_ptr->found_error(filename_, *line_ptr, comment_not_complete);
#ifdef XML_DEBUG
    dump_error(__FILE__, __LINE__, "xmlcnf_parser::parse_xml_comment::comment_not_complete");
#endif
    return comment_not_complete;
  }
  // check 4 first characters if they must be "<!--"
  if(element.substr(0, 4) != "<!--") {
    // this is not a comment
    xmlcnf_ptr->found_error(filename_, *line_ptr, comment_not_complete);
#ifdef XML_DEBUG
    dump_error(__FILE__, __LINE__, "xmlcnf_parser::parse_xml_comment::comment_not_complete");
#endif
    return comment_not_complete;
  }
  // check 3 last characters if they must be "-->"
  if(element.substr(element.length()-3, 3) != "-->") {
    // this is not a comment
    xmlcnf_ptr->found_error(filename_, *line_ptr, comment_not_complete);
#ifdef XML_DEBUG
    dump_error(__FILE__, __LINE__, "xmlcnf_parser::parse_xml_comment::comment_not_complete");
#endif
    return comment_not_complete;
  }
  // this is a comment
  xmlcnf_ptr->found_comment(element.substr(4, element.length()-7));
  return error_none;
}

int xmlcnf_parser::parse_xml_start_element(xmlcnf_interface *xmlcnf_ptr, xmluint32 *line_ptr, const string& element) {
  string start_element = element.substr(1, element.length()-2);
  const char *str = start_element.c_str();
  size_t len = start_element.length();

  if(len < 3) { // there is no an attribute
    elements_.push(xmlcnf_element(str, ""));
    xmlcnf_ptr->found_start_element(start_element);
  }
  else {
    // check if first 3 characters are 'xml', 'XML', 'Xml, or etc
    if(*str == 'x' || *str == 'X') {
      if(*(str+1) == 'm' || *(str+1) == 'M') {
        if(*(str+2) == 'l' || *(str+2) == 'L') {
          xmlcnf_ptr->found_error(filename_, *line_ptr, element_is_reserved);
#ifdef XML_DEBUG
          dump_error(__FILE__, __LINE__, "xmlcnf_parser::parse_xml_start_element::element_is_reserved");
#endif
          return element_is_reserved;
        }
      }
    }
    // element may have attributes
    size_t pos = start_element.find_first_of('='); // find if there are some '='
    if(pos != string::npos) { // found
      pos = start_element.find_first_of(" \t\r");
      string tag = start_element.substr(0, pos);
      elements_.push(xmlcnf_element(tag, ""));
      xmlcnf_ptr->found_start_element(tag);

      //skip whitespaces
      pos = start_element.find_first_not_of(" \t\r", pos+1);

      int rc = parse_xml_attribute(xmlcnf_ptr, line_ptr, start_element.substr(pos, start_element.length()-1));
      if(rc != error_none) return rc;
    }
    else {
      // only element found
      pos = start_element.find_first_of(' ');
      if(pos != string::npos) {
        string tag = start_element.substr(0, pos);
      }
      elements_.push(xmlcnf_element(str, ""));
      xmlcnf_ptr->found_start_element(start_element);
    }
  }

  return error_none;
}

int xmlcnf_parser::parse_xml_end_element(xmlcnf_interface *xmlcnf_ptr, xmluint32 *line_ptr, const string& element) {
  if(elements_.empty()) {
    xmlcnf_ptr->found_error(filename_, *line_ptr, end_element_not_matched);
#ifdef XML_DEBUG
    dump_error(__FILE__, __LINE__, "xmlcnf_parser::parse_xml_attribute::end_element_not_matched");
#endif
    return end_element_not_matched;
  }
  string end_tag = element.substr(2, element.length()-3);
  if(end_tag != elements_.top().name()) {
#ifdef XML_DEBUG
cerr << "end_tag: [" << end_tag << "]"
     << "\ttop: [" << elements_.top().name() << "]" << endl;
#endif
    xmlcnf_ptr->found_error(filename_, *line_ptr, end_element_not_matched);
#ifdef XML_DEBUG
    dump_error(__FILE__, __LINE__, "xmlcnf_parser::parse_xml_attribute::end_element_not_matched");
#endif
    return end_element_not_matched;
  }
  // found end tag and matched
  xmlcnf_ptr->found_end_element(end_tag);

  // remove the top
  elements_.pop();

  return end_of_element;
}

int xmlcnf_parser::parse_xml_attribute(xmlcnf_interface *xmlcnf_ptr, xmluint32 *line_ptr, const string& element) {
  xmlcnf_element start_element = elements_.top();
  string sub_element = trim(element);
  size_t pos_attribute = 0;
  while(sub_element.length() > 0) {
    // read attribute til found whitespaces or '='
    string attribute;
    size_t end_attribute = sub_element.find_first_of('=');
    if(end_attribute == string::npos) {
      end_attribute = sub_element.find_first_of('=');
      if(end_attribute == string::npos) {
        //missing attribute
        xmlcnf_ptr->found_error(filename_, *line_ptr, attribute_is_not_complete);
#ifdef XML_DEBUG
        dump_error(__FILE__, __LINE__, "xmlcnf_parser::parse_xml_attribute::attribute_is_not_complete");
#endif
        return attribute_is_not_complete;
      }
    }
    attribute = sub_element.substr(pos_attribute, end_attribute);
    attribute = trim(attribute);
    if(!is_valid_name(attribute)) {
      xmlcnf_ptr->found_error(filename_, *line_ptr, invalid_attribute_name);
#ifdef XML_DEBUG
      dump_error(__FILE__, __LINE__, "xmlcnf_parser::parse_xml_attribute::invalid_attribute_name");
#endif
      return invalid_attribute_name;
    }

    // read value
    sub_element = sub_element.substr(end_attribute+1);
    sub_element = trim(sub_element);

    char quote = sub_element[0];
    if(quote != '\'' && quote != '"') {
      xmlcnf_ptr->found_error(filename_, *line_ptr, missing_quote);
#ifdef XML_DEBUG
      dump_error(__FILE__, __LINE__, "xmlcnf_parser::parse_xml_attribute::missing_qoute");
#endif
      return missing_quote;
    }
    // pair of qoute
    size_t end_value = sub_element.find_first_of(quote, 1);

    if(end_value == string::npos) {
      xmlcnf_ptr->found_error(filename_, *line_ptr, missing_quote);
#ifdef XML_DEBUG
      dump_error(__FILE__, __LINE__, "xmlcnf_parser::parse_xml_attribute::missing_qoute");
#endif
      return missing_quote;
    }

    string value = sub_element.substr(0, end_value+1);
    value = trim(value);

    // got attribute and value
    xmlcnf_ptr->found_attribute(start_element.name(), attribute, value.substr(1,value.length()-2));

    // next attribute
    sub_element = sub_element.substr(end_value+1);
    sub_element = trim(sub_element);

    size_t pos_equal = sub_element.find_first_of('=');
    if(pos_equal == string::npos) {
      // no more attributes found
      break;
    }
  }

  return error_none;
}

bool xmlcnf_parser::is_valid_name(const string& name) {
  //no whitespaces
  size_t pos_whitespaces = name.find_first_of(" \t\r\n\v\f");
  if(pos_whitespaces != string::npos) {
    return false;
  }
  return true;
}

/*
    interface_object_is_null = -1,
    file_is_not_opened       = -2,
    file_is_bad              = -3,
    file_not_complete        = -4,
    // parsing error
    element_is_not_correct    = 1000,
    found_new_start_element   = 1001,
    element_is_reserved       = 1002,
    attribute_is_not_complete = 1003,
    missing_quote             = 1004,
    end_element_not_matched   = 1005,
    comment_not_complete      = 1006,
    invalid_xml_element       = 1007,
    invalid_attribute_name    = 1008
 */
string xmlcnf_parser::errtext(xmlint32 errcode) const {
  switch(errcode) {
    case interface_object_is_null:  return "interface object is a null object";
    case file_is_not_opened:        return "file is not opened";
    case file_is_bad:               return "file is bad";
    case file_not_complete:         return "file is not complete";
    case element_is_not_correct:    return "element is not correct";
    case found_new_start_element:   return "found new start element";
    case element_is_reserved:       return "element is reserved";
    case attribute_is_not_complete: return "attribute  is not complete";
    case missing_quote:             return "missing quote";
    case end_element_not_matched:   return "found end element not matched with start element";
    case comment_not_complete:      return "comment is not complete";
    case invalid_xml_element:       return "invalid XML element";
    case invalid_attribute_name:    return "invalid attribute name";
  }
  return "";
}

//////////////////////////////////////////////////////////////////////////////////////////////////////////
// xmlcnf
void xmlcnf::start_doc() {
  map_keys_.clear();
  vector_keys_.clear();
}

void xmlcnf::end_doc() {
}

int xmlcnf::parse() { 
  return xmlcnf_parser::parse((xmlcnf_interface*)this); 
}

void xmlcnf::found_error(const std::string& filename, xmluint32 line_no, xmlint32 errcode) {
#ifdef XML_DEBUG
cerr << "xmlcnf::found_error: filename: [" << filename << "]"
     << "\tline_no: [" << line_no << "]"
     << "\terrcode: [" << errcode << "]"
     << "\terrtext: [" << errtext(errcode) << "]"
     << endl;
#endif
}

void xmlcnf::found_xml_element(const string& element) {
}

void xmlcnf::found_start_element(const string& element) {
#ifdef XML_DEBUG
cerr << "xmlcnf::found_start_element: key: [" << element << "]" << endl;
#endif
  vector_keys_.push_back(element);
}

void xmlcnf::found_end_element(const string& element) {
#ifdef XML_DEBUG
cerr << "xmlcnf::found_end_element: key: [" << element << "]" << endl;
#endif
  vector_keys_.pop_back();
}

void xmlcnf::found_comment(const string& comment) {
#ifdef XML_DEBUG
cerr << "xmlcnf::found_comment: comment: [" << comment << "]" << endl;
#endif
}

void xmlcnf::found_text(const string& element, const string& text) {
  string key;
  vector<string>::iterator it;
  for(it = vector_keys_.begin(); it != vector_keys_.end(); it++) {
    key += *it;
    key += ".";
  }
  // remove '.' at the last
  key = key.substr(0, key.length()-1);
  map_keys_[key] = text;
}

void xmlcnf::found_attribute(const string& element, const string& attribute, const string& value) {
  string key;
  vector<string>::iterator it;
  for(it = vector_keys_.begin(); it != vector_keys_.end(); it++) {
    key += *it;
    key += ".";
  }
  key += attribute;
  map_keys_[key] = value;
}

#ifdef XML_DEBUG
void xmlcnf::dump_keys() {
  map<string, string>::iterator it;
  for(it = map_keys_.begin(); it != map_keys_.end(); it++) {
    cout << "[" << it->first << "] = [" << it->second << "]" << endl;
  }
}
#endif

string xmlcnf::get_string(const string& key, const string& def_value) {
  map<string, string>::iterator it;
  it = map_keys_.find(key);
  if(it != map_keys_.end()) return it->second;
  return def_value;
}

xmlint64 xmlcnf::get_int(const string& key, xmlint64 def_value) {
  map<string, string>::iterator it;
  it = map_keys_.find(key);
  if(it != map_keys_.end()) {
    char *end = 0;
    return strtol(it->second.c_str(), &end, 10);
  }
  return def_value;
}

xmluint64 xmlcnf::get_uint(const string& key, xmlint64 def_value) {
  map<string, string>::iterator it;
  it = map_keys_.find(key);
  if(it != map_keys_.end()) {
    char *end = 0;
    return strtoul(it->second.c_str(), &end, 10);
  }
  return def_value;
}

xmlfloat64 xmlcnf::get_float(const string& key, xmlfloat64 def_value) {
  map<string, string>::iterator it;
  it = map_keys_.find(key);
  if(it != map_keys_.end()) {
    char *end = 0;
    return strtod(it->second.c_str(), &end);
  }
  return def_value;
}

xmlbool xmlcnf::get_bool(const string& key, const string& true_value) {
  map<string, string>::iterator it;
  it = map_keys_.find(key);
  if(it != map_keys_.end() && it->second == true_value) return true;
  return false;
}

////////////////////////
// xmlstring

string trim_left(const string& str) {
  string temp(str);
  return temp.erase(0, temp.find_first_not_of(" \t\r\n\f\v"));
}

string trim_right(const string& str) {
  string temp(str);
  return temp.erase(1+temp.find_last_not_of(" \t\r\n\f\v"));
}

string trim(const string& str) {
  string temp(str);
  temp = trim_left(temp);
  return trim_right(temp);
}

//////////////////////////////////////////////////////////////////////////////////////////////
// test.cpp
// Author: Seree Rakwong
// Date: 25-OCT-10
// Purpose: Read a simple XML file
#include "xmlcnf.h"
#include <iostream>

using namespace std;

class bookcnf : public xmlcnf {
public:
  bool use_cache() const { return use_cache_; }
  string log_name() const { return log_name_; }
  string log_level() const { return log_level_; }
  xmluint64 log_size() const { return log_size_; }

protected:
  void end_doc() {
    use_cache_ = get_bool("Interday.Cache.use", "YES");
    log_name_  = get_string("Interday.Cache.Log.Name");
    log_level_ = get_string("Interday.Cache.Log.Level");

    string log_size = get_string("Interday.Cache.Log.Size");
    size_t len = log_size.length();
    if(len > 1) {
      char unit = log_size[len-1];
      int  multiply = 1;
      switch(unit) {
        case 'K': multiply = 1024;       break;
        case 'M': multiply = 1048576;    break;
        case 'G': multiply = 1073741824; break;
        default:  multiply = 1;          break;
      }

      char *end = 0;
      if(multiply == 1) {
        log_size_ = strtoul(log_size.c_str(), &end, 10);
      }
      else {
        log_size_ = strtoul(log_size.substr(0, len-1).c_str(), &end, 10)*multiply;
      }
    }
    else {
      log_size_ = get_uint("Interday.Cache.Log.Size");
    }
  }

private:
  bool   use_cache_;
  string log_name_;
  xmluint64    log_size_;
  string log_level_;
};

int main(int argc, char *argv[]) {
  string filename;
  if(argc < 2) {
    filename = "books.xml";
  }
  else filename = argv[1];
  bookcnf cnf;
  cnf.open(filename);
  cnf.parse();
#ifdef XML_DEBUG
  cnf.dump_keys();
#endif

  cout << "use cache: [" << (cnf.use_cache() ? "YES" : "NO") << "]" << endl;
  cout << "log name: [" << cnf.log_name() << "]" << endl;
  cout << "log size: [" << cnf.log_size() << "]" << endl;
  cout << "log level: [" << cnf.log_level() << "]" << endl;

  return 0;
}




/////////////////////////////////////////////////////////////
// myconfig.xml
<?xml version="1.0" encoding="ISO-8859-1"?>
<Interday>
  <Cache use="YES">
    <Log>
      <Name>../log/interday.log</Name>
      <!-- minsize = 0, maxsize = 3.99G -->
      <!-- K = 2^10 -->
      <!-- M = 2^20 -->
      <!-- G = 2^30 -->
      <Size>3.99G</Size>
      <Level>System</Level>
    </Log>
  </Cache>
</Interday>