/// <summary> /// Finds the next entry, relative to the specified entry, which is accepted by the entry filter. /// </summary> /// <remarks> /// The caller is required to hold a reader lock on the entry's <see cref="TableOfContentsModel"/>. /// </remarks> public TableOfContentsModel.Entry FindPrevious(TableOfContentsModel.Entry entry) { if (entry == null) { return(null); } // Pre-order traversal: we start with the last descendent of the entry's previous sibling. using (Synchronizer.Lock(entry.SyncRoot)) { TableOfContentsModel.Entry parent = entry.Parent; TableOfContentsModel.EntryCollection siblings = (parent == null) ? entry.TableOfContents.Entries : parent.Children; int index = siblings.IndexOf(entry); // If there exists a previous sibling, we're in good shape. // (This also handles the case when the given entry is not a child of its parent, // which can happen when entries are received over the network in a strange order.) if (index > 0) { entry = siblings[index - 1]; // Find the *last* descendent of the sibling by going down as far as possible. siblings = entry.Children; while (siblings.Count > 0) { entry = siblings[siblings.Count - 1]; siblings = entry.Children; } // Now, we've either got the original sibling or the last descendent of it. // Use this as the previous entry, or start here if it's not accepted by the filter. if (this.m_Filter(entry)) { return(entry); } else { return(this.FindPrevious(entry)); } } // If there's no previous sibling and no parent, we're screwed. else if (parent == null) { return(null); } // Otherwise, if there's no previous sibling, use the parent. else { if (this.m_Filter(parent)) { return(parent); } else { return(this.FindPrevious(parent)); } } } }
/// <summary> /// Finds the next entry, in pre-order relative to the specified entry, which is accepted by the entry filter. /// </summary> /// <remarks> /// The caller is required to hold a reader lock on the entry's <see cref="TableOfContentsModel"/>. /// </remarks> public TableOfContentsModel.Entry FindNext(TableOfContentsModel.Entry entry) { if (entry == null) { return(null); } using (Synchronizer.Lock(entry.SyncRoot)) { // Pre-order traversal: we've already visited the "current" entry, so visit it's children next. if (entry.Children.Count > 0) { TableOfContentsModel.Entry child = entry.Children[0]; if (this.m_Filter(child)) { return(child); } else { return(this.FindNext(child)); } } // If there are no children, look for a next sibling, parent's sibling, etc. else { TableOfContentsModel.Entry parent = entry.Parent; TableOfContentsModel.EntryCollection siblings = (parent == null) ? entry.TableOfContents.Entries : parent.Children; while (siblings != null) { int index = siblings.IndexOf(entry); // Use the entry's sibling within the parent, if possible. if (index >= 0 && index < siblings.Count - 1) { TableOfContentsModel.Entry sibling = siblings[index + 1]; if (this.m_Filter(sibling)) { return(sibling); } else { return(this.FindNext(sibling)); } } // If there is no sibling in the parent, go up another level. // Do not consider parent nodes in the pre-order traversal. else { entry = parent; parent = (entry == null) ? null : entry.Parent; siblings = (parent == null) ? null : parent.Children; } } // If we get here, then we've run out of parents and there is no next entry! return(null); } } }