/**
* The Search Component
*
* @module aui-search
* @submodule aui-search-ternary-search-tree
*/
var Lang = A.Lang;
/**
* A base class for TernarySearchTree.
*
* @class A.TernarySearchTree
* @extends Base
* @param {Object} config Object literal specifying widget configuration
* properties.
* @constructor
*/
var TernarySearchTree = A.Component.create({
/**
* Static property provides a string to identify the class.
*
* @property NAME
* @type String
* @static
*/
NAME: 'TernarySearchTree',
/**
* Static property provides a string to identify the namespace.
*
* @property NS
* @type String
* @static
*/
NS: 'ternarysearchtree',
/**
* Static property used to define which component it extends.
*
* @property EXTENDS
* @type Object
* @static
*/
EXTENDS: A.Base,
prototype: {
/**
* Adds a word in the tree.
*
* @method add
* @param word
*/
add: function(word) {
var instance = this;
var root = instance._root;
var node = instance._insert(root, word, 0);
if (!Lang.isValue(root)) {
instance._root = node;
}
},
/**
* Checks if the argument is part of the tree.
*
* @method contains
* @param word
* @return {Boolean}
*/
contains: function(word) {
var instance = this;
var node = instance._search(instance._root, word, 0);
return !!(Lang.isValue(node) && node.isEndOfWord());
},
/**
* Set tree's root to `null`.
*
* @method empty
*/
empty: function() {
var instance = this;
instance._root = null;
},
/**
* Checks if a pattern match.
*
* @method patternMatch
* @param pattern
* @return {Array}
*/
patternMatch: function(pattern) {
var instance = this;
var results = [];
instance._patternMatch(instance._root, pattern, 0, results);
return results;
},
/**
* Searches for a prefix in the tree.
*
* @method prefixSearch
* @param prefix
* @return {Array}
*/
prefixSearch: function(prefix) {
var instance = this;
var results = [];
var node = instance._search(instance._root, prefix, 0);
if (node) {
instance._inOrderTraversal(node.get('child'), results);
}
return results;
},
/**
* Traversals a tree.
*
* @method _inOrderTraversal
* @param node
* @param results
* @protected
*/
_inOrderTraversal: function(node, results) {
var instance = this;
if (!Lang.isValue(node)) {
return;
}
instance._inOrderTraversal(node.get('smallerNode'), results);
if (node.isEndOfWord()) {
results.push(node.get('word'));
}
instance._inOrderTraversal(node.get('child'), results);
instance._inOrderTraversal(node.get('largerNode'), results);
},
/**
* Insert a node in the tree.
*
* @method _insert
* @param node
* @param word
* @param index
* @protected
*/
_insert: function(node, word, index) {
var instance = this;
var character = word.charAt(index);
if (Lang.isValue(node)) {
if (character === node.get('character')) {
if (index + 1 < word.length) {
node.set('child', instance._insert(node.get('child'), word, index + 1));
}
else {
node.set('word', word);
}
}
else if (character < node.get('character')) {
node.set('smallerNode', instance._insert(node.get('smallerNode'), word, index));
}
else {
node.set('largerNode', instance._insert(node.get('largerNode'), word, index));
}
}
else {
node = instance._insert(
new A.TernarySearchNode({
character: character
}),
word,
index
);
}
return node;
},
/**
* Recursive search for a pattern in the tree.
*
* @method _patternMatch
* @param node
* @param pattern
* @param index
* @param results
* @protected
*/
_patternMatch: function(node, pattern, index, results) {
var instance = this;
if (Lang.isValue(node)) {
var character = pattern.charAt(index);
var nodeCharacter = node.get('character');
var patternChar = TernarySearchTree.PATTERN_CHAR;
if (character === patternChar || character < nodeCharacter) {
instance._patternMatch(node.get('smallerNode'), pattern, index, results);
}
if (character === patternChar || character === nodeCharacter) {
if (index + 1 < pattern.length) {
instance._patternMatch(node.get('child'), pattern, index + 1, results);
}
else if (node.isEndOfWord()) {
results.push(node.get('word'));
}
}
if (character === patternChar || character > nodeCharacter) {
instance._patternMatch(node.get('largerNode'), pattern, index, results);
}
}
},
/**
* Recursive search for a node in the tree.
*
* @method _search
* @param node
* @param word
* @param index
* @protected
*/
_search: function(node, word, index) {
var instance = this;
var result = node;
if (Lang.isValue(node)) {
var character = word.charAt(index);
var nodeCharacter = node.get('character');
if (character === nodeCharacter) {
if (index + 1 < word.length) {
result = instance._search(node.get('child'), word, index + 1);
}
}
else if (character < nodeCharacter) {
result = instance._search(node.get('smallerNode'), word, index);
}
else {
result = instance._search(node.get('largerNode'), word, index);
}
}
return result;
}
}
});
TernarySearchTree.PATTERN_CHAR = '?';
A.TernarySearchTree = TernarySearchTree;