Update wmts-hidpi, add nicer-api-docs

This commit is contained in:
Andreas Hocevar
2014-05-06 13:02:46 -05:00
parent b3ac1afd00
commit 1e25fc5585
2239 changed files with 3726515 additions and 37010 deletions

View File

@@ -0,0 +1,835 @@
// Copyright 2007 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 Datastructure: AvlTree.
*
*
* This file provides the implementation of an AVL-Tree datastructure. The tree
* maintains a set of unique values in a sorted order. The values can be
* accessed efficiently in their sorted order since the tree enforces an O(logn)
* maximum height. See http://en.wikipedia.org/wiki/Avl_tree for more detail.
*
* The big-O notation for all operations are below:
* <pre>
* Method big-O
* ----------------------------------------------------------------------------
* - add O(logn)
* - remove O(logn)
* - clear O(1)
* - contains O(logn)
* - getCount O(1)
* - getMinimum O(1), or O(logn) when optional root is specified
* - getMaximum O(1), or O(logn) when optional root is specified
* - getHeight O(1)
* - getValues O(n)
* - inOrderTraverse O(logn + k), where k is number of traversed nodes
* - reverseOrderTraverse O(logn + k), where k is number of traversed nodes
* </pre>
*/
goog.provide('goog.structs.AvlTree');
goog.provide('goog.structs.AvlTree.Node');
goog.require('goog.structs.Collection');
/**
* Constructs an AVL-Tree, which uses the specified comparator to order its
* values. The values can be accessed efficiently in their sorted order since
* the tree enforces a O(logn) maximum height.
*
* @param {Function=} opt_comparator Function used to order the tree's nodes.
* @constructor
* @implements {goog.structs.Collection}
*/
goog.structs.AvlTree = function(opt_comparator) {
this.comparator_ = opt_comparator ||
goog.structs.AvlTree.DEFAULT_COMPARATOR_;
};
/**
* String comparison function used to compare values in the tree. This function
* is used by default if no comparator is specified in the tree's constructor.
*
* @param {string} a The first string.
* @param {string} b The second string.
* @return {number} -1 if a < b, 1 if a > b, 0 if a = b.
* @private
*/
goog.structs.AvlTree.DEFAULT_COMPARATOR_ = function(a, b) {
if (String(a) < String(b)) {
return -1;
} else if (String(a) > String(b)) {
return 1;
}
return 0;
};
/**
* Pointer to the root node of the tree.
*
* @type {goog.structs.AvlTree.Node}
* @private
*/
goog.structs.AvlTree.prototype.root_ = null;
/**
* Comparison function used to compare values in the tree. This function should
* take two values, a and b, and return x where:
* <pre>
* x < 0 if a < b,
* x > 0 if a > b,
* x = 0 otherwise
* </pre>
*
* @type {Function}
* @private
*/
goog.structs.AvlTree.prototype.comparator_ = null;
/**
* Pointer to the node with the smallest value in the tree.
*
* @type {goog.structs.AvlTree.Node}
* @private
*/
goog.structs.AvlTree.prototype.minNode_ = null;
/**
* Pointer to the node with the largest value in the tree.
*
* @type {goog.structs.AvlTree.Node}
* @private
*/
goog.structs.AvlTree.prototype.maxNode_ = null;
/**
* Inserts a node into the tree with the specified value if the tree does
* not already contain a node with the specified value. If the value is
* inserted, the tree is balanced to enforce the AVL-Tree height property.
*
* @param {*} value Value to insert into the tree.
* @return {boolean} Whether value was inserted into the tree.
* @override
*/
goog.structs.AvlTree.prototype.add = function(value) {
// If the tree is empty, create a root node with the specified value
if (this.root_ == null) {
this.root_ = new goog.structs.AvlTree.Node(value);
this.minNode_ = this.root_;
this.maxNode_ = this.root_;
return true;
}
// This will be set to the new node if a new node is added.
var newNode = null;
// Depth traverse the tree and insert the value if we reach a null node
this.traverse_(function(node) {
var retNode = null;
if (this.comparator_(node.value, value) > 0) {
retNode = node.left;
if (node.left == null) {
newNode = new goog.structs.AvlTree.Node(value, node);
node.left = newNode;
if (node == this.minNode_) {
this.minNode_ = newNode;
}
}
} else if (this.comparator_(node.value, value) < 0) {
retNode = node.right;
if (node.right == null) {
newNode = new goog.structs.AvlTree.Node(value, node);
node.right = newNode;
if (node == this.maxNode_) {
this.maxNode_ = newNode;
}
}
}
return retNode; // If null, we'll stop traversing the tree
});
// If a node was added, increment counts and balance tree.
if (newNode) {
this.traverse_(
function(node) {
node.count++;
return node.parent;
},
newNode.parent);
this.balance_(newNode.parent); // Maintain the AVL-tree balance
}
// Return true if a node was added, false otherwise
return !!newNode;
};
/**
* Removes a node from the tree with the specified value if the tree contains a
* node with this value. If a node is removed the tree is balanced to enforce
* the AVL-Tree height property. The value of the removed node is returned.
*
* @param {*} value Value to find and remove from the tree.
* @return {*} The value of the removed node or null if the value was not in
* the tree.
* @override
*/
goog.structs.AvlTree.prototype.remove = function(value) {
// Assume the value is not removed and set the value when it is removed
var retValue = null;
// Depth traverse the tree and remove the value if we find it
this.traverse_(function(node) {
var retNode = null;
if (this.comparator_(node.value, value) > 0) {
retNode = node.left;
} else if (this.comparator_(node.value, value) < 0) {
retNode = node.right;
} else {
retValue = node.value;
this.removeNode_(node);
}
return retNode; // If null, we'll stop traversing the tree
});
// Return the value that was removed, null if the value was not in the tree
return retValue;
};
/**
* Removes all nodes from the tree.
*/
goog.structs.AvlTree.prototype.clear = function() {
this.root_ = null;
this.minNode_ = null;
this.maxNode_ = null;
};
/**
* Returns true if the tree contains a node with the specified value, false
* otherwise.
*
* @param {*} value Value to find in the tree.
* @return {boolean} Whether the tree contains a node with the specified value.
* @override
*/
goog.structs.AvlTree.prototype.contains = function(value) {
// Assume the value is not in the tree and set this value if it is found
var isContained = false;
// Depth traverse the tree and set isContained if we find the node
this.traverse_(function(node) {
var retNode = null;
if (this.comparator_(node.value, value) > 0) {
retNode = node.left;
} else if (this.comparator_(node.value, value) < 0) {
retNode = node.right;
} else {
isContained = true;
}
return retNode; // If null, we'll stop traversing the tree
});
// Return true if the value is contained in the tree, false otherwise
return isContained;
};
/**
* Returns the number of values stored in the tree.
*
* @return {number} The number of values stored in the tree.
* @override
*/
goog.structs.AvlTree.prototype.getCount = function() {
return this.root_ ? this.root_.count : 0;
};
/**
* Returns a k-th smallest value, based on the comparator, where 0 <= k <
* this.getCount().
* @param {number} k The number k.
* @return {*} The k-th smallest value.
*/
goog.structs.AvlTree.prototype.getKthValue = function(k) {
if (k < 0 || k >= this.getCount()) {
return null;
}
return this.getKthNode_(k).value;
};
/**
* Returns the value u, such that u is contained in the tree and u < v, for all
* values v in the tree where v != u.
*
* @return {*} The minimum value contained in the tree.
*/
goog.structs.AvlTree.prototype.getMinimum = function() {
return this.getMinNode_().value;
};
/**
* Returns the value u, such that u is contained in the tree and u > v, for all
* values v in the tree where v != u.
*
* @return {*} The maximum value contained in the tree.
*/
goog.structs.AvlTree.prototype.getMaximum = function() {
return this.getMaxNode_().value;
};
/**
* Returns the height of the tree (the maximum depth). This height should
* always be <= 1.4405*(Math.log(n+2)/Math.log(2))-1.3277, where n is the
* number of nodes in the tree.
*
* @return {number} The height of the tree.
*/
goog.structs.AvlTree.prototype.getHeight = function() {
return this.root_ ? this.root_.height : 0;
};
/**
* Inserts the values stored in the tree into a new Array and returns the Array.
*
* @return {Array} An array containing all of the trees values in sorted order.
*/
goog.structs.AvlTree.prototype.getValues = function() {
var ret = [];
this.inOrderTraverse(function(value) {
ret.push(value);
});
return ret;
};
/**
* Performs an in-order traversal of the tree and calls {@code func} with each
* traversed node, optionally starting from the smallest node with a value >= to
* the specified start value. The traversal ends after traversing the tree's
* maximum node or when {@code func} returns a value that evaluates to true.
*
* @param {Function} func Function to call on each traversed node.
* @param {Object=} opt_startValue If specified, traversal will begin on the
* node with the smallest value >= opt_startValue.
*/
goog.structs.AvlTree.prototype.inOrderTraverse =
function(func, opt_startValue) {
// If our tree is empty, return immediately
if (!this.root_) {
return;
}
// Depth traverse the tree to find node to begin in-order traversal from
var startNode;
if (opt_startValue) {
this.traverse_(function(node) {
var retNode = null;
if (this.comparator_(node.value, opt_startValue) > 0) {
retNode = node.left;
startNode = node;
} else if (this.comparator_(node.value, opt_startValue) < 0) {
retNode = node.right;
} else {
startNode = node;
}
return retNode; // If null, we'll stop traversing the tree
});
} else {
startNode = this.getMinNode_();
}
// Traverse the tree and call func on each traversed node's value
var node = startNode, prev = startNode.left ? startNode.left : startNode;
while (node != null) {
if (node.left != null && node.left != prev && node.right != prev) {
node = node.left;
} else {
if (node.right != prev) {
if (func(node.value)) {
return;
}
}
var temp = node;
node = node.right != null && node.right != prev ?
node.right :
node.parent;
prev = temp;
}
}
};
/**
* Performs a reverse-order traversal of the tree and calls {@code func} with
* each traversed node, optionally starting from the largest node with a value
* <= to the specified start value. The traversal ends after traversing the
* tree's minimum node or when func returns a value that evaluates to true.
*
* @param {Function} func Function to call on each traversed node.
* @param {Object=} opt_startValue If specified, traversal will begin on the
* node with the largest value <= opt_startValue.
*/
goog.structs.AvlTree.prototype.reverseOrderTraverse =
function(func, opt_startValue) {
// If our tree is empty, return immediately
if (!this.root_) {
return;
}
// Depth traverse the tree to find node to begin reverse-order traversal from
var startNode;
if (opt_startValue) {
this.traverse_(goog.bind(function(node) {
var retNode = null;
if (this.comparator_(node.value, opt_startValue) > 0) {
retNode = node.left;
} else if (this.comparator_(node.value, opt_startValue) < 0) {
retNode = node.right;
startNode = node;
} else {
startNode = node;
}
return retNode; // If null, we'll stop traversing the tree
}, this));
} else {
startNode = this.getMaxNode_();
}
// Traverse the tree and call func on each traversed node's value
var node = startNode, prev = startNode.right ? startNode.right : startNode;
while (node != null) {
if (node.right != null && node.right != prev && node.left != prev) {
node = node.right;
} else {
if (node.left != prev) {
if (func(node.value)) {
return;
}
}
var temp = node;
node = node.left != null && node.left != prev ?
node.left :
node.parent;
prev = temp;
}
}
};
/**
* Performs a traversal defined by the supplied {@code traversalFunc}. The first
* call to {@code traversalFunc} is passed the root or the optionally specified
* startNode. After that, calls {@code traversalFunc} with the node returned
* by the previous call to {@code traversalFunc} until {@code traversalFunc}
* returns null or the optionally specified endNode. The first call to
* traversalFunc is passed the root or the optionally specified startNode.
*
* @param {Function} traversalFunc Function used to traverse the tree. Takes a
* node as a parameter and returns a node.
* @param {goog.structs.AvlTree.Node=} opt_startNode The node at which the
* traversal begins.
* @param {goog.structs.AvlTree.Node=} opt_endNode The node at which the
* traversal ends.
* @private
*/
goog.structs.AvlTree.prototype.traverse_ =
function(traversalFunc, opt_startNode, opt_endNode) {
var node = opt_startNode ? opt_startNode : this.root_;
var endNode = opt_endNode ? opt_endNode : null;
while (node && node != endNode) {
node = traversalFunc.call(this, node);
}
};
/**
* Ensures that the specified node and all its ancestors are balanced. If they
* are not, performs left and right tree rotations to achieve a balanced
* tree. This method assumes that at most 2 rotations are necessary to balance
* the tree (which is true for AVL-trees that are balanced after each node is
* added or removed).
*
* @param {goog.structs.AvlTree.Node} node Node to begin balance from.
* @private
*/
goog.structs.AvlTree.prototype.balance_ = function(node) {
this.traverse_(function(node) {
// Calculate the left and right node's heights
var lh = node.left ? node.left.height : 0;
var rh = node.right ? node.right.height : 0;
// Rotate tree rooted at this node if it is not AVL-tree balanced
if (lh - rh > 1) {
if (node.left.right && (!node.left.left ||
node.left.left.height < node.left.right.height)) {
this.leftRotate_(node.left);
}
this.rightRotate_(node);
} else if (rh - lh > 1) {
if (node.right.left && (!node.right.right ||
node.right.right.height < node.right.left.height)) {
this.rightRotate_(node.right);
}
this.leftRotate_(node);
}
// Recalculate the left and right node's heights
lh = node.left ? node.left.height : 0;
rh = node.right ? node.right.height : 0;
// Set this node's height
node.height = Math.max(lh, rh) + 1;
// Traverse up tree and balance parent
return node.parent;
}, node);
};
/**
* Performs a left tree rotation on the specified node.
*
* @param {goog.structs.AvlTree.Node} node Pivot node to rotate from.
* @private
*/
goog.structs.AvlTree.prototype.leftRotate_ = function(node) {
// Re-assign parent-child references for the parent of the node being removed
if (node.isLeftChild()) {
node.parent.left = node.right;
node.right.parent = node.parent;
} else if (node.isRightChild()) {
node.parent.right = node.right;
node.right.parent = node.parent;
} else {
this.root_ = node.right;
this.root_.parent = null;
}
// Re-assign parent-child references for the child of the node being removed
var temp = node.right;
node.right = node.right.left;
if (node.right != null) node.right.parent = node;
temp.left = node;
node.parent = temp;
// Update counts.
temp.count = node.count;
node.count -= (temp.right ? temp.right.count : 0) + 1;
};
/**
* Performs a right tree rotation on the specified node.
*
* @param {goog.structs.AvlTree.Node} node Pivot node to rotate from.
* @private
*/
goog.structs.AvlTree.prototype.rightRotate_ = function(node) {
// Re-assign parent-child references for the parent of the node being removed
if (node.isLeftChild()) {
node.parent.left = node.left;
node.left.parent = node.parent;
} else if (node.isRightChild()) {
node.parent.right = node.left;
node.left.parent = node.parent;
} else {
this.root_ = node.left;
this.root_.parent = null;
}
// Re-assign parent-child references for the child of the node being removed
var temp = node.left;
node.left = node.left.right;
if (node.left != null) node.left.parent = node;
temp.right = node;
node.parent = temp;
// Update counts.
temp.count = node.count;
node.count -= (temp.left ? temp.left.count : 0) + 1;
};
/**
* Removes the specified node from the tree and ensures the tree still
* maintains the AVL-tree balance.
*
* @param {goog.structs.AvlTree.Node} node The node to be removed.
* @private
*/
goog.structs.AvlTree.prototype.removeNode_ = function(node) {
// Perform normal binary tree node removal, but balance the tree, starting
// from where we removed the node
if (node.left != null || node.right != null) {
var b = null; // Node to begin balance from
var r; // Node to replace the node being removed
if (node.left != null) {
r = this.getMaxNode_(node.left);
// Update counts.
this.traverse_(function(node) {
node.count--;
return node.parent;
}, r);
if (r != node.left) {
r.parent.right = r.left;
if (r.left) r.left.parent = r.parent;
r.left = node.left;
r.left.parent = r;
b = r.parent;
}
r.parent = node.parent;
r.right = node.right;
if (r.right) r.right.parent = r;
if (node == this.maxNode_) this.maxNode_ = r;
r.count = node.count;
} else {
r = this.getMinNode_(node.right);
// Update counts.
this.traverse_(function(node) {
node.count--;
return node.parent;
}, r);
if (r != node.right) {
r.parent.left = r.right;
if (r.right) r.right.parent = r.parent;
r.right = node.right;
r.right.parent = r;
b = r.parent;
}
r.parent = node.parent;
r.left = node.left;
if (r.left) r.left.parent = r;
if (node == this.minNode_) this.minNode_ = r;
r.count = node.count;
}
// Update the parent of the node being removed to point to its replace
if (node.isLeftChild()) {
node.parent.left = r;
} else if (node.isRightChild()) {
node.parent.right = r;
} else {
this.root_ = r;
}
// Balance the tree
this.balance_(b ? b : r);
} else {
// Update counts.
this.traverse_(function(node) {
node.count--;
return node.parent;
}, node.parent);
// If the node is a leaf, remove it and balance starting from its parent
if (node.isLeftChild()) {
this.special = 1;
node.parent.left = null;
if (node == this.minNode_) this.minNode_ = node.parent;
this.balance_(node.parent);
} else if (node.isRightChild()) {
node.parent.right = null;
if (node == this.maxNode_) this.maxNode_ = node.parent;
this.balance_(node.parent);
} else {
this.clear();
}
}
};
/**
* Returns the node in the tree that has k nodes before it in an in-order
* traversal, optionally rooted at {@code opt_rootNode}.
*
* @param {number} k The number of nodes before the node to be returned in an
* in-order traversal, where 0 <= k < root.count.
* @param {goog.structs.AvlTree.Node=} opt_rootNode Optional root node.
* @return {goog.structs.AvlTree.Node} The node at the specified index.
* @private
*/
goog.structs.AvlTree.prototype.getKthNode_ = function(k, opt_rootNode) {
var root = opt_rootNode || this.root_;
var numNodesInLeftSubtree = root.left ? root.left.count : 0;
if (k < numNodesInLeftSubtree) {
return this.getKthNode_(k, root.left);
} else if (k == numNodesInLeftSubtree) {
return root;
} else {
return this.getKthNode_(k - numNodesInLeftSubtree - 1, root.right);
}
};
/**
* Returns the node with the smallest value in tree, optionally rooted at
* {@code opt_rootNode}.
*
* @param {goog.structs.AvlTree.Node=} opt_rootNode Optional root node.
* @return {goog.structs.AvlTree.Node} The node with the smallest value in
* the tree.
* @private
*/
goog.structs.AvlTree.prototype.getMinNode_ = function(opt_rootNode) {
if (!opt_rootNode) {
return this.minNode_;
}
var minNode = opt_rootNode;
this.traverse_(function(node) {
var retNode = null;
if (node.left) {
minNode = node.left;
retNode = node.left;
}
return retNode; // If null, we'll stop traversing the tree
}, opt_rootNode);
return minNode;
};
/**
* Returns the node with the largest value in tree, optionally rooted at
* opt_rootNode.
*
* @param {goog.structs.AvlTree.Node=} opt_rootNode Optional root node.
* @return {goog.structs.AvlTree.Node} The node with the largest value in
* the tree.
* @private
*/
goog.structs.AvlTree.prototype.getMaxNode_ = function(opt_rootNode) {
if (!opt_rootNode) {
return this.maxNode_;
}
var maxNode = opt_rootNode;
this.traverse_(function(node) {
var retNode = null;
if (node.right) {
maxNode = node.right;
retNode = node.right;
}
return retNode; // If null, we'll stop traversing the tree
}, opt_rootNode);
return maxNode;
};
/**
* Constructs an AVL-Tree node with the specified value. If no parent is
* specified, the node's parent is assumed to be null. The node's height
* defaults to 1 and its children default to null.
*
* @param {*} value Value to store in the node.
* @param {goog.structs.AvlTree.Node=} opt_parent Optional parent node.
* @constructor
*/
goog.structs.AvlTree.Node = function(value, opt_parent) {
/**
* The value stored by the node.
*
* @type {*}
*/
this.value = value;
/**
* The node's parent. Null if the node is the root.
*
* @type {goog.structs.AvlTree.Node}
*/
this.parent = opt_parent ? opt_parent : null;
/**
* The number of nodes in the subtree rooted at this node.
*
* @type {number}
*/
this.count = 1;
};
/**
* The node's left child. Null if the node does not have a left child.
*
* @type {goog.structs.AvlTree.Node?}
*/
goog.structs.AvlTree.Node.prototype.left = null;
/**
* The node's right child. Null if the node does not have a right child.
*
* @type {goog.structs.AvlTree.Node?}
*/
goog.structs.AvlTree.Node.prototype.right = null;
/**
* The height of the tree rooted at this node.
*
* @type {number}
*/
goog.structs.AvlTree.Node.prototype.height = 1;
/**
* Returns true iff the specified node has a parent and is the right child of
* its parent.
*
* @return {boolean} Whether the specified node has a parent and is the right
* child of its parent.
*/
goog.structs.AvlTree.Node.prototype.isRightChild = function() {
return !!this.parent && this.parent.right == this;
};
/**
* Returns true iff the specified node has a parent and is the left child of
* its parent.
*
* @return {boolean} Whether the specified node has a parent and is the left
* child of its parent.
*/
goog.structs.AvlTree.Node.prototype.isLeftChild = function() {
return !!this.parent && this.parent.left == this;
};

