/// <summary> /// Collects the roots of the set of graphs that make up the metric type dependencies. /// </summary> /// <param name="metricType">Type of <see cref="Metric"/> to process.</param> /// <param name="roots">List of collected root nodes.</param> /// <param name="collectedMetricTypes">Mapping of metric types and their nodes that have been collected so far.</param> /// <returns>The <see cref="MetricNode"/> associated with <paramref name="metricType"/>.</returns> private static MetricNode CollectMetricTypeGraphs(Type metricType, List <MetricNode> roots, Dictionary <Type, MetricNode> collectedMetricTypes) { if (collectedMetricTypes.TryGetValue(metricType, out MetricNode metricNode)) { // We've encountered a type that we've processed already. Ensure that this isn't // the result of a cycle in the graph. DetectMetricTypeDependencyCycle(metricNode, metricNode); return(metricNode); } List <Type> metricTypeDependencies = GetMetricTypeDependencies(metricType); if (!metricTypeDependencies.Any()) { MetricNode root = new MetricNode(metricType); roots.Add(root); metricNode = root; collectedMetricTypes.Add(metricType, metricNode); } else { MetricNode node = new MetricNode(metricType); collectedMetricTypes.Add(metricType, node); foreach (Type dependency in metricTypeDependencies) { MetricNode dependencyNode = CollectMetricTypeGraphs(dependency, roots, collectedMetricTypes); dependencyNode.Dependencies.Add(node); } metricNode = node; } return(metricNode); }
/// <summary> /// Sorts the metrics according to their dependencies. /// </summary> private void SortMetrics() { this.sortedMetrics = new List <Metric>(); List <MetricNode> roots = new List <MetricNode>(); Dictionary <Type, MetricNode> collectedMetricTypes = new Dictionary <Type, GenFx.GeneticAlgorithm.MetricNode>(); foreach (Type metricType in this.metrics.Select(m => m.GetType())) { CollectMetricTypeGraphs(metricType, roots, collectedMetricTypes); } // Iterate through the nodes in the graphs, breadth first, and add them to the list // of sorted metric. Since we're iterating them in this way, they are inherently sorted. Queue <MetricNode> nodesToIterate = new Queue <MetricNode>(roots); while (nodesToIterate.Any()) { MetricNode node = nodesToIterate.Dequeue(); Metric metric = this.metrics.First(m => m.GetType() == node.MetricType); this.sortedMetrics.Add(metric); foreach (MetricNode dependentNode in node.Dependencies) { nodesToIterate.Enqueue(dependentNode); } } }
/// <summary> /// Detects whether there's a cycle in the metric type dependency graph and throws an exception /// if there is. /// </summary> /// <param name="currentNode">The node to search dependencies of.</param> /// <param name="nodeToSearch">The node to search for a match of.</param> private static void DetectMetricTypeDependencyCycle(MetricNode currentNode, MetricNode nodeToSearch) { foreach (MetricNode dependentNode in currentNode.Dependencies) { if (dependentNode.MetricType == nodeToSearch.MetricType) { throw new InvalidOperationException( StringUtil.GetFormattedString(Resources.ErrorMsg_CycleInMetricDependencyGraph, nodeToSearch.MetricType)); } DetectMetricTypeDependencyCycle(dependentNode, nodeToSearch); } }