#include #include #include #include /*************************************************************************/ void ZKVTree::NodeCheckIn(Node* _node) { for (size_t i = 0; i < _node->Children.Size(); i++) NodeCheckIn(_node->Children[i]); _node->Children.Clear(); NodeAllocator.Deallocate(_node); } /*************************************************************************/ ZKVTree::ZKVTree() : RootNode() { RootNode.ParentNode = NULL; RootNode.Index = 0; } /*************************************************************************/ ZKVTree::ZKVTree( const ZKVTree& _other ) : RootNode() { RootNode.ParentNode = NULL; RootNode.Index = 0; for (ZKVTree::Iterator itr = _other.Begin(); itr != _other.End(); ++itr) { Put(itr, itr.GetValue()); } } /*************************************************************************/ ZKVTree::~ZKVTree() { for (size_t i = 0; i < RootNode.Children.Size(); i++) NodeCheckIn(RootNode.Children[i]); } /*************************************************************************/ ZKVTree& ZKVTree::operator = ( const ZKVTree& _other ) { Clear(); for (ZKVTree::Iterator itr = _other.Begin(); itr != _other.End(); ++itr) { Put(itr.GetPath(), itr.GetValue()); } return *this; } /*************************************************************************/ // This will convert to a full path, with subscripts everywhere and path separators. EXAMPLES: // // A.B[2].C.d -> A[0].B[2].C[0].d[0] // A.B[0][1] -> A[0].B[0].[1] // static bool ConvertPath(const ZString& _path, ZString& _out) { const ZString zeroSubscript("[0]"); size_t i = 0; size_t j = 0; size_t k = 0; //We are going to loop, looking for path separator and making sure we have subscripts and proper path separators while (i != ZSTL::InvalidPos) { // find the next path separator or the first occurrence of a nested array access j = ZStringAlgo::FindFirst(_path, ZKVTREE_PATH_SEPARATOR, i, _path.Length()); k = ZStringAlgo::FindSub(_path, i, _path.Length(), "][", 0, 2); // if a nested array access occurs if (k < j) { // append the first part of it, a path separators, and then process from there ZStringAlgo::Append(_out, _path, i, k + 1); _out.PushBack(ZKVTREE_PATH_SEPARATOR); i = k + 1; } else if (i == j) { // this means we are dealing with a node without a name (potentially first node) if (_out.Empty() || _out.Back() != ']') ZStringAlgo::Append(_out, zeroSubscript); _out.PushBack(ZKVTREE_PATH_SEPARATOR); i = j + 1; } else if (j != ZSTL::InvalidPos) { // found a substring between two path separators ZStringAlgo::Append(_out, _path, i, j); if (_out.Back() != ']') ZStringAlgo::Append(_out, zeroSubscript); _out.PushBack(ZKVTREE_PATH_SEPARATOR); i = j + 1; } else { // we have reached the end ZStringAlgo::Append(_out, _path, i, _path.Length()); if (_out.Empty() || _out.Back() != ']') ZStringAlgo::Append(_out, zeroSubscript); i = ZSTL::InvalidPos; } } // this method may be expanded later for path verification via regex or some such return true; } //This will insert a child node at the given index and increment the indices on the other child values static void InsertNode( ZKVTree::Node* _parent, ZKVTree::Node* _child, size_t _index ) { _parent->Children.Insert(_index, _child); for (size_t i = _index + 1; i < _parent->Children.Size(); i++) _parent->Children.Data()[i]->Index++; } //This will erase a child node at the given index and decrement the indices on the other child values static ZKVTree::Node* EraseChildNode( ZKVTree::Node* _parent, size_t _index ) { for (size_t i = _index + 1; i < _parent->Children.Size(); i++) _parent->Children.Data()[i]->Index--; return _parent->Children.Erase(_index); } /*************************************************************************/ // gets the value from a completed path (returned from ConvertFullPath) ZKVTree::Node* ZKVTree::GetFromPath(const ZString& _path) const { size_t i, j, k; size_t subscript; const Node* node = &RootNode; // path[i...j] is our path window (current value we are looking for) // path[subscript].nextpath // ^ ^ ^ // i j k // or // path.nextpath // ^ ^ // i j,k i = 0; j = 0; k = 0; //While we still have a node to look in while (node != NULL) { //See if we have reached the end if (i >= _path.Length()) break; subscript = 0; j = ZStringAlgo::FindFirst(_path, ZKVTREE_PATH_SEPARATOR, i, _path.Length()); //If we can't find a path separator, go to the end if (j == ZSTL::InvalidPos) j = _path.Length(); //See if we have found the node if (i >= j) break; k = j; //Determine if it has a subscript if (_path[j - 1] == ']') { j = ZStringAlgo::FindLast(_path, '[', i, j); //Make sure this is bounded correct if (j == ZSTL::InvalidPos || j < i) { node = NULL; break; } subscript = (size_t)ZStringAlgo::NumericInt(_path, j + 1, k - 1); } node = node->GetChildByName(_path, i, j, subscript); //Move up our path 'window' i = k + 1; } return const_cast(node); } /*************************************************************************/ ZKVTree::Node* ZKVTree::CreatePath(const ZString& _path) { size_t i, j, k; size_t subscript; Node* node = &RootNode; // path[i...j] is our path window (current value we are looking for) // path[subscript].nextpath // ^ ^ ^ // i j k // or // path.nextpath // ^ ^ // i j,k i = 0; j = 0; k = 0; // while we still have a node to look in while (node != NULL) { // see if we have reached the end if (i >= _path.Length()) { break; } subscript = 0; j = ZStringAlgo::FindFirst(_path, ZKVTREE_PATH_SEPARATOR, i, _path.Length()); //If we can't find a path separator, go to the end if (j == ZSTL::InvalidPos) { j = _path.Length(); } //See if we are done creating if (i == j) { break; } k = j; // determine if it has a subscript if (_path[j - 1] == ']') { j = ZStringAlgo::FindLast(_path, '[', i, j); // make sure this is bounded correct (if not, bail out) if (j == ZSTL::InvalidPos || j < i) { node = NULL; break; } subscript = (size_t)ZStringAlgo::NumericInt(_path, j + 1, k - 1); } Node* childNode = node->GetChildByName(_path, i, j, subscript); // create this node if it doesn't exist if (childNode == NULL) { // get the child count by name at the parent node size_t count = node->GetChildCountByName(_path, i, j); // see if the subscript at the end is the 'next' value (if not, bail out) if (count != subscript) { node = NULL; break; } // get a new node, set the parent node, name, index (leave out the value) childNode = NodeAllocator.Allocate(); childNode->ParentNode = node; childNode->Name.Resize(j - i); ZStringAlgo::Copy(childNode->Name, 0, childNode->Name.Length(), _path, i, j); if (count > 0) { childNode->Index = node->GetLastIndexByName(_path, i, j) + 1; // increment the index on the rest of the nodes for (size_t i = childNode->Index; i < node->Children.Size(); i++) node->Children[i]->Index++; node->Children.Insert(childNode->Index, childNode); } else { childNode->Index = node->Children.Size(); node->Children.PushBack(childNode); } } // set our new node node = childNode; // move up our 'path window' i = k + 1; } return node; } /*************************************************************************/ ZKVTree::Iterator ZKVTree::Add( const ZString& _name, const ZString& _value ) { return Add(End(), _name, _value); } /*************************************************************************/ ZKVTree::Iterator ZKVTree::Add( const ZString& _parent, const ZString& _name, const ZString& _value ) { ZString path; if (!ConvertPath(_parent, path)) return End(); Node* node = CreatePath(path); return Add(Iterator(node, this), _name, _value); } /*************************************************************************/ ZKVTree::Iterator ZKVTree::Add( const Iterator& _itr, const ZString& _name, const ZString& _value ) { ZKVTree::Node* parentNode = NULL; ZKVTree::Node* childNode = NULL; if (_itr.CurrentNode == NULL) { return End(); } if (_itr.KVTree == this) { parentNode = _itr.CurrentNode; // allocate a child node and set values childNode = NodeAllocator.Allocate(); childNode->ParentNode = parentNode; childNode->Name = _name; childNode->Value = _value; // determine if any other children of the same name exist (grouped on insert) size_t count = parentNode->GetChildCountByName(_name, 0, _name.Length()); if (count > 0) { childNode->Index = parentNode->GetLastIndexByName(_name, 0, _name.Length()) + 1; } else { childNode->Index = parentNode->Children.Size(); } InsertNode(parentNode, childNode, childNode->Index); return Iterator(childNode, this); } else { // if this is from another tree, just add at the path return Add(_itr.GetPath(), _name, _value); } } /*************************************************************************/ void ZKVTree::Clear() { for (size_t i = 0; i < RootNode.Children.Size(); i++) { NodeCheckIn(RootNode.Children.Data()[i]); } RootNode.Children.Clear(); } /*************************************************************************/ void ZKVTree::Erase( const ZString& _path ) { Erase(Find(_path)); } /*************************************************************************/ void ZKVTree::Erase( const Iterator& _itr ) { ZASSERT_RUNTIME(_itr.CurrentNode != NULL, "ZKVTree: Unable to erase value from provided iterator!"); ZASSERT_RUNTIME(_itr.CurrentNode != &RootNode, "ZKVTree: Unable to erase root node!"); if (_itr.KVTree == this) { NodeCheckIn(EraseChildNode(_itr.CurrentNode->ParentNode, _itr.CurrentNode->Index)); } else { Erase(_itr.GetPath()); } } /*************************************************************************/ ZKVTree::Iterator ZKVTree::Find( const ZString& _path ) const { ZString path; if (!ConvertPath(_path, path)) { return End(); } Node* node = GetFromPath(path); if (node == NULL) { return End(); } return ZKVTree::Iterator(node, const_cast(this)); } /*************************************************************************/ ZKVTree::Iterator ZKVTree::Find( const Iterator& _itr, const ZString& _path ) const { ZString path; if (!ConvertPath(_path, path)) { return End(); } Node* node = GetFromPath(_itr.GetPath() + ZKVTREE_PATH_SEPARATOR + path); if (node == NULL) { return End(); } return ZKVTree::Iterator(node, const_cast(this)); } /*************************************************************************/ const ZString& ZKVTree::Get( const ZString& _path ) const { ZKVTree::Iterator itr = Find(_path); ZASSERT_RUNTIME(itr != End(), "ZKVTree: No value mapped to path!"); return itr.GetValue(); } /*************************************************************************/ const ZString& ZKVTree::Get( const Iterator& _itr, const ZString& _path ) { ZKVTree::Iterator itr = Find(_itr, _path); ZASSERT_RUNTIME(itr != End(), "ZKVTree: No value mapped to path!"); return itr.GetValue(); } /*************************************************************************/ void ZKVTree::Merge( const ZKVTree& _other, bool _overwrite ) { for (ZKVTree::Iterator itr = _other.Begin(); itr != _other.End(); ++itr) { if (_overwrite) { Put(itr.GetPath(), itr.GetValue()); } else { ZKVTree::Iterator found = Find(itr.GetPath()); if (found == End()) { Put(itr.GetPath(), itr.GetValue()); } } } } /*************************************************************************/ ZKVTree::Iterator ZKVTree::Put( const ZString& _path, const ZString& _value ) { ZString path; if (!ConvertPath(_path, path)) return End(); // create the node at the path given (will return the existing node if there) Node* node = CreatePath(path); // if we were able to successfully create the node, set its value if (node != NULL) { node->Value = _value; return Iterator(node, this); } else return End(); } /*************************************************************************/ ZKVTree::Iterator ZKVTree::Put(const Iterator& _itr, const ZString& _value) { // ensure this iterator belongs to us if (_itr.KVTree == this) { // it does, just set the value _itr.CurrentNode->Value = _value; return _itr; } else { // it does not, just create a new value using the path variant return Put(_itr.GetPath(), _value); } } /*************************************************************************/ ZKVTree::Iterator ZKVTree::Begin() const { if (RootNode.Children.Empty()) { return End(); } else return ZKVTree::Iterator(const_cast(RootNode.Children.Front()), const_cast(this)); } /*************************************************************************/ const ZKVTree::Iterator ZKVTree::End() const { return ZKVTree::Iterator(const_cast(&RootNode), const_cast(this)); } /*************************************************************************/ static bool isInvalidKeyNameChar(const char _char) { return ( isspace(_char) != 0) || ( iscntrl(_char) != 0) || ( _char == ZKVTREE_PATH_SEPARATOR ) || ( _char == '[' ) || ( _char == ']' ); } bool ZKVTree::IsValidKeyName( const ZString& _name ) const { for (size_t i = 0; i < _name.Length(); i++) { if (isInvalidKeyNameChar( _name.Data()[i])) { return false; } } return true; }