UNPKG

7.69 kBJavaScriptView Raw
1"use strict";
2// *****************************************************************************
3// Copyright (C) 2017 TypeFox and others.
4//
5// This program and the accompanying materials are made available under the
6// terms of the Eclipse Public License v. 2.0 which is available at
7// http://www.eclipse.org/legal/epl-2.0.
8//
9// This Source Code may also be made available under the following Secondary
10// Licenses when the conditions for such availability set forth in the Eclipse
11// Public License v. 2.0 are satisfied: GNU General Public License, version 2
12// with the GNU Classpath Exception which is available at
13// https://www.gnu.org/software/classpath/license.html.
14//
15// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
16// *****************************************************************************
17Object.defineProperty(exports, "__esModule", { value: true });
18exports.Iterators = exports.BottomUpTreeIterator = exports.TopDownTreeIterator = exports.BreadthFirstTreeIterator = exports.DepthFirstTreeIterator = exports.AbstractTreeIterator = exports.TreeIterator = void 0;
19const tree_1 = require("./tree");
20const tree_expansion_1 = require("./tree-expansion");
21var TreeIterator;
22(function (TreeIterator) {
23 TreeIterator.DEFAULT_OPTIONS = {
24 pruneCollapsed: false,
25 pruneSiblings: false
26 };
27})(TreeIterator = exports.TreeIterator || (exports.TreeIterator = {}));
28class AbstractTreeIterator {
29 constructor(root, options) {
30 this.root = root;
31 this.options = {
32 ...TreeIterator.DEFAULT_OPTIONS,
33 ...options
34 };
35 this.delegate = this.iterator(this.root);
36 }
37 // tslint:disable-next-line:typedef
38 [Symbol.iterator]() {
39 return this.delegate;
40 }
41 next() {
42 return this.delegate.next();
43 }
44 children(node) {
45 if (!tree_1.CompositeTreeNode.is(node)) {
46 return undefined;
47 }
48 if (this.options.pruneCollapsed && this.isCollapsed(node)) {
49 return undefined;
50 }
51 return node.children.slice();
52 }
53 isCollapsed(node) {
54 return tree_expansion_1.ExpandableTreeNode.isCollapsed(node);
55 }
56 isEmpty(nodes) {
57 return nodes === undefined || nodes.length === 0;
58 }
59}
60exports.AbstractTreeIterator = AbstractTreeIterator;
61class DepthFirstTreeIterator extends AbstractTreeIterator {
62 iterator(root) {
63 return Iterators.depthFirst(root, this.children.bind(this));
64 }
65}
66exports.DepthFirstTreeIterator = DepthFirstTreeIterator;
67class BreadthFirstTreeIterator extends AbstractTreeIterator {
68 iterator(root) {
69 return Iterators.breadthFirst(root, this.children.bind(this));
70 }
71}
72exports.BreadthFirstTreeIterator = BreadthFirstTreeIterator;
73/**
74 * This tree iterator visits all nodes from top to bottom considering the following rules.
75 *
76 * Let assume the following tree:
77 * ```
78 * R
79 * |
80 * +---1
81 * | |
82 * | +---1.1
83 * | |
84 * | +---1.2
85 * | |
86 * | +---1.3
87 * | | |
88 * | | +---1.3.1
89 * | | |
90 * | | +---1.3.2
91 * | |
92 * | +---1.4
93 * |
94 * +---2
95 * |
96 * +---2.1
97 * ```
98 * When selecting `1.2` as the root, the normal `DepthFirstTreeIterator` would stop on `1.2` as it does not have children,
99 * but this iterator will visit the next sibling (`1.3` and `1.4` but **not** `1.1`) nodes. So the expected traversal order will be
100 * `1.2`, `1.3`, `1.3.1`, `1.3.2`, and `1.4` then jumps to `2` and continues with `2.1`.
101 */
102class TopDownTreeIterator extends AbstractTreeIterator {
103 iterator(root) {
104 const doNext = this.doNext.bind(this);
105 return (function* () {
106 let next = root;
107 while (next) {
108 yield next;
109 next = doNext(next);
110 }
111 }).bind(this)();
112 }
113 doNext(node) {
114 return this.findFirstChild(node) || this.findNextSibling(node);
115 }
116 findFirstChild(node) {
117 return (this.children(node) || [])[0];
118 }
119 findNextSibling(node) {
120 if (!node) {
121 return undefined;
122 }
123 if (this.options.pruneSiblings && node === this.root) {
124 return undefined;
125 }
126 if (node.nextSibling) {
127 return node.nextSibling;
128 }
129 return this.findNextSibling(node.parent);
130 }
131}
132exports.TopDownTreeIterator = TopDownTreeIterator;
133/**
134 * Unlike other tree iterators, this does not visit all the nodes, it stops once it reaches the root node
135 * while traversing up the tree hierarchy in an inverse pre-order fashion. This is the counterpart of the `TopDownTreeIterator`.
136 */
137class BottomUpTreeIterator extends AbstractTreeIterator {
138 iterator(root) {
139 const doNext = this.doNext.bind(this);
140 return (function* () {
141 let next = root;
142 while (next) {
143 yield next;
144 next = doNext(next);
145 }
146 }).bind(this)();
147 }
148 doNext(node) {
149 const previousSibling = node.previousSibling;
150 const lastChild = this.lastChild(previousSibling);
151 return lastChild || node.parent;
152 }
153 lastChild(node) {
154 const children = node ? this.children(node) : [];
155 if (this.isEmpty(children)) {
156 return node;
157 }
158 if (tree_1.CompositeTreeNode.is(node)) {
159 const lastChild = tree_1.CompositeTreeNode.getLastChild(node);
160 return this.lastChild(lastChild);
161 }
162 return undefined;
163 }
164}
165exports.BottomUpTreeIterator = BottomUpTreeIterator;
166var Iterators;
167(function (Iterators) {
168 /**
169 * Generator for depth first, pre-order tree traversal iteration.
170 */
171 function* depthFirst(root, children, include = () => true) {
172 let stack = [];
173 stack.push(root);
174 while (stack.length > 0) {
175 const top = stack.pop();
176 yield top;
177 stack = stack.concat((children(top) || []).filter(include).reverse());
178 }
179 }
180 Iterators.depthFirst = depthFirst;
181 /**
182 * Generator for breadth first tree traversal iteration.
183 */
184 function* breadthFirst(root, children, include = () => true) {
185 let queue = [];
186 queue.push(root);
187 while (queue.length > 0) {
188 const head = queue.shift();
189 yield head;
190 queue = queue.concat((children(head) || []).filter(include));
191 }
192 }
193 Iterators.breadthFirst = breadthFirst;
194 /**
195 * Returns with the iterator of the argument.
196 */
197 function asIterator(elements) {
198 return elements.slice()[Symbol.iterator]();
199 }
200 Iterators.asIterator = asIterator;
201 /**
202 * Returns an iterator that cycles indefinitely over the elements of iterable.
203 * - If `start` is given it starts the iteration from that element. Otherwise, it starts with the first element of the array.
204 * - If `start` is given, it must contain by the `elements` array. Otherwise, an error will be thrown.
205 *
206 * **Warning**: Typical uses of the resulting iterator may produce an infinite loop. You should use an explicit break.
207 */
208 function* cycle(elements, start) {
209 const copy = elements.slice();
210 let index = !!start ? copy.indexOf(start) : 0;
211 if (index === -1) {
212 throw new Error(`${start} is not contained in ${copy}.`);
213 }
214 while (true) {
215 yield copy[index];
216 index++;
217 if (index === copy.length) {
218 index = 0;
219 }
220 }
221 }
222 Iterators.cycle = cycle;
223})(Iterators = exports.Iterators || (exports.Iterators = {}));
224//# sourceMappingURL=tree-iterator.js.map
\No newline at end of file