ufe  4.2
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.10-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 
19 UFE_NS_DEF {
20 
21 //==============================================================================
22 // CLASS Ufe::TrieNode
23 //==============================================================================
24 
25 template<typename T>
27  : fComponent(component), fParent(), fChildren(), fHasData(false),
28  fData()
29 {}
30 
31 template<typename T>
33 {}
34 
35 template<typename T>
36 void 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 
48 template<typename T>
49 void 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 
57 template<typename T>
59 {
60  fComponent = PathComponent();
61  fParent = ParentPtr();
62  fChildren.clear();
63  fHasData = false;
64 }
65 
66 template<typename T>
68 {
69  return fParent.lock();
70 }
71 
72 template<typename T>
73 bool TrieNode<T>::contains(const PathComponent& component) const
74 {
75  return fChildren.find(component) != fChildren.end();
76 }
77 
78 template<typename T>
79 typename TrieNode<T>::Ptr TrieNode<T>::operator[](const PathComponent& component) const
80 {
81  typename Children::const_iterator const found = fChildren.find(component);
82  return found == fChildren.end() ? Ptr() : found->second;
83 }
84 
85 template<typename T>
86 std::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 
97 template<typename T>
98 int TrieNode<T>::closestCommonAncestor( int depth ) const
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 
126 template<typename T>
127 std::size_t TrieNode<T>::size() const
128 {
129  return fChildren.size();
130 }
131 
132 template<typename T>
133 std::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 
142 template<typename T>
143 bool TrieNode<T>::empty() const
144 {
145  return size() == 0;
146 }
147 
148 template<typename T>
150 {
151  return fComponent;
152 }
153 
154 template<typename T>
155 void 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 
166 template<typename T>
167 void 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 
180 template<typename T>
181 void TrieNode<T>::setData(const T& data)
182 {
183  fHasData = true;
184  fData = data;
185 }
186 
187 template<typename T>
189 {
190  fHasData = false;
191 }
192 
193 template<typename T>
194 const T& TrieNode<T>::data() const
195 {
196  return fData;
197 }
198 
199 template<typename T>
201 {
202  return fHasData;
203 }
204 
205 template<typename T>
207 {
208  fParent = ParentPtr(parent);
209 }
210 
211 //==============================================================================
212 // CLASS Ufe::Trie
213 //==============================================================================
214 
215 template<typename T>
216 Trie<T>::Trie() : fRoot(std::make_shared< TrieNode<T> >())
217 {}
218 
219 template<typename T>
220 Trie<T>::Trie(Trie&& rhs) : fRoot(std::move(rhs.fRoot))
221 {
222  rhs.fRoot = std::make_shared< TrieNode<T> >();
223 }
224 
225 template<typename T>
227 {
228  fRoot = std::move(rhs.fRoot);
229  rhs.fRoot = std::make_shared< TrieNode<T> >();
230  return *this;
231 }
232 
233 template<typename T>
235 {
236  return fRoot;
237 }
238 
239 template<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 
256 template<typename T>
257 typename 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 
266 template<typename T>
267 typename TrieNode<T>::Ptr Trie<T>::find(const Path& path) const
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 
276 template<typename T>
277 typename TrieNode<T>::Ptr Trie<T>::node(const Path& path) const
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 
291 template<typename T>
292 bool Trie<T>::contains(const Path& path) const
293 {
294  return bool(find(path));
295 }
296 
297 template<typename T>
298 template<bool INCLUDE_ANCESTOR>
299 bool 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 
329 template<typename T>
330 bool Trie<T>::containsDescendant(const Path& ancestorPath) const
331 {
332  return containsDescendantHelper<false>(ancestorPath);
333 }
334 
335 template<typename T>
336 bool Trie<T>::containsDescendantInclusive(const Path& ancestorPath) const
337 {
338  return containsDescendantHelper<true>(ancestorPath);
339 }
340 
341 template<typename T>
342 template<bool INCLUDE_DESCENDANT>
343 bool 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 
383 template<typename T>
384 bool Trie<T>::containsAncestor(const Path& descendantPath) const
385 {
386  return containsAncestorHelper<false>(descendantPath);
387 }
388 
389 template<typename T>
390 bool Trie<T>::containsAncestorInclusive(const Path& descendantPath) const
391 {
392  return containsAncestorHelper<true>(descendantPath);
393 }
394 
395 template<typename T>
397 {
398  return root()->closestCommonAncestor(-1);
399 }
400 
401 template<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 
420 template<typename T>
421 void 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 
432 template<typename T>
433 typename 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 
446 template<typename T>
448 {
449  root()->clear();
450 }
451 
452 template<typename T>
453 std::size_t Trie<T>::size() const
454 {
455  return root()->treeSize();
456 }
457 
458 template<typename T>
459 bool Trie<T>::empty() const
460 {
461  return root()->empty();
462 }
463 
464 }
465 
466 #endif /* _ufeTrie_imp */
void setData(const T &data)
Definition: trie.imp.h:181
TrieNode< T >::Ptr node(const Path &path) const
Definition: trie.imp.h:277
bool containsDescendantHelper(const Path &path) const
Definition: trie.imp.h:299
void clear()
Definition: trie.imp.h:58
void cleanUpNode(const typename TrieNode< T >::Ptr &node)
Definition: trie.imp.h:421
std::size_t treeSize() const
Definition: trie.imp.h:133
Components::const_iterator cbegin() const
Iteration interface on PathComponents.
bool empty() const
bool containsAncestor(const Path &path) const
Definition: trie.imp.h:384
void remove(const Ptr &child)
Definition: trie.imp.h:49
std::shared_ptr< ObservableSelection > Ptr
bool containsAncestorInclusive(const Path &path) const
Definition: trie.imp.h:390
Constant string representation with fixed space and O(1) comparison.
Definition: pathComponent.h:33
Components::const_iterator cend() const
Iteration interface on PathComponents.
Definition: path.h:197
const T & data() const
Definition: trie.imp.h:194
Trie & operator=(const Trie &)=delete
std::vector< PathComponent > childrenComponents() const
Definition: trie.imp.h:86
bool containsAncestorHelper(const Path &path) const
Definition: trie.imp.h:343
bool contains(const Path &path) const
Definition: trie.imp.h:292
void setParent(Ptr parent)
Definition: trie.imp.h:206
PathComponent component() const
Definition: trie.imp.h:149
Node for Universal Front End trie.
#define UFE_ASSERT_MSG(EXPR, MSG)
Definition: ufeAssert.h:34
bool containsDescendantInclusive(const Path &path) const
Definition: trie.imp.h:336
bool containsDescendant(const Path &path) const
Definition: trie.imp.h:330
void rename(const PathComponent &component)
Definition: trie.imp.h:155
void add(const Ptr &child)
Definition: trie.imp.h:36
Identify an object or 3D path in the scene.
Definition: path.h:37
int closestCommonAncestor() const
Definition: trie.imp.h:396
std::size_t size() const
Definition: trie.imp.h:127
void clearData()
Definition: trie.imp.h:188
bool contains(const PathComponent &child) const
Definition: trie.imp.h:73
PathComponent back() const
Ptr operator[](const PathComponent &child) const
Definition: trie.imp.h:79
TrieNode< T >::Ptr remove(const Path &)
Definition: trie.imp.h:402
#define UFE_NS_DEF
Definition: ufe.h:35
Ptr parent() const
Definition: trie.imp.h:67
bool hasData() const
Definition: trie.imp.h:200
TrieNode< T >::Ptr root() const
Definition: trie.imp.h:234
void move(const PathComponent &component, const Ptr &newParent)
Definition: trie.imp.h:167
bool empty() const
Definition: trie.imp.h:143
std::size_t size() const
Definition: trie.imp.h:453
TrieNode< T >::Ptr find(const Path &path) const
Definition: trie.imp.h:267
std::weak_ptr< TrieNode > ParentPtr
Definition: trie.h:101
int closestCommonAncestor(int depth) const
Definition: trie.imp.h:98
std::shared_ptr< TrieNode > Ptr
Definition: trie.h:45
void clear()
Definition: trie.imp.h:447
Path path(const std::string &pathString)
#define UFE_ASSERT_COMPILED(CODE)
Definition: ufeAssert.h:37
TrieNode< T >::Ptr move(const Path &oldPath, const Path &newPath)
Definition: trie.imp.h:433
Path pop() const
TrieNode< T >::Ptr add(const Path &path, const T &data)
Definition: trie.imp.h:257
bool empty() const
Definition: trie.imp.h:459
TrieNode< T >::Ptr createNode(const Path &path)
Definition: trie.imp.h:240