idlize

Форк
0
/
IncrementalNode.ts 
192 строки · 6.2 Кб
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, uint32 } from "@koalaui/compat"
17
import { Disposable } from "../states/Disposable"
18
import { ReadonlyTreeNode } from "./ReadonlyTreeNode"
19

20
/**
21
 * This is a node implementation for a tree, which is represented as a pair of bidirectional lists.
22
 * It allows nodes to be added or removed incrementally using the state manager.
23
 */
24
export class IncrementalNode implements Disposable, ReadonlyTreeNode {
25
    private _disposed = false
26
    private _child: IncrementalNode | undefined = undefined
27
    private _prev: IncrementalNode | undefined = undefined
28
    private _next: IncrementalNode | undefined = undefined
29
    private _parent: IncrementalNode | undefined = undefined
30
    private _incremental: IncrementalNode | undefined = undefined
31

32
    /**
33
     * This callback is called when a child node is added to this parent.
34
     */
35
    protected onChildInserted: ((node: IncrementalNode) => void) | undefined = undefined
36

37
    /**
38
     * This callback is called when a child node is removed from this parent.
39
     */
40
    protected onChildRemoved: ((node: IncrementalNode) => void) | undefined = undefined
41

42
    /**
43
     * This kind can be used to distinguish nodes.
44
     * @see isKind
45
     */
46
    readonly kind: uint32
47

48
    /**
49
     * @param kind - the kind of this instance
50
     * @see PrimeNumbers
51
     */
52
    constructor(kind: uint32 = 1) {
53
        this.kind = kind
54
    }
55

56
    /**
57
     * Use this method instead of standard instanceof for the sake of speed and reliability.
58
     * @param kind - a kind of this or parent instance to check against
59
     * @returns `true` if this node is an instance of the expected kind
60
     * @see PrimeNumbers
61
     */
62
    isKind(kind: uint32): boolean {
63
        return this.kind % kind == 0
64
    }
65

66
    /**
67
     * @returns `true` if this node should no longer be used
68
     */
69
    get disposed(): boolean {
70
        return this._disposed
71
    }
72

73
    /**
74
     * This method is called to remove this node from the hierarchy.
75
     */
76
    dispose(): void {
77
        if (this._disposed) return
78
        const prev = this._prev
79
        const next = this._next
80
        const parent = this._parent
81
        this._disposed = true
82
        this._child = undefined
83
        if (prev !== undefined) {
84
            this._prev = undefined
85
            prev._next = next
86
        }
87
        if (next !== undefined) {
88
            this._next = undefined
89
            next._prev = prev
90
        }
91
        if (parent !== undefined) {
92
            this._parent = undefined
93
            if (parent._child === this) {
94
                parent._child = next
95
            }
96
            parent.onChildRemoved?.(this)
97
        }
98
    }
99

100
    /**
101
     * @returns a parent node if it is exist
102
     */
103
    get parent(): IncrementalNode | undefined {
104
        return this._parent
105
    }
106

107
    /**
108
     * @returns text representation of the node
109
     */
110
    toString(): string {
111
        return className(this) + ": " + this.kind
112
    }
113

114
    /**
115
     * @returns text representation of a tree hierarchy starting from this node
116
     */
117
    toHierarchy(): string {
118
        let str = ""
119
        for (let node = this._parent; node !== undefined; node = node!._parent) str += "  "
120
        str += this.toString()
121
        for (let node = this._child; node !== undefined; node = node!._next) str += "\n" + node!.toHierarchy()
122
        return str
123
    }
124

125
    /**
126
     * @returns the first child node contained in this node if it is exist
127
     */
128
    get firstChild(): IncrementalNode | undefined {
129
        return this._child
130
    }
131

132
    /**
133
     * @returns the next sibling of this node if it is exist
134
     */
135
    get nextSibling(): IncrementalNode | undefined {
136
        return this._next
137
    }
138

139
    /**
140
     * @returns the previous sibling of this node if it is exist
141
     */
142
    get previousSibling(): IncrementalNode | undefined {
143
        return this._prev
144
    }
145

146
    /**
147
     * This method is called by the state manager
148
     * when the incremental update should skip several unchanged child nodes.
149
     * @param count - a number of child nodes to skip during the incremental update
150
     * @internal
151
     */
152
    incrementalUpdateSkip(count: uint32) {
153
        if (count > 0) {
154
            const prev = this._incremental
155
            let next = prev !== undefined ? prev._next : this._child
156
            while (1 < count--) {
157
                if (next === undefined) throw new Error("child node is expected here")
158
                next = next._next
159
            }
160
            this._incremental = next
161
        }
162
        else throw new Error("unexpected count of child nodes to skip: " + count)
163
    }
164

165
    /**
166
     * This method is called by the state manager
167
     * when the incremental update of all children of this node is completed.
168
     * @internal
169
     */
170
    incrementalUpdateDone(parent?: IncrementalNode) {
171
        if (this._disposed) throw new Error("child node is already disposed")
172
        this._incremental = undefined
173
        if (parent !== undefined) {
174
            const prev = parent._incremental
175
            const next = prev !== undefined ? prev._next : parent._child
176
            if (this._parent !== undefined) {
177
                if (this._parent != parent) throw new Error("child node belongs to another parent")
178
                if (this != next) throw new Error("child node is not expected here")
179
                parent._incremental = this
180
            } else {
181
                parent._incremental = this
182
                this._prev = prev
183
                this._next = next
184
                this._parent = parent
185
                if (next !== undefined) next._prev = this
186
                if (prev !== undefined) prev._next = this
187
                else parent._child = this
188
                parent.onChildInserted?.(this)
189
            }
190
        }
191
    }
192
}
193

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

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

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

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