From ae5528042cd2f948cea3e50c32a5426eb302d3f7 Mon Sep 17 00:00:00 2001 From: Evgeny Mandrikov Date: Thu, 15 Dec 2011 14:02:49 +0400 Subject: SONAR-3060 Improve new CPD algorithm * Remove recursive part of algorithm to avoid StackOverflowError * Fix violations --- .../duplications/detector/original/Filter.java | 3 +- .../duplications/detector/suffixtree/Search.java | 61 ++++++++++++---------- 2 files changed, 33 insertions(+), 31 deletions(-) (limited to 'sonar-duplications/src/main') diff --git a/sonar-duplications/src/main/java/org/sonar/duplications/detector/original/Filter.java b/sonar-duplications/src/main/java/org/sonar/duplications/detector/original/Filter.java index f9e1fecdec9..5c317dbe0ce 100644 --- a/sonar-duplications/src/main/java/org/sonar/duplications/detector/original/Filter.java +++ b/sonar-duplications/src/main/java/org/sonar/duplications/detector/original/Filter.java @@ -25,7 +25,6 @@ import java.util.List; import org.sonar.duplications.detector.ContainsInComparator; import org.sonar.duplications.index.CloneGroup; import org.sonar.duplications.index.ClonePart; -import org.sonar.duplications.utils.FastStringComparator; import org.sonar.duplications.utils.SortedListsUtils; import com.google.common.collect.Lists; @@ -103,7 +102,7 @@ final class Filter { *

*

* Important: this method relies on fact that all parts were already sorted by resourceId and unitStart by using - * {@link BlocksGroup.BlockComparator}, which uses {@link FastStringComparator} for comparison by resourceId. + * {@link BlocksGroup.BlockComparator}, which uses {@link org.sonar.duplications.utils.FastStringComparator} for comparison by resourceId. *

*

* Running time - O(|A|+|B|). diff --git a/sonar-duplications/src/main/java/org/sonar/duplications/detector/suffixtree/Search.java b/sonar-duplications/src/main/java/org/sonar/duplications/detector/suffixtree/Search.java index d0c32f74765..8e5b4e665d1 100644 --- a/sonar-duplications/src/main/java/org/sonar/duplications/detector/suffixtree/Search.java +++ b/sonar-duplications/src/main/java/org/sonar/duplications/detector/suffixtree/Search.java @@ -21,14 +21,16 @@ package org.sonar.duplications.detector.suffixtree; import java.util.*; +import com.google.common.collect.Lists; + public final class Search { private final SuffixTree tree; private final TextSet text; private final Collector reporter; - private final List list = new ArrayList(); - private final List innerNodes = new ArrayList(); + private final List list = Lists.newArrayList(); + private final List innerNodes = Lists.newArrayList(); public static void perform(TextSet text, Collector reporter) { new Search(SuffixTree.create(text), text, reporter).compute(); @@ -42,52 +44,53 @@ public final class Search { private void compute() { // O(N) - computeDepth(); + dfs(); // O(N * log(N)) Collections.sort(innerNodes, DEPTH_COMPARATOR); - // O(N), recursive - createListOfLeafs(tree.getRootNode()); - // O(N) visitInnerNodes(); } - private void computeDepth() { - Queue queue = new LinkedList(); - queue.add(tree.getRootNode()); - tree.getRootNode().depth = 0; - while (!queue.isEmpty()) { - Node node = queue.remove(); - if (!node.getEdges().isEmpty()) { + private static final Comparator DEPTH_COMPARATOR = new Comparator() { + public int compare(Node o1, Node o2) { + return o2.depth - o1.depth; + } + }; + + /** + * Depth-first search (DFS). + */ + private void dfs() { + LinkedList stack = Lists.newLinkedList(); + stack.add(tree.getRootNode()); + while (!stack.isEmpty()) { + Node node = stack.removeLast(); + node.startSize = list.size(); + if (node.getEdges().isEmpty()) { // leaf + list.add(node.depth); + node.endSize = list.size(); + } else { if (node != tree.getRootNode()) { // inner node = not leaf and not root innerNodes.add(node); } for (Edge edge : node.getEdges()) { Node endNode = edge.getEndNode(); endNode.depth = node.depth + edge.getSpan() + 1; - queue.add(endNode); + stack.addLast(endNode); } } } - } - - private static final Comparator DEPTH_COMPARATOR = new Comparator() { - public int compare(Node o1, Node o2) { - return o2.depth - o1.depth; - } - }; - - private void createListOfLeafs(Node node) { - node.startSize = list.size(); - if (node.getEdges().isEmpty()) { // leaf - list.add(node.depth); - } else { + // At this point all inner nodes are ordered by the time of entering, so we visit them from last to first + ListIterator iterator = innerNodes.listIterator(innerNodes.size()); + while (iterator.hasPrevious()) { + Node node = iterator.previous(); + int max = -1; for (Edge edge : node.getEdges()) { - createListOfLeafs(edge.getEndNode()); + max = Math.max(edge.getEndNode().endSize, max); } - node.endSize = list.size(); + node.endSize = max; } } -- cgit v1.2.3