ALib C++ Framework
by
Library Version: 2605 R0
Documentation generated by doxygen
Loading...
Searching...
No Matches
stringtreeiterator.hpp
Go to the documentation of this file.
1//==================================================================================================
2/// \file
3/// This header-file is part of module \alib_containers of the \aliblong.
4///
5/// Copyright 2013-2026 A-Worx GmbH, Germany.
6/// Published under #"mainpage_license".
7//==================================================================================================
8ALIB_EXPORT namespace alib { namespace containers {
9
10/// This class is to be used with instances of class #"StringTree" and allows
11/// iterating recursively through its nodes.<br>
12/// The type does <b>not</b> apply to the concept of \c std::iterator_traits.
13/// The rationale for this is the fact that mechanics for sorting the child nodes are
14/// provided, which requires the allocation of more resources than usual container iterators do.
15/// Therefore, objects of this type are not supposed to be temporary and
16/// created "on the fly", e.g., in C++ range-based loops.
17/// Instead, instances should rather be created once and then re-used with later iterations.
18///
19/// The sorting of child nodes is optional and can be changed before each recursion.
20/// Whenever a recursion in iteration occurs, the most recent settings of sorting are respected
21/// for the children of the node that is processed with that recursion.<br>
22/// A built-in comparison function which works on node names (path names) allows choosing
23/// ascending and descending order and to ignore or be sensitive about the letter case.
24/// Besides this, custom comparison functions that take a combination of arbitrary node
25/// attributes, including a node's value of template type \p{T} can be established.
26/// See method #"SetSorting" for details on this topic.
27///
28/// Method #".Initialize" starts a new 'use' of this class. Besides the start node, a boolean
29/// parameter allows deciding whether the start node should be included in the iteration or
30/// not. This is useful in cases where the start-node could optionally be a leaf-node.
31/// For example, when processing files with class #"FTree", an application might
32/// allow accepting a single file or a folder that contains files. In this case, the
33/// iteration should include the start node, as otherwise, in case a file was given, that
34/// leaf-node would be skipped.
35///
36/// The maximum depth of recursion may be limited with optional parameter \p{depth}
37/// found with each overloaded version of #".Initialize".
38/// During the iteration, the recursion can be individually selected per node visited.
39/// This is done by using either of the methods #".Next" or #".NextSibling" to proceed.
40/// Furthermore, the method #"NextParentSibling" allows skipping the rest of the current iteration
41/// branch.<br>
42/// The end of an iteration is detected with the method #"IsValid".
43///
44/// Finally, the generation of a string representing the actual path to the current
45/// iteration node, relative to the iteration's start node, can be activated.
46/// See method #"SetPathGeneration" for more information about this feature.
47///
48/// \see
49/// Further information on how this class is used are given with paragraph
50/// #"alib_ns_containers_stringtree_iterator"
51/// of the description of class #"%StringTree".
52///
53/// @tparam TStringTree The #"StringTree" that this class is working with.
54template<typename TStringTree>
56 #if !DOXYGEN
57 #if ALIB_DEBUG_CRITICAL_SECTIONS
58 #undef DCS
59 #undef DCSSHRD
60 #define DCS ALIB_DCS_WITH( tree->DbgGetDCS())
61 #define DCSSHRD ALIB_DCS_SHARED_WITH(tree->DbgGetDCS())
62 #else
63 #define DCS
64 #define DCSSHRD
65 #endif
66 #endif
67
68 public:
69 /// Publicly Exposes template parameter \p{TStringTree}.
70 using StringTreeType= TStringTree;
71
72 /// Evaluates to \c true if the given template parameter \p{TStringTree} is a constant type.
73 static constexpr bool IsConst = std::is_const_v<TStringTree>;
74
75 /// Publicly exposes the cursor type that is used to walk the tree.
76 /// Evaluates to the constant or mutable types #"StringTree::Cursor;*" or
77 /// or #"StringTree::ConstCursor;*".
78 using CursorType = std::conditional_t< !IsConst,
79 typename StringTreeType::Cursor,
80 typename StringTreeType::ConstCursor>;
81
82 /// Exposes #"StringTree::CharacterType;*" #"StringTreeType".
83 using CharacterType = typename StringTreeType::CharacterType;
84
85 protected:
86 /// Constant or mutable version of the base tree type, depending on template parameter
87 /// \p{TStringTree}
88 using cursorHandle= std::conditional_t<!IsConst,
89 typename StringTreeType::CursorHandle,
90 typename StringTreeType::ConstCursorHandle>;
91
92
93 //############################################# Sorter ###########################################
94 public:
95
96 /// Abstract base type to be used to implement custom sorting.
97 /// One simple built-in descendant is provided with struct #"NameSorter".
98 struct Sorter {
99 /// Necessary virtual destructor.
100 virtual ~Sorter() =default;
101
102 /// Abstract method which needs to be implemented by descendants.
103 /// @param lhs The left-hand side cursor to the node to compare.
104 /// @param rhs The right-hand side cursor to the node to compare.
105 /// @return Needs to return \c true if \p{lhs} is 'smaller' than \p{rhs}, and \c false
106 /// otherwise.
107 virtual bool Compare( const StringTreeType::ConstCursor& lhs,
108 const StringTreeType::ConstCursor& rhs) =0;
109 };
110
111 /// Built-in descendant of struct #"Sorter" used to perform simple sorting based on
112 /// the name of <b>StringTree</b>-nodes.
114 /// Unless changed by the caller, this is copied with every recursion step.
115 bool Descending =false;
116
117 /// Unless changed by the caller, this is copied with every recursion step.
118 bool CaseSensitive =false;
119
120 /// Implementation of the abstract method. Compares the names of the nodes using
121 /// the string method #"TString;CompareTo".
122 /// @param lhs The left-hand side cursor to the node to compare.
123 /// @param rhs The right-hand side cursor to the node to compare.
124 /// @return Returns \c true if \p{lhs} is 'smaller' than \p{rhs}, and \c false otherwise.
125 bool Compare(const StringTreeType::ConstCursor& lhs,
126 const StringTreeType::ConstCursor& rhs) override {
127
128 int compResult= CaseSensitive
129 ? lhs.Name().template CompareTo<CHK,lang::Case::Sensitive>(rhs.Name())
130 : lhs.Name().template CompareTo<CHK,lang::Case::Ignore >(rhs.Name());
131 return Descending ? compResult > 0
132 : compResult < 0;
133 }
134 };
135
136 //#################################### Inner type RecursionData ##################################
137 protected:
138 /// Protected, internal struct used to store the data of recursive iterations.
140 /// Combines a node hook and a current vector index used to remember the
141 /// current child in unsorted, respectively sorted mode.
143 /// The current child of the current node in case of unsorted access.
144 /// If this is pointing to the end of the child map, then
145 /// the actual node itself is selected by this StringTreeIterator.
147
148 /// The current child index in case of sorted access.
149 /// A value of <c>size_t(-1)</c> indicates that
150 /// the actual node itself is selected.
151 size_t sortedIdx;
152 };
153
154 /// The actual child handle, respectively index.
156
157 /// The child hook of the parent node, used with unsorted iteration.
158 /// Note that this is declared \c const, in case \c constexpr field \p{IsConst}
159 /// equals \c true.
161
162 /// A pointer to a dynamically allocated vector of children used with sorting.
163 std::vector<cursorHandle> sortedChildren;
164
165 /// The (optional) sorter used with the actual node.
166 /// Unless changed by the caller, this is copied with every recursion step.
168
169 /// The path string length of the actual recursion depth.
171
172 /// Trivial default constructor.
173 RecursionData() noexcept =default;
174
175 /// Trivial default copy constructor.
176 RecursionData( const RecursionData& ) =default;
177
178 /// Trivial default move constructor.
179 RecursionData( RecursionData&& ) noexcept =default;
180
181 /// Trivial default copy assign operator.
182 /// @return A reference to \c this.
183 RecursionData& operator=( const RecursionData& ) =default;
184
185 /// Trivial default move assign operator.
186 /// @return A reference to \c this.
187 RecursionData& operator=( RecursionData&& ) noexcept =default;
188
189 /// Trivial default destructor.
190 ~RecursionData() noexcept =default;
191 }; // inner struct RecursionData
192
193 //############################################ members ###########################################
194 /// The #"%StringTree" that this iterator works on.
195 TStringTree* tree =nullptr;
196
197 /// The pointer to the actual node.
199
200 /// A stack holding the recursive list of unsorted or sorted children and the
201 /// hook to the current child. Implemented as a vector in combination with member #"actDepth",
202 /// to reuse allocated storage space during iteration and when this iterator is
203 /// re-used (freshly initialized).
204 std::vector<RecursionData> stack;
205
206 /// The path to the actual node (excluding the name of the actual node).
207 /// If this object is \e nulled, no paths are generated.
209
210 /// The current depth of the iteration (and usage but not size of field #".stack").
211 /// set to \c -1 to if iteration is finished, respectively this iterator was not
212 /// initialized.
213 int actDepth =-1;
214
215 /// The requested maximum depth of iteration recursion.
216 unsigned maxDepth =(std::numeric_limits<unsigned int>::max)();
217
218 /// A pointer to a user-defined comparison object used with the next iteration.
219 Sorter* nextSorter =nullptr;
220
221//###################################### Constructor/Destructor ####################################
222 public:
223 /// Default constructor.
225
226 /// Trivial copy constructor.
228
229 /// Trivial default move constructor.
231
232 /// Trivial default copy assign operator.
233 /// @return A reference to \c this.
234 StringTreeIterator& operator =( const StringTreeIterator& ) =default;
235
236 /// Trivial default move assign operator.
237 /// @return A reference to \c this.
238 StringTreeIterator& operator =( StringTreeIterator&& ) =default;
239
240
241 /// Destructor.
243
244 //########################################### Interface ##########################################
245 /// With this method, the assembly of a string representing the absolute path of the actual
246 /// node is activated or deactivated.<br>
247 /// If activated, the path to the current node can be received with the method #"Path".
248 ///
249 /// Note that, for technical reasons, the invocation of the method invalidates this iterator.
250 /// @param pathGeneration Denotes whether the path should be generated and retrievable or not.
251 void SetPathGeneration( lang::Switch pathGeneration ) {
252 Invalidate();
253 actPath.Reset( pathGeneration == lang::Switch::On ? EMPTY_STRING
254 : NULL_STRING );
255 }
256
257 /// Resets this iterator to the first child of the node that the given cursor object
258 /// represents, or, in case parameter \p{includeStartNode} indicates so, to the given
259 /// \p{startNode} itself.<br>
260 /// If the given node has no children, this iterator is marked invalid when this method returns,
261 /// unless param \p{includeStartNode} is set to \c true. In the latter case, at least the
262 /// start node is part of the iteration.
263 /// @param startNode The cursor that defines the branch of the tree to be iterated.
264 /// In debug-builts it is asserted that this instance is valid.
265 /// @param includeStartNode Denotes whether the \p{startNode} is included in the iteration or
266 /// not. If so, the start node will be the first node visited.
267 void Initialize( CursorType startNode, lang::Inclusion includeStartNode ) {
268 ALIB_ASSERT_ERROR( startNode.IsValid(), "STRINGTREE", "Invalid start-node given." )
269 this->tree= &startNode.template Tree<TStringTree>();
270 stack.clear();
271 DCSSHRD
272 if( actPath.IsNotNull() )
273 startNode.AssemblePath(actPath, lang::CurrentData::Clear);
274
275 node= startNode.Export();
276
277 if ( includeStartNode == lang::Inclusion::Include) {
278 RecursionData& rd= stack.emplace_back();
279 node= startNode.Export();
280 rd.actChild.sortedIdx= 0;
281 rd.sortedChildren.emplace_back(node);
282 actDepth= 0;
283 return;
284 }
285
286 actDepth= -1;
287 if( startNode.HasChildren() )
288 recursion();
289 }
290
291 /// Invalidates this object. After invoking this method, this iterator cannot be
292 /// used further until one of the overloaded methods #".Initialize" is invoked.
293 /// After the invocation, the method #".IsValid" will return \c false.
294 void Invalidate() { actDepth= -1; }
295
296 /// Determines if this instance is valid. #"%StringTreeIterator" instances may become invalid
297 /// after invocations of one of the methods #".Next", #".NextSibling" or #".NextParentSibling"
298 /// (at the end of the iteration) and become valid with the invocation of one of the
299 /// overloaded methods #".Initialize".
300 ///
301 /// @return \c true if this is a valid iterator. If invalid, \c false is returned and
302 /// the iterator must not be evaluated before being initialized.
303 bool IsValid() const { return actDepth != -1; }
304
305 /// The negation of #".IsValid".
306 ///
307 /// @return \c false if this is a valid iterator. If invalid, \c true is returned and
308 /// the iterator must not be evaluated before being initialized.
309 bool IsInvalid() const { return !IsValid(); }
310
311
312 /// Sets a sorter instance which is used for any next recursion step.
313 ///
314 /// This method may be invoked at any time, even on invalid iterators and those that are not
315 /// initialized, yet. The given \p{sorter} is stored for future use.
316 /// Such a use happens whenever a recursive iteration over a list of child nodes is
317 /// started. At that moment the current configuration of sorting is applied to
318 /// the list of direct children.
319 /// @param sorter A custom comparison method used for sorting the children
320 /// of nodes.
321 void SetSorting(Sorter *sorter) { nextSorter= sorter; }
322
323 /// Iterates to the first child of the current node. If no such child exists,
324 /// to the next sibling node. If also no sibling exists, iteration continues
325 /// with the next available node of a previous recursion level.
326 ///
327 /// @return \c true if a next node was found, \c false otherwise.
328 /// If \c false is returned, this iterator is invalid after the call.
329 bool Next() { DCSSHRD return next(0); }
330
331 /// Omits recursion on the current node's children, even if the current depth
332 /// is lower than #".MaxDepth".<br>
333 ///
334 /// If no sibling exists, iteration continues with the next available node of a
335 /// previous recursion level.
336 ///
337 /// @return \c true if a next node was found, \c false otherwise.
338 /// If \c false is returned, this iterator is invalid after the call.
339 bool NextSibling() { DCSSHRD return next(1); }
340
341 /// Skips the remaining siblings of the current recursion level and continues with
342 /// the next available sibling of a previous level.
343 ///
344 /// @return \c true if a next node was found, \c false otherwise.
345 /// If \c false is returned, this iterator is invalid after the call.
346 bool NextParentSibling() { DCSSHRD return next(2); }
347
348
349 /// Retrieves the current path of walking as a string representation. The path returned is
350 /// absolute with a leading separator character.
351 ///
352 /// Note that this method can be used only if path generation was activated before the current
353 /// iteration. Activation is performed with the method SetPathGeneration.
354 ///
355 /// @return The path of the current node.
356 const TStringTree::NameType Path() const {
357 ALIB_ASSERT_ERROR( actPath.IsNotNull(), "STRINGTREE", "Path generation not activated" )
358 return actPath;
359 }
360
361 /// Returns the requested maximum depth of iteration, set with #".Initialize".
362 /// @see For the current iteration depth, use #".CurrentDepth".
363 /// @return The distance of the current node and the node of the start of the
364 /// iteration.
365 unsigned MaxDepth() const { return maxDepth; }
366
367 /// Changes the maximum depth of iteration. This method may be invoked any time, also after
368 /// iteration has started.
369 /// @param newMaxDepth The maximum depth to use from now on.
370 /// Defaults to the maximum <c>unsigned int</c> value.
371 void SetMaxDepth(unsigned int newMaxDepth= (std::numeric_limits<unsigned>::max)())
372 { maxDepth= newMaxDepth; }
373
374 /// Returns the depth of the current iteration. This is value is available to the
375 /// algorithm, which means this method executes in constant time.
376 ///
377 /// To get the absolute depth of the current node, the method
378 /// #"TCursor::Depth",
379 /// may be used.
380 ///
381 /// @return The distance of the current node and the node of the start of the
382 /// iteration.
383 unsigned CurrentDepth() const {
384 ALIB_ASSERT_ERROR( IsValid(), "STRINGTREE",
385 "StringTreeIterator not initialized or exceeded (invalid)." )
386 return unsigned(actDepth);
387 }
388
389 /// Returns the current node, encapsulated in a cursor object.
390 ///
391 /// \note
392 /// It is \b not allowed to use the method #"TCursor::Delete" on the node returned by
393 /// this method.
394 /// As a replacement, use the method #".DeleteNode" provided with this class.<br>
395 /// However, the methods #"TCursor::DeleteChild(TCur)" and #"TCursor::DeleteChildren"
396 /// are allowed to be invoked and therefore have no replacement in this class.
397 ///
398 /// @return An instance of the public node interface pointing to the currently
399 /// referenced tree node.
400 CursorType Node() const {
401 ALIB_ASSERT_ERROR( IsValid(), "STRINGTREE",
402 "StringTreeIterator not initialized or exceeded (invalid)." )
403 return tree->ImportCursor(node);
404 }
405
406 //######################################### node deletion ########################################
407
408 /// Deletes the node that this iterator currently refers to from the tree.
409 /// After the operation, the iterator is moved forward to the next sibling
410 /// of the current node, respectively of the first sibling found in the
411 /// recursion stack.
412 ///
413 /// \note
414 /// This method constitutes a legal alternative to method
415 /// #"TCursor::Delete",
416 /// which is forbidden to be invoked on the node returned by method #".Node" as this would
417 /// invalidate this iterator.<br>
418 /// The methods #"TCursor::DeleteChild(TCursor&)" and "TCursor::DeleteChildren"
419 /// are allowed with this iterator type.
420 /// Consequently, no replacement method for those is given with this class.
421 ///
422 /// @return The total number of nodes deleted.
424 ALIB_ASSERT_ERROR( IsValid(), "STRINGTREE",
425 "StringTreeIterator not initialized or exceeded (invalid)." )
426 auto nodeToDelete= node;
427 next( 1 ); // next sibling
428 auto ntd= tree->ImportCursor(nodeToDelete);
429 return ntd.Parent().DeleteChild( ntd );
430 }
431
432 //################################## StringTreeIterator Internals ################################
433 protected:
434 /// Sets this iterator to point to the first child of the actual node.
435 /// If sorting is enabled, copies all children from the map to a vector and sorts
436 /// them there.
437 void recursion() {
438 ++actDepth;
439 if( stack.size() == size_t(actDepth) )
440 stack.emplace_back();
441
442 auto& rd= stack[size_t(actDepth)];
443
444 auto cursor= tree->ImportCursor(node);
445 rd.sorter= nextSorter;
446
447 // no sorting: set link to node's child hook
448 if (!rd.sorter) {
449 rd.unsortedParent= tree->ImportCursor(node).FirstChild().Export();
450 node= (rd.actChild.unsortedHandle= rd.unsortedParent);
451 } else {
452
453 // sorting: copy children to a sortable vector
454 rd.sortedChildren.clear();
455 rd.sortedChildren.reserve( size_t( cursor.CountChildren() ) );
456 cursor.GoToFirstChild();
457 while( cursor.IsValid() ) {
458 rd.sortedChildren.emplace_back( cursor.Export() );
459 cursor.GoToNextSibling();
460 }
461
462 // sort
463 std::sort( rd.sortedChildren.begin(), rd.sortedChildren.end(),
464 [this,&rd]( cursorHandle lhs,
465 cursorHandle rhs )
466 {
467 return rd.sorter->Compare( tree->ImportCursor(lhs),
468 tree->ImportCursor(rhs) );
469 }
470 );
471
472 // set to first child
473 rd.actChild.sortedIdx= 0;
474 node= rd.sortedChildren[0];
475 }
476
477 // add path information
478 if ( actPath.IsNotEmpty() ) {
479 rd.pathStringLen= actPath.Length();
480 if ( rd.pathStringLen == 1 ) rd.pathStringLen= 0;
481 else actPath.template _<NC>( tree->Separator() );
482
483 actPath.template _<NC>( tree->ImportCursor(node).Name() );
484 } }
485
486
487 /// Goes to the next node.
488 /// This method is used with interface methods #".Next", #".NextSibling" and
489 /// #".NextParentSibling", as well as with #".DeleteNode"}.
490 ///
491 /// @param skipMode \c 0: iterates to the first child (if available),
492 /// \c 1: iterates to the next sibling (if available) and
493 /// \c 2: to the next available sibling of the parent, respectively the
494 /// current recursion stack.
495 /// @return \c true if this iterator is valid (a next node was found), \c false
496 /// otherwise.
497 bool next(int skipMode) {
498 ALIB_ASSERT_ERROR( actDepth != -1, "STRINGTREE", "Invalid iterator" )
499
500 auto cursor= tree->ImportCursor(node);
501
502 // recursion to first child of actual node?
503 if( skipMode == 0
504 && unsigned(actDepth) < maxDepth
505 && cursor.CountChildren() )
506 {
507 // increase stack capacity
508 if( stack.size() == size_t(actDepth) + 1 )
509 stack.emplace_back();
510
511 recursion();
512
513 return true;
514 }
515
516 for(;;) {
517 if( skipMode != 2 ) {
518 // next sibling
519 RecursionData& rd= stack[ size_t(actDepth) ];
520 if( rd.sortedChildren.size() ) {
521 ++rd.actChild.sortedIdx;
522 if( rd.actChild.sortedIdx < rd.sortedChildren.size() ) {
523 node= rd.sortedChildren[rd.actChild.sortedIdx];
524 break;
525 }
526 } else {
527 auto next= tree->ImportCursor(rd.actChild.unsortedHandle).NextSibling();
528 node= (rd.actChild.unsortedHandle= next.Export());
529 if ( next.IsValid() )
530 break;
531 } }
532
533 skipMode= 0;
534
535 // climb down
536 if (--actDepth == -1 )
537 return false;
538 }
539
540 // adjust path
541 if ( actPath.IsNotEmpty() ) {
542 actPath.ShortenTo(stack[ size_t(actDepth) ].pathStringLen + 1)
543 .template _<NC>(tree->ImportCursor(node).Name());
544 }
545
546 return true;
547 }
548
549 #if !DOXYGEN
550 #if ALIB_DEBUG_CRITICAL_SECTIONS
551 #undef DCS
552 #undef DCSSHRD
553 #endif
554 #endif
555
556}; // class "StringTreeIterator"
557
558} // namespace alib::[containers]
559
560/// Type alias in namespace #"%alib".
561template<typename TTree>
563
564} // namespace [alib]
#define ALIB_EXPORT
#define ALIB_ASSERT_ERROR(cond, domain,...)
TStringTree StringTreeType
Publicly Exposes template parameter TStringTree.
std::conditional_t< !IsConst, typename StringTreeType::Cursor, typename StringTreeType::ConstCursor > CursorType
StringTreeIterator()=default
Default constructor.
void SetMaxDepth(unsigned int newMaxDepth=(std::numeric_limits< unsigned >::max)())
void Initialize(CursorType startNode, lang::Inclusion includeStartNode)
typename StringTreeType::CharacterType CharacterType
Exposes #"StringTree::CharacterType;*" #"StringTreeType".
const TStringTree::NameType Path() const
void SetPathGeneration(lang::Switch pathGeneration)
std::conditional_t<!IsConst, typename StringTreeType::CursorHandle, typename StringTreeType::ConstCursorHandle > cursorHandle
@ On
Switch it on, switched on, etc.
@ Clear
Chooses to clear existing data.
Inclusion
Denotes how members of a set something should be taken into account.
@ Include
Chooses inclusion.
Definition alox.cpp:14
constexpr String NULL_STRING
A nulled string of the default character type.
Definition string.hpp:2247
constexpr const String EMPTY_STRING
An empty string of the default character type.
Definition string.hpp:2227
lang::integer integer
Type alias in namespace #"%alib".
Definition integers.hpp:149
lang::uinteger uinteger
Type alias in namespace #"%alib".
Definition integers.hpp:152
containers::StringTreeIterator< TTree > StringTreeIterator
Type alias in namespace #"%alib".
bool Compare(const StringTreeType::ConstCursor &lhs, const StringTreeType::ConstCursor &rhs) override
bool CaseSensitive
Unless changed by the caller, this is copied with every recursion step.
bool Descending
Unless changed by the caller, this is copied with every recursion step.
integer pathStringLen
The path string length of the actual recursion depth.
std::vector< cursorHandle > sortedChildren
A pointer to a dynamically allocated vector of children used with sorting.
ActChildIdentifier actChild
The actual child handle, respectively index.
RecursionData() noexcept=default
Trivial default constructor.
virtual bool Compare(const StringTreeType::ConstCursor &lhs, const StringTreeType::ConstCursor &rhs)=0
virtual ~Sorter()=default
Necessary virtual destructor.