Skip to content

Commit

Permalink
Merge pull request hapijs#660 from thegoleffect/feature/ext
Browse files Browse the repository at this point in the history
unoptimized plugin dependency sorting algo
  • Loading branch information
Eran Hammer committed Mar 12, 2013
2 parents d38c2e2 + 3ea9db6 commit 227d2b6
Showing 1 changed file with 158 additions and 1 deletion.
159 changes: 158 additions & 1 deletion lib/ext.js
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ internals.Ext.prototype.sort = function (event) {

// Sort

var sorted = input;
var sorted = internals.depsort(input);

// Apply

Expand Down Expand Up @@ -137,3 +137,160 @@ internals.Ext.runProtected = function (log, tags, callback, setup) {
},
finish);
};


internals.getGraph = function (plugins) {

var groups = {};
var graph = {};
var graphAfters = {};
var result = [];

for(var i = 0, il = plugins.length; i < il; ++i) {
var plugin = plugins[i];
var index = plugin.priority;
var group = plugin.group;

// Determine Groups
if (groups.hasOwnProperty(group)) {
if (groups[group].indexOf(index) == -1) {
groups[group].push(index);
}
}
else {
groups[group] = [index];
}

var hasBefore = plugin.hasOwnProperty('before');
var hasAfter = plugin.hasOwnProperty('after');

// Build intermediary graph using 'before'
if (hasBefore) {
var before = plugin.before;
if (!(before instanceof Array)) {
before = [before];
}

graph[index] = (graph[index] || []).concat(before);
}

// Build second intermediary graph with "after"
if (hasAfter) {
var after = plugin.after;
if (!(after instanceof Array)) {
after = [after];
}

for(var j = 0, jl = after.length; j < jl; ++j) {
graphAfters[after[j]] = (graphAfters[after[j]] || []).concat(index);
}
}
}

// Expand intermediary graph
var nodes = Object.keys(graph);
for(var m in nodes) {
var node = nodes[m];
var expandedGroups = [];

for(var groupIndex in graph[node]) {
var group = graph[node][groupIndex];
groups[group].forEach(function(d){
expandedGroups.push(d);
})
}
graph[node] = expandedGroups;
}

// Merge intermediary graph using graphAfters into final graph
var afterNodes = Object.keys(graphAfters);
for(var n in afterNodes) {
var group = afterNodes[n];

for(var itemIndex in groups[group]) {
var node = groups[group][itemIndex];
graph[node] = (graph[node] || []).concat(graphAfters[group])
}
}

return graph;
};


internals.getAncestors = function (graph) {

var ancestors = {};
var graphNodes = Object.keys(graph);
for(var i in graphNodes) {
var node = graphNodes[i];
var children = graph[node];

for(var j = 0, jl = children.length; j < jl; ++j) {
ancestors[children[j]] = (ancestors[children[j]] || []).concat(node);
}
}

return ancestors;
};

internals.toposort = function (ancestorsGraph, length) {

var visited = {};
var sorted = [];
length = length || ancestorsGraph.length;

var ancNodes = Object.keys(ancestorsGraph);

for(var i = 0, il = length; i < il; ++i) {
var next = i;

if (ancestorsGraph[i]) {
next = null;
for(var j = 0, jl = length; j < jl; ++j) {
if (visited[j] == true) {
continue;
}

if (!ancestorsGraph[j]) {
ancestorsGraph[j] = [];
}

var shouldSeeCount = ancestorsGraph[j].length;
var seenCount = 0;
for(var l = 0, ll = shouldSeeCount; l < ll; ++l) {
if (sorted.indexOf(ancestorsGraph[j][l]) >= 0) {
++seenCount;
}
}

if (seenCount == shouldSeeCount) {
next = j;
break;
}
}
}

if (next !== null) {
next = next.toString(); // normalize to string
visited[next] = true;
sorted.push(next);
}
}

if (sorted.length != length) {
throw "Invalid plugin input supplied"; // Occurs if before/after set to item's group (but not limited to this case)
}

return sorted;
};


internals.depsort = function (plugins) {

var graph = internals.getGraph(plugins);
var ancestors = internals.getAncestors(graph);
return internals.toposort(ancestors, plugins.length).map(function (value, index) {

return plugins[value];
});
};

0 comments on commit 227d2b6

Please sign in to comment.