sku-pg
Version:
Skulpt is a Javascript implementation of Python 2.x. Python that runs in your browser!
284 lines (235 loc) • 7.56 kB
JavaScript
// Copyright 2010 The Closure Library Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS-IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/**
* @fileoverview Generic tree node data structure with arbitrary number of child
* nodes.
*
*
*/
goog.provide('goog.structs.TreeNode');
goog.require('goog.array');
goog.require('goog.asserts');
goog.require('goog.structs.Node');
/**
* Generic tree node data structure with arbitrary number of child nodes.
* It is possible to create a dynamic tree structure by overriding
* {@link #getParent} and {@link #getChildren} in a subclass. All other getters
* will automatically work.
*
* @param {*} key Key.
* @param {*} value Value.
* @constructor
* @extends {goog.structs.Node}
*/
goog.structs.TreeNode = function(key, value) {
goog.structs.Node.call(this, key, value);
};
goog.inherits(goog.structs.TreeNode, goog.structs.Node);
/**
* Constant for empty array to avoid unnecessary allocations.
* @private
*/
goog.structs.TreeNode.EMPTY_ARRAY_ = [];
/**
* Reference to the parent node or null if it has no parent.
* @type {goog.structs.TreeNode}
* @private
*/
goog.structs.TreeNode.prototype.parent_ = null;
/**
* Child nodes or null in case of leaf node.
* @type {Array.<!goog.structs.TreeNode>}
* @private
*/
goog.structs.TreeNode.prototype.children_ = null;
/**
* @return {!goog.structs.TreeNode} Clone of the tree node without its parent
* and child nodes. The key and the value are copied by reference.
*/
goog.structs.TreeNode.prototype.clone = function() {
return new goog.structs.TreeNode(this.getKey(), this.getValue());
};
/**
* @return {!goog.structs.TreeNode} Clone of the subtree with this node as root.
*/
goog.structs.TreeNode.prototype.deepClone = function() {
var clone = this.clone();
this.forEachChild(function(child) {
clone.addChild(child.deepClone());
});
return clone;
};
/**
* @return {goog.structs.TreeNode} Parent node or null if it has no parent.
*/
goog.structs.TreeNode.prototype.getParent = function() {
return this.parent_;
};
/**
* @return {boolean} Whether the node is a leaf node.
*/
goog.structs.TreeNode.prototype.isLeaf = function() {
return this.getChildren().length == 0;
};
/**
* @return {!Array.<!goog.structs.TreeNode>} Immutable child nodes.
*/
goog.structs.TreeNode.prototype.getChildren = function() {
return this.children_ || goog.structs.TreeNode.EMPTY_ARRAY_;
};
/**
* Gets the child node of this node at the given index.
* @param {number} index Child index.
* @return {goog.structs.TreeNode} The node at the given index or null if not
* found.
*/
goog.structs.TreeNode.prototype.getChildAt = function(index) {
return this.getChildren()[index] || null;
};
/**
* @return {number} The number of ancestors of the node.
*/
goog.structs.TreeNode.prototype.getDepth = function() {
var depth = 0;
var node = this;
while (node.getParent()) {
depth++;
node = node.getParent();
}
return depth;
};
/**
* @return {!Array.<!goog.structs.TreeNode>} All ancestor nodes in bottom-up
* order.
*/
goog.structs.TreeNode.prototype.getAncestors = function() {
var ancestors = [];
var node = this.getParent();
while (node) {
ancestors.push(node);
node = node.getParent();
}
return ancestors;
};
/**
* @return {!goog.structs.TreeNode} The root of the tree structure, i.e. the
* farthest ancestor of the node or the node itself if it has no parents.
*/
goog.structs.TreeNode.prototype.getRoot = function() {
var root = this;
while (root.getParent()) {
root = root.getParent();
}
return root;
};
/**
* Tells whether this node is the ancestor of the given node.
* @param {!goog.structs.TreeNode} node A node.
* @return {boolean} Whether this node is the ancestor of {@code node}.
*/
goog.structs.TreeNode.prototype.contains = function(node) {
do {
node = node.getParent();
} while (node && node != this);
return Boolean(node);
};
/**
* Traverses all child nodes.
* @param {function(!goog.structs.TreeNode, number,
* !Array.<!goog.structs.TreeNode>)} f Callback function. It takes the
* node, its index and the array of all child nodes as arguments.
* @param {Object=} opt_this The object to be used as the value of {@code this}
* within {@code f}.
*/
goog.structs.TreeNode.prototype.forEachChild = function(f, opt_this) {
goog.array.forEach(this.getChildren(), f, opt_this);
};
/**
* Traverses all child nodes recursively.
* @param {function(!goog.structs.TreeNode)} f Callback function. It takes the
* node as argument.
* @param {Object=} opt_this The object to be used as the value of {@code this}
* within {@code f}.
*/
goog.structs.TreeNode.prototype.forEachDescendant = function(f, opt_this) {
goog.array.forEach(this.getChildren(), function(child) {
f.call(opt_this, child);
child.forEachDescendant(f, opt_this);
});
};
/**
* Sets the parent node of this node. The callers must ensure that the parent
* node and only that has this node among its children.
* @param {goog.structs.TreeNode} parent The parent to set. If null, the node
* will be detached from the tree.
* @protected
*/
goog.structs.TreeNode.prototype.setParent = function(parent) {
this.parent_ = parent;
};
/**
* Appends a child node to this node.
* @param {!goog.structs.TreeNode} child Orphan child node.
*/
goog.structs.TreeNode.prototype.addChild = function(child) {
this.addChildAt(child, this.children_ ? this.children_.length : 0);
};
/**
* Inserts a child node at the given index.
* @param {!goog.structs.TreeNode} child Orphan child node.
* @param {number} index The position to insert at.
*/
goog.structs.TreeNode.prototype.addChildAt = function(child, index) {
goog.asserts.assert(!child.getParent());
child.setParent(this);
this.children_ = this.children_ || [];
goog.asserts.assert(index >= 0 && index <= this.children_.length);
goog.array.insertAt(this.children_, child, index);
};
/**
* Removes the child node at the given index.
* @param {number} index The position to remove from.
* @return {goog.structs.TreeNode} The removed node if any.
*/
goog.structs.TreeNode.prototype.removeChildAt = function(index) {
var child = this.children_ && this.children_[index];
if (child) {
child.setParent(null);
goog.array.removeAt(this.children_, index);
if (this.children_.length == 0) {
delete this.children_;
}
return child;
}
return null;
};
/**
* Removes the given child node of this node.
* @param {goog.structs.TreeNode} child The node to remove.
* @return {goog.structs.TreeNode} The removed node if any.
*/
goog.structs.TreeNode.prototype.removeChild = function(child) {
return this.removeChildAt(goog.array.indexOf(this.getChildren(), child));
};
/**
* Removes all child nodes of this node.
*/
goog.structs.TreeNode.prototype.removeChildren = function() {
if (this.children_) {
goog.array.forEach(this.children_, function(child) {
child.setParent(null);
});
}
delete this.children_;
};