nSearchTreeNode BasicSearchTree::SplitBranch(nSearchTreeNode n,size_t depth) { if (!n || !m_Nodes[n] || m_Nodes[n]->GetDepth()==depth) return n; // for !n it returns the rootnode // for !m_Nodes[n], it fails by returning n. // for m_Nodes[n]->GetDepth()==depth, it's a special case (given position is a node) // so we just return n. SearchTreeNode* child = m_Nodes[n]; nSearchTreeNode old_parent = child->GetParent(); // Create new node "middle", add it to old_parent in place of child. // Calculate the parent offset and the new labels' parameters. size_t parent_offset = depth - child->GetLabelStartDepth(); nSearchTreeLabel labelno = child->GetLabelNo(); unsigned int oldlabelstart = child->GetLabelStart(); unsigned int oldlabellen = child->GetLabelLen(); unsigned int middle_start = oldlabelstart; unsigned int middle_len = parent_offset; unsigned int child_start = middle_start + middle_len; unsigned int child_len = oldlabellen - middle_len; wxChar middle_char = m_Labels[labelno][middle_start]; wxChar child_char = m_Labels[labelno][child_start]; // Now we're ready to create the middle node and update accordingly SearchTreeNode* newnode = CreateNode(depth,old_parent,labelno,middle_start,middle_len); m_Nodes.push_back(newnode); nSearchTreeNode middle = m_Nodes.size() - 1; // Add child to middle child->SetParent(middle); child->SetLabel(labelno,child_start,child_len); child->RecalcDepth(this); newnode->m_Children[child_char]=n; child->UpdateItems(this); // Add middle to old_parent m_Nodes[old_parent]->m_Children[middle_char]=middle; return middle; }
size_t BasicSearchTree::FindMatches(const wxString& s, std::set<size_t>& result, bool caseSensitive, bool is_prefix) { // NOTE: Current algorithm is suboptimal, but certainly it's much better // than an exhaustive search. result.clear(); wxString s2,curcmp,s3; SearchTreeNode* curnode = 0; BasicSearchTreeIterator it(this); SearchTreeItemsMap::iterator it2; bool matches; if (!caseSensitive) s2 = s.Lower(); else s2 = s; while (!it.Eof()) { matches = false; curnode = m_Nodes[*it]; if (!curnode) break; // Error! Found a NULL Node if (curnode->m_Depth < s.length()) { // Node's string is shorter than S, therefore it CANNOT be a suffix // However, we can test if it does NOT match the current string. if (!curnode->m_Depth) matches = true; else { s3 = s2.substr(curnode->GetLabelStartDepth(),curnode->GetLabelLen()); curcmp = curnode->GetLabel(this); if (!caseSensitive) curcmp = curcmp.Lower(); matches = (s3 == curcmp); } } else { if (curnode->GetLabelStartDepth() >= s2.length()) matches = is_prefix; else { s3 = s2.substr(curnode->GetLabelStartDepth()); curcmp = curnode->GetLabel(this); if (!caseSensitive) curcmp = curcmp.Lower(); matches = curcmp.StartsWith(s3); } if (matches) { // Begin items addition if (!is_prefix) { // Easy part: Only one length to search it2 = curnode->m_Items.find(s2.length()); if (it2 != curnode->m_Items.end()) result.insert(it2->second); } else { for (it2 = curnode->m_Items.lower_bound(s2.length()); it2 != curnode->m_Items.end(); ++it2) { result.insert(it2->second); } } matches = is_prefix; // End items addition } } it.FindNext(matches); } return result.size(); }
SearchTreePoint BasicSearchTree::AddNode(const wxString& s, nSearchTreeNode nparent) { SearchTreePoint result(0,0); nSearchTreeNode n = 0; bool found = this->FindNode(s,nparent,&result); if (!found) { // Create new node // If necessary, split the edge with a new node 'middle' // If result is exactly a node, middle will be just result.n. nSearchTreeNode middle = SplitBranch(result.n,result.depth); // Now add the node to the middle node SearchTreeNode* newnode; wxString newlabel; if (m_Nodes[middle]->IsLeaf()) { // If it's a leaf node, just extend the label and change // the new node's depth to reflect the changes. n = middle; newnode = m_Nodes[n]; // We take the part of the string that corresponds to node middle. // Since s starts at nparent's depth, we just get the difference and // it will be the position inside the string. newlabel = s.substr(m_Nodes[middle]->GetLabelStartDepth() - m_Nodes[nparent]->GetDepth()); // Modify the leaf node's label to extend the point // Since it's a leaf node, we just concatenate to the current label the missing part. unsigned int oldlen = newnode->GetDepth() - newnode->GetLabelStartDepth(); if (oldlen < newlabel.length()) // Safety check against segfaults { m_Labels[newnode->GetLabelNo()] << newlabel.substr(oldlen); m_Labels[newnode->GetLabelNo()].Shrink(); } newnode->SetLabel(newnode->GetLabelNo(),newnode->GetLabelStart(),newlabel.length()); newnode->RecalcDepth(this); } else { // Get the string's depth. This will be the depth of our new leaf node. size_t newdepth = m_Nodes[nparent]->GetDepth() + s.length(); // start = middle's depth - nparent's depth. newlabel = s.substr(m_Nodes[middle]->GetDepth() - m_Nodes[nparent]->GetDepth()); // Now we create the new label to be accessed by the leaf node "newnode". m_Labels.push_back(newlabel); nSearchTreeLabel nlabel = m_Labels.size() - 1; m_Labels[nlabel].Shrink(); // Finally, we create the new node and link it to "middle". newnode = CreateNode(newdepth,middle,nlabel,0,newlabel.length()); m_Nodes.push_back(newnode); n = m_Nodes.size()-1; m_Nodes[middle]->m_Children[newlabel[0u]]=n; } result.n = n; result.depth = newnode->GetDepth(); } return result; }