diff options
author | Duarte Meneses <duarte.meneses@sonarsource.com> | 2015-11-09 16:56:58 +0100 |
---|---|---|
committer | Duarte Meneses <duarte.meneses@sonarsource.com> | 2015-11-11 10:25:37 +0100 |
commit | 3e019c0afb4a3df57ed86fe394a090b75f2d7218 (patch) | |
tree | f4a38a9b9dce181a366da4a9e30fd024f702bb1b /sonar-core | |
parent | af5d5ff485d1cd76e76543477488b97695209b8c (diff) | |
download | sonarqube-3e019c0afb4a3df57ed86fe394a090b75f2d7218.tar.gz sonarqube-3e019c0afb4a3df57ed86fe394a090b75f2d7218.zip |
SONAR-7003 Refactor batch issue tracking
Diffstat (limited to 'sonar-core')
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\\-_]+$"); + } +} |