/************************************************************************
    MeOS - Orienteering Software
    Copyright (C) 2009-2017 Melin Software HB

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.

    Melin Software HB - software@melin.nu - www.melin.nu
    Eksoppsvägen 16, SE-75646 UPPSALA, Sweden

************************************************************************/

// xmlparser.cpp: implementation of the xmlparser class.
//
//////////////////////////////////////////////////////////////////////

#include "stdafx.h"
#include "xmlparser.h"
#include "meos_util.h"
#include "progress.h"
#include "meosexception.h"
#include "gdioutput.h"

//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////

extern gdioutput *gdi_main;

xmlparser::xmlparser() : utfConverter(gdi_main)
{
  progress = 0;
  lastIndex = 0;
  tagStackPointer=0;
  isUTF = false;
  cutMode = false;
  toString = false;
}

xmlparser::~xmlparser()
{
  delete progress;
  fin.close();
  foutFile.close();
}

inline bool isBlankSpace(char b) {
  return b == ' ' || b == '\t' || b == '\n' || b == '\r';
}

void xmlparser::setProgress(HWND hWnd)
{
  progress = new ProgressWindow(hWnd);
}

void xmlparser::access(int index) {
  if (progress && (index-lastIndex)>1000 ) {
    lastIndex = index;
    progress->setProgress(500 + int(500.0 * index/xmlinfo.size()));
  }
}


xmlobject::xmlobject()
{
  parser = 0;
}

xmlobject::~xmlobject()
{
  //MessageBox(NULL, name.c_str(), "Destroying: ", MB_OK);

//	if (objects) delete objects;
}


const string &xmlparser::encodeXML(const wstring &input) {
  return ::encodeXML(gdi_main->toUTF8(input));
}

const string &xmlparser::encodeXML(const string &input) {
  if (utfConverter)
    return ::encodeXML(utfConverter->toUTF8(gdi_main->widen(input))); //XXX WCS
  else
    return ::encodeXML(input);
}

void xmlparser::write(const char *tag, const wstring &Value)
{
  if (!cutMode || !Value.empty()) {
    fOut() << "<" << tag << ">"
           << encodeXML(Value)
           << "</" << tag << ">" << endl;
  }
  if (!fOut().good())
    throw meosException("Writing to XML file failed.");
}

void xmlparser::write(const char *tag, const string &Value)
{
  if (!cutMode || Value!="") {
    fOut() << "<" << tag << ">"
           << encodeXML(Value)
           << "</" << tag << ">" << endl;
  }
  if (!fOut().good())
    throw meosException("Writing to XML file failed.");
}

void xmlparser::write(const char *tag)
{
  fOut() << "<" << tag << "/>" << endl;
  if (!fOut().good())
    throw meosException("Writing to XML file failed.");
}

void xmlparser::write(const char *tag, const char *Property, const string &Value)
{
  if (!cutMode || Value!="") {
    fOut() << "<" << tag << " " << Property << "=\""
           << encodeXML(Value) << "\"/>" << endl;
  }
  if (!fOut().good())
    throw meosException("Writing to XML file failed.");
}

void xmlparser::write(const char *tag, const char *Property, const wstring &Value)
{
  if (!cutMode || !Value.empty()) {
    fOut() << "<" << tag << " " << Property << "=\""
           << encodeXML(Value) << "\"/>" << endl;
  }
  if (!fOut().good())
    throw meosException("Writing to XML file failed.");
}

void xmlparser::write(const char *tag, const char *prop, const wchar_t *value)
{
  write(tag, prop, wstring(value));
}

void xmlparser::writeBool(const char *tag, const char *prop, bool value)
{
  if (!cutMode || value)
    write(tag, prop, value ? L"true" : L"false");
}

/*
void xmlparser::write(const char *tag, const char *Property, const string &PropValue, const string &Value)
{
  if (!cutMode || Value != "" || PropValue != "") {
    fOut() << "<" << tag << " " << Property << "=\""
           << encodeXML(PropValue) << "\">" << encodeXML(Value)
           << "</" << tag << ">" << endl;
  }
  if (!fOut().good())
    throw meosException("Writing to XML file failed.");
}
*/

