0
0
mirror of https://github.com/zeux/pugixml.git synced 2024-12-31 00:13:01 +08:00

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:
arseny.kapoulkine 2010-08-29 15:35:22 +00:00
parent 5442ff6aba
commit 7b4141582d
2 changed files with 374 additions and 17 deletions

View File

@ -4603,28 +4603,28 @@ namespace
{
using namespace pugi;
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)));
if (!result) return 0; // $$ out of memory
memcpy(result, string, length * sizeof(char_t));
result[length] = 0;
return result;
}
char_t* duplicate_string(const char_t* string)
{
return duplicate_string(string, strlength(string));
}
class xpath_string
{
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)));
if (!result) return 0; // $$ out of memory
memcpy(result, string, length * sizeof(char_t));
result[length] = 0;
return result;
}
static char_t* duplicate_string(const char_t* string)
{
return duplicate_string(string, strlength(string));
}
public:
xpath_string(): _buffer(PUGIXML_TEXT("")), _uses_heap(false)
{
}
@ -5331,6 +5331,119 @@ namespace
// zero-terminate
*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
@ -8105,6 +8218,188 @@ namespace pugi
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)
{
_result.error = 0;

View File

@ -1804,6 +1804,68 @@ namespace pugi
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
*/