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