void xmlparser::write(const char *tag, const char *Property, const wstring &PropValue, const wstring &Value)
{
  if (!cutMode || Value != L"" || PropValue != L"") {
    fOut() << "<" << tag << " " << Property << "=\""
           << encodeXML(PropValue) << "\">" << encodeXML(Value)
           << "</" << tag << ">" << endl;
  }
  if (!fOut().good())
    throw meosException("Writing to XML file failed.");
}

/*
void xmlparser::write(const char *tag, const vector< pair<string, string> > &propValue, const string &value) {
  if (!cutMode || value != "" || !propValue.empty()) {
    fOut() << "<" << tag;
    for (size_t k = 0; k < propValue.size(); k++) {
      fOut() << " " << propValue[k].first << "=\"" << encodeXML(propValue[k].second) << "\"";
    }
    if (!value.empty()) {
      fOut() << ">" << encodeXML(value)
             << "</" << tag << ">" << endl;
    }
    else
      fOut() << "/>" << endl;
  }
  if (!fOut().good())
    throw meosException("Writing to XML file failed.");
}

void xmlparser::write(const char *tag, const vector< pair<string, string> > &propValue, const wstring &value) {
  if (!cutMode || value != L"" || !propValue.empty()) {
    fOut() << "<" << tag;
    for (size_t k = 0; k < propValue.size(); k++) {
      fOut() << " " << propValue[k].first << "=\"" << encodeXML(propValue[k].second) << "\"";
    }
    if (!value.empty()) {
      fOut() << ">" << encodeXML(value)
             << "</" << tag << ">" << endl;
    }
    else
      fOut() << "/>" << endl;
  }
  if (!fOut().good())
    throw meosException("Writing to XML file failed.");
}
*/
void xmlparser::write(const char *tag, const vector< pair<string, wstring> > &propValue, const wstring &value) {
  if (!cutMode || value != L"" || !propValue.empty()) {
    fOut() << "<" << tag;
    for (size_t k = 0; k < propValue.size(); k++) {
      fOut() << " " << propValue[k].first << "=\"" << encodeXML(propValue[k].second) << "\"";
    }
    if (!value.empty()) {
      fOut() << ">" << encodeXML(value)
             << "</" << tag << ">" << endl;
    }
    else
      fOut() << "/>" << endl;
  }
  if (!fOut().good())
    throw meosException("Writing to XML file failed.");
}

/*
void xmlparser::write(const char *tag, const char *prop,
                      bool propValue, const string &value) {
  write(tag, prop, propValue ? "true" : "false", value);
}*/

void xmlparser::writeBool(const char *tag, const char *prop,
                      bool propValue, const wstring &value) {
  write(tag, prop, propValue ? L"true" : L"false", value);
}
/*
void xmlparser::write(const char *tag, const char *prop,
                      const char *propValue, const string &value) {
  write(tag, prop, string(propValue), value);
}*/

void xmlparser::write(const char *tag, const char *prop,
                      const wchar_t *propValue, const wstring &value) {
  write(tag, prop, wstring(propValue), value);
}

void xmlparser::write(const char *tag, int Value)
{
  if (!cutMode || Value!=0) {
    fOut() << "<" << tag << ">"
           << Value
           << "</" << tag << ">" << endl;
  }
  if (!fOut().good())
    throw meosException("Writing to XML file failed.");
}

void xmlparser::writeBool(const char *tag, bool value)
{
  if (!cutMode || value) {
    fOut() << "<" << tag << ">"
           << (value ? "true" : "false")
           << "</" << tag << ">" << endl;
  }
  if (!fOut().good())
    throw meosException("Writing to XML file failed.");
}


