aboutsummaryrefslogtreecommitdiffstats
path: root/sonar-plugin-api
diff options
context:
space:
mode:
authorJulien HENRY <julien.henry@sonarsource.com>2017-05-09 12:31:20 +0200
committerJulien HENRY <henryju@yahoo.fr>2017-05-09 18:02:07 +0200
commit53caac9fa3f2c97ca67936fe9d11ae47ae55c6ca (patch)
tree3d2042b8062f49ae33077d14765183185e4ee196 /sonar-plugin-api
parent72e45fffdea16673f257cb80b40269e73ccffaba (diff)
downloadsonarqube-53caac9fa3f2c97ca67936fe9d11ae47ae55c6ca.tar.gz
sonarqube-53caac9fa3f2c97ca67936fe9d11ae47ae55c6ca.zip
SONAR-9199 InputFile::content() and InputFile::inputStream() should filter BOM
Diffstat (limited to 'sonar-plugin-api')
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/IndexedFile.java11
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/InputFile.java13
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/DefaultIndexedFile.java10
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/DefaultInputFile.java26
-rw-r--r--sonar-plugin-api/src/test/java/org/sonar/api/batch/fs/internal/DefaultInputFileTest.java44
5 files changed, 73 insertions, 31 deletions
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/IndexedFile.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/IndexedFile.java
index 9d0a976580d..057c94efa72 100644
--- a/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/IndexedFile.java
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/IndexedFile.java
@@ -22,15 +22,9 @@ package org.sonar.api.batch.fs;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
-import java.nio.file.Files;
import java.nio.file.Path;
-
import javax.annotation.CheckForNull;
-import org.sonar.api.batch.fs.FileSystem;
-import org.sonar.api.batch.fs.InputFile;
-import org.sonar.api.batch.fs.InputPath;
-
/**
* Represents the indexed view of an {@link InputFile}. Accessing any of data exposed here won't trigger the expensive generation of
* metadata for the {@link InputFile}.
@@ -94,10 +88,7 @@ public interface IndexedFile extends InputPath {
/**
* Creates a stream of the file's contents. Depending on the runtime context, the source might be a file in a physical or virtual filesystem.
* Typically, it won't be buffered. <b>The stream must be closed by the caller</b>.
- * Note that there is a default implementation.
* @since 6.2
*/
- default InputStream inputStream() throws IOException {
- return Files.newInputStream(path());
- }
+ InputStream inputStream() throws IOException;
}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/InputFile.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/InputFile.java
index 77eae1ceddf..00463b1ed09 100644
--- a/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/InputFile.java
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/InputFile.java
@@ -23,7 +23,6 @@ import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.Charset;
-import java.nio.file.Files;
import java.nio.file.Path;
import javax.annotation.CheckForNull;
import org.sonar.api.batch.fs.internal.TestInputFileBuilder;
@@ -112,22 +111,18 @@ public interface InputFile extends IndexedFile {
/**
* Creates a stream of the file's contents. Depending on the runtime context, the source might be a file in a physical or virtual filesystem.
* Typically, it won't be buffered. <b>The stream must be closed by the caller</b>.
- * Note that there is a default implementation.
+ * Since 6.4 BOM is automatically filtered out.
* @since 6.2
*/
@Override
- default InputStream inputStream() throws IOException {
- return Files.newInputStream(path());
- }
+ InputStream inputStream() throws IOException;
/**
* Fetches the entire contents of the file, decoding with the {@link #charset}.
- * Note that there is a default implementation.
+ * Since 6.4 BOM is automatically filtered out.
* @since 6.2
*/
- default String contents() throws IOException {
- return new String(Files.readAllBytes(path()), charset());
- }
+ String contents() throws IOException;
/**
* Status regarding previous analysis
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/DefaultIndexedFile.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/DefaultIndexedFile.java
index c349c4296da..d526e7f57cc 100644
--- a/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/DefaultIndexedFile.java
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/DefaultIndexedFile.java
@@ -20,11 +20,12 @@
package org.sonar.api.batch.fs.internal;
import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.file.Files;
import java.nio.file.Path;
-
import javax.annotation.CheckForNull;
import javax.annotation.Nullable;
-
import org.sonar.api.batch.fs.IndexedFile;
import org.sonar.api.batch.fs.InputFile.Type;
import org.sonar.api.utils.PathUtils;
@@ -83,6 +84,11 @@ public class DefaultIndexedFile extends DefaultInputComponent implements Indexed
return moduleBaseDir.resolve(relativePath);
}
+ @Override
+ public InputStream inputStream() throws IOException {
+ return Files.newInputStream(path());
+ }
+
@CheckForNull
@Override
public String language() {
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/DefaultInputFile.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/DefaultInputFile.java
index cd41f9fbc6d..1e0f5b369bd 100644
--- a/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/DefaultInputFile.java
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/DefaultInputFile.java
@@ -20,8 +20,8 @@
package org.sonar.api.batch.fs.internal;
import com.google.common.base.Preconditions;
-
import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
@@ -30,10 +30,10 @@ import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Arrays;
import java.util.function.Consumer;
-
import javax.annotation.CheckForNull;
import javax.annotation.Nullable;
-
+import org.apache.commons.io.ByteOrderMark;
+import org.apache.commons.io.input.BOMInputStream;
import org.sonar.api.batch.fs.InputFile;
import org.sonar.api.batch.fs.TextPointer;
import org.sonar.api.batch.fs.TextRange;
@@ -43,6 +43,9 @@ import org.sonar.api.batch.fs.TextRange;
* To create {@link InputFile} in tests, use {@link TestInputFileBuilder}.
*/
public class DefaultInputFile extends DefaultInputComponent implements InputFile {
+
+ private static final int DEFAULT_BUFFER_SIZE = 1024 * 4;
+
private final DefaultIndexedFile indexedFile;
private final Consumer<DefaultInputFile> metadataGenerator;
private Status status;
@@ -55,6 +58,7 @@ public class DefaultInputFile extends DefaultInputComponent implements InputFile
this(indexedFile, metadataGenerator, null);
}
+ // For testing
public DefaultInputFile(DefaultIndexedFile indexedFile, Consumer<DefaultInputFile> metadataGenerator, @Nullable String contents) {
super(indexedFile.batchId());
this.indexedFile = indexedFile;
@@ -72,12 +76,24 @@ public class DefaultInputFile extends DefaultInputComponent implements InputFile
@Override
public InputStream inputStream() throws IOException {
- return contents != null ? new ByteArrayInputStream(contents.getBytes(charset())) : Files.newInputStream(path());
+ return contents != null ? new ByteArrayInputStream(contents.getBytes(charset())) : new BOMInputStream(Files.newInputStream(path()),
+ ByteOrderMark.UTF_8, ByteOrderMark.UTF_16LE, ByteOrderMark.UTF_16BE, ByteOrderMark.UTF_32LE, ByteOrderMark.UTF_32BE);
}
@Override
public String contents() throws IOException {
- return contents != null ? contents : new String(Files.readAllBytes(path()), charset());
+ if (contents != null) {
+ return contents;
+ } else {
+ ByteArrayOutputStream result = new ByteArrayOutputStream();
+ byte[] buffer = new byte[DEFAULT_BUFFER_SIZE];
+ int length;
+ InputStream inputStream = inputStream();
+ while ((length = inputStream.read(buffer)) != -1) {
+ result.write(buffer, 0, length);
+ }
+ return result.toString(charset().name());
+ }
}
/**
diff --git a/sonar-plugin-api/src/test/java/org/sonar/api/batch/fs/internal/DefaultInputFileTest.java b/sonar-plugin-api/src/test/java/org/sonar/api/batch/fs/internal/DefaultInputFileTest.java
index 44be449c762..139b9bf4e01 100644
--- a/sonar-plugin-api/src/test/java/org/sonar/api/batch/fs/internal/DefaultInputFileTest.java
+++ b/sonar-plugin-api/src/test/java/org/sonar/api/batch/fs/internal/DefaultInputFileTest.java
@@ -20,7 +20,9 @@
package org.sonar.api.batch.fs.internal;
import java.io.BufferedReader;
+import java.io.BufferedWriter;
import java.io.File;
+import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
@@ -29,8 +31,8 @@ import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
+import java.nio.file.StandardOpenOption;
import java.util.stream.Collectors;
-
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
@@ -72,7 +74,11 @@ public class DefaultInputFileTest {
Path baseDir = temp.newFolder().toPath();
Path testFile = baseDir.resolve("src").resolve("Foo.php");
Files.createDirectories(testFile.getParent());
- Files.write(testFile, "test string".getBytes(StandardCharsets.UTF_8));
+ String content = "test é string";
+ Files.write(testFile, content.getBytes(StandardCharsets.ISO_8859_1));
+
+ assertThat(Files.readAllLines(testFile, StandardCharsets.ISO_8859_1).get(0)).hasSize(content.length());
+
Metadata metadata = new Metadata(42, 30, "", new int[0], 0);
DefaultInputFile inputFile = new DefaultInputFile(new DefaultIndexedFile("ABCDE", baseDir, "src/Foo.php", InputFile.Type.TEST, 0)
@@ -80,10 +86,38 @@ public class DefaultInputFileTest {
.setStatus(InputFile.Status.ADDED)
.setCharset(StandardCharsets.ISO_8859_1);
- assertThat(inputFile.contents()).isEqualTo("test string");
+ assertThat(inputFile.contents()).isEqualTo(content);
+ try (InputStream inputStream = inputFile.inputStream()) {
+ String result = new BufferedReader(new InputStreamReader(inputStream, inputFile.charset())).lines().collect(Collectors.joining());
+ assertThat(result).isEqualTo(content);
+ }
+
+ }
+
+ @Test
+ public void test_content_exclude_bom() throws IOException {
+ Path baseDir = temp.newFolder().toPath();
+ Path testFile = baseDir.resolve("src").resolve("Foo.php");
+ Files.createDirectories(testFile.getParent());
+ try (BufferedWriter out = new BufferedWriter(new FileWriter(testFile.toFile()))) {
+ out.write('\ufeff');
+ }
+ String content = "test é string €";
+ Files.write(testFile, content.getBytes(StandardCharsets.UTF_8), StandardOpenOption.APPEND);
+
+ assertThat(Files.readAllLines(testFile, StandardCharsets.UTF_8).get(0)).hasSize(content.length() + 1);
+
+ Metadata metadata = new Metadata(42, 30, "", new int[0], 0);
+
+ DefaultInputFile inputFile = new DefaultInputFile(new DefaultIndexedFile("ABCDE", baseDir, "src/Foo.php", InputFile.Type.TEST, 0)
+ .setLanguage("php"), f -> f.setMetadata(metadata))
+ .setStatus(InputFile.Status.ADDED)
+ .setCharset(StandardCharsets.UTF_8);
+
+ assertThat(inputFile.contents()).isEqualTo(content);
try (InputStream inputStream = inputFile.inputStream()) {
- String result = new BufferedReader(new InputStreamReader(inputStream)).lines().collect(Collectors.joining());
- assertThat(result).isEqualTo("test string");
+ String result = new BufferedReader(new InputStreamReader(inputStream, inputFile.charset())).lines().collect(Collectors.joining());
+ assertThat(result).isEqualTo(content);
}
}