aboutsummaryrefslogtreecommitdiffstats
path: root/sonar-core
diff options
context:
space:
mode:
authorDuarte Meneses <duarte.meneses@sonarsource.com>2015-11-09 16:56:58 +0100
committerDuarte Meneses <duarte.meneses@sonarsource.com>2015-11-11 10:25:37 +0100
commit3e019c0afb4a3df57ed86fe394a090b75f2d7218 (patch)
treef4a38a9b9dce181a366da4a9e30fd024f702bb1b /sonar-core
parentaf5d5ff485d1cd76e76543477488b97695209b8c (diff)
downloadsonarqube-3e019c0afb4a3df57ed86fe394a090b75f2d7218.tar.gz
sonarqube-3e019c0afb4a3df57ed86fe394a090b75f2d7218.zip
SONAR-7003 Refactor batch issue tracking
Diffstat (limited to 'sonar-core')
-rw-r--r--sonar-core/src/main/java/org/sonar/core/issue/tracking/LineHashSequence.java41
-rw-r--r--sonar-core/src/main/java/org/sonar/core/issue/tracking/Tracker.java17
-rw-r--r--sonar-core/src/main/java/org/sonar/core/util/UuidFactoryFast.java75
-rw-r--r--sonar-core/src/main/java/org/sonar/core/util/Uuids.java4
-rw-r--r--sonar-core/src/test/java/org/sonar/core/util/UuidFactoryFastTest.java45
5 files changed, 151 insertions, 31 deletions
diff --git a/sonar-core/src/main/java/org/sonar/core/issue/tracking/LineHashSequence.java b/sonar-core/src/main/java/org/sonar/core/issue/tracking/LineHashSequence.java
index 2601ccd05b5..37da9e4ca48 100644
--- a/sonar-core/src/main/java/org/sonar/core/issue/tracking/LineHashSequence.java
+++ b/sonar-core/src/main/java/org/sonar/core/issue/tracking/LineHashSequence.java
@@ -19,11 +19,13 @@
*/
package org.sonar.core.issue.tracking;
+import com.google.common.collect.SetMultimap;
+import com.google.common.collect.HashMultimap;
import com.google.common.base.Strings;
-import java.util.HashMap;
+
import java.util.List;
-import java.util.Map;
-import javax.annotation.Nullable;
+import java.util.Set;
+
import org.sonar.core.hash.SourceLinesHashesComputer;
/**
@@ -31,21 +33,21 @@ import org.sonar.core.hash.SourceLinesHashesComputer;
*/
public class LineHashSequence {
- private static final int[] EMPTY_INTS = new int[0];
-
/**
* Hashes of lines. Line 1 is at index 0. No null elements.
*/
private final List<String> hashes;
- private final Map<String, int[]> linesByHash;
+ private final SetMultimap<String, Integer> lineByHash;
public LineHashSequence(List<String> hashes) {
this.hashes = hashes;
- this.linesByHash = new HashMap<>(hashes.size());
- for (int line = 1; line <= hashes.size(); line++) {
- String hash = hashes.get(line - 1);
- int[] lines = linesByHash.get(hash);
- linesByHash.put(hash, appendLineTo(line, lines));
+ this.lineByHash = HashMultimap.create();
+
+ int lineNo = 1;
+
+ for (String hash : hashes) {
+ lineByHash.put(hash, lineNo);
+ lineNo++;
}
}
@@ -66,9 +68,8 @@ public class LineHashSequence {
/**
* The lines, starting with 1, that matches the given hash.
*/
- public int[] getLinesForHash(String hash) {
- int[] lines = linesByHash.get(hash);
- return lines == null ? EMPTY_INTS : lines;
+ public Set<Integer> getLinesForHash(String hash) {
+ return lineByHash.get(hash);
}
/**
@@ -86,18 +87,6 @@ public class LineHashSequence {
return hashes;
}
- private static int[] appendLineTo(int line, @Nullable int[] to) {
- int[] result;
- if (to == null) {
- result = new int[] {line};
- } else {
- result = new int[to.length + 1];
- System.arraycopy(to, 0, result, 0, to.length);
- result[result.length - 1] = line;
- }
- return result;
- }
-
public static LineHashSequence createForLines(List<String> lines) {
SourceLinesHashesComputer hashesComputer = new SourceLinesHashesComputer(lines.size());
for (String line : lines) {
diff --git a/sonar-core/src/main/java/org/sonar/core/issue/tracking/Tracker.java b/sonar-core/src/main/java/org/sonar/core/issue/tracking/Tracker.java
index 66d103134c6..859cab178fa 100644
--- a/sonar-core/src/main/java/org/sonar/core/issue/tracking/Tracker.java
+++ b/sonar-core/src/main/java/org/sonar/core/issue/tracking/Tracker.java
@@ -19,18 +19,25 @@
*/
package org.sonar.core.issue.tracking;
+import org.sonar.api.batch.BatchSide;
+import org.sonar.api.batch.InstantiationStrategy;
import com.google.common.base.Predicate;
import com.google.common.base.Strings;
import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.Multimap;
+
import java.util.Collection;
import java.util.Objects;
+import java.util.Set;
+
import javax.annotation.Nonnull;
+
import org.apache.commons.lang.StringUtils;
import org.sonar.api.rule.RuleKey;
-
import static com.google.common.collect.FluentIterable.from;
+@InstantiationStrategy(InstantiationStrategy.PER_BATCH)
+@BatchSide
public class Tracker<RAW extends Trackable, BASE extends Trackable> {
public Tracking<RAW, BASE> track(Input<RAW> rawInput, Input<BASE> baseInput) {
@@ -98,10 +105,10 @@ public class Tracker<RAW extends Trackable, BASE extends Trackable> {
baseHash = baseInput.getLineHashSequence().getHashForLine(base.getLine());
}
if (!Strings.isNullOrEmpty(baseHash)) {
- int[] rawLines = rawInput.getLineHashSequence().getLinesForHash(baseHash);
- if (rawLines.length == 1) {
- tracking.keepManualIssueOpen(base, rawLines[0]);
- } else if (rawLines.length == 0 && rawInput.getLineHashSequence().hasLine(base.getLine())) {
+ Set<Integer> rawLines = rawInput.getLineHashSequence().getLinesForHash(baseHash);
+ if (rawLines.size() == 1) {
+ tracking.keepManualIssueOpen(base, rawLines.iterator().next());
+ } else if (rawLines.isEmpty() && rawInput.getLineHashSequence().hasLine(base.getLine())) {
// still valid (???). We didn't manage to correctly detect code move, so the
// issue is kept at the same location, even if code changes
tracking.keepManualIssueOpen(base, base.getLine());
diff --git a/sonar-core/src/main/java/org/sonar/core/util/UuidFactoryFast.java b/sonar-core/src/main/java/org/sonar/core/util/UuidFactoryFast.java
new file mode 100644
index 00000000000..5b3a1930f52
--- /dev/null
+++ b/sonar-core/src/main/java/org/sonar/core/util/UuidFactoryFast.java
@@ -0,0 +1,75 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.core.util;
+
+/**
+ * NOT thread safe
+ * About 10x faster than {@link UuidFactoryImpl}
+ */
+public class UuidFactoryFast implements UuidFactory {
+ private static UuidFactoryFast instance = new UuidFactoryFast();
+ private static final char[] HEX_ARRAY = "0123456789ABCDEF".toCharArray();
+ private static int sequenceNumber = 0;
+
+ private UuidFactoryFast() {
+ //
+ }
+
+ @Override
+ public String create() {
+ long timestamp = System.currentTimeMillis();
+
+ byte[] uuidBytes = new byte[9];
+
+ // Only use lower 6 bytes of the timestamp (this will suffice beyond the year 10000):
+ putLong(uuidBytes, timestamp, 0, 6);
+
+ // Sequence number adds 3 bytes:
+ putLong(uuidBytes, getSequenceNumber(), 6, 3);
+
+ return byteArrayToHex(uuidBytes);
+ }
+
+ public static UuidFactoryFast getInstance() {
+ return instance;
+ }
+
+ private static int getSequenceNumber() {
+ return sequenceNumber++;
+ }
+
+ /** Puts the lower numberOfLongBytes from l into the array, starting index pos. */
+ private static void putLong(byte[] array, long l, int pos, int numberOfLongBytes) {
+ for (int i = 0; i < numberOfLongBytes; ++i) {
+ array[pos + numberOfLongBytes - i - 1] = (byte) (l >>> (i * 8));
+ }
+ }
+
+ public static String byteArrayToHex(byte[] bytes) {
+ char[] hexChars = new char[bytes.length * 2];
+ for (int j = 0; j < bytes.length; j++) {
+ int v = bytes[j] & 0xFF;
+ hexChars[j * 2] = HEX_ARRAY[v >>> 4];
+ hexChars[j * 2 + 1] = HEX_ARRAY[v & 0x0F];
+ }
+ return new String(hexChars);
+ }
+
+}
diff --git a/sonar-core/src/main/java/org/sonar/core/util/Uuids.java b/sonar-core/src/main/java/org/sonar/core/util/Uuids.java
index 2db82d0f899..e19f77c67da 100644
--- a/sonar-core/src/main/java/org/sonar/core/util/Uuids.java
+++ b/sonar-core/src/main/java/org/sonar/core/util/Uuids.java
@@ -44,4 +44,8 @@ public class Uuids {
public static String create() {
return UuidFactoryImpl.INSTANCE.create();
}
+
+ public static String createFast() {
+ return UuidFactoryFast.getInstance().create();
+ }
}
diff --git a/sonar-core/src/test/java/org/sonar/core/util/UuidFactoryFastTest.java b/sonar-core/src/test/java/org/sonar/core/util/UuidFactoryFastTest.java
new file mode 100644
index 00000000000..d45643f02a2
--- /dev/null
+++ b/sonar-core/src/test/java/org/sonar/core/util/UuidFactoryFastTest.java
@@ -0,0 +1,45 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.core.util;
+
+import org.junit.Test;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class UuidFactoryFastTest {
+ UuidFactory underTest = UuidFactoryFast.getInstance();
+
+ @Test
+ public void create_different_uuids() {
+ // this test is not enough to ensure that generated strings are unique,
+ // but it still does a simple and stupid verification
+ assertThat(underTest.create()).isNotEqualTo(underTest.create());
+ }
+
+ @Test
+ public void test_format_of_uuid() throws Exception {
+ String uuid = underTest.create();
+
+ assertThat(uuid.length()).isGreaterThan(10).isLessThan(40);
+
+ // URL-safe: only letters, digits, dash and underscore.
+ assertThat(uuid).matches("^[\\w\\-_]+$");
+ }
+}