void xmlparser::write64(const char *tag, __int64 Value)
{
  if (!cutMode || Value!=0) {
    fOut() << "<" << tag << ">"
           << Value
           << "</" << tag << ">" << endl;
  }
  if (!fOut().good())
    throw meosException("Writing to XML file failed.");
}

void xmlparser::startTag(const char *tag, const char *prop, const wstring &Value)
{
  if (tagStackPointer<32) {
    fOut() << "<" << tag << " " << prop << "=\"" << encodeXML(Value) << "\">" << endl;
    tagStack[tagStackPointer++]=tag;
    if (!fOut().good())
      throw meosException("Writing to XML file failed.");
  }
  else
    throw meosException("Tag depth too large.");
}


void xmlparser::startTag(const char *tag, const char *prop, const string &Value)
{
  if (tagStackPointer<32) {
    fOut() << "<" << tag << " " << prop << "=\"" << encodeXML(Value) << "\">" << endl;
    tagStack[tagStackPointer++]=tag;
    if (!fOut().good())
      throw meosException("Writing to XML file failed.");
  }
  else
    throw meosException("Tag depth too large.");
}
/*
void xmlparser::startTag(const char *tag, const vector<string> &propvalue)
{
  if (tagStackPointer<32) {
    fOut() << "<" << tag << " ";
    for (size_t k=0;k<propvalue.size(); k+=2) {
      fOut() << propvalue[k] << "=\"" << encodeXML(propvalue[k+1]) << "\" ";
    }
    fOut() << ">" << endl;
    tagStack[tagStackPointer++]=tag;
    if (!fOut().good())
      throw meosException("Writing to XML file failed.");
  }
  else
    throw meosException("Tag depth too large.");
}
*/
void xmlparser::startTag(const char *tag, const vector<wstring> &propvalue)
{
  if (tagStackPointer<32) {
    fOut() << "<" << tag << " ";
    for (size_t k=0;k<propvalue.size(); k+=2) {
      fOut() << encodeXML(propvalue[k]) << "=\"" << encodeXML(propvalue[k+1]) << "\" ";
    }
    fOut() << ">" << endl;
    tagStack[tagStackPointer++]=tag;
    if (!fOut().good())
      throw meosException("Writing to XML file failed.");
  }
  else
    throw meosException("Tag depth too large.");
}

void xmlparser::startTag(const char *tag)
{
  if (tagStackPointer<32) {
    fOut() << "<" << tag << ">" << endl;
    tagStack[tagStackPointer++]=tag;
    if (!fOut().good())
      throw meosException("Writing to XML file failed.");
  }
  else
    throw meosException("Tag depth too large.");
}

void xmlparser::endTag()
{
  if (tagStackPointer>0) {
    fOut() << "</" << tagStack[--tagStackPointer] << ">" << endl;
    if (!fOut().good())
      throw meosException("Writing to XML file failed.");
  }
  else throw std::exception("BAD XML CODE");
}

void xmlparser::openMemoryOutput(bool useCutMode) {
  cutMode = useCutMode;
  toString = true;
  foutString.clear();
  if (utfConverter)
    fOut() << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n\n";
  else
    fOut() << "<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?>\n\n\n";

  string out = foutString.str();
}

void xmlparser::getMemoryOutput(string &res) {
  res = foutString.str();
  foutString.clear();
}

void xmlparser::openOutput(const wchar_t *file, bool useCutMode)
{
  openOutputT(file, useCutMode, "");
}

void xmlparser::openOutputT(const wchar_t *file, bool useCutMode, const string &type)
{
  toString = false;
  cutMode = useCutMode;
  foutFile.open(file);
  checkWriteAccess(file);
  tagStackPointer=0;

  if (foutFile.bad())
    throw meosException(wstring(L"Writing to XML file failed: ") + wstring(file));

  if (utfConverter)
    fOut() << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n\n";
  else
    fOut() << "<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?>\n\n\n";

  if (!type.empty()) {
    startTag(type.c_str());
  }
  return;
}

