private static void WalkTreeCheckCycleAndNearestWins(CyclesAndDowngrades context, GraphNode <RemoteResolveResult> node) { // Cycle: // // A -> B -> A (cycle) // // Downgrade: // // A -> B -> C -> D 2.0 (downgrade) // -> D 1.0 // // Potential downgrades that turns out to not be downgrades: // // 1. This should never happen in practice since B would have never been valid to begin with. // // A -> B -> C -> D 2.0 // -> D 1.0 // -> D 2.0 // // 2. This occurs if none of the sources have version C 1.0 so C 1.0 is bumped up to C 2.0. // // A -> B -> C 2.0 // -> C 1.0 var cycles = context.Cycles; var workingDowngrades = context.Downgrades; if (node.Disposition == Disposition.Cycle) { cycles.Add(node); // Remove this node from the tree so the nothing else evaluates this. // This is ok since we have a parent pointer and we can still print the path node.OuterNode.InnerNodes.Remove(node); return; } if (node.Disposition != Disposition.PotentiallyDowngraded) { return; } // REVIEW: This could probably be done in a single pass where we keep track // of what is nearer as we walk down the graph (BFS) for (var n = node.OuterNode; n != null; n = n.OuterNode) { var innerNodes = n.InnerNodes; var count = innerNodes.Count; for (var i = 0; i < count; i++) { var sideNode = innerNodes[i]; if (sideNode != node && StringComparer.OrdinalIgnoreCase.Equals(sideNode.Key.Name, node.Key.Name)) { // Nodes that have no version range should be ignored as potential downgrades e.g. framework reference if (sideNode.Key.VersionRange != null && node.Key.VersionRange != null && !RemoteDependencyWalker.IsGreaterThanOrEqualTo(sideNode.Key.VersionRange, node.Key.VersionRange)) { // Is the resolved version actually within node's version range? This happen if there // was a different request for a lower version of the library than this version range // allows but no matching library was found, so the library is bumped up into this // version range. var resolvedVersion = sideNode?.Item?.Data?.Match?.Library?.Version; if (resolvedVersion != null && node.Key.VersionRange.Satisfies(resolvedVersion)) { continue; } workingDowngrades[node] = sideNode; } else { workingDowngrades.Remove(node); } } } } // Remove this node from the tree so the nothing else evaluates this. // This is ok since we have a parent pointer and we can still print the path node.OuterNode.InnerNodes.Remove(node); }
private static void CheckCycleAndNearestWins <TItem>(this GraphNode <TItem> root, List <DowngradeResult <TItem> > downgrades, List <GraphNode <TItem> > cycles) { // Cycle // A -> B -> A (cycle) // Downgrade // A -> B -> C -> D 2.0 (downgrage) // -> D 1.0 // Potential downgrade that turns out to not downgrade // This should never happen in practice since B would have never been valid to begin with. // A -> B -> C -> D 2.0 // -> D 1.0 // -> D 2.0 var workingDowngrades = new Dictionary <GraphNode <TItem>, GraphNode <TItem> >(); root.ForEach(node => { if (node.Disposition == Disposition.Cycle) { cycles.Add(node); // Remove this node from the tree so the nothing else evaluates this. // This is ok since we have a parent pointer and we can still print the path node.OuterNode.InnerNodes.Remove(node); return; } if (node.Disposition != Disposition.PotentiallyDowngraded) { return; } // REVIEW: This could probably be done in a single pass where we keep track // of what is nearer as we walk down the graph (BFS) for (var n = node.OuterNode; n != null; n = n.OuterNode) { foreach (var sideNode in n.InnerNodes) { if (sideNode != node && StringComparer.OrdinalIgnoreCase.Equals(sideNode.Key.Name, node.Key.Name)) { // Nodes that have no version range should be ignored as potential downgrades e.g. framework reference if (sideNode.Key.VersionRange != null && node.Key.VersionRange != null && !RemoteDependencyWalker.IsGreaterThanOrEqualTo(sideNode.Key.VersionRange, node.Key.VersionRange)) { workingDowngrades[node] = sideNode; } else { workingDowngrades.Remove(node); } } } } // Remove this node from the tree so the nothing else evaluates this. // This is ok since we have a parent pointer and we can still print the path node.OuterNode.InnerNodes.Remove(node); }); downgrades.AddRange(workingDowngrades.Select(p => new DowngradeResult <TItem> { DowngradedFrom = p.Key, DowngradedTo = p.Value })); }