View File

@@ -0,0 +1,223 @@
// Copyright 2006 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 Datastructure: Circular Buffer.
*
* Implements a buffer with a maximum size. New entries override the oldest
* entries when the maximum size has been reached.
*
*/
goog.provide('goog.structs.CircularBuffer');
/**
* Class for CircularBuffer.
* @param {number=} opt_maxSize The maximum size of the buffer.
* @constructor
*/
goog.structs.CircularBuffer = function(opt_maxSize) {
/**
* Maximum size of the the circular array structure.
* @type {number}
* @private
*/
this.maxSize_ = opt_maxSize || 100;
/**
* Underlying array for the CircularBuffer.
* @type {Array}
* @private
*/
this.buff_ = [];
};
/**
* Index of the next element in the circular array structure.
* @type {number}
* @private
*/
goog.structs.CircularBuffer.prototype.nextPtr_ = 0;
/**
* Adds an item to the buffer. May remove the oldest item if the buffer is at
* max size.
* @param {*} item The item to add.
* @return {*} The removed old item, if the buffer is at max size.
* Return undefined, otherwise.
*/
goog.structs.CircularBuffer.prototype.add = function(item) {
var previousItem = this.buff_[this.nextPtr_];
this.buff_[this.nextPtr_] = item;
this.nextPtr_ = (this.nextPtr_ + 1) % this.maxSize_;
return previousItem;
};
/**
* Returns the item at the specified index.
* @param {number} index The index of the item. The index of an item can change
* after calls to {@code add()} if the buffer is at maximum size.
* @return {*} The item at the specified index.
*/
goog.structs.CircularBuffer.prototype.get = function(index) {
index = this.normalizeIndex_(index);
return this.buff_[index];
};
/**
* Sets the item at the specified index.
* @param {number} index The index of the item. The index of an item can change
* after calls to {@code add()} if the buffer is at maximum size.
* @param {*} item The item to add.
*/
goog.structs.CircularBuffer.prototype.set = function(index, item) {
index = this.normalizeIndex_(index);
this.buff_[index] = item;
};
/**
* Returns the current number of items in the buffer.
* @return {number} The current number of items in the buffer.
*/
goog.structs.CircularBuffer.prototype.getCount = function() {
return this.buff_.length;
};
/**
* @return {boolean} Whether the buffer is empty.
*/
goog.structs.CircularBuffer.prototype.isEmpty = function() {
return this.buff_.length == 0;
};
/**
* Empties the current buffer.
*/
goog.structs.CircularBuffer.prototype.clear = function() {
this.buff_.length = 0;
this.nextPtr_ = 0;
};
/**
* @return {Array} The values in the buffer.
*/
goog.structs.CircularBuffer.prototype.getValues = function() {
// getNewestValues returns all the values if the maxCount parameter is the
// count
return this.getNewestValues(this.getCount());
};
/**
* Returns the newest values in the buffer up to {@code count}.
* @param {number} maxCount The maximum number of values to get. Should be a
* positive number.
* @return {Array} The newest values in the buffer up to {@code count}.
*/
goog.structs.CircularBuffer.prototype.getNewestValues = function(maxCount) {
var l = this.getCount();
var start = this.getCount() - maxCount;
var rv = [];
for (var i = start; i < l; i++) {
rv[i] = this.get(i);
}
return rv;
};
/**
* @return {Array} The indexes in the buffer.
*/
goog.structs.CircularBuffer.prototype.getKeys = function() {
var rv = [];
var l = this.getCount();
for (var i = 0; i < l; i++) {
rv[i] = i;
}
return rv;
};
/**
* Whether the buffer contains the key/index.
* @param {number} key The key/index to check for.
* @return {boolean} Whether the buffer contains the key/index.
*/
goog.structs.CircularBuffer.prototype.containsKey = function(key) {
return key < this.getCount();
};
/**
* Whether the buffer contains the given value.
* @param {*} value The value to check for.
* @return {boolean} Whether the buffer contains the given value.
*/
goog.structs.CircularBuffer.prototype.containsValue = function(value) {
var l = this.getCount();
for (var i = 0; i < l; i++) {
if (this.get(i) == value) {
return true;
}
}
return false;
};
/**
* Returns the last item inserted into the buffer.
* @return {*} The last item inserted into the buffer, or null if the buffer is
* empty.
*/
goog.structs.CircularBuffer.prototype.getLast = function() {
if (this.getCount() == 0) {
return null;
}
return this.get(this.getCount() - 1);
};
/**
* Helper function to convert an index in the number space of oldest to
* newest items in the array to the position that the element will be at in the
* underlying array.
* @param {number} index The index of the item in a list ordered from oldest to
* newest.
* @return {number} The index of the item in the CircularBuffer's underlying
* array.
* @private
*/
goog.structs.CircularBuffer.prototype.normalizeIndex_ = function(index) {
if (index >= this.buff_.length) {
throw Error('Out of bounds exception');
}
if (this.buff_.length < this.maxSize_) {
return index;
}
return (this.nextPtr_ + Number(index)) % this.maxSize_;
};

View File

@@ -0,0 +1,55 @@
// Copyright 2011 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 Defines the collection interface.
*
* @author nnaze@google.com (Nathan Naze)
*/
goog.provide('goog.structs.Collection');
/**
* An interface for a collection of values.
* @interface
*/
goog.structs.Collection = function() {};
/**
* @param {*} value Value to add to the collection.
*/
goog.structs.Collection.prototype.add;
/**
* @param {*} value Value to remove from the collection.
*/
goog.structs.Collection.prototype.remove;
/**
* @param {*} value Value to find in the tree.
* @return {boolean} Whether the collection contains the specified value.
*/
goog.structs.Collection.prototype.contains;
/**
* @return {number} The number of values stored in the collection.
*/
goog.structs.Collection.prototype.getCount;

View File

@@ -0,0 +1,333 @@
// Copyright 2006 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 Datastructure: Heap.
*
*
* This file provides the implementation of a Heap datastructure. Smaller keys
* rise to the top.
*
* The big-O notation for all operations are below:
* <pre>
* Method big-O
* ----------------------------------------------------------------------------
* - insert O(logn)
* - remove O(logn)
* - peek O(1)
* - contains O(n)
* </pre>
*/
// TODO(user): Should this rely on natural ordering via some Comparable
// interface?
goog.provide('goog.structs.Heap');
goog.require('goog.array');
goog.require('goog.object');
goog.require('goog.structs.Node');
/**
* Class for a Heap datastructure.
*
* @param {goog.structs.Heap|Object=} opt_heap Optional goog.structs.Heap or
* Object to initialize heap with.
* @constructor
*/
goog.structs.Heap = function(opt_heap) {
/**
* The nodes of the heap.
* @private
* @type {Array.<goog.structs.Node>}
*/
this.nodes_ = [];
if (opt_heap) {
this.insertAll(opt_heap);
}
};
/**
* Insert the given value into the heap with the given key.
* @param {*} key The key.
* @param {*} value The value.
*/
goog.structs.Heap.prototype.insert = function(key, value) {
var node = new goog.structs.Node(key, value);
var nodes = this.nodes_;
nodes.push(node);
this.moveUp_(nodes.length - 1);
};
/**
* Adds multiple key-value pairs from another goog.structs.Heap or Object
* @param {goog.structs.Heap|Object} heap Object containing the data to add.
*/
goog.structs.Heap.prototype.insertAll = function(heap) {
var keys, values;
if (heap instanceof goog.structs.Heap) {
keys = heap.getKeys();
values = heap.getValues();
// If it is a heap and the current heap is empty, I can realy on the fact
// that the keys/values are in the correct order to put in the underlying
// structure.
if (heap.getCount() <= 0) {
var nodes = this.nodes_;
for (var i = 0; i < keys.length; i++) {
nodes.push(new goog.structs.Node(keys[i], values[i]));
}
return;
}
} else {
keys = goog.object.getKeys(heap);
values = goog.object.getValues(heap);
}
for (var i = 0; i < keys.length; i++) {
this.insert(keys[i], values[i]);
}
};
/**
* Retrieves and removes the root value of this heap.
* @return {*} The value removed from the root of the heap. Returns
* undefined if the heap is empty.
*/
goog.structs.Heap.prototype.remove = function() {
var nodes = this.nodes_;
var count = nodes.length;
var rootNode = nodes[0];
if (count <= 0) {
return undefined;
} else if (count == 1) {
goog.array.clear(nodes);
} else {
nodes[0] = nodes.pop();
this.moveDown_(0);
}
return rootNode.getValue();
};
/**
* Retrieves but does not remove the root value of this heap.
* @return {*} The value at the root of the heap. Returns
* undefined if the heap is empty.
*/
goog.structs.Heap.prototype.peek = function() {
var nodes = this.nodes_;
if (nodes.length == 0) {
return undefined;
}
return nodes[0].getValue();
};
/**
* Retrieves but does not remove the key of the root node of this heap.
* @return {*} The key at the root of the heap. Returns undefined if the
* heap is empty.
*/
goog.structs.Heap.prototype.peekKey = function() {
return this.nodes_[0] && this.nodes_[0].getKey();
};
/**
* Moves the node at the given index down to its proper place in the heap.
* @param {number} index The index of the node to move down.
* @private
*/
goog.structs.Heap.prototype.moveDown_ = function(index) {
var nodes = this.nodes_;
var count = nodes.length;
// Save the node being moved down.
var node = nodes[index];
// While the current node has a child.
while (index < (count >> 1)) {
var leftChildIndex = this.getLeftChildIndex_(index);
var rightChildIndex = this.getRightChildIndex_(index);
// Determine the index of the smaller child.
var smallerChildIndex = rightChildIndex < count &&
nodes[rightChildIndex].getKey() < nodes[leftChildIndex].getKey() ?
rightChildIndex : leftChildIndex;
// If the node being moved down is smaller than its children, the node
// has found the correct index it should be at.
if (nodes[smallerChildIndex].getKey() > node.getKey()) {
break;
}
// If not, then take the smaller child as the current node.
nodes[index] = nodes[smallerChildIndex];
index = smallerChildIndex;
}
nodes[index] = node;
};
/**
* Moves the node at the given index up to its proper place in the heap.
* @param {number} index The index of the node to move up.
* @private
*/
goog.structs.Heap.prototype.moveUp_ = function(index) {
var nodes = this.nodes_;
var node = nodes[index];
// While the node being moved up is not at the root.
while (index > 0) {
// If the parent is less than the node being moved up, move the parent down.
var parentIndex = this.getParentIndex_(index);
if (nodes[parentIndex].getKey() > node.getKey()) {
nodes[index] = nodes[parentIndex];
index = parentIndex;
} else {
break;
}
}
nodes[index] = node;
};
/**
* Gets the index of the left child of the node at the given index.
* @param {number} index The index of the node to get the left child for.
* @return {number} The index of the left child.
* @private
*/
goog.structs.Heap.prototype.getLeftChildIndex_ = function(index) {
return index * 2 + 1;
};
/**
* Gets the index of the right child of the node at the given index.
* @param {number} index The index of the node to get the right child for.
* @return {number} The index of the right child.
* @private
*/
goog.structs.Heap.prototype.getRightChildIndex_ = function(index) {
return index * 2 + 2;
};
/**
* Gets the index of the parent of the node at the given index.
* @param {number} index The index of the node to get the parent for.
* @return {number} The index of the parent.
* @private
*/
goog.structs.Heap.prototype.getParentIndex_ = function(index) {
return (index - 1) >> 1;
};
/**
* Gets the values of the heap.
* @return {Array} The values in the heap.
*/
goog.structs.Heap.prototype.getValues = function() {
var nodes = this.nodes_;
var rv = [];
var l = nodes.length;
for (var i = 0; i < l; i++) {
rv.push(nodes[i].getValue());
}
return rv;
};
/**
* Gets the keys of the heap.
* @return {Array} The keys in the heap.
*/
goog.structs.Heap.prototype.getKeys = function() {
var nodes = this.nodes_;
var rv = [];
var l = nodes.length;
for (var i = 0; i < l; i++) {
rv.push(nodes[i].getKey());
}
return rv;
};
/**
* Whether the heap contains the given value.
* @param {Object} val The value to check for.
* @return {boolean} Whether the heap contains the value.
*/
goog.structs.Heap.prototype.containsValue = function(val) {
return goog.array.some(this.nodes_, function(node) {
return node.getValue() == val;
});
};
/**
* Whether the heap contains the given key.
* @param {Object} key The key to check for.
* @return {boolean} Whether the heap contains the key.
*/
goog.structs.Heap.prototype.containsKey = function(key) {
return goog.array.some(this.nodes_, function(node) {
return node.getKey() == key;
});
};
/**
* Clones a heap and returns a new heap
* @return {goog.structs.Heap} A new goog.structs.Heap with the same key-value
* pairs.
*/
goog.structs.Heap.prototype.clone = function() {
return new goog.structs.Heap(this);
};
/**
* The number of key-value pairs in the map
* @return {number} The number of pairs.
*/
goog.structs.Heap.prototype.getCount = function() {
return this.nodes_.length;
};
/**
* Returns true if this heap contains no elements.
* @return {boolean} Whether this heap contains no elements.
*/
goog.structs.Heap.prototype.isEmpty = function() {
return goog.array.isEmpty(this.nodes_);
};
/**
* Removes all elements from the heap.
*/
goog.structs.Heap.prototype.clear = function() {
goog.array.clear(this.nodes_);
};

View File