int xmlparser::closeOut()
{
  while(tagStackPointer>0)
    endTag();

  int len = (int)foutFile.tellp();
  foutFile.close();

  return len;
}

xmldata::xmldata(const char *t, char *d) : tag(t), data(d)
{
  parent = -1;
  next = 0;
}

xmlattrib::xmlattrib(const char *t, char *d, const xmlparser *p) : tag(t), data(d), parser(p) {}

void xmlparser::read(const wstring &file, int maxobj)
{
  fin.open(file.c_str(), ios::binary);

  if (!fin.good())
    throw meosException(L"Failed to open 'X' for reading.#" + file);

  char bf[1024];
  bf[0]=0;

  do {
    fin.getline(bf, 1024, '>');
    lineNumber++;
  }
  while(fin.good() && bf[0]==0);

  char *ptr=ltrim(bf);
  isUTF = checkUTF(ptr);
  int p1 = (int)fin.tellg();

  fin.seekg(0, ios::end);
  int p2 = (int)fin.tellg();
  fin.seekg(p1, ios::beg);

  int asize = p2-p1;
  if (maxobj>0)
    asize = min(asize, maxobj*256);

  if (progress && asize>80000)
    progress->init();

  xbf.resize(asize+1);
  xmlinfo.clear();
  xbf.reserve(xbf.size() / 30); // Guess number of tags

  parseStack.clear();

  fin.read(&xbf[0], xbf.size());
  xbf[asize] = 0;

  fin.close();

  parse(maxobj);
}

void xmlparser::readMemory(const string &mem, int maxobj)
{
  if (mem.empty())
    return;

  char bf[1024];
  bf[0] = mem[0];
  int i = 1;
  int stop = min<int>(1020, mem.length());
  while (i < stop && mem[i-1] != '>'){
    bf[i] = mem[i];
    i++;
  }
  bf[i] = 0;

  char *ptr=ltrim(bf);
  isUTF = checkUTF(ptr);
  int p1 = i;
  int p2 = mem.size();

  int asize = p2-p1;
  if (maxobj>0)
    asize = min(asize, maxobj*256);

  if (progress && asize>80000)
    progress->init();

  xbf.resize(asize+1);
  xmlinfo.clear();
  xmlinfo.reserve(xbf.size() / 30); // Guess number of tags

  parseStack.clear();

  memcpy(&xbf[0], mem.c_str() + p1, xbf.size());
  xbf[asize] = 0;

  parse(maxobj);
}

bool xmlparser::checkUTF(const char *ptr) const {
  bool utf = false;

  if (ptr[0] == -17 && ptr[1]==-69 && ptr[2]==-65) {
    utf = true;
    ptr+=3; //Windows UTF attribute
  }

  if (memcmp(ptr, "<?xml", 5) == 0) {
    int i = 5;
    bool hasEncode = false;
    while (ptr[i]) {
      if ((ptr[i] == 'U' || ptr[i] == 'u') && _memicmp(ptr+i, "UTF-8", 5)==0) {
        utf = true;
        break;
      }
      if (ptr[i] == 'e' && memcmp(ptr+i, "encoding", 8)==0) {
        hasEncode = true;
      }
      i++;
    }
    if (!hasEncode)
      utf = true; // Assume UTF
  }
  else if (ptr[0] == '<' && ptr[1] == '?') {
    // Assume UTF XML if not specified
    utf = true;
  }
  else {
    throw std::exception("Invalid XML file.");
  }
  return utf;
}

