// Copyright 2019 PDFium Authors. All rights reserved. // 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 "core/fxcrt/fx_system.h" #include "third_party/base/logging.h" namespace fxcrt { // Implements the usual DOM/XML-ish trees. template class TreeNode { public: TreeNode() = default; virtual ~TreeNode() = default; T* GetParent() const { return m_pParent; } T* GetFirstChild() const { return m_pFirstChild; } T* GetLastChild() const { return m_pLastChild; } T* GetNextSibling() const { return m_pNextSibling; } T* GetPrevSibling() const { return m_pPrevSibling; } bool HasChild(const T* child) const { return child != this && child->m_pParent == 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 (m_pFirstChild) { CHECK(m_pLastChild); m_pFirstChild->m_pPrevSibling = child; child->m_pNextSibling = m_pFirstChild; m_pFirstChild = child; } else { CHECK(!m_pLastChild); m_pFirstChild = child; m_pLastChild = child; } } void AppendLastChild(T* child) { BecomeParent(child); if (m_pLastChild) { CHECK(m_pFirstChild); m_pLastChild->m_pNextSibling = child; child->m_pPrevSibling = m_pLastChild; m_pLastChild = child; } else { CHECK(!m_pFirstChild); m_pFirstChild = child; m_pLastChild = child; } } void InsertBefore(T* child, T* other) { if (!other) { AppendLastChild(child); return; } BecomeParent(child); CHECK(HasChild(other)); child->m_pNextSibling = other; child->m_pPrevSibling = other->m_pPrevSibling; if (m_pFirstChild == other) { CHECK(!other->m_pPrevSibling); m_pFirstChild = child; } else { other->m_pPrevSibling->m_pNextSibling = child; } other->m_pPrevSibling = child; } void InsertAfter(T* child, T* other) { if (!other) { AppendFirstChild(child); return; } BecomeParent(child); CHECK(HasChild(other)); child->m_pNextSibling = other->m_pNextSibling; child->m_pPrevSibling = other; if (m_pLastChild == other) { CHECK(!other->m_pNextSibling); m_pLastChild = child; } else { other->m_pNextSibling->m_pPrevSibling = child; } other->m_pNextSibling = child; } void RemoveChild(T* child) { CHECK(HasChild(child)); if (m_pLastChild == child) { CHECK(!child->m_pNextSibling); m_pLastChild = child->m_pPrevSibling; } else { child->m_pNextSibling->m_pPrevSibling = child->m_pPrevSibling; } if (m_pFirstChild == child) { CHECK(!child->m_pPrevSibling); m_pFirstChild = child->m_pNextSibling; } else { child->m_pPrevSibling->m_pNextSibling = child->m_pNextSibling; } child->m_pParent = nullptr; child->m_pPrevSibling = nullptr; child->m_pNextSibling = nullptr; } void RemoveAllChildren() { while (T* child = GetFirstChild()) RemoveChild(child); } void RemoveSelfIfParented() { if (T* parent = GetParent()) parent->RemoveChild(static_cast(this)); } private: // 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->TreeNode::RemoveChild(child); child->m_pParent = static_cast(this); CHECK(!child->m_pNextSibling); CHECK(!child->m_pPrevSibling); } 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_