UNPKG

7.67 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 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 = Object.assign(Object.assign({}, TreeIterator.DEFAULT_OPTIONS), options);
32 this.delegate = this.iterator(this.root);
33 }
34 // tslint:disable-next-line:typedef
35 [Symbol.iterator]() {
36 return this.delegate;
37 }
38 next() {
39 return this.delegate.next();
40 }
41 children(node) {
42 if (!tree_1.CompositeTreeNode.is(node)) {
43 return undefined;
44 }
45 if (this.options.pruneCollapsed && this.isCollapsed(node)) {
46 return undefined;
47 }
48 return node.children.slice();
49 }
50 isCollapsed(node) {
51 return tree_expansion_1.ExpandableTreeNode.isCollapsed(node);
52 }
53 isEmpty(nodes) {
54 return nodes === undefined || nodes.length === 0;
55 }
56}
57exports.AbstractTreeIterator = AbstractTreeIterator;
58class DepthFirstTreeIterator extends AbstractTreeIterator {
59 iterator(root) {
60 return Iterators.depthFirst(root, this.children.bind(this));
61 }
62}
63exports.DepthFirstTreeIterator = DepthFirstTreeIterator;
64class BreadthFirstTreeIterator extends AbstractTreeIterator {
65 iterator(root) {
66 return Iterators.breadthFirst(root, this.children.bind(this));
67 }
68}
69exports.BreadthFirstTreeIterator = BreadthFirstTreeIterator;
70/**
71 * This tree iterator visits all nodes from top to bottom considering the following rules.
72 *
73 * Let assume the following tree:
74 * ```
75 * R
76 * |
77 * +---1
78 * | |
79 * | +---1.1
80 * | |
81 * | +---1.2
82 * | |
83 * | +---1.3
84 * | | |
85 * | | +---1.3.1
86 * | | |
87 * | | +---1.3.2
88 * | |
89 * | +---1.4
90 * |
91 * +---2
92 * |
93 * +---2.1
94 * ```
95 * When selecting `1.2` as the root, the normal `DepthFirstTreeIterator` would stop on `1.2` as it does not have children,
96 * 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
97 * `1.2`, `1.3`, `1.3.1`, `1.3.2`, and `1.4` then jumps to `2` and continues with `2.1`.
98 */
99class TopDownTreeIterator extends AbstractTreeIterator {
100 iterator(root) {
101 const doNext = this.doNext.bind(this);
102 return (function* () {
103 let next = root;
104 while (next) {
105 yield next;
106 next = doNext(next);
107 }
108 }).bind(this)();
109 }
110 doNext(node) {
111 return this.findFirstChild(node) || this.findNextSibling(node);
112 }
113 findFirstChild(node) {
114 return (this.children(node) || [])[0];
115 }
116 findNextSibling(node) {
117 if (!node) {
118 return undefined;
119 }
120 if (this.options.pruneSiblings && node === this.root) {
121 return undefined;
122 }
123 if (node.nextSibling) {
124 return node.nextSibling;
125 }
126 return this.findNextSibling(node.parent);
127 }
128}
129exports.TopDownTreeIterator = TopDownTreeIterator;
130/**
131 * Unlike other tree iterators, this does not visit all the nodes, it stops once it reaches the root node
132 * while traversing up the tree hierarchy in an inverse pre-order fashion. This is the counterpart of the `TopDownTreeIterator`.
133 */
134class BottomUpTreeIterator extends AbstractTreeIterator {
135 iterator(root) {
136 const doNext = this.doNext.bind(this);
137 return (function* () {
138 let next = root;
139 while (next) {
140 yield next;
141 next = doNext(next);
142 }
143 }).bind(this)();
144 }
145 doNext(node) {
146 const previousSibling = node.previousSibling;
147 const lastChild = this.lastChild(previousSibling);
148 return lastChild || node.parent;
149 }
150 lastChild(node) {
151 const children = node ? this.children(node) : [];
152 if (this.isEmpty(children)) {
153 return node;
154 }
155 if (tree_1.CompositeTreeNode.is(node)) {
156 const lastChild = tree_1.CompositeTreeNode.getLastChild(node);
157 return this.lastChild(lastChild);
158 }
159 return undefined;
160 }
161}
162exports.BottomUpTreeIterator = BottomUpTreeIterator;
163var Iterators;
164(function (Iterators) {
165 /**
166 * Generator for depth first, pre-order tree traversal iteration.
167 */
168 function* depthFirst(root, children, include = () => true) {
169 const stack = [];
170 stack.push(root);
171 while (stack.length > 0) {
172 const top = stack.pop();
173 yield top;
174 stack.push(...(children(top) || []).filter(include).reverse());
175 }
176 }
177 Iterators.depthFirst = depthFirst;
178 /**
179 * Generator for breadth first tree traversal iteration.
180 */
181 function* breadthFirst(root, children, include = () => true) {
182 const queue = [];
183 queue.push(root);
184 while (queue.length > 0) {
185 const head = queue.shift();
186 yield head;
187 queue.push(...(children(head) || []).filter(include));
188 }
189 }
190 Iterators.breadthFirst = breadthFirst;
191 /**
192 * Returns with the iterator of the argument.
193 */
194 function asIterator(elements) {
195 return elements.slice()[Symbol.iterator]();
196 }
197 Iterators.asIterator = asIterator;
198 /**
199 * Returns an iterator that cycles indefinitely over the elements of iterable.
200 * - If `start` is given it starts the iteration from that element. Otherwise, it starts with the first element of the array.
201 * - If `start` is given, it must contain by the `elements` array. Otherwise, an error will be thrown.
202 *
203 * **Warning**: Typical uses of the resulting iterator may produce an infinite loop. You should use an explicit break.
204 */
205 function* cycle(elements, start) {
206 const copy = elements.slice();
207 let index = !!start ? copy.indexOf(start) : 0;
208 if (index === -1) {
209 throw new Error(`${start} is not contained in ${copy}.`);
210 }
211 while (true) {
212 yield copy[index];
213 index++;
214 if (index === copy.length) {
215 index = 0;
216 }
217 }
218 }
219 Iterators.cycle = cycle;
220})(Iterators = exports.Iterators || (exports.Iterators = {}));
221//# sourceMappingURL=tree-iterator.js.map
\No newline at end of file