bool xmlparser::parse(int maxobj) {
  lineNumber=0;
  int oldPrg = -50001;
  int pp = 0;
  const int size = xbf.size()-2;
  while (pp < size) {
    while (pp < size && xbf[pp] != '<') pp++;

    // Update progress while parsing
    if (progress && (pp - oldPrg)> 50000) {
      progress->setProgress(int(500.0*pp/size));
      oldPrg = pp;
    }

    // Found tag
    if (xbf[pp] == '<') {
      xbf[pp] = 0;
      char *start = &xbf[pp+1];
      while (pp < size && xbf[pp] != '>') pp++;

      if (xbf[pp] == '>') {
        xbf[pp] = 0;
      }
      if (*start=='!')
        continue; //Comment

      processTag(start, &xbf[pp-1]);
    }

    if (maxobj>0 && int(xmlinfo.size()) >= maxobj) {
      xbf[pp+1] = 0;
      return true;
    }
    pp++;
  }

  lastIndex = 0;
  return true;
}

void inplaceDecodeXML(char *in);

bool xmlparser::processTag(char *start, char *end) {
  static char err[64];
  bool onlyAttrib = *end == '/';
  bool endTag = *start == '/';

  char *tag = start;

  if (endTag)
    tag++;

  while (start<=end && /**start!=' ' && *start!='\t'*/ !isBlankSpace(*start))
    start++;

  *start = 0;

  if (!endTag && !onlyAttrib) {
    parseStack.push_back(xmlinfo.size());
    xmlinfo.push_back(xmldata(tag, end+2));
    int p = parseStack.size()-2;
    xmlinfo.back().parent = p>=0 ? parseStack[p] : -1;
  }
  else if (endTag) {
    if (!parseStack.empty()){
      xmldata &xd = xmlinfo[parseStack.back()];
      inplaceDecodeXML(xd.data);
      if (strcmp(tag, xd.tag)== 0) {
        parseStack.pop_back();
        xd.next = xmlinfo.size();
      }
      else {
        sprintf_s(err, "Unmatched tag '%s', expected '%s'.", tag, xd.tag);
        throw std::exception(err);
      }
    }
    else
    {
      sprintf_s(err, "Unmatched tag '%s'.", tag);
      throw std::exception(err);
    }
  }
  else if (onlyAttrib) {
    *end = 0;
    xmlinfo.push_back(xmldata(tag, 0));
    int p = parseStack.size() - 1;
    xmlinfo.back().parent = p>=0 ? parseStack[p] : -1;
    xmlinfo.back().next = xmlinfo.size();
  }
  return true;
}

char * xmlparser::ltrim(char *s)
{
  while(*s && isspace(BYTE(*s)))
    s++;

  return s;
}

const char * xmlparser::ltrim(const char *s)
{
  while(*s && isspace(BYTE(*s)))
    s++;

  return s;
}
/*
const char * xmlparser::getError()
{
  return errorMessage.c_str();
}*/

xmlobject xmlobject::getObject(const char *pname) const
{
  if (pname == 0)
    return *this;
  if (isnull())
    throw std::exception("Null pointer exception");

  vector<xmldata> &xmlinfo = parser->xmlinfo;

  parser->access(index);

  unsigned child = index+1;
  while (child < xmlinfo.size() && xmlinfo[child].parent == index) {
    if (strcmp(xmlinfo[child].tag, pname)==0)
      return xmlobject(parser, child);
    else
      child = xmlinfo[child].next;
  }
  return xmlobject(0);
}


void xmlobject::getObjects(xmlList &obj) const
{
  obj.clear();

  if (isnull())
    throw std::exception("Null pointer exception");

  vector<xmldata> &xmlinfo = parser->xmlinfo;
  unsigned child = index+1;
  parser->access(index);

  while (child < xmlinfo.size() && xmlinfo[child].parent == index) {
    obj.push_back(xmlobject(parser, child));
    child = xmlinfo[child].next;
  }
}

void xmlobject::getObjects(const char *tag, xmlList &obj) const
{
  obj.clear();

  if (isnull())
    throw std::exception("Null pointer exception");

  vector<xmldata> &xmlinfo = parser->xmlinfo;
  unsigned child = index+1;
  parser->access(index);

  while (child < xmlinfo.size() && xmlinfo[child].parent == index) {
    if (strcmp(tag, xmlinfo[child].tag) == 0)
      obj.push_back(xmlobject(parser, child));
    child = xmlinfo[child].next;
  }
}


