16
import { className, float64, int32, uint32 } from "@koalaui/compat"
17
import { KoalaProfiler } from "@koalaui/common"
18
import { Disposable } from "../states/Disposable"
19
import { ReadonlyTreeNode } from "./ReadonlyTreeNode"
24
export class TreeNode implements Disposable, ReadonlyTreeNode {
25
private myIndex: int32 = -1
26
private myParent: TreeNode | undefined = undefined
27
private myChildren = Array.of<TreeNode>()
28
private myIndicesValid: boolean = true
29
private myDisposed: boolean = false
33
protected onChildInserted: ((node: TreeNode, index: int32) => void) | undefined
34
protected onChildRemoved: ((node: TreeNode, index: int32) => void) | undefined
36
constructor(kind: uint32 = 1) {
38
KoalaProfiler.nodeCreated(this.kind, this)
39
KoalaProfiler.counters?.node()
42
get disposed(): boolean {
43
return this.myDisposed
51
this.myDisposed = true
52
KoalaProfiler.nodeDisposed(this.kind, this)
58
get parent(): TreeNode | undefined {
68
let parent = this.myParent
69
while (parent !== undefined) {
70
parent = parent!.myParent
79
get childrenCount(): uint32 {
80
return this.myChildren.length as uint32
87
get children(): ReadonlyArray<TreeNode> {
88
return this.myChildren
96
const parent = this.myParent
97
if (parent?.myIndicesValid == false) {
98
parent!.myIndicesValid = true
100
const children = parent!.myChildren
101
const length = children.length
102
for (let i = 0; i < length; i++) {
103
children[i].myIndex = i
116
isKind(kind: uint32): boolean {
117
return this.kind % kind == 0
123
find<V>(valueOf: (node: TreeNode, index: int32) => V | undefined): V | undefined {
124
const children = this.myChildren
125
const length = children.length
126
for (let i = 0; i < length; i++) {
127
let value = valueOf(children[i], i)
128
if (value !== undefined) return value
136
forEach(action: (node: TreeNode, index: float64) => void): void {
138
this.myChildren.forEach(action)
144
every(predicate: (node: TreeNode, index: float64) => boolean): boolean {
146
return this.myChildren.every(predicate)
152
some(predicate: (node: TreeNode, index: float64) => boolean): boolean {
154
return this.myChildren.some(predicate)
160
childAt(index: int32): TreeNode | undefined {
162
return this.myChildren[index]
168
appendChild(node: TreeNode): boolean {
169
return this.insertChildAt(this.childrenCount, node)
175
insertChildAt(index: int32, node: TreeNode): boolean {
177
if (!this.insertable(node)) return false
178
this.insertNodeAt(index, node)
185
appendChildren(...nodes: TreeNode[]): boolean {
186
return this.insertChildrenAt(this.childrenCount, ...nodes)
192
insertChildrenAt(index: int32, ...nodes: TreeNode[]): boolean {
193
if (!this.accessible(index, 0)) return false
194
if (nodes.length < 1) return false
195
const length = nodes.length
196
for (let i = 0; i < length; i++) {
197
if (!this.insertable(nodes[i])) return false
199
for (let i = 0; i < length; i++) {
200
this.insertNodeAt(index + i, nodes[i])
208
removeChildAt(index: int32): TreeNode | undefined {
210
return this.removeNodes(index, 1)[0]
216
removeChildrenAt(index: int32, count?: uint32): Array<TreeNode> {
217
if (count === undefined) count = this.childrenCount - index
218
if (count < 1 || !this.accessible(index, count)) return Array.of<TreeNode>()
219
return this.removeNodes(index, count)
225
removeChild(node: TreeNode): boolean {
226
if (node.myParent !== this) return false
227
const index: int32 = this.myIndicesValid ? node.index : this.myChildren.indexOf(node) as int32
228
return undefined !== this.removeChildAt(index)
234
removeFromParent(): void {
235
this.myParent?.removeChild(this)
238
private removeNodes(index: int32, count: uint32): Array<TreeNode> {
239
const nodes = this.myChildren.splice(index, count)
240
if (index < this.childrenCount) this.myIndicesValid = false
241
const length = nodes.length
242
for (let i = 0; i < length; i++) {
243
const current = nodes[i]
245
current.myParent = undefined
246
this.onChildRemoved?.(current, index)
251
private insertNodeAt(index: int32, node: TreeNode): void {
254
if (index < this.childrenCount) this.myIndicesValid = false
255
this.myChildren.splice(index, 0, node)
256
this.onChildInserted?.(node, index)
259
private insertable(node: TreeNode): boolean {
263
return node.parent === undefined
266
private accessible(index: int32, count: uint32 = 1): boolean {
267
return Number.isInteger(index) && 0 <= index
268
&& Number.isInteger(count) && index <= this.childrenCount - count
272
return className(this)
275
toHierarchy(): string {
276
return this.collectNodes().map((node: TreeNode) => " ".repeat(node.depth) + node.toString()).join("\n")
279
collectNodes(): Array<TreeNode> {
280
const array = Array.of<TreeNode>()
281
this.collectParentsTo(array)
283
this.collectChildrenTo(array, true)
287
collectParentsTo(array: Array<TreeNode>): void {
288
const index = array.length as int32
289
let parent = this.myParent
290
while (parent !== undefined) {
291
array.splice(index, 0, parent!)
292
parent = parent!.myParent
296
collectChildrenTo(array: Array<TreeNode>, deep: boolean = false): void {
297
const children = this.myChildren
298
const length = children.length
299
for (let i = 0; i < length; i++) {
300
const current = children[i]
302
if (deep) current.collectChildrenTo(array, deep)