197 lines
5.2 KiB
C
197 lines
5.2 KiB
C
|
|
// 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 "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>;
|
||
|
|
|
||
|
|
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_
|