@@ -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()}; |
@@ -1,7 +1,7 @@ | |||
<dataset> | |||
<file_sources id="1" project_uuid="uuid-MyProject" file_uuid="uuid-MyFile.xoo" created_at="1416238020000" updated_at="1414770242000" | |||
data="aef12a,alice,2014-04-25T12:34:56+0100,1,4,2,2,5,3,3,6,4,,class Foo { abe465,bob,2014-07-25T12:34:56+0100,,,,,,,,,,, // Empty afb789,carol,2014-03-23T12:34:56+0100,0,,,0,,,0,,,,} afb789,carol,2014-03-23T12:34:56+0100,,,,,,,,,,, " | |||
data="aef12a,alice,2014-04-25T12:34:56+0100,1,4,2,2,5,3,3,6,4,,,class Foo { abe465,bob,2014-07-25T12:34:56+0100,,,,,,,,,,,, // Empty afb789,carol,2014-03-23T12:34:56+0100,0,,,0,,,0,,,,,} afb789,carol,2014-03-23T12:34:56+0100,,,,,,,,,,,, " | |||
line_hashes="6a19ce786467960a3a9b0d26383a464a aab2dbc5fdeaa80b050b1d049ede357c cbb184dd8e05c9709e5dcaedaa0495cf " | |||
data_hash="" /> | |||
@@ -1,7 +1,7 @@ | |||
<dataset> | |||
<file_sources id="1" project_uuid="uuid-MyProject" file_uuid="uuid-MyFile.xoo" created_at="1416238020000" updated_at="1414770242000" | |||
data=",,,,,,,,,,,,,class Foo { ,,,,,,,,,,,,, // Empty ,,,,,,,,,,,,,} ,,,,,,,,,,,,, " | |||
data=",,,,,,,,,,,,,,class Foo { ,,,,,,,,,,,,,, // Empty ,,,,,,,,,,,,,,} ,,,,,,,,,,,,,, " | |||
line_hashes="6a19ce786467960a3a9b0d26383a464a aab2dbc5fdeaa80b050b1d049ede357c cbb184dd8e05c9709e5dcaedaa0495cf " | |||
data_hash="" /> | |||
@@ -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<Integer, String> 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 <G> void writeDataPerLine(long[] originalLineOffsets, G item, int globalStartOffset, int globalEndOffset, StringBuilder[] dataPerLine, int startLine, | |||
RangeItemWriter<G> 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 <G> void writeItem(G item, StringBuilder[] dataPerLine, int currentLineIdx, long startLineOffset, long endLineOffset, RangeItemWriter<G> 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<G> { | |||
/** | |||
* Write item on a single line | |||
*/ | |||
void writeItem(StringBuilder currentLineSb, long startLineOffset, long endLineOffset, G item); | |||
} | |||
private static class RuleItemWriter implements RangeItemWriter<SyntaxHighlightingRule> { | |||
@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<Integer> { | |||
@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<Integer, String> getLineMetric(DefaultInputFile file, String metricKey) { |
@@ -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<Integer> symbolReferencesFor(InputFile file, int symbolStartOffset, int symbolEndOffset) { | |||
public List<Integer> symbolReferencesFor(InputFile file, int symbolStartOffset, int symbolEndOffset) { | |||
SymbolData data = symbolTablePerFile.get(file); | |||
if (data == null) { | |||
return null; |
@@ -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<org.sonar.api.batch.sensor.symbol.Symbol, Integer> referencesBySymbol; | |||
private Map<Symbol, List<Integer>> referencesBySymbol; | |||
private DefaultSymbolTable(SortedSetMultimap<org.sonar.api.batch.sensor.symbol.Symbol, Integer> referencesBySymbol) { | |||
private DefaultSymbolTable(Map<Symbol, List<Integer>> referencesBySymbol) { | |||
this.referencesBySymbol = referencesBySymbol; | |||
} | |||
public SortedSetMultimap<org.sonar.api.batch.sensor.symbol.Symbol, Integer> getReferencesBySymbol() { | |||
public Map<Symbol, List<Integer>> getReferencesBySymbol() { | |||
return referencesBySymbol; | |||
} | |||
@@ -58,27 +57,29 @@ public class DefaultSymbolTable implements Symbolizable.SymbolTable { | |||
public static class Builder implements Symbolizable.SymbolTableBuilder { | |||
private final SortedSetMultimap<org.sonar.api.batch.sensor.symbol.Symbol, Integer> referencesBySymbol; | |||
private final Map<Symbol, List<Integer>> referencesBySymbol = new LinkedHashMap<Symbol, List<Integer>>(); | |||
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<Integer>()); | |||
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 |
@@ -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); | |||
} | |||
} |
@@ -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<Symbol, Integer> referencesBySymbol; | |||
private final Map<org.sonar.api.source.Symbol, List<Integer>> referencesBySymbol = new LinkedHashMap<org.sonar.api.source.Symbol, List<Integer>>(); | |||
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<Integer>()); | |||
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<Symbol>, Serializable { |
@@ -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<Symbol, Integer> referencesBySymbol; | |||
private final Map<org.sonar.api.source.Symbol, List<Integer>> referencesBySymbol; | |||
public SymbolData(SortedSetMultimap<Symbol, Integer> referencesBySymbol) { | |||
public SymbolData(Map<org.sonar.api.source.Symbol, List<Integer>> referencesBySymbol) { | |||
this.referencesBySymbol = referencesBySymbol; | |||
} | |||
public SortedSetMultimap<Symbol, Integer> referencesBySymbol() { | |||
public Map<org.sonar.api.source.Symbol, List<Integer>> 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<Integer> symbolReferences = referencesBySymbol.get(symbol); | |||
for (Integer symbolReference : symbolReferences) { | |||
sb.append(FIELD_SEPARATOR).append(symbolReference); | |||
} | |||
sb.append(SYMBOL_SEPARATOR); | |||
} | |||
return sb.toString(); |
@@ -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); |
@@ -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); | |||
} | |||
} |
@@ -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}"); | |||
} | |||
} |
@@ -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)); | |||
} | |||
} |
@@ -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<SymbolData> argCaptor = ArgumentCaptor.forClass(SymbolData.class); | |||
verify(componentDataCache).setData(eq("foo"), eq(SnapshotDataTypes.SYMBOL_HIGHLIGHTING), argCaptor.capture()); | |||
SortedSetMultimap<Symbol, Integer> referencesBySymbol = argCaptor.getValue().referencesBySymbol(); | |||
Map<org.sonar.api.source.Symbol, List<Integer>> referencesBySymbol = argCaptor.getValue().referencesBySymbol(); | |||
assertThat(new ArrayList<Symbol>(referencesBySymbol.keySet())).containsExactly(firstSymbol, thirdSymbol, secondSymbol); | |||
assertThat(new ArrayList<Integer>(referencesBySymbol.get(firstSymbol))).containsExactly(10, 32); | |||
assertThat(new ArrayList<Integer>(referencesBySymbol.get(secondSymbol))).containsExactly(84, 124); | |||
assertThat(new ArrayList<Integer>(referencesBySymbol.get(thirdSymbol))).containsExactly(55, 70); | |||
assertThat(new ArrayList<Symbol>(referencesBySymbol.keySet())).containsExactly(firstSymbol, secondSymbol, thirdSymbol); | |||
assertThat(new ArrayList<Integer>(referencesBySymbol.get(firstSymbol))).containsExactly(32); | |||
assertThat(new ArrayList<Integer>(referencesBySymbol.get(secondSymbol))).containsExactly(124); | |||
assertThat(new ArrayList<Integer>(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<SymbolData> 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 |
@@ -1,8 +1,8 @@ | |||
<dataset> | |||
<file_sources id="101" project_uuid="projectUuid" file_uuid="uuidsame" | |||
data=",,,,,,,,,,,,,unchanged ,,,,,,,,,,,,,content " | |||
data=",,,,,,,,,,,,,,unchanged ,,,,,,,,,,,,,,content " | |||
line_hashes="8d7b3d6b83c0a517eac07e1aac94b773 9a0364b9e99bb480dd25e1f0284c8555" | |||
data_hash="4fc300c67452d04e8852cbc315e16010" | |||
data_hash="21a2d025d55b25d6412b1565afb516a5" | |||
created_at="1412952242000" updated_at="1412952242000" /> | |||
</dataset> |
@@ -1,9 +1,9 @@ | |||
<dataset> | |||
<file_sources id="101" project_uuid="projectUuid" file_uuid="uuidsame" | |||
data=",,,,,,,,,,,,,unchanged ,,,,,,,,,,,,,content " | |||
data=",,,,,,,,,,,,,,unchanged ,,,,,,,,,,,,,,content " | |||
line_hashes="8d7b3d6b83c0a517eac07e1aac94b773 9a0364b9e99bb480dd25e1f0284c8555" | |||
data_hash="4fc300c67452d04e8852cbc315e16010" | |||
data_hash="21a2d025d55b25d6412b1565afb516a5" | |||
created_at="1412952242000" updated_at="1412952242000" /> | |||
</dataset> |
@@ -1,8 +1,8 @@ | |||
<dataset> | |||
<file_sources id="101" project_uuid="projectUuid" file_uuid="uuidsame" | |||
data=",,,,,,,,,,,,,unchanged ,,,,,,,,,,,,,content " | |||
data=",,,,,,,,,,,,,,unchanged ,,,,,,,,,,,,,,content " | |||
line_hashes="8d7b3d6b83c0a517eac07e1aac94b773 9a0364b9e99bb480dd25e1f0284c8555" | |||
data_hash="4fc300c67452d04e8852cbc315e16010" | |||
data_hash="21a2d025d55b25d6412b1565afb516a5" | |||
created_at="1412952242000" updated_at="1412952242000" /> | |||
<file_sources id="102" project_uuid="projectUuid" file_uuid="uuidempty" data="[null]" |
@@ -27,20 +27,14 @@ import java.io.Serializable; | |||
public class DefaultSymbol implements Symbol, org.sonar.api.source.Symbol, Serializable { | |||
private final String componentKey; | |||
private final int declarationStartOffset; | |||
private final int declarationEndOffset; | |||
public DefaultSymbol(String componentKey, int startOffset, int endOffset) { | |||
this.componentKey = componentKey; | |||
public DefaultSymbol(int startOffset, int endOffset) { | |||
this.declarationStartOffset = startOffset; | |||
this.declarationEndOffset = endOffset; | |||
} | |||
public String componentKey() { | |||
return componentKey; | |||
} | |||
@Override | |||
public int getDeclarationStartOffset() { | |||
return declarationStartOffset; | |||
@@ -59,7 +53,6 @@ public class DefaultSymbol implements Symbol, org.sonar.api.source.Symbol, Seria | |||
@Override | |||
public String toString() { | |||
return Objects.toStringHelper("Symbol") | |||
.add("component", componentKey) | |||
.add("offset", String.format("%d-%d", declarationStartOffset, declarationEndOffset)) | |||
.toString(); | |||
} |