From c8b766ce8f89d4b978fd52e485c3cc3c97032a4b Mon Sep 17 00:00:00 2001 From: Julien HENRY Date: Wed, 17 Dec 2014 16:29:22 +0100 Subject: [PATCH] SONAR-5867 Sort symbol references to avoid variations between consecutive runs --- .../sonar/batch/index/SourcePersister.java | 12 +++++++++++- .../batch/mediumtest/BatchMediumTester.java | 3 ++- .../batch/source/DefaultSymbolTable.java | 12 +++++++----- .../symbol/DefaultSymbolTableBuilder.java | 8 ++++---- .../org/sonar/batch/symbol/SymbolData.java | 8 ++++---- .../batch/index/SourcePersisterTest.java | 19 +++++++++++++++++++ .../symbol/DefaultSymbolTableBuilderTest.java | 4 ++-- 7 files changed, 49 insertions(+), 17 deletions(-) diff --git a/sonar-batch/src/main/java/org/sonar/batch/index/SourcePersister.java b/sonar-batch/src/main/java/org/sonar/batch/index/SourcePersister.java index 186e37edaa6..342c3503fb6 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/index/SourcePersister.java +++ b/sonar-batch/src/main/java/org/sonar/batch/index/SourcePersister.java @@ -58,7 +58,9 @@ import javax.annotation.Nullable; import java.io.IOException; import java.io.StringWriter; +import java.util.ArrayList; import java.util.Collections; +import java.util.Comparator; import java.util.Date; import java.util.HashMap; import java.util.Iterator; @@ -330,7 +332,15 @@ public class SourcePersister implements ScanPersister { StringBuilder[] symbolRefsPerLine = new StringBuilder[file.lines()]; long[] originalLineOffsets = file.originalLineOffsets(); int symbolId = 1; - for (Symbol symbol : symbolRefs.referencesBySymbol().keySet()) { + List symbols = new ArrayList(symbolRefs.referencesBySymbol().keySet()); + // Sort symbols to avoid false variation that would lead to an unnecessary update + Collections.sort(symbols, new Comparator() { + @Override + public int compare(Symbol o1, Symbol o2) { + return o1.getDeclarationStartOffset() - o2.getDeclarationStartOffset(); + } + }); + for (Symbol symbol : symbols) { int declarationStartOffset = symbol.getDeclarationStartOffset(); int declarationEndOffset = symbol.getDeclarationEndOffset(); int length = declarationEndOffset - declarationStartOffset; diff --git a/sonar-batch/src/main/java/org/sonar/batch/mediumtest/BatchMediumTester.java b/sonar-batch/src/main/java/org/sonar/batch/mediumtest/BatchMediumTester.java index 8499776bfb7..2886cfbc28c 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/mediumtest/BatchMediumTester.java +++ b/sonar-batch/src/main/java/org/sonar/batch/mediumtest/BatchMediumTester.java @@ -80,6 +80,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Properties; +import java.util.Set; /** * Main utility class for writing batch medium tests. @@ -406,7 +407,7 @@ public class BatchMediumTester { * @param symbolEndOffset 0-based end offset for the symbol in file */ @CheckForNull - public List symbolReferencesFor(InputFile file, int symbolStartOffset, int symbolEndOffset) { + public Set symbolReferencesFor(InputFile file, int symbolStartOffset, int symbolEndOffset) { SymbolData data = symbolTablePerFile.get(file); if (data == null) { return null; diff --git a/sonar-batch/src/main/java/org/sonar/batch/source/DefaultSymbolTable.java b/sonar-batch/src/main/java/org/sonar/batch/source/DefaultSymbolTable.java index 8aafb847386..b4ce0c15878 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/source/DefaultSymbolTable.java +++ b/sonar-batch/src/main/java/org/sonar/batch/source/DefaultSymbolTable.java @@ -28,16 +28,18 @@ import java.util.ArrayList; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; +import java.util.Set; +import java.util.TreeSet; public class DefaultSymbolTable implements Symbolizable.SymbolTable { - private Map> referencesBySymbol; + private Map> referencesBySymbol; - private DefaultSymbolTable(Map> referencesBySymbol) { + private DefaultSymbolTable(Map> referencesBySymbol) { this.referencesBySymbol = referencesBySymbol; } - public Map> getReferencesBySymbol() { + public Map> getReferencesBySymbol() { return referencesBySymbol; } @@ -57,7 +59,7 @@ public class DefaultSymbolTable implements Symbolizable.SymbolTable { public static class Builder implements Symbolizable.SymbolTableBuilder { - private final Map> referencesBySymbol = new LinkedHashMap>(); + private final Map> referencesBySymbol = new LinkedHashMap>(); private final String componentKey; public Builder(String componentKey) { @@ -67,7 +69,7 @@ public class DefaultSymbolTable implements Symbolizable.SymbolTable { @Override public Symbol newSymbol(int fromOffset, int toOffset) { Symbol symbol = new DefaultSymbol(fromOffset, toOffset); - referencesBySymbol.put(symbol, new ArrayList()); + referencesBySymbol.put(symbol, new TreeSet()); return symbol; } diff --git a/sonar-batch/src/main/java/org/sonar/batch/symbol/DefaultSymbolTableBuilder.java b/sonar-batch/src/main/java/org/sonar/batch/symbol/DefaultSymbolTableBuilder.java index a6e8c8c0aaf..72d4c050eb4 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/symbol/DefaultSymbolTableBuilder.java +++ b/sonar-batch/src/main/java/org/sonar/batch/symbol/DefaultSymbolTableBuilder.java @@ -27,17 +27,17 @@ import org.sonar.batch.index.ComponentDataCache; import org.sonar.core.source.SnapshotDataTypes; import java.io.Serializable; -import java.util.ArrayList; import java.util.Comparator; import java.util.LinkedHashMap; -import java.util.List; import java.util.Map; +import java.util.Set; +import java.util.TreeSet; public class DefaultSymbolTableBuilder implements SymbolTableBuilder { private final String componentKey; private final ComponentDataCache cache; - private final Map> referencesBySymbol = new LinkedHashMap>(); + private final Map> referencesBySymbol = new LinkedHashMap>(); public DefaultSymbolTableBuilder(String componentKey, ComponentDataCache cache) { this.componentKey = componentKey; @@ -47,7 +47,7 @@ public class DefaultSymbolTableBuilder implements SymbolTableBuilder { @Override public Symbol newSymbol(int fromOffset, int toOffset) { org.sonar.api.source.Symbol symbol = new DefaultSymbol(fromOffset, toOffset); - referencesBySymbol.put(symbol, new ArrayList()); + referencesBySymbol.put(symbol, new TreeSet()); return symbol; } diff --git a/sonar-batch/src/main/java/org/sonar/batch/symbol/SymbolData.java b/sonar-batch/src/main/java/org/sonar/batch/symbol/SymbolData.java index 0f55e194e8b..baf897c6d1b 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/symbol/SymbolData.java +++ b/sonar-batch/src/main/java/org/sonar/batch/symbol/SymbolData.java @@ -24,21 +24,21 @@ import org.sonar.api.batch.sensor.symbol.Symbol; import org.sonar.batch.index.Data; import java.util.Collection; -import java.util.List; import java.util.Map; +import java.util.Set; public class SymbolData implements Data { public static final String FIELD_SEPARATOR = ","; public static final String SYMBOL_SEPARATOR = ";"; - private final Map> referencesBySymbol; + private final Map> referencesBySymbol; - public SymbolData(Map> referencesBySymbol) { + public SymbolData(Map> referencesBySymbol) { this.referencesBySymbol = referencesBySymbol; } - public Map> referencesBySymbol() { + public Map> referencesBySymbol() { return referencesBySymbol; } diff --git a/sonar-batch/src/test/java/org/sonar/batch/index/SourcePersisterTest.java b/sonar-batch/src/test/java/org/sonar/batch/index/SourcePersisterTest.java index af2c5474d94..4b405cca96c 100644 --- a/sonar-batch/src/test/java/org/sonar/batch/index/SourcePersisterTest.java +++ b/sonar-batch/src/test/java/org/sonar/batch/index/SourcePersisterTest.java @@ -362,6 +362,25 @@ public class SourcePersisterTest extends AbstractDaoTestCase { assertThat(symbolsPerLine).containsOnly("1,2,1;0,2,2", "0,1,1;0,2,2", "4,5,1;0,2,2"); } + @Test + public void verifyDeclarationOrderOfSymbolHasNoImpact() { + DefaultInputFile file = new DefaultInputFile(PROJECT_KEY, "src/foo.java") + .setLines(3) + .setOriginalLineOffsets(new long[] {0, 4, 7}); + + DefaultSymbolTableBuilder symbolBuilder = new DefaultSymbolTableBuilder(PROJECT_KEY + ":" + "src/foo.java", null); + org.sonar.api.batch.sensor.symbol.Symbol s2 = symbolBuilder.newSymbol(4, 6); + symbolBuilder.newReference(s2, 7); + symbolBuilder.newReference(s2, 0); + org.sonar.api.batch.sensor.symbol.Symbol s1 = symbolBuilder.newSymbol(1, 2); + symbolBuilder.newReference(s1, 11); + symbolBuilder.newReference(s1, 4); + + String[] symbolsPerLine = sourcePersister.computeSymbolReferencesPerLine(file, symbolBuilder.build()); + + assertThat(symbolsPerLine).containsOnly("1,2,1;0,2,2", "0,1,1;0,2,2", "4,5,1;0,2,2"); + } + private void mockResourceCache(String relativePathEmpty, String projectKey, String uuid) { File sonarFile = File.create(relativePathEmpty); sonarFile.setUuid(uuid); diff --git a/sonar-batch/src/test/java/org/sonar/batch/symbol/DefaultSymbolTableBuilderTest.java b/sonar-batch/src/test/java/org/sonar/batch/symbol/DefaultSymbolTableBuilderTest.java index aa8c67edc56..c68156c0a2d 100644 --- a/sonar-batch/src/test/java/org/sonar/batch/symbol/DefaultSymbolTableBuilderTest.java +++ b/sonar-batch/src/test/java/org/sonar/batch/symbol/DefaultSymbolTableBuilderTest.java @@ -30,8 +30,8 @@ import org.sonar.batch.index.ComponentDataCache; import org.sonar.core.source.SnapshotDataTypes; import java.util.ArrayList; -import java.util.List; import java.util.Map; +import java.util.Set; import static org.fest.assertions.Assertions.assertThat; import static org.mockito.Matchers.eq; @@ -58,7 +58,7 @@ public class DefaultSymbolTableBuilderTest { ArgumentCaptor argCaptor = ArgumentCaptor.forClass(SymbolData.class); verify(componentDataCache).setData(eq("foo"), eq(SnapshotDataTypes.SYMBOL_HIGHLIGHTING), argCaptor.capture()); - Map> referencesBySymbol = argCaptor.getValue().referencesBySymbol(); + Map> referencesBySymbol = argCaptor.getValue().referencesBySymbol(); assertThat(new ArrayList(referencesBySymbol.keySet())).containsExactly(firstSymbol, secondSymbol, thirdSymbol); assertThat(new ArrayList(referencesBySymbol.get(firstSymbol))).containsExactly(32); -- 2.39.5