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;
StringBuilder[] symbolRefsPerLine = new StringBuilder[file.lines()];
long[] originalLineOffsets = file.originalLineOffsets();
int symbolId = 1;
- for (Symbol symbol : symbolRefs.referencesBySymbol().keySet()) {
+ List<Symbol> symbols = new ArrayList<Symbol>(symbolRefs.referencesBySymbol().keySet());
+ // Sort symbols to avoid false variation that would lead to an unnecessary update
+ Collections.sort(symbols, new Comparator<Symbol>() {
+ @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;
import java.util.List;
import java.util.Map;
import java.util.Properties;
+import java.util.Set;
/**
* Main utility class for writing batch medium tests.
* @param symbolEndOffset 0-based end offset for the symbol in file
*/
@CheckForNull
- public List<Integer> symbolReferencesFor(InputFile file, int symbolStartOffset, int symbolEndOffset) {
+ public Set<Integer> symbolReferencesFor(InputFile file, int symbolStartOffset, int symbolEndOffset) {
SymbolData data = symbolTablePerFile.get(file);
if (data == null) {
return null;
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<Symbol, List<Integer>> referencesBySymbol;
+ private Map<Symbol, Set<Integer>> referencesBySymbol;
- private DefaultSymbolTable(Map<Symbol, List<Integer>> referencesBySymbol) {
+ private DefaultSymbolTable(Map<Symbol, Set<Integer>> referencesBySymbol) {
this.referencesBySymbol = referencesBySymbol;
}
- public Map<Symbol, List<Integer>> getReferencesBySymbol() {
+ public Map<Symbol, Set<Integer>> getReferencesBySymbol() {
return referencesBySymbol;
}
public static class Builder implements Symbolizable.SymbolTableBuilder {
- private final Map<Symbol, List<Integer>> referencesBySymbol = new LinkedHashMap<Symbol, List<Integer>>();
+ private final Map<Symbol, Set<Integer>> referencesBySymbol = new LinkedHashMap<Symbol, Set<Integer>>();
private final String componentKey;
public Builder(String componentKey) {
@Override
public Symbol newSymbol(int fromOffset, int toOffset) {
Symbol symbol = new DefaultSymbol(fromOffset, toOffset);
- referencesBySymbol.put(symbol, new ArrayList<Integer>());
+ referencesBySymbol.put(symbol, new TreeSet<Integer>());
return symbol;
}
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<org.sonar.api.source.Symbol, List<Integer>> referencesBySymbol = new LinkedHashMap<org.sonar.api.source.Symbol, List<Integer>>();
+ private final Map<org.sonar.api.source.Symbol, Set<Integer>> referencesBySymbol = new LinkedHashMap<org.sonar.api.source.Symbol, Set<Integer>>();
public DefaultSymbolTableBuilder(String componentKey, ComponentDataCache cache) {
this.componentKey = componentKey;
@Override
public Symbol newSymbol(int fromOffset, int toOffset) {
org.sonar.api.source.Symbol symbol = new DefaultSymbol(fromOffset, toOffset);
- referencesBySymbol.put(symbol, new ArrayList<Integer>());
+ referencesBySymbol.put(symbol, new TreeSet<Integer>());
return 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<org.sonar.api.source.Symbol, List<Integer>> referencesBySymbol;
+ private final Map<org.sonar.api.source.Symbol, Set<Integer>> referencesBySymbol;
- public SymbolData(Map<org.sonar.api.source.Symbol, List<Integer>> referencesBySymbol) {
+ public SymbolData(Map<org.sonar.api.source.Symbol, Set<Integer>> referencesBySymbol) {
this.referencesBySymbol = referencesBySymbol;
}
- public Map<org.sonar.api.source.Symbol, List<Integer>> referencesBySymbol() {
+ public Map<org.sonar.api.source.Symbol, Set<Integer>> referencesBySymbol() {
return referencesBySymbol;
}
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);
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;
ArgumentCaptor<SymbolData> argCaptor = ArgumentCaptor.forClass(SymbolData.class);
verify(componentDataCache).setData(eq("foo"), eq(SnapshotDataTypes.SYMBOL_HIGHLIGHTING), argCaptor.capture());
- Map<org.sonar.api.source.Symbol, List<Integer>> referencesBySymbol = argCaptor.getValue().referencesBySymbol();
+ Map<org.sonar.api.source.Symbol, Set<Integer>> referencesBySymbol = argCaptor.getValue().referencesBySymbol();
assertThat(new ArrayList<Symbol>(referencesBySymbol.keySet())).containsExactly(firstSymbol, secondSymbol, thirdSymbol);
assertThat(new ArrayList<Integer>(referencesBySymbol.get(firstSymbol))).containsExactly(32);