From c8089295e8edc2d902e2da2375703783e240d192 Mon Sep 17 00:00:00 2001 From: Julia Beliaeva Date: Thu, 8 Nov 2018 20:30:46 +0300 Subject: [PATCH 1/8] [vcs-log-graph] rename BfsUtil to kotlin --- .../com/intellij/vcs/log/graph/utils/{BfsUtil.java => BfsUtil.kt} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename platform/vcs-log/graph/src/com/intellij/vcs/log/graph/utils/{BfsUtil.java => BfsUtil.kt} (100%) diff --git a/platform/vcs-log/graph/src/com/intellij/vcs/log/graph/utils/BfsUtil.java b/platform/vcs-log/graph/src/com/intellij/vcs/log/graph/utils/BfsUtil.kt similarity index 100% rename from platform/vcs-log/graph/src/com/intellij/vcs/log/graph/utils/BfsUtil.java rename to platform/vcs-log/graph/src/com/intellij/vcs/log/graph/utils/BfsUtil.kt From d373f3563bca4e50d91f119c81e66b5ab4e367da Mon Sep 17 00:00:00 2001 From: Julia Beliaeva Date: Thu, 8 Nov 2018 20:32:14 +0300 Subject: [PATCH 2/8] [vcs-log-graph] convert BfsUtil to kotlin --- .../intellij/vcs/log/graph/utils/BfsUtil.kt | 64 +++++++++---------- 1 file changed, 30 insertions(+), 34 deletions(-) diff --git a/platform/vcs-log/graph/src/com/intellij/vcs/log/graph/utils/BfsUtil.kt b/platform/vcs-log/graph/src/com/intellij/vcs/log/graph/utils/BfsUtil.kt index 451378210d18..3f2041a2020a 100644 --- a/platform/vcs-log/graph/src/com/intellij/vcs/log/graph/utils/BfsUtil.kt +++ b/platform/vcs-log/graph/src/com/intellij/vcs/log/graph/utils/BfsUtil.kt @@ -13,59 +13,55 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.intellij.vcs.log.graph.utils; +package com.intellij.vcs.log.graph.utils -import com.intellij.util.containers.ContainerUtil; -import com.intellij.vcs.log.graph.api.LiteLinearGraph; -import org.jetbrains.annotations.NotNull; +import com.intellij.util.containers.ContainerUtil +import com.intellij.vcs.log.graph.api.LiteLinearGraph +import java.util.* -import java.util.ArrayList; -import java.util.List; -import java.util.Queue; +object BfsUtil { + fun getCorrespondingParent(graph: LiteLinearGraph, startNode: Int, endNode: Int, visited: Flags): Int { + val candidates = graph.getNodes(startNode, LiteLinearGraph.NodeFilter.DOWN) + if (candidates.size == 1) return candidates[0] + if (candidates.contains(endNode)) return endNode -public class BfsUtil { - public static int getCorrespondingParent(@NotNull LiteLinearGraph graph, int startNode, int endNode, @NotNull Flags visited) { - List candidates = graph.getNodes(startNode, LiteLinearGraph.NodeFilter.DOWN); - if (candidates.size() == 1) return candidates.get(0); - if (candidates.contains(endNode)) return endNode; - - List> queues = new ArrayList<>(candidates.size()); - for (int candidate : candidates) { - queues.add(ContainerUtil.newLinkedList(candidate)); + val queues = ArrayList>(candidates.size) + for (candidate in candidates) { + queues.add(ContainerUtil.newLinkedList(candidate)) } - int emptyCount; - visited.setAll(false); + var emptyCount: Int + visited.setAll(false) do { - emptyCount = 0; - for (Queue queue : queues) { + emptyCount = 0 + for (queue in queues) { if (queue.isEmpty()) { - emptyCount++; + emptyCount++ } else { - boolean found = runNextBfsStep(graph, queue, visited, endNode); + val found = runNextBfsStep(graph, queue, visited, endNode) if (found) { - return candidates.get(queues.indexOf(queue)); + return candidates[queues.indexOf(queue)] } } } } - while (emptyCount < queues.size()); + while (emptyCount < queues.size) - return candidates.get(0); + return candidates[0] } - private static boolean runNextBfsStep(@NotNull LiteLinearGraph graph, @NotNull Queue queue, @NotNull Flags visited, int target) { + private fun runNextBfsStep(graph: LiteLinearGraph, queue: Queue, visited: Flags, target: Int): Boolean { while (!queue.isEmpty()) { - Integer node = queue.poll(); - if (!visited.get(node)) { - visited.set(node, true); - List next = graph.getNodes(node, LiteLinearGraph.NodeFilter.DOWN); - if (next.contains(target)) return true; - queue.addAll(next); - return false; + val node = queue.poll() + if (!visited.get(node!!)) { + visited.set(node, true) + val next = graph.getNodes(node, LiteLinearGraph.NodeFilter.DOWN) + if (next.contains(target)) return true + queue.addAll(next) + return false } } - return false; + return false } } From 993e4d1a8eb2684b248816db786b47d0ddeed59d Mon Sep 17 00:00:00 2001 From: Julia Beliaeva Date: Thu, 8 Nov 2018 21:25:57 +0300 Subject: [PATCH 3/8] [vcs-log-graph] introduce BfsWalk class --- .../intellij/vcs/log/graph/utils/BfsUtil.kt | 49 ++++++++++--------- 1 file changed, 25 insertions(+), 24 deletions(-) diff --git a/platform/vcs-log/graph/src/com/intellij/vcs/log/graph/utils/BfsUtil.kt b/platform/vcs-log/graph/src/com/intellij/vcs/log/graph/utils/BfsUtil.kt index 3f2041a2020a..9d46884a5c11 100644 --- a/platform/vcs-log/graph/src/com/intellij/vcs/log/graph/utils/BfsUtil.kt +++ b/platform/vcs-log/graph/src/com/intellij/vcs/log/graph/utils/BfsUtil.kt @@ -17,7 +17,25 @@ package com.intellij.vcs.log.graph.utils import com.intellij.util.containers.ContainerUtil import com.intellij.vcs.log.graph.api.LiteLinearGraph -import java.util.* + +class BfsWalk(val start: Int, private val graph: LiteLinearGraph, private val visited: Flags) { + private val queue = ContainerUtil.newLinkedList(start) + + fun isFinished() = queue.isEmpty() + + fun step(): List { + while (!queue.isEmpty()) { + val node = queue.poll() + if (!visited.get(node)) { + visited.set(node, true) + val next = graph.getNodes(node, LiteLinearGraph.NodeFilter.DOWN) + queue.addAll(next) + return next + } + } + return emptyList() + } +} object BfsUtil { fun getCorrespondingParent(graph: LiteLinearGraph, startNode: Int, endNode: Int, visited: Flags): Int { @@ -25,43 +43,26 @@ object BfsUtil { if (candidates.size == 1) return candidates[0] if (candidates.contains(endNode)) return endNode - val queues = ArrayList>(candidates.size) - for (candidate in candidates) { - queues.add(ContainerUtil.newLinkedList(candidate)) - } + val bfsWalks = candidates.map { BfsWalk(it, graph, visited) } var emptyCount: Int visited.setAll(false) do { emptyCount = 0 - for (queue in queues) { - if (queue.isEmpty()) { + for (walk in bfsWalks) { + if (walk.isFinished()) { emptyCount++ } else { - val found = runNextBfsStep(graph, queue, visited, endNode) - if (found) { - return candidates[queues.indexOf(queue)] + if (walk.step().contains(endNode)) { + return walk.start } } } } - while (emptyCount < queues.size) + while (emptyCount < bfsWalks.size) return candidates[0] } - private fun runNextBfsStep(graph: LiteLinearGraph, queue: Queue, visited: Flags, target: Int): Boolean { - while (!queue.isEmpty()) { - val node = queue.poll() - if (!visited.get(node!!)) { - visited.set(node, true) - val next = graph.getNodes(node, LiteLinearGraph.NodeFilter.DOWN) - if (next.contains(target)) return true - queue.addAll(next) - return false - } - } - return false - } } From 347c8de080422f2510a0ca7d709eba9578247941 Mon Sep 17 00:00:00 2001 From: Julia Beliaeva Date: Thu, 8 Nov 2018 21:31:36 +0300 Subject: [PATCH 4/8] [vcs-log-graph] simplify --- .../com/intellij/vcs/log/graph/utils/BfsUtil.kt | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/platform/vcs-log/graph/src/com/intellij/vcs/log/graph/utils/BfsUtil.kt b/platform/vcs-log/graph/src/com/intellij/vcs/log/graph/utils/BfsUtil.kt index 9d46884a5c11..ee53ebef2b70 100644 --- a/platform/vcs-log/graph/src/com/intellij/vcs/log/graph/utils/BfsUtil.kt +++ b/platform/vcs-log/graph/src/com/intellij/vcs/log/graph/utils/BfsUtil.kt @@ -43,24 +43,18 @@ object BfsUtil { if (candidates.size == 1) return candidates[0] if (candidates.contains(endNode)) return endNode - val bfsWalks = candidates.map { BfsWalk(it, graph, visited) } + val bfsWalks = candidates.mapTo(mutableListOf()) { BfsWalk(it, graph, visited) } - var emptyCount: Int visited.setAll(false) do { - emptyCount = 0 for (walk in bfsWalks) { - if (walk.isFinished()) { - emptyCount++ - } - else { - if (walk.step().contains(endNode)) { - return walk.start - } + if (walk.step().contains(endNode)) { + return walk.start } } + bfsWalks.removeIf { it.isFinished() } } - while (emptyCount < bfsWalks.size) + while (bfsWalks.isNotEmpty()) return candidates[0] } From 788bd894c995bf295e5f5a378b8cb27126cb8cbb Mon Sep 17 00:00:00 2001 From: Julia Beliaeva Date: Fri, 9 Nov 2018 00:02:35 +0300 Subject: [PATCH 5/8] [vcs-log-graph] add tests --- .../intellij/vcs/log/graph/utils/BfsUtil.kt | 6 ++ .../vcs/log/graph/utils/impl/BitSetFlags.java | 20 ++++ .../intellij/vcs/log/graph/impl/BfsTests.kt | 94 +++++++++++++++++++ 3 files changed, 120 insertions(+) create mode 100644 platform/vcs-log/graph/test/com/intellij/vcs/log/graph/impl/BfsTests.kt diff --git a/platform/vcs-log/graph/src/com/intellij/vcs/log/graph/utils/BfsUtil.kt b/platform/vcs-log/graph/src/com/intellij/vcs/log/graph/utils/BfsUtil.kt index ee53ebef2b70..fbb014dded29 100644 --- a/platform/vcs-log/graph/src/com/intellij/vcs/log/graph/utils/BfsUtil.kt +++ b/platform/vcs-log/graph/src/com/intellij/vcs/log/graph/utils/BfsUtil.kt @@ -35,6 +35,12 @@ class BfsWalk(val start: Int, private val graph: LiteLinearGraph, private val vi } return emptyList() } + + fun walk() { + while (!isFinished()) { + step() + } + } } object BfsUtil { diff --git a/platform/vcs-log/graph/src/com/intellij/vcs/log/graph/utils/impl/BitSetFlags.java b/platform/vcs-log/graph/src/com/intellij/vcs/log/graph/utils/impl/BitSetFlags.java index 933a0dbeb4e7..8cb3dc8a645e 100644 --- a/platform/vcs-log/graph/src/com/intellij/vcs/log/graph/utils/impl/BitSetFlags.java +++ b/platform/vcs-log/graph/src/com/intellij/vcs/log/graph/utils/impl/BitSetFlags.java @@ -20,6 +20,7 @@ import com.intellij.vcs.log.graph.utils.Flags; import org.jetbrains.annotations.NotNull; import java.util.BitSet; +import java.util.Objects; public class BitSetFlags implements Flags { @@ -65,4 +66,23 @@ public class BitSetFlags implements Flags { if (index < 0) throw new IndexOutOfBoundsException("index is " + index + " which is less then zero"); if (index >= mySize) throw new IndexOutOfBoundsException("index is " + index + " and set size is " + mySize); } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + BitSetFlags flags = (BitSetFlags)o; + return mySize == flags.mySize && + myBitSet.equals(flags.myBitSet); + } + + @Override + public int hashCode() { + return Objects.hash(mySize, myBitSet); + } + + @Override + public String toString() { + return myBitSet + ", size = " + mySize; + } } diff --git a/platform/vcs-log/graph/test/com/intellij/vcs/log/graph/impl/BfsTests.kt b/platform/vcs-log/graph/test/com/intellij/vcs/log/graph/impl/BfsTests.kt new file mode 100644 index 000000000000..649b25adae0a --- /dev/null +++ b/platform/vcs-log/graph/test/com/intellij/vcs/log/graph/impl/BfsTests.kt @@ -0,0 +1,94 @@ +// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. +package com.intellij.vcs.log.graph.impl + +import com.intellij.vcs.log.graph.TestGraphBuilder +import com.intellij.vcs.log.graph.asString +import com.intellij.vcs.log.graph.graph +import com.intellij.vcs.log.graph.utils.BfsUtil +import com.intellij.vcs.log.graph.utils.BfsWalk +import com.intellij.vcs.log.graph.utils.LinearGraphUtils +import com.intellij.vcs.log.graph.utils.impl.BitSetFlags +import org.junit.Test +import kotlin.test.assertEquals + +class BfsTests { + + private fun assertCorrespondingParent(startNode: Int, endNode: Int, expectedParent: Int, graphBuilder: TestGraphBuilder.() -> Unit) { + val graph = graph(graphBuilder) + val actualParent = BfsUtil.getCorrespondingParent(LinearGraphUtils.asLiteLinearGraph(graph), startNode, endNode, + BitSetFlags(graph.nodesCount())) + assertEquals(expectedParent, actualParent, + "Incorrect parent found when walking from ${startNode} to ${endNode} in ${graph.asString(true)}") + } + + @Test + fun `one parent`() { + assertCorrespondingParent(0, 5, 1) { + 0(1) + 1(2) + 2(3, 4) + 3(5) + 4(5) + 5() + } + } + + @Test + fun `target parent`() { + assertCorrespondingParent(0, 2, 2) { + 0(1, 2) + 1(3) + 2(3) + 3() + } + } + + @Test + fun `merge commit`() { + assertCorrespondingParent(0, 4, 2) { + 0(1, 2) + 1(3) + 2(3, 4) + 3() + 4() + } + } + + @Test + fun `test visited`() { + val graph = graph { + 0(3) + 1(3) + 2(4) + 3(4) + 4(5, 6) + 5(7) + 6(8) + 7() + 8() + } + val nodesCount = graph.nodesCount() + val visited = BitSetFlags(nodesCount) + + BfsWalk(0, LinearGraphUtils.asLiteLinearGraph(graph), visited).walk() + assertEquals(BitSetFlags(nodesCount).setAll(0, 3, 4, 5, 6, 7, 8), visited) + visited.setAll(false) + + BfsWalk(3, LinearGraphUtils.asLiteLinearGraph(graph), visited).walk() + assertEquals(BitSetFlags(nodesCount).setAll(3, 4, 5, 6, 7, 8), visited) + visited.setAll(false) + + BfsWalk(5, LinearGraphUtils.asLiteLinearGraph(graph), visited).walk() + assertEquals(BitSetFlags(nodesCount).setAll(5, 7), visited) + visited.setAll(false) + + BfsWalk(8, LinearGraphUtils.asLiteLinearGraph(graph), visited).walk() + assertEquals(BitSetFlags(nodesCount).setAll(8), visited) + visited.setAll(false) + } + + private fun BitSetFlags.setAll(vararg values: Int): BitSetFlags { + values.forEach { this[it] = true } + return this + } +} \ No newline at end of file From 9c4312ab8c741cd8038dc4ce90df24e15169c22f Mon Sep 17 00:00:00 2001 From: Julia Beliaeva Date: Fri, 9 Nov 2018 20:12:10 +0300 Subject: [PATCH 6/8] [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 From 0fe79a37006cb7c66199565a9398dd6fc5be8ee7 Mon Sep 17 00:00:00 2001 From: Julia Beliaeva Date: Fri, 9 Nov 2018 20:34:17 +0300 Subject: [PATCH 7/8] [vcs-log-graph] introduce GraphUtil and move methods there --- .../collapsing/BranchFilterController.java | 4 +- .../graph/collapsing/CollapsedController.java | 4 +- .../log/graph/impl/facade/ReachableNodes.kt | 20 ------ .../intellij/vcs/log/graph/utils/BfsUtil.kt | 26 +------ .../intellij/vcs/log/graph/utils/DfsUtil.kt | 28 +------- .../intellij/vcs/log/graph/utils/GraphUtil.kt | 68 +++++++++++++++++++ .../intellij/vcs/log/graph/impl/BfsTests.kt | 6 +- .../intellij/vcs/log/history/FileHistory.kt | 6 +- 8 files changed, 78 insertions(+), 84 deletions(-) create mode 100644 platform/vcs-log/graph/src/com/intellij/vcs/log/graph/utils/GraphUtil.kt diff --git a/platform/vcs-log/graph/src/com/intellij/vcs/log/graph/collapsing/BranchFilterController.java b/platform/vcs-log/graph/src/com/intellij/vcs/log/graph/collapsing/BranchFilterController.java index 6311a96081b0..b27506cfb533 100644 --- a/platform/vcs-log/graph/src/com/intellij/vcs/log/graph/collapsing/BranchFilterController.java +++ b/platform/vcs-log/graph/src/com/intellij/vcs/log/graph/collapsing/BranchFilterController.java @@ -20,7 +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.impl.facade.CascadeController; import com.intellij.vcs.log.graph.impl.facade.LinearGraphController; -import com.intellij.vcs.log.graph.impl.facade.ReachableNodes; +import com.intellij.vcs.log.graph.utils.GraphUtilKt; import com.intellij.vcs.log.graph.utils.UnsignedBitSet; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -35,7 +35,7 @@ public class BranchFilterController extends CascadeController { @NotNull PermanentGraphInfo permanentGraphInfo, @Nullable Set idsOfVisibleBranches) { super(delegateLinearGraphController, permanentGraphInfo); - myVisibility = ReachableNodes.getReachableNodes(myPermanentGraphInfo.getLinearGraph(), idsOfVisibleBranches); + myVisibility = GraphUtilKt.getReachableNodes(myPermanentGraphInfo.getLinearGraph(), idsOfVisibleBranches); myCollapsedGraph = update(); } diff --git a/platform/vcs-log/graph/src/com/intellij/vcs/log/graph/collapsing/CollapsedController.java b/platform/vcs-log/graph/src/com/intellij/vcs/log/graph/collapsing/CollapsedController.java index cac8f4c1b984..dabf2f2fa6c0 100644 --- a/platform/vcs-log/graph/src/com/intellij/vcs/log/graph/collapsing/CollapsedController.java +++ b/platform/vcs-log/graph/src/com/intellij/vcs/log/graph/collapsing/CollapsedController.java @@ -24,7 +24,7 @@ import com.intellij.vcs.log.graph.api.permanent.PermanentGraphInfo; import com.intellij.vcs.log.graph.impl.facade.CascadeController; import com.intellij.vcs.log.graph.impl.facade.GraphChanges; import com.intellij.vcs.log.graph.impl.facade.LinearGraphController; -import com.intellij.vcs.log.graph.impl.facade.ReachableNodes; +import com.intellij.vcs.log.graph.utils.GraphUtilKt; import com.intellij.vcs.log.graph.utils.UnsignedBitSet; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -38,7 +38,7 @@ public class CollapsedController extends CascadeController { @NotNull final PermanentGraphInfo permanentGraphInfo, @Nullable Set idsOfVisibleBranches) { super(delegateLinearGraphController, permanentGraphInfo); - UnsignedBitSet initVisibility = ReachableNodes.getReachableNodes(permanentGraphInfo.getLinearGraph(), idsOfVisibleBranches); + UnsignedBitSet initVisibility = GraphUtilKt.getReachableNodes(permanentGraphInfo.getLinearGraph(), idsOfVisibleBranches); myCollapsedGraph = CollapsedGraph.newInstance(getDelegateController().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 6190cad58591..c80b31b42400 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 @@ -17,11 +17,9 @@ 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.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.* @@ -52,22 +50,4 @@ class ReachableNodes(private val graph: LiteLinearGraph) { DfsWalk(startNodes, graph, visited).walk(goDown, consumer) } } - - companion object { - @JvmStatic - fun getReachableNodes(graph: LinearGraph, headNodeIndexes: Set?): UnsignedBitSet { - if (headNodeIndexes == null) { - val nodesVisibility = UnsignedBitSet() - nodesVisibility.set(0, graph.nodesCount() - 1, true) - return nodesVisibility - } - - val result = UnsignedBitSet() - DfsWalk(headNodeIndexes, graph).walk(true) { node: Int -> - result.set(node, true) - true - } - return result - } - } } diff --git a/platform/vcs-log/graph/src/com/intellij/vcs/log/graph/utils/BfsUtil.kt b/platform/vcs-log/graph/src/com/intellij/vcs/log/graph/utils/BfsUtil.kt index fbb014dded29..2a3903ae63d4 100644 --- a/platform/vcs-log/graph/src/com/intellij/vcs/log/graph/utils/BfsUtil.kt +++ b/platform/vcs-log/graph/src/com/intellij/vcs/log/graph/utils/BfsUtil.kt @@ -41,28 +41,4 @@ class BfsWalk(val start: Int, private val graph: LiteLinearGraph, private val vi step() } } -} - -object BfsUtil { - fun getCorrespondingParent(graph: LiteLinearGraph, startNode: Int, endNode: Int, visited: Flags): Int { - val candidates = graph.getNodes(startNode, LiteLinearGraph.NodeFilter.DOWN) - if (candidates.size == 1) return candidates[0] - if (candidates.contains(endNode)) return endNode - - val bfsWalks = candidates.mapTo(mutableListOf()) { BfsWalk(it, graph, visited) } - - visited.setAll(false) - do { - for (walk in bfsWalks) { - if (walk.step().contains(endNode)) { - return walk.start - } - } - bfsWalks.removeIf { it.isFinished() } - } - while (bfsWalks.isNotEmpty()) - - return candidates[0] - } - -} +} \ No newline at end of file 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 75d25af10436..6f1c517ac54b 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 @@ -16,7 +16,6 @@ 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 @@ -136,29 +135,4 @@ private fun isDown(stack: IntStack): Boolean { val previousNode = getPreviousNode(stack) if (previousNode == Dfs.NextNode.NODE_NOT_FOUND) return true return previousNode < currentNode -} - -fun LiteLinearGraph.isAncestor(lowerNode: Int, upperNode: Int): Boolean { - val visited = BitSetFlags(nodesCount(), false) - - val result = Ref.create(false) - walk(lowerNode) { currentNode -> - visited.set(currentNode, true) - - if (currentNode == upperNode) { - result.set(true) - return@walk Dfs.NextNode.EXIT - } - if (currentNode > upperNode) { - for (nextNode in getNodes(currentNode, LiteLinearGraph.NodeFilter.UP)) { - if (!visited.get(nextNode)) { - return@walk nextNode - } - } - } - - Dfs.NextNode.NODE_NOT_FOUND - } - - return result.get() -} +} \ No newline at end of file diff --git a/platform/vcs-log/graph/src/com/intellij/vcs/log/graph/utils/GraphUtil.kt b/platform/vcs-log/graph/src/com/intellij/vcs/log/graph/utils/GraphUtil.kt new file mode 100644 index 000000000000..d7aa40fc1d0d --- /dev/null +++ b/platform/vcs-log/graph/src/com/intellij/vcs/log/graph/utils/GraphUtil.kt @@ -0,0 +1,68 @@ +// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. +package com.intellij.vcs.log.graph.utils + +import com.intellij.openapi.util.Ref +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 + +fun getReachableNodes(graph: LinearGraph, headNodes: Set?): UnsignedBitSet { + if (headNodes == null) { + val nodesVisibility = UnsignedBitSet() + nodesVisibility.set(0, graph.nodesCount() - 1, true) + return nodesVisibility + } + + val result = UnsignedBitSet() + DfsWalk(headNodes, graph).walk(true) { node: Int -> + result.set(node, true) + true + } + return result +} + +fun LiteLinearGraph.isAncestor(lowerNode: Int, upperNode: Int): Boolean { + val visited = BitSetFlags(nodesCount(), false) + + val result = Ref.create(false) + walk(lowerNode) { currentNode -> + visited.set(currentNode, true) + + if (currentNode == upperNode) { + result.set(true) + return@walk Dfs.NextNode.EXIT + } + if (currentNode > upperNode) { + for (nextNode in getNodes(currentNode, LiteLinearGraph.NodeFilter.UP)) { + if (!visited.get(nextNode)) { + return@walk nextNode + } + } + } + + Dfs.NextNode.NODE_NOT_FOUND + } + + return result.get() +} + +fun getCorrespondingParent(graph: LiteLinearGraph, startNode: Int, endNode: Int, visited: Flags): Int { + val candidates = graph.getNodes(startNode, LiteLinearGraph.NodeFilter.DOWN) + if (candidates.size == 1) return candidates[0] + if (candidates.contains(endNode)) return endNode + + val bfsWalks = candidates.mapTo(mutableListOf()) { BfsWalk(it, graph, visited) } + + visited.setAll(false) + do { + for (walk in bfsWalks) { + if (walk.step().contains(endNode)) { + return walk.start + } + } + bfsWalks.removeIf { it.isFinished() } + } + while (bfsWalks.isNotEmpty()) + + return candidates[0] +} \ No newline at end of file diff --git a/platform/vcs-log/graph/test/com/intellij/vcs/log/graph/impl/BfsTests.kt b/platform/vcs-log/graph/test/com/intellij/vcs/log/graph/impl/BfsTests.kt index 649b25adae0a..540dcdbe0f52 100644 --- a/platform/vcs-log/graph/test/com/intellij/vcs/log/graph/impl/BfsTests.kt +++ b/platform/vcs-log/graph/test/com/intellij/vcs/log/graph/impl/BfsTests.kt @@ -4,9 +4,9 @@ package com.intellij.vcs.log.graph.impl import com.intellij.vcs.log.graph.TestGraphBuilder import com.intellij.vcs.log.graph.asString import com.intellij.vcs.log.graph.graph -import com.intellij.vcs.log.graph.utils.BfsUtil import com.intellij.vcs.log.graph.utils.BfsWalk import com.intellij.vcs.log.graph.utils.LinearGraphUtils +import com.intellij.vcs.log.graph.utils.getCorrespondingParent import com.intellij.vcs.log.graph.utils.impl.BitSetFlags import org.junit.Test import kotlin.test.assertEquals @@ -15,8 +15,8 @@ class BfsTests { private fun assertCorrespondingParent(startNode: Int, endNode: Int, expectedParent: Int, graphBuilder: TestGraphBuilder.() -> Unit) { val graph = graph(graphBuilder) - val actualParent = BfsUtil.getCorrespondingParent(LinearGraphUtils.asLiteLinearGraph(graph), startNode, endNode, - BitSetFlags(graph.nodesCount())) + val actualParent = getCorrespondingParent(LinearGraphUtils.asLiteLinearGraph(graph), startNode, endNode, + BitSetFlags(graph.nodesCount())) assertEquals(expectedParent, actualParent, "Incorrect parent found when walking from ${startNode} to ${endNode} in ${graph.asString(true)}") } diff --git a/platform/vcs-log/impl/src/com/intellij/vcs/log/history/FileHistory.kt b/platform/vcs-log/impl/src/com/intellij/vcs/log/history/FileHistory.kt index 60b1405add61..a705fdab489d 100644 --- a/platform/vcs-log/impl/src/com/intellij/vcs/log/history/FileHistory.kt +++ b/platform/vcs-log/impl/src/com/intellij/vcs/log/history/FileHistory.kt @@ -19,12 +19,8 @@ import com.intellij.vcs.log.graph.api.permanent.PermanentCommitsInfo import com.intellij.vcs.log.graph.api.permanent.PermanentGraphInfo import com.intellij.vcs.log.graph.collapsing.CollapsedGraph import com.intellij.vcs.log.graph.impl.facade.* -import com.intellij.vcs.log.graph.utils.BfsUtil.getCorrespondingParent -import com.intellij.vcs.log.graph.utils.Dfs -import com.intellij.vcs.log.graph.utils.LinearGraphUtils +import com.intellij.vcs.log.graph.utils.* import com.intellij.vcs.log.graph.utils.impl.BitSetFlags -import com.intellij.vcs.log.graph.utils.isAncestor -import com.intellij.vcs.log.graph.utils.walk import gnu.trove.* import java.util.* import java.util.function.BiConsumer From d29243cfca2207c33a5fc63e928cb2b148184c2d Mon Sep 17 00:00:00 2001 From: Julia Beliaeva Date: Fri, 9 Nov 2018 20:40:25 +0300 Subject: [PATCH 8/8] [vcs-log-graph] transform functions to extension functions of the graph --- .../com/intellij/vcs/log/graph/utils/GraphUtil.kt | 12 ++++++------ .../test/com/intellij/vcs/log/graph/impl/BfsTests.kt | 3 +-- .../src/com/intellij/vcs/log/history/FileHistory.kt | 4 ++-- 3 files changed, 9 insertions(+), 10 deletions(-) diff --git a/platform/vcs-log/graph/src/com/intellij/vcs/log/graph/utils/GraphUtil.kt b/platform/vcs-log/graph/src/com/intellij/vcs/log/graph/utils/GraphUtil.kt index d7aa40fc1d0d..e089eea9294b 100644 --- a/platform/vcs-log/graph/src/com/intellij/vcs/log/graph/utils/GraphUtil.kt +++ b/platform/vcs-log/graph/src/com/intellij/vcs/log/graph/utils/GraphUtil.kt @@ -6,15 +6,15 @@ 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 -fun getReachableNodes(graph: LinearGraph, headNodes: Set?): UnsignedBitSet { +fun LinearGraph.getReachableNodes(headNodes: Set?): UnsignedBitSet { if (headNodes == null) { val nodesVisibility = UnsignedBitSet() - nodesVisibility.set(0, graph.nodesCount() - 1, true) + nodesVisibility.set(0, nodesCount() - 1, true) return nodesVisibility } val result = UnsignedBitSet() - DfsWalk(headNodes, graph).walk(true) { node: Int -> + DfsWalk(headNodes, this).walk(true) { node: Int -> result.set(node, true) true } @@ -46,12 +46,12 @@ fun LiteLinearGraph.isAncestor(lowerNode: Int, upperNode: Int): Boolean { return result.get() } -fun getCorrespondingParent(graph: LiteLinearGraph, startNode: Int, endNode: Int, visited: Flags): Int { - val candidates = graph.getNodes(startNode, LiteLinearGraph.NodeFilter.DOWN) +fun LiteLinearGraph.getCorrespondingParent(startNode: Int, endNode: Int, visited: Flags): Int { + val candidates = getNodes(startNode, LiteLinearGraph.NodeFilter.DOWN) if (candidates.size == 1) return candidates[0] if (candidates.contains(endNode)) return endNode - val bfsWalks = candidates.mapTo(mutableListOf()) { BfsWalk(it, graph, visited) } + val bfsWalks = candidates.mapTo(mutableListOf()) { BfsWalk(it, this, visited) } visited.setAll(false) do { diff --git a/platform/vcs-log/graph/test/com/intellij/vcs/log/graph/impl/BfsTests.kt b/platform/vcs-log/graph/test/com/intellij/vcs/log/graph/impl/BfsTests.kt index 540dcdbe0f52..472671da66b8 100644 --- a/platform/vcs-log/graph/test/com/intellij/vcs/log/graph/impl/BfsTests.kt +++ b/platform/vcs-log/graph/test/com/intellij/vcs/log/graph/impl/BfsTests.kt @@ -15,8 +15,7 @@ class BfsTests { private fun assertCorrespondingParent(startNode: Int, endNode: Int, expectedParent: Int, graphBuilder: TestGraphBuilder.() -> Unit) { val graph = graph(graphBuilder) - val actualParent = getCorrespondingParent(LinearGraphUtils.asLiteLinearGraph(graph), startNode, endNode, - BitSetFlags(graph.nodesCount())) + val actualParent = LinearGraphUtils.asLiteLinearGraph(graph).getCorrespondingParent(startNode, endNode, BitSetFlags(graph.nodesCount())) assertEquals(expectedParent, actualParent, "Incorrect parent found when walking from ${startNode} to ${endNode} in ${graph.asString(true)}") } diff --git a/platform/vcs-log/impl/src/com/intellij/vcs/log/history/FileHistory.kt b/platform/vcs-log/impl/src/com/intellij/vcs/log/history/FileHistory.kt index a705fdab489d..4aaf3df4f1d5 100644 --- a/platform/vcs-log/impl/src/com/intellij/vcs/log/history/FileHistory.kt +++ b/platform/vcs-log/impl/src/com/intellij/vcs/log/history/FileHistory.kt @@ -198,7 +198,7 @@ internal class FileHistoryRefiner(private val visibleLinearGraph: LinearGraph, namesData.getPathInParentRevision(previousCommit, permanentCommitsInfo.getCommitId(parentIndex), previousPath.filePath) } val path = findPathWithoutConflict(previousNodeId, pathGetter) - path ?: pathGetter(getCorrespondingParent(permanentLinearGraph, previousNodeId, currentNodeId, visibilityBuffer)) + path ?: pathGetter(permanentLinearGraph.getCorrespondingParent(previousNodeId, currentNodeId, visibilityBuffer)) } else { val pathGetter = { parentIndex: Int -> @@ -206,7 +206,7 @@ internal class FileHistoryRefiner(private val visibleLinearGraph: LinearGraph, } val path = findPathWithoutConflict(currentNodeId, pathGetter) // since in reality there is no edge between the nodes, but the whole path, we need to know, which parent is affected by this path - path ?: pathGetter(getCorrespondingParent(permanentLinearGraph, currentNodeId, previousNodeId, visibilityBuffer)) + path ?: pathGetter(permanentLinearGraph.getCorrespondingParent(currentNodeId, previousNodeId, visibilityBuffer)) } }