@@ -0,0 +1,159 @@
// Copyright 2008 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 Provides inversion and inversion map functionality for storing
* integer ranges and corresponding values.
*
*/
goog.provide('goog.structs.InversionMap');
goog.require('goog.array');
/**
* Maps ranges to values using goog.structs.Inversion.
* @param {Array.<number>} rangeArray An array of monotonically
* increasing integer values, with at least one instance.
* @param {Array.<*>} valueArray An array of corresponding values.
* Length must be the same as rangeArray.
* @param {boolean=} opt_delta If true, saves only delta from previous value.
* @constructor
*/
goog.structs.InversionMap = function(rangeArray, valueArray, opt_delta) {
if (rangeArray.length != valueArray.length) {
// rangeArray and valueArray has to match in number of entries.
return null;
}
this.storeInversion_(rangeArray, opt_delta);
/**
* @type {Array}
* @protected
*/
this.values = valueArray;
};
/**
* @type {Array}
* @protected
*/
goog.structs.InversionMap.prototype.rangeArray;
/**
* Stores the integers as ranges (half-open).
* If delta is true, the integers are delta from the previous value and
* will be restored to the absolute value.
* When used as a set, even indices are IN, and odd are OUT.
* @param {Array.<number?>} rangeArray An array of monotonically
* increasing integer values, with at least one instance.
* @param {boolean=} opt_delta If true, saves only delta from previous value.
* @private
*/
goog.structs.InversionMap.prototype.storeInversion_ = function(rangeArray,
opt_delta) {
this.rangeArray = rangeArray;
for (var i = 1; i < rangeArray.length; i++) {
if (rangeArray[i] == null) {
rangeArray[i] = rangeArray[i - 1] + 1;
} else if (opt_delta) {
rangeArray[i] += rangeArray[i - 1];
}
}
};
/**
* Splices a range -> value map into this inversion map.
* @param {Array.<number>} rangeArray An array of monotonically
* increasing integer values, with at least one instance.
* @param {Array.<*>} valueArray An array of corresponding values.
* Length must be the same as rangeArray.
* @param {boolean=} opt_delta If true, saves only delta from previous value.
*/
goog.structs.InversionMap.prototype.spliceInversion = function(
rangeArray, valueArray, opt_delta) {
// By building another inversion map, we build the arrays that we need
// to splice in.
var otherMap = new goog.structs.InversionMap(
rangeArray, valueArray, opt_delta);
// Figure out where to splice those arrays.
var startRange = otherMap.rangeArray[0];
var endRange =
/** @type {number} */ (goog.array.peek(otherMap.rangeArray));
var startSplice = this.getLeast(startRange);
var endSplice = this.getLeast(endRange);
// The inversion map works by storing the start points of ranges...
if (startRange != this.rangeArray[startSplice]) {
// ...if we're splicing in a start point that isn't already here,
// then we need to insert it after the insertion point.
startSplice++;
} // otherwise we overwrite the insertion point.
var spliceLength = endSplice - startSplice + 1;
goog.partial(goog.array.splice, this.rangeArray, startSplice,
spliceLength).apply(null, otherMap.rangeArray);
goog.partial(goog.array.splice, this.values, startSplice,
spliceLength).apply(null, otherMap.values);
};
/**
* Gets the value corresponding to a number from the inversion map.
* @param {number} intKey The number for which value needs to be retrieved
* from inversion map.
* @return {*} Value retrieved from inversion map; null if not found.
*/
goog.structs.InversionMap.prototype.at = function(intKey) {
var index = this.getLeast(intKey);
if (index < 0) {
return null;
}
return this.values[index];
};
/**
* Gets the largest index such that rangeArray[index] <= intKey from the
* inversion map.
* @param {number} intKey The probe for which rangeArray is searched.
* @return {number} Largest index such that rangeArray[index] <= intKey.
* @protected
*/
goog.structs.InversionMap.prototype.getLeast = function(intKey) {
var arr = this.rangeArray;
var low = 0;
var high = arr.length;
while (high - low > 8) {
var mid = (high + low) >> 1;
if (arr[mid] <= intKey) {
low = mid;
} else {
high = mid;
}
}
for (; low < high; ++low) {
if (intKey < arr[low]) {
break;
}
}
return low - 1;
};

View File

@@ -0,0 +1,473 @@
// Copyright 2007 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 A LinkedMap data structure that is accessed using key/value
* pairs like an ordinary Map, but which guarantees a consistent iteration
* order over its entries. The iteration order is either insertion order (the
* default) or ordered from most recent to least recent use. By setting a fixed
* size, the LRU version of the LinkedMap makes an effective object cache. This
* data structure is similar to Java's LinkedHashMap.
*
* @author brenneman@google.com (Shawn Brenneman)
*/
goog.provide('goog.structs.LinkedMap');
goog.require('goog.structs.Map');
/**
* Class for a LinkedMap datastructure, which combines O(1) map access for
* key/value pairs with a linked list for a consistent iteration order. Sample
* usage:
*
* <pre>
* var m = new LinkedMap();
* m.set('param1', 'A');
* m.set('param2', 'B');
* m.set('param3', 'C');
* alert(m.getKeys()); // param1, param2, param3
*
* var c = new LinkedMap(5, true);
* for (var i = 0; i < 10; i++) {
* c.set('entry' + i, false);
* }
* alert(c.getKeys()); // entry9, entry8, entry7, entry6, entry5
*
* c.set('entry5', true);
* c.set('entry1', false);
* alert(c.getKeys()); // entry1, entry5, entry9, entry8, entry7
* </pre>
*
* @param {number=} opt_maxCount The maximum number of objects to store in the
* LinkedMap. If unspecified or 0, there is no maximum.
* @param {boolean=} opt_cache When set, the LinkedMap stores items in order
* from most recently used to least recently used, instead of insertion
* order.
* @constructor
*/
goog.structs.LinkedMap = function(opt_maxCount, opt_cache) {
/**
* The maximum number of entries to allow, or null if there is no limit.
* @type {?number}
* @private
*/
this.maxCount_ = opt_maxCount || null;
/**
* @type {boolean}
* @private
*/
this.cache_ = !!opt_cache;
this.map_ = new goog.structs.Map();
this.head_ = new goog.structs.LinkedMap.Node_('', undefined);
this.head_.next = this.head_.prev = this.head_;
};
/**
* Finds a node and updates it to be the most recently used.
* @param {string} key The key of the node.
* @return {goog.structs.LinkedMap.Node_} The node or null if not found.
* @private
*/
goog.structs.LinkedMap.prototype.findAndMoveToTop_ = function(key) {
var node = /** @type {goog.structs.LinkedMap.Node_} */ (this.map_.get(key));
if (node) {
if (this.cache_) {
node.remove();
this.insert_(node);
}
}
return node;
};
/**
* Retrieves the value for a given key. If this is a caching LinkedMap, the
* entry will become the most recently used.
* @param {string} key The key to retrieve the value for.
* @param {*=} opt_val A default value that will be returned if the key is
* not found, defaults to undefined.
* @return {*} The retrieved value.
*/
goog.structs.LinkedMap.prototype.get = function(key, opt_val) {
var node = this.findAndMoveToTop_(key);
return node ? node.value : opt_val;
};
/**
* Retrieves the value for a given key without updating the entry to be the
* most recently used.
* @param {string} key The key to retrieve the value for.
* @param {*=} opt_val A default value that will be returned if the key is
* not found.
* @return {*} The retrieved value.
*/
goog.structs.LinkedMap.prototype.peekValue = function(key, opt_val) {
var node = this.map_.get(key);
return node ? node.value : opt_val;
};
/**
* Sets a value for a given key. If this is a caching LinkedMap, this entry
* will become the most recently used.
* @param {string} key The key to retrieve the value for.
* @param {*} value A default value that will be returned if the key is
* not found.
*/
goog.structs.LinkedMap.prototype.set = function(key, value) {
var node = this.findAndMoveToTop_(key);
if (node) {
node.value = value;
} else {
node = new goog.structs.LinkedMap.Node_(key, value);
this.map_.set(key, node);
this.insert_(node);
}
};
/**
* Returns the value of the first node without making any modifications.
* @return {*} The value of the first node or undefined if the map is empty.
*/
goog.structs.LinkedMap.prototype.peek = function() {
return this.head_.next.value;
};
/**
* Returns the value of the last node without making any modifications.
* @return {*} The value of the last node or undefined if the map is empty.
*/
goog.structs.LinkedMap.prototype.peekLast = function() {
return this.head_.prev.value;
};
/**
* Removes the first node from the list and returns its value.
* @return {*} The value of the popped node, or undefined if the map was empty.
*/
goog.structs.LinkedMap.prototype.shift = function() {
return this.popNode_(this.head_.next);
};
/**
* Removes the last node from the list and returns its value.
* @return {*} The value of the popped node, or undefined if the map was empty.
*/
goog.structs.LinkedMap.prototype.pop = function() {
return this.popNode_(this.head_.prev);
};
/**
* Removes a value from the LinkedMap based on its key.
* @param {string} key The key to remove.
* @return {boolean} True if the entry was removed, false if the key was not
* found.
*/
goog.structs.LinkedMap.prototype.remove = function(key) {
var node = /** @type {goog.structs.LinkedMap.Node_} */ (this.map_.get(key));
if (node) {
this.removeNode(node);
return true;
}
return false;
};
/**
* Removes a node from the {@code LinkedMap}. It can be overridden to do
* further cleanup such as disposing of the node value.
* @param {!goog.structs.LinkedMap.Node_} node The node to remove.
* @protected
*/
goog.structs.LinkedMap.prototype.removeNode = function(node) {
node.remove();
this.map_.remove(node.key);
};
/**
* @return {number} The number of items currently in the LinkedMap.
*/
goog.structs.LinkedMap.prototype.getCount = function() {
return this.map_.getCount();
};
/**
* @return {boolean} True if the cache is empty, false if it contains any items.
*/
goog.structs.LinkedMap.prototype.isEmpty = function() {
return this.map_.isEmpty();
};
/**
* Sets the maximum number of entries allowed in this object, truncating any
* excess objects if necessary.
* @param {number} maxCount The new maximum number of entries to allow.
*/
goog.structs.LinkedMap.prototype.setMaxCount = function(maxCount) {
this.maxCount_ = maxCount || null;
if (this.maxCount_ != null) {
this.truncate_(this.maxCount_);
}
};
/**
* @return {!Array.<string>} The list of the keys in the appropriate order for
* this LinkedMap.
*/
goog.structs.LinkedMap.prototype.getKeys = function() {
return this.map(function(val, key) {
return key;
});
};
/**
* @return {!Array} The list of the values in the appropriate order for
* this LinkedMap.
*/
goog.structs.LinkedMap.prototype.getValues = function() {
return this.map(function(val, key) {
return val;
});
};
/**
* Tests whether a provided value is currently in the LinkedMap. This does not
* affect item ordering in cache-style LinkedMaps.
* @param {Object} value The value to check for.
* @return {boolean} Whether the value is in the LinkedMap.
*/
goog.structs.LinkedMap.prototype.contains = function(value) {
return this.some(function(el) {
return el == value;
});
};
/**
* Tests whether a provided key is currently in the LinkedMap. This does not
* affect item ordering in cache-style LinkedMaps.
* @param {string} key The key to check for.
* @return {boolean} Whether the key is in the LinkedMap.
*/
goog.structs.LinkedMap.prototype.containsKey = function(key) {
return this.map_.containsKey(key);
};
/**
* Removes all entries in this object.
*/
goog.structs.LinkedMap.prototype.clear = function() {
this.truncate_(0);
};
/**
* Calls a function on each item in the LinkedMap.
*
* @see goog.structs.forEach
* @param {Function} f The function to call for each item. The function takes
* three arguments: the value, the key, and the LinkedMap.
* @param {Object=} opt_obj The object context to use as "this" for the
* function.
*/
goog.structs.LinkedMap.prototype.forEach = function(f, opt_obj) {
for (var n = this.head_.next; n != this.head_; n = n.next) {
f.call(opt_obj, n.value, n.key, this);
}
};
/**
* Calls a function on each item in the LinkedMap and returns the results of
* those calls in an array.
*
* @see goog.structs.map
* @param {!Function} f The function to call for each item. The function takes
* three arguments: the value, the key, and the LinkedMap.
* @param {Object=} opt_obj The object context to use as "this" for the
* function.
* @return {!Array} The results of the function calls for each item in the
* LinkedMap.
*/
goog.structs.LinkedMap.prototype.map = function(f, opt_obj) {
var rv = [];
for (var n = this.head_.next; n != this.head_; n = n.next) {
rv.push(f.call(opt_obj, n.value, n.key, this));
}
return rv;
};
/**
* Calls a function on each item in the LinkedMap and returns true if any of
* those function calls returns a true-like value.
*
* @see goog.structs.some
* @param {Function} f The function to call for each item. The function takes
* three arguments: the value, the key, and the LinkedMap, and returns a
* boolean.
* @param {Object=} opt_obj The object context to use as "this" for the
* function.
* @return {boolean} Whether f evaluates to true for at least one item in the
* LinkedMap.
*/
goog.structs.LinkedMap.prototype.some = function(f, opt_obj) {
for (var n = this.head_.next; n != this.head_; n = n.next) {
if (f.call(opt_obj, n.value, n.key, this)) {
return true;
}
}
return false;
};
/**
* Calls a function on each item in the LinkedMap and returns true only if every
* function call returns a true-like value.
*
* @see goog.structs.some
* @param {Function} f The function to call for each item. The function takes
* three arguments: the value, the key, and the Cache, and returns a
* boolean.
* @param {Object=} opt_obj The object context to use as "this" for the
* function.
* @return {boolean} Whether f evaluates to true for every item in the Cache.
*/
goog.structs.LinkedMap.prototype.every = function(f, opt_obj) {
for (var n = this.head_.next; n != this.head_; n = n.next) {
if (!f.call(opt_obj, n.value, n.key, this)) {
return false;
}
}
return true;
};
/**
* Appends a node to the list. LinkedMap in cache mode adds new nodes to
* the head of the list, otherwise they are appended to the tail. If there is a
* maximum size, the list will be truncated if necessary.
*
* @param {goog.structs.LinkedMap.Node_} node The item to insert.
* @private
*/
goog.structs.LinkedMap.prototype.insert_ = function(node) {
if (this.cache_) {
node.next = this.head_.next;
node.prev = this.head_;
this.head_.next = node;
node.next.prev = node;
} else {
node.prev = this.head_.prev;
node.next = this.head_;
this.head_.prev = node;
node.prev.next = node;
}
if (this.maxCount_ != null) {
this.truncate_(this.maxCount_);
}
};
/**
* Removes elements from the LinkedMap if the given count has been exceeded.
* In cache mode removes nodes from the tail of the list. Otherwise removes
* nodes from the head.
* @param {number} count Number of elements to keep.
* @private
*/
goog.structs.LinkedMap.prototype.truncate_ = function(count) {
for (var i = this.map_.getCount(); i > count; i--) {
this.removeNode(this.cache_ ? this.head_.prev : this.head_.next);
}
};
/**
* Removes the node from the LinkedMap if it is not the head, and returns
* the node's value.
* @param {!goog.structs.LinkedMap.Node_} node The item to remove.
* @return {*} The value of the popped node.
* @private
*/
goog.structs.LinkedMap.prototype.popNode_ = function(node) {
if (this.head_ != node) {
this.removeNode(node);
}
return node.value;
};
/**
* Internal class for a doubly-linked list node containing a key/value pair.
* @param {string} key The key.
* @param {*} value The value.
* @constructor
* @private
*/
goog.structs.LinkedMap.Node_ = function(key, value) {
this.key = key;
this.value = value;
};
/**
* The next node in the list.
* @type {!goog.structs.LinkedMap.Node_}
*/
goog.structs.LinkedMap.Node_.prototype.next;
/**
* The previous node in the list.
* @type {!goog.structs.LinkedMap.Node_}
*/
goog.structs.LinkedMap.Node_.prototype.prev;
/**
* Causes this node to remove itself from the list.
*/
goog.structs.LinkedMap.Node_.prototype.remove = function() {
this.prev.next = this.next;
this.next.prev = this.prev;
delete this.prev;
delete this.next;
};

View File

