From: Julien HENRY Date: Fri, 28 Nov 2014 16:11:21 +0000 (+0100) Subject: SONAR-5867 Add symbol references data into file_sources table X-Git-Tag: 5.0-RC1~160 X-Git-Url: https://source.dussan.org/?a=commitdiff_plain;h=0646b637664ac6018983cf4030398c692a50db24;p=sonarqube.git SONAR-5867 Add symbol references data into file_sources table --- diff --git a/server/sonar-server/src/main/java/org/sonar/server/db/migrations/v50/FileSourceDto.java b/server/sonar-server/src/main/java/org/sonar/server/db/migrations/v50/FileSourceDto.java index 5de5c1f97a6..176efa0be44 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/db/migrations/v50/FileSourceDto.java +++ b/server/sonar-server/src/main/java/org/sonar/server/db/migrations/v50/FileSourceDto.java @@ -72,6 +72,7 @@ class FileSourceDto { String[] getSourceData() { String highlighting = ""; + String symbolRefs = ""; ByteArrayOutputStream output = new ByteArrayOutputStream(); int line = 0; String sourceLine = null; @@ -85,7 +86,7 @@ class FileSourceDto { utHits.get(line), utConditions.get(line), utCoveredConditions.get(line), itHits.get(line), itConditions.get(line), itCoveredConditions.get(line), overallHits.get(line), overallConditions.get(line), overallCoveredConditions.get(line), - highlighting, sourceLine); + highlighting, symbolRefs, sourceLine); } csv.close(); return new String[] {new String(output.toByteArray(), UTF_8), lineHashes.toString()}; diff --git a/server/sonar-server/src/test/resources/org/sonar/server/db/migrations/v50/FeedFileSourcesTest/after-with-scm.xml b/server/sonar-server/src/test/resources/org/sonar/server/db/migrations/v50/FeedFileSourcesTest/after-with-scm.xml index 7ad56d49fbb..e68c3d0ef92 100644 --- a/server/sonar-server/src/test/resources/org/sonar/server/db/migrations/v50/FeedFileSourcesTest/after-with-scm.xml +++ b/server/sonar-server/src/test/resources/org/sonar/server/db/migrations/v50/FeedFileSourcesTest/after-with-scm.xml @@ -1,7 +1,7 @@ diff --git a/server/sonar-server/src/test/resources/org/sonar/server/db/migrations/v50/FeedFileSourcesTest/after.xml b/server/sonar-server/src/test/resources/org/sonar/server/db/migrations/v50/FeedFileSourcesTest/after.xml index 7eecbfd4e71..b8058e0390f 100644 --- a/server/sonar-server/src/test/resources/org/sonar/server/db/migrations/v50/FeedFileSourcesTest/after.xml +++ b/server/sonar-server/src/test/resources/org/sonar/server/db/migrations/v50/FeedFileSourcesTest/after.xml @@ -1,7 +1,7 @@ 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 546485e1686..cfbcd33800e 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 @@ -29,7 +29,7 @@ import org.apache.ibatis.session.ResultHandler; import org.sonar.api.batch.fs.InputFile; import org.sonar.api.batch.fs.InputPath; import org.sonar.api.batch.fs.internal.DefaultInputFile; -import org.sonar.api.batch.sensor.highlighting.TypeOfText; +import org.sonar.api.batch.sensor.symbol.Symbol; import org.sonar.api.database.model.Snapshot; import org.sonar.api.measures.CoreMetrics; import org.sonar.api.measures.Measure; @@ -43,6 +43,7 @@ import org.sonar.batch.highlighting.SyntaxHighlightingRule; import org.sonar.batch.scan.filesystem.InputPathCache; import org.sonar.batch.scan.measure.MeasureCache; import org.sonar.batch.source.CodeColorizers; +import org.sonar.batch.symbol.SymbolData; import org.sonar.core.persistence.DbSession; import org.sonar.core.persistence.MyBatis; import org.sonar.core.source.SnapshotDataTypes; @@ -221,6 +222,7 @@ public class SourcePersister implements ScanPersister { Map overallCoveredCondByLine = getLineMetric(file, CoreMetrics.OVERALL_COVERED_CONDITIONS_BY_LINE_KEY); SyntaxHighlightingData highlighting = loadHighlighting(file); String[] highlightingPerLine = computeHighlightingPerLine(file, highlighting); + String[] symbolReferencesPerLine = computeSymbolReferencesPerLine(file, loadSymbolReferences(file)); ByteArrayOutputStream output = new ByteArrayOutputStream(); CsvWriter csv = CsvWriter.of(new OutputStreamWriter(output, UTF_8)); @@ -229,7 +231,7 @@ public class SourcePersister implements ScanPersister { utHitsByLine.get(lineIdx), utCondByLine.get(lineIdx), utCoveredCondByLine.get(lineIdx), itHitsByLine.get(lineIdx), itCondByLine.get(lineIdx), itCoveredCondByLine.get(lineIdx), overallHitsByLine.get(lineIdx), overallCondByLine.get(lineIdx), overallCoveredCondByLine.get(lineIdx), - highlightingPerLine[lineIdx - 1], + highlightingPerLine[lineIdx - 1], symbolReferencesPerLine[lineIdx - 1], CharMatcher.anyOf(BOM).removeFrom(lines.get(lineIdx - 1))); } csv.close(); @@ -245,6 +247,11 @@ public class SourcePersister implements ScanPersister { return highlighting; } + @CheckForNull + private SymbolData loadSymbolReferences(DefaultInputFile file) { + return componentDataCache.getData(file.key(), SnapshotDataTypes.SYMBOL_HIGHLIGHTING); + } + String[] computeHighlightingPerLine(DefaultInputFile file, @Nullable SyntaxHighlightingData highlighting) { String[] result = new String[file.lines()]; if (highlighting == null) { @@ -259,7 +266,7 @@ public class SourcePersister implements ScanPersister { currentLineIdx++; } // Now we know current rule starts on current line - writeRule(file, rule, highlightingPerLine, currentLineIdx); + writeDataPerLine(file.originalLineOffsets(), rule, rule.getStartPosition(), rule.getEndPosition(), highlightingPerLine, currentLineIdx, new RuleItemWriter()); } for (int i = 0; i < file.lines(); i++) { result[i] = highlightingPerLine[i] != null ? highlightingPerLine[i].toString() : null; @@ -267,41 +274,115 @@ public class SourcePersister implements ScanPersister { return result; } - private void writeRule(DefaultInputFile file, SyntaxHighlightingRule rule, StringBuilder[] highlightingPerLine, int currentLine) { - int currentLineIdx = currentLine; + String[] computeSymbolReferencesPerLine(DefaultInputFile file, @Nullable SymbolData symbolRefs) { + String[] result = new String[file.lines()]; + if (symbolRefs == null) { + return result; + } + StringBuilder[] symbolRefsPerLine = new StringBuilder[file.lines()]; + long[] originalLineOffsets = file.originalLineOffsets(); + int symbolId = 1; + for (Symbol symbol : symbolRefs.referencesBySymbol().keySet()) { + int declarationStartOffset = symbol.getDeclarationStartOffset(); + int declarationEndOffset = symbol.getDeclarationEndOffset(); + int length = declarationEndOffset - declarationStartOffset; + addSymbol(symbolId, declarationStartOffset, declarationEndOffset, originalLineOffsets, symbolRefsPerLine); + for (Integer referenceStartOffset : symbolRefs.referencesBySymbol().get(symbol)) { + if (referenceStartOffset == declarationStartOffset) { + // Ignore old API that used to store reference as first declaration + continue; + } + addSymbol(symbolId, referenceStartOffset, referenceStartOffset + length, originalLineOffsets, symbolRefsPerLine); + } + symbolId++; + } + for (int i = 0; i < file.lines(); i++) { + result[i] = symbolRefsPerLine[i] != null ? symbolRefsPerLine[i].toString() : null; + } + return result; + } + + private void addSymbol(int symbolId, int startOffset, int endOffset, long[] originalLineOffsets, StringBuilder[] result) { + int startLine = binarySearchLine(startOffset, originalLineOffsets); + writeDataPerLine(originalLineOffsets, symbolId, startOffset, endOffset, result, startLine, new SymbolItemWriter()); + } + + private int binarySearchLine(int declarationStartOffset, long[] originalLineOffsets) { + int begin = 0; + int end = originalLineOffsets.length - 1; + while (begin < end) { + int mid = (int) Math.round((begin + end) / 2D); + if (declarationStartOffset < originalLineOffsets[mid]) { + end = mid - 1; + } else { + begin = mid; + } + } + return begin + 1; + } + + private void writeDataPerLine(long[] originalLineOffsets, G item, int globalStartOffset, int globalEndOffset, StringBuilder[] dataPerLine, int startLine, + RangeItemWriter writer) { + int currentLineIdx = startLine; // We know current rule starts on current line - long ruleStartOffsetCurrentLine = rule.getStartPosition(); - while (currentLineIdx < file.lines() && rule.getEndPosition() >= file.originalLineOffsets()[currentLineIdx]) { + long ruleStartOffsetCurrentLine = globalStartOffset; + while (currentLineIdx < originalLineOffsets.length && globalEndOffset >= originalLineOffsets[currentLineIdx]) { // rule continue on next line so write current line and continue on next line with same rule - writeRule(highlightingPerLine, currentLineIdx, ruleStartOffsetCurrentLine - file.originalLineOffsets()[currentLineIdx - 1], file.originalLineOffsets()[currentLineIdx] - - file.originalLineOffsets()[currentLineIdx - 1], - rule.getTextType()); + writeItem(item, dataPerLine, currentLineIdx, ruleStartOffsetCurrentLine - originalLineOffsets[currentLineIdx - 1], originalLineOffsets[currentLineIdx] + - originalLineOffsets[currentLineIdx - 1], writer); currentLineIdx++; - ruleStartOffsetCurrentLine = file.originalLineOffsets()[currentLineIdx - 1]; + ruleStartOffsetCurrentLine = originalLineOffsets[currentLineIdx - 1]; } // Rule ends on current line - writeRule(highlightingPerLine, currentLineIdx, ruleStartOffsetCurrentLine - file.originalLineOffsets()[currentLineIdx - 1], rule.getEndPosition() - - file.originalLineOffsets()[currentLineIdx - 1], - rule.getTextType()); + writeItem(item, dataPerLine, currentLineIdx, ruleStartOffsetCurrentLine - originalLineOffsets[currentLineIdx - 1], globalEndOffset + - originalLineOffsets[currentLineIdx - 1], writer); } - private void writeRule(StringBuilder[] highlightingPerLine, int currentLineIdx, long startLineOffset, long endLineOffset, TypeOfText textType) { - if (highlightingPerLine[currentLineIdx - 1] == null) { - highlightingPerLine[currentLineIdx - 1] = new StringBuilder(); + private void writeItem(G item, StringBuilder[] dataPerLine, int currentLineIdx, long startLineOffset, long endLineOffset, RangeItemWriter writer) { + if (dataPerLine[currentLineIdx - 1] == null) { + dataPerLine[currentLineIdx - 1] = new StringBuilder(); } - StringBuilder currentLineSb = highlightingPerLine[currentLineIdx - 1]; - writeRule(currentLineSb, startLineOffset, endLineOffset, textType); + StringBuilder currentLineSb = dataPerLine[currentLineIdx - 1]; + writer.writeItem(currentLineSb, startLineOffset, endLineOffset, item); } - private void writeRule(StringBuilder currentLineSb, long startLineOffset, long endLineOffset, TypeOfText textType) { - if (currentLineSb.length() > 0) { - currentLineSb.append(SyntaxHighlightingData.RULE_SEPARATOR); + private static interface RangeItemWriter { + /** + * Write item on a single line + */ + void writeItem(StringBuilder currentLineSb, long startLineOffset, long endLineOffset, G item); + } + + private static class RuleItemWriter implements RangeItemWriter { + + @Override + public void writeItem(StringBuilder currentLineSb, long startLineOffset, long endLineOffset, SyntaxHighlightingRule item) { + if (currentLineSb.length() > 0) { + currentLineSb.append(SyntaxHighlightingData.RULE_SEPARATOR); + } + currentLineSb.append(startLineOffset) + .append(SyntaxHighlightingData.FIELD_SEPARATOR) + .append(endLineOffset) + .append(SyntaxHighlightingData.FIELD_SEPARATOR) + .append(item.getTextType().cssClass()); } - currentLineSb.append(startLineOffset) - .append(SyntaxHighlightingData.FIELD_SEPARATOR) - .append(endLineOffset) - .append(SyntaxHighlightingData.FIELD_SEPARATOR) - .append(textType.cssClass()); + + } + + private static class SymbolItemWriter implements RangeItemWriter { + + @Override + public void writeItem(StringBuilder currentLineSb, long startLineOffset, long endLineOffset, Integer symbolId) { + if (currentLineSb.length() > 0) { + currentLineSb.append(SymbolData.SYMBOL_SEPARATOR); + } + currentLineSb.append(startLineOffset) + .append(SymbolData.FIELD_SEPARATOR) + .append(endLineOffset) + .append(SymbolData.FIELD_SEPARATOR) + .append(symbolId); + } + } private Map getLineMetric(DefaultInputFile file, String metricKey) { 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 2886cfbc28c..8499776bfb7 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,7 +80,6 @@ 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. @@ -407,7 +406,7 @@ public class BatchMediumTester { * @param symbolEndOffset 0-based end offset for the symbol in file */ @CheckForNull - public Set symbolReferencesFor(InputFile file, int symbolStartOffset, int symbolEndOffset) { + public List 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 ee8997c259c..555057d4d14 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 @@ -20,25 +20,24 @@ package org.sonar.batch.source; -import com.google.common.collect.SortedSetMultimap; -import com.google.common.collect.TreeMultimap; import org.sonar.api.batch.sensor.symbol.internal.DefaultSymbol; import org.sonar.api.source.Symbol; import org.sonar.api.source.Symbolizable; -import org.sonar.batch.symbol.DefaultSymbolTableBuilder; import java.util.ArrayList; +import java.util.LinkedHashMap; import java.util.List; +import java.util.Map; public class DefaultSymbolTable implements Symbolizable.SymbolTable { - private SortedSetMultimap referencesBySymbol; + private Map> referencesBySymbol; - private DefaultSymbolTable(SortedSetMultimap referencesBySymbol) { + private DefaultSymbolTable(Map> referencesBySymbol) { this.referencesBySymbol = referencesBySymbol; } - public SortedSetMultimap getReferencesBySymbol() { + public Map> getReferencesBySymbol() { return referencesBySymbol; } @@ -58,27 +57,29 @@ public class DefaultSymbolTable implements Symbolizable.SymbolTable { public static class Builder implements Symbolizable.SymbolTableBuilder { - private final SortedSetMultimap referencesBySymbol; + private final Map> referencesBySymbol = new LinkedHashMap>(); private final String componentKey; public Builder(String componentKey) { this.componentKey = componentKey; - referencesBySymbol = TreeMultimap.create(new DefaultSymbolTableBuilder.SymbolComparator(), new DefaultSymbolTableBuilder.ReferenceComparator()); } @Override public Symbol newSymbol(int fromOffset, int toOffset) { - Symbol symbol = new DefaultSymbol(componentKey, fromOffset, toOffset); - referencesBySymbol.put(symbol, symbol.getDeclarationStartOffset()); + Symbol symbol = new DefaultSymbol(fromOffset, toOffset); + referencesBySymbol.put(symbol, new ArrayList()); return symbol; } @Override public void newReference(Symbol symbol, int fromOffset) { + if (!referencesBySymbol.containsKey(symbol)) { + throw new UnsupportedOperationException("Cannot add reference to a symbol in another file"); + } if (fromOffset >= symbol.getDeclarationStartOffset() && fromOffset < symbol.getDeclarationEndOffset()) { throw new UnsupportedOperationException("Cannot add reference (" + fromOffset + ") overlapping " + symbol); } - referencesBySymbol.put(symbol, fromOffset); + referencesBySymbol.get(symbol).add(fromOffset); } @Override diff --git a/sonar-batch/src/main/java/org/sonar/batch/source/DefaultSymbolizable.java b/sonar-batch/src/main/java/org/sonar/batch/source/DefaultSymbolizable.java index de073e50629..8a6f6421408 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/source/DefaultSymbolizable.java +++ b/sonar-batch/src/main/java/org/sonar/batch/source/DefaultSymbolizable.java @@ -49,6 +49,6 @@ public class DefaultSymbolizable implements Symbolizable { @Override public void setSymbolTable(SymbolTable symbolTable) { SymbolData symbolData = new SymbolData(((DefaultSymbolTable) symbolTable).getReferencesBySymbol()); - cache.setStringData(component().key(), SnapshotDataTypes.SYMBOL_HIGHLIGHTING, symbolData.writeString()); + cache.setData(component().key(), SnapshotDataTypes.SYMBOL_HIGHLIGHTING, symbolData); } } 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 ed347e60bd0..a6e8c8c0aaf 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 @@ -20,8 +20,6 @@ package org.sonar.batch.symbol; -import com.google.common.collect.SortedSetMultimap; -import com.google.common.collect.TreeMultimap; import org.sonar.api.batch.sensor.symbol.Symbol; import org.sonar.api.batch.sensor.symbol.SymbolTableBuilder; import org.sonar.api.batch.sensor.symbol.internal.DefaultSymbol; @@ -29,43 +27,48 @@ 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; public class DefaultSymbolTableBuilder implements SymbolTableBuilder { private final String componentKey; private final ComponentDataCache cache; - private final SortedSetMultimap referencesBySymbol; + private final Map> referencesBySymbol = new LinkedHashMap>(); public DefaultSymbolTableBuilder(String componentKey, ComponentDataCache cache) { this.componentKey = componentKey; this.cache = cache; - this.referencesBySymbol = TreeMultimap.create(new SymbolComparator(), new ReferenceComparator()); } @Override public Symbol newSymbol(int fromOffset, int toOffset) { - Symbol symbol = new DefaultSymbol(componentKey, fromOffset, toOffset); - referencesBySymbol.put(symbol, symbol.getDeclarationStartOffset()); + org.sonar.api.source.Symbol symbol = new DefaultSymbol(fromOffset, toOffset); + referencesBySymbol.put(symbol, new ArrayList()); return symbol; } @Override public void newReference(Symbol symbol, int fromOffset) { - String otherComponentKey = ((DefaultSymbol) symbol).componentKey(); - if (!otherComponentKey.equals(componentKey)) { - throw new UnsupportedOperationException("Cannot add reference from (" + componentKey + ") to another file (" + otherComponentKey + ")"); + if (!referencesBySymbol.containsKey(symbol)) { + throw new UnsupportedOperationException("Cannot add reference to a symbol in another file"); } if (fromOffset >= symbol.getDeclarationStartOffset() && fromOffset < symbol.getDeclarationEndOffset()) { throw new UnsupportedOperationException("Cannot add reference (" + fromOffset + ") overlapping " + symbol); } - referencesBySymbol.put(symbol, fromOffset); + referencesBySymbol.get(symbol).add(fromOffset); + } + + public SymbolData build() { + return new SymbolData(referencesBySymbol); } @Override public void done() { - SymbolData symbolData = new SymbolData(referencesBySymbol); - cache.setData(componentKey, SnapshotDataTypes.SYMBOL_HIGHLIGHTING, symbolData); + cache.setData(componentKey, SnapshotDataTypes.SYMBOL_HIGHLIGHTING, build()); } public static class SymbolComparator implements Comparator, Serializable { 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 11ab5cca7e5..0f55e194e8b 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 @@ -20,24 +20,25 @@ package org.sonar.batch.symbol; -import com.google.common.collect.SortedSetMultimap; 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; public class SymbolData implements Data { - private static final String FIELD_SEPARATOR = ","; - private static final String SYMBOL_SEPARATOR = ";"; + public static final String FIELD_SEPARATOR = ","; + public static final String SYMBOL_SEPARATOR = ";"; - private final SortedSetMultimap referencesBySymbol; + private final Map> referencesBySymbol; - public SymbolData(SortedSetMultimap referencesBySymbol) { + public SymbolData(Map> referencesBySymbol) { this.referencesBySymbol = referencesBySymbol; } - public SortedSetMultimap referencesBySymbol() { + public Map> referencesBySymbol() { return referencesBySymbol; } @@ -46,15 +47,19 @@ public class SymbolData implements Data { StringBuilder sb = new StringBuilder(); for (Symbol symbol : referencesBySymbol.keySet()) { + if (sb.length() > 0) { + sb.append(SYMBOL_SEPARATOR); + } sb.append(symbol.getDeclarationStartOffset()) .append(FIELD_SEPARATOR) - .append(symbol.getDeclarationEndOffset()); + .append(symbol.getDeclarationEndOffset()) + .append(FIELD_SEPARATOR) + .append(symbol.getDeclarationStartOffset()); Collection symbolReferences = referencesBySymbol.get(symbol); for (Integer symbolReference : symbolReferences) { sb.append(FIELD_SEPARATOR).append(symbolReference); } - sb.append(SYMBOL_SEPARATOR); } return sb.toString(); 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 1c19e65b48c..9117015cca4 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 @@ -42,6 +42,7 @@ import org.sonar.batch.highlighting.SyntaxHighlightingDataBuilder; import org.sonar.batch.scan.filesystem.InputPathCache; import org.sonar.batch.scan.measure.MeasureCache; import org.sonar.batch.source.CodeColorizers; +import org.sonar.batch.symbol.DefaultSymbolTableBuilder; import org.sonar.core.persistence.AbstractDaoTestCase; import org.sonar.core.source.SnapshotDataTypes; import org.sonar.core.source.db.FileSourceDao; @@ -145,9 +146,9 @@ public class SourcePersisterTest extends AbstractDaoTestCase { assertThat(fileSourceDto.getCreatedAt()).isEqualTo(DateUtils.parseDateTime("2014-10-10T16:44:02+0200").getTime()); assertThat(fileSourceDto.getUpdatedAt()).isEqualTo(now.getTime()); assertThat(fileSourceDto.getData()).isEqualTo( - ",,,,,,,,,,,,,changed\r\n,,,,,,,,,,,,,content\r\n"); + ",,,,,,,,,,,,,,changed\r\n,,,,,,,,,,,,,,content\r\n"); assertThat(fileSourceDto.getLineHashes()).isEqualTo(md5Hex("changed") + "\n" + md5Hex("content")); - assertThat(fileSourceDto.getDataHash()).isEqualTo("00141b1194a360a5d5d1f9dcffb27359"); + assertThat(fileSourceDto.getDataHash()).isEqualTo("bd582d7001cfca180c3dacab10043292"); } @Test @@ -189,9 +190,9 @@ public class SourcePersisterTest extends AbstractDaoTestCase { assertThat(fileSourceDto.getCreatedAt()).isEqualTo(now.getTime()); assertThat(fileSourceDto.getUpdatedAt()).isEqualTo(now.getTime()); assertThat(fileSourceDto.getData()).isEqualTo( - ",,,,,,,,,,,,,foo\r\n,,,,,,,,,,,,,bar\r\n,,,,,,,,,,,,,biz\r\n"); + ",,,,,,,,,,,,,,foo\r\n,,,,,,,,,,,,,,bar\r\n,,,,,,,,,,,,,,biz\r\n"); assertThat(fileSourceDto.getLineHashes()).isEqualTo(md5Hex("foo") + "\n" + md5Hex("bar") + "\n" + md5Hex("biz")); - assertThat(fileSourceDto.getDataHash()).isEqualTo("e6860232a097eb0616b9fe1bad760941"); + assertThat(fileSourceDto.getDataHash()).isEqualTo("e1827ac156bb76144486e6570a591cfb"); } @@ -246,6 +247,16 @@ public class SourcePersisterTest extends AbstractDaoTestCase { when(componentDataCache.getData(PROJECT_KEY + ":" + relativePathNew, SnapshotDataTypes.SYNTAX_HIGHLIGHTING)) .thenReturn(highlighting); + DefaultSymbolTableBuilder symbolBuilder = new DefaultSymbolTableBuilder(PROJECT_KEY + ":" + relativePathNew, null); + org.sonar.api.batch.sensor.symbol.Symbol s1 = symbolBuilder.newSymbol(1, 2); + symbolBuilder.newReference(s1, 4); + symbolBuilder.newReference(s1, 11); + org.sonar.api.batch.sensor.symbol.Symbol s2 = symbolBuilder.newSymbol(4, 6); + symbolBuilder.newReference(s2, 0); + symbolBuilder.newReference(s2, 7); + when(componentDataCache.getData(PROJECT_KEY + ":" + relativePathNew, SnapshotDataTypes.SYMBOL_HIGHLIGHTING)) + .thenReturn(symbolBuilder.build()); + sourcePersister.persist(); FileSourceDto fileSourceDto = new FileSourceDao(getMyBatis()).select("uuidnew"); @@ -253,10 +264,10 @@ public class SourcePersisterTest extends AbstractDaoTestCase { assertThat(fileSourceDto.getUpdatedAt()).isEqualTo(now.getTime()); assertThat(fileSourceDto.getLineHashes()).isEqualTo(md5Hex("foo") + "\n" + md5Hex("bar") + "\n" + md5Hex("biz")); assertThat(fileSourceDto.getData()).isEqualTo( - "123,julien,2014-10-11T16:44:02+0100,1,4,2,2,5,3,3,6,4,\"0,3,a\",foo\r\n" - + "234,simon,2014-10-12T16:44:02+0100,,,,,,,,,,\"0,1,cd\",bar\r\n" - + "345,julien,2014-10-13T16:44:02+0100,0,,,0,,,0,,,\"0,9,c\",biz\r\n"); - assertThat(fileSourceDto.getDataHash()).isEqualTo("cb7bdbb98bd053c7367c92e6596b37c0"); + "123,julien,2014-10-11T16:44:02+0100,1,4,2,2,5,3,3,6,4,\"0,3,a\",\"1,2,1;0,2,2\",foo\r\n" + + "234,simon,2014-10-12T16:44:02+0100,,,,,,,,,,\"0,1,cd\",\"0,1,1;0,2,2\",bar\r\n" + + "345,julien,2014-10-13T16:44:02+0100,0,,,0,,,0,,,\"0,9,c\",\"4,5,1;0,2,2\",biz\r\n"); + assertThat(fileSourceDto.getDataHash()).isEqualTo("594752666dd282f4a3bb985829c790fa"); } @Test @@ -329,6 +340,25 @@ public class SourcePersisterTest extends AbstractDaoTestCase { assertThat(highlightingPerLine).containsOnly("0,3,a", "0,3,c;0,2,cd", "0,9,c;1,8,k"); } + @Test + public void testSimpleConversionOfSymbolOffset() { + 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 s1 = symbolBuilder.newSymbol(1, 2); + symbolBuilder.newReference(s1, 4); + symbolBuilder.newReference(s1, 11); + org.sonar.api.batch.sensor.symbol.Symbol s2 = symbolBuilder.newSymbol(4, 6); + symbolBuilder.newReference(s2, 0); + symbolBuilder.newReference(s2, 7); + + 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/mediumtest/symbol/SymbolMediumTest.java b/sonar-batch/src/test/java/org/sonar/batch/mediumtest/symbol/SymbolMediumTest.java index 10c9f37f47f..5c5227547a2 100644 --- a/sonar-batch/src/test/java/org/sonar/batch/mediumtest/symbol/SymbolMediumTest.java +++ b/sonar-batch/src/test/java/org/sonar/batch/mediumtest/symbol/SymbolMediumTest.java @@ -81,7 +81,7 @@ public class SymbolMediumTest { .start(); InputFile file = result.inputFile("src/sample.xoo"); - assertThat(result.symbolReferencesFor(file, 7, 10)).containsOnly(7, 27); + assertThat(result.symbolReferencesFor(file, 7, 10)).containsOnly(27); } } diff --git a/sonar-batch/src/test/java/org/sonar/batch/source/DefaultSymbolTableTest.java b/sonar-batch/src/test/java/org/sonar/batch/source/DefaultSymbolTableTest.java index a1058b27d64..09394231a34 100644 --- a/sonar-batch/src/test/java/org/sonar/batch/source/DefaultSymbolTableTest.java +++ b/sonar-batch/src/test/java/org/sonar/batch/source/DefaultSymbolTableTest.java @@ -44,10 +44,10 @@ public class DefaultSymbolTableTest { symbolTableBuilder.newReference(thirdSymbol, 70); Symbolizable.SymbolTable symbolTable = symbolTableBuilder.build(); - assertThat(symbolTable.symbols()).containsExactly(firstSymbol, thirdSymbol, secondSymbol); - assertThat(symbolTable.references(firstSymbol)).containsExactly(10, 32); - assertThat(symbolTable.references(secondSymbol)).containsExactly(84, 124); - assertThat(symbolTable.references(thirdSymbol)).containsExactly(55, 70); + assertThat(symbolTable.symbols()).containsExactly(firstSymbol, secondSymbol, thirdSymbol); + assertThat(symbolTable.references(firstSymbol)).containsExactly(32); + assertThat(symbolTable.references(secondSymbol)).containsExactly(124); + assertThat(symbolTable.references(thirdSymbol)).containsExactly(70); } @Test @@ -64,6 +64,6 @@ public class DefaultSymbolTableTest { Symbolizable.SymbolTableBuilder symbolTableBuilder = new DefaultSymbolTable.Builder("foo"); Symbol symbol = symbolTableBuilder.newSymbol(10, 20); - assertThat(symbol.toString()).isEqualTo("Symbol{component=foo, offset=10-20}"); + assertThat(symbol.toString()).isEqualTo("Symbol{offset=10-20}"); } } diff --git a/sonar-batch/src/test/java/org/sonar/batch/source/DefaultSymbolizableTest.java b/sonar-batch/src/test/java/org/sonar/batch/source/DefaultSymbolizableTest.java index 40cf108abd0..940c47ed46e 100644 --- a/sonar-batch/src/test/java/org/sonar/batch/source/DefaultSymbolizableTest.java +++ b/sonar-batch/src/test/java/org/sonar/batch/source/DefaultSymbolizableTest.java @@ -25,9 +25,14 @@ import org.sonar.api.component.Component; import org.sonar.api.source.Symbol; import org.sonar.api.source.Symbolizable; import org.sonar.batch.index.ComponentDataCache; +import org.sonar.batch.symbol.SymbolData; import org.sonar.core.source.SnapshotDataTypes; -import static org.mockito.Mockito.*; +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; public class DefaultSymbolizableTest { @@ -52,6 +57,6 @@ public class DefaultSymbolizableTest { symbolPerspective.setSymbolTable(symbolTable); - verify(cache).setStringData("myComponent", SnapshotDataTypes.SYMBOL_HIGHLIGHTING, "4,8,4,12,70;25,33,25,44,60,108;"); + verify(cache).setData(eq("myComponent"), eq(SnapshotDataTypes.SYMBOL_HIGHLIGHTING), any(SymbolData.class)); } } 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 c661da30af1..aa8c67edc56 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 @@ -20,7 +20,6 @@ package org.sonar.batch.symbol; -import com.google.common.collect.SortedSetMultimap; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; @@ -31,6 +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 static org.fest.assertions.Assertions.assertThat; import static org.mockito.Matchers.eq; @@ -43,7 +44,7 @@ public class DefaultSymbolTableBuilderTest { public ExpectedException throwable = ExpectedException.none(); @Test - public void should_order_symbol_and_references() throws Exception { + public void should_write_symbol_and_references() throws Exception { ComponentDataCache componentDataCache = mock(ComponentDataCache.class); SymbolTableBuilder symbolTableBuilder = new DefaultSymbolTableBuilder("foo", componentDataCache); Symbol firstSymbol = symbolTableBuilder.newSymbol(10, 20); @@ -57,14 +58,14 @@ public class DefaultSymbolTableBuilderTest { ArgumentCaptor argCaptor = ArgumentCaptor.forClass(SymbolData.class); verify(componentDataCache).setData(eq("foo"), eq(SnapshotDataTypes.SYMBOL_HIGHLIGHTING), argCaptor.capture()); - SortedSetMultimap referencesBySymbol = argCaptor.getValue().referencesBySymbol(); + Map> referencesBySymbol = argCaptor.getValue().referencesBySymbol(); - assertThat(new ArrayList(referencesBySymbol.keySet())).containsExactly(firstSymbol, thirdSymbol, secondSymbol); - assertThat(new ArrayList(referencesBySymbol.get(firstSymbol))).containsExactly(10, 32); - assertThat(new ArrayList(referencesBySymbol.get(secondSymbol))).containsExactly(84, 124); - assertThat(new ArrayList(referencesBySymbol.get(thirdSymbol))).containsExactly(55, 70); + assertThat(new ArrayList(referencesBySymbol.keySet())).containsExactly(firstSymbol, secondSymbol, thirdSymbol); + assertThat(new ArrayList(referencesBySymbol.get(firstSymbol))).containsExactly(32); + assertThat(new ArrayList(referencesBySymbol.get(secondSymbol))).containsExactly(124); + assertThat(new ArrayList(referencesBySymbol.get(thirdSymbol))).containsExactly(70); - assertThat(argCaptor.getValue().writeString()).isEqualTo("10,20,10,32;55,62,55,70;84,92,84,124;"); + assertThat(argCaptor.getValue().writeString()).isEqualTo("10,20,10,32;84,92,84,124;55,62,55,70"); } @Test @@ -78,7 +79,7 @@ public class DefaultSymbolTableBuilderTest { ArgumentCaptor argCaptor = ArgumentCaptor.forClass(SymbolData.class); verify(componentDataCache).setData(eq("foo"), eq(SnapshotDataTypes.SYMBOL_HIGHLIGHTING), argCaptor.capture()); - assertThat(argCaptor.getValue().writeString()).isEqualTo("10,20,10;"); + assertThat(argCaptor.getValue().writeString()).isEqualTo("10,20,10"); } @Test diff --git a/sonar-batch/src/test/resources/org/sonar/batch/index/SourcePersisterTest/file_sources.xml b/sonar-batch/src/test/resources/org/sonar/batch/index/SourcePersisterTest/file_sources.xml index cab8d665a7f..8c17f16c453 100644 --- a/sonar-batch/src/test/resources/org/sonar/batch/index/SourcePersisterTest/file_sources.xml +++ b/sonar-batch/src/test/resources/org/sonar/batch/index/SourcePersisterTest/file_sources.xml @@ -1,8 +1,8 @@ diff --git a/sonar-batch/src/test/resources/org/sonar/batch/index/SourcePersisterTest/testPersistDontTouchUnchanged-result.xml b/sonar-batch/src/test/resources/org/sonar/batch/index/SourcePersisterTest/testPersistDontTouchUnchanged-result.xml index 1b5e638558f..c4a070d9ade 100644 --- a/sonar-batch/src/test/resources/org/sonar/batch/index/SourcePersisterTest/testPersistDontTouchUnchanged-result.xml +++ b/sonar-batch/src/test/resources/org/sonar/batch/index/SourcePersisterTest/testPersistDontTouchUnchanged-result.xml @@ -1,9 +1,9 @@ diff --git a/sonar-batch/src/test/resources/org/sonar/batch/index/SourcePersisterTest/testPersistEmptyFile-result.xml b/sonar-batch/src/test/resources/org/sonar/batch/index/SourcePersisterTest/testPersistEmptyFile-result.xml index caaae04c00c..130cede5b5d 100644 --- a/sonar-batch/src/test/resources/org/sonar/batch/index/SourcePersisterTest/testPersistEmptyFile-result.xml +++ b/sonar-batch/src/test/resources/org/sonar/batch/index/SourcePersisterTest/testPersistEmptyFile-result.xml @@ -1,8 +1,8 @@