1 // TortoiseSVN - a Windows shell extension for easy version control
\r
3 // Copyright (C) 2007-2008 - TortoiseSVN
\r
5 // This program is free software; you can redistribute it and/or
\r
6 // modify it under the terms of the GNU General Public License
\r
7 // as published by the Free Software Foundation; either version 2
\r
8 // of the License, or (at your option) any later version.
\r
10 // This program is distributed in the hope that it will be useful,
\r
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
\r
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
\r
13 // GNU General Public License for more details.
\r
15 // You should have received a copy of the GNU General Public License
\r
16 // along with this program; if not, write to the Free Software Foundation,
\r
17 // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
\r
21 #include "SearchPathTree.h"
\r
22 #include "FullGraphNode.h"
\r
25 #define new DEBUG_NEW
\r
27 static char THIS_FILE[] = __FILE__;
\r
30 void CSearchPathTree::DeLink()
\r
35 previous->next = next;
\r
37 next->previous = previous;
\r
39 if (parent->firstChild == this)
\r
40 parent->firstChild = next;
\r
41 if (parent->lastChild == this)
\r
42 parent->lastChild = previous;
\r
47 void CSearchPathTree::Link (CSearchPathTree* newParent)
\r
49 assert (parent == NULL);
\r
50 assert (newParent != NULL);
\r
52 // ensure that for every cached path element hierarchy level
\r
53 // there is a node in the tree hierarchy
\r
55 index_t pathID = path.GetBasePath().GetIndex();
\r
56 index_t parentPathID = path.IsFullyCachedPath()
\r
57 ? path.GetDictionary()->GetParent (pathID)
\r
60 if (newParent->path.GetBasePath().GetIndex() < parentPathID)
\r
62 CDictionaryBasedPath parentPath ( path.GetDictionary()
\r
64 newParent = new CSearchPathTree ( CDictionaryBasedTempPath (parentPath)
\r
65 , (revision_t)NO_REVISION
\r
71 // insert into parent's child list ordered by pathID
\r
73 // O(1) insertion in most cases
\r
75 if (parent->firstChild == NULL)
\r
79 parent->firstChild = this;
\r
80 parent->lastChild = this;
\r
82 else if (parent->lastChild->path.GetBasePath().GetIndex() <= pathID)
\r
84 // insert at the end
\r
86 previous = parent->lastChild;
\r
88 parent->lastChild = this;
\r
89 previous->next = this;
\r
91 else if (parent->firstChild->path.GetBasePath().GetIndex() >= pathID)
\r
93 // insert at the beginning
\r
95 next = parent->firstChild;
\r
97 parent->firstChild = this;
\r
98 next->previous = this;
\r
102 assert (parent->firstChild != parent->lastChild);
\r
104 // scan for insertion position
\r
106 CSearchPathTree* node = parent->firstChild;
\r
107 while (node->path.GetBasePath().GetIndex() < pathID)
\r
112 previous = node->previous;
\r
115 previous->next = this;
\r
116 node->previous = this;
\r
120 // construction / destruction
\r
122 CSearchPathTree::CSearchPathTree (const CPathDictionary* dictionary)
\r
123 : path (dictionary, std::string())
\r
124 , startRevision ((revision_t)NO_REVISION)
\r
127 , firstChild (NULL)
\r
134 CSearchPathTree::CSearchPathTree ( const CDictionaryBasedTempPath& path
\r
135 , revision_t startrev
\r
136 , CSearchPathTree* parent)
\r
138 , startRevision (startrev)
\r
141 , firstChild (NULL)
\r
149 CSearchPathTree::~CSearchPathTree()
\r
151 while (firstChild != NULL)
\r
158 // add a node for the given path and rev. to the tree
\r
160 CSearchPathTree* CSearchPathTree::Insert ( const CDictionaryBasedTempPath& path
\r
161 , revision_t startrev)
\r
163 assert (startrev != NO_REVISION);
\r
165 // exact match (will happen on root node only)?
\r
167 if (this->path == path)
\r
169 startRevision = startrev;
\r
173 // (partly or fully) overlap with an existing child?
\r
175 CDictionaryBasedPath cachedPath = path.GetBasePath();
\r
176 for (CSearchPathTree* child = firstChild; child != NULL; child = child->next)
\r
178 CDictionaryBasedTempPath commonPath
\r
179 = child->path.GetCommonRoot (path);
\r
181 if (commonPath != this->path)
\r
183 if (child->path == path)
\r
185 // there is already a node for the exact same path
\r
186 // -> use it, if unused so far; append a new node otherwise
\r
188 if (child->startRevision == NO_REVISION)
\r
189 child->startRevision = startrev;
\r
191 return new CSearchPathTree (path, startrev, this);
\r
195 // the path is a (true) sub-node of the child
\r
197 return child->Insert (path, startrev);
\r
202 // no overlap with any existing node
\r
203 // -> create a new child
\r
205 return new CSearchPathTree (path, startrev, this);
\r
208 void CSearchPathTree::Remove()
\r
210 startRevision = revision_t (NO_REVISION);
\r
213 CSearchPathTree* node = this;
\r
214 while (node->IsEmpty() && (node->parent != NULL))
\r
216 CSearchPathTree* temp = node;
\r
217 node = node->parent;
\r
223 // there is a new revision entry for this path
\r
225 void CSearchPathTree::ChainEntries (CFullGraphNode* entry)
\r
227 assert (entry != NULL);
\r
230 startRevision = max (startRevision, entry->GetRevision());
\r
233 // return true for active paths that don't have a revEntry for this revision
\r
235 bool CSearchPathTree::YetToCover (revision_t revision) const
\r
238 && ((lastEntry == NULL) || (lastEntry->GetRevision() < revision));
\r
241 // return next node in pre-order
\r
243 CSearchPathTree* CSearchPathTree::GetPreOrderNext (CSearchPathTree* lastNode)
\r
245 if (firstChild != NULL)
\r
248 // there is no sub-tree
\r
250 return GetSkipSubTreeNext (lastNode);
\r
253 // return next node in pre-order but skip this sub-tree
\r
255 CSearchPathTree* CSearchPathTree::GetSkipSubTreeNext (CSearchPathTree* lastNode)
\r
257 CSearchPathTree* result = this;
\r
258 while ((result != lastNode) && (result->next == NULL))
\r
259 result = result->parent;
\r
261 return result == lastNode
\r
266 // find sub-tree of pathID
\r
267 // (return NULL if there is no such node)
\r
269 CSearchPathTree* CSearchPathTree::FindCommonParent (index_t pathID)
\r
271 index_t nodePathID = path.GetBasePath().GetIndex();
\r
273 // collect all path elements to find *below* this one
\r
275 index_t pathToFind[MAX_PATH];
\r
278 while ((pathID != NO_INDEX) && (pathID > nodePathID))
\r
280 pathToFind[index] = pathID;
\r
282 assert (index < MAX_PATH);
\r
284 pathID = path.GetDictionary()->GetParent (pathID);
\r
287 // start search at *this node
\r
289 CSearchPathTree* node = this;
\r
290 CSearchPathTree* oldNode = node;
\r
292 while (node != NULL)
\r
294 // follow the desired path until it hits or passes the node
\r
296 while ((index != 0) && (nodePathID > pathID))
\r
297 pathID = pathToFind[--index];
\r
299 // scan sibling branches for a match, if this node wasn't one.
\r
300 // Since all are on the *same* level, either
\r
301 // * one must match
\r
302 // * nodePathID < pathID (i.e. node is a true child)
\r
303 // * path not found
\r
305 while ((nodePathID < pathID) && (node->next != NULL))
\r
308 nodePathID = node->path.GetBasePath().GetIndex();
\r
311 // end of search or not in this sub-tree?
\r
313 if ((index == 0) || (nodePathID != pathID))
\r
316 // descend one level
\r
318 pathID = pathToFind[--index];
\r
321 node = node->firstChild;
\r
323 nodePathID = node->path.GetBasePath().GetIndex();
\r