Path: blob/trunk/third_party/closure/goog/datasource/datasource.js
2868 views
// Copyright 2006 The Closure Library Authors. All Rights Reserved.1//2// Licensed under the Apache License, Version 2.0 (the "License");3// you may not use this file except in compliance with the License.4// You may obtain a copy of the License at5//6// http://www.apache.org/licenses/LICENSE-2.07//8// Unless required by applicable law or agreed to in writing, software9// distributed under the License is distributed on an "AS-IS" BASIS,10// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.11// See the License for the specific language governing permissions and12// limitations under the License.1314/**15* @fileoverview Generic rich data access API.16*17* Abstraction for data sources that allows listening for changes at different18* levels of the data tree and updating the data via XHR requests19*20*/212223goog.provide('goog.ds.BaseDataNode');24goog.provide('goog.ds.BasicNodeList');25goog.provide('goog.ds.DataNode');26goog.provide('goog.ds.DataNodeList');27goog.provide('goog.ds.EmptyNodeList');28goog.provide('goog.ds.LoadState');29goog.provide('goog.ds.SortedNodeList');30goog.provide('goog.ds.Util');31goog.provide('goog.ds.logger');3233goog.require('goog.array');34goog.require('goog.log');35363738/**39* Interface for node in rich data tree.40*41* Names that are reserved for system use and shouldn't be used for data node42* names: eval, toSource, toString, unwatch, valueOf, watch. Behavior is43* undefined if these names are used.44*45* @constructor46*/47goog.ds.DataNode = function() {};484950/**51* Get the value of the node52* @param {...?} var_args Do not check arity of arguments, because53* some subclasses require args.54* @return {*} The value of the node, or null if no value.55*/56goog.ds.DataNode.prototype.get = goog.abstractMethod;575859/**60* Set the value of the node61* @param {*} value The new value of the node.62*/63goog.ds.DataNode.prototype.set = goog.abstractMethod;646566/**67* Gets all of the child nodes of the current node.68* Should return an empty DataNode list if no child nodes.69* @param {string=} opt_selector String selector to choose child nodes.70* @return {!goog.ds.DataNodeList} The child nodes.71*/72goog.ds.DataNode.prototype.getChildNodes = goog.abstractMethod;737475/**76* Gets a named child node of the current node77* @param {string} name The node name.78* @param {boolean=} opt_canCreate Whether to create a child node if it does not79* exist.80* @return {goog.ds.DataNode} The child node, or null81* if no node of this name exists.82*/83goog.ds.DataNode.prototype.getChildNode = goog.abstractMethod;848586/**87* Gets the value of a child node88* @param {string} name The node name.89* @return {*} The value of the node, or null if no value or the child node90* doesn't exist.91*/92goog.ds.DataNode.prototype.getChildNodeValue = goog.abstractMethod;939495/**96* Sets a named child node of the current node.97*98* @param {string} name The node name.99* @param {Object} value The value to set, can be DataNode, object, property,100* or null. If value is null, removes the child node.101* @return {Object} The child node, if the node was set.102*/103goog.ds.DataNode.prototype.setChildNode = goog.abstractMethod;104105106/**107* Get the name of the node relative to the parent node108* @return {string} The name of the node.109*/110goog.ds.DataNode.prototype.getDataName = goog.abstractMethod;111112113/**114* Set the name of the node relative to the parent node115* @param {string} name The name of the node.116*/117goog.ds.DataNode.prototype.setDataName = goog.abstractMethod;118119120/**121* Gets the a qualified data path to this node122* @return {string} The data path.123*/124goog.ds.DataNode.prototype.getDataPath = goog.abstractMethod;125126127/**128* Load or reload the backing data for this node129*/130goog.ds.DataNode.prototype.load = goog.abstractMethod;131132133/**134* Gets the state of the backing data for this node135* @return {goog.ds.LoadState} The state.136*/137goog.ds.DataNode.prototype.getLoadState = goog.abstractMethod;138139140/**141* Whether the value of this node is a homogeneous list of data142* @return {boolean} True if a list.143*/144goog.ds.DataNode.prototype.isList = goog.abstractMethod;145146147/**148* Enum for load state of a DataNode.149* @enum {string}150*/151goog.ds.LoadState = {152LOADED: 'LOADED',153LOADING: 'LOADING',154FAILED: 'FAILED',155NOT_LOADED: 'NOT_LOADED'156};157158159160/**161* Base class for data node functionality, has default implementations for162* many of the functions.163*164* implements {goog.ds.DataNode}165* @constructor166*/167goog.ds.BaseDataNode = function() {};168169170/**171* Set the value of the node172* @param {Object} value The new value of the node.173*/174goog.ds.BaseDataNode.prototype.set = goog.abstractMethod;175176177/**178* Gets all of the child nodes of the current node.179* Should return an empty DataNode list if no child nodes.180* @param {string=} opt_selector String selector to choose child nodes.181* @return {!goog.ds.DataNodeList} The child nodes.182*/183goog.ds.BaseDataNode.prototype.getChildNodes = function(opt_selector) {184return new goog.ds.EmptyNodeList();185};186187188/**189* Gets a named child node of the current node190* @param {string} name The node name.191* @param {boolean=} opt_canCreate Whether you can create the child node if192* it doesn't exist already.193* @return {goog.ds.DataNode} The child node, or null if no node of194* this name exists and opt_create is false.195*/196goog.ds.BaseDataNode.prototype.getChildNode = function(name, opt_canCreate) {197return null;198};199200201/**202* Gets the value of a child node203* @param {string} name The node name.204* @return {Object} The value of the node, or null if no value or the205* child node doesn't exist.206*/207goog.ds.BaseDataNode.prototype.getChildNodeValue = function(name) {208return null;209};210211212/**213* Get the name of the node relative to the parent node214* @return {string} The name of the node.215*/216goog.ds.BaseDataNode.prototype.getDataName = goog.abstractMethod;217218219/**220* Gets the a qualified data path to this node221* @return {string} The data path.222*/223goog.ds.BaseDataNode.prototype.getDataPath = function() {224var parentPath = '';225var myName = this.getDataName();226if (this.getParent()) {227parentPath = this.getParent().getDataPath() +228(myName.indexOf(229/** @suppress {missingRequire} */ goog.ds.STR_ARRAY_START) != -1 ?230'' :231/** @suppress {missingRequire} */ goog.ds.STR_PATH_SEPARATOR);232}233234return parentPath + myName;235};236237238/**239* Load or reload the backing data for this node240*/241goog.ds.BaseDataNode.prototype.load = goog.nullFunction;242243244/**245* Gets the state of the backing data for this node246* @return {goog.ds.LoadState} The state.247*/248goog.ds.BaseDataNode.prototype.getLoadState = function() {249return goog.ds.LoadState.LOADED;250};251252253/**254* Gets the parent node. Subclasses implement this function255* @return {?goog.ds.DataNode}256* @protected257*/258goog.ds.BaseDataNode.prototype.getParent = goog.abstractMethod;259260261/**262* Interface for node list in rich data tree.263*264* Has both map and list-style accessors265*266* @constructor267* @extends {goog.ds.DataNode}268*/269// TODO(arv): Use interfaces when available.270goog.ds.DataNodeList = function() {};271272273/**274* Add a node to the node list.275* If the node has a dataName, uses this for the key in the map.276*277* @param {goog.ds.DataNode} node The node to add.278*/279goog.ds.DataNodeList.prototype.add = goog.abstractMethod;280281282/**283* Get a node by string key.284* Returns null if node doesn't exist.285*286* @param {string} key String lookup key.287* @return {*} The node, or null if doesn't exist.288* @override289*/290goog.ds.DataNodeList.prototype.get = goog.abstractMethod;291292293/**294* Get a node by index295* Returns null if the index is out of range296*297* @param {number} index The index of the node.298* @return {goog.ds.DataNode} The node, or null if doesn't exist.299*/300goog.ds.DataNodeList.prototype.getByIndex = goog.abstractMethod;301302303/**304* Gets the size of the node list305*306* @return {number} The size of the list.307*/308goog.ds.DataNodeList.prototype.getCount = goog.abstractMethod;309310311/**312* Sets a node in the list of a given name313* @param {string} name Name of the node.314* @param {goog.ds.DataNode} node The node.315*/316goog.ds.DataNodeList.prototype.setNode = goog.abstractMethod;317318319/**320* Removes a node in the list of a given name321* @param {string} name Name of the node.322* @return {boolean} True if node existed and was deleted.323*/324goog.ds.DataNodeList.prototype.removeNode = goog.abstractMethod;325326327/**328* Simple node list implementation with underlying array and map329* implements goog.ds.DataNodeList.330*331* Names that are reserved for system use and shouldn't be used for data node332* names: eval, toSource, toString, unwatch, valueOf, watch. Behavior is333* undefined if these names are used.334*335* @param {Array<goog.ds.DataNode>=} opt_nodes optional nodes to add to list.336* @constructor337* @extends {goog.ds.DataNodeList}338*/339// TODO(arv): Use interfaces when available.340goog.ds.BasicNodeList = function(opt_nodes) {341this.map_ = {};342this.list_ = [];343this.indexMap_ = {};344if (opt_nodes) {345for (var i = 0, node; node = opt_nodes[i]; i++) {346this.add(node);347}348}349};350351352/**353* Add a node to the node list.354* If the node has a dataName, uses this for the key in the map.355* TODO(user) Remove function as well356*357* @param {goog.ds.DataNode} node The node to add.358* @override359*/360goog.ds.BasicNodeList.prototype.add = function(node) {361this.list_.push(node);362var dataName = node.getDataName();363if (dataName) {364this.map_[dataName] = node;365this.indexMap_[dataName] = this.list_.length - 1;366}367};368369370/**371* Get a node by string key.372* Returns null if node doesn't exist.373*374* @param {string} key String lookup key.375* @return {goog.ds.DataNode} The node, or null if doesn't exist.376* @override377*/378goog.ds.BasicNodeList.prototype.get = function(key) {379return this.map_[key] || null;380};381382383/**384* Get a node by index385* Returns null if the index is out of range386*387* @param {number} index The index of the node.388* @return {goog.ds.DataNode} The node, or null if doesn't exist.389* @override390*/391goog.ds.BasicNodeList.prototype.getByIndex = function(index) {392return this.list_[index] || null;393};394395396/**397* Gets the size of the node list398*399* @return {number} The size of the list.400* @override401*/402goog.ds.BasicNodeList.prototype.getCount = function() {403return this.list_.length;404};405406407/**408* Sets a node in the list of a given name409* @param {string} name Name of the node.410* @param {goog.ds.DataNode} node The node.411* @override412*/413goog.ds.BasicNodeList.prototype.setNode = function(name, node) {414if (node == null) {415this.removeNode(name);416} else {417var existingNode = this.indexMap_[name];418if (existingNode != null) {419this.map_[name] = node;420this.list_[existingNode] = node;421} else {422this.add(node);423}424}425};426427428/**429* Removes a node in the list of a given name430* @param {string} name Name of the node.431* @return {boolean} True if node existed and was deleted.432* @override433*/434goog.ds.BasicNodeList.prototype.removeNode = function(name) {435var existingNode = this.indexMap_[name];436if (existingNode != null) {437this.list_.splice(existingNode, 1);438delete this.map_[name];439delete this.indexMap_[name];440for (var index in this.indexMap_) {441if (this.indexMap_[index] > existingNode) {442this.indexMap_[index]--;443}444}445}446return existingNode != null;447};448449450/**451* Get the index of a named node452* @param {string} name The name of the node to get the index of.453* @return {number|undefined} The index.454*/455goog.ds.BasicNodeList.prototype.indexOf = function(name) {456return this.indexMap_[name];457};458459460/**461* Immulatable empty node list462* @extends {goog.ds.BasicNodeList}463* @constructor464* @final465*/466467goog.ds.EmptyNodeList = function() {468goog.ds.BasicNodeList.call(this);469};470goog.inherits(goog.ds.EmptyNodeList, goog.ds.BasicNodeList);471472473/**474* Add a node to the node list.475* If the node has a dataName, uses this for the key in the map.476*477* @param {goog.ds.DataNode} node The node to add.478* @override479*/480goog.ds.EmptyNodeList.prototype.add = function(node) {481throw Error('Can\'t add to EmptyNodeList');482};483484485486/**487* Node list implementation which maintains sort order during insertion and488* modification operations based on a comparison function.489*490* The SortedNodeList does not guarantee sort order will be maintained if491* the underlying data nodes are modified externally.492*493* Names that are reserved for system use and shouldn't be used for data node494* names: eval, toSource, toString, unwatch, valueOf, watch. Behavior is495* undefined if these names are used.496*497* @param {Function} compareFn Comparison function by which the498* node list is sorted. Should take 2 arguments to compare, and return a499* negative integer, zero, or a positive integer depending on whether the500* first argument is less than, equal to, or greater than the second.501* @param {Array<goog.ds.DataNode>=} opt_nodes optional nodes to add to list;502* these are assumed to be in sorted order.503* @extends {goog.ds.BasicNodeList}504* @constructor505*/506goog.ds.SortedNodeList = function(compareFn, opt_nodes) {507this.compareFn_ = compareFn;508goog.ds.BasicNodeList.call(this, opt_nodes);509};510goog.inherits(goog.ds.SortedNodeList, goog.ds.BasicNodeList);511512513/**514* Add a node to the node list, maintaining sort order.515* If the node has a dataName, uses this for the key in the map.516*517* @param {goog.ds.DataNode} node The node to add.518* @override519*/520goog.ds.SortedNodeList.prototype.add = function(node) {521if (!this.compareFn_) {522this.append(node);523return;524}525526var searchLoc = goog.array.binarySearch(this.list_, node, this.compareFn_);527528// if there is another node that is "equal" according to the comparison529// function, insert before that one; otherwise insert at the location530// goog.array.binarySearch indicated531if (searchLoc < 0) {532searchLoc = -(searchLoc + 1);533}534535// update any indexes that are after the insertion point536for (var index in this.indexMap_) {537if (this.indexMap_[index] >= searchLoc) {538this.indexMap_[index]++;539}540}541542goog.array.insertAt(this.list_, node, searchLoc);543var dataName = node.getDataName();544if (dataName) {545this.map_[dataName] = node;546this.indexMap_[dataName] = searchLoc;547}548};549550551/**552* Adds the given node to the end of the SortedNodeList. This should553* only be used when the caller can guarantee that the sort order will554* be maintained according to this SortedNodeList's compareFn (e.g.555* when initializing a new SortedNodeList from a list of nodes that has556* already been sorted).557* @param {goog.ds.DataNode} node The node to append.558*/559goog.ds.SortedNodeList.prototype.append = function(node) {560goog.ds.SortedNodeList.superClass_.add.call(this, node);561};562563564/**565* Sets a node in the list of a given name, maintaining sort order.566* @param {string} name Name of the node.567* @param {goog.ds.DataNode} node The node.568* @override569*/570goog.ds.SortedNodeList.prototype.setNode = function(name, node) {571if (node == null) {572this.removeNode(name);573} else {574var existingNode = this.indexMap_[name];575if (existingNode != null) {576if (this.compareFn_) {577var compareResult = this.compareFn_(this.list_[existingNode], node);578if (compareResult == 0) {579// the new node can just replace the old one580this.map_[name] = node;581this.list_[existingNode] = node;582} else {583// remove the old node, then add the new one584this.removeNode(name);585this.add(node);586}587}588} else {589this.add(node);590}591}592};593594595/**596* The character denoting an attribute.597* @type {string}598*/599goog.ds.STR_ATTRIBUTE_START = '@';600601602/**603* The character denoting all children.604* @type {string}605*/606goog.ds.STR_ALL_CHILDREN_SELECTOR = '*';607608609/**610* The wildcard character.611* @type {string}612*/613goog.ds.STR_WILDCARD = '*';614615616/**617* The character denoting path separation.618* @type {string}619*/620goog.ds.STR_PATH_SEPARATOR = '/';621622623/**624* The character denoting the start of an array.625* @type {string}626*/627goog.ds.STR_ARRAY_START = '[';628629630/**631* Shared logger instance for data package632* @type {goog.log.Logger}633*/634goog.ds.logger = goog.log.getLogger('goog.ds');635636637/**638* Create a data node that references another data node,639* useful for pointer-like functionality.640* All functions will return same values as the original node except for641* getDataName()642* @param {!goog.ds.DataNode} node The original node.643* @param {string} name The new name.644* @return {!goog.ds.DataNode} The new data node.645*/646goog.ds.Util.makeReferenceNode = function(node, name) {647/**648* @constructor649* @extends {goog.ds.DataNode}650* @final651*/652var nodeCreator = function() {};653nodeCreator.prototype = node;654var newNode = new nodeCreator();655newNode.getDataName = function() { return name; };656return newNode;657};658659660