/// <summary> /// Assigns every node in the constraint graph a score that corresponds to its absolute position /// in the desired execution order. Score collisions are possible but unlikely. /// </summary> /// <param name="graph"></param> /// <returns></returns> private static Dictionary <Type, int> ScoreGraphNodes(Dictionary <Type, List <OrderConstraint> > graph, IEnumerable <Type> types) { Dictionary <Type, int> graphScores = new Dictionary <Type, int>(graph.Count); // Initialize graph scores for every node. Has to be done explicitly, so we // have proper scores for types that are unconstrained / not part of the graph. int baseScore = 0; foreach (Type type in types) { graphScores.Add(type, baseScore++); } Stack <Type> visitSchedule = new Stack <Type>(); // For every node, traverse all other reachable nodes and propagate a score // value through them that increases with the number of visited nodes in // the chain. foreach (Type startNode in graph.Keys) { visitSchedule.Clear(); visitSchedule.Push(startNode); while (visitSchedule.Count > 0) { Type node = visitSchedule.Pop(); List <OrderConstraint> links; if (!graph.TryGetValue(node, out links)) { continue; } int nodeScore = graphScores[node]; // Push the current node's score to its neighbour nodes and schedule // them for traversal. for (int i = 0; i < links.Count; i++) { OrderConstraint link = links[i]; Type nextNode = link.LastType; int nextNodeScore = graphScores[nextNode]; // Determine the minimum score the neighbour node should have int minNextNodeScore = nodeScore + 1; // Propagate scores when out of order if (nextNodeScore < minNextNodeScore) { graphScores[nextNode] = minNextNodeScore; visitSchedule.Push(nextNode); } } } } return(graphScores); }
public override bool Equals(object obj) { if (!(obj is OrderConstraint)) { return(false); } OrderConstraint other = (OrderConstraint)obj; return (other.FirstType == this.FirstType && other.LastType == this.LastType && other.Priority == this.Priority); }
/// <summary> /// Clears the specified constraint graph of all loops. /// </summary> /// <param name="graph"></param> private static void ResolveConstraintLoops(Dictionary <Type, List <OrderConstraint> > graph) { while (true) { List <OrderConstraint> loop = FindConstraintLoop(graph); if (loop == null) { return; } // Found a loop? Find the weakest link in it OrderConstraint weakestLink = loop[0]; for (int i = 1; i < loop.Count; i++) { OrderConstraint link = loop[i]; if ((int)link.Priority < (int)weakestLink.Priority) { weakestLink = link; } } // If the loops weakest link was an explicit constraint, log a warning if ((int)weakestLink.Priority >= (int)ConstraintPriority.ExplicitWeak) { Log.Core.WriteWarning( "Found a loop in the component execution order constraint graph. Ignoring the weakest constraint " + "({0} must be executed before {1}). Please check your ExecutionOrder attributes.", Log.Type(weakestLink.FirstType), Log.Type(weakestLink.LastType)); } // Remove the weakest link List <OrderConstraint> links = graph[weakestLink.FirstType]; links.Remove(weakestLink); if (links.Count == 0) { graph.Remove(weakestLink.FirstType); } } }
/// <summary> /// Searches for constraint loops in the specified constraint graph and returns the first one. /// The result is not necessarily the smallest loop. Returns null if no loop was found. /// </summary> /// <param name="graph"></param> /// <returns></returns> private static List <OrderConstraint> FindConstraintLoop(Dictionary <Type, List <OrderConstraint> > graph) { if (graph.Count == 0) { return(null); } // Note that in our specific case of a constraint graph, all valid graphs share // the property that there is a maximum of one connection between each two nodes, // and there are no loops within the graph, so it forms a propert tree. // // Thus, we can traverse a valid constraint graph in its entirety and never // encounter the same node twice. HashSet <Type> visitedNodes = new HashSet <Type>(); Stack <Type> visitSchedule = new Stack <Type>(); Dictionary <Type, OrderConstraint> prevLinks = new Dictionary <Type, OrderConstraint>(); foreach (Type startNode in graph.Keys) { visitedNodes.Clear(); visitSchedule.Clear(); prevLinks.Clear(); // Do a breadth-first graph traversal to see if we'll end up at the start visitSchedule.Push(startNode); while (visitSchedule.Count > 0) { Type node = visitSchedule.Pop(); // Already visited this node in a previous run? Can't be part of a loop then. if (!visitedNodes.Add(node)) { continue; } List <OrderConstraint> links; if (!graph.TryGetValue(node, out links)) { continue; } for (int i = 0; i < links.Count; i++) { OrderConstraint link = links[i]; Type nextNode = link.LastType; // Found the starting node again through traversal? Found a loop then! if (nextNode == startNode) { List <OrderConstraint> loopPath = new List <OrderConstraint>(); while (true) { loopPath.Add(link); if (!prevLinks.TryGetValue(link.FirstType, out link)) { break; } } return(loopPath); } prevLinks[nextNode] = link; visitSchedule.Push(nextNode); } } } return(null); }