185 lines
6.1 KiB
C#
185 lines
6.1 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
|
|
namespace Umbraco.Core
|
|
{
|
|
/// <summary>
|
|
/// Topological Sort algorithm for sorting items based on dependencies.
|
|
/// Use the static method TopologicalSorter.GetSortedItems for a convenient
|
|
/// way of sorting a list of items with dependencies between them.
|
|
/// </summary>
|
|
public class TopologicalSorter
|
|
{
|
|
private readonly int[] _vertices; // list of vertices
|
|
private readonly int[,] _matrix; // adjacency matrix
|
|
private int _numVerts; // current number of vertices
|
|
private readonly int[] _sortedArray;
|
|
|
|
public TopologicalSorter(int size)
|
|
{
|
|
_vertices = new int[size];
|
|
_matrix = new int[size, size];
|
|
_numVerts = 0;
|
|
for (int i = 0; i < size; i++)
|
|
for (int j = 0; j < size; j++)
|
|
_matrix[i, j] = 0;
|
|
_sortedArray = new int[size]; // sorted vert labels
|
|
}
|
|
|
|
#region Public Methods
|
|
|
|
public int AddVertex(int vertex)
|
|
{
|
|
_vertices[_numVerts++] = vertex;
|
|
return _numVerts - 1;
|
|
}
|
|
|
|
public void AddEdge(int start, int end)
|
|
{
|
|
_matrix[start, end] = 1;
|
|
}
|
|
|
|
public int[] Sort() // toplogical sort
|
|
{
|
|
while (_numVerts > 0) // while vertices remain,
|
|
{
|
|
// get a vertex with no successors, or -1
|
|
int currentVertex = NoSuccessors();
|
|
if (currentVertex == -1) // must be a cycle
|
|
throw new Exception("Graph has cycles");
|
|
|
|
// insert vertex label in sorted array (start at end)
|
|
_sortedArray[_numVerts - 1] = _vertices[currentVertex];
|
|
|
|
DeleteVertex(currentVertex); // delete vertex
|
|
}
|
|
|
|
// vertices all gone; return sortedArray
|
|
return _sortedArray;
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Private Helper Methods
|
|
|
|
// returns vert with no successors (or -1 if no such verts)
|
|
private int NoSuccessors()
|
|
{
|
|
for (int row = 0; row < _numVerts; row++)
|
|
{
|
|
bool isEdge = false; // edge from row to column in adjMat
|
|
for (int col = 0; col < _numVerts; col++)
|
|
{
|
|
if (_matrix[row, col] > 0) // if edge to another,
|
|
{
|
|
isEdge = true;
|
|
break; // this vertex has a successor try another
|
|
}
|
|
}
|
|
if (!isEdge) // if no edges, has no successors
|
|
return row;
|
|
}
|
|
return -1; // no
|
|
}
|
|
|
|
private void DeleteVertex(int delVert)
|
|
{
|
|
// if not last vertex, delete from vertexList
|
|
if (delVert != _numVerts - 1)
|
|
{
|
|
for (int j = delVert; j < _numVerts - 1; j++)
|
|
_vertices[j] = _vertices[j + 1];
|
|
|
|
for (int row = delVert; row < _numVerts - 1; row++)
|
|
MoveRowUp(row, _numVerts);
|
|
|
|
for (int col = delVert; col < _numVerts - 1; col++)
|
|
MoveColLeft(col, _numVerts - 1);
|
|
}
|
|
_numVerts--; // one less vertex
|
|
}
|
|
|
|
private void MoveRowUp(int row, int length)
|
|
{
|
|
for (int col = 0; col < length; col++)
|
|
_matrix[row, col] = _matrix[row + 1, col];
|
|
}
|
|
|
|
private void MoveColLeft(int col, int length)
|
|
{
|
|
for (int row = 0; row < length; row++)
|
|
_matrix[row, col] = _matrix[row, col + 1];
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Static methods
|
|
|
|
public static IEnumerable<T> GetSortedItems<T>(List<DependencyField<T>> fields) where T : class
|
|
{
|
|
int[] sortOrder = GetTopologicalSortOrder(fields);
|
|
var list = new List<T>();
|
|
for (int i = 0; i < sortOrder.Length; i++)
|
|
{
|
|
var field = fields[sortOrder[i]];
|
|
list.Add(field.Item.Value);
|
|
}
|
|
list.Reverse();
|
|
return list;
|
|
}
|
|
|
|
internal static int[] GetTopologicalSortOrder<T>(List<DependencyField<T>> fields) where T : class
|
|
{
|
|
var g = new TopologicalSorter(fields.Count());
|
|
var indexes = new Dictionary<string, int>();
|
|
|
|
//add vertices
|
|
for (int i = 0; i < fields.Count(); i++)
|
|
{
|
|
indexes[fields[i].Alias.ToLowerInvariant()] = g.AddVertex(i);
|
|
}
|
|
|
|
//add edges
|
|
for (int i = 0; i < fields.Count; i++)
|
|
{
|
|
if (fields[i].DependsOn != null)
|
|
{
|
|
for (int j = 0; j < fields[i].DependsOn.Length; j++)
|
|
{
|
|
if (indexes.ContainsKey(fields[i].DependsOn[j].ToLowerInvariant()) == false)
|
|
throw new IndexOutOfRangeException(
|
|
string.Format(
|
|
"The alias '{0}' has an invalid dependency. The dependency '{1}' does not exist in the list of aliases",
|
|
fields[i], fields[i].DependsOn[j]));
|
|
|
|
g.AddEdge(i, indexes[fields[i].DependsOn[j].ToLowerInvariant()]);
|
|
}
|
|
}
|
|
}
|
|
|
|
int[] result = g.Sort();
|
|
return result;
|
|
}
|
|
|
|
#endregion
|
|
|
|
public class DependencyField<T> where T : class
|
|
{
|
|
public DependencyField()
|
|
{
|
|
}
|
|
|
|
public DependencyField(string @alias, string[] dependsOn, Lazy<T> item)
|
|
{
|
|
Alias = alias;
|
|
DependsOn = dependsOn;
|
|
Item = item;
|
|
}
|
|
|
|
public string Alias { get; set; }
|
|
public string[] DependsOn { get; set; }
|
|
public Lazy<T> Item { get; set; }
|
|
}
|
|
}
|
|
} |