XPath: Added variable interface and implementation
git-svn-id: http://pugixml.googlecode.com/svn/trunk@676 99668b35-9821-0410-8761-19e4c4f06640
This commit is contained in:
parent
5442ff6aba
commit
7b4141582d
309
src/pugixml.cpp
309
src/pugixml.cpp
@ -4603,12 +4603,7 @@ namespace
|
|||||||
{
|
{
|
||||||
using namespace pugi;
|
using namespace pugi;
|
||||||
|
|
||||||
class xpath_string
|
char_t* duplicate_string(const char_t* string, size_t length)
|
||||||
{
|
|
||||||
const char_t* _buffer;
|
|
||||||
bool _uses_heap;
|
|
||||||
|
|
||||||
static char_t* duplicate_string(const char_t* string, size_t length)
|
|
||||||
{
|
{
|
||||||
char_t* result = static_cast<char_t*>(get_memory_allocation_function()((length + 1) * sizeof(char_t)));
|
char_t* result = static_cast<char_t*>(get_memory_allocation_function()((length + 1) * sizeof(char_t)));
|
||||||
if (!result) return 0; // $$ out of memory
|
if (!result) return 0; // $$ out of memory
|
||||||
@ -4619,12 +4614,17 @@ namespace
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
static char_t* duplicate_string(const char_t* string)
|
char_t* duplicate_string(const char_t* string)
|
||||||
{
|
{
|
||||||
return duplicate_string(string, strlength(string));
|
return duplicate_string(string, strlength(string));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class xpath_string
|
||||||
|
{
|
||||||
|
const char_t* _buffer;
|
||||||
|
bool _uses_heap;
|
||||||
public:
|
public:
|
||||||
|
|
||||||
xpath_string(): _buffer(PUGIXML_TEXT("")), _uses_heap(false)
|
xpath_string(): _buffer(PUGIXML_TEXT("")), _uses_heap(false)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
@ -5331,6 +5331,119 @@ namespace
|
|||||||
// zero-terminate
|
// zero-terminate
|
||||||
*write = 0;
|
*write = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct xpath_variable_boolean: xpath_variable
|
||||||
|
{
|
||||||
|
bool value;
|
||||||
|
char_t name[1];
|
||||||
|
};
|
||||||
|
|
||||||
|
struct xpath_variable_number: xpath_variable
|
||||||
|
{
|
||||||
|
double value;
|
||||||
|
char_t name[1];
|
||||||
|
};
|
||||||
|
|
||||||
|
struct xpath_variable_string: xpath_variable
|
||||||
|
{
|
||||||
|
char_t* value;
|
||||||
|
char_t name[1];
|
||||||
|
};
|
||||||
|
|
||||||
|
struct xpath_variable_node_set: xpath_variable
|
||||||
|
{
|
||||||
|
xpath_node_set value;
|
||||||
|
char_t name[1];
|
||||||
|
};
|
||||||
|
|
||||||
|
const xpath_node_set dummy_node_set;
|
||||||
|
|
||||||
|
unsigned int hash_string(const char_t* str)
|
||||||
|
{
|
||||||
|
// Jenkins one-at-a-time hash (http://en.wikipedia.org/wiki/Jenkins_hash_function#one-at-a-time)
|
||||||
|
unsigned int result = 0;
|
||||||
|
|
||||||
|
while (*str)
|
||||||
|
{
|
||||||
|
result += static_cast<unsigned int>(*str++);
|
||||||
|
result += result << 10;
|
||||||
|
result ^= result >> 6;
|
||||||
|
}
|
||||||
|
|
||||||
|
result += result << 3;
|
||||||
|
result ^= result >> 11;
|
||||||
|
result += result << 15;
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T> T* new_xpath_variable(const char_t* name)
|
||||||
|
{
|
||||||
|
size_t length = strlength(name);
|
||||||
|
|
||||||
|
// we can't use offsetof(T, name) because T is non-POD, so we just allocate additional length characters
|
||||||
|
void* memory = get_memory_allocation_function()(sizeof(T) + length * sizeof(char_t));
|
||||||
|
if (!memory) return 0;
|
||||||
|
|
||||||
|
T* result = new (memory) T();
|
||||||
|
|
||||||
|
memcpy(result->name, name, (length + 1) * sizeof(char_t));
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
xpath_variable* new_xpath_variable(xpath_value_type type, const char_t* name)
|
||||||
|
{
|
||||||
|
switch (type)
|
||||||
|
{
|
||||||
|
case xpath_type_node_set:
|
||||||
|
return new_xpath_variable<xpath_variable_node_set>(name);
|
||||||
|
|
||||||
|
case xpath_type_number:
|
||||||
|
return new_xpath_variable<xpath_variable_number>(name);
|
||||||
|
|
||||||
|
case xpath_type_string:
|
||||||
|
return new_xpath_variable<xpath_variable_string>(name);
|
||||||
|
|
||||||
|
case xpath_type_boolean:
|
||||||
|
return new_xpath_variable<xpath_variable_boolean>(name);
|
||||||
|
|
||||||
|
default:
|
||||||
|
assert(false);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T> void delete_xpath_variable(xpath_variable* var)
|
||||||
|
{
|
||||||
|
static_cast<T*>(var)->~T();
|
||||||
|
get_memory_deallocation_function()(var);
|
||||||
|
}
|
||||||
|
|
||||||
|
void delete_xpath_variable(xpath_value_type type, xpath_variable* var)
|
||||||
|
{
|
||||||
|
switch (type)
|
||||||
|
{
|
||||||
|
case xpath_type_node_set:
|
||||||
|
delete_xpath_variable<xpath_variable_node_set>(var);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case xpath_type_number:
|
||||||
|
delete_xpath_variable<xpath_variable_number>(var);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case xpath_type_string:
|
||||||
|
delete_xpath_variable<xpath_variable_string>(var);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case xpath_type_boolean:
|
||||||
|
delete_xpath_variable<xpath_variable_boolean>(var);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
assert(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace pugi
|
namespace pugi
|
||||||
@ -8105,6 +8218,188 @@ namespace pugi
|
|||||||
return error ? error : "No error";
|
return error ? error : "No error";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const char_t* xpath_variable::name() const
|
||||||
|
{
|
||||||
|
switch (_type)
|
||||||
|
{
|
||||||
|
case xpath_type_node_set:
|
||||||
|
return static_cast<const xpath_variable_node_set*>(this)->name;
|
||||||
|
|
||||||
|
case xpath_type_number:
|
||||||
|
return static_cast<const xpath_variable_number*>(this)->name;
|
||||||
|
|
||||||
|
case xpath_type_string:
|
||||||
|
return static_cast<const xpath_variable_string*>(this)->name;
|
||||||
|
|
||||||
|
case xpath_type_boolean:
|
||||||
|
return static_cast<const xpath_variable_boolean*>(this)->name;
|
||||||
|
|
||||||
|
default:
|
||||||
|
assert(false);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
xpath_value_type xpath_variable::type() const
|
||||||
|
{
|
||||||
|
return _type;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool xpath_variable::get_boolean() const
|
||||||
|
{
|
||||||
|
return (_type == xpath_type_boolean) ? static_cast<const xpath_variable_boolean*>(this)->value : false;
|
||||||
|
}
|
||||||
|
|
||||||
|
double xpath_variable::get_number() const
|
||||||
|
{
|
||||||
|
return (_type == xpath_type_number) ? static_cast<const xpath_variable_number*>(this)->value : gen_nan();
|
||||||
|
}
|
||||||
|
|
||||||
|
const char_t* xpath_variable::get_string() const
|
||||||
|
{
|
||||||
|
const char_t* value = (_type == xpath_type_string) ? static_cast<const xpath_variable_string*>(this)->value : 0;
|
||||||
|
return value ? value : PUGIXML_TEXT("");
|
||||||
|
}
|
||||||
|
|
||||||
|
const xpath_node_set& xpath_variable::get_node_set() const
|
||||||
|
{
|
||||||
|
return (_type == xpath_type_node_set) ? static_cast<const xpath_variable_node_set*>(this)->value : dummy_node_set;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool xpath_variable::set(bool value)
|
||||||
|
{
|
||||||
|
if (_type != xpath_type_boolean) return false;
|
||||||
|
|
||||||
|
static_cast<xpath_variable_boolean*>(this)->value = value;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool xpath_variable::set(double value)
|
||||||
|
{
|
||||||
|
if (_type != xpath_type_number) return false;
|
||||||
|
|
||||||
|
static_cast<xpath_variable_number*>(this)->value = value;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool xpath_variable::set(const char_t* value)
|
||||||
|
{
|
||||||
|
if (_type != xpath_type_string) return false;
|
||||||
|
|
||||||
|
xpath_variable_string* var = static_cast<xpath_variable_string*>(this);
|
||||||
|
|
||||||
|
// duplicate string
|
||||||
|
char_t* copy = duplicate_string(value);
|
||||||
|
if (!copy) return false;
|
||||||
|
|
||||||
|
// replace old string
|
||||||
|
if (var->value) get_memory_deallocation_function()(var->value);
|
||||||
|
var->value = copy;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool xpath_variable::set(const xpath_node_set& value)
|
||||||
|
{
|
||||||
|
if (_type != xpath_type_node_set) return false;
|
||||||
|
|
||||||
|
static_cast<xpath_variable_node_set*>(this)->value = value;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
xpath_variable_set::xpath_variable_set()
|
||||||
|
{
|
||||||
|
for (size_t i = 0; i < sizeof(_data) / sizeof(_data[0]); ++i) _data[i] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
xpath_variable_set::~xpath_variable_set()
|
||||||
|
{
|
||||||
|
for (size_t i = 0; i < sizeof(_data) / sizeof(_data[0]); ++i)
|
||||||
|
{
|
||||||
|
xpath_variable* var = _data[i];
|
||||||
|
|
||||||
|
while (var)
|
||||||
|
{
|
||||||
|
xpath_variable* next = var->_next;
|
||||||
|
|
||||||
|
delete_xpath_variable(var->_type, var);
|
||||||
|
|
||||||
|
var = next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
xpath_variable* xpath_variable_set::find(const char_t* name) const
|
||||||
|
{
|
||||||
|
const size_t hash_size = sizeof(_data) / sizeof(_data[0]);
|
||||||
|
size_t hash = hash_string(name) % hash_size;
|
||||||
|
|
||||||
|
// look for existing variable
|
||||||
|
for (xpath_variable* var = _data[hash]; var; var = var->_next)
|
||||||
|
if (strequal(var->name(), name))
|
||||||
|
return var;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
xpath_variable* xpath_variable_set::add(const char_t* name, xpath_value_type type)
|
||||||
|
{
|
||||||
|
const size_t hash_size = sizeof(_data) / sizeof(_data[0]);
|
||||||
|
size_t hash = hash_string(name) % hash_size;
|
||||||
|
|
||||||
|
// look for existing variable
|
||||||
|
for (xpath_variable* var = _data[hash]; var; var = var->_next)
|
||||||
|
if (strequal(var->name(), name))
|
||||||
|
return var->type() == type ? var : 0;
|
||||||
|
|
||||||
|
// add new variable
|
||||||
|
xpath_variable* result = new_xpath_variable(type, name);
|
||||||
|
|
||||||
|
if (result)
|
||||||
|
{
|
||||||
|
result->_type = type;
|
||||||
|
result->_next = _data[hash];
|
||||||
|
|
||||||
|
_data[hash] = result;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool xpath_variable_set::set(const char_t* name, bool value)
|
||||||
|
{
|
||||||
|
xpath_variable* var = add(name, xpath_type_boolean);
|
||||||
|
return var ? var->set(value) : false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool xpath_variable_set::set(const char_t* name, double value)
|
||||||
|
{
|
||||||
|
xpath_variable* var = add(name, xpath_type_number);
|
||||||
|
return var ? var->set(value) : false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool xpath_variable_set::set(const char_t* name, const char_t* value)
|
||||||
|
{
|
||||||
|
xpath_variable* var = add(name, xpath_type_string);
|
||||||
|
return var ? var->set(value) : false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool xpath_variable_set::set(const char_t* name, const xpath_node_set& value)
|
||||||
|
{
|
||||||
|
xpath_variable* var = add(name, xpath_type_node_set);
|
||||||
|
return var ? var->set(value) : false;
|
||||||
|
}
|
||||||
|
|
||||||
|
xpath_variable* xpath_variable_set::get(const char_t* name)
|
||||||
|
{
|
||||||
|
return find(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
const xpath_variable* xpath_variable_set::get(const char_t* name) const
|
||||||
|
{
|
||||||
|
return find(name);
|
||||||
|
}
|
||||||
|
|
||||||
xpath_query::xpath_query(const char_t* query): _alloc(0), _root(0)
|
xpath_query::xpath_query(const char_t* query): _alloc(0), _root(0)
|
||||||
{
|
{
|
||||||
_result.error = 0;
|
_result.error = 0;
|
||||||
|
|||||||
@ -1804,6 +1804,68 @@ namespace pugi
|
|||||||
const char* description() const;
|
const char* description() const;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A class that holds XPath variable
|
||||||
|
*/
|
||||||
|
class xpath_variable
|
||||||
|
{
|
||||||
|
friend class xpath_variable_set;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
// Non-copyable semantics
|
||||||
|
xpath_variable(const xpath_variable&);
|
||||||
|
xpath_variable& operator=(const xpath_variable&);
|
||||||
|
|
||||||
|
xpath_value_type _type;
|
||||||
|
xpath_variable* _next;
|
||||||
|
|
||||||
|
xpath_variable() {}
|
||||||
|
~xpath_variable() {}
|
||||||
|
|
||||||
|
public:
|
||||||
|
const char_t* name() const;
|
||||||
|
xpath_value_type type() const;
|
||||||
|
|
||||||
|
bool get_boolean() const;
|
||||||
|
double get_number() const;
|
||||||
|
const char_t* get_string() const;
|
||||||
|
const xpath_node_set& get_node_set() const;
|
||||||
|
|
||||||
|
bool set(bool value);
|
||||||
|
bool set(double value);
|
||||||
|
bool set(const char_t* value);
|
||||||
|
bool set(const xpath_node_set& value);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A class that holds XPath variables
|
||||||
|
*/
|
||||||
|
class xpath_variable_set
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
// Non-copyable semantics
|
||||||
|
xpath_variable_set(const xpath_variable_set&);
|
||||||
|
xpath_variable_set& operator=(const xpath_variable_set&);
|
||||||
|
|
||||||
|
xpath_variable* _data[64];
|
||||||
|
|
||||||
|
xpath_variable* find(const char_t* name) const;
|
||||||
|
|
||||||
|
public:
|
||||||
|
xpath_variable_set();
|
||||||
|
~xpath_variable_set();
|
||||||
|
|
||||||
|
xpath_variable* add(const char_t* name, xpath_value_type type);
|
||||||
|
|
||||||
|
bool set(const char_t* name, bool value);
|
||||||
|
bool set(const char_t* name, double value);
|
||||||
|
bool set(const char_t* name, const char_t* value);
|
||||||
|
bool set(const char_t* name, const xpath_node_set& value);
|
||||||
|
|
||||||
|
xpath_variable* get(const char_t* name);
|
||||||
|
const xpath_variable* get(const char_t* name) const;
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A class that holds compiled XPath query and allows to evaluate query result
|
* A class that holds compiled XPath query and allows to evaluate query result
|
||||||
*/
|
*/
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user