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