[vcs-log-graph] introduce DfsWalk class and use it instead of ReachableNodes

This commit is contained in:
Julia Beliaeva
2018-11-09 20:42:56 +03:00
parent 788bd894c9
commit 9c4312ab8c
4 changed files with 63 additions and 49 deletions
@@ -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);
@@ -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<Int>): Set<Int> {
val result = HashSet<Int>()
@@ -45,27 +47,9 @@ class ReachableNodes(private val graph: LiteLinearGraph) {
}
fun walk(startNodes: Collection<Int>, 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
}
@@ -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<Int>, private val graph: LiteLinearGraph, private val visited: Flags) {
private val stack = IntStack()
constructor(startNodes: Collection<Int>, 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()
}
@@ -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<Int>()
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