diff options
7 files changed, 212 insertions, 34 deletions
diff --git a/sonar-batch-protocol/src/main/java/org/sonar/batch/protocol/output/BatchReportWriter.java b/sonar-batch-protocol/src/main/java/org/sonar/batch/protocol/output/BatchReportWriter.java index 97b7e5f2caa..86ec1c53f6e 100644 --- a/sonar-batch-protocol/src/main/java/org/sonar/batch/protocol/output/BatchReportWriter.java +++ b/sonar-batch-protocol/src/main/java/org/sonar/batch/protocol/output/BatchReportWriter.java @@ -118,4 +118,8 @@ public class BatchReportWriter { ProtobufUtil.writeMessagesToFile(coverageList, file); } + public File getSourceFile(int componentRef) { + return fileStructure.fileFor(FileStructure.Domain.SOURCE, componentRef); + } + } diff --git a/sonar-batch-protocol/src/main/java/org/sonar/batch/protocol/output/FileStructure.java b/sonar-batch-protocol/src/main/java/org/sonar/batch/protocol/output/FileStructure.java index 21df5a4cafb..e0add6865a0 100644 --- a/sonar-batch-protocol/src/main/java/org/sonar/batch/protocol/output/FileStructure.java +++ b/sonar-batch-protocol/src/main/java/org/sonar/batch/protocol/output/FileStructure.java @@ -27,22 +27,24 @@ import java.io.File; public class FileStructure { public enum Domain { - ISSUES("issues-"), - ISSUES_ON_DELETED("issues-deleted-"), - COMPONENT("component-"), - MEASURES("measures-"), - DUPLICATIONS("duplications-"), - SYNTAX_HIGHLIGHTING("syntax-highlighting-"), - SCM("scm-"), - SYMBOLS("symbol-"), - COVERAGE("coverage-"), - SOURCE("source-") - ; - + ISSUES("issues-", Domain.PB), + ISSUES_ON_DELETED("issues-deleted-", Domain.PB), + COMPONENT("component-", Domain.PB), + MEASURES("measures-", Domain.PB), + DUPLICATIONS("duplications-", Domain.PB), + SYNTAX_HIGHLIGHTING("syntax-highlighting-", Domain.PB), + SCM("scm-", Domain.PB), + SYMBOLS("symbol-", Domain.PB), + COVERAGE("coverage-", Domain.PB), + SOURCE("source-", ".txt"); + + private static final String PB = ".pb"; private final String filePrefix; + private final String fileSuffix; - Domain(String filePrefix) { + Domain(String filePrefix, String fileSuffix) { this.filePrefix = filePrefix; + this.fileSuffix = fileSuffix; } } @@ -60,7 +62,7 @@ public class FileStructure { } public File fileFor(Domain domain, int componentRef) { - return new File(dir, domain.filePrefix + componentRef + ".pb"); + return new File(dir, domain.filePrefix + componentRef + domain.fileSuffix); } } diff --git a/sonar-batch/src/main/java/org/sonar/batch/report/SourcePublisher.java b/sonar-batch/src/main/java/org/sonar/batch/report/SourcePublisher.java new file mode 100644 index 00000000000..ccb91681e2b --- /dev/null +++ b/sonar-batch/src/main/java/org/sonar/batch/report/SourcePublisher.java @@ -0,0 +1,68 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * SonarQube is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.batch.report; + +import com.google.common.base.Charsets; +import org.apache.commons.io.ByteOrderMark; +import org.apache.commons.io.IOUtils; +import org.apache.commons.io.input.BOMInputStream; +import org.sonar.api.batch.fs.internal.DefaultInputFile; +import org.sonar.batch.index.BatchResource; +import org.sonar.batch.index.ResourceCache; +import org.sonar.batch.protocol.output.BatchReportWriter; + +import java.io.*; + +public class SourcePublisher implements ReportPublisherStep { + + private final ResourceCache resourceCache; + + public SourcePublisher(ResourceCache resourceCache) { + this.resourceCache = resourceCache; + } + + @Override + public void publish(BatchReportWriter writer) { + for (final BatchResource resource : resourceCache.all()) { + if (!resource.isFile()) { + continue; + } + + DefaultInputFile inputFile = (DefaultInputFile) resource.inputPath(); + File iofile = writer.getSourceFile(1); + int line = 0; + try (FileOutputStream output = new FileOutputStream(iofile); BOMInputStream bomIn = new BOMInputStream(new FileInputStream(inputFile.file()), + ByteOrderMark.UTF_8, ByteOrderMark.UTF_16LE, ByteOrderMark.UTF_16BE, ByteOrderMark.UTF_32LE, ByteOrderMark.UTF_32BE); + BufferedReader reader = new BufferedReader(new InputStreamReader(bomIn, inputFile.charset()))) { + String lineStr = reader.readLine(); + while (lineStr != null) { + IOUtils.write(lineStr, output, Charsets.UTF_8); + line++; + if (line < inputFile.lines()) { + IOUtils.write("\n", output, Charsets.UTF_8); + } + lineStr = reader.readLine(); + } + } catch (IOException e) { + throw new IllegalStateException("Unable to store file source in the report", e); + } + } + } +} diff --git a/sonar-batch/src/main/java/org/sonar/batch/scan/ProjectScanContainer.java b/sonar-batch/src/main/java/org/sonar/batch/scan/ProjectScanContainer.java index 43ba3500126..12b0bc0f21d 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/scan/ProjectScanContainer.java +++ b/sonar-batch/src/main/java/org/sonar/batch/scan/ProjectScanContainer.java @@ -188,6 +188,7 @@ public class ProjectScanContainer extends ComponentContainer { MeasuresPublisher.class, DuplicationsPublisher.class, CoveragePublisher.class, + SourcePublisher.class, ScanTaskObservers.class); } diff --git a/sonar-batch/src/test/java/org/sonar/batch/report/CoveragePublisherTest.java b/sonar-batch/src/test/java/org/sonar/batch/report/CoveragePublisherTest.java index 2e1614bbdf1..347761203f7 100644 --- a/sonar-batch/src/test/java/org/sonar/batch/report/CoveragePublisherTest.java +++ b/sonar-batch/src/test/java/org/sonar/batch/report/CoveragePublisherTest.java @@ -51,7 +51,6 @@ public class CoveragePublisherTest { private MeasureCache measureCache; private CoveragePublisher publisher; - private org.sonar.api.resources.File aFile = org.sonar.api.resources.File.create("org/foo/Bar.java"); private org.sonar.api.resources.Resource sampleFile; diff --git a/sonar-batch/src/test/java/org/sonar/batch/report/SourcePublisherTest.java b/sonar-batch/src/test/java/org/sonar/batch/report/SourcePublisherTest.java new file mode 100644 index 00000000000..824a6da2db9 --- /dev/null +++ b/sonar-batch/src/test/java/org/sonar/batch/report/SourcePublisherTest.java @@ -0,0 +1,104 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * SonarQube is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.batch.report; + +import com.google.common.base.Charsets; +import org.apache.commons.io.FileUtils; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; +import org.sonar.api.batch.fs.internal.DefaultInputFile; +import org.sonar.api.database.model.Snapshot; +import org.sonar.api.resources.Project; +import org.sonar.batch.index.ResourceCache; +import org.sonar.batch.protocol.output.BatchReportWriter; + +import java.io.File; +import java.io.IOException; +import java.util.Date; + +import static org.assertj.core.api.Assertions.assertThat; + +public class SourcePublisherTest { + + @Rule + public TemporaryFolder temp = new TemporaryFolder(); + + private SourcePublisher publisher; + + private File sourceFile; + + private BatchReportWriter writer; + + @Before + public void prepare() throws IOException { + Project p = new Project("foo").setAnalysisDate(new Date(1234567L)); + ResourceCache resourceCache = new ResourceCache(); + org.sonar.api.resources.Resource sampleFile = org.sonar.api.resources.File.create("src/Foo.php").setEffectiveKey("foo:src/Foo.php"); + resourceCache.add(p, null).setSnapshot(new Snapshot().setId(2)); + File baseDir = temp.newFolder(); + sourceFile = new File(baseDir, "src/Foo.php"); + resourceCache.add(sampleFile, null).setInputPath(new DefaultInputFile("foo", "src/Foo.php").setLines(5).setModuleBaseDir(baseDir.toPath()).setCharset(Charsets.ISO_8859_1)); + publisher = new SourcePublisher(resourceCache); + File outputDir = temp.newFolder(); + writer = new BatchReportWriter(outputDir); + } + + @Test + public void publishEmptySource() throws Exception { + FileUtils.write(sourceFile, "", Charsets.ISO_8859_1); + + publisher.publish(writer); + + File out = writer.getSourceFile(1); + assertThat(FileUtils.readFileToString(out, Charsets.UTF_8)).isEqualTo(""); + } + + @Test + public void publishSourceWithLastEmptyLine() throws Exception { + FileUtils.write(sourceFile, "1\n2\n3\n4\n", Charsets.ISO_8859_1); + + publisher.publish(writer); + + File out = writer.getSourceFile(1); + assertThat(FileUtils.readFileToString(out, Charsets.UTF_8)).isEqualTo("1\n2\n3\n4\n"); + } + + @Test + public void publishSourceWithLastLineNotEmpty() throws Exception { + FileUtils.write(sourceFile, "1\n2\n3\n4\n5", Charsets.ISO_8859_1); + + publisher.publish(writer); + + File out = writer.getSourceFile(1); + assertThat(FileUtils.readFileToString(out, Charsets.UTF_8)).isEqualTo("1\n2\n3\n4\n5"); + } + + @Test + public void cleanLineEnds() throws Exception { + FileUtils.write(sourceFile, "\n2\r\n3\n4\r5", Charsets.ISO_8859_1); + + publisher.publish(writer); + + File out = writer.getSourceFile(1); + assertThat(FileUtils.readFileToString(out, Charsets.UTF_8)).isEqualTo("\n2\n3\n4\n5"); + } +} diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/FileMetadata.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/FileMetadata.java index 40a0cb87d89..ea7b38ce9c7 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/FileMetadata.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/FileMetadata.java @@ -50,18 +50,18 @@ public class FileMetadata implements BatchComponent { private static final char LINE_FEED = '\n'; private static final char CARRIAGE_RETURN = '\r'; - private abstract static class CharHandler { + public abstract static class CharHandler { - void handleAll(char c) { + protected void handleAll(char c) { } - void handleIgnoreEoL(char c) { + protected void handleIgnoreEoL(char c) { } - void newLine() { + protected void newLine() { } - void eof() { + protected void eof() { } } @@ -79,7 +79,7 @@ public class FileMetadata implements BatchComponent { } @Override - void handleAll(char c) { + protected void handleAll(char c) { if (this.lines == 0) { this.lines = 1; } @@ -91,7 +91,7 @@ public class FileMetadata implements BatchComponent { } @Override - void newLine() { + protected void newLine() { lines++; if (!blankLine) { nonBlankLines++; @@ -100,14 +100,14 @@ public class FileMetadata implements BatchComponent { } @Override - void handleIgnoreEoL(char c) { + protected void handleIgnoreEoL(char c) { if (!Character.isWhitespace(c)) { blankLine = false; } } @Override - void eof() { + protected void eof() { if (!blankLine) { nonBlankLines++; } @@ -128,19 +128,19 @@ public class FileMetadata implements BatchComponent { private StringBuilder sb = new StringBuilder(); @Override - void handleIgnoreEoL(char c) { + protected void handleIgnoreEoL(char c) { sb.append(c); } @Override - void newLine() { + protected void newLine() { sb.append(LINE_FEED); globalMd5Digest.update(sb.toString().getBytes(Charsets.UTF_8)); sb.setLength(0); } @Override - void eof() { + protected void eof() { if (sb.length() > 0) { globalMd5Digest.update(sb.toString().getBytes(Charsets.UTF_8)); } @@ -163,21 +163,21 @@ public class FileMetadata implements BatchComponent { } @Override - void handleIgnoreEoL(char c) { + protected void handleIgnoreEoL(char c) { if (!Character.isWhitespace(c)) { sb.append(c); } } @Override - void newLine() { + protected void newLine() { consumer.consume(line, sb.length() > 0 ? lineMd5Digest.digest(sb.toString().getBytes(Charsets.UTF_8)) : null); sb.setLength(0); line++; } @Override - void eof() { + protected void eof() { consumer.consume(line, sb.length() > 0 ? lineMd5Digest.digest(sb.toString().getBytes(Charsets.UTF_8)) : null); } @@ -193,17 +193,17 @@ public class FileMetadata implements BatchComponent { } @Override - void handleAll(char c) { + protected void handleAll(char c) { currentOriginalOffset++; } @Override - void newLine() { + protected void newLine() { originalLineOffsets.add(currentOriginalOffset); } @Override - void eof() { + protected void eof() { lastValidOffset = currentOriginalOffset; } @@ -246,7 +246,7 @@ public class FileMetadata implements BatchComponent { lineOffsetCounter.getLastValidOffset()); } - private static void readFile(File file, Charset encoding, CharHandler... handlers) { + public static void readFile(File file, Charset encoding, CharHandler... handlers) { try (BOMInputStream bomIn = new BOMInputStream(new FileInputStream(file), ByteOrderMark.UTF_8, ByteOrderMark.UTF_16LE, ByteOrderMark.UTF_16BE, ByteOrderMark.UTF_32LE, ByteOrderMark.UTF_32BE); Reader reader = new BufferedReader(new InputStreamReader(bomIn, encoding))) { |