const xmlobject xmlparser::getObject(const char *pname) const
{
  if (xmlinfo.size()>0){
    if (pname == 0 || strcmp(xmlinfo[0].tag, pname) == 0)
      return xmlobject(const_cast<xmlparser *>(this), 0);
    else return xmlobject(const_cast<xmlparser *>(this), 0).getObject(pname);
  }
  else return xmlobject(0);
}

xmlattrib xmlobject::getAttrib(const char *pname) const
{
  if (pname != 0) {
    char *start = const_cast<char *>(parser->xmlinfo[index].tag);
    const char *end = parser->xmlinfo[index].data;

    if (end)
      end-=2;
    else {
      if (size_t(index + 1) < parser->xmlinfo.size())
        end = parser->xmlinfo[index+1].tag;
      else
        end = &parser->xbf.back();
    }

    // Scan past tag.
    while (start<end && *start != 0)
      start++;
    start++;

    char *oldStart = start;
    while (start<end) {
      while(start<end && isBlankSpace(*start))
        start++;

      char *tag = start;

      while(start<end && *start!='=' && *start!=0)
        start++;

      if (start<end && (start[1]=='"' || start[1] == 0)) {
        *start = 0;
        ++start;
        char *value = ++start;

        while(start<end && (*start!='"' && *start != 0))
          start++;

        if (start<=end) {
          *start = 0;
          if (strcmp(pname, tag) == 0)
            return xmlattrib(tag, value, parser);
          start++;
        }
        else {//Error
        }
      }

      if (oldStart == start)
        break;
      else
        oldStart = start;
    }
  }
  return xmlattrib(0,0, parser);
}

static int unconverted = 0;




const wchar_t *xmlobject::getw() const
{
  const char *ptr = getRaw();
  if (ptr == 0)
    return 0;
  static wchar_t buff[buff_pre_alloc];
  int len = strlen(ptr);
  len = min(len+1, buff_pre_alloc-10);
  if (parser->isUTF) {
    int wlen = MultiByteToWideChar(CP_UTF8, 0, ptr, len, buff, buff_pre_alloc);
    buff[wlen-1] = 0;
  }
  else {
    int cp = 1252;
  /*XXX TODO: switch(getEncoding()) {
    case Russian:
      cp = 1251;
      break;
    case EastEurope:
      cp = 1250;
      break;
    case Hebrew:
      cp = 1255;
      break;
  }*/
    int wlen = MultiByteToWideChar(cp, MB_PRECOMPOSED, ptr, len, buff, buff_pre_alloc);
    buff[wlen-1] = 0;
  }
  return buff;
}


const char *xmlobject::get() const
{
  const char *ptr = getRaw();
  if (ptr == 0)
    return 0;
  static char buff[buff_pre_alloc];
  if (parser->isUTF) {
    int len = strlen(ptr);
    len = min(len+1, buff_pre_alloc-10);
    int wlen = MultiByteToWideChar(CP_UTF8, 0, ptr, len, parser->strbuffw, buff_pre_alloc);
    parser->strbuffw[wlen-1] = 0;
    for (int k = 0; k< wlen; k++) {
      buff[k] = parser->strbuffw[k] & 0xFF; 
    }
  }
  else {
    return ptr;
  }
  return buff;
}


void xmlparser::convertString(const char *in, char *out, int maxlen) const
{
  if (in==0)
    throw std::exception("Null pointer exception");

  if (!isUTF) {
    strncpy_s(out, maxlen, in, maxlen-1);
    out[maxlen-1] = 0;
    return;
  }

  wchar_t buff[buff_pre_alloc];
  int len = strlen(in);
  len = min(min(len+1, maxlen), buff_pre_alloc-10);

  int wlen = MultiByteToWideChar(CP_UTF8, 0, in, len, buff, buff_pre_alloc);
  buff[wlen-1] = 0;

  BOOL untranslated = false;
  WideCharToMultiByte(CP_ACP, 0, buff, wlen, out, buff_pre_alloc, "?", &untranslated);
  out[wlen-1] = 0;

  if (untranslated)
    unconverted++;
}

void xmlparser::convertString(const char *in, wchar_t *out, int maxlen) const
{
  if (in==0)
    throw std::exception("Null pointer exception");

  int len = strlen(in);
  len = min(min(len+1, maxlen), buff_pre_alloc-10);

  if (!isUTF) {
    int cp = 1252;
  /*XXX TODO: switch(getEncoding()) {
    case Russian:
      cp = 1251;
      break;
    case EastEurope:
      cp = 1250;
      break;
    case Hebrew:
      cp = 1255;
      break;
  }*/
    int wlen = MultiByteToWideChar(cp, MB_PRECOMPOSED, in, len, out, maxlen);
    out[wlen-1] = 0;
    return;
  }

  int wlen = MultiByteToWideChar(CP_UTF8, 0, in, len, out, maxlen);
  out[wlen-1] = 0;
}


bool xmlobject::getObjectBool(const char *pname) const
{
  string tmp;
  getObjectString(pname, tmp);

  return tmp=="true" ||
         atoi(tmp.c_str()) > 0 ||
         _strcmpi(trim(tmp).c_str(), "true") == 0;
}

string &xmlobject::getObjectString(const char *pname, string &out) const
{
  xmlobject x=getObject(pname);
  if (x) {
    const char *bf = x.getRaw();
    if (bf) {
      parser->convertString(x.getRaw(), parser->strbuff, buff_pre_alloc);
      out = parser->strbuff;
      return out;
    }
  }

  xmlattrib xa(getAttrib(pname));
  if (xa && xa.data) {
    parser->convertString(xa.get(), parser->strbuff, buff_pre_alloc);
    out = parser->strbuff;
  }
  else
    out = "";

  return out;
}

wstring &xmlobject::getObjectString(const char *pname, wstring &out) const
{
  xmlobject x=getObject(pname);
  if (x) {
    const wchar_t *bf = x.getw();
    if (bf) {
      out = bf;
      return out;
    }
  }

  xmlattrib xa(getAttrib(pname));
  if (xa && xa.data) {
    parser->convertString(xa.get(), parser->strbuffw, buff_pre_alloc);
    out = parser->strbuffw;
  }
  else
    out = L"";

  return out;
}


char *xmlobject::getObjectString(const char *pname, char *out, int maxlen) const
{
  xmlobject x=getObject(pname);
  if (x) {
    const char *bf = x.getRaw();
    if (bf) {
      parser->convertString(bf, out, maxlen);
      return out;
    }
    else
      out[0] = 0;
  }
  else {
    xmlattrib xa(getAttrib(pname));
    if (xa && xa.data) {
      parser->convertString(xa.data, out, maxlen);
      inplaceDecodeXML(out);
    } else
       out[0] = 0;
  }
  return out;
}

wchar_t *xmlobject::getObjectString(const char *pname, wchar_t *out, int maxlen) const
{
  xmlobject x=getObject(pname);
  if (x) {
    const char *bf = x.getRaw();
    if (bf) {
      parser->convertString(bf, out, maxlen);
      return out;
    }
    else 
      out[0] = 0;
  }
  else {
    xmlattrib xa(getAttrib(pname));
    if (xa && xa.data) {
      inplaceDecodeXML(xa.data); //WCS XXX  Only once!?
      parser->convertString(xa.data, out, maxlen);
    } else
       out[0] = 0;
  }
  return out;
}


const char *xmlattrib::get() const
{
  if (data)
    return decodeXML(data);
  else
    return 0;
}

const wchar_t *xmlattrib::wget() const
{
  if (data) {
    const char *dec = decodeXML(data);
    static wchar_t xbf[buff_pre_alloc];
    parser->convertString(dec, xbf, buff_pre_alloc);
    return xbf;
  }
  else
    return 0;
}