@@ -0,0 +1,449 @@
// Copyright 2006 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 Datastructure: Hash Map.
*
* @author arv@google.com (Erik Arvidsson)
* @author jonp@google.com (Jon Perlow) Optimized for IE6
*
* This file contains an implementation of a Map structure. It implements a lot
* of the methods used in goog.structs so those functions work on hashes. This
* is best suited for complex key types. For simple keys such as numbers and
* strings, and where special names like __proto__ are not a concern, consider
* using the lighter-weight utilities in goog.object.
*/
goog.provide('goog.structs.Map');
goog.require('goog.iter.Iterator');
goog.require('goog.iter.StopIteration');
goog.require('goog.object');
/**
* Class for Hash Map datastructure.
* @param {*=} opt_map Map or Object to initialize the map with.
* @param {...*} var_args If 2 or more arguments are present then they
* will be used as key-value pairs.
* @constructor
*/
goog.structs.Map = function(opt_map, var_args) {
/**
* Underlying JS object used to implement the map.
* @type {!Object}
* @private
*/
this.map_ = {};
/**
* An array of keys. This is necessary for two reasons:
* 1. Iterating the keys using for (var key in this.map_) allocates an
* object for every key in IE which is really bad for IE6 GC perf.
* 2. Without a side data structure, we would need to escape all the keys
* as that would be the only way we could tell during iteration if the
* key was an internal key or a property of the object.
*
* This array can contain deleted keys so it's necessary to check the map
* as well to see if the key is still in the map (this doesn't require a
* memory allocation in IE).
* @type {!Array.<string>}
* @private
*/
this.keys_ = [];
var argLength = arguments.length;
if (argLength > 1) {
if (argLength % 2) {
throw Error('Uneven number of arguments');
}
for (var i = 0; i < argLength; i += 2) {
this.set(arguments[i], arguments[i + 1]);
}
} else if (opt_map) {
this.addAll(/** @type {Object} */ (opt_map));
}
};
/**
* The number of key value pairs in the map.
* @private
* @type {number}
*/
goog.structs.Map.prototype.count_ = 0;
/**
* Version used to detect changes while iterating.
* @private
* @type {number}
*/
goog.structs.Map.prototype.version_ = 0;
/**
* @return {number} The number of key-value pairs in the map.
*/
goog.structs.Map.prototype.getCount = function() {
return this.count_;
};
/**
* Returns the values of the map.
* @return {!Array} The values in the map.
*/
goog.structs.Map.prototype.getValues = function() {
this.cleanupKeysArray_();
var rv = [];
for (var i = 0; i < this.keys_.length; i++) {
var key = this.keys_[i];
rv.push(this.map_[key]);
}
return rv;
};
/**
* Returns the keys of the map.
* @return {!Array.<string>} Array of string values.
*/
goog.structs.Map.prototype.getKeys = function() {
this.cleanupKeysArray_();
return /** @type {!Array.<string>} */ (this.keys_.concat());
};
/**
* Whether the map contains the given key.
* @param {*} key The key to check for.
* @return {boolean} Whether the map contains the key.
*/
goog.structs.Map.prototype.containsKey = function(key) {
return goog.structs.Map.hasKey_(this.map_, key);
};
/**
* Whether the map contains the given value. This is O(n).
* @param {*} val The value to check for.
* @return {boolean} Whether the map contains the value.
*/
goog.structs.Map.prototype.containsValue = function(val) {
for (var i = 0; i < this.keys_.length; i++) {
var key = this.keys_[i];
if (goog.structs.Map.hasKey_(this.map_, key) && this.map_[key] == val) {
return true;
}
}
return false;
};
/**
* Whether this map is equal to the argument map.
* @param {goog.structs.Map} otherMap The map against which to test equality.
* @param {function(?, ?) : boolean=} opt_equalityFn Optional equality function
* to test equality of values. If not specified, this will test whether
* the values contained in each map are identical objects.
* @return {boolean} Whether the maps are equal.
*/
goog.structs.Map.prototype.equals = function(otherMap, opt_equalityFn) {
if (this === otherMap) {
return true;
}
if (this.count_ != otherMap.getCount()) {
return false;
}
var equalityFn = opt_equalityFn || goog.structs.Map.defaultEquals;
this.cleanupKeysArray_();
for (var key, i = 0; key = this.keys_[i]; i++) {
if (!equalityFn(this.get(key), otherMap.get(key))) {
return false;
}
}
return true;
};
/**
* Default equality test for values.
* @param {*} a The first value.
* @param {*} b The second value.
* @return {boolean} Whether a and b reference the same object.
*/
goog.structs.Map.defaultEquals = function(a, b) {
return a === b;
};
/**
* @return {boolean} Whether the map is empty.
*/
goog.structs.Map.prototype.isEmpty = function() {
return this.count_ == 0;
};
/**
* Removes all key-value pairs from the map.
*/
goog.structs.Map.prototype.clear = function() {
this.map_ = {};
this.keys_.length = 0;
this.count_ = 0;
this.version_ = 0;
};
/**
* Removes a key-value pair based on the key. This is O(logN) amortized due to
* updating the keys array whenever the count becomes half the size of the keys
* in the keys array.
* @param {*} key The key to remove.
* @return {boolean} Whether object was removed.
*/
goog.structs.Map.prototype.remove = function(key) {
if (goog.structs.Map.hasKey_(this.map_, key)) {
delete this.map_[key];
this.count_--;
this.version_++;
// clean up the keys array if the threshhold is hit
if (this.keys_.length > 2 * this.count_) {
this.cleanupKeysArray_();
}
return true;
}
return false;
};
/**
* Cleans up the temp keys array by removing entries that are no longer in the
* map.
* @private
*/
goog.structs.Map.prototype.cleanupKeysArray_ = function() {
if (this.count_ != this.keys_.length) {
// First remove keys that are no longer in the map.
var srcIndex = 0;
var destIndex = 0;
while (srcIndex < this.keys_.length) {
var key = this.keys_[srcIndex];
if (goog.structs.Map.hasKey_(this.map_, key)) {
this.keys_[destIndex++] = key;
}
srcIndex++;
}
this.keys_.length = destIndex;
}
if (this.count_ != this.keys_.length) {
// If the count still isn't correct, that means we have duplicates. This can
// happen when the same key is added and removed multiple times. Now we have
// to allocate one extra Object to remove the duplicates. This could have
// been done in the first pass, but in the common case, we can avoid
// allocating an extra object by only doing this when necessary.
var seen = {};
var srcIndex = 0;
var destIndex = 0;
while (srcIndex < this.keys_.length) {
var key = this.keys_[srcIndex];
if (!(goog.structs.Map.hasKey_(seen, key))) {
this.keys_[destIndex++] = key;
seen[key] = 1;
}
srcIndex++;
}
this.keys_.length = destIndex;
}
};
/**
* Returns the value for the given key. If the key is not found and the default
* value is not given this will return {@code undefined}.
* @param {*} key The key to get the value for.
* @param {*=} opt_val The value to return if no item is found for the given
* key, defaults to undefined.
* @return {*} The value for the given key.
*/
goog.structs.Map.prototype.get = function(key, opt_val) {
if (goog.structs.Map.hasKey_(this.map_, key)) {
return this.map_[key];
}
return opt_val;
};
/**
* Adds a key-value pair to the map.
* @param {*} key The key.
* @param {*} value The value to add.
* @return {*} Some subclasses return a value.
*/
goog.structs.Map.prototype.set = function(key, value) {
if (!(goog.structs.Map.hasKey_(this.map_, key))) {
this.count_++;
this.keys_.push(key);
// Only change the version if we add a new key.
this.version_++;
}
this.map_[key] = value;
};
/**
* Adds multiple key-value pairs from another goog.structs.Map or Object.
* @param {Object} map Object containing the data to add.
*/
goog.structs.Map.prototype.addAll = function(map) {
var keys, values;
if (map instanceof goog.structs.Map) {
keys = map.getKeys();
values = map.getValues();
} else {
keys = goog.object.getKeys(map);
values = goog.object.getValues(map);
}
// we could use goog.array.forEach here but I don't want to introduce that
// dependency just for this.
for (var i = 0; i < keys.length; i++) {
this.set(keys[i], values[i]);
}
};
/**
* Clones a map and returns a new map.
* @return {!goog.structs.Map} A new map with the same key-value pairs.
*/
goog.structs.Map.prototype.clone = function() {
return new goog.structs.Map(this);
};
/**
* Returns a new map in which all the keys and values are interchanged
* (keys become values and values become keys). If multiple keys map to the
* same value, the chosen transposed value is implementation-dependent.
*
* It acts very similarly to {goog.object.transpose(Object)}.
*
* @return {!goog.structs.Map} The transposed map.
*/
goog.structs.Map.prototype.transpose = function() {
var transposed = new goog.structs.Map();
for (var i = 0; i < this.keys_.length; i++) {
var key = this.keys_[i];
var value = this.map_[key];
transposed.set(value, key);
}
return transposed;
};
/**
* @return {!Object} Object representation of the map.
*/
goog.structs.Map.prototype.toObject = function() {
this.cleanupKeysArray_();
var obj = {};
for (var i = 0; i < this.keys_.length; i++) {
var key = this.keys_[i];
obj[key] = this.map_[key];
}
return obj;
};
/**
* Returns an iterator that iterates over the keys in the map. Removal of keys
* while iterating might have undesired side effects.
* @return {!goog.iter.Iterator} An iterator over the keys in the map.
*/
goog.structs.Map.prototype.getKeyIterator = function() {
return this.__iterator__(true);
};
/**
* Returns an iterator that iterates over the values in the map. Removal of
* keys while iterating might have undesired side effects.
* @return {!goog.iter.Iterator} An iterator over the values in the map.
*/
goog.structs.Map.prototype.getValueIterator = function() {
return this.__iterator__(false);
};
/**
* Returns an iterator that iterates over the values or the keys in the map.
* This throws an exception if the map was mutated since the iterator was
* created.
* @param {boolean=} opt_keys True to iterate over the keys. False to iterate
* over the values. The default value is false.
* @return {!goog.iter.Iterator} An iterator over the values or keys in the map.
*/
goog.structs.Map.prototype.__iterator__ = function(opt_keys) {
// Clean up keys to minimize the risk of iterating over dead keys.
this.cleanupKeysArray_();
var i = 0;
var keys = this.keys_;
var map = this.map_;
var version = this.version_;
var selfObj = this;
var newIter = new goog.iter.Iterator;
newIter.next = function() {
while (true) {
if (version != selfObj.version_) {
throw Error('The map has changed since the iterator was created');
}
if (i >= keys.length) {
throw goog.iter.StopIteration;
}
var key = keys[i++];
return opt_keys ? key : map[key];
}
};
return newIter;
};
/**
* Safe way to test for hasOwnProperty. It even allows testing for
* 'hasOwnProperty'.
* @param {Object} obj The object to test for presence of the given key.
* @param {*} key The key to check for.
* @return {boolean} Whether the object has the key.
* @private
*/
goog.structs.Map.hasKey_ = function(obj, key) {
return Object.prototype.hasOwnProperty.call(obj, key);
};

View File

@@ -0,0 +1,74 @@
// Copyright 2006 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 immutable node object to be used in collections.
*
*/
goog.provide('goog.structs.Node');
/**
* A generic immutable node. This can be used in various collections that
* require a node object for its item (such as a heap).
* @param {*} key Key.
* @param {*} value Vaue.
* @constructor
*/
goog.structs.Node = function(key, value) {
/**
* The key.
* @type {*}
* @private
*/
this.key_ = key;
/**
* The value.
* @type {*}
* @private
*/
this.value_ = value;
};
/**
* Gets the key.
* @return {*} The key.
*/
goog.structs.Node.prototype.getKey = function() {
return this.key_;
};
/**
* Gets the value.
* @return {*} The value.
*/
goog.structs.Node.prototype.getValue = function() {
return this.value_;
};
/**
* Clones a node and returns a new node.
* @return {goog.structs.Node} A new goog.structs.Node with the same key value
* pair.
*/
goog.structs.Node.prototype.clone = function() {
return new goog.structs.Node(this.key_, this.value_);
};

View File

@@ -0,0 +1,381 @@
// Copyright 2006 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 Datastructure: Pool.
*
*
* A generic class for handling pools of objects.
* When an object is released, it is attempted to be reused.
*/
goog.provide('goog.structs.Pool');
goog.require('goog.Disposable');
goog.require('goog.structs.Queue');
goog.require('goog.structs.Set');
/**
* A generic pool class. If min is greater than max, an error is thrown.
* @param {number=} opt_minCount Min. number of objects (Default: 1).
* @param {number=} opt_maxCount Max. number of objects (Default: 10).
* @constructor
* @extends {goog.Disposable}
*/
goog.structs.Pool = function(opt_minCount, opt_maxCount) {
goog.Disposable.call(this);
/**
* Minimum number of objects allowed
* @type {number}
* @private
*/
this.minCount_ = opt_minCount || 0;
/**
* Maximum number of objects allowed
* @type {number}
* @private
*/
this.maxCount_ = opt_maxCount || 10;
// Make sure that the max and min constraints are valid.
if (this.minCount_ > this.maxCount_) {
throw Error(goog.structs.Pool.ERROR_MIN_MAX_);
}
/**
* Set used to store objects that are currently in the pool and available
* to be used.
* @type {goog.structs.Queue}
* @private
*/
this.freeQueue_ = new goog.structs.Queue();
/**
* Set used to store objects that are currently in the pool and in use.
* @type {goog.structs.Set}
* @private
*/
this.inUseSet_ = new goog.structs.Set();
/**
* The minimum delay between objects being made available, in milliseconds. If
* this is 0, no minimum delay is enforced.
* @type {number}
* @protected
*/
this.delay = 0;
/**
* The time of the last object being made available, in milliseconds since the
* epoch (i.e., the result of Date#toTime). If this is null, no access has
* occurred yet.
* @type {number?}
* @protected
*/
this.lastAccess = null;
// Make sure that the minCount constraint is satisfied.
this.adjustForMinMax();
// TODO(user): Remove once JSCompiler's undefined properties warnings
// don't error for guarded properties.
var magicProps = {canBeReused: 0};
};
goog.inherits(goog.structs.Pool, goog.Disposable);
/**
* Error to throw when the max/min constraint is attempted to be invalidated.
* I.e., when it is attempted for maxCount to be less than minCount.
* @type {string}
* @private
*/
goog.structs.Pool.ERROR_MIN_MAX_ =
'[goog.structs.Pool] Min can not be greater than max';
/**
* Error to throw when the Pool is attempted to be disposed and it is asked to
* make sure that there are no objects that are in use (i.e., haven't been
* released).
* @type {string}
* @private
*/
goog.structs.Pool.ERROR_DISPOSE_UNRELEASED_OBJS_ =
'[goog.structs.Pool] Objects not released';
/**
* Sets the minimum count of the pool.
* If min is greater than the max count of the pool, an error is thrown.
* @param {number} min The minimum count of the pool.
*/
goog.structs.Pool.prototype.setMinimumCount = function(min) {
// Check count constraints.
if (min > this.maxCount_) {
throw Error(goog.structs.Pool.ERROR_MIN_MAX_);
}
this.minCount_ = min;
// Adjust the objects in the pool as needed.
this.adjustForMinMax();
};
/**
* Sets the maximum count of the pool.
* If max is less than the max count of the pool, an error is thrown.
* @param {number} max The maximium count of the pool.
*/
goog.structs.Pool.prototype.setMaximumCount = function(max) {
// Check count constraints.
if (max < this.minCount_) {
throw Error(goog.structs.Pool.ERROR_MIN_MAX_);
}
this.maxCount_ = max;
// Adjust the objects in the pool as needed.
this.adjustForMinMax();
};
/**
* Sets the minimum delay between objects being returned by getObject, in
* milliseconds. This defaults to zero, meaning that no minimum delay is
* enforced and objects may be used as soon as they're available.
* @param {number} delay The minimum delay, in milliseconds.
*/
goog.structs.Pool.prototype.setDelay = function(delay) {
this.delay = delay;
};
/**
* @return {Object|undefined} A new object from the pool if there is one
* available, otherwise undefined.
*/
goog.structs.Pool.prototype.getObject = function() {
var time = goog.now();
if (goog.isDefAndNotNull(this.lastAccess) &&
time - this.lastAccess < this.delay) {
return undefined;
}
var obj = this.removeFreeObject_();
if (obj) {
this.lastAccess = time;
this.inUseSet_.add(obj);
}
return obj;
};
/**
* Returns an object to the pool of available objects so that it can be reused.
* @param {Object} obj The object to return to the pool of free objects.
* @return {boolean} Whether the object was found in the Pool's set of in-use
* objects (in other words, whether any action was taken).
*/
goog.structs.Pool.prototype.releaseObject = function(obj) {
if (this.inUseSet_.remove(obj)) {
this.addFreeObject(obj);
return true;
}
return false;
};
/**
* Removes a free object from the collection of objects that are free so that it
* can be used.
*
* NOTE: This method does not mark the returned object as in use.
*
* @return {Object|undefined} The object removed from the free collection, if
* there is one available. Otherwise, undefined.
* @private
*/
goog.structs.Pool.prototype.removeFreeObject_ = function() {
var obj;
while (this.getFreeCount() > 0) {
obj = /** @type {Object} */(this.freeQueue_.dequeue());
if (!this.objectCanBeReused(obj)) {
this.adjustForMinMax();
} else {
break;
}
}
if (!obj && this.getCount() < this.maxCount_) {
obj = this.createObject();
}
return obj;
};
/**
* Adds an object to the collection of objects that are free. If the object can
* not be added, then it is disposed.
*
* @param {Object} obj The object to add to collection of free objects.
*/
goog.structs.Pool.prototype.addFreeObject = function(obj) {
this.inUseSet_.remove(obj);
if (this.objectCanBeReused(obj) && this.getCount() < this.maxCount_) {
this.freeQueue_.enqueue(obj);
} else {
this.disposeObject(obj);
}
};
/**
* Adjusts the objects held in the pool to be within the min/max constraints.
*
* NOTE: It is possible that the number of objects in the pool will still be
* greater than the maximum count of objects allowed. This will be the case
* if no more free objects can be disposed of to get below the minimum count
* (i.e., all objects are in use).
*/
goog.structs.Pool.prototype.adjustForMinMax = function() {
var freeQueue = this.freeQueue_;
// Make sure the at least the minimum number of objects are created.
while (this.getCount() < this.minCount_) {
freeQueue.enqueue(this.createObject());
}
// Make sure no more than the maximum number of objects are created.
while (this.getCount() > this.maxCount_ && this.getFreeCount() > 0) {
this.disposeObject(/** @type {Object} */(freeQueue.dequeue()));
}
};
/**
* Should be overriden by sub-classes to return an instance of the object type
* that is expected in the pool.
* @return {Object} The created object.
*/
goog.structs.Pool.prototype.createObject = function() {
return {};
};
/**
* Should be overriden to dispose of an object. Default implementation is to
* remove all its members, which should render it useless. Calls the object's
* {@code dispose()} method, if available.
* @param {Object} obj The object to dispose.
*/
goog.structs.Pool.prototype.disposeObject = function(obj) {
if (typeof obj.dispose == 'function') {
obj.dispose();
} else {
for (var i in obj) {
obj[i] = null;
}
}
};
/**
* Should be overriden to determine whether an object has become unusable and
* should not be returned by getObject(). Calls the object's
* {@code canBeReused()} method, if available.
* @param {Object} obj The object to test.
* @return {boolean} Whether the object can be reused.
*/
goog.structs.Pool.prototype.objectCanBeReused = function(obj) {
if (typeof obj.canBeReused == 'function') {
return obj.canBeReused();
}
return true;
};
/**
* Returns true if the given object is in the pool.
* @param {Object} obj The object to check the pool for.
* @return {boolean} Whether the pool contains the object.
*/
goog.structs.Pool.prototype.contains = function(obj) {
return this.freeQueue_.contains(obj) || this.inUseSet_.contains(obj);
};
/**
* Returns the number of objects currently in the pool.
* @return {number} Number of objects currently in the pool.
*/
goog.structs.Pool.prototype.getCount = function() {
return this.freeQueue_.getCount() + this.inUseSet_.getCount();
};
/**
* Returns the number of objects currently in use in the pool.
* @return {number} Number of objects currently in use in the pool.
*/
goog.structs.Pool.prototype.getInUseCount = function() {
return this.inUseSet_.getCount();
};
/**
* Returns the number of objects currently free in the pool.
* @return {number} Number of objects currently free in the pool.
*/
goog.structs.Pool.prototype.getFreeCount = function() {
return this.freeQueue_.getCount();
};
/**
* Determines if the pool contains no objects.
* @return {boolean} Whether the pool contains no objects.
*/
goog.structs.Pool.prototype.isEmpty = function() {
return this.freeQueue_.isEmpty() && this.inUseSet_.isEmpty();
};
/**
* Disposes of the pool and all objects currently held in the pool.
* @override
* @protected
*/
goog.structs.Pool.prototype.disposeInternal = function() {
goog.structs.Pool.superClass_.disposeInternal.call(this);
if (this.getInUseCount() > 0) {
throw Error(goog.structs.Pool.ERROR_DISPOSE_UNRELEASED_OBJS_);
}
delete this.inUseSet_;
// Call disposeObject on each object held by the pool.
var freeQueue = this.freeQueue_;
while (!freeQueue.isEmpty()) {
this.disposeObject(/** @type {Object} */ (freeQueue.dequeue()));
}
delete this.freeQueue_;
};

