ufe 5.5
Universal Front End is a DCC-agnostic component that will allow a DCC to browse and edit data in multiple data models
trie.imp.h
Go to the documentation of this file.
1#line 1 "S:/jenkins/workspace/ECP/ufe/ufe-full-python3.11-windows/ufe/include/trie.imp.h"
2#ifndef _ufeTrie_imp
3#define _ufeTrie_imp
4// =======================================================================
5// Copyright 2019 Autodesk, Inc. All rights reserved.
6//
7// This computer source code and related instructions and comments are the
8// unpublished confidential and proprietary information of Autodesk, Inc.
9// and are protected under applicable copyright and trade secret law. They
10// may not be disclosed to, copied or used by any third party without the
11// prior written consent of Autodesk, Inc.
12// =======================================================================
13
14#include "trie.h"
15#include "ufeAssert.h"
16
17#include <numeric>
18
20
21//==============================================================================
22// CLASS Ufe::TrieNode
23//==============================================================================
24
25template<typename T>
27 : fComponent(component), fParent(), fChildren(), fHasData(false),
28 fData()
29{}
30
31template<typename T>
33{}
34
35template<typename T>
36void TrieNode<T>::add(const Ptr& child)
37{
38 // Child must not exist yet.
39 UFE_ASSERT_MSG(fChildren.find(child->component()) == fChildren.end(),
40 "Child trie node already exists.");
41
42 fChildren[child->component()] = child;
43 // Go through this pointer to prevent "no arguments to ‘shared_from_this’
44 // that depend on a template parameter" error.
45 child->setParent(this->shared_from_this());
46}
47
48template<typename T>
49void TrieNode<T>::remove(const Ptr& child)
50{
51 UFE_ASSERT_COMPILED(typename Children::size_type nbErased = )
52 fChildren.erase(child->component());
53 UFE_ASSERT_MSG(nbErased == 1, "Child trie node does not exist.");
54 child->setParent(Ptr());
55}
56
57template<typename T>
59{
60 fComponent = PathComponent();
61 fParent = ParentPtr();
62 fChildren.clear();
63 fHasData = false;
64}
65
66template<typename T>
68{
69 return fParent.lock();
70}
71
72template<typename T>
73bool TrieNode<T>::contains(const PathComponent& component) const
74{
75 return fChildren.find(component) != fChildren.end();
76}
77
78template<typename T>
80{
81 typename Children::const_iterator const found = fChildren.find(component);
82 return found == fChildren.end() ? Ptr() : found->second;
83}
84
85template<typename T>
86std::vector<PathComponent> TrieNode<T>::childrenComponents() const
87{
88 std::vector<PathComponent> ret;
89 ret.reserve(fChildren.size());
90 for (auto c : fChildren)
91 {
92 ret.emplace_back(c.first);
93 }
94 return ret;
95}
96
97template<typename T>
99{
100 std::size_t sz = size();
101
102 // If this node has data or it's a leaf node then return
103 // before incrementing. The common ancestor is our
104 // parent.
105 //
106 // In practice both of these conditions should be the same,
107 // but we can call this function with an empty trie in which
108 // case we will have size 0 and no data in the empty root node.
109 if ( hasData() || 0 == sz) {
110 return depth;
111 }
112
113 depth++;
114
115 // If this node has multiple children then it is the
116 // common ancestor.
117 if (1 < sz) {
118 return depth;
119 }
120
121 // Else we need to keep going...
122 typename Children::const_iterator const next = fChildren.begin();
123 return next->second->closestCommonAncestor(depth);
124}
125
126template<typename T>
127std::size_t TrieNode<T>::size() const
128{
129 return fChildren.size();
130}
131
132template<typename T>
133std::size_t TrieNode<T>::treeSize() const
134{
135 return std::accumulate(
136 fChildren.begin(), fChildren.end(), std::size_t(1),
137 [](std::size_t treeSize, const typename Children::value_type& child) {
138 return treeSize+child.second->treeSize();
139 });
140}
141
142template<typename T>
144{
145 return size() == 0;
146}
147
148template<typename T>
150{
151 return fComponent;
152}
153
154template<typename T>
155void TrieNode<T>::rename(const PathComponent& component)
156{
157 UFE_ASSERT_MSG(parent()->contains(fComponent),
158 "Cannot rename child trie node, not found in parent.");
159 auto self = this->shared_from_this();
160 auto p = parent();
161 p->remove(self);
162 fComponent = component;
163 p->add(self);
164}
165
166template<typename T>
167void TrieNode<T>::move(const PathComponent& component, const Ptr& newParent)
168{
169 auto oldParent = parent();
170 if (newParent == oldParent)
171 return;
172 UFE_ASSERT_MSG(!newParent->contains(component), "Cannot reparent child trie node, new component already exists in new parent.");
173 UFE_ASSERT_MSG(oldParent->contains(fComponent), "Cannot reparent child trie node, not found in parent.");
174 auto self = this->shared_from_this();
175 oldParent->remove(self);
176 fComponent = component;
177 newParent->add(self);
178}
179
180template<typename T>
181void TrieNode<T>::setData(const T& data)
182{
183 fHasData = true;
184 fData = data;
185}
186
187template<typename T>
189{
190 fHasData = false;
191}
192
193template<typename T>
194const T& TrieNode<T>::data() const
195{
196 return fData;
197}
198
199template<typename T>
201{
202 return fHasData;
203}
204
205template<typename T>
207{
208 fParent = ParentPtr(parent);
209}
210
211//==============================================================================
212// CLASS Ufe::Trie
213//==============================================================================
214
215template<typename T>
216Trie<T>::Trie() : fRoot(std::make_shared< TrieNode<T> >())
217{}
218
219template<typename T>
220Trie<T>::Trie(Trie&& rhs) : fRoot(std::move(rhs.fRoot))
221{
222 rhs.fRoot = std::make_shared< TrieNode<T> >();
223}
224
225template<typename T>
227{
228 fRoot = std::move(rhs.fRoot);
229 rhs.fRoot = std::make_shared< TrieNode<T> >();
230 return *this;
231}
232
233template<typename T>
235{
236 return fRoot;
237}
238
239template<typename T>
241{
242 // Walk down the path inside the tree, adding trie nodes if required.
243 typename TrieNode<T>::Ptr trieNode = root();
244 for (const PathComponent& c : path) {
245 typename TrieNode<T>::Ptr child = (*trieNode)[c];
246 if (!child) {
247 child = std::make_shared< TrieNode<T> >(c);
248 trieNode->add(child);
249 }
250 trieNode = child;
251 }
252
253 return trieNode;
254}
255
256template<typename T>
257typename TrieNode<T>::Ptr Trie<T>::add(const Path& path, const T& data)
258{
259 typename TrieNode<T>::Ptr trieNode = createNode(path);
260
261 // Last trie node gets the data.
262 trieNode->setData(data);
263 return trieNode;
264}
265
266template<typename T>
268{
269 // Walk down the path inside the tree.
270 typename TrieNode<T>::Ptr trieNode = node(path);
271 if (trieNode && trieNode->hasData())
272 return trieNode;
273 return typename TrieNode<T>::Ptr();
274}
275
276template<typename T>
278{
279 // Walk down the path inside the tree.
280 typename TrieNode<T>::Ptr trieNode = root();
281 for (const PathComponent& c : path) {
282 typename TrieNode<T>::Ptr child = (*trieNode)[c];
283 if (!child) {
284 return typename TrieNode<T>::Ptr();
285 }
286 trieNode = child;
287 }
288 return trieNode;
289}
290
291template<typename T>
292bool Trie<T>::contains(const Path& path) const
293{
294 return bool(find(path));
295}
296
297template<typename T>
298template<bool INCLUDE_ANCESTOR>
299bool Trie<T>::containsDescendantHelper(const Path& ancestorPath) const
300{
301 // Algorithm summary: walk down the path tree to the end of the argument
302 // path. If that trie node has children, a descendant is in the trie.
303
304 // By definition, an empty path has no descendants.
305 if (ancestorPath.empty()) {
306 return false;
307 }
308
309 // Walk down the complete path inside the tree, if possible.
310 typename TrieNode<T>::Ptr trieNode = root();
311 for (const PathComponent& c : ancestorPath) {
312 typename TrieNode<T>::Ptr child = (*trieNode)[c];
313 // If we've reached a trie leaf node before the end of our path, there
314 // cannot be any descendants in the trie.
315 if (!child) {
316 return false;
317 }
318 trieNode = child;
319 }
320 // We reached the end of the argument path. Whether the trieNode we
321 // reached has data or not, return true if it has descendants. To
322 // implement a containsDescendantInclusive, which would include the
323 // argument path, we simply return true: at this point we've reached
324 // either an internal node, which by definition has children, or a leaf
325 // node with no descendants, which by definition has data.
326 return INCLUDE_ANCESTOR ? true : !trieNode->empty();
327}
328
329template<typename T>
330bool Trie<T>::containsDescendant(const Path& ancestorPath) const
331{
332 return containsDescendantHelper<false>(ancestorPath);
333}
334
335template<typename T>
336bool Trie<T>::containsDescendantInclusive(const Path& ancestorPath) const
337{
338 return containsDescendantHelper<true>(ancestorPath);
339}
340
341template<typename T>
342template<bool INCLUDE_DESCENDANT>
343bool Trie<T>::containsAncestorHelper(const Path& descendantPath) const
344{
345 // Algorithm summary: walk down the path tree trying to find a node
346 // with data.
347
348 // By definition, an empty path has no ancestors.
349 if (descendantPath.empty()) {
350 return false;
351 }
352
353 // Walk down the path inside the tree. As soon as we find a trieNode with
354 // data, return true.
355 typename TrieNode<T>::Ptr trieNode = root();
356 auto pathEndIt = descendantPath.cend();
357 // When we are not including descendentPath then finding the last PathComponent of
358 // descendentPath in the trie does not count. Set up the iterator so that we don't
359 // check the final PathComponent.
360 if (!INCLUDE_DESCENDANT) {
361 pathEndIt = std::prev(pathEndIt);
362 }
363 for (auto pathIt = descendantPath.cbegin(); pathIt != pathEndIt; ++pathIt) {
364 const PathComponent& c = *pathIt;
365 typename TrieNode<T>::Ptr child = (*trieNode)[c];
366 // If we've reached a trie leaf node before the end of our path, there
367 // is no trie node with data as ancestor of the path.
368 if (!child) {
369 return false;
370 }
371 trieNode = child;
372
373 // Found a trieNode with data.
374 if (trieNode->hasData()) {
375 return true;
376 }
377 }
378 // We reached the end of the parent path without returning true, therefore
379 // there are no ancestors.
380 return false;
381}
382
383template<typename T>
384bool Trie<T>::containsAncestor(const Path& descendantPath) const
385{
386 return containsAncestorHelper<false>(descendantPath);
387}
388
389template<typename T>
390bool Trie<T>::containsAncestorInclusive(const Path& descendantPath) const
391{
392 return containsAncestorHelper<true>(descendantPath);
393}
394
395template<typename T>
397{
398 return root()->closestCommonAncestor(-1);
399}
400
401template<typename T>
403{
404 typename TrieNode<T>::Ptr found = find(p);
405 if (!found) {
406 return found;
407 }
408
409 // First, clear the data from the trie node.
410 UFE_ASSERT_MSG(found->hasData(), "Trie node has no associated data.");
411 found->clearData();
412
413 // Next, clean up trie if required. If trie node has no children and
414 // no data, remove it, and recurse up to its parent.
415 cleanUpNode(found);
416
417 return found;
418}
419
420template<typename T>
421void Trie<T>::cleanUpNode(const typename TrieNode<T>::Ptr& node)
422{
423 typename TrieNode<T>::Ptr child = node;
424 while (child->empty() && !child->hasData() && child != root()) {
425 typename TrieNode<T>::Ptr parent = child->parent();
426 parent->remove(child);
427 child = parent;
428 }
429}
430
431
432template<typename T>
433typename TrieNode<T>::Ptr Trie<T>::move(const Path& oldPath, const Path& newPath)
434{
435 typename TrieNode<T>::Ptr newParentNode = createNode(newPath.pop());
436 typename TrieNode<T>::Ptr trieNode = node(oldPath);
437 UFE_ASSERT_MSG(trieNode, "Trie does not contain path");
438 typename TrieNode<T>::Ptr oldParentNode = trieNode->parent();
439
440 trieNode->move(newPath.back(), newParentNode);
441 cleanUpNode(oldParentNode);
442
443 return trieNode;
444}
445
446template<typename T>
448{
449 root()->clear();
450}
451
452template<typename T>
453std::size_t Trie<T>::size() const
454{
455 return root()->treeSize();
456}
457
458template<typename T>
459bool Trie<T>::empty() const
460{
461 return root()->empty();
462}
463
464}
465
466#endif /* _ufeTrie_imp */
Constant string representation with fixed space and O(1) comparison.
Definition: pathComponent.h:33
Identify an object or 3D path in the scene.
Definition: path.h:38
bool empty() const
PathComponent back() const
Components::const_iterator cbegin() const
Iteration interface on PathComponents.
Path pop() const
Components::const_iterator cend() const
Node for Universal Front End trie.
Definition: trie.h:128
bool containsAncestor(const Path &path) const
Definition: trie.imp.h:384
TrieNode< T >::Ptr add(const Path &path, const T &data)
Definition: trie.imp.h:257
TrieNode< T >::Ptr node(const Path &path) const
Definition: trie.imp.h:277
bool empty() const
Definition: trie.imp.h:459
bool containsDescendant(const Path &path) const
Definition: trie.imp.h:330
bool containsAncestorInclusive(const Path &path) const
Definition: trie.imp.h:390
TrieNode< T >::Ptr find(const Path &path) const
Definition: trie.imp.h:267
void clear()
Definition: trie.imp.h:447
Trie & operator=(const Trie &)=delete
TrieNode< T >::Ptr createNode(const Path &path)
Definition: trie.imp.h:240
bool containsDescendantHelper(const Path &path) const
Definition: trie.imp.h:299
int closestCommonAncestor() const
Definition: trie.imp.h:396
std::size_t size() const
Definition: trie.imp.h:453
void cleanUpNode(const typename TrieNode< T >::Ptr &node)
Definition: trie.imp.h:421
bool contains(const Path &path) const
Definition: trie.imp.h:292
TrieNode< T >::Ptr move(const Path &oldPath, const Path &newPath)
Definition: trie.imp.h:433
TrieNode< T >::Ptr root() const
Definition: trie.imp.h:234
TrieNode< T >::Ptr remove(const Path &)
Definition: trie.imp.h:402
bool containsAncestorHelper(const Path &path) const
Definition: trie.imp.h:343
bool containsDescendantInclusive(const Path &path) const
Definition: trie.imp.h:336
void clearData()
Definition: trie.imp.h:188
void remove(const Ptr &child)
Definition: trie.imp.h:49
void add(const Ptr &child)
Definition: trie.imp.h:36
bool empty() const
Definition: trie.imp.h:143
void rename(const PathComponent &component)
Definition: trie.imp.h:155
std::shared_ptr< TrieNode > Ptr
Definition: trie.h:45
void setParent(Ptr parent)
Definition: trie.imp.h:206
const T & data() const
Definition: trie.imp.h:194
void move(const PathComponent &component, const Ptr &newParent)
Definition: trie.imp.h:167
bool hasData() const
Definition: trie.imp.h:200
void setData(const T &data)
Definition: trie.imp.h:181
std::size_t treeSize() const
Definition: trie.imp.h:133
std::weak_ptr< TrieNode > ParentPtr
Definition: trie.h:101
int closestCommonAncestor(int depth) const
Definition: trie.imp.h:98
std::size_t size() const
Definition: trie.imp.h:127
std::vector< PathComponent > childrenComponents() const
Definition: trie.imp.h:86
Ptr operator[](const PathComponent &child) const
Definition: trie.imp.h:79
void clear()
Definition: trie.imp.h:58
PathComponent component() const
Definition: trie.imp.h:149
Ptr parent() const
Definition: trie.imp.h:67
bool contains(const PathComponent &child) const
Definition: trie.imp.h:73
std::shared_ptr< ObservableSelection > Ptr
Path path(const std::string &pathString)
Definition: path.h:201
#define UFE_NS_DEF
Definition: ufe.h:35
#define UFE_ASSERT_MSG(EXPR, MSG)
Definition: ufeAssert.h:34
#define UFE_ASSERT_COMPILED(CODE)
Definition: ufeAssert.h:37