/** * DependencyGraph is a basic data structure that helps decide the evaluation * order of a certain node. * * @module modules/base/dependency_graph */ define(function(require) { 'use strict'; var Module = require('modules/base/module'); /** * @class DependencyGraph * @requires module:modules/base/module * @params {DependencyGraph} dpGraph * The newly created graph will be initialized using * dpGraph. * @returns {DependencyGraph} */ var DependencyGraph = Module.create(function DependencyGraph(dpGraph) { this._nodes = dpGraph ? JSON.parse(JSON.stringify(dpGraph._nodes)) : {}; }); /** * Print the content of the nodes for debugging. * * @access private * @memberOf DependencyGraph.prototype */ DependencyGraph.prototype._printNodes = function dg_printNodes() { Object.keys(this._nodes).forEach((name) => { var node = this._nodes[name]; console.log('name: ' + name); console.log('children: ' + node.children.reduce((result, name) => { return result + ' ' + name; }, '')); console.log('dependent nodes: ' + node.dependentNodes.reduce((result, name) => { return result + ' ' + name; }, '')); console.log('======================'); }); }; /** * Traverse the tree and add the dependent nodes to all nodes in the path. * * @access private * @memberOf DependencyGraph.prototype * @param {Object} node * The node that we have new dependent nodes to add to. * @param {Array} dependentNodes * New dependent nodes to add. */ DependencyGraph.prototype._addDependentNodes = function dg_addDependentNodes(node, dependentNodes, traversedNodes) { traversedNodes = traversedNodes || []; if (traversedNodes.indexOf(node.name) >= 0) { traversedNodes.push(node.name); var error = traversedNodes.join(' -> '); this.throw('circular dependency detected! ' + error); } if (dependentNodes) { // add all nodes in "dependendNodes" to the current node dependentNodes.forEach((name) => { if (node.dependentNodes.indexOf(name) < 0) { node.dependentNodes.push(name); } }); } else { // Nothing to add, prepare the nodes to be added to the children. dependentNodes = Array.prototype.slice.call(node.dependentNodes); } dependentNodes.push(node.name); traversedNodes.push(node.name); node.children.forEach((name) => { this._addDependentNodes(this._nodes[name], dependentNodes, traversedNodes); }); dependentNodes.pop(); traversedNodes.pop(); }; /** * Add dependency. Calling to this function adds a dependency from the first * parameter towards the second one. * * @access public * @memberOf DependencyGraph.prototype * @param {String} name1 * The name that depends on the other. * @param {String} name2 * The name that is being depended on. */ DependencyGraph.prototype.addDependency = function dg_addDep(name1, name2) { var node1 = this._nodes[name1]; var node2 = this._nodes[name2]; if (!node1) { node1 = this._nodes[name1] = { name: name1, children: [], dependentNodes: [] }; } if (!node2) { node2 = this._nodes[name2] = { name: name2, children: [], dependentNodes: [] }; } if (node1.children.indexOf(name2) < 0) { node1.children.push(name2); this._addDependentNodes(node1); } }; /** * Get all dependent names of a name. * * @access public * @memberOf DependencyGraph.prototype * @param {String} name */ DependencyGraph.prototype.getAllDependent = function dg_getAllDep(name) { var node = this._nodes[name]; return node && node.dependentNodes; }; return DependencyGraph; });