idlize

Форк
0
305 строк · 9.8 Кб
1
/*
2
 * Copyright (c) 2022-2024 Huawei Device Co., Ltd.
3
 * Licensed under the Apache License, Version 2.0 (the "License");
4
 * you may not use this file except in compliance with the License.
5
 * You may obtain a copy of the License at
6
 *
7
 * http://www.apache.org/licenses/LICENSE-2.0
8
 *
9
 * Unless required by applicable law or agreed to in writing, software
10
 * distributed under the License is distributed on an "AS IS" BASIS,
11
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
 * See the License for the specific language governing permissions and
13
 * limitations under the License.
14
 */
15

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"
20

21
/**
22
 * A tree node used to build an incremental runtime and a component tree.
23
 */
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
30

31
    readonly kind: uint32
32

33
    protected onChildInserted: ((node: TreeNode, index: int32) => void) | undefined
34
    protected onChildRemoved: ((node: TreeNode, index: int32) => void) | undefined
35

36
    constructor(kind: uint32 = 1) {
37
        this.kind = kind
38
        KoalaProfiler.nodeCreated(this.kind, this)
39
        KoalaProfiler.counters?.node()
40
    }
41

42
    get disposed(): boolean {
43
        return this.myDisposed
44
    }
45

46
    /**
47
     * This method may be called to notify this node that it
48
     * is completely removed from the hierarchy and is no longer in use.
49
     */
50
    dispose(): void {
51
        this.myDisposed = true
52
        KoalaProfiler.nodeDisposed(this.kind, this)
53
    }
54

55
    /**
56
     * Returns the parent node.
57
     */
58
    get parent(): TreeNode | undefined {
59
        return this.myParent
60
    }
61

62
    /**
63
     * Returns the depth of this node relative to the root node.
64
     * The root node returns 0.
65
     */
66
    get depth(): uint32 {
67
        let count: uint32 = 0
68
        let parent = this.myParent
69
        while (parent !== undefined) {
70
            parent = parent!.myParent
71
            count++
72
        }
73
        return count
74
    }
75

76
    /**
77
     * Returns the number of children of this node.
78
     */
79
    get childrenCount(): uint32 {
80
        return this.myChildren.length as uint32
81
    }
82

83
    /**
84
     * Returns all children of this node.
85
     * Note that this array is updated automatically.
86
     */
87
    get children(): ReadonlyArray<TreeNode> {
88
        return this.myChildren
89
    }
90

91
    /**
92
     * Returns the index of this node in the parent's children.
93
     * The root node returns -1.
94
     */
95
    get index(): int32 {
96
        const parent = this.myParent
97
        if (parent?.myIndicesValid == false) {
98
            parent!.myIndicesValid = true
99
            // recalculate indices of all children
100
            const children = parent!.myChildren
101
            const length = children.length
102
            for (let i = 0; i < length; i++) {
103
                children[i].myIndex = i
104
            }
105
        }
106
        return this.myIndex
107
    }
108

109
    /**
110
     * @internal
111
     *
112
     * Use this method instead of standard instanceof for the sake of speed and reliability.
113
     * @param kind - kind id of this or parent instance to check against.
114
     * @returns true if this TreeNode is instance of given kind.
115
     */
116
    isKind(kind: uint32): boolean {
117
        return this.kind % kind == 0
118
    }
119

120
    /**
121
     * Iterates through the child nodes and returns the first defined value.
122
     */
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
129
        }
130
        return undefined
131
    }
132

133
    /**
134
     * Performs the specified action for each child node.
135
     */
136
    forEach(action: (node: TreeNode, index: float64) => void): void {
137
        // must be int32, but ArkTS array.forEach requires index to be float64
138
        this.myChildren.forEach(action)
139
    }
140

141
    /**
142
     * Determines whether all child nodes satisfy the specified predicate.
143
     */
144
    every(predicate: (node: TreeNode, index: float64) => boolean): boolean {
145
        // must be int32, but ArkTS array.every requires index to be float64
146
        return this.myChildren.every(predicate)
147
    }
148

149
    /**
150
     * Determines whether any child node satisfies the specified predicate.
151
     */
152
    some(predicate: (node: TreeNode, index: float64) => boolean): boolean {
153
        // must be int32, but ArkTS array.some requires index to be float64
154
        return this.myChildren.some(predicate)
155
    }
156