View File

@@ -0,0 +1,184 @@
// Copyright 2006 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 Datastructure: Priority Pool.
*
*
* An extending of Pool that handles queueing and prioritization.
*/
goog.provide('goog.structs.PriorityPool');
goog.require('goog.structs.Pool');
goog.require('goog.structs.PriorityQueue');
/**
* A generic pool class. If max is greater than min, an error is thrown.
* @param {number=} opt_minCount Min. number of objects (Default: 1).
* @param {number=} opt_maxCount Max. number of objects (Default: 10).
* @constructor
* @extends {goog.structs.Pool}
*/
goog.structs.PriorityPool = function(opt_minCount, opt_maxCount) {
/**
* Queue of requests for pool objects.
* @type {goog.structs.PriorityQueue}
* @private
*/
this.requestQueue_ = new goog.structs.PriorityQueue();
// Must break convention of putting the super-class's constructor first. This
// is because the super-class constructor calls adjustForMinMax, which this
// class overrides. In this class's implementation, it assumes that there
// is a requestQueue_, and will error if not present.
goog.structs.Pool.call(this, opt_minCount, opt_maxCount);
};
goog.inherits(goog.structs.PriorityPool, goog.structs.Pool);
/**
* The key for the most recent timeout created.
* @type {number|undefined}
* @private
*/
goog.structs.PriorityPool.prototype.delayTimeout_;
/**
* Default priority for pool objects requests.
* @type {number}
* @private
*/
goog.structs.PriorityPool.DEFAULT_PRIORITY_ = 100;
/** @override */
goog.structs.PriorityPool.prototype.setDelay = function(delay) {
goog.base(this, 'setDelay', delay);
// If the pool hasn't been accessed yet, no need to do anything.
if (!goog.isDefAndNotNull(this.lastAccess)) {
return;
}
goog.global.clearTimeout(this.delayTimeout_);
this.delayTimeout_ = goog.global.setTimeout(
goog.bind(this.handleQueueRequests_, this),
this.delay + this.lastAccess - goog.now());
// Handle all requests.
this.handleQueueRequests_();
};
/**
* Get a new object from the the pool, if there is one available, otherwise
* return undefined.
* @param {Function=} opt_callback The function to callback when an object is
* available. This could be immediately. If this is not present, then an
* object is immediately returned if available, or undefined if not.
* @param {*=} opt_priority The priority of the request. A smaller value means a
* higher priority.
* @return {Object|undefined} The new object from the pool if there is one
* available and a callback is not given. Otherwise, undefined.
* @override
*/
goog.structs.PriorityPool.prototype.getObject = function(opt_callback,
opt_priority) {
if (!opt_callback) {
var result = goog.base(this, 'getObject');
if (result && this.delay) {
this.delayTimeout_ = goog.global.setTimeout(
goog.bind(this.handleQueueRequests_, this),
this.delay);
}
return result;
}
var priority = goog.isDef(opt_priority) ? opt_priority :
goog.structs.PriorityPool.DEFAULT_PRIORITY_;
this.requestQueue_.enqueue(priority, opt_callback);
// Handle all requests.
this.handleQueueRequests_();
return undefined;
};
/**
* Handles the request queue. Tries to fires off as many queued requests as
* possible.
* @private
*/
goog.structs.PriorityPool.prototype.handleQueueRequests_ = function() {
var requestQueue = this.requestQueue_;
while (requestQueue.getCount() > 0) {
var obj = this.getObject();
if (!obj) {
return;
} else {
var requestCallback = requestQueue.dequeue();
requestCallback.apply(this, [obj]);
}
}
};
/**
* Adds an object to the collection of objects that are free. If the object can
* not be added, then it is disposed.
*
* NOTE: This method does not remove the object from the in use collection.
*
* @param {Object} obj The object to add to the collection of free objects.
* @override
*/
goog.structs.PriorityPool.prototype.addFreeObject = function(obj) {
goog.structs.PriorityPool.superClass_.addFreeObject.call(this, obj);
// Handle all requests.
this.handleQueueRequests_();
};
/**
* Adjusts the objects held in the pool to be within the min/max constraints.
*
* NOTE: It is possible that the number of objects in the pool will still be
* greater than the maximum count of objects allowed. This will be the case
* if no more free objects can be disposed of to get below the minimum count
* (i.e., all objects are in use).
* @override
*/
goog.structs.PriorityPool.prototype.adjustForMinMax = function() {
goog.structs.PriorityPool.superClass_.adjustForMinMax.call(this);
// Handle all requests.
this.handleQueueRequests_();
};
/** @override */
goog.structs.PriorityPool.prototype.disposeInternal = function() {
goog.structs.PriorityPool.superClass_.disposeInternal.call(this);
goog.global.clearTimeout(this.delayTimeout_);
this.requestQueue_.clear();
this.requestQueue_ = null;
};

View File

@@ -0,0 +1,64 @@
// Copyright 2006 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 Datastructure: Priority Queue.
*
*
* This file provides the implementation of a Priority Queue. Smaller priorities
* move to the front of the queue. If two values have the same priority,
* it is arbitrary which value will come to the front of the queue first.
*/
// TODO(user): Should this rely on natural ordering via some Comparable
// interface?
goog.provide('goog.structs.PriorityQueue');
goog.require('goog.structs.Heap');
/**
* Class for Priority Queue datastructure.
*
* @constructor
* @extends {goog.structs.Heap}
*/
goog.structs.PriorityQueue = function() {
goog.structs.Heap.call(this);
};
goog.inherits(goog.structs.PriorityQueue, goog.structs.Heap);
/**
* Puts the specified value in the queue.
* @param {*} priority The priority of the value. A smaller value here means a
* higher priority.
* @param {*} value The value.
*/
goog.structs.PriorityQueue.prototype.enqueue = function(priority, value) {
this.insert(priority, value);
};
/**
* Retrieves and removes the head of this queue.
* @return {*} The element at the head of this queue. Returns
* undefined if the queue is empty.
*/
goog.structs.PriorityQueue.prototype.dequeue = function() {
return this.remove();
};

View File

@@ -0,0 +1,571 @@
// Copyright 2008 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 Datastructure: A point Quad Tree for representing 2D data. Each
* region has the same ratio as the bounds for the tree.
*
* The implementation currently requires pre-determined bounds for data as it
* can not rebalance itself to that degree.
*
* @see ../demos/quadtree.html
*/
goog.provide('goog.structs.QuadTree');
goog.provide('goog.structs.QuadTree.Node');
goog.provide('goog.structs.QuadTree.Point');
goog.require('goog.math.Coordinate');
/**
* Constructs a new quad tree.
* @param {number} minX Minimum x-value that can be held in tree.
* @param {number} minY Minimum y-value that can be held in tree.
* @param {number} maxX Maximum x-value that can be held in tree.
* @param {number} maxY Maximum y-value that can be held in tree.
* @constructor
*/
goog.structs.QuadTree = function(minX, minY, maxX, maxY) {
/**
* The root node for the quad tree.
* @type {goog.structs.QuadTree.Node}
* @private
*/
this.root_ = new goog.structs.QuadTree.Node(
minX, minY, maxX - minX, maxY - minY);
};
/**
* Count of the number of items in the tree.
* @type {number}
* @private
*/
goog.structs.QuadTree.prototype.count_ = 0;
/**
* Returns a reference to the tree's root node. Callers shouldn't modify nodes,
* directly. This is a convenience for visualization and debugging purposes.
* @return {goog.structs.QuadTree.Node} The root node.
*/
goog.structs.QuadTree.prototype.getRootNode = function() {
return this.root_;
};
/**
* Sets the value of an (x, y) point within the quad-tree.
* @param {number} x The x-coordinate.
* @param {number} y The y-coordinate.
* @param {*} value The value associated with the point.
*/
goog.structs.QuadTree.prototype.set = function(x, y, value) {
var root = this.root_;
if (x < root.x || y < root.y || x > root.x + root.w || y > root.y + root.h) {
throw Error('Out of bounds : (' + x + ', ' + y + ')');
}
if (this.insert_(root, new goog.structs.QuadTree.Point(x, y, value))) {
this.count_++;
}
};
/**
* Gets the value of the point at (x, y) or null if the point is empty.
* @param {number} x The x-coordinate.
* @param {number} y The y-coordinate.
* @param {*=} opt_default The default value to return if the node doesn't
* exist.
* @return {*} The value of the node, the default value if the node
* doesn't exist, or undefined if the node doesn't exist and no default
* has been provided.
*/
goog.structs.QuadTree.prototype.get = function(x, y, opt_default) {
var node = this.find_(this.root_, x, y);
return node ? node.point.value : opt_default;
};
/**
* Removes a point from (x, y) if it exists.
* @param {number} x The x-coordinate.
* @param {number} y The y-coordinate.
* @return {*} The value of the node that was removed, or null if the
* node doesn't exist.
*/
goog.structs.QuadTree.prototype.remove = function(x, y) {
var node = this.find_(this.root_, x, y);
if (node) {
var value = node.point.value;
node.point = null;
node.nodeType = goog.structs.QuadTree.NodeType.EMPTY;
this.balance_(node);
this.count_--;
return value;
} else {
return null;
}
};
/**
* Returns true if the point at (x, y) exists in the tree.
* @param {number} x The x-coordinate.
* @param {number} y The y-coordinate.
* @return {boolean} Whether the tree contains a point at (x, y).
*/
goog.structs.QuadTree.prototype.contains = function(x, y) {
return this.get(x, y) != null;
};
/**
* @return {boolean} Whether the tree is empty.
*/
goog.structs.QuadTree.prototype.isEmpty = function() {
return this.root_.nodeType == goog.structs.QuadTree.NodeType.EMPTY;
};
/**
* @return {number} The number of items in the tree.
*/
goog.structs.QuadTree.prototype.getCount = function() {
return this.count_;
};
/**
* Removes all items from the tree.
*/
goog.structs.QuadTree.prototype.clear = function() {
this.root_.nw = this.root_.ne = this.root_.sw = this.root_.se = null;
this.root_.nodeType = goog.structs.QuadTree.NodeType.EMPTY;
this.root_.point = null;
this.count_ = 0;
};
/**
* Returns an array containing the coordinates of each point stored in the tree.
* @return {Array.<goog.math.Coordinate?>} Array of coordinates.
*/
goog.structs.QuadTree.prototype.getKeys = function() {
var arr = [];
this.traverse_(this.root_, function(node) {
arr.push(new goog.math.Coordinate(node.point.x, node.point.y));
});
return arr;
};
/**
* Returns an array containing all values stored within the tree.
* @return {Array.<Object>} The values stored within the tree.
*/
goog.structs.QuadTree.prototype.getValues = function() {
var arr = [];
this.traverse_(this.root_, function(node) {
// Must have a point because it's a leaf.
arr.push(node.point.value);
});
return arr;
};
/**
* Clones the quad-tree and returns the new instance.
* @return {goog.structs.QuadTree} A clone of the tree.
*/
goog.structs.QuadTree.prototype.clone = function() {
var x1 = this.root_.x;
var y1 = this.root_.y;
var x2 = x1 + this.root_.w;
var y2 = y1 + this.root_.h;
var clone = new goog.structs.QuadTree(x1, y1, x2, y2);
// This is inefficient as the clone needs to recalculate the structure of the
// tree, even though we know it already. But this is easier and can be
// optimized when/if needed.
this.traverse_(this.root_, function(node) {
clone.set(node.point.x, node.point.y, node.point.value);
});
return clone;
};
/**
* Traverses the tree and calls a function on each node.
* @param {function(?, goog.math.Coordinate, goog.structs.QuadTree)} fn
* The function to call for every value. This function takes 3 arguments
* (the value, the coordinate, and the tree itself) and the return value is
* irrelevant.
* @param {Object=} opt_obj The object to be used as the value of 'this'
* within {@ code fn}.
*/
goog.structs.QuadTree.prototype.forEach = function(fn, opt_obj) {
this.traverse_(this.root_, function(node) {
var coord = new goog.math.Coordinate(node.point.x, node.point.y);
fn.call(opt_obj, node.point.value, coord, this);
});
};
/**
* Traverses the tree depth-first, with quadrants being traversed in clockwise
* order (NE, SE, SW, NW). The provided function will be called for each
* leaf node that is encountered.
* @param {goog.structs.QuadTree.Node} node The current node.
* @param {function(goog.structs.QuadTree.Node)} fn The function to call
* for each leaf node. This function takes the node as an argument, and its
* return value is irrelevant.
* @private
*/
goog.structs.QuadTree.prototype.traverse_ = function(node, fn) {
switch (node.nodeType) {
case goog.structs.QuadTree.NodeType.LEAF:
fn.call(this, node);
break;
case goog.structs.QuadTree.NodeType.POINTER:
this.traverse_(node.ne, fn);
this.traverse_(node.se, fn);
this.traverse_(node.sw, fn);
this.traverse_(node.nw, fn);
break;
}
};
/**
* Finds a leaf node with the same (x, y) coordinates as the target point, or
* null if no point exists.
* @param {goog.structs.QuadTree.Node} node The node to search in.
* @param {number} x The x-coordinate of the point to search for.
* @param {number} y The y-coordinate of the point to search for.
* @return {goog.structs.QuadTree.Node} The leaf node that matches the target,
* or null if it doesn't exist.
* @private
*/
goog.structs.QuadTree.prototype.find_ = function(node, x, y) {
switch (node.nodeType) {
case goog.structs.QuadTree.NodeType.EMPTY:
return null;
case goog.structs.QuadTree.NodeType.LEAF:
return node.point.x == x && node.point.y == y ? node : null;
case goog.structs.QuadTree.NodeType.POINTER:
return this.find_(this.getQuadrantForPoint_(node, x, y), x, y);
default:
throw Error('Invalid nodeType');
}
};
/**
* Inserts a point into the tree, updating the tree's structure if necessary.
* @param {goog.structs.QuadTree.Node} parent The parent to insert the point
* into.
* @param {goog.structs.QuadTree.Point} point The point to insert.
* @return {boolean} True if a new node was added to the tree; False if a node
* already existed with the correpsonding coordinates and had its value
* reset.
* @private
*/
goog.structs.QuadTree.prototype.insert_ = function(parent, point) {
switch (parent.nodeType) {
case goog.structs.QuadTree.NodeType.EMPTY:
this.setPointForNode_(parent, point);
return true;
case goog.structs.QuadTree.NodeType.LEAF:
if (parent.point.x == point.x && parent.point.y == point.y) {
this.setPointForNode_(parent, point);
return false;
} else {
this.split_(parent);
return this.insert_(parent, point);
}
case goog.structs.QuadTree.NodeType.POINTER:
return this.insert_(
this.getQuadrantForPoint_(parent, point.x, point.y), point);
default:
throw Error('Invalid nodeType in parent');
}
};
/**
* Converts a leaf node to a pointer node and reinserts the node's point into
* the correct child.
* @param {goog.structs.QuadTree.Node} node The node to split.
* @private
*/
goog.structs.QuadTree.prototype.split_ = function(node) {
var oldPoint = node.point;
node.point = null;
node.nodeType = goog.structs.QuadTree.NodeType.POINTER;
var x = node.x;
var y = node.y;
var hw = node.w / 2;
var hh = node.h / 2;
node.nw = new goog.structs.QuadTree.Node(x, y, hw, hh, node);
node.ne = new goog.structs.QuadTree.Node(x + hw, y, hw, hh, node);
node.sw = new goog.structs.QuadTree.Node(x, y + hh, hw, hh, node);
node.se = new goog.structs.QuadTree.Node(x + hw, y + hh, hw, hh, node);
this.insert_(node, oldPoint);
};
/**
* Attempts to balance a node. A node will need balancing if all its children
* are empty or it contains just one leaf.
* @param {goog.structs.QuadTree.Node} node The node to balance.
* @private
*/
goog.structs.QuadTree.prototype.balance_ = function(node) {
switch (node.nodeType) {
case goog.structs.QuadTree.NodeType.EMPTY:
case goog.structs.QuadTree.NodeType.LEAF:
if (node.parent) {
this.balance_(node.parent);
}
break;
case goog.structs.QuadTree.NodeType.POINTER:
var nw = node.nw, ne = node.ne, sw = node.sw, se = node.se;
var firstLeaf = null;
// Look for the first non-empty child, if there is more than one then we
// break as this node can't be balanced.
if (nw.nodeType != goog.structs.QuadTree.NodeType.EMPTY) {
firstLeaf = nw;
}
if (ne.nodeType != goog.structs.QuadTree.NodeType.EMPTY) {
if (firstLeaf) {
break;
}
firstLeaf = ne;
}
if (sw.nodeType != goog.structs.QuadTree.NodeType.EMPTY) {
if (firstLeaf) {
break;
}
firstLeaf = sw;
}
if (se.nodeType != goog.structs.QuadTree.NodeType.EMPTY) {
if (firstLeaf) {
break;
}
firstLeaf = se;
}
if (!firstLeaf) {
// All child nodes are empty: so make this node empty.
node.nodeType = goog.structs.QuadTree.NodeType.EMPTY;
node.nw = node.ne = node.sw = node.se = null;
} else if (firstLeaf.nodeType == goog.structs.QuadTree.NodeType.POINTER) {
// Only child was a pointer, therefore we can't rebalance.
break;
} else {
// Only child was a leaf: so update node's point and make it a leaf.
node.nodeType = goog.structs.QuadTree.NodeType.LEAF;
node.nw = node.ne = node.sw = node.se = null;
node.point = firstLeaf.point;
}
// Try and balance the parent as well.
if (node.parent) {
this.balance_(node.parent);
}
break;
}
};
/**
* Returns the child quadrant within a node that contains the given (x, y)
* coordinate.
* @param {goog.structs.QuadTree.Node} parent The node.
* @param {number} x The x-coordinate to look for.
* @param {number} y The y-coordinate to look for.
* @return {goog.structs.QuadTree.Node} The child quadrant that contains the
* point.
* @private
*/
goog.structs.QuadTree.prototype.getQuadrantForPoint_ = function(parent, x, y) {
var mx = parent.x + parent.w / 2;
var my = parent.y + parent.h / 2;
if (x < mx) {
return y < my ? parent.nw : parent.sw;
} else {
return y < my ? parent.ne : parent.se;
}
};
/**
* Sets the point for a node, as long as the node is a leaf or empty.
* @param {goog.structs.QuadTree.Node} node The node to set the point for.
* @param {goog.structs.QuadTree.Point} point The point to set.
* @private
*/
goog.structs.QuadTree.prototype.setPointForNode_ = function(node, point) {
if (node.nodeType == goog.structs.QuadTree.NodeType.POINTER) {
throw Error('Can not set point for node of type POINTER');
}
node.nodeType = goog.structs.QuadTree.NodeType.LEAF;
node.point = point;
};
/**
* Enumeration of node types.
* @enum {number}
*/
goog.structs.QuadTree.NodeType = {
EMPTY: 0,
LEAF: 1,
POINTER: 2
};
/**
* Constructs a new quad tree node.
* @param {number} x X-coordiate of node.
* @param {number} y Y-coordinate of node.
* @param {number} w Width of node.
* @param {number} h Height of node.
* @param {goog.structs.QuadTree.Node=} opt_parent Optional parent node.
* @constructor
*/
goog.structs.QuadTree.Node = function(x, y, w, h, opt_parent) {
/**
* The x-coordinate of the node.
* @type {number}
*/
this.x = x;
/**
* The y-coordinate of the node.
* @type {number}
*/
this.y = y;
/**
* The width of the node.
* @type {number}
*/
this.w = w;
/**
* The height of the node.
* @type {number}
*/
this.h = h;
/**
* The parent node.
* @type {goog.structs.QuadTree.Node?}
*/
this.parent = opt_parent || null;
};
/**
* The node's type.
* @type {goog.structs.QuadTree.NodeType}
*/
goog.structs.QuadTree.Node.prototype.nodeType =
goog.structs.QuadTree.NodeType.EMPTY;
/**
* The child node in the North-West quadrant.
* @type {goog.structs.QuadTree.Node?}
*/
goog.structs.QuadTree.Node.prototype.nw = null;
/**
* The child node in the North-East quadrant.
* @type {goog.structs.QuadTree.Node?}
*/
goog.structs.QuadTree.Node.prototype.ne = null;
/**
* The child node in the South-West quadrant.
* @type {goog.structs.QuadTree.Node?}
*/
goog.structs.QuadTree.Node.prototype.sw = null;
/**
* The child node in the South-East quadrant.
* @type {goog.structs.QuadTree.Node?}
*/
goog.structs.QuadTree.Node.prototype.se = null;
/**
* The point for the node, if it is a leaf node.
* @type {goog.structs.QuadTree.Point?}
*/
goog.structs.QuadTree.Node.prototype.point = null;
/**
* Creates a new point object.
* @param {number} x The x-coordinate of the point.
* @param {number} y The y-coordinate of the point.
* @param {*=} opt_value Optional value associated with the point.
* @constructor
*/
goog.structs.QuadTree.Point = function(x, y, opt_value) {
/**
* The x-coordinate for the point.
* @type {number}
*/
this.x = x;
/**
* The y-coordinate for the point.
* @type {number}
*/
this.y = y;
/**
* Optional value associated with the point.
* @type {*}
*/
this.value = goog.isDef(opt_value) ? opt_value : null;
};

