156 lines
4.1 KiB
C
156 lines
4.1 KiB
C
|
// 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 <typename T>
|
||
|
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<T*>(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<T>::RemoveChild(child);
|
||
|
child->m_pParent = static_cast<T*>(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_
|