// 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 #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 class TreeNodeBase { public: TreeNodeBase() = default; virtual ~TreeNodeBase() = default; inline T* GetParent() const { return static_cast(this)->m_pParent; } inline T* GetFirstChild() const { return static_cast(this)->m_pFirstChild; } inline T* GetLastChild() const { return static_cast(this)->m_pLastChild; } inline T* GetNextSibling() const { return static_cast(this)->m_pNextSibling; } inline T* GetPrevSibling() const { return static_cast(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(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(this)->m_pParent = pParent; } inline void SetFirstChild(T* pChild) { static_cast(this)->m_pFirstChild = pChild; } inline void SetLastChild(T* pChild) { static_cast(this)->m_pLastChild = pChild; } inline void SetNextSibling(T* pSibling) { static_cast(this)->m_pNextSibling = pSibling; } inline void SetPrevSibling(T* pSibling) { static_cast(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::RemoveChild(child); child->m_pParent = static_cast(this); CHECK(!child->m_pNextSibling); CHECK(!child->m_pPrevSibling); } }; // Tree connected using C-style pointers. template class TreeNode : public TreeNodeBase { public: TreeNode() = default; virtual ~TreeNode() = default; private: friend class TreeNodeBase; T* m_pParent = nullptr; // Raw, intra-tree pointer. T* m_pFirstChild = nullptr; // Raw, intra-tree pointer. T* m_pLastChild = nullptr; // Raw, intra-tree pointer. T* m_pNextSibling = nullptr; // Raw, intra-tree pointer T* m_pPrevSibling = nullptr; // Raw, intra-tree pointer }; } // namespace fxcrt using fxcrt::TreeNode; #endif // CORE_FXCRT_TREE_NODE_H_