157
    /**
158
     * Returns a child node at the specified index.
159
     */
160
    childAt(index: int32): TreeNode | undefined {
161
        // if (!this.accessible(index)) return undefined // index out of bounds
162
        return this.myChildren[index]
163
    }
164

165
    /**
166
     * Adds the given node after existing children if possible.
167
     */
168
    appendChild(node: TreeNode): boolean {
169
        return this.insertChildAt(this.childrenCount, node)
170
    }
171

172
    /**
173
     * Inserts the given node at the specified index if possible.
174
     */
175
    insertChildAt(index: int32, node: TreeNode): boolean {
176
        // if (!this.accessible(index, 0)) return false // index out of bounds
177
        if (!this.insertable(node)) return false // cannot be inserted
178
        this.insertNodeAt(index, node)
179
        return true
180
    }
181

182
    /**
183
     * Adds several nodes after existing children if possible.
184
     */
185
    appendChildren(...nodes: TreeNode[]): boolean {
186
        return this.insertChildrenAt(this.childrenCount, ...nodes)
187
    }
188

189
    /**
190
     * Inserts several nodes at the specified index if possible.
191
     */
192
    insertChildrenAt(index: int32, ...nodes: TreeNode[]): boolean {
193
        if (!this.accessible(index, 0)) return false // index out of bounds
194
        if (nodes.length < 1) return false // nothing to insert
195
        const length = nodes.length
196
        for (let i = 0; i < length; i++) {
197
            if (!this.insertable(nodes[i])) return false // cannot be inserted
198
        }
199
        for (let i = 0; i < length; i++) {
200
            this.insertNodeAt(index + i, nodes[i])
201
        }
202
        return true
203
    }
204

205
    /**
206
     * Removes a child node at the specified index if possible.
207
     */
208
    removeChildAt(index: int32): TreeNode | undefined {
209
        // if (!this.accessible(index)) return undefined // index out of bounds
210
        return this.removeNodes(index, 1)[0]
211
    }
212

213
    /**
214
     * Removes several nodes at the specified index if possible.
215
     */
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>() // index out of bounds
219
        return this.removeNodes(index, count)
220
    }
221

222
    /**
223
     * Removes a child node if possible.
224
     */
225
    removeChild(node: TreeNode): boolean {
226
        if (node.myParent !== this) return false // not in hierarchy
227
        const index: int32 = this.myIndicesValid ? node.index : this.myChildren.indexOf(node) as int32
228
        return undefined !== this.removeChildAt(index)
229
    }
230

231
    /**
232
     * Removes this node from its parent.
233
     */
234
    removeFromParent(): void {
235
        this.myParent?.removeChild(this)
236
    }
237

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]
244
            current.myIndex = -1
245
            current.myParent = undefined
246
            this.onChildRemoved?.(current, index)
247
        }
248
        return nodes
249
    }
250

251
    private insertNodeAt(index: int32, node: TreeNode): void {
252
        node.myIndex = index
253
        node.myParent = this
254
        if (index < this.childrenCount) this.myIndicesValid = false
255
        this.myChildren.splice(index, 0, node)
256
        this.onChildInserted?.(node, index)
257
    }
258

259
    private insertable(node: TreeNode): boolean {
260
        // We decided to remove the check for cyclic dependency to improve performance.
261
        // Attention! Now it is possible to create a tree with a cyclic dependencies.
262
        // So you have to be sure that the inserting node is not the root of this node.
263
        return node.parent === undefined
264
    }
265

266
    private accessible(index: int32, count: uint32 = 1): boolean {
267
        return Number.isInteger(index) && 0 <= index
268
            && Number.isInteger(count) && index <= this.childrenCount - count // integer index within range
269
    }
270

271
    toString(): string {
272
        return className(this)
273
    }
274

275
    toHierarchy(): string {
276
        return this.collectNodes().map((node: TreeNode) => "  ".repeat(node.depth) + node.toString()).join("\n")
277
    }
278

279
    collectNodes(): Array<TreeNode> {
280
        const array = Array.of<TreeNode>()
281
        this.collectParentsTo(array)
282
        array.push(this)
283
        this.collectChildrenTo(array, true)
284
        return array
285
    }
286

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
293
        }
294
    }
295

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]
301
            array.push(current)
302
            if (deep) current.collectChildrenTo(array, deep)
303
        }
304
    }
305
}
306

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.