summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorarseny.kapoulkine <arseny.kapoulkine@99668b35-9821-0410-8761-19e4c4f06640>2010-08-29 15:35:22 +0000
committerarseny.kapoulkine <arseny.kapoulkine@99668b35-9821-0410-8761-19e4c4f06640>2010-08-29 15:35:22 +0000
commit7b4141582d9390bf951f211a561b53cea3aa1705 (patch)
tree0198501cc657d94e3839d3e333459a5c18e1e623
parent5442ff6abaec9fa28c73b8b1c4fbf9fbecf686b0 (diff)
XPath: Added variable interface and implementation
git-svn-id: http://pugixml.googlecode.com/svn/trunk@676 99668b35-9821-0410-8761-19e4c4f06640
-rw-r--r--src/pugixml.cpp327
-rw-r--r--src/pugixml.hpp62
2 files changed, 373 insertions, 16 deletions
diff --git a/src/pugixml.cpp b/src/pugixml.cpp
index 7ec18ae..dbf5f2f 100644
--- a/src/pugixml.cpp
+++ b/src/pugixml.cpp
@@ -4603,28 +4603,28 @@ namespace
{
using namespace pugi;
- class xpath_string
+ char_t* duplicate_string(const char_t* string, size_t length)
{
- const char_t* _buffer;
- bool _uses_heap;
+ char_t* result = static_cast<char_t*>(get_memory_allocation_function()((length + 1) * sizeof(char_t)));
+ if (!result) return 0; // $$ out of memory
- 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;
+ memcpy(result, string, length * sizeof(char_t));
+ result[length] = 0;
- return result;
- }
+ return result;
+ }
- static char_t* duplicate_string(const char_t* string)
- {
- return duplicate_string(string, strlength(string));
- }
+ char_t* duplicate_string(const char_t* string)
+ {
+ return duplicate_string(string, strlength(string));
+ }
+ class xpath_string
+ {
+ const char_t* _buffer;
+ bool _uses_heap;
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;
diff --git a/src/pugixml.hpp b/src/pugixml.hpp
index b31e251..7e09551 100644
--- a/src/pugixml.hpp
+++ b/src/pugixml.hpp
@@ -1805,6 +1805,68 @@ namespace pugi
};
/**
+ * 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
*/
class PUGIXML_CLASS xpath_query