@@ -81,17 +81,36 @@ public class SymbolReferencesSensor implements Sensor { | |||
private static void processLine(File symbolFile, int lineNumber, Symbolizable.SymbolTableBuilder symbolTableBuilder, String line) { | |||
try { | |||
Iterator<String> split = Splitter.on(",").split(line).iterator(); | |||
int startOffset = Integer.parseInt(split.next()); | |||
int endOffset = Integer.parseInt(split.next()); | |||
Symbol s = symbolTableBuilder.newSymbol(startOffset, endOffset); | |||
Symbol s = addSymbol(symbolTableBuilder, split.next()); | |||
while (split.hasNext()) { | |||
symbolTableBuilder.newReference(s, Integer.parseInt(split.next())); | |||
addReference(symbolTableBuilder, s, split.next()); | |||
} | |||
} catch (Exception e) { | |||
throw new IllegalStateException("Error processing line " + lineNumber + " of file " + symbolFile.getAbsolutePath(), e); | |||
} | |||
} | |||
private static void addReference(Symbolizable.SymbolTableBuilder symbolTableBuilder, Symbol s, String str) { | |||
if (str.contains(":")) { | |||
Iterator<String> split = Splitter.on(":").split(str).iterator(); | |||
int startOffset = Integer.parseInt(split.next()); | |||
int toOffset = Integer.parseInt(split.next()); | |||
symbolTableBuilder.newReference(s, startOffset, toOffset); | |||
} else { | |||
symbolTableBuilder.newReference(s, Integer.parseInt(str)); | |||
} | |||
} | |||
private static Symbol addSymbol(Symbolizable.SymbolTableBuilder symbolTableBuilder, String str) { | |||
Iterator<String> split = Splitter.on(":").split(str).iterator(); | |||
int startOffset = Integer.parseInt(split.next()); | |||
int endOffset = Integer.parseInt(split.next()); | |||
return symbolTableBuilder.newSymbol(startOffset, endOffset); | |||
} | |||
@Override | |||
public void describe(SensorDescriptor descriptor) { | |||
descriptor |
@@ -74,7 +74,7 @@ public class SymbolReferencesSensorTest { | |||
@Test | |||
public void testExecution() throws IOException { | |||
File symbol = new File(baseDir, "src/foo.xoo.symbol"); | |||
FileUtils.write(symbol, "1,4,7\n12,15,23\n\n#comment"); | |||
FileUtils.write(symbol, "1:4,7\n12:15,23:33\n\n#comment"); | |||
DefaultInputFile inputFile = new DefaultInputFile("foo", "src/foo.xoo").setLanguage("xoo"); | |||
fileSystem.add(inputFile); | |||
Symbolizable symbolizable = mock(Symbolizable.class); | |||
@@ -92,7 +92,7 @@ public class SymbolReferencesSensorTest { | |||
verify(symbolTableBuilder).newSymbol(1, 4); | |||
verify(symbolTableBuilder).newReference(symbol1, 7); | |||
verify(symbolTableBuilder).newSymbol(12, 15); | |||
verify(symbolTableBuilder).newReference(symbol2, 23); | |||
verify(symbolTableBuilder).newReference(symbol2, 23, 33); | |||
} | |||
} |
@@ -99,10 +99,15 @@ public class DefaultSymbolTable implements Symbolizable.SymbolTable { | |||
@Override | |||
public void newReference(Symbol symbol, int fromOffset) { | |||
newReference(symbol, fromOffset, fromOffset + ((DefaultSymbol) symbol).getLength()); | |||
} | |||
@Override | |||
public void newReference(Symbol symbol, int fromOffset, int toOffset) { | |||
if (!referencesBySymbol.containsKey(symbol)) { | |||
throw new UnsupportedOperationException("Cannot add reference to a symbol in another file"); | |||
} | |||
TextRange referenceRange = inputFile.newRange(fromOffset, fromOffset + ((DefaultSymbol) symbol).getLength()); | |||
TextRange referenceRange = inputFile.newRange(fromOffset, toOffset); | |||
if (referenceRange.overlap(((DefaultSymbol) symbol).range())) { | |||
throw new UnsupportedOperationException("Cannot add reference (" + fromOffset + ") overlapping " + symbol + " in " + inputFile.key()); |
@@ -45,6 +45,11 @@ public class DefaultSymbolizable implements Symbolizable { | |||
// Do nothing | |||
} | |||
@Override | |||
public void newReference(Symbol symbol, int fromOffset, int toOffset) { | |||
// Do nothing | |||
} | |||
@Override | |||
public SymbolTable build() { | |||
return NO_OP_SYMBOL_TABLE; |
@@ -66,7 +66,7 @@ public class SymbolMediumTest { | |||
File xooSymbolFile = new File(srcDir, "sample.xoo.symbol"); | |||
FileUtils.write(xooFile, "Sample xoo\ncontent\nanother xoo"); | |||
// Highlight xoo symbol | |||
FileUtils.write(xooSymbolFile, "7,10,27"); | |||
FileUtils.write(xooSymbolFile, "7:10,27"); | |||
TaskResult result = tester.newTask() | |||
.properties(ImmutableMap.<String, String>builder() | |||
@@ -84,4 +84,33 @@ public class SymbolMediumTest { | |||
assertThat(result.symbolReferencesFor(file, 1, 7)).containsOnly(BatchReport.TextRange.newBuilder().setStartLine(3).setStartOffset(8).setEndLine(3).setEndOffset(11).build()); | |||
} | |||
@Test | |||
public void computeSymbolReferencesWithVariableLength() throws IOException { | |||
File baseDir = temp.getRoot(); | |||
File srcDir = new File(baseDir, "src"); | |||
srcDir.mkdir(); | |||
File xooFile = new File(srcDir, "sample.xoo"); | |||
File xooSymbolFile = new File(srcDir, "sample.xoo.symbol"); | |||
FileUtils.write(xooFile, "Sample xoo\ncontent\nanother xoo\nyet another"); | |||
// Highlight xoo symbol | |||
FileUtils.write(xooSymbolFile, "7:10,27:32"); | |||
TaskResult result = tester.newTask() | |||
.properties(ImmutableMap.<String, String>builder() | |||
.put("sonar.task", "scan") | |||
.put("sonar.projectBaseDir", baseDir.getAbsolutePath()) | |||
.put("sonar.projectKey", "com.foo.project") | |||
.put("sonar.projectName", "Foo Project") | |||
.put("sonar.projectVersion", "1.0-SNAPSHOT") | |||
.put("sonar.projectDescription", "Description of Foo Project") | |||
.put("sonar.sources", "src") | |||
.build()) | |||
.start(); | |||
InputFile file = result.inputFile("src/sample.xoo"); | |||
assertThat(result.symbolReferencesFor(file, 1, 7)).containsOnly(BatchReport.TextRange.newBuilder().setStartLine(3).setStartOffset(8).setEndLine(4).setEndOffset(1).build()); | |||
} | |||
} |
@@ -20,8 +20,12 @@ | |||
package org.sonar.batch.source; | |||
import org.sonar.api.batch.fs.TextRange; | |||
import com.google.common.base.Strings; | |||
import java.io.StringReader; | |||
import java.util.Set; | |||
import org.junit.Before; | |||
import org.junit.Rule; | |||
import org.junit.Test; | |||
@@ -30,7 +34,6 @@ import org.sonar.api.batch.fs.internal.DefaultInputFile; | |||
import org.sonar.api.batch.fs.internal.FileMetadata; | |||
import org.sonar.api.source.Symbol; | |||
import org.sonar.api.source.Symbolizable; | |||
import static org.assertj.core.api.Assertions.assertThat; | |||
public class DefaultSymbolTableTest { | |||
@@ -60,6 +63,25 @@ public class DefaultSymbolTableTest { | |||
assertThat(symbolTable.symbols()).containsExactly(firstSymbol, secondSymbol, thirdSymbol); | |||
} | |||
@Test | |||
public void variable_length_references() { | |||
Symbolizable.SymbolTableBuilder symbolTableBuilder = new DefaultSymbolTable.Builder(inputFile); | |||
Symbol firstSymbol = symbolTableBuilder.newSymbol(10, 20); | |||
symbolTableBuilder.newReference(firstSymbol, 32); | |||
symbolTableBuilder.newReference(firstSymbol, 44, 47); | |||
DefaultSymbolTable symbolTable = (DefaultSymbolTable) symbolTableBuilder.build(); | |||
assertThat(symbolTable.symbols()).containsExactly(firstSymbol); | |||
Set<TextRange> references = symbolTable.getReferencesBySymbol().get(firstSymbol); | |||
assertThat(references).containsExactly(range(32, 42), range(44, 47)); | |||
} | |||
private TextRange range(int start, int end) { | |||
return inputFile.newRange(start, end); | |||
} | |||
@Test | |||
public void should_reject_reference_conflicting_with_declaration() { | |||
throwable.expect(UnsupportedOperationException.class); |
@@ -33,11 +33,29 @@ public interface Symbolizable extends Perspective { | |||
interface SymbolTableBuilder { | |||
/** | |||
* Creates a new Symbol. | |||
* The offsets are global in the file. | |||
*/ | |||
Symbol newSymbol(int fromOffset, int toOffset); | |||
/** | |||
* Creates a new reference for a symbol. | |||
* The length of the reference is assumed to be the same as the symbol's length. | |||
*/ | |||
void newReference(Symbol symbol, int fromOffset); | |||
/** | |||
* Creates a new reference for a symbol. | |||
* The offsets are global in the file. | |||
*/ | |||
void newReference(Symbol symbol, int fromOffset, int toOffset); | |||
/** | |||
* Creates a {@link SymbolTable} containing all symbols and references previously created in this file. | |||
*/ | |||
SymbolTable build(); | |||
} | |||
interface SymbolTable { |