From 9c4312ab8c741cd8038dc4ce90df24e15169c22f Mon Sep 17 00:00:00 2001 From: Julia Beliaeva Date: Fri, 9 Nov 2018 20:12:10 +0300 Subject: [PATCH] [vcs-log-graph] introduce DfsWalk class and use it instead of ReachableNodes --- .../graph/impl/facade/FilteredController.java | 7 +- .../log/graph/impl/facade/ReachableNodes.kt | 33 +++------- .../intellij/vcs/log/graph/utils/DfsUtil.kt | 66 ++++++++++++++----- .../vcs/log/history/ReachableNodesUtil.kt | 6 +- 4 files changed, 63 insertions(+), 49 deletions(-) diff --git a/platform/vcs-log/graph/src/com/intellij/vcs/log/graph/impl/facade/FilteredController.java b/platform/vcs-log/graph/src/com/intellij/vcs/log/graph/impl/facade/FilteredController.java index b1c452249274..57bf1d0913d3 100644 --- a/platform/vcs-log/graph/src/com/intellij/vcs/log/graph/impl/facade/FilteredController.java +++ b/platform/vcs-log/graph/src/com/intellij/vcs/log/graph/impl/facade/FilteredController.java @@ -20,6 +20,7 @@ import com.intellij.vcs.log.graph.api.elements.GraphElement; import com.intellij.vcs.log.graph.api.permanent.PermanentGraphInfo; import com.intellij.vcs.log.graph.collapsing.CollapsedGraph; import com.intellij.vcs.log.graph.collapsing.DottedFilterEdgesGenerator; +import com.intellij.vcs.log.graph.utils.DfsWalk; import com.intellij.vcs.log.graph.utils.LinearGraphUtils; import com.intellij.vcs.log.graph.utils.UnsignedBitSet; import org.jetbrains.annotations.NotNull; @@ -44,13 +45,13 @@ public class FilteredController extends CascadeController { UnsignedBitSet initVisibility = new UnsignedBitSet(); if (visibleHeadsIds != null) { - ReachableNodes getter = new ReachableNodes(LinearGraphUtils.asLiteLinearGraph(myPermanentGraphInfo.getLinearGraph())); - getter.walkDown(visibleHeadsIds, node -> { + new DfsWalk(visibleHeadsIds, myPermanentGraphInfo.getLinearGraph()).walk(true, node -> { if (matchedIds.contains(node)) initVisibility.set(node, true); + return true; }); } else { - for (Integer matchedId: matchedIds) initVisibility.set(matchedId, true); + for (Integer matchedId : matchedIds) initVisibility.set(matchedId, true); } myCollapsedGraph = CollapsedGraph.newInstance(delegateLinearGraphController.getCompiledGraph(), initVisibility); diff --git a/platform/vcs-log/graph/src/com/intellij/vcs/log/graph/impl/facade/ReachableNodes.kt b/platform/vcs-log/graph/src/com/intellij/vcs/log/graph/impl/facade/ReachableNodes.kt index 32e623edf1c1..6190cad58591 100644 --- a/platform/vcs-log/graph/src/com/intellij/vcs/log/graph/impl/facade/ReachableNodes.kt +++ b/platform/vcs-log/graph/src/com/intellij/vcs/log/graph/impl/facade/ReachableNodes.kt @@ -19,12 +19,14 @@ package com.intellij.vcs.log.graph.impl.facade import com.intellij.util.Consumer import com.intellij.vcs.log.graph.api.LinearGraph import com.intellij.vcs.log.graph.api.LiteLinearGraph -import com.intellij.vcs.log.graph.utils.* +import com.intellij.vcs.log.graph.utils.DfsWalk +import com.intellij.vcs.log.graph.utils.Flags +import com.intellij.vcs.log.graph.utils.UnsignedBitSet import com.intellij.vcs.log.graph.utils.impl.BitSetFlags import java.util.* class ReachableNodes(private val graph: LiteLinearGraph) { - private val flags: Flags = BitSetFlags(graph.nodesCount()) + private val visited: Flags = BitSetFlags(graph.nodesCount()) fun getContainingBranches(nodeIndex: Int, branchNodeIndexes: Collection): Set { val result = HashSet() @@ -45,27 +47,9 @@ class ReachableNodes(private val graph: LiteLinearGraph) { } fun walk(startNodes: Collection, goDown: Boolean, consumer: (Int) -> Boolean) { - synchronized(flags) { - - flags.setAll(false) - for (start in startNodes) { - if (start < 0) continue - if (flags.get(start)) continue - flags.set(start, true) - if (!consumer(start)) return - - walk(start) nextNode@{ currentNode -> - for (downNode in graph.getNodes(currentNode, if (goDown) LiteLinearGraph.NodeFilter.DOWN else LiteLinearGraph.NodeFilter.UP)) { - if (!flags.get(downNode)) { - flags.set(downNode, true) - if (!consumer(downNode)) return@nextNode Dfs.NextNode.EXIT - return@nextNode downNode - } - } - - Dfs.NextNode.NODE_NOT_FOUND - } - } + synchronized(visited) { + visited.setAll(false) + DfsWalk(startNodes, graph, visited).walk(goDown, consumer) } } @@ -79,8 +63,7 @@ class ReachableNodes(private val graph: LiteLinearGraph) { } val result = UnsignedBitSet() - val reachableNodes = ReachableNodes(LinearGraphUtils.asLiteLinearGraph(graph)) - reachableNodes.walk(headNodeIndexes, true) { node: Int -> + DfsWalk(headNodeIndexes, graph).walk(true) { node: Int -> result.set(node, true) true } diff --git a/platform/vcs-log/graph/src/com/intellij/vcs/log/graph/utils/DfsUtil.kt b/platform/vcs-log/graph/src/com/intellij/vcs/log/graph/utils/DfsUtil.kt index ec7525bda59a..75d25af10436 100644 --- a/platform/vcs-log/graph/src/com/intellij/vcs/log/graph/utils/DfsUtil.kt +++ b/platform/vcs-log/graph/src/com/intellij/vcs/log/graph/utils/DfsUtil.kt @@ -18,6 +18,7 @@ package com.intellij.vcs.log.graph.utils import com.intellij.openapi.util.Ref import com.intellij.util.containers.IntStack +import com.intellij.vcs.log.graph.api.LinearGraph import com.intellij.vcs.log.graph.api.LiteLinearGraph import com.intellij.vcs.log.graph.utils.impl.BitSetFlags @@ -33,6 +34,54 @@ object Dfs { } } +private fun walk(start: Int, stack: IntStack, nextNodeFun: (Int) -> Int) { + stack.push(start) + + while (!stack.empty()) { + val nextNode = nextNodeFun(stack.peek()) + if (nextNode == Dfs.NextNode.EXIT) return + if (nextNode != Dfs.NextNode.NODE_NOT_FOUND) { + stack.push(nextNode) + } + else { + stack.pop() + } + } + stack.clear() +} + +fun walk(start: Int, nextNodeFun: (Int) -> Int) { + walk(start, IntStack(), nextNodeFun) +} + +class DfsWalk(private val startNodes: Collection, private val graph: LiteLinearGraph, private val visited: Flags) { + private val stack = IntStack() + + constructor(startNodes: Collection, linearGraph: LinearGraph) : + this(startNodes, LinearGraphUtils.asLiteLinearGraph(linearGraph), BitSetFlags(linearGraph.nodesCount())) + + fun walk(goDown: Boolean, consumer: (Int) -> Boolean) { + for (start in startNodes) { + if (start < 0) continue + if (visited.get(start)) continue + visited.set(start, true) + if (!consumer(start)) return + + walk(start, stack) nextNode@{ currentNode -> + for (downNode in graph.getNodes(currentNode, if (goDown) LiteLinearGraph.NodeFilter.DOWN else LiteLinearGraph.NodeFilter.UP)) { + if (!visited.get(downNode)) { + visited.set(downNode, true) + if (!consumer(downNode)) return@nextNode Dfs.NextNode.EXIT + return@nextNode downNode + } + } + + Dfs.NextNode.NODE_NOT_FOUND + } + } + } +} + /* * Depth-first walk for a graph. For each node, walks both into upward and downward siblings. * Tries to preserve direction of travel: when a node is entered from up-sibling, goes to the down-siblings first. @@ -113,20 +162,3 @@ fun LiteLinearGraph.isAncestor(lowerNode: Int, upperNode: Int): Boolean { return result.get() } - -fun walk(startRowIndex: Int, nextNodeFun: (Int) -> Int) { - val stack = IntStack() - stack.push(startRowIndex) - - while (!stack.empty()) { - val nextNode = nextNodeFun(stack.peek()) - if (nextNode == Dfs.NextNode.EXIT) return - if (nextNode != Dfs.NextNode.NODE_NOT_FOUND) { - stack.push(nextNode) - } - else { - stack.pop() - } - } - stack.clear() -} diff --git a/platform/vcs-log/impl/src/com/intellij/vcs/log/history/ReachableNodesUtil.kt b/platform/vcs-log/impl/src/com/intellij/vcs/log/history/ReachableNodesUtil.kt index 41dde0bbd782..16d4105ad1d0 100644 --- a/platform/vcs-log/impl/src/com/intellij/vcs/log/history/ReachableNodesUtil.kt +++ b/platform/vcs-log/impl/src/com/intellij/vcs/log/history/ReachableNodesUtil.kt @@ -5,16 +5,14 @@ import com.intellij.openapi.util.Ref import com.intellij.vcs.log.data.DataPack import com.intellij.vcs.log.graph.api.LinearGraph import com.intellij.vcs.log.graph.api.permanent.PermanentGraphInfo -import com.intellij.vcs.log.graph.impl.facade.ReachableNodes import com.intellij.vcs.log.graph.impl.facade.VisibleGraphImpl -import com.intellij.vcs.log.graph.utils.LinearGraphUtils +import com.intellij.vcs.log.graph.utils.DfsWalk import com.intellij.vcs.log.visible.VisiblePack fun LinearGraph.findAncestorNode(startNodeId: Int, condition: (Int) -> Boolean): Int? { val resultNodeId = Ref() - val reachableNodes = ReachableNodes(LinearGraphUtils.asLiteLinearGraph(this)) - reachableNodes.walk(setOf(startNodeId), true) { currentNodeId -> + DfsWalk(setOf(startNodeId), this).walk(true) { currentNodeId: Int -> if (condition(currentNodeId)) { resultNodeId.set(currentNodeId) false // stop walk, we have found it