| // Copyright 2019 The PDFium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #ifndef CORE_FXCRT_TREE_NODE_H_ |
| #define CORE_FXCRT_TREE_NODE_H_ |
| |
| #include <stdint.h> |
| |
| #include "core/fxcrt/unowned_ptr_exclusion.h" |
| #include "third_party/base/check.h" |
| |
| namespace fxcrt { |
| |
| // Implements the usual DOM/XML-ish trees allowing for a variety of |
| // pointer types with which to connect the nodes. Public methods maintain |
| // the invariants of the tree. |
| |
| template <typename T> |
| class TreeNodeBase { |
| public: |
| TreeNodeBase() = default; |
| virtual ~TreeNodeBase() = default; |
| |
| inline T* GetParent() const { return static_cast<const T*>(this)->m_pParent; } |
| inline T* GetFirstChild() const { |
| return static_cast<const T*>(this)->m_pFirstChild; |
| } |
| inline T* GetLastChild() const { |
| return static_cast<const T*>(this)->m_pLastChild; |
| } |
| inline T* GetNextSibling() const { |
| return static_cast<const T*>(this)->m_pNextSibling; |
| } |
| inline T* GetPrevSibling() const { |
| return static_cast<const T*>(this)->m_pPrevSibling; |
| } |
| |
| bool HasChild(const T* child) const { |
| return child != this && child->GetParent() == this; |
| } |
| |
| T* GetNthChild(int32_t n) { |
| if (n < 0) |
| return nullptr; |
| T* result = GetFirstChild(); |
| while (n-- && result) { |
| result = result->GetNextSibling(); |
| } |
| return result; |
| } |
| |
| void AppendFirstChild(T* child) { |
| BecomeParent(child); |
| if (GetFirstChild()) { |
| CHECK(GetLastChild()); |
| GetFirstChild()->SetPrevSibling(child); |
| child->SetNextSibling(GetFirstChild()); |
| SetFirstChild(child); |
| } else { |
| CHECK(!GetLastChild()); |
| SetFirstChild(child); |
| SetLastChild(child); |
| } |
| } |
| |
| void AppendLastChild(T* child) { |
| BecomeParent(child); |
| if (GetLastChild()) { |
| CHECK(GetFirstChild()); |
| GetLastChild()->SetNextSibling(child); |
| child->SetPrevSibling(GetLastChild()); |
| SetLastChild(child); |
| } else { |
| CHECK(!GetFirstChild()); |
| SetFirstChild(child); |
| SetLastChild(child); |
| } |
| } |
| |
| void InsertBefore(T* child, T* other) { |
| if (!other) { |
| AppendLastChild(child); |
| return; |
| } |
| BecomeParent(child); |
| CHECK(HasChild(other)); |
| child->SetNextSibling(other); |
| child->SetPrevSibling(other->GetPrevSibling()); |
| if (GetFirstChild() == other) { |
| CHECK(!other->GetPrevSibling()); |
| SetFirstChild(child); |
| } else { |
| other->GetPrevSibling()->SetNextSibling(child); |
| } |
| other->m_pPrevSibling = child; |
| } |
| |
| void InsertAfter(T* child, T* other) { |
| if (!other) { |
| AppendFirstChild(child); |
| return; |
| } |
| BecomeParent(child); |
| CHECK(HasChild(other)); |
| child->SetNextSibling(other->GetNextSibling()); |
| child->SetPrevSibling(other); |
| if (GetLastChild() == other) { |
| CHECK(!other->GetNextSibling()); |
| SetLastChild(child); |
| } else { |
| other->GetNextSibling()->SetPrevSibling(child); |
| } |
| other->SetNextSibling(child); |
| } |
| |
| void RemoveChild(T* child) { |
| CHECK(HasChild(child)); |
| if (GetLastChild() == child) { |
| CHECK(!child->GetNextSibling()); |
| SetLastChild(child->GetPrevSibling()); |
| } else { |
| child->GetNextSibling()->SetPrevSibling(child->GetPrevSibling()); |
| } |
| if (GetFirstChild() == child) { |
| CHECK(!child->GetPrevSibling()); |
| SetFirstChild(child->GetNextSibling()); |
| } else { |
| child->GetPrevSibling()->SetNextSibling(child->GetNextSibling()); |
| } |
| child->SetParent(nullptr); |
| child->SetPrevSibling(nullptr); |
| child->SetNextSibling(nullptr); |
| } |
| |
| void RemoveAllChildren() { |
| while (T* child = GetFirstChild()) |
| RemoveChild(child); |
| } |
| |
| void RemoveSelfIfParented() { |
| if (T* parent = GetParent()) |
| parent->RemoveChild(static_cast<T*>(this)); |
| } |
| |
| private: |
| // These are private because they may leave the tree in an invalid state |
| // until subsequent operations restore it. |
| inline void SetParent(T* pParent) { |
| static_cast<T*>(this)->m_pParent = pParent; |
| } |
| inline void SetFirstChild(T* pChild) { |
| static_cast<T*>(this)->m_pFirstChild = pChild; |
| } |
| inline void SetLastChild(T* pChild) { |
| static_cast<T*>(this)->m_pLastChild = pChild; |
| } |
| inline void SetNextSibling(T* pSibling) { |
| static_cast<T*>(this)->m_pNextSibling = pSibling; |
| } |
| inline void SetPrevSibling(T* pSibling) { |
| static_cast<T*>(this)->m_pPrevSibling = pSibling; |
| } |
| |
| // Child left in state where sibling members need subsequent adjustment. |
| void BecomeParent(T* child) { |
| CHECK(child != this); // Detect attempts at self-insertion. |
| if (child->m_pParent) |
| child->m_pParent->TreeNodeBase<T>::RemoveChild(child); |
| child->m_pParent = static_cast<T*>(this); |
| CHECK(!child->m_pNextSibling); |
| CHECK(!child->m_pPrevSibling); |
| } |
| }; |
| |
| // Tree connected using C-style pointers. |
| template <typename T> |
| class TreeNode : public TreeNodeBase<T> { |
| public: |
| TreeNode() = default; |
| virtual ~TreeNode() = default; |
| |
| private: |
| friend class TreeNodeBase<T>; |
| |
| UNOWNED_PTR_EXCLUSION T* m_pParent = nullptr; // intra-tree pointer. |
| UNOWNED_PTR_EXCLUSION T* m_pFirstChild = nullptr; // intra-tree pointer. |
| UNOWNED_PTR_EXCLUSION T* m_pLastChild = nullptr; // intra-tree pointer. |
| UNOWNED_PTR_EXCLUSION T* m_pNextSibling = nullptr; // intra-tree pointer. |
| UNOWNED_PTR_EXCLUSION T* m_pPrevSibling = nullptr; // intra-tree pointer. |
| }; |
| |
| } // namespace fxcrt |
| |
| using fxcrt::TreeNode; |
| |
| #endif // CORE_FXCRT_TREE_NODE_H_ |