View File

@@ -0,0 +1,157 @@
// Copyright 2006 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 Datastructure: Queue.
*
*
* This file provides the implementation of a FIFO Queue structure.
* API is similar to that of com.google.common.collect.IntQueue
*/
goog.provide('goog.structs.Queue');
goog.require('goog.array');
/**
* Class for FIFO Queue data structure.
*
* @constructor
*/
goog.structs.Queue = function() {
this.elements_ = [];
};
/**
* The index of the next element to be removed from the queue.
* @private
* @type {number}
*/
goog.structs.Queue.prototype.head_ = 0;
/**
* The index at which the next element would be added to the queue.
* @private
* @type {number}
*/
goog.structs.Queue.prototype.tail_ = 0;
/**
* Puts the specified element on this queue.
* @param {*} element The element to be added to the queue.
*/
goog.structs.Queue.prototype.enqueue = function(element) {
this.elements_[this.tail_++] = element;
};
/**
* Retrieves and removes the head of this queue.
* @return {*} The element at the head of this queue. Returns undefined if the
* queue is empty.
*/
goog.structs.Queue.prototype.dequeue = function() {
if (this.head_ == this.tail_) {
return undefined;
}
var result = this.elements_[this.head_];
delete this.elements_[this.head_];
this.head_++;
return result;
};
/**
* Retrieves but does not remove the head of this queue.
* @return {*} The element at the head of this queue. Returns undefined if the
* queue is empty.
*/
goog.structs.Queue.prototype.peek = function() {
if (this.head_ == this.tail_) {
return undefined;
}
return this.elements_[this.head_];
};
/**
* Returns the number of elements in this queue.
* @return {number} The number of elements in this queue.
*/
goog.structs.Queue.prototype.getCount = function() {
return this.tail_ - this.head_;
};
/**
* Returns true if this queue contains no elements.
* @return {boolean} true if this queue contains no elements.
*/
goog.structs.Queue.prototype.isEmpty = function() {
return this.tail_ - this.head_ == 0;
};
/**
* Removes all elements from the queue.
*/
goog.structs.Queue.prototype.clear = function() {
this.elements_.length = 0;
this.head_ = 0;
this.tail_ = 0;
};
/**
* Returns true if the given value is in the queue.
* @param {*} obj The value to look for.
* @return {boolean} Whether the object is in the queue.
*/
goog.structs.Queue.prototype.contains = function(obj) {
return goog.array.contains(this.elements_, obj);
};
/**
* Removes the first occurrence of a particular value from the queue.
* @param {*} obj Object to remove.
* @return {boolean} True if an element was removed.
*/
goog.structs.Queue.prototype.remove = function(obj) {
var index = goog.array.indexOf(this.elements_, obj);
if (index < 0) {
return false;
}
if (index == this.head_) {
this.dequeue();
} else {
goog.array.removeAt(this.elements_, index);
this.tail_--;
}
return true;
};
/**
* Returns all the values in the queue.
* @return {Array} An array of the values in the queue.
*/
goog.structs.Queue.prototype.getValues = function() {
return this.elements_.slice(this.head_, this.tail_);
};

View File

@@ -0,0 +1,274 @@
// Copyright 2006 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 Datastructure: Set.
*
* @author arv@google.com (Erik Arvidsson)
* @author pallosp@google.com (Peter Pallos)
*
* This class implements a set data structure. Adding and removing is O(1). It
* supports both object and primitive values. Be careful because you can add
* both 1 and new Number(1), because these are not the same. You can even add
* multiple new Number(1) because these are not equal.
*/
goog.provide('goog.structs.Set');
goog.require('goog.structs');
goog.require('goog.structs.Collection');
goog.require('goog.structs.Map');
/**
* A set that can contain both primitives and objects. Adding and removing
* elements is O(1). Primitives are treated as identical if they have the same
* type and convert to the same string. Objects are treated as identical only
* if they are references to the same object. WARNING: A goog.structs.Set can
* contain both 1 and (new Number(1)), because they are not the same. WARNING:
* Adding (new Number(1)) twice will yield two distinct elements, because they
* are two different objects. WARNING: Any object that is added to a
* goog.structs.Set will be modified! Because goog.getUid() is used to
* identify objects, every object in the set will be mutated.
* @param {Array|Object=} opt_values Initial values to start with.
* @constructor
* @implements {goog.structs.Collection}
*/
goog.structs.Set = function(opt_values) {
this.map_ = new goog.structs.Map;
if (opt_values) {
this.addAll(opt_values);
}
};
/**
* Obtains a unique key for an element of the set. Primitives will yield the
* same key if they have the same type and convert to the same string. Object
* references will yield the same key only if they refer to the same object.
* @param {*} val Object or primitive value to get a key for.
* @return {string} A unique key for this value/object.
* @private
*/
goog.structs.Set.getKey_ = function(val) {
var type = typeof val;
if (type == 'object' && val || type == 'function') {
return 'o' + goog.getUid(/** @type {Object} */ (val));
} else {
return type.substr(0, 1) + val;
}
};
/**
* @return {number} The number of elements in the set.
* @override
*/
goog.structs.Set.prototype.getCount = function() {
return this.map_.getCount();
};
/**
* Add a primitive or an object to the set.
* @param {*} element The primitive or object to add.
* @override
*/
goog.structs.Set.prototype.add = function(element) {
this.map_.set(goog.structs.Set.getKey_(element), element);
};
/**
* Adds all the values in the given collection to this set.
* @param {Array|Object} col A collection containing the elements to add.
*/
goog.structs.Set.prototype.addAll = function(col) {
var values = goog.structs.getValues(col);
var l = values.length;
for (var i = 0; i < l; i++) {
this.add(values[i]);
}
};
/**
* Removes all values in the given collection from this set.
* @param {Array|Object} col A collection containing the elements to remove.
*/
goog.structs.Set.prototype.removeAll = function(col) {
var values = goog.structs.getValues(col);
var l = values.length;
for (var i = 0; i < l; i++) {
this.remove(values[i]);
}
};
/**
* Removes the given element from this set.
* @param {*} element The primitive or object to remove.
* @return {boolean} Whether the element was found and removed.
* @override
*/
goog.structs.Set.prototype.remove = function(element) {
return this.map_.remove(goog.structs.Set.getKey_(element));
};
/**
* Removes all elements from this set.
*/
goog.structs.Set.prototype.clear = function() {
this.map_.clear();
};
/**
* Tests whether this set is empty.
* @return {boolean} True if there are no elements in this set.
*/
goog.structs.Set.prototype.isEmpty = function() {
return this.map_.isEmpty();
};
/**
* Tests whether this set contains the given element.
* @param {*} element The primitive or object to test for.
* @return {boolean} True if this set contains the given element.
* @override
*/
goog.structs.Set.prototype.contains = function(element) {
return this.map_.containsKey(goog.structs.Set.getKey_(element));
};
/**
* Tests whether this set contains all the values in a given collection.
* Repeated elements in the collection are ignored, e.g. (new
* goog.structs.Set([1, 2])).containsAll([1, 1]) is True.
* @param {Object} col A collection-like object.
* @return {boolean} True if the set contains all elements.
*/
goog.structs.Set.prototype.containsAll = function(col) {
return goog.structs.every(col, this.contains, this);
};
/**
* Finds all values that are present in both this set and the given collection.
* @param {Array|Object} col A collection.
* @return {!goog.structs.Set} A new set containing all the values (primitives
* or objects) present in both this set and the given collection.
*/
goog.structs.Set.prototype.intersection = function(col) {
var result = new goog.structs.Set();
var values = goog.structs.getValues(col);
for (var i = 0; i < values.length; i++) {
var value = values[i];
if (this.contains(value)) {
result.add(value);
}
}
return result;
};
/**
* Finds all values that are present in this set and not in the given
* collection.
* @param {Array|Object} col A collection.
* @return {!goog.structs.Set} A new set containing all the values
* (primitives or objects) present in this set but not in the given
* collection.
*/
goog.structs.Set.prototype.difference = function(col) {
var result = this.clone();
result.removeAll(col);
return result;
};
/**
* Returns an array containing all the elements in this set.
* @return {!Array} An array containing all the elements in this set.
*/
goog.structs.Set.prototype.getValues = function() {
return this.map_.getValues();
};
/**
* Creates a shallow clone of this set.
* @return {!goog.structs.Set} A new set containing all the same elements as
* this set.
*/
goog.structs.Set.prototype.clone = function() {
return new goog.structs.Set(this);
};
/**
* Tests whether the given collection consists of the same elements as this set,
* regardless of order, without repetition. Primitives are treated as equal if
* they have the same type and convert to the same string; objects are treated
* as equal if they are references to the same object. This operation is O(n).
* @param {Object} col A collection.
* @return {boolean} True if the given collection consists of the same elements
* as this set, regardless of order, without repetition.
*/
goog.structs.Set.prototype.equals = function(col) {
return this.getCount() == goog.structs.getCount(col) && this.isSubsetOf(col);
};
/**
* Tests whether the given collection contains all the elements in this set.
* Primitives are treated as equal if they have the same type and convert to the
* same string; objects are treated as equal if they are references to the same
* object. This operation is O(n).
* @param {Object} col A collection.
* @return {boolean} True if this set is a subset of the given collection.
*/
goog.structs.Set.prototype.isSubsetOf = function(col) {
var colCount = goog.structs.getCount(col);
if (this.getCount() > colCount) {
return false;
}
// TODO(user) Find the minimal collection size where the conversion makes
// the contains() method faster.
if (!(col instanceof goog.structs.Set) && colCount > 5) {
// Convert to a goog.structs.Set so that goog.structs.contains runs in
// O(1) time instead of O(n) time.
col = new goog.structs.Set(col);
}
return goog.structs.every(this, function(value) {
return goog.structs.contains(col, value);
});
};
/**
* Returns an iterator that iterates over the elements in this set.
* @param {boolean=} opt_keys This argument is ignored.
* @return {!goog.iter.Iterator} An iterator over the elements in this set.
*/
goog.structs.Set.prototype.__iterator__ = function(opt_keys) {
return this.map_.__iterator__(false);
};

View File

@@ -0,0 +1,205 @@
// Copyright 2007 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 Datastructure: Pool.
*
*
* A generic class for handling pools of objects that is more efficient than
* goog.structs.Pool because it doesn't maintain a list of objects that are in
* use. See constructor comment.
*/
goog.provide('goog.structs.SimplePool');
goog.require('goog.Disposable');
/**
* A generic pool class. Simpler and more efficient than goog.structs.Pool
* because it doesn't maintain a list of objects that are in use. This class
* has constant overhead and doesn't create any additional objects as part of
* the pool management after construction time.
*
* IMPORTANT: If the objects being pooled are arrays or maps that can have
* unlimited number of properties, they need to be cleaned before being
* returned to the pool.
*
* Also note that {@see goog.object.clean} actually allocates an array to clean
* the object passed to it, so simply using this function would defy the
* purpose of using the pool.
*
* @param {number} initialCount Initial number of objects to populate the
* free pool at construction time.
* @param {number} maxCount Maximum number of objects to keep in the free pool.
* @constructor
* @extends {goog.Disposable}
*/
goog.structs.SimplePool = function(initialCount, maxCount) {
goog.Disposable.call(this);
/**
* Maximum number of objects allowed
* @type {number}
* @private
*/
this.maxCount_ = maxCount;
/**
* Queue used to store objects that are currently in the pool and available
* to be used.
* @type {Array}
* @private
*/
this.freeQueue_ = [];
this.createInitial_(initialCount);
};
goog.inherits(goog.structs.SimplePool, goog.Disposable);
/**
* Function for overriding createObject. The avoids a common case requiring
* subclassing this class.
* @type {Function}
* @private
*/
goog.structs.SimplePool.prototype.createObjectFn_ = null;
/**
* Function for overriding disposeObject. The avoids a common case requiring
* subclassing this class.
* @type {Function}
* @private
*/
goog.structs.SimplePool.prototype.disposeObjectFn_ = null;
/**
* Sets the {@code createObject} function which is used for creating a new
* object in the pool.
* @param {Function} createObjectFn Create object function which returns the
* newly createrd object.
*/
goog.structs.SimplePool.prototype.setCreateObjectFn = function(createObjectFn) {
this.createObjectFn_ = createObjectFn;
};
/**
* Sets the {@code disposeObject} function which is used for disposing of an
* object in the pool.
* @param {Function} disposeObjectFn Dispose object function which takes the
* object to dispose as a parameter.
*/
goog.structs.SimplePool.prototype.setDisposeObjectFn = function(
disposeObjectFn) {
this.disposeObjectFn_ = disposeObjectFn;
};
/**
* Gets an unused object from the the pool, if there is one available,
* otherwise creates a new one.
* @return {*} An object from the pool or a new one if necessary.
*/
goog.structs.SimplePool.prototype.getObject = function() {
if (this.freeQueue_.length) {
return this.freeQueue_.pop();
}
return this.createObject();
};
/**
* Returns an object to the pool so that it can be reused. If the pool is
* already full, the object is disposed instead.
* @param {*} obj The object to release.
*/
goog.structs.SimplePool.prototype.releaseObject = function(obj) {
if (this.freeQueue_.length < this.maxCount_) {
this.freeQueue_.push(obj);
} else {
this.disposeObject(obj);
}
};
/**
* Populates the pool with initialCount objects.
* @param {number} initialCount The number of objects to add to the pool.
* @private
*/
goog.structs.SimplePool.prototype.createInitial_ = function(initialCount) {
if (initialCount > this.maxCount_) {
throw Error('[goog.structs.SimplePool] Initial cannot be greater than max');
}
for (var i = 0; i < initialCount; i++) {
this.freeQueue_.push(this.createObject());
}
};
/**
* Should be overriden by sub-classes to return an instance of the object type
* that is expected in the pool.
* @return {*} The created object.
*/
goog.structs.SimplePool.prototype.createObject = function() {
if (this.createObjectFn_) {
return this.createObjectFn_();
} else {
return {};
}
};
/**
* Should be overriden to dispose of an object. Default implementation is to
* remove all of the object's members, which should render it useless. Calls the
* object's dispose method, if available.
* @param {*} obj The object to dispose.
*/
goog.structs.SimplePool.prototype.disposeObject = function(obj) {
if (this.disposeObjectFn_) {
this.disposeObjectFn_(obj);
} else if (goog.isObject(obj)) {
if (goog.isFunction(obj.dispose)) {
obj.dispose();
} else {
for (var i in obj) {
delete obj[i];
}
}
}
};
/**
* Disposes of the pool and all objects currently held in the pool.
* @override
* @protected
*/
goog.structs.SimplePool.prototype.disposeInternal = function() {
goog.structs.SimplePool.superClass_.disposeInternal.call(this);
// Call disposeObject on each object held by the pool.
var freeQueue = this.freeQueue_;
while (freeQueue.length) {
this.disposeObject(freeQueue.pop());
}
delete this.freeQueue_;
};

View File

@@ -0,0 +1,403 @@
// Copyright 2009 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 Data structure for set of strings.
*
*
* This class implements a set data structure for strings. Adding and removing
* is O(1). It doesn't contain any bloat from {@link goog.structs.Set}, i.e.
* it isn't optimized for IE6 garbage collector (see the description of
* {@link goog.structs.Map#keys_} for details), and it distinguishes its
* elements by their string value not by hash code.
* The implementation assumes that no new keys are added to Object.prototype.
*/
goog.provide('goog.structs.StringSet');
goog.require('goog.asserts');
goog.require('goog.iter');
/**
* Creates a set of strings.
* @param {!Array=} opt_elements Elements to add to the set. The non-string
* items will be converted to strings, so 15 and '15' will mean the same.
* @constructor
*/
goog.structs.StringSet = function(opt_elements) {
/**
* An object storing the escaped elements of the set in its keys.
* @type {!Object}
* @private
*/
this.elements_ = {};
if (opt_elements) {
for (var i = 0; i < opt_elements.length; i++) {
this.elements_[this.encode(opt_elements[i])] = null;
}
}
goog.asserts.assertObjectPrototypeIsIntact();
};
/**
* Empty object. Referring to it is faster than creating a new empty object in
* {@link #encode}.
* @type {Object}
* @private
*/
goog.structs.StringSet.EMPTY_OBJECT_ = {};
/**
* The '__proto__' and the '__count__' keys aren't enumerable in Firefox, and
* 'toString', 'valueOf', 'constructor', etc. aren't enumerable in IE so they
* have to be escaped before they are added to the internal object.
* NOTE: When a new set is created, 50-80% of the CPU time is spent in encode.
* @param {*} element The element to escape.
* @return {*} The escaped element or the element itself if it doesn't have to
* be escaped.
* @protected
*/
goog.structs.StringSet.prototype.encode = function(element) {
return element in goog.structs.StringSet.EMPTY_OBJECT_ ||
String(element).charCodeAt(0) == 32 ? ' ' + element : element;
};
/**
* Inverse function of {@link #encode}.
* NOTE: forEach would be 30% faster in FF if the compiler inlined decode.
* @param {string} key The escaped element used as the key of the internal
* object.
* @return {string} The unescaped element.
* @protected
*/
goog.structs.StringSet.prototype.decode = function(key) {
return key.charCodeAt(0) == 32 ? key.substr(1) : key;
};
/**
* Adds a single element to the set.
* @param {*} element The element to add. It will be converted to string.
*/
goog.structs.StringSet.prototype.add = function(element) {
this.elements_[this.encode(element)] = null;
};
/**
* Adds a the elements of an array to this set.
* @param {!Array} arr The array to add the elements of.
*/
goog.structs.StringSet.prototype.addArray = function(arr) {
for (var i = 0; i < arr.length; i++) {
this.elements_[this.encode(arr[i])] = null;
}
};
/**
* Adds the elements which are in {@code set1} but not in {@code set2} to this
* set.
* @param {!goog.structs.StringSet} set1 First set.
* @param {!goog.structs.StringSet} set2 Second set.
* @private
*/
goog.structs.StringSet.prototype.addDifference_ = function(set1, set2) {
for (var key in set1.elements_) {
if (!(key in set2.elements_)) {
this.elements_[key] = null;
}
}
};
/**
* Adds a the elements of a set to this set.
* @param {!goog.structs.StringSet} stringSet The set to add the elements of.
*/
goog.structs.StringSet.prototype.addSet = function(stringSet) {
for (var key in stringSet.elements_) {
this.elements_[key] = null;
}
};
/**
* Removes all elements of the set.
*/
goog.structs.StringSet.prototype.clear = function() {
this.elements_ = {};
};
/**
* @return {!goog.structs.StringSet} Clone of the set.
*/
goog.structs.StringSet.prototype.clone = function() {
var ret = new goog.structs.StringSet;
ret.addSet(this);
return ret;
};
/**
* Tells if the set contains the given element.
* @param {*} element The element to check.
* @return {boolean} Whether it is in the set.
*/
goog.structs.StringSet.prototype.contains = function(element) {
return this.encode(element) in this.elements_;
};
/**
* Tells if the set contains all elements of the array.
* @param {!Array} arr The elements to check.
* @return {boolean} Whether they are in the set.
*/
goog.structs.StringSet.prototype.containsArray = function(arr) {
for (var i = 0; i < arr.length; i++) {
if (!(this.encode(arr[i]) in this.elements_)) {
return false;
}
}
return true;
};
/**
* Tells if this set has the same elements as the given set.
* @param {!goog.structs.StringSet} stringSet The other set.
* @return {boolean} Whether they have the same elements.
*/
goog.structs.StringSet.prototype.equals = function(stringSet) {
return this.isSubsetOf(stringSet) && stringSet.isSubsetOf(this);
};
/**
* Calls a function for each element in the set.
* @param {function(string, undefined, !goog.structs.StringSet)} f The function
* to call for every element. It takes the element, undefined (because sets
* have no notion of keys), and the set.
* @param {Object=} opt_obj The object to be used as the value of 'this'
* within {@code f}.
*/
goog.structs.StringSet.prototype.forEach = function(f, opt_obj) {
for (var key in this.elements_) {
f.call(opt_obj, this.decode(key), undefined, this);
}
};
/**
* Counts the number of elements in the set in linear time.
* NOTE: getCount is always called at most once per set instance in google3.
* If this usage pattern won't change, the linear getCount implementation is
* better, because
* <li>populating a set and getting the number of elements in it takes the same
* amount of time as keeping a count_ member up to date and getting its value;
* <li>if getCount is not called, adding and removing elements have no overhead.
* @return {number} The number of elements in the set.
*/
goog.structs.StringSet.prototype.getCount = Object.keys ?
function() {
return Object.keys(this.elements_).length;
} :
function() {
var count = 0;
for (var key in this.elements_) {
count++;
}
return count;
};
/**
* Calculates the difference of two sets.
* @param {!goog.structs.StringSet} stringSet The set to subtract from this set.
* @return {!goog.structs.StringSet} {@code this} minus {@code stringSet}.
*/
goog.structs.StringSet.prototype.getDifference = function(stringSet) {
var ret = new goog.structs.StringSet;
ret.addDifference_(this, stringSet);
return ret;
};
/**
* Calculates the intersection of this set with another set.
* @param {!goog.structs.StringSet} stringSet The set to take the intersection
* with.
* @return {!goog.structs.StringSet} A new set with the common elements.
*/
goog.structs.StringSet.prototype.getIntersection = function(stringSet) {
var ret = new goog.structs.StringSet;
for (var key in this.elements_) {
if (key in stringSet.elements_) {
ret.elements_[key] = null;
}
}
return ret;
};
/**
* Calculates the symmetric difference of two sets.
* @param {!goog.structs.StringSet} stringSet The other set.
* @return {!goog.structs.StringSet} A new set with the elements in exactly one
* of {@code this} and {@code stringSet}.
*/
goog.structs.StringSet.prototype.getSymmetricDifference = function(stringSet) {
var ret = new goog.structs.StringSet;
ret.addDifference_(this, stringSet);
ret.addDifference_(stringSet, this);
return ret;
};
/**
* Calculates the union of this set and another set.
* @param {!goog.structs.StringSet} stringSet The set to take the union with.
* @return {!goog.structs.StringSet} A new set with the union of elements.
*/
goog.structs.StringSet.prototype.getUnion = function(stringSet) {
var ret = this.clone();
ret.addSet(stringSet);
return ret;
};
/**
* @return {!Array.<string>} The elements of the set.
*/
goog.structs.StringSet.prototype.getValues = Object.keys ?
function() {
// Object.keys was introduced in JavaScript 1.8.5, Array#map in 1.6.
return Object.keys(this.elements_).map(this.decode, this);
} :
function() {
var ret = [];
for (var key in this.elements_) {
ret.push(this.decode(key));
}
return ret;
};
/**
* Tells if this set and the given set are disjoint.
* @param {!goog.structs.StringSet} stringSet The other set.
* @return {boolean} True iff they don't have common elements.
*/
goog.structs.StringSet.prototype.isDisjoint = function(stringSet) {
for (var key in this.elements_) {
if (key in stringSet.elements_) {
return false;
}
}
return true;
};
/**
* @return {boolean} Whether the set is empty.
*/
goog.structs.StringSet.prototype.isEmpty = function() {
for (var key in this.elements_) {
return false;
}
return true;
};
/**
* Tells if this set is the subset of the given set.
* @param {!goog.structs.StringSet} stringSet The other set.
* @return {boolean} Whether this set if the subset of that.
*/
goog.structs.StringSet.prototype.isSubsetOf = function(stringSet) {
for (var key in this.elements_) {
if (!(key in stringSet.elements_)) {
return false;
}
}
return true;
};
/**
* Tells if this set is the superset of the given set.
* @param {!goog.structs.StringSet} stringSet The other set.
* @return {boolean} Whether this set if the superset of that.
*/
goog.structs.StringSet.prototype.isSupersetOf = function(stringSet) {
return stringSet.isSubsetOf(this);
};
/**
* Removes a single element from the set.
* @param {*} element The element to remove.
* @return {boolean} Whether the element was in the set.
*/
goog.structs.StringSet.prototype.remove = function(element) {
var key = this.encode(element);
if (key in this.elements_) {
delete this.elements_[key];
return true;
}
return false;
};
/**
* Removes all elements of the given array from this set.
* @param {!Array} arr The elements to remove.
*/
goog.structs.StringSet.prototype.removeArray = function(arr) {
for (var i = 0; i < arr.length; i++) {
delete this.elements_[this.encode(arr[i])];
}
};
/**
* Removes all elements of the given set from this set.
* @param {!goog.structs.StringSet} stringSet The set of elements to remove.
*/
goog.structs.StringSet.prototype.removeSet = function(stringSet) {
for (var key in stringSet.elements_) {
delete this.elements_[key];
}
};
/**
* Returns an iterator that iterates over the elements in the set.
* NOTE: creating the iterator copies the whole set so use {@link #forEach} when
* possible.
* @param {boolean=} opt_keys Ignored for sets.
* @return {!goog.iter.Iterator} An iterator over the elements in the set.
*/
goog.structs.StringSet.prototype.__iterator__ = function(opt_keys) {
return goog.iter.toIterator(this.getValues());
};

View File

@@ -0,0 +1,351 @@
// Copyright 2006 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 Generics method for collection-like classes and objects.
*
* @author arv@google.com (Erik Arvidsson)
*
* This file contains functions to work with collections. It supports using
* Map, Set, Array and Object and other classes that implement collection-like
* methods.
*/
goog.provide('goog.structs');
goog.require('goog.array');
goog.require('goog.object');
// We treat an object as a dictionary if it has getKeys or it is an object that
// isn't arrayLike.
/**
* Returns the number of values in the collection-like object.
* @param {Object} col The collection-like object.
* @return {number} The number of values in the collection-like object.
*/
goog.structs.getCount = function(col) {
if (typeof col.getCount == 'function') {
return col.getCount();
}
if (goog.isArrayLike(col) || goog.isString(col)) {
return col.length;
}
return goog.object.getCount(col);
};
/**
* Returns the values of the collection-like object.
* @param {Object} col The collection-like object.
* @return {!Array} The values in the collection-like object.
*/
goog.structs.getValues = function(col) {
if (typeof col.getValues == 'function') {
return col.getValues();
}
if (goog.isString(col)) {
return col.split('');
}
if (goog.isArrayLike(col)) {
var rv = [];
var l = col.length;
for (var i = 0; i < l; i++) {
rv.push(col[i]);
}
return rv;
}
return goog.object.getValues(col);
};
/**
* Returns the keys of the collection. Some collections have no notion of
* keys/indexes and this function will return undefined in those cases.
* @param {Object} col The collection-like object.
* @return {!Array|undefined} The keys in the collection.
*/
goog.structs.getKeys = function(col) {
if (typeof col.getKeys == 'function') {
return col.getKeys();
}
// if we have getValues but no getKeys we know this is a key-less collection
if (typeof col.getValues == 'function') {
return undefined;
}
if (goog.isArrayLike(col) || goog.isString(col)) {
var rv = [];
var l = col.length;
for (var i = 0; i < l; i++) {
rv.push(i);
}
return rv;
}
return goog.object.getKeys(col);
};
/**
* Whether the collection contains the given value. This is O(n) and uses
* equals (==) to test the existence.
* @param {Object} col The collection-like object.
* @param {*} val The value to check for.
* @return {boolean} True if the map contains the value.
*/
goog.structs.contains = function(col, val) {
if (typeof col.contains == 'function') {
return col.contains(val);
}
if (typeof col.containsValue == 'function') {
return col.containsValue(val);
}
if (goog.isArrayLike(col) || goog.isString(col)) {
return goog.array.contains(/** @type {Array} */ (col), val);
}
return goog.object.containsValue(col, val);
};
/**
* Whether the collection is empty.
* @param {Object} col The collection-like object.
* @return {boolean} True if empty.
*/
goog.structs.isEmpty = function(col) {
if (typeof col.isEmpty == 'function') {
return col.isEmpty();
}
// We do not use goog.string.isEmpty because here we treat the string as
// collection and as such even whitespace matters
if (goog.isArrayLike(col) || goog.isString(col)) {
return goog.array.isEmpty(/** @type {Array} */ (col));
}
return goog.object.isEmpty(col);
};
/**
* Removes all the elements from the collection.
* @param {Object} col The collection-like object.
*/
goog.structs.clear = function(col) {
// NOTE(arv): This should not contain strings because strings are immutable
if (typeof col.clear == 'function') {
col.clear();
} else if (goog.isArrayLike(col)) {
goog.array.clear(/** @type {goog.array.ArrayLike} */ (col));
} else {
goog.object.clear(col);
}
};
/**
* Calls a function for each value in a collection. The function takes
* three arguments; the value, the key and the collection.
*
* @param {S} col The collection-like object.
* @param {function(this:T,?,?,S):?} f The function to call for every value.
* This function takes
* 3 arguments (the value, the key or undefined if the collection has no
* notion of keys, and the collection) and the return value is irrelevant.
* @param {T=} opt_obj The object to be used as the value of 'this'
* within {@code f}.
* @template T,S
*/
goog.structs.forEach = function(col, f, opt_obj) {
if (typeof col.forEach == 'function') {
col.forEach(f, opt_obj);
} else if (goog.isArrayLike(col) || goog.isString(col)) {
goog.array.forEach(/** @type {Array} */ (col), f, opt_obj);
} else {
var keys = goog.structs.getKeys(col);
var values = goog.structs.getValues(col);
var l = values.length;
for (var i = 0; i < l; i++) {
f.call(opt_obj, values[i], keys && keys[i], col);
}
}
};
/**
* Calls a function for every value in the collection. When a call returns true,
* adds the value to a new collection (Array is returned by default).
*
* @param {S} col The collection-like object.
* @param {function(this:T,?,?,S):boolean} f The function to call for every
* value. This function takes
* 3 arguments (the value, the key or undefined if the collection has no
* notion of keys, and the collection) and should return a Boolean. If the
* return value is true the value is added to the result collection. If it
* is false the value is not included.
* @param {T=} opt_obj The object to be used as the value of 'this'
* within {@code f}.
* @return {!Object|!Array} A new collection where the passed values are
* present. If col is a key-less collection an array is returned. If col
* has keys and values a plain old JS object is returned.
* @template T,S
*/
goog.structs.filter = function(col, f, opt_obj) {
if (typeof col.filter == 'function') {
return col.filter(f, opt_obj);
}
if (goog.isArrayLike(col) || goog.isString(col)) {
return goog.array.filter(/** @type {!Array} */ (col), f, opt_obj);
}
var rv;
var keys = goog.structs.getKeys(col);
var values = goog.structs.getValues(col);
var l = values.length;
if (keys) {
rv = {};
for (var i = 0; i < l; i++) {
if (f.call(opt_obj, values[i], keys[i], col)) {
rv[keys[i]] = values[i];
}
}
} else {
// We should not use goog.array.filter here since we want to make sure that
// the index is undefined as well as make sure that col is passed to the
// function.
rv = [];
for (var i = 0; i < l; i++) {
if (f.call(opt_obj, values[i], undefined, col)) {
rv.push(values[i]);
}
}
}
return rv;
};
/**
* Calls a function for every value in the collection and adds the result into a
* new collection (defaults to creating a new Array).
*
* @param {S} col The collection-like object.
* @param {function(this:T,?,?,S):V} f The function to call for every value.
* This function takes 3 arguments (the value, the key or undefined if the
* collection has no notion of keys, and the collection) and should return
* something. The result will be used as the value in the new collection.
* @param {T=} opt_obj The object to be used as the value of 'this'
* within {@code f}.
* @return {!Object.<V>|!Array.<V>} A new collection with the new values. If
* col is a key-less collection an array is returned. If col has keys and
* values a plain old JS object is returned.
* @template T,S,V
*/
goog.structs.map = function(col, f, opt_obj) {
if (typeof col.map == 'function') {
return col.map(f, opt_obj);
}
if (goog.isArrayLike(col) || goog.isString(col)) {
return goog.array.map(/** @type {!Array} */ (col), f, opt_obj);
}
var rv;
var keys = goog.structs.getKeys(col);
var values = goog.structs.getValues(col);
var l = values.length;
if (keys) {
rv = {};
for (var i = 0; i < l; i++) {
rv[keys[i]] = f.call(opt_obj, values[i], keys[i], col);
}
} else {
// We should not use goog.array.map here since we want to make sure that
// the index is undefined as well as make sure that col is passed to the
// function.
rv = [];
for (var i = 0; i < l; i++) {
rv[i] = f.call(opt_obj, values[i], undefined, col);
}
}
return rv;
};
/**
* Calls f for each value in a collection. If any call returns true this returns
* true (without checking the rest). If all returns false this returns false.
*
* @param {S} col The collection-like object.
* @param {function(this:T,?,?,S):boolean} f The function to call for every
* value. This function takes 3 arguments (the value, the key or undefined
* if the collection has no notion of keys, and the collection) and should
* return a boolean.
* @param {T=} opt_obj The object to be used as the value of 'this'
* within {@code f}.
* @return {boolean} True if any value passes the test.
* @template T,S
*/
goog.structs.some = function(col, f, opt_obj) {
if (typeof col.some == 'function') {
return col.some(f, opt_obj);
}
if (goog.isArrayLike(col) || goog.isString(col)) {
return goog.array.some(/** @type {!Array} */ (col), f, opt_obj);
}
var keys = goog.structs.getKeys(col);
var values = goog.structs.getValues(col);
var l = values.length;
for (var i = 0; i < l; i++) {
if (f.call(opt_obj, values[i], keys && keys[i], col)) {
return true;
}
}
return false;
};
/**
* Calls f for each value in a collection. If all calls return true this return
* true this returns true. If any returns false this returns false at this point
* and does not continue to check the remaining values.
*
* @param {S} col The collection-like object.
* @param {function(this:T,?,?,S):boolean} f The function to call for every
* value. This function takes 3 arguments (the value, the key or
* undefined if the collection has no notion of keys, and the collection)
* and should return a boolean.
* @param {T=} opt_obj The object to be used as the value of 'this'
* within {@code f}.
* @return {boolean} True if all key-value pairs pass the test.
* @template T,S
*/
goog.structs.every = function(col, f, opt_obj) {
if (typeof col.every == 'function') {
return col.every(f, opt_obj);
}
if (goog.isArrayLike(col) || goog.isString(col)) {
return goog.array.every(/** @type {!Array} */ (col), f, opt_obj);
}
var keys = goog.structs.getKeys(col);
var values = goog.structs.getValues(col);
var l = values.length;
for (var i = 0; i < l; i++) {
if (!f.call(opt_obj, values[i], keys && keys[i], col)) {
return false;
}
}
return true;
};

View File

@@ -0,0 +1,428 @@
// 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.
* @override
*/
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.getChildCount();
};
/**
* Tells if the node is the last child of its parent. This method helps how to
* connect the tree nodes with lines: L shapes should be used before the last
* children and |- shapes before the rest. Schematic tree visualization:
*
* <pre>
* Node1
* |-Node2
* | L-Node3
* | |-Node4
* | L-Node5
* L-Node6
* </pre>
*
* @return {boolean} Whether the node has parent and is the last child of it.
*/
goog.structs.TreeNode.prototype.isLastChild = function() {
var parent = this.getParent();
return Boolean(parent && this == goog.array.peek(parent.getChildren()));
};
/**
* @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 children.
*/
goog.structs.TreeNode.prototype.getChildCount = function() {
return this.getChildren().length;
};
/**
* @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;
};
/**
* Builds a nested array structure from the node keys in this node's subtree to
* facilitate testing tree operations that change the hierarchy.
* @return {!Array} The structure of this node's descendants as nested array
* of node keys. The number of unclosed opening brackets up to a particular
* node is proportional to the indentation of that node in the graphical
* representation of the tree. Example:
* <pre>
* this
* |- child1
* | L- grandchild
* L- child2
* </pre>
* is represented as ['child1', ['grandchild'], 'child2'].
*/
goog.structs.TreeNode.prototype.getSubtreeKeys = function() {
var ret = [];
this.forEachChild(function(child) {
ret.push(child.getKey());
if (!child.isLeaf()) {
ret.push(child.getSubtreeKeys());
}
});
return ret;
};
/**
* 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) {
var current = node;
do {
current = current.getParent();
} while (current && current != this);
return Boolean(current);
};
/**
* Finds the deepest common ancestor of the given nodes. The concept of
* ancestor is not strict in this case, it includes the node itself.
* @param {...!goog.structs.TreeNode} var_args The nodes.
* @return {goog.structs.TreeNode} The common ancestor of the nodes or null if
* they are from different trees.
*/
goog.structs.TreeNode.findCommonAncestor = function(var_args) {
var ret = arguments[0];
if (!ret) {
return null;
}
var retDepth = ret.getDepth();
for (var i = 1; i < arguments.length; i++) {
var node = arguments[i];
var depth = node.getDepth();
while (node != ret) {
if (depth <= retDepth) {
ret = ret.getParent();
retDepth--;
}
if (depth > retDepth) {
node = node.getParent();
depth--;
}
}
}
return ret;
};
/**
* 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 in preorder.
* @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);
});
};
/**
* Traverses the subtree with the possibility to skip branches. Starts with
* this node, and visits the descendant nodes depth-first, in preorder.
* @param {function(!goog.structs.TreeNode): (boolean|undefined)} f Callback
* function. It takes the node as argument. The children of this node will
* be visited if the callback returns true or undefined, and will be
* skipped if the callback returns false.
* @param {Object=} opt_this The object to be used as the value of {@code this}
* within {@code f}.
*/
goog.structs.TreeNode.prototype.traverse = function(f, opt_this) {
if (f.call(opt_this, this) !== false) {
goog.array.forEach(this.getChildren(), function(child) {
child.traverse(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);
};
/**
* Replaces a child node at the given index.
* @param {!goog.structs.TreeNode} newChild Child node to set. It must not have
* parent node.
* @param {number} index Valid index of the old child to replace.
* @return {!goog.structs.TreeNode} The original child node, detached from its
* parent.
*/
goog.structs.TreeNode.prototype.replaceChildAt = function(newChild, index) {
goog.asserts.assert(!newChild.getParent(),
'newChild must not have parent node');
var children = this.getChildren();
var oldChild = children[index];
goog.asserts.assert(oldChild, 'Invalid child or child index is given.');
oldChild.setParent(null);
children[index] = newChild;
newChild.setParent(this);
return oldChild;
};
/**
* Replaces the given child node.
* @param {!goog.structs.TreeNode} newChild New node to replace
* {@code oldChild}. It must not have parent node.
* @param {!goog.structs.TreeNode} oldChild Existing child node to be replaced.
* @return {!goog.structs.TreeNode} The replaced child node detached from its
* parent.
*/
goog.structs.TreeNode.prototype.replaceChild = function(newChild, oldChild) {
return this.replaceChildAt(newChild,
goog.array.indexOf(this.getChildren(), oldChild));
};
/**
* 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_;
};

View File

@@ -0,0 +1,368 @@
// Copyright 2007 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 Datastructure: Trie.
*
*
* This file provides the implementation of a trie data structure. A trie is a
* data structure that stores key/value pairs in a prefix tree. See:
* http://en.wikipedia.org/wiki/Trie
*/
goog.provide('goog.structs.Trie');
goog.require('goog.object');
goog.require('goog.structs');
/**
* Class for a Trie datastructure. Trie data structures are made out of trees
* of Trie classes.
*
* @param {Object=} opt_trie Optional goog.structs.Trie or Object to initialize
* trie with.
* @constructor
*/
goog.structs.Trie = function(opt_trie) {
/**
* This trie's child nodes.
* @private
* @type {Object.<goog.structs.Trie>}
*/
this.childNodes_ = {};
if (opt_trie) {
this.setAll(opt_trie);
}
};
/**
* This trie's value. For the base trie, this will be the value of the
* empty key, if defined.
* @private
* @type {*}
*/
goog.structs.Trie.prototype.value_ = undefined;
/**
* Sets the given key/value pair in the trie. O(L), where L is the length
* of the key.
* @param {string} key The key.
* @param {*} value The value.
*/
goog.structs.Trie.prototype.set = function(key, value) {
this.setOrAdd_(key, value, false);
};
/**
* Adds the given key/value pair in the trie. Throw an exception if the key
* already exists in the trie. O(L), where L is the length of the key.
* @param {string} key The key.
* @param {*} value The value.
*/
goog.structs.Trie.prototype.add = function(key, value) {
this.setOrAdd_(key, value, true);
};
/**
* Helper function for set and add. Adds the given key/value pair to
* the trie, or, if the key already exists, sets the value of the key. If
* opt_add is true, then throws an exception if the key already has a value in
* the trie. O(L), where L is the length of the key.
* @param {string} key The key.
* @param {*} value The value.
* @param {boolean=} opt_add Throw exception if key is already in the trie.
* @private
*/
goog.structs.Trie.prototype.setOrAdd_ = function(key, value, opt_add) {
var node = this;
for (var characterPosition = 0; characterPosition < key.length;
characterPosition++) {
var currentCharacter = key.charAt(characterPosition);
if (!node.childNodes_[currentCharacter]) {
node.childNodes_[currentCharacter] = new goog.structs.Trie();
}
node = node.childNodes_[currentCharacter];
}
if (opt_add && node.value_ !== undefined) {
throw Error('The collection already contains the key "' + key + '"');
} else {
node.value_ = value;
}
};
/**
* Adds multiple key/value pairs from another goog.structs.Trie or Object.
* O(N) where N is the number of nodes in the trie.
* @param {Object|goog.structs.Trie} trie Object containing the data to add.
*/
goog.structs.Trie.prototype.setAll = function(trie) {
var keys = goog.structs.getKeys(trie);
var values = goog.structs.getValues(trie);
for (var i = 0; i < keys.length; i++) {
this.set(keys[i], values[i]);
}
};
/**
* Retrieves a value from the trie given a key. O(L), where L is the length of
* the key.
* @param {string} key The key to retrieve from the trie.
* @return {*} The value of the key in the trie, or undefined if the trie does
* not contain this key.
*/
goog.structs.Trie.prototype.get = function(key) {
var node = this;
for (var characterPosition = 0; characterPosition < key.length;
characterPosition++) {
var currentCharacter = key.charAt(characterPosition);
if (!node.childNodes_[currentCharacter]) {
return undefined;
}
node = node.childNodes_[currentCharacter];
}
return node.value_;
};
/**
* Retrieves all values from the trie that correspond to prefixes of the given
* input key. O(L), where L is the length of the key.
*
* @param {string} key The key to use for lookup. The given key as well as all
* prefixes of the key are retrieved.
* @param {?number=} opt_keyStartIndex Optional position in key to start lookup
* from. Defaults to 0 if not specified.
* @return {Object} Map of end index of matching prefixes and corresponding
* values. Empty if no match found.
*/
goog.structs.Trie.prototype.getKeyAndPrefixes = function(key,
opt_keyStartIndex) {
var node = this;
var matches = {};
var characterPosition = opt_keyStartIndex || 0;
if (node.value_ !== undefined) {
matches[characterPosition] = node.value_;
}
for (; characterPosition < key.length; characterPosition++) {
var currentCharacter = key.charAt(characterPosition);
if (!(currentCharacter in node.childNodes_)) {
break;
}
node = node.childNodes_[currentCharacter];
if (node.value_ !== undefined) {
matches[characterPosition] = node.value_;
}
}
return matches;
};
/**
* Gets the values of the trie. Not returned in any reliable order. O(N) where
* N is the number of nodes in the trie. Calls getValuesInternal_.
* @return {Array} The values in the trie.
*/
goog.structs.Trie.prototype.getValues = function() {
var allValues = [];
this.getValuesInternal_(allValues);
return allValues;
};
/**
* Gets the values of the trie. Not returned in any reliable order. O(N) where
* N is the number of nodes in the trie. Builds the values as it goes.
* @param {Array.<string>} allValues Array to place values into.
* @private
*/
goog.structs.Trie.prototype.getValuesInternal_ = function(allValues) {
if (this.value_ !== undefined) {
allValues.push(this.value_);
}
for (var childNode in this.childNodes_) {
this.childNodes_[childNode].getValuesInternal_(allValues);
}
};
/**
* Gets the keys of the trie. Not returned in any reliable order. O(N) where
* N is the number of nodes in the trie (or prefix subtree).
* @param {string=} opt_prefix Find only keys with this optional prefix.
* @return {Array} The keys in the trie.
*/
goog.structs.Trie.prototype.getKeys = function(opt_prefix) {
var allKeys = [];
if (opt_prefix) {
// Traverse to the given prefix, then call getKeysInternal_ to dump the
// keys below that point.
var node = this;
for (var characterPosition = 0; characterPosition < opt_prefix.length;
characterPosition++) {
var currentCharacter = opt_prefix.charAt(characterPosition);
if (!node.childNodes_[currentCharacter]) {
return [];
}
node = node.childNodes_[currentCharacter];
}
node.getKeysInternal_(opt_prefix, allKeys);
} else {
this.getKeysInternal_('', allKeys);
}
return allKeys;
};
/**
* Private method to get keys from the trie. Builds the keys as it goes.
* @param {string} keySoFar The partial key (prefix) traversed so far.
* @param {Array} allKeys The partially built array of keys seen so far.
* @private
*/
goog.structs.Trie.prototype.getKeysInternal_ = function(keySoFar, allKeys) {
if (this.value_ !== undefined) {
allKeys.push(keySoFar);
}
for (var childNode in this.childNodes_) {
this.childNodes_[childNode].getKeysInternal_(keySoFar + childNode, allKeys);
}
};
/**
* Checks to see if a certain key is in the trie. O(L), where L is the length
* of the key.
* @param {string} key A key that may be in the trie.
* @return {boolean} Whether the trie contains key.
*/
goog.structs.Trie.prototype.containsKey = function(key) {
return this.get(key) !== undefined;
};
/**
* Checks to see if a certain value is in the trie. Worst case is O(N) where
* N is the number of nodes in the trie.
* @param {*} value A value that may be in the trie.
* @return {boolean} Whether the trie contains the value.
*/
goog.structs.Trie.prototype.containsValue = function(value) {
if (this.value_ === value) {
return true;
}
for (var childNode in this.childNodes_) {
if (this.childNodes_[childNode].containsValue(value)) {
return true;
}
}
return false;
};
/**
* Completely empties a trie of all keys and values. ~O(1)
*/
goog.structs.Trie.prototype.clear = function() {
this.childNodes_ = {};
this.value_ = undefined;
};
/**
* Removes a key from the trie or throws an exception if the key is not in the
* trie. O(L), where L is the length of the key.
* @param {string} key A key that should be removed from the trie.
* @return {*} The value whose key was removed.
*/
goog.structs.Trie.prototype.remove = function(key) {
var node = this;
var parents = [];
for (var characterPosition = 0; characterPosition < key.length;
characterPosition++) {
var currentCharacter = key.charAt(characterPosition);
if (!node.childNodes_[currentCharacter]) {
throw Error('The collection does not have the key "' + key + '"');
}
// Archive the current parent and child name (key in childNodes_) so that
// we may remove the following node and its parents if they are empty.
parents.push([node, currentCharacter]);
node = node.childNodes_[currentCharacter];
}
var oldValue = node.value_;
delete node.value_;
while (parents.length > 0) {
var currentParentAndCharacter = parents.pop();
var currentParent = currentParentAndCharacter[0];
var currentCharacter = currentParentAndCharacter[1];
if (goog.object.isEmpty(
currentParent.childNodes_[currentCharacter].childNodes_)) {
// If we have no child nodes, then remove this node.
delete currentParent.childNodes_[currentCharacter];
} else {
// No point of traversing back any further, since we can't remove this
// path.
break;
}
}
return oldValue;
};
/**
* Clones a trie and returns a new trie. O(N), where N is the number of nodes
* in the trie.
* @return {goog.structs.Trie} A new goog.structs.Trie with the same key value
* pairs.
*/
goog.structs.Trie.prototype.clone = function() {
return new goog.structs.Trie(this);
};
/**
* Returns the number of key value pairs in the trie. O(N), where N is the
* number of nodes in the trie.
* TODO: This could be optimized by storing a weight (count below) in every
* node.
* @return {number} The number of pairs.
*/
goog.structs.Trie.prototype.getCount = function() {
return goog.structs.getCount(this.getValues());
};
/**
* Returns true if this trie contains no elements. ~O(1).
* @return {boolean} True iff this trie contains no elements.
*/
goog.structs.Trie.prototype.isEmpty = function() {
return this.value_ === undefined && goog.structs.isEmpty(this.childNodes_);
};