mirror of
https://github.com/reactos/reactos.git
synced 2024-08-20 02:13:29 +00:00
2999 lines
61 KiB
C++
2999 lines
61 KiB
C++
|
|
//
|
|
// XML storage C++ classes version 1.3
|
|
//
|
|
// Copyright (c) 2004, 2005, 2006, 2007, 2008, 2009, 2010 Martin Fuchs <martin-fuchs@gmx.net>
|
|
//
|
|
|
|
/// \file xmlstorage.h
|
|
/// XMLStorage header file
|
|
|
|
|
|
/*
|
|
|
|
All rights reserved.
|
|
|
|
Redistribution and use in source and binary forms, with or without
|
|
modification, are permitted provided that the following conditions are met:
|
|
|
|
* Redistributions of source code must retain the above copyright
|
|
notice, this list of conditions and the following disclaimer.
|
|
* Redistributions in binary form must reproduce the above copyright
|
|
notice, this list of conditions and the following disclaimer in
|
|
the documentation and/or other materials provided with the
|
|
distribution.
|
|
|
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
|
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
POSSIBILITY OF SUCH DAMAGE.
|
|
|
|
*/
|
|
|
|
#ifndef _XMLSTORAGE_H
|
|
|
|
|
|
#ifdef UNICODE
|
|
#ifndef _UNICODE
|
|
#define _UNICODE
|
|
#endif
|
|
#else
|
|
#ifdef _UNICODE
|
|
#define UNICODE
|
|
#endif
|
|
#endif
|
|
|
|
#ifndef _WIN32
|
|
#ifdef UNICODE
|
|
#error no UNICODE build in Unix version available
|
|
#endif
|
|
#ifndef XS_STRING_UTF8
|
|
#define XS_STRING_UTF8
|
|
#endif
|
|
#endif
|
|
|
|
|
|
#if _MSC_VER>=1400 // VS2005 or higher
|
|
#ifndef _CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES
|
|
#define _CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES 1
|
|
#define _CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES_COUNT 1
|
|
#define _CRT_SECURE_CPP_OVERLOAD_SECURE_NAMES 1
|
|
#endif
|
|
#endif
|
|
|
|
|
|
#ifdef XS_USE_XERCES
|
|
|
|
#ifndef UNICODE
|
|
#ifndef XS_STRING_UTF8
|
|
#define XS_STRING_UTF8
|
|
#endif
|
|
#endif
|
|
|
|
#include <xercesc/parsers/SAXParser.hpp>
|
|
#include <xercesc/sax/HandlerBase.hpp>
|
|
|
|
using XERCES_CPP_NAMESPACE_QUALIFIER Locator;
|
|
using XERCES_CPP_NAMESPACE_QUALIFIER SAXParser;
|
|
using XERCES_CPP_NAMESPACE_QUALIFIER HandlerBase;
|
|
using XERCES_CPP_NAMESPACE_QUALIFIER InputSource;
|
|
using XERCES_CPP_NAMESPACE_QUALIFIER AttributeList;
|
|
using XERCES_CPP_NAMESPACE_QUALIFIER SAXParseException;
|
|
|
|
typedef XMLCh XML_Char;
|
|
|
|
#elif defined(XS_USE_EXPAT)
|
|
|
|
#include <expat/expat.h>
|
|
|
|
#endif
|
|
|
|
|
|
#ifdef _MSC_VER
|
|
#pragma warning(disable: 4786)
|
|
|
|
#ifndef XS_NO_COMMENT
|
|
|
|
#ifdef XS_USE_XERCES
|
|
#ifdef _DEBUG
|
|
#pragma comment(lib, "xerces-c_2D")
|
|
#else
|
|
#pragma comment(lib, "xerces-c_2")
|
|
#endif
|
|
#elif defined(XS_USE_EXPAT)
|
|
#ifdef XML_STATIC
|
|
#ifndef _DEBUG
|
|
#pragma comment(lib, "libexpatMT")
|
|
#endif
|
|
#else
|
|
#pragma comment(lib, "libexpat")
|
|
#endif
|
|
#endif
|
|
|
|
#ifndef _STRING_DEFINED // _STRING_DEFINED only allowed if using xmlstorage.cpp embedded in the project
|
|
#if defined(_DEBUG) && defined(_DLL) // DEBUG version only supported with MSVCRTD
|
|
#if _MSC_VER==1500
|
|
#pragma comment(lib, "xmlstorage-vc9d")
|
|
#elif _MSC_VER==1400
|
|
#pragma comment(lib, "xmlstorage-vc8d")
|
|
#else
|
|
#pragma comment(lib, "xmlstorage-vc6d")
|
|
#endif
|
|
#else
|
|
#ifdef _DLL
|
|
#if _MSC_VER==1500
|
|
#pragma comment(lib, "xmlstorage-vc9")
|
|
#elif _MSC_VER==1400
|
|
#pragma comment(lib, "xmlstorage-vc8")
|
|
#else
|
|
#pragma comment(lib, "xmlstorage-vc6")
|
|
#endif
|
|
#elif defined(_MT)
|
|
#if _MSC_VER==1500
|
|
#pragma comment(lib, "xmlstorage-vc9t")
|
|
#elif _MSC_VER==1400
|
|
#pragma comment(lib, "xmlstorage-vc8t")
|
|
#else
|
|
#pragma comment(lib, "xmlstorage-vc6t")
|
|
#endif
|
|
#else
|
|
// -ML is no more supported since VS2005.
|
|
#pragma comment(lib, "xmlstorage-vc6l")
|
|
#endif
|
|
#endif
|
|
#endif // _STRING_DEFINED
|
|
|
|
#endif // XS_NO_COMMENT
|
|
|
|
#endif // _MSC_VER
|
|
|
|
|
|
#ifdef _WIN32
|
|
|
|
//#include <windows.h> // for LPCTSTR
|
|
//#include <tchar.h>
|
|
#include <malloc.h>
|
|
|
|
#ifndef _MSC_VER
|
|
#include <stdio.h> // vsnprintf(), snprintf()
|
|
#endif
|
|
|
|
#else // _WIN32
|
|
|
|
#include <wchar.h>
|
|
#include <stdlib.h>
|
|
#include <string.h> // strcasecmp()
|
|
#include <stdarg.h>
|
|
|
|
typedef char CHAR;
|
|
#ifdef _WCHAR_T_DEFINED
|
|
#define __wchar_t wchar_t
|
|
#endif
|
|
|
|
typedef __wchar_t WCHAR;
|
|
typedef unsigned char UCHAR;
|
|
typedef char* LPSTR;
|
|
typedef const char* LPCSTR;
|
|
typedef WCHAR* LPWSTR;
|
|
typedef const WCHAR* LPCWSTR;
|
|
|
|
#ifndef UNICODE
|
|
#define TEXT(x) x
|
|
typedef char TCHAR;
|
|
typedef unsigned char _TUCHAR;
|
|
typedef CHAR* PTSTR;
|
|
typedef CHAR* LPTSTR;
|
|
typedef const CHAR* LPCTSTR;
|
|
|
|
#define _ttoi atoi
|
|
#define _tfopen fopen
|
|
#define _tcstod strtod
|
|
#define _tcslen strlen
|
|
#define _tcsstr strstr
|
|
#define _snprintf snprintf
|
|
#define _sntprintf snprintf
|
|
#define _vsnprintf vsnprintf
|
|
#define _vsntprintf vsnprintf
|
|
#define _stricmp strcasecmp
|
|
#define _tcsicmp strcasecmp
|
|
#define strnicmp strncasecmp
|
|
#define _tcsnicmp strncasecmp
|
|
#endif // UNICODE
|
|
|
|
#endif // _WIN32
|
|
|
|
#ifdef __BORLANDC__
|
|
#define _stricmp stricmp
|
|
#endif
|
|
|
|
|
|
#include <fstream>
|
|
#include <sstream>
|
|
#include <string>
|
|
#include <stack>
|
|
#include <list>
|
|
#include <map>
|
|
|
|
|
|
#ifndef BUFFER_LEN
|
|
#define BUFFER_LEN 2048
|
|
#endif
|
|
|
|
|
|
namespace XMLStorage {
|
|
|
|
|
|
#ifndef XS_String
|
|
|
|
#ifdef XS_STRING_UTF8
|
|
#define XS_CHAR char
|
|
#define XS_TEXT(x) x
|
|
#define LPXSSTR LPSTR
|
|
#define LPCXSSTR LPCSTR
|
|
#define XS_cmp strcmp
|
|
#define XS_icmp _stricmp
|
|
#define XS_ncmp strncmp
|
|
#define XS_nicmp strnicmp
|
|
#define XS_toi atoi
|
|
#define XS_tod strtod
|
|
#define XS_len strlen
|
|
#define XS_snprintf _snprintf
|
|
#define XS_vsnprintf _vsnprintf
|
|
#define XS_strstr strstr
|
|
#else
|
|
#define XS_CHAR TCHAR
|
|
#define XS_TEXT(x) TEXT(x)
|
|
#define LPXSSTR LPTSTR
|
|
#define LPCXSSTR LPCTSTR
|
|
#define XS_cmp _tcscmp
|
|
#define XS_icmp _tcsicmp
|
|
#define XS_ncmp _tcsncmp
|
|
#define XS_nicmp _tcsnicmp
|
|
#define XS_toi _ttoi
|
|
#define XS_tod _tcstod
|
|
#define XS_len _tcslen
|
|
#define XS_snprintf _sntprintf
|
|
#define XS_vsnprintf _vsntprintf
|
|
#define XS_strstr _tcsstr
|
|
#endif
|
|
|
|
#ifndef COUNTOF
|
|
#if _MSC_VER>=1400
|
|
#define COUNTOF _countof
|
|
#else
|
|
#define COUNTOF(b) (sizeof(b)/sizeof(b[0]))
|
|
#endif
|
|
#endif
|
|
|
|
|
|
extern const char* get_xmlsym_end_utf8(const char* p);
|
|
|
|
|
|
#if defined(_STRING_DEFINED) && !defined(XS_STRING_UTF8)
|
|
|
|
#define XS_String String
|
|
|
|
#else // _STRING_DEFINED, !XS_STRING_UTF8
|
|
|
|
/// string class for TCHAR strings
|
|
|
|
struct XS_String
|
|
#if defined(UNICODE) && !defined(XS_STRING_UTF8)
|
|
: public std::wstring
|
|
#else
|
|
: public std::string
|
|
#endif
|
|
{
|
|
#if defined(UNICODE) && !defined(XS_STRING_UTF8)
|
|
typedef std::wstring super;
|
|
#else
|
|
typedef std::string super;
|
|
#endif
|
|
|
|
XS_String() {}
|
|
|
|
XS_String(LPCXSSTR s) {if (s) super::assign(s);}
|
|
XS_String(LPCXSSTR s, size_t l) : super(s, l) {}
|
|
|
|
XS_String(const super& other) : super(other) {}
|
|
XS_String(const XS_String& other) : super(other) {}
|
|
|
|
#ifdef _WIN32
|
|
#if defined(UNICODE) && !defined(XS_STRING_UTF8)
|
|
XS_String(LPCSTR s) {assign(s);}
|
|
XS_String(LPCSTR s, size_t l) {assign(s, l);}
|
|
XS_String(const std::string& s) {assign(s.c_str());}
|
|
XS_String& operator=(LPCSTR s) {assign(s); return *this;}
|
|
void assign(LPCSTR s) {if (s) {size_t bl=strlen(s); LPWSTR b=(LPWSTR)alloca(sizeof(WCHAR)*bl); super::assign(b, MultiByteToWideChar(CP_ACP, 0, s, bl, b, bl));} else erase();}
|
|
void assign(LPCSTR s, size_t l) {if (s) {size_t bl=l; LPWSTR b=(LPWSTR)alloca(sizeof(WCHAR)*bl); super::assign(b, MultiByteToWideChar(CP_ACP, 0, s, l, b, bl));} else erase();}
|
|
#else
|
|
XS_String(LPCWSTR s) {assign(s);}
|
|
XS_String(LPCWSTR s, size_t l) {assign(s, l);}
|
|
XS_String(const std::wstring& ws) {assign(ws.c_str());}
|
|
XS_String& operator=(LPCWSTR s) {assign(s); return *this;}
|
|
#ifdef XS_STRING_UTF8
|
|
void assign(LPCWSTR s) {if (s) {size_t bl=wcslen(s); LPSTR b=(LPSTR)alloca(bl); super::assign(b, WideCharToMultiByte(CP_UTF8, 0, s, (int)bl, b, (int)bl, 0, 0));} else erase();}
|
|
void assign(LPCWSTR s, size_t l) {size_t bl=l; if (s) {LPSTR b=(LPSTR)alloca(bl); super::assign(b, WideCharToMultiByte(CP_UTF8, 0, s, (int)l, b, (int)bl, 0, 0));} else erase();}
|
|
#else // if !UNICODE && !XS_STRING_UTF8
|
|
void assign(LPCWSTR s) {if (s) {size_t bl=wcslen(s); LPSTR b=(LPSTR)alloca(bl); super::assign(b, WideCharToMultiByte(CP_ACP, 0, s, (int)bl, b, (int)bl, 0, 0));} else erase();}
|
|
void assign(LPCWSTR s, size_t l) {size_t bl=l; if (s) {LPSTR b=(LPSTR)alloca(bl); super::assign(b, WideCharToMultiByte(CP_ACP, 0, s, (int)l, b, (int)bl, 0, 0));} else erase();}
|
|
#endif
|
|
#endif
|
|
#endif // _WIN32
|
|
|
|
#ifdef __ISSD_H
|
|
// XS_String(const _ISSD RString& s) {assign(s.c_str());}
|
|
// void assign(const _ISSD RString& s) {assign(s.c_str());}
|
|
XS_String& operator=(const _ISSD RString& s) {assign(s); return *this;}
|
|
#endif
|
|
|
|
#ifdef XS_STRING_UTF8
|
|
void assign(const XS_String& s) {assign(s.c_str());}
|
|
#endif
|
|
|
|
XS_String& operator=(LPCXSSTR s) {if (s) super::assign(s); else erase(); return *this;}
|
|
XS_String& operator=(const super& s) {super::assign(s); return *this;}
|
|
void assign(LPCXSSTR s) {super::assign(s);}
|
|
void assign(LPCXSSTR s, size_t l) {super::assign(s, l);}
|
|
|
|
operator LPCXSSTR() const {return c_str();}
|
|
|
|
#ifdef _WIN32
|
|
#ifdef XS_STRING_UTF8
|
|
operator std::wstring() const {size_t bl=length(); LPWSTR b=(LPWSTR)alloca(sizeof(WCHAR)*bl); return std::wstring(b, MultiByteToWideChar(CP_UTF8, 0, c_str(), bl, b, bl));}
|
|
#elif defined(UNICODE)
|
|
operator std::string() const {size_t bl=length(); LPSTR b=(LPSTR)alloca(bl); return std::string(b, WideCharToMultiByte(CP_ACP, 0, c_str(), bl, b, bl, 0, 0));}
|
|
#else
|
|
operator std::wstring() const {size_t bl=length(); LPWSTR b=(LPWSTR)alloca(sizeof(WCHAR)*bl); return std::wstring(b, MultiByteToWideChar(CP_ACP, 0, c_str(), (int)bl, b, (int)bl));}
|
|
#endif
|
|
#endif
|
|
|
|
XS_String& printf(LPCXSSTR fmt, ...)
|
|
{
|
|
va_list l;
|
|
XS_CHAR b[BUFFER_LEN];
|
|
|
|
va_start(l, fmt);
|
|
super::assign(b, XS_vsnprintf(b, COUNTOF(b), fmt, l));
|
|
va_end(l);
|
|
|
|
return *this;
|
|
}
|
|
|
|
XS_String& vprintf(LPCXSSTR fmt, va_list l)
|
|
{
|
|
XS_CHAR b[BUFFER_LEN];
|
|
|
|
super::assign(b, XS_vsnprintf(b, COUNTOF(b), fmt, l));
|
|
|
|
return *this;
|
|
}
|
|
|
|
XS_String& appendf(LPCXSSTR fmt, ...)
|
|
{
|
|
va_list l;
|
|
XS_CHAR b[BUFFER_LEN];
|
|
|
|
va_start(l, fmt);
|
|
super::append(b, XS_vsnprintf(b, COUNTOF(b), fmt, l));
|
|
va_end(l);
|
|
|
|
return *this;
|
|
}
|
|
|
|
XS_String& vappendf(LPCXSSTR fmt, va_list l)
|
|
{
|
|
XS_CHAR b[BUFFER_LEN];
|
|
|
|
super::append(b, XS_vsnprintf(b, COUNTOF(b), fmt, l));
|
|
|
|
return *this;
|
|
}
|
|
};
|
|
|
|
#endif // _STRING_DEFINED, !XS_STRING_UTF8
|
|
|
|
#endif // XS_String
|
|
|
|
|
|
#define XS_EMPTY_STR XS_TEXT("")
|
|
#define XS_TRUE_STR XS_TEXT("true")
|
|
#define XS_FALSE_STR XS_TEXT("false")
|
|
#define XS_INTFMT_STR XS_TEXT("%d")
|
|
#define XS_FLOATFMT_STR XS_TEXT("%f")
|
|
|
|
#define XS_KEY_STR XS_TEXT("key")
|
|
#define XS_VALUE_STR XS_TEXT("value")
|
|
#define XS_PROPERTY_STR XS_TEXT("property")
|
|
|
|
// work around GCC's wide string constant bug
|
|
#ifdef __GNUC__
|
|
extern const LPCXSSTR XS_EMPTY;
|
|
extern const LPCXSSTR XS_TRUE;
|
|
extern const LPCXSSTR XS_FALSE;
|
|
extern const LPCXSSTR XS_INTFMT;
|
|
extern const LPCXSSTR XS_FLOATFMT;
|
|
#else
|
|
#define XS_EMPTY XS_EMPTY_STR
|
|
#define XS_TRUE XS_TRUE_STR
|
|
#define XS_FALSE XS_FALSE_STR
|
|
#define XS_INTFMT XS_INTFMT_STR
|
|
#define XS_FLOATFMT XS_FLOATFMT_STR
|
|
#endif
|
|
|
|
extern const XS_String XS_KEY;
|
|
extern const XS_String XS_VALUE;
|
|
extern const XS_String XS_PROPERTY;
|
|
|
|
#define CDATA_START "<![CDATA["
|
|
#define CDATA_END "]]>"
|
|
|
|
|
|
#ifndef XS_STRING_UTF8
|
|
|
|
// from UTF-8 to XS internal string encoding
|
|
inline void assign_utf8(XS_String& s, const char* str, size_t lutf8)
|
|
{
|
|
#ifdef UNICODE
|
|
LPTSTR buffer = (LPTSTR)alloca(sizeof(TCHAR)*lutf8);
|
|
int l = MultiByteToWideChar(CP_UTF8, 0, str, (int)lutf8, buffer, (int)lutf8);
|
|
#else
|
|
LPWSTR wbuffer = (LPWSTR)alloca(sizeof(WCHAR)*lutf8);
|
|
int l = MultiByteToWideChar(CP_UTF8, 0, str, (int)lutf8, wbuffer, (int)lutf8);
|
|
|
|
int bl=2*l; LPSTR buffer = (LPSTR)alloca(bl);
|
|
l = WideCharToMultiByte(CP_ACP, 0, wbuffer, l, buffer, bl, 0, 0);
|
|
#endif
|
|
|
|
s.assign(buffer, l);
|
|
}
|
|
|
|
// from UTF-8 to XS internal string encoding
|
|
inline void assign_utf8(XS_String& s, const char* str)
|
|
{
|
|
assign_utf8(s, str, strlen(str));
|
|
}
|
|
|
|
// from XS internal string encoding to UTF-8
|
|
inline std::string get_utf8(LPCTSTR s, size_t l)
|
|
{
|
|
#ifdef UNICODE
|
|
size_t bl=2*l; LPSTR buffer = (LPSTR)alloca(bl);
|
|
l = WideCharToMultiByte(CP_UTF8, 0, s, (int)l, buffer, (int)bl, 0, 0);
|
|
#else
|
|
LPWSTR wbuffer = (LPWSTR)alloca(sizeof(WCHAR)*l);
|
|
l = MultiByteToWideChar(CP_ACP, 0, s, (int)l, wbuffer, (int)l);
|
|
|
|
size_t bl=2*l; LPSTR buffer = (LPSTR)alloca(bl);
|
|
l = WideCharToMultiByte(CP_UTF8, 0, wbuffer, (int)l, buffer, (int)bl, 0, 0);
|
|
#endif
|
|
|
|
return std::string(buffer, l);
|
|
}
|
|
|
|
#ifdef UNICODE
|
|
// from XS internal string encoding to UTF-8
|
|
inline std::string get_utf8(const char* s, size_t l)
|
|
{
|
|
LPWSTR wbuffer = (LPWSTR)alloca(sizeof(WCHAR)*l);
|
|
l = MultiByteToWideChar(CP_ACP, 0, s, (int)l, wbuffer, (int)l);
|
|
|
|
size_t bl=2*l; LPSTR buffer = (LPSTR)alloca(bl);
|
|
l = WideCharToMultiByte(CP_UTF8, 0, wbuffer, (int)l, buffer, (int)bl, 0, 0);
|
|
|
|
return std::string(buffer, l);
|
|
}
|
|
#endif
|
|
|
|
// from XS internal string encoding to UTF-8
|
|
inline std::string get_utf8(const XS_String& s)
|
|
{
|
|
return get_utf8(s.c_str(), s.length());
|
|
}
|
|
|
|
#endif // XS_STRING_UTF8
|
|
|
|
extern std::string EncodeXMLString(const XS_String& str, bool cdata=false);
|
|
extern XS_String DecodeXMLString(const std::string& str);
|
|
|
|
|
|
#ifdef __GNUC__
|
|
#include <ext/stdio_filebuf.h>
|
|
#define FILE_FILEBUF __gnu_cxx::stdio_filebuf<char>
|
|
#elif defined(_MSC_VER)
|
|
#define FILE_FILEBUF std::filebuf
|
|
#endif
|
|
|
|
#ifdef FILE_FILEBUF
|
|
|
|
/// base class for XMLStorage::tifstream and XMLStorage::tofstream
|
|
struct FileHolder
|
|
{
|
|
protected:
|
|
FileHolder()
|
|
{
|
|
}
|
|
|
|
~FileHolder()
|
|
{
|
|
if (_pfile)
|
|
fclose(_pfile);
|
|
delete _buf;
|
|
}
|
|
|
|
FILE_FILEBUF* init_buf(LPCTSTR path, std::ios_base::openmode mode)
|
|
{
|
|
PCTSTR modestr = mode == std::ios::in ? TEXT("rb") : TEXT("wb");
|
|
//@@ _MS_VER: temporarily needed for the ReactOS build environment
|
|
#if defined(__STDC_WANT_SECURE_LIB__) && defined(_MS_VER) // secure CRT functions using VS 2005
|
|
if (_tfopen_s(&_pfile, path, modestr) != 0)
|
|
_pfile = NULL;
|
|
#else
|
|
_pfile = _tfopen(path, modestr);
|
|
#endif
|
|
|
|
#ifdef __GNUC__
|
|
_buf = new FILE_FILEBUF(_pfile, mode);
|
|
#else
|
|
_buf = new FILE_FILEBUF;
|
|
if (_pfile)
|
|
_buf->open(_pfile, mode);
|
|
#endif
|
|
return _buf;
|
|
}
|
|
|
|
FILE* _pfile;
|
|
FILE_FILEBUF* _buf;
|
|
};
|
|
|
|
/// input file stream with ANSI/UNICODE file names
|
|
struct tifstream : public std::istream, FileHolder
|
|
{
|
|
typedef std::istream super;
|
|
|
|
tifstream(LPCTSTR path)
|
|
: super(init_buf(path, std::ios::in))
|
|
{
|
|
if (!_pfile)
|
|
setstate(badbit);
|
|
}
|
|
};
|
|
|
|
/// output file stream with ANSI/UNICODE file names
|
|
struct tofstream : public std::ostream, FileHolder
|
|
{
|
|
typedef std::ostream super;
|
|
|
|
tofstream(LPCTSTR path)
|
|
: super(init_buf(path, std::ios::out))
|
|
{
|
|
if (!_pfile)
|
|
setstate(badbit);
|
|
}
|
|
|
|
~tofstream()
|
|
{
|
|
flush();
|
|
}
|
|
};
|
|
|
|
#else // FILE_FILEBUF
|
|
|
|
#ifdef UNICODE
|
|
#error UNICODE not supported for this platform
|
|
#endif
|
|
|
|
struct tifstream : public std::ifstream
|
|
{
|
|
typedef std::ifstream super;
|
|
|
|
tifstream(const char* path)
|
|
: super(path, std::ios::in|std::ios::binary)
|
|
{
|
|
}
|
|
};
|
|
|
|
struct tofstream : public std::ofstream
|
|
{
|
|
typedef std::ofstream super;
|
|
|
|
tofstream(const char* path)
|
|
: super(path, std::ios::out|std::ios::binary)
|
|
{
|
|
}
|
|
};
|
|
|
|
#endif
|
|
|
|
|
|
// write XML files with 2 spaces indenting
|
|
#define XML_INDENT_SPACE " "
|
|
|
|
|
|
#if defined(XS_USE_XERCES) || defined(XS_USE_EXPAT)
|
|
|
|
#if defined(XML_UNICODE)/*Expat*/ || defined(XS_USE_XERCES)/*Xerces*/ // Are Expat/Xerces XML strings UTF-16 encoded?
|
|
typedef XS_String String_from_XML_Char;
|
|
|
|
#elif defined(XS_STRING_UTF8)
|
|
typedef XS_String String_from_XML_Char;
|
|
|
|
#else
|
|
|
|
/// converter from Expat/Xerces strings to XMLStorage internal strings
|
|
struct String_from_XML_Char : public XS_String
|
|
{
|
|
String_from_XML_Char(const XML_Char* str)
|
|
{
|
|
assign_utf8(*this, str);
|
|
}
|
|
};
|
|
|
|
#endif
|
|
|
|
#endif // defined(XS_USE_XERCES) || defined(XS_USE_EXPAT)
|
|
|
|
|
|
#if defined(UNICODE) && !defined(XS_STRING_UTF8)
|
|
|
|
// optimization for faster UNICODE/ASCII string comparison without temporary A/U conversion
|
|
inline bool operator==(const XS_String& s1, const char* s2)
|
|
{
|
|
LPCWSTR p = s1;
|
|
const unsigned char* q = (const unsigned char*)s2;
|
|
|
|
while(*p && *q)
|
|
if (*p++ != *q++)
|
|
return false;
|
|
|
|
return *p == *q;
|
|
};
|
|
|
|
#endif
|
|
|
|
|
|
/// XML Error with message and location
|
|
struct XMLError
|
|
{
|
|
XMLError()
|
|
: _line(0),
|
|
_column(0),
|
|
_error_code(0)
|
|
{
|
|
}
|
|
|
|
std::string str() const;
|
|
friend std::ostream& operator<<(std::ostream&, const XMLError& err);
|
|
|
|
XS_String _message;
|
|
XS_String _systemId;
|
|
int _line;
|
|
int _column;
|
|
int _error_code;
|
|
};
|
|
|
|
/// list of XMLError entries
|
|
struct XMLErrorList : public std::list<XMLError>
|
|
{
|
|
XS_String str() const;
|
|
};
|
|
|
|
|
|
#ifdef XMLNODE_LOCATION
|
|
/// location of XML Node including XML file name
|
|
struct XMLLocation
|
|
{
|
|
XMLLocation()
|
|
: _pdisplay_path(NULL),
|
|
_line(0),
|
|
_column(0)
|
|
{
|
|
}
|
|
|
|
XMLLocation(const char* display_path, int line, int column)
|
|
: _pdisplay_path(display_path),
|
|
_line(line),
|
|
_column(column)
|
|
{
|
|
}
|
|
|
|
std::string str() const;
|
|
|
|
protected:
|
|
const char* _pdisplay_path; // character pointer for fast reference
|
|
int _line;
|
|
int _column;
|
|
};
|
|
#endif
|
|
|
|
|
|
enum PRETTY_FLAGS {
|
|
PRETTY_PLAIN = 0,
|
|
PRETTY_LINEFEED = 1,
|
|
PRETTY_INDENT = 2
|
|
};
|
|
|
|
|
|
/// XML Stylesheet entry
|
|
struct StyleSheet
|
|
{
|
|
std::string _href; // CDATA #REQUIRED
|
|
std::string _type; // CDATA #REQUIRED
|
|
std::string _title; // CDATA #IMPLIED
|
|
std::string _media; // CDATA #IMPLIED
|
|
std::string _charset; // CDATA #IMPLIED
|
|
bool _alternate; // (yes|no) "no"
|
|
|
|
StyleSheet() : _alternate(false) {}
|
|
|
|
StyleSheet(const std::string& href, const std::string& type="text/xsl", bool alternate=false)
|
|
: _href(href),
|
|
_type(type),
|
|
_alternate(alternate)
|
|
{
|
|
}
|
|
|
|
bool empty() const {return _href.empty();}
|
|
void print(std::ostream& out) const;
|
|
};
|
|
|
|
/// list of StyleSheet entries
|
|
struct StyleSheetList : public std::list<StyleSheet>
|
|
{
|
|
void set(const StyleSheet& stylesheet)
|
|
{
|
|
clear();
|
|
push_back(stylesheet);
|
|
}
|
|
};
|
|
|
|
|
|
/// XML document type description
|
|
struct DocType
|
|
{
|
|
std::string _name;
|
|
|
|
// External Document Types are noted, but not parsed.
|
|
std::string _public;
|
|
std::string _system;
|
|
|
|
// Internal DTDs are not supported.
|
|
|
|
void parse(const char* str);
|
|
bool empty() const {return _name.empty();}
|
|
};
|
|
|
|
/// Management of XML file headers and formating
|
|
struct XMLFormat
|
|
{
|
|
XMLFormat(PRETTY_FLAGS pretty=PRETTY_INDENT, const std::string& xml_version="1.0", const std::string& encoding="utf-8", const DocType& doctype=DocType())
|
|
: _pretty(pretty),
|
|
_endl("\n"),
|
|
_version(xml_version),
|
|
_encoding(encoding),
|
|
_doctype(doctype),
|
|
_standalone(-1)
|
|
{
|
|
}
|
|
|
|
void print_header(std::ostream& out, bool lf=true) const;
|
|
|
|
PRETTY_FLAGS _pretty;
|
|
const char* _endl; // line ending string: "\n" or "\r\n"
|
|
|
|
std::string _version;
|
|
std::string _encoding;
|
|
|
|
DocType _doctype;
|
|
|
|
StyleSheetList _stylesheets;
|
|
|
|
// std::string _additional;
|
|
|
|
int _standalone;
|
|
};
|
|
|
|
|
|
enum WRITE_MODE {
|
|
FORMAT_PLAIN, /// write XML without any white space
|
|
FORMAT_SMART, /// preserve original white space and comments if present; pretty print otherwise
|
|
FORMAT_ORIGINAL, /// write XML stream preserving original white space and comments
|
|
FORMAT_PRETTY /// pretty print node to stream without preserving original white space
|
|
};
|
|
|
|
|
|
struct XMLNode;
|
|
|
|
struct XPathElement
|
|
{
|
|
XPathElement() : _child_idx(-1) {}
|
|
|
|
XPathElement(const XS_String& child_name, int child_idx=-1)
|
|
: _child_name(child_name), _child_idx(child_idx) {}
|
|
|
|
XPathElement(const XS_String& child_name, int child_idx, const XS_String& attr_name, const XS_String& attr_value)
|
|
: _child_name(child_name), _child_idx(child_idx),
|
|
_attr_name(attr_name), _attr_value(attr_value)
|
|
{
|
|
}
|
|
|
|
XS_String _child_name;
|
|
int _child_idx;
|
|
|
|
XS_String _attr_name;
|
|
XS_String _attr_value;
|
|
|
|
const char* parse(const char* path);
|
|
|
|
XMLNode* find(XMLNode* node) const;
|
|
const XMLNode* const_find(const XMLNode* node) const;
|
|
|
|
bool matches(const XMLNode& node, int& n) const;
|
|
};
|
|
|
|
struct XPath : std::list<XPathElement>
|
|
{
|
|
XPath() : _absolute(false) {}
|
|
XPath(const char* path) {init(path);}
|
|
XPath(const std::string path) {init(path.c_str());}
|
|
|
|
void init(const char* path);
|
|
|
|
bool _absolute;
|
|
};
|
|
|
|
|
|
/// in memory representation of an XML node
|
|
struct XMLNode : public XS_String
|
|
{
|
|
#if defined(UNICODE) && !defined(XS_STRING_UTF8)
|
|
/// map of XML node attributes
|
|
// optimized read access without temporary A/U conversion when using ASCII attribute names
|
|
struct AttributeMap : public std::map<XS_String, XS_String>
|
|
{
|
|
typedef std::map<XS_String, XS_String> super;
|
|
|
|
const_iterator find(const char* x) const
|
|
{
|
|
for(const_iterator it=begin(); it!=end(); ++it)
|
|
if (it->first == x)
|
|
return it;
|
|
|
|
return end();
|
|
}
|
|
|
|
const_iterator find(const key_type& x) const
|
|
{
|
|
return super::find(x);
|
|
}
|
|
|
|
iterator find(const key_type& x)
|
|
{
|
|
return super::find(x);
|
|
}
|
|
|
|
XS_String get(const char* x, LPCXSSTR def=XS_EMPTY_STR) const
|
|
{
|
|
const_iterator found = find(x);
|
|
|
|
if (found != end())
|
|
return found->second;
|
|
else
|
|
return def;
|
|
}
|
|
};
|
|
#else
|
|
/// map of XML node attributes
|
|
struct AttributeMap : public std::map<XS_String, XS_String>
|
|
{
|
|
XS_String get(const char* x, LPCXSSTR def=XS_EMPTY_STR) const
|
|
{
|
|
const_iterator found = find(x);
|
|
|
|
if (found != end())
|
|
return found->second;
|
|
else
|
|
return def;
|
|
}
|
|
};
|
|
#endif
|
|
|
|
/// internal children node list
|
|
struct Children : public std::list<XMLNode*>
|
|
{
|
|
typedef std::list<XMLNode*> super;
|
|
|
|
Children()
|
|
{
|
|
}
|
|
|
|
Children(Children& other)
|
|
{
|
|
for(Children::const_iterator it=other.begin(); it!=other.end(); ++it)
|
|
push_back(*it);
|
|
}
|
|
|
|
void assign(Children& other)
|
|
{
|
|
clear();
|
|
move(other);
|
|
}
|
|
|
|
void move(Children& other)
|
|
{
|
|
for(Children::const_iterator it=other.begin(); it!=other.end(); ++it)
|
|
push_back(*it);
|
|
|
|
other.reset();
|
|
}
|
|
|
|
Children& operator=(Children& other)
|
|
{
|
|
assign(other);
|
|
return *this;
|
|
}
|
|
|
|
void copy(const Children& other)
|
|
{
|
|
for(Children::const_iterator it=other.begin(); it!=other.end(); ++it)
|
|
push_back(new XMLNode(**it));
|
|
}
|
|
|
|
void clear()
|
|
{
|
|
while(!empty()) {
|
|
XMLNode* node = back();
|
|
pop_back();
|
|
|
|
node->clear();
|
|
delete node;
|
|
}
|
|
}
|
|
|
|
bool remove(XMLNode* node)
|
|
{
|
|
for(iterator it=begin(); it!=end(); ++it)
|
|
if (*it == node) {
|
|
erase(it);
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
private:
|
|
void reset()
|
|
{
|
|
super::clear();
|
|
}
|
|
};
|
|
|
|
// access to protected class members for XMLPos and XMLReader
|
|
friend struct XMLPos;
|
|
friend struct const_XMLPos;
|
|
friend struct XMLReaderBase;
|
|
friend struct XPathElement;
|
|
|
|
XMLNode(const XS_String& name)
|
|
: XS_String(name),
|
|
_cdata_content(false)
|
|
{
|
|
}
|
|
|
|
XMLNode(const XS_String& name, const std::string& leading)
|
|
: XS_String(name),
|
|
_leading(leading),
|
|
_cdata_content(false)
|
|
{
|
|
}
|
|
|
|
XMLNode(const XMLNode& other)
|
|
: XS_String(other),
|
|
_attributes(other._attributes),
|
|
_leading(other._leading),
|
|
_content(other._content),
|
|
_end_leading(other._end_leading),
|
|
_trailing(other._trailing),
|
|
#ifdef XMLNODE_LOCATION
|
|
_location(other._location),
|
|
#endif
|
|
_cdata_content(false)
|
|
{
|
|
for(Children::const_iterator it=other._children.begin(); it!=other._children.end(); ++it)
|
|
_children.push_back(new XMLNode(**it));
|
|
}
|
|
|
|
enum COPY_FLAGS {COPY_NOCHILDREN};
|
|
|
|
XMLNode(const XMLNode& other, COPY_FLAGS copy_no_children)
|
|
: XS_String(other),
|
|
_attributes(other._attributes),
|
|
_leading(other._leading),
|
|
_content(other._content),
|
|
_end_leading(other._end_leading),
|
|
_trailing(other._trailing),
|
|
#ifdef XMLNODE_LOCATION
|
|
_location(other._location),
|
|
#endif
|
|
_cdata_content(false)
|
|
{
|
|
// assert(copy_no_children==COPY_NOCHILDREN);
|
|
}
|
|
|
|
virtual ~XMLNode()
|
|
{
|
|
while(!_children.empty()) {
|
|
delete _children.back();
|
|
_children.pop_back();
|
|
}
|
|
}
|
|
|
|
void clear()
|
|
{
|
|
_leading.erase();
|
|
_content.erase();
|
|
_end_leading.erase();
|
|
_trailing.erase();
|
|
|
|
_attributes.clear();
|
|
_children.clear();
|
|
|
|
XS_String::erase();
|
|
}
|
|
|
|
XMLNode& operator=(const XMLNode& other)
|
|
{
|
|
_children.clear();
|
|
_children.copy(other._children);
|
|
|
|
_attributes = other._attributes;
|
|
|
|
_leading = other._leading;
|
|
_content = other._content;
|
|
_end_leading = other._end_leading;
|
|
_trailing = other._trailing;
|
|
|
|
return *this;
|
|
}
|
|
|
|
/// add a new child node
|
|
void add_child(XMLNode* child)
|
|
{
|
|
_children.push_back(child);
|
|
}
|
|
|
|
/// remove all children named 'name'
|
|
void remove_children(const XS_String& name)
|
|
{
|
|
Children::iterator it, next=_children.begin();
|
|
|
|
while((it=next++) != _children.end())
|
|
if (**it == name)
|
|
_children.erase(it);
|
|
}
|
|
|
|
/// write access to an attribute
|
|
void put(const XS_String& attr_name, const XS_String& value)
|
|
{
|
|
_attributes[attr_name] = value;
|
|
}
|
|
|
|
/// index operator write access to an attribute
|
|
XS_String& operator[](const XS_String& attr_name)
|
|
{
|
|
return _attributes[attr_name];
|
|
}
|
|
|
|
/// read only access to an attribute
|
|
template<typename T> XS_String get(const T& attr_name, LPCXSSTR def=XS_EMPTY_STR) const
|
|
{
|
|
AttributeMap::const_iterator found = _attributes.find(attr_name);
|
|
|
|
if (found != _attributes.end())
|
|
return found->second;
|
|
else
|
|
return def;
|
|
}
|
|
|
|
/// remove the attribute 'attr_name'
|
|
void erase(const XS_String& attr_name)
|
|
{
|
|
_attributes.erase(attr_name);
|
|
}
|
|
|
|
/// convenient value access in children node
|
|
XS_String subvalue(const XS_String& child_name, const XS_String& attr_name, int n=0) const
|
|
{
|
|
const XMLNode* node = XPathElement(child_name, n).const_find(this);
|
|
|
|
if (node)
|
|
return node->get(attr_name);
|
|
else
|
|
return XS_String();
|
|
}
|
|
|
|
/// convenient storage of distinct values in children node
|
|
XS_String& subvalue(const XS_String& child_name, const XS_String& attr_name, int n=0)
|
|
{
|
|
XMLNode* node = XPathElement(child_name, n).find(this);
|
|
|
|
if (!node) {
|
|
node = new XMLNode(child_name);
|
|
add_child(node);
|
|
}
|
|
|
|
return (*node)[attr_name];
|
|
}
|
|
|
|
#if defined(UNICODE) && !defined(XS_STRING_UTF8)
|
|
/// convenient value access in children node
|
|
XS_String subvalue(const char* child_name, const char* attr_name, int n=0) const
|
|
{
|
|
const XMLNode* node = XPathElement(child_name, n).const_find(this);
|
|
|
|
if (node)
|
|
return node->get(attr_name);
|
|
else
|
|
return XS_String();
|
|
}
|
|
|
|
/// convenient storage of distinct values in children node
|
|
XS_String& subvalue(const char* child_name, const XS_String& attr_name, int n=0)
|
|
{
|
|
XMLNode* node = XPathElement(child_name, n).find(this);
|
|
|
|
if (!node) {
|
|
node = new XMLNode(child_name);
|
|
add_child(node);
|
|
}
|
|
|
|
return (*node)[attr_name];
|
|
}
|
|
#endif
|
|
|
|
const Children& get_children() const
|
|
{
|
|
return _children;
|
|
}
|
|
|
|
Children& get_children()
|
|
{
|
|
return _children;
|
|
}
|
|
|
|
const AttributeMap& get_attributes() const
|
|
{
|
|
return _attributes;
|
|
}
|
|
|
|
AttributeMap& get_attributes()
|
|
{
|
|
return _attributes;
|
|
}
|
|
|
|
/// read element node content
|
|
XS_String get_content() const
|
|
{
|
|
return DecodeXMLString(_content);
|
|
}
|
|
|
|
/// read content of a subnode specified by an XPath expression
|
|
XS_String get_sub_content(const XPath& xpath) const
|
|
{
|
|
const XMLNode* node = find_relative(xpath);
|
|
|
|
if (node)
|
|
return node->get_content();
|
|
else
|
|
return XS_EMPTY_STR;
|
|
}
|
|
|
|
/// set element node content
|
|
void set_content(const XS_String& s, bool cdata=false)
|
|
{
|
|
_content.assign(EncodeXMLString(s.c_str(), cdata));
|
|
}
|
|
|
|
/// set content of a subnode specified by an XPath expression
|
|
bool set_sub_content(const XPath& xpath, const XS_String& s, bool cdata=false)
|
|
{
|
|
XMLNode* node = create_relative(xpath);
|
|
|
|
if (node) {
|
|
node->set_content(s, cdata);
|
|
return true;
|
|
} else
|
|
return false;
|
|
}
|
|
|
|
#ifdef XMLNODE_LOCATION
|
|
const XMLLocation& get_location() const {return _location;}
|
|
#endif
|
|
|
|
/// write node with children tree to output stream
|
|
bool write(std::ostream& out, const XMLFormat& format, WRITE_MODE mode=FORMAT_SMART, int indent=0) const
|
|
{
|
|
switch(mode) {
|
|
case FORMAT_PLAIN:
|
|
plain_write_worker(out);
|
|
break;
|
|
|
|
case FORMAT_PRETTY:
|
|
pretty_write_worker(out, format, indent);
|
|
break;
|
|
|
|
case FORMAT_ORIGINAL:
|
|
original_write_worker(out);
|
|
break;
|
|
|
|
default: // FORMAT_SMART
|
|
smart_write_worker(out, format, indent);
|
|
}
|
|
|
|
return out.good();
|
|
}
|
|
|
|
/// count the nodes matching the given relative XPath expression
|
|
int count(const XPath& xpath) const
|
|
{
|
|
return count(xpath.begin(), xpath.end());
|
|
}
|
|
|
|
/// count the nodes matching the given relative XPath expression
|
|
int count(XPath::const_iterator from, const XPath::const_iterator& to) const;
|
|
|
|
/// copy matching tree nodes using the given XPath filter expression
|
|
bool filter(const XPath& xpath, XMLNode& target) const;
|
|
|
|
/// XPath find function (const)
|
|
const XMLNode* find_relative(const XPath& xpath) const;
|
|
|
|
/// XPath find function
|
|
XMLNode* find_relative(const XPath& xpath);
|
|
|
|
XMLNode* get_first_child() const
|
|
{
|
|
if (!_children.empty())
|
|
return _children.front();
|
|
else
|
|
return NULL;
|
|
}
|
|
|
|
protected:
|
|
Children _children;
|
|
AttributeMap _attributes;
|
|
|
|
std::string _leading; // UTF-8 encoded
|
|
std::string _content; // UTF-8 and entity encoded, may contain CDATA sections; decode with DecodeXMLString()
|
|
std::string _end_leading; // UTF-8 encoded
|
|
std::string _trailing; // UTF-8 encoded
|
|
|
|
#ifdef XMLNODE_LOCATION
|
|
XMLLocation _location;
|
|
#endif
|
|
|
|
bool _cdata_content;
|
|
|
|
/// relative XPath create function
|
|
XMLNode* create_relative(const XPath& xpath);
|
|
|
|
/// create a new node tree using the given XPath filter expression
|
|
XMLNode* filter(XPath::const_iterator from, const XPath::const_iterator& to) const;
|
|
|
|
void original_write_worker(std::ostream& out) const;
|
|
void plain_write_worker(std::ostream& out) const;
|
|
void pretty_write_worker(std::ostream& out, const XMLFormat& format, int indent) const;
|
|
void smart_write_worker(std::ostream& out, const XMLFormat& format, int indent) const;
|
|
};
|
|
|
|
|
|
/// iterator access to children nodes with name filtering
|
|
struct XMLChildrenFilter
|
|
{
|
|
XMLChildrenFilter(XMLNode::Children& children, const XS_String& name)
|
|
: _begin(children.begin(), children.end(), name),
|
|
_end(children.end(), children.end(), name)
|
|
{
|
|
}
|
|
|
|
XMLChildrenFilter(XMLNode* node, const XS_String& name)
|
|
: _begin(node->get_children().begin(), node->get_children().end(), name),
|
|
_end(node->get_children().end(), node->get_children().end(), name)
|
|
{
|
|
}
|
|
|
|
/// internal iterator class
|
|
struct iterator
|
|
{
|
|
typedef XMLNode::Children::iterator BaseIterator;
|
|
typedef iterator myType;
|
|
|
|
iterator(BaseIterator begin, BaseIterator end, const XS_String& filter_name)
|
|
: _cur(begin),
|
|
_end(end),
|
|
_filter_name(filter_name)
|
|
{
|
|
search_next();
|
|
}
|
|
|
|
operator BaseIterator()
|
|
{
|
|
return _cur;
|
|
}
|
|
|
|
const XMLNode* operator*() const
|
|
{
|
|
return *_cur;
|
|
}
|
|
|
|
XMLNode* operator*()
|
|
{
|
|
return *_cur;
|
|
}
|
|
|
|
myType& operator++()
|
|
{
|
|
++_cur;
|
|
search_next();
|
|
|
|
return *this;
|
|
}
|
|
|
|
myType operator++(int)
|
|
{
|
|
myType ret = *this;
|
|
|
|
++_cur;
|
|
search_next();
|
|
|
|
return ret;
|
|
}
|
|
|
|
bool operator==(const myType& other) const
|
|
{
|
|
return _cur == other._cur;
|
|
}
|
|
|
|
bool operator!=(const myType& other) const
|
|
{
|
|
return _cur != other._cur;
|
|
}
|
|
|
|
protected:
|
|
BaseIterator _cur;
|
|
BaseIterator _end;
|
|
XS_String _filter_name;
|
|
|
|
void search_next()
|
|
{
|
|
while(_cur!=_end && **_cur!=_filter_name)
|
|
++_cur;
|
|
}
|
|
};
|
|
|
|
iterator begin()
|
|
{
|
|
return _begin;
|
|
}
|
|
|
|
iterator end()
|
|
{
|
|
return _end;
|
|
}
|
|
|
|
protected:
|
|
iterator _begin;
|
|
iterator _end;
|
|
};
|
|
|
|
|
|
/// read only iterator access to children nodes with name filtering
|
|
struct const_XMLChildrenFilter
|
|
{
|
|
const_XMLChildrenFilter(const XMLNode::Children& children, const XS_String& name)
|
|
: _begin(children.begin(), children.end(), name),
|
|
_end(children.end(), children.end(), name)
|
|
{
|
|
}
|
|
|
|
const_XMLChildrenFilter(const XMLNode* node, const XS_String& name)
|
|
: _begin(node->get_children().begin(), node->get_children().end(), name),
|
|
_end(node->get_children().end(), node->get_children().end(), name)
|
|
{
|
|
}
|
|
|
|
/// internal iterator class
|
|
struct const_iterator
|
|
{
|
|
typedef XMLNode::Children::const_iterator BaseIterator;
|
|
typedef const_iterator myType;
|
|
|
|
const_iterator(BaseIterator begin, BaseIterator end, const XS_String& filter_name)
|
|
: _cur(begin),
|
|
_end(end),
|
|
_filter_name(filter_name)
|
|
{
|
|
search_next();
|
|
}
|
|
|
|
operator BaseIterator()
|
|
{
|
|
return _cur;
|
|
}
|
|
|
|
const XMLNode* operator*() const
|
|
{
|
|
return *_cur;
|
|
}
|
|
|
|
myType& operator++()
|
|
{
|
|
++_cur;
|
|
search_next();
|
|
|
|
return *this;
|
|
}
|
|
|
|
myType operator++(int)
|
|
{
|
|
myType ret = *this;
|
|
|
|
++_cur;
|
|
search_next();
|
|
|
|
return ret;
|
|
}
|
|
|
|
bool operator==(const myType& other) const
|
|
{
|
|
return _cur == other._cur;
|
|
}
|
|
|
|
bool operator!=(const myType& other) const
|
|
{
|
|
return _cur != other._cur;
|
|
}
|
|
|
|
protected:
|
|
BaseIterator _cur;
|
|
BaseIterator _end;
|
|
XS_String _filter_name;
|
|
|
|
void search_next()
|
|
{
|
|
while(_cur!=_end && **_cur!=_filter_name)
|
|
++_cur;
|
|
}
|
|
};
|
|
|
|
const_iterator begin()
|
|
{
|
|
return _begin;
|
|
}
|
|
|
|
const_iterator end()
|
|
{
|
|
return _end;
|
|
}
|
|
|
|
protected:
|
|
const_iterator _begin;
|
|
const_iterator _end;
|
|
};
|
|
|
|
|
|
/// iterator for XML trees
|
|
struct XMLPos
|
|
{
|
|
XMLPos(XMLNode* root)
|
|
: _root(root),
|
|
_cur(root)
|
|
{
|
|
}
|
|
|
|
XMLPos(const XMLPos& other)
|
|
: _root(other._root),
|
|
_cur(other._cur)
|
|
{ // don't copy _stack
|
|
}
|
|
|
|
XMLPos(XMLNode* node, const XS_String& name)
|
|
: _root(node),
|
|
_cur(node)
|
|
{
|
|
smart_create(name);
|
|
}
|
|
|
|
XMLPos(XMLNode* node, const XS_String& name, const XS_String& attr_name, const XS_String& attr_value)
|
|
: _root(node),
|
|
_cur(node)
|
|
{
|
|
smart_create(name, attr_name, attr_value);
|
|
}
|
|
|
|
XMLPos(const XMLPos& other, const XS_String& name)
|
|
: _root(other._root),
|
|
_cur(other._cur)
|
|
{
|
|
smart_create(name);
|
|
}
|
|
|
|
XMLPos(const XMLPos& other, const XS_String& name, const XS_String& attr_name, const XS_String& attr_value)
|
|
: _root(other._root),
|
|
_cur(other._cur)
|
|
{
|
|
smart_create(name, attr_name, attr_value);
|
|
}
|
|
|
|
/// access to current node
|
|
XMLNode& cur()
|
|
{
|
|
return *_cur;
|
|
}
|
|
|
|
const XMLNode& cur() const
|
|
{
|
|
return *_cur;
|
|
}
|
|
|
|
/// automatic access to current node
|
|
operator const XMLNode*() const {return _cur;}
|
|
operator XMLNode*() {return _cur;}
|
|
|
|
const XMLNode* operator->() const {return _cur;}
|
|
XMLNode* operator->() {return _cur;}
|
|
|
|
const XMLNode& operator*() const {return *_cur;}
|
|
XMLNode& operator*() {return *_cur;}
|
|
|
|
/// attribute access
|
|
XS_String get(const XS_String& attr_name, LPCXSSTR def=XS_EMPTY_STR) const
|
|
{
|
|
return _cur->get(attr_name, def);
|
|
}
|
|
|
|
/// attribute setting
|
|
void put(const XS_String& attr_name, const XS_String& value)
|
|
{
|
|
_cur->put(attr_name, value);
|
|
}
|
|
|
|
/// index operator attribute access
|
|
template<typename T> XS_String get(const T& attr_name) const {return (*_cur)[attr_name];}
|
|
XS_String& operator[](const XS_String& attr_name) {return (*_cur)[attr_name];}
|
|
const XS_String& operator[](const XS_String& attr_name) const {return (*_cur)[attr_name];}
|
|
|
|
/// insert children when building tree
|
|
void add_down(XMLNode* child)
|
|
{
|
|
_cur->add_child(child);
|
|
go_to(child);
|
|
}
|
|
|
|
/// go back to previous position
|
|
bool back()
|
|
{
|
|
if (!_stack.empty()) {
|
|
_cur = _stack.top();
|
|
_stack.pop();
|
|
return true;
|
|
} else
|
|
return false;
|
|
}
|
|
|
|
/// go down to first child
|
|
bool go_down()
|
|
{
|
|
XMLNode* node = _cur->get_first_child();
|
|
|
|
if (node) {
|
|
go_to(node);
|
|
return true;
|
|
} else
|
|
return false;
|
|
}
|
|
|
|
/// search for child and go down
|
|
bool go_down(const XS_String& child_name, int n=0)
|
|
{
|
|
XMLNode* node = XPathElement(child_name, n).find(_cur);
|
|
|
|
if (node) {
|
|
go_to(node);
|
|
return true;
|
|
} else
|
|
return false;
|
|
}
|
|
|
|
/// iterate to the next matching child
|
|
bool iterate(const XS_String& child_name, size_t& cnt)
|
|
{
|
|
XMLNode* node = XPathElement(child_name, cnt).find(_cur);
|
|
|
|
if (node) {
|
|
go_to(node);
|
|
++cnt;
|
|
return true;
|
|
} else
|
|
return false;
|
|
}
|
|
|
|
/// move to the position defined by xpath in XML tree
|
|
bool go(const XPath& xpath);
|
|
|
|
/// create child nodes using XPath notation and move to the deepest child
|
|
bool create_relative(const XPath& xpath)
|
|
{
|
|
XMLNode* node = _cur->create_relative(xpath);
|
|
if (!node)
|
|
return false; // invalid path specified
|
|
|
|
go_to(node);
|
|
return true;
|
|
}
|
|
|
|
/// create node and move to it
|
|
void create(const XS_String& name)
|
|
{
|
|
add_down(new XMLNode(name));
|
|
}
|
|
|
|
/// create node with string content
|
|
void create_node_content(const XS_String& node_name, const XS_String& content)
|
|
{
|
|
XMLNode* pNode = new XMLNode(node_name);
|
|
pNode->set_content(content);
|
|
_cur->add_child(pNode);
|
|
}
|
|
|
|
/// create node if not already existing and move to it
|
|
void smart_create(const XS_String& child_name)
|
|
{
|
|
XMLNode* node = XPathElement(child_name).find(_cur);
|
|
|
|
if (node)
|
|
go_to(node);
|
|
else
|
|
add_down(new XMLNode(child_name));
|
|
}
|
|
|
|
/// search matching child node identified by key name and an attribute value
|
|
void smart_create(const XS_String& child_name, const XS_String& attr_name, const XS_String& attr_value)
|
|
{
|
|
XMLNode* node = XPathElement(child_name, 0, attr_name, attr_value).find(_cur);
|
|
|
|
if (node)
|
|
go_to(node);
|
|
else {
|
|
node = new XMLNode(child_name);
|
|
add_down(node);
|
|
(*node)[attr_name] = attr_value;
|
|
}
|
|
}
|
|
|
|
/// count the nodes matching the given relative XPath expression
|
|
int count(const XPath& xpath) const
|
|
{
|
|
return _cur->count(xpath);
|
|
}
|
|
|
|
/// create a new node tree using the given XPath filter expression
|
|
int filter(const XPath& xpath, XMLNode& target) const
|
|
{
|
|
return _cur->filter(xpath, target);
|
|
}
|
|
|
|
#if defined(UNICODE) && !defined(XS_STRING_UTF8)
|
|
/// search for child and go down
|
|
bool go_down(const char* child_name, int n=0)
|
|
{
|
|
XMLNode* node = XPathElement(child_name, n).find(_cur);
|
|
|
|
if (node) {
|
|
go_to(node);
|
|
return true;
|
|
} else
|
|
return false;
|
|
}
|
|
|
|
/// create node and move to it
|
|
void create(const char* child_name)
|
|
{
|
|
add_down(new XMLNode(child_name));
|
|
}
|
|
|
|
/// create node if not already existing and move to it
|
|
void smart_create(const char* child_name)
|
|
{
|
|
XMLNode* node = XPathElement(child_name).find(_cur);
|
|
|
|
if (node)
|
|
go_to(node);
|
|
else
|
|
add_down(new XMLNode(child_name));
|
|
}
|
|
|
|
/// search matching child node identified by key name and an attribute value
|
|
template<typename T, typename U>
|
|
void smart_create(const char* child_name, const T& attr_name, const U& attr_value)
|
|
{
|
|
XMLNode* node = XPathElement(child_name, 0, attr_name, attr_value).find(_cur);
|
|
|
|
if (node)
|
|
go_to(node);
|
|
else {
|
|
node = new XMLNode(child_name);
|
|
add_down(node);
|
|
(*node)[attr_name] = attr_value;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
/// delete current node and go back to previous position
|
|
bool delete_this()
|
|
{
|
|
if (!_stack.empty()) {
|
|
XMLNode* pLast = _stack.top();
|
|
|
|
if (pLast->_children.remove(_cur)) {
|
|
_cur = _stack.top();
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/// remove all children named 'name'
|
|
void remove_children(const XS_String& name)
|
|
{
|
|
_cur->remove_children(name);
|
|
}
|
|
|
|
/// remove the attribute 'attr_name' from the current node
|
|
void erase(const XS_String& attr_name)
|
|
{
|
|
_cur->erase(attr_name);
|
|
}
|
|
|
|
XS_String& str() {return *_cur;}
|
|
const XS_String& str() const {return *_cur;}
|
|
|
|
// property (key/value pair) setter functions
|
|
void set_property(const XS_String& key, int value, const XS_String& name=XS_PROPERTY);
|
|
void set_property(const XS_String& key, double value, const XS_String& name=XS_PROPERTY);
|
|
void set_property(const XS_String& key, const XS_String& value, const XS_String& name=XS_PROPERTY);
|
|
void set_property(const XS_String& key, const struct XMLBool& value, const XS_String& name=XS_PROPERTY);
|
|
|
|
void set_property(const XS_String& key, const char* value, const XS_String& name=XS_PROPERTY)
|
|
{set_property(key, XS_String(value), name);}
|
|
|
|
protected:
|
|
friend struct const_XMLPos; // access to _root
|
|
|
|
XMLNode* _root;
|
|
XMLNode* _cur;
|
|
std::stack<XMLNode*> _stack;
|
|
|
|
/// go to specified node
|
|
void go_to(XMLNode* child)
|
|
{
|
|
_stack.push(_cur);
|
|
_cur = child;
|
|
}
|
|
};
|
|
|
|
|
|
/// iterator for XML trees
|
|
struct const_XMLPos
|
|
{
|
|
const_XMLPos(const XMLNode* root)
|
|
: _root(root),
|
|
_cur(root)
|
|
{
|
|
}
|
|
|
|
const_XMLPos(const const_XMLPos& other)
|
|
: _root(other._root),
|
|
_cur(other._cur)
|
|
{ // don't copy _stack
|
|
}
|
|
|
|
const_XMLPos(const XMLPos& other)
|
|
: _root(other._root),
|
|
_cur(other._cur)
|
|
{ // don't copy _stack
|
|
}
|
|
|
|
/// access to current node
|
|
const XMLNode& cur() const
|
|
{
|
|
return *_cur;
|
|
}
|
|
|
|
/// automatic access to current node
|
|
operator const XMLNode*() const {return _cur;}
|
|
|
|
const XMLNode* operator->() const {return _cur;}
|
|
|
|
const XMLNode& operator*() const {return *_cur;}
|
|
|
|
/// attribute access
|
|
XS_String get(const XS_String& attr_name) const
|
|
{
|
|
return _cur->get(attr_name);
|
|
}
|
|
|
|
/// index operator attribute access
|
|
template<typename T> XS_String get(const T& attr_name) const {return _cur->get(attr_name);}
|
|
XS_String operator[](const XS_String& attr_name) const {return _cur->get(attr_name);}
|
|
|
|
/// go back to previous position
|
|
bool back()
|
|
{
|
|
if (!_stack.empty()) {
|
|
_cur = _stack.top();
|
|
_stack.pop();
|
|
return true;
|
|
} else
|
|
return false;
|
|
}
|
|
|
|
/// go down to first child
|
|
bool go_down()
|
|
{
|
|
const XMLNode* node = _cur->get_first_child();
|
|
|
|
if (node) {
|
|
go_to(node);
|
|
return true;
|
|
} else
|
|
return false;
|
|
}
|
|
|
|
/// search for child and go down
|
|
bool go_down(const XS_String& child_name, int n=0)
|
|
{
|
|
const XMLNode* node = XPathElement(child_name, n).const_find(_cur);
|
|
|
|
if (node) {
|
|
go_to(node);
|
|
return true;
|
|
} else
|
|
return false;
|
|
}
|
|
|
|
/// iterate to the next matching child
|
|
bool iterate(const XS_String& child_name, size_t& cnt)
|
|
{
|
|
const XMLNode* node = XPathElement(child_name, cnt).const_find(_cur);
|
|
|
|
if (node) {
|
|
go_to(node);
|
|
++cnt;
|
|
return true;
|
|
} else
|
|
return false;
|
|
}
|
|
|
|
/// move to the position defined by xpath in XML tree
|
|
bool go(const XPath& xpath);
|
|
|
|
#if defined(UNICODE) && !defined(XS_STRING_UTF8)
|
|
/// search for child and go down
|
|
bool go_down(const char* child_name, int n=0)
|
|
{
|
|
const XMLNode* node = XPathElement(child_name, n).const_find(_cur);
|
|
|
|
if (node) {
|
|
go_to(node);
|
|
return true;
|
|
} else
|
|
return false;
|
|
}
|
|
#endif
|
|
|
|
const XS_String& str() const {return *_cur;}
|
|
|
|
protected:
|
|
const XMLNode* _root;
|
|
const XMLNode* _cur;
|
|
std::stack<const XMLNode*> _stack;
|
|
|
|
/// go to specified node
|
|
void go_to(const XMLNode* child)
|
|
{
|
|
_stack.push(_cur);
|
|
_cur = child;
|
|
}
|
|
};
|
|
|
|
|
|
/// type converter for boolean data
|
|
struct XMLBool
|
|
{
|
|
XMLBool(bool value=false)
|
|
: _value(value)
|
|
{
|
|
}
|
|
|
|
XMLBool(LPCXSSTR value, bool def=false)
|
|
{
|
|
if (value && *value)//@@ also handle white space and return def instead of false
|
|
_value = !XS_icmp(value, XS_TRUE);
|
|
else
|
|
_value = def;
|
|
}
|
|
|
|
XMLBool(const XMLNode* node, const XS_String& attr_name, bool def=false)
|
|
{
|
|
const XS_String& value = node->get(attr_name);
|
|
|
|
if (!value.empty())
|
|
_value = !XS_icmp(value.c_str(), XS_TRUE);
|
|
else
|
|
_value = def;
|
|
}
|
|
|
|
operator bool() const
|
|
{
|
|
return _value;
|
|
}
|
|
|
|
bool operator!() const
|
|
{
|
|
return !_value;
|
|
}
|
|
|
|
operator LPCXSSTR() const
|
|
{
|
|
return _value? XS_TRUE: XS_FALSE;
|
|
}
|
|
|
|
protected:
|
|
bool _value;
|
|
|
|
private:
|
|
void operator=(const XMLBool&); // disallow assignment operations
|
|
};
|
|
|
|
/// type converter for boolean data with write access
|
|
struct XMLBoolRef
|
|
{
|
|
XMLBoolRef(XMLNode* node, const XS_String& attr_name, bool def=false)
|
|
: _ref((*node)[attr_name])
|
|
{
|
|
if (_ref.empty())
|
|
assign(def);
|
|
}
|
|
|
|
operator bool() const
|
|
{
|
|
return !XS_icmp(_ref.c_str(), XS_TRUE);
|
|
}
|
|
|
|
bool operator!() const
|
|
{
|
|
return XS_icmp(_ref.c_str(), XS_TRUE)? true: false;
|
|
}
|
|
|
|
XMLBoolRef& operator=(bool value)
|
|
{
|
|
assign(value);
|
|
|
|
return *this;
|
|
}
|
|
|
|
void assign(bool value)
|
|
{
|
|
_ref.assign(value? XS_TRUE: XS_FALSE);
|
|
}
|
|
|
|
void toggle()
|
|
{
|
|
assign(!operator bool());
|
|
}
|
|
|
|
protected:
|
|
XS_String& _ref;
|
|
};
|
|
|
|
|
|
/// type converter for integer data
|
|
struct XMLInt
|
|
{
|
|
XMLInt(int value)
|
|
: _value(value)
|
|
{
|
|
}
|
|
|
|
XMLInt(LPCXSSTR value, int def=0)
|
|
{
|
|
if (value && *value)//@@ also handle white space and return def instead of 0
|
|
_value = XS_toi(value);
|
|
else
|
|
_value = def;
|
|
}
|
|
|
|
XMLInt(const XMLNode* node, const XS_String& attr_name, int def=0)
|
|
{
|
|
const XS_String& value = node->get(attr_name);
|
|
|
|
if (!value.empty())
|
|
_value = XS_toi(value.c_str());
|
|
else
|
|
_value = def;
|
|
}
|
|
|
|
operator int() const
|
|
{
|
|
return _value;
|
|
}
|
|
|
|
operator XS_String() const
|
|
{
|
|
XS_CHAR buffer[32];
|
|
XS_snprintf(buffer, COUNTOF(buffer), XS_INTFMT, _value);
|
|
return XS_String(buffer);
|
|
}
|
|
|
|
protected:
|
|
int _value;
|
|
|
|
private:
|
|
void operator=(const XMLInt&); // disallow assignment operations
|
|
};
|
|
|
|
/// type converter for integer data with write access
|
|
struct XMLIntRef
|
|
{
|
|
XMLIntRef(XMLNode* node, const XS_String& attr_name, int def=0)
|
|
: _ref((*node)[attr_name])
|
|
{
|
|
if (_ref.empty())
|
|
assign(def);
|
|
}
|
|
|
|
XMLIntRef& operator=(int value)
|
|
{
|
|
assign(value);
|
|
|
|
return *this;
|
|
}
|
|
|
|
operator int() const
|
|
{
|
|
return XS_toi(_ref.c_str());
|
|
}
|
|
|
|
void assign(int value)
|
|
{
|
|
XS_CHAR buffer[32];
|
|
XS_snprintf(buffer, COUNTOF(buffer), XS_INTFMT, value);
|
|
_ref.assign(buffer);
|
|
}
|
|
|
|
protected:
|
|
XS_String& _ref;
|
|
};
|
|
|
|
|
|
/// type converter for numeric data
|
|
struct XMLDouble
|
|
{
|
|
XMLDouble(double value)
|
|
: _value(value)
|
|
{
|
|
}
|
|
|
|
XMLDouble(LPCXSSTR value, double def=0.)
|
|
{
|
|
LPTSTR end;
|
|
|
|
if (value && *value)//@@ also handle white space and return def instead of 0
|
|
_value = XS_tod(value, &end);
|
|
else
|
|
_value = def;
|
|
}
|
|
|
|
XMLDouble(const XMLNode* node, const XS_String& attr_name, double def=0.)
|
|
{
|
|
LPTSTR end;
|
|
const XS_String& value = node->get(attr_name);
|
|
|
|
if (!value.empty())
|
|
_value = XS_tod(value.c_str(), &end);
|
|
else
|
|
_value = def;
|
|
}
|
|
|
|
operator double() const
|
|
{
|
|
return _value;
|
|
}
|
|
|
|
operator XS_String() const
|
|
{
|
|
XS_CHAR buffer[32];
|
|
XS_snprintf(buffer, COUNTOF(buffer), XS_FLOATFMT, _value);
|
|
return XS_String(buffer);
|
|
}
|
|
|
|
protected:
|
|
double _value;
|
|
|
|
private:
|
|
void operator=(const XMLDouble&); // disallow assignment operations
|
|
};
|
|
|
|
/// type converter for numeric data with write access
|
|
struct XMLDoubleRef
|
|
{
|
|
XMLDoubleRef(XMLNode* node, const XS_String& attr_name, double def=0.)
|
|
: _ref((*node)[attr_name])
|
|
{
|
|
if (_ref.empty())
|
|
assign(def);
|
|
}
|
|
|
|
XMLDoubleRef& operator=(double value)
|
|
{
|
|
assign(value);
|
|
|
|
return *this;
|
|
}
|
|
|
|
operator double() const
|
|
{
|
|
LPTSTR end;
|
|
return XS_tod(_ref.c_str(), &end);
|
|
}
|
|
|
|
void assign(double value)
|
|
{
|
|
XS_CHAR buffer[32];
|
|
XS_snprintf(buffer, COUNTOF(buffer), XS_FLOATFMT, value);
|
|
_ref.assign(buffer);
|
|
}
|
|
|
|
protected:
|
|
XS_String& _ref;
|
|
};
|
|
|
|
|
|
/// type converter for string data
|
|
struct XMLString
|
|
{
|
|
XMLString(const XS_String& value)
|
|
: _value(value)
|
|
{
|
|
}
|
|
|
|
XMLString(LPCXSSTR value, LPCXSSTR def=XS_EMPTY)
|
|
{
|
|
if (value && *value)
|
|
_value = value;
|
|
else
|
|
_value = def;
|
|
}
|
|
|
|
XMLString(const XMLNode* node, const XS_String& attr_name, LPCXSSTR def=XS_EMPTY)
|
|
{
|
|
const XS_String& value = node->get(attr_name);
|
|
|
|
if (!value.empty())
|
|
_value = value;
|
|
else
|
|
_value = def;
|
|
}
|
|
|
|
operator const XS_String&() const
|
|
{
|
|
return _value;
|
|
}
|
|
|
|
const XS_String& c_str() const
|
|
{
|
|
return _value;
|
|
}
|
|
|
|
protected:
|
|
XS_String _value;
|
|
|
|
private:
|
|
void operator=(const XMLString&); // disallow assignment operations
|
|
};
|
|
|
|
/// type converter for string data with write access
|
|
struct XMLStringRef
|
|
{
|
|
XMLStringRef(XMLNode* node, const XS_String& attr_name, LPCXSSTR def=XS_EMPTY)
|
|
: _ref((*node)[attr_name])
|
|
{
|
|
if (_ref.empty())
|
|
assign(def);
|
|
}
|
|
|
|
XMLStringRef(const XS_String& node_name, XMLNode* node, const XS_String& attr_name, LPCXSSTR def=XS_EMPTY)
|
|
: _ref(node->subvalue(node_name, attr_name))
|
|
{
|
|
if (_ref.empty())
|
|
assign(def);
|
|
}
|
|
|
|
XMLStringRef& operator=(const XS_String& value)
|
|
{
|
|
assign(value);
|
|
|
|
return *this;
|
|
}
|
|
|
|
operator const XS_String&() const
|
|
{
|
|
return _ref;
|
|
}
|
|
|
|
void assign(const XS_String& value)
|
|
{
|
|
_ref.assign(value);
|
|
}
|
|
|
|
protected:
|
|
XS_String& _ref;
|
|
};
|
|
|
|
|
|
// read option (for example configuration) values from XML node attributes
|
|
template<typename T>
|
|
inline void read_option(T& var, const_XMLPos& cfg, LPCXSSTR key)
|
|
{
|
|
const XS_String& val = cfg.get(key);
|
|
|
|
if (!val.empty())
|
|
var = val;
|
|
}
|
|
|
|
// read integer option values from XML node attributes
|
|
template<>
|
|
inline void read_option(int& var, const_XMLPos& cfg, LPCXSSTR key)
|
|
{
|
|
const XS_String& val = cfg.get(key);
|
|
|
|
if (!val.empty())
|
|
var = XS_toi(val.c_str());
|
|
}
|
|
|
|
|
|
inline void XMLPos::set_property(const XS_String& key, int value, const XS_String& name)
|
|
{
|
|
smart_create(name, XS_KEY, key);
|
|
XMLIntRef(_cur, XS_VALUE) = value;
|
|
back();
|
|
}
|
|
|
|
inline void XMLPos::set_property(const XS_String& key, double value, const XS_String& name)
|
|
{
|
|
smart_create(name, XS_KEY, key);
|
|
XMLDoubleRef(_cur, XS_VALUE) = value;
|
|
back();
|
|
}
|
|
|
|
inline void XMLPos::set_property(const XS_String& key, const XS_String& value, const XS_String& name)
|
|
{
|
|
smart_create(name, XS_KEY, key);
|
|
put(XS_VALUE, value);
|
|
back();
|
|
}
|
|
|
|
inline void XMLPos::set_property(const XS_String& key, const XMLBool& value, const XS_String& name)
|
|
{
|
|
smart_create(name, XS_KEY, key);
|
|
XMLBoolRef(_cur, XS_VALUE) = value;
|
|
back();
|
|
}
|
|
|
|
|
|
/// a key/value pair for property data access
|
|
struct XMLProperty {
|
|
XMLProperty(const XMLNode* node)
|
|
: _key(node->get(XS_KEY)),
|
|
_value(node->get(XS_VALUE))
|
|
{
|
|
}
|
|
|
|
XS_String _key;
|
|
XS_String _value;
|
|
};
|
|
|
|
|
|
/// utility class to read property settings from a XML tree
|
|
struct XMLPropertyReader
|
|
{
|
|
XMLPropertyReader(const XMLNode::Children& children)
|
|
: _filter(children, XS_PROPERTY),
|
|
_begin(_filter.begin(), _filter.end()),
|
|
_end(_filter.end(), _filter.end())
|
|
{
|
|
}
|
|
|
|
XMLPropertyReader(const XMLNode* node)
|
|
: _filter(node, XS_PROPERTY),
|
|
_begin(_filter.begin(), _filter.end()),
|
|
_end(_filter.end(), _filter.end())
|
|
{
|
|
}
|
|
|
|
/// internal iterator class
|
|
struct const_iterator
|
|
{
|
|
typedef const_XMLChildrenFilter::const_iterator BaseIterator;
|
|
typedef const_iterator myType;
|
|
|
|
const_iterator(BaseIterator begin, BaseIterator end)
|
|
: _cur(begin),
|
|
_end(end)
|
|
{
|
|
}
|
|
|
|
operator BaseIterator()
|
|
{
|
|
return _cur;
|
|
}
|
|
|
|
XMLProperty operator*() const
|
|
{
|
|
return XMLProperty(*_cur);
|
|
}
|
|
|
|
const XMLNode* get_node() const
|
|
{
|
|
return *_cur;
|
|
}
|
|
|
|
myType& operator++()
|
|
{
|
|
++_cur;
|
|
|
|
return *this;
|
|
}
|
|
|
|
myType operator++(int)
|
|
{
|
|
myType ret = *this;
|
|
|
|
++_cur;
|
|
|
|
return ret;
|
|
}
|
|
|
|
bool operator==(const myType& other) const
|
|
{
|
|
return _cur == other._cur;
|
|
}
|
|
|
|
bool operator!=(const myType& other) const
|
|
{
|
|
return _cur != other._cur;
|
|
}
|
|
|
|
protected:
|
|
BaseIterator _cur;
|
|
BaseIterator _end;
|
|
};
|
|
|
|
const_iterator begin()
|
|
{
|
|
return _begin;
|
|
}
|
|
|
|
const_iterator end()
|
|
{
|
|
return _end;
|
|
}
|
|
|
|
protected:
|
|
const_XMLChildrenFilter _filter;
|
|
|
|
const_iterator _begin;
|
|
const_iterator _end;
|
|
};
|
|
|
|
|
|
#ifdef _MSC_VER
|
|
#pragma warning(disable: 4355)
|
|
#endif
|
|
|
|
/// XML reader base class
|
|
struct XMLReaderBase
|
|
#ifdef XS_USE_XERCES
|
|
: public HandlerBase
|
|
#endif
|
|
{
|
|
#ifdef XS_USE_XERCES
|
|
|
|
XMLReaderBase(XMLNode* node, InputSource* source, bool adoptSource=false);
|
|
virtual ~XMLReaderBase();
|
|
|
|
void read();
|
|
|
|
protected:
|
|
SAXParser* _parser;
|
|
InputSource* _source;
|
|
bool _deleteSource;
|
|
|
|
virtual void XMLDecl(const XMLCh* const versionStr, const XMLCh* const encodingStr,
|
|
const XMLCh* const standaloneStr, const XMLCh* const actualEncodingStr);
|
|
|
|
// Handlers for the SAX DocumentHandler interface
|
|
virtual void setDocumentLocator(const Locator* const locator);
|
|
virtual void startElement(const XMLCh* const name, AttributeList& attributes);
|
|
virtual void endElement(const XMLCh* const name);
|
|
virtual void characters(const XMLCh* const chars, const unsigned int length);
|
|
virtual void ignorableWhitespace(const XMLCh* const chars, const unsigned int length);
|
|
|
|
// Handlers for the SAX ErrorHandler interface
|
|
virtual void error(const SAXParseException& e);
|
|
virtual void fatalError(const SAXParseException& e);
|
|
virtual void warning(const SAXParseException& e);
|
|
virtual void resetErrors();
|
|
|
|
#elif defined(XS_USE_EXPAT) // !XS_USE_XERCES
|
|
|
|
XMLReaderBase(XMLNode* node);
|
|
virtual ~XMLReaderBase();
|
|
|
|
protected:
|
|
XML_Parser _parser;
|
|
|
|
static void XMLCALL XML_XmlDeclHandler(void* userData, const XML_Char* version, const XML_Char* encoding, int standalone=-1);
|
|
static void XMLCALL XML_StartElementHandler(void* userData, const XML_Char* name, const XML_Char** atts);
|
|
static void XMLCALL XML_EndElementHandler(void* userData, const XML_Char* name);
|
|
static void XMLCALL XML_DefaultHandler(void* userData, const XML_Char* s, int len);
|
|
|
|
static std::string get_expat_error_string(XML_Error error_code);
|
|
|
|
#else // XS_USE_EXPAT
|
|
|
|
XMLReaderBase(XMLNode* node)
|
|
: _pos(node),
|
|
_endl_defined(false),
|
|
_utf8(false)
|
|
{
|
|
_last_tag = TAG_NONE;
|
|
}
|
|
|
|
virtual ~XMLReaderBase();
|
|
|
|
bool parse();
|
|
|
|
#endif
|
|
|
|
public:
|
|
#ifndef XS_USE_XERCES
|
|
void read();
|
|
|
|
std::string get_position() const;
|
|
#endif
|
|
const XMLFormat& get_format() const {return _format;}
|
|
const char* get_endl() const {return _endl_defined? _format._endl: "\n";}
|
|
|
|
const XMLErrorList& get_errors() const {return _errors;}
|
|
const XMLErrorList& get_warnings() const {return _warnings;}
|
|
|
|
void clear_errors() {_errors.clear(); _warnings.clear();}
|
|
|
|
#ifdef XMLNODE_LOCATION
|
|
const char* _display_path; // character pointer for fast reference in XMLLocation
|
|
|
|
#ifdef XS_USE_XERCES
|
|
const Locator* _locator;
|
|
#endif
|
|
|
|
XMLLocation get_location() const;
|
|
#endif
|
|
|
|
protected:
|
|
XMLPos _pos;
|
|
|
|
std::string _content; // UTF-8 encoded
|
|
enum {TAG_NONE, TAG_START, TAG_END} _last_tag;
|
|
|
|
XMLErrorList _errors;
|
|
XMLErrorList _warnings;
|
|
|
|
XMLFormat _format;
|
|
bool _endl_defined;
|
|
|
|
#ifdef XS_USE_XERCES
|
|
//@@
|
|
#elif defined(XS_USE_EXPAT)
|
|
virtual int read_buffer(char* buffer, int len) = 0;
|
|
#else
|
|
virtual int get() = 0;
|
|
int eat_endl();
|
|
|
|
bool _utf8;
|
|
#endif
|
|
|
|
void finish_read();
|
|
|
|
virtual void XmlDeclHandler(const char* version, const char* encoding, int standalone);
|
|
virtual void StartElementHandler(const XS_String& name, const XMLNode::AttributeMap& attributes);
|
|
virtual void EndElementHandler();
|
|
#if defined(XS_USE_XERCES) || defined(XS_USE_EXPAT)
|
|
virtual void DefaultHandler(const XML_Char* s, int len);
|
|
#else
|
|
virtual void DefaultHandler(const std::string& s);
|
|
#endif
|
|
};
|
|
|
|
|
|
/// XML file reader
|
|
|
|
#ifdef XS_USE_XERCES
|
|
|
|
struct XercesXMLReader : public XMLReaderBase
|
|
{
|
|
XercesXMLReader(XMLNode* node, InputSource* source, bool adoptSource=false)
|
|
: XMLReaderBase(node, source, adoptSource)
|
|
{
|
|
}
|
|
|
|
XercesXMLReader(XMLNode* node, LPCTSTR path);
|
|
XercesXMLReader(XMLNode* node, const XMLByte* buffer, size_t bytes, const std::string& system_id=std::string());
|
|
};
|
|
|
|
#define XMLReader XercesXMLReader
|
|
|
|
#elif defined(XS_USE_EXPAT)
|
|
|
|
struct ExpatXMLReader : public XMLReaderBase
|
|
{
|
|
ExpatXMLReader(XMLNode* node, std::istream& in)
|
|
: XMLReaderBase(node),
|
|
_in(in)
|
|
{
|
|
}
|
|
|
|
/// read XML stream into XML tree below _pos
|
|
int read_buffer(char* buffer, int len)
|
|
{
|
|
if (!_in.good())
|
|
return -1;
|
|
|
|
_in.read(buffer, len);
|
|
|
|
return _in.gcount();
|
|
}
|
|
|
|
protected:
|
|
std::istream& _in;
|
|
};
|
|
|
|
#define XMLReader ExpatXMLReader
|
|
|
|
#else // XS_USE_XERCES, XS_USE_EXPAT
|
|
|
|
struct XMLReader : public XMLReaderBase
|
|
{
|
|
XMLReader(XMLNode* node, std::istream& in)
|
|
: XMLReaderBase(node),
|
|
_in(in)
|
|
{
|
|
}
|
|
|
|
/// read one character from XML stream
|
|
int get()
|
|
{
|
|
return _in.get();
|
|
}
|
|
|
|
protected:
|
|
std::istream& _in;
|
|
};
|
|
|
|
#endif // XS_USE_XERCES
|
|
|
|
|
|
#if defined(_MSC_VER) && _MSC_VER<1400
|
|
|
|
struct fast_ostringbuffer : public std::streambuf
|
|
{
|
|
typedef char _E;
|
|
typedef std::char_traits<_E> _Tr;
|
|
|
|
explicit fast_ostringbuffer()
|
|
{_Init(0, 0, std::_Noread);} // optimized for ios::out mode
|
|
|
|
virtual ~fast_ostringbuffer()
|
|
{_Tidy();}
|
|
|
|
std::string str() const
|
|
{if (pptr() != 0)
|
|
{std::string _Str(pbase(),
|
|
(_Seekhigh<pptr()? pptr(): _Seekhigh) - pbase());
|
|
return _Str;}
|
|
else
|
|
return std::string();}
|
|
|
|
protected:
|
|
virtual int_type overflow(int_type _C = _Tr::eof())
|
|
{if (_Tr::eq_int_type(_Tr::eof(), _C))
|
|
return _Tr::not_eof(_C);
|
|
else if (pptr() != 0 && pptr() < epptr())
|
|
{*_Pninc() = _Tr::to_char_type(_C);
|
|
return _C;}
|
|
else
|
|
{size_t _Os = gptr() == 0 ? 0 : epptr() - eback();
|
|
size_t _Ns = _Os + _Alsize;
|
|
_E *_P = _Al.allocate(_Ns, (void *)0);
|
|
if (0 < _Os)
|
|
_Tr::copy(_P, eback(), _Os);
|
|
else if (_ALSIZE < _Alsize)
|
|
_Alsize = _ALSIZE;
|
|
|
|
if (_Strmode & std::_Allocated)
|
|
_Al.deallocate(eback(), _Os);
|
|
|
|
_Strmode |= std::_Allocated;
|
|
|
|
if (_Os == 0)
|
|
{_Seekhigh = _P;
|
|
setp(_P, _P + _Ns);
|
|
setg(_P, _P, _P); }
|
|
else
|
|
{_Seekhigh = _Seekhigh - eback() + _P;
|
|
setp(pbase() - eback() + _P, pptr() - eback() + _P, _P + _Ns);
|
|
setg(_P, _P, _P);}
|
|
*_Pninc() = _Tr::to_char_type(_C);
|
|
|
|
return _C;}}
|
|
|
|
void _Init(const _E *_S, size_t _N, std::_Strstate _M)
|
|
{_Pendsave = 0, _Seekhigh = 0;
|
|
_Alsize = _MINSIZE, _Strmode = _M;
|
|
setg(0, 0, 0);
|
|
setp(0, 0);}
|
|
|
|
void _Tidy()
|
|
{if (_Strmode & std::_Allocated)
|
|
_Al.deallocate(eback(), (pptr() != 0 ? epptr() : egptr()) - eback());
|
|
_Seekhigh = 0;
|
|
_Strmode &= ~std::_Allocated;}
|
|
|
|
private:
|
|
enum {_ALSIZE = 65536/*512*/, _MINSIZE = 32768/*32*/}; // bigger buffer sizes
|
|
|
|
_E *_Pendsave, *_Seekhigh;
|
|
int _Alsize;
|
|
std::_Strstate _Strmode;
|
|
std::allocator<_E> _Al;
|
|
};
|
|
|
|
struct fast_ostringstream : public std::iostream
|
|
{
|
|
typedef std::iostream super;
|
|
|
|
explicit fast_ostringstream()
|
|
: super(&_Sb) {}
|
|
|
|
std::string str() const
|
|
{return _Sb.str();}
|
|
|
|
private:
|
|
fast_ostringbuffer _Sb;
|
|
};
|
|
|
|
#else
|
|
|
|
typedef std::ostringstream fast_ostringstream;
|
|
|
|
#endif
|
|
|
|
|
|
/// XML document holder
|
|
struct XMLDoc : public XMLNode
|
|
{
|
|
XMLDoc()
|
|
: XMLNode("")
|
|
{
|
|
}
|
|
|
|
XMLDoc(LPCTSTR path)
|
|
: XMLNode("")
|
|
{
|
|
read_file(path);
|
|
}
|
|
|
|
#ifdef XS_USE_XERCES
|
|
bool read_file(LPCTSTR path)
|
|
{
|
|
XMLReader reader(this, path);
|
|
|
|
#if defined(_STRING_DEFINED) && !defined(XS_STRING_UTF8)
|
|
return read(reader, std::string(ANS(path)));
|
|
#else
|
|
return read(reader, XS_String(path));
|
|
#endif
|
|
}
|
|
|
|
bool read_buffer(const char* buffer, size_t len, const std::string& system_id=std::string())
|
|
{
|
|
XMLReader reader(this, (const XMLByte*)buffer, len, system_id);
|
|
|
|
return read(reader, system_id);
|
|
}
|
|
|
|
bool read_buffer(const std::string& in, const std::string& system_id=std::string())
|
|
{
|
|
return read_buffer(in.c_str(), in.length(), system_id);
|
|
}
|
|
|
|
#else // XS_USE_XERCES
|
|
|
|
bool read_file(LPCTSTR path)
|
|
{
|
|
tifstream in(path);
|
|
if (!in.good())
|
|
return false;
|
|
|
|
XMLReader reader(this, in);
|
|
|
|
#if defined(_STRING_DEFINED) && !defined(XS_STRING_UTF8)
|
|
return read(reader, std::string(ANS(path)));
|
|
#else
|
|
return read(reader, XS_String(path));
|
|
#endif
|
|
}
|
|
|
|
bool read_buffer(const char* buffer, size_t len, const std::string& system_id=std::string())
|
|
{
|
|
return read_buffer(std::string(buffer, len), system_id);
|
|
}
|
|
|
|
bool read_buffer(const std::string& buffer, const std::string& system_id=std::string())
|
|
{
|
|
std::istringstream istr(buffer);
|
|
|
|
return read_stream(istr, system_id);
|
|
}
|
|
|
|
bool read_stream(std::istream& in, const std::string& system_id=std::string())
|
|
{
|
|
XMLReader reader(this, in);
|
|
|
|
return read(reader, system_id);
|
|
}
|
|
#endif // XS_USE_XERCES
|
|
|
|
bool read(XMLReaderBase& reader, const std::string& display_path)
|
|
{
|
|
#ifdef XMLNODE_LOCATION
|
|
// make a string copy to handle temporary string objects
|
|
_display_path = display_path;
|
|
reader._display_path = _display_path.c_str();
|
|
#endif
|
|
|
|
reader.clear_errors();
|
|
reader.read();
|
|
|
|
_format = reader.get_format();
|
|
_format._endl = reader.get_endl();
|
|
|
|
if (!reader.get_errors().empty()) {
|
|
_errors = reader.get_errors();
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/// write XML stream
|
|
// FORMAT_SMART: preserving previous white space and comments
|
|
bool write(std::ostream& out, WRITE_MODE mode=FORMAT_SMART) const
|
|
{
|
|
_format.print_header(out, mode!=FORMAT_PLAIN);
|
|
|
|
if (_children.size() == 1)
|
|
_children.front()->write(out, _format, mode);
|
|
else if (!_children.empty()) {
|
|
//throw Exception("more than one XML root!");
|
|
return false;
|
|
}
|
|
|
|
return out.good();
|
|
}
|
|
|
|
/// write XML stream with formating
|
|
bool write_formating(std::ostream& out) const
|
|
{
|
|
return write(out, FORMAT_PRETTY);
|
|
}
|
|
|
|
bool write_file(LPCTSTR path, WRITE_MODE mode=FORMAT_SMART) const
|
|
{
|
|
tofstream out(path);
|
|
|
|
return write(out, mode);
|
|
}
|
|
|
|
bool write_formating(LPCTSTR path) const
|
|
{
|
|
tofstream out(path);
|
|
|
|
return write_formating(out);
|
|
}
|
|
|
|
XMLFormat _format;
|
|
XMLErrorList _errors;
|
|
|
|
#ifdef XMLNODE_LOCATION
|
|
std::string _display_path;
|
|
#endif
|
|
};
|
|
|
|
|
|
/// XML message wrapper
|
|
struct XMLMessage : public XMLDoc
|
|
{
|
|
XMLMessage(const char* name)
|
|
: _pos(this)
|
|
{
|
|
_pos.create(name);
|
|
}
|
|
|
|
std::string toString() const
|
|
{
|
|
std::ostringstream out;
|
|
|
|
write(out);
|
|
|
|
return out.str();
|
|
}
|
|
|
|
XMLPos _pos;
|
|
|
|
protected:
|
|
XMLMessage()
|
|
: _pos(this)
|
|
{
|
|
}
|
|
};
|
|
|
|
|
|
/// helper structure to read XML messages from strings
|
|
struct XMLMessageFromString : public XMLMessage
|
|
{
|
|
XMLMessageFromString(const std::string& xml_str, const std::string& system_id=std::string())
|
|
{
|
|
read_buffer(xml_str.c_str(), xml_str.length(), system_id);
|
|
}
|
|
};
|
|
|
|
|
|
/// Reader for XML Messages
|
|
struct XMLMessageReader : public XMLPos
|
|
{
|
|
XMLMessageReader(const std::string& xml_str, const std::string& system_id=std::string())
|
|
: XMLPos(&_msg)
|
|
{
|
|
_msg.read_buffer(xml_str.c_str(), xml_str.length(), system_id);
|
|
}
|
|
|
|
const XMLDoc& get_document()
|
|
{
|
|
return _msg;
|
|
}
|
|
|
|
protected:
|
|
XMLDoc _msg;
|
|
};
|
|
|
|
|
|
/// on the fly XML writer
|
|
struct XMLWriter
|
|
{
|
|
XMLWriter(std::ostream& out, const XMLFormat& format=XMLFormat())
|
|
: _pofstream(NULL),
|
|
_out(out),
|
|
_format(format)
|
|
{
|
|
format.print_header(_out, false); // _format._endl is printed in write_pre()
|
|
}
|
|
|
|
XMLWriter(LPCTSTR path, const XMLFormat& format=XMLFormat())
|
|
: _pofstream(new tofstream(path)),
|
|
_out(*_pofstream),
|
|
_format(format)
|
|
{
|
|
format.print_header(_out, false); // _format._endl is printed in write_pre()
|
|
}
|
|
|
|
~XMLWriter()
|
|
{
|
|
_out << _format._endl;
|
|
delete _pofstream;
|
|
}
|
|
|
|
/// create node and move to it
|
|
void create(const XS_String& name);
|
|
|
|
/// go back to previous position
|
|
bool back();
|
|
|
|
/// attribute setting
|
|
void put(const XS_String& attr_name, const XS_String& value)
|
|
{
|
|
if (!_stack.empty())
|
|
_stack.top()._attributes[attr_name] = value;
|
|
}
|
|
|
|
/// index operator write access to an attribute
|
|
XS_String& operator[](const XS_String& attr_name)
|
|
{
|
|
if (_stack.empty())
|
|
return s_empty_attr;
|
|
|
|
return _stack.top()._attributes[attr_name];
|
|
}
|
|
|
|
void set_content(const XS_String& s, bool cdata=false)
|
|
{
|
|
if (!_stack.empty())
|
|
_stack.top()._content = EncodeXMLString(s.c_str(), cdata);
|
|
}
|
|
|
|
/// create node with string content
|
|
void create_node_content(const XS_String& node_name, const XS_String& content)
|
|
{
|
|
create(node_name);
|
|
set_content(content);
|
|
back();
|
|
}
|
|
|
|
// public for access in StackEntry
|
|
enum WRITESTATE {
|
|
NOTHING, /*PRE,*/ ATTRIBUTES, PRE_CLOSED, /*CONTENT,*/ POST
|
|
};
|
|
|
|
protected:
|
|
tofstream* _pofstream;
|
|
std::ostream& _out;
|
|
XMLFormat _format;
|
|
|
|
typedef XMLNode::AttributeMap AttrMap;
|
|
|
|
/// container for XMLWriter state information
|
|
struct StackEntry {
|
|
XS_String _node_name;
|
|
AttrMap _attributes;
|
|
std::string _content;
|
|
WRITESTATE _state;
|
|
bool _children;
|
|
|
|
StackEntry() : _state(NOTHING), _children(false) {}
|
|
};
|
|
|
|
std::stack<StackEntry> _stack;
|
|
|
|
static XS_String s_empty_attr;
|
|
|
|
void close_pre(StackEntry& entry);
|
|
void write_pre(StackEntry& entry);
|
|
void write_attributes(StackEntry& entry);
|
|
void write_post(StackEntry& entry);
|
|
};
|
|
|
|
|
|
} // namespace XMLStorage
|
|
|
|
#define _XMLSTORAGE_H
|
|
#endif // _XMLSTORAGE_H
|