aboutsummaryrefslogtreecommitdiffstats
path: root/sonar-plugin-api/src/main/java/org
diff options
context:
space:
mode:
authorDuarte Meneses <duarte.meneses@sonarsource.com>2017-07-10 10:38:27 +0200
committerDuarte Meneses <duarte.meneses@sonarsource.com>2017-07-11 08:51:38 +0200
commit6f107dcbe90b0fea564e1ecaa96643bfc539329a (patch)
tree45478253f6006a0f1381fb506f92852ad2f5c5df /sonar-plugin-api/src/main/java/org
parentf6a0f6bb86f7d244bec0b6781020e2ea066124c6 (diff)
downloadsonarqube-6f107dcbe90b0fea564e1ecaa96643bfc539329a.tar.gz
sonarqube-6f107dcbe90b0fea564e1ecaa96643bfc539329a.zip
SONAR-9477 Deprecate ProjectReactor and ProjectBuilder
Mark Immutable classes in the Plugin API and Scanner
Diffstat (limited to 'sonar-plugin-api/src/main/java/org')
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/SonarRuntime.java3
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/batch/bootstrap/ProjectBuilder.java3
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/batch/bootstrap/ProjectDefinition.java2
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/batch/bootstrap/ProjectReactor.java5
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/batch/bootstrap/internal/ProjectBuilderContext.java2
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/batch/debt/DebtRemediationFunction.java2
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/InputModule.java4
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/DefaultIndexedFile.java24
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/DefaultInputFile.java3
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/DefaultInputModule.java97
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/FileMetadata.java232
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/InputModuleHierarchy.java3
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/Metadata.java7
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/PathPattern.java28
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/PathPatternPredicate.java2
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/TestInputFileBuilder.java14
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/charhandler/CharHandler.java35
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/charhandler/FileHashComputer.java85
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/charhandler/IntArrayList.java (renamed from sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/IntArrayList.java)7
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/charhandler/LineCounter.java83
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/charhandler/LineHashComputer.java82
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/charhandler/LineOffsetCounter.java60
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/charhandler/package-info.java24
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/batch/measure/MetricFinder.java3
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/batch/rule/ActiveRule.java3
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/batch/rule/ActiveRules.java2
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/batch/rule/RuleParam.java4
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/batch/rule/Rules.java2
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/batch/rule/internal/DefaultActiveRules.java27
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/batch/rule/internal/DefaultRules.java5
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/cpd/internal/DefaultCpdTokens.java2
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/resources/Project.java5
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/scan/filesystem/PathResolver.java3
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/scan/issue/filter/FilterableIssue.java2
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/scan/issue/filter/IssueFilter.java7
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/scan/issue/filter/IssueFilterChain.java3
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/utils/WildcardPattern.java14
37 files changed, 596 insertions, 293 deletions
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/SonarRuntime.java b/sonar-plugin-api/src/main/java/org/sonar/api/SonarRuntime.java
index bea756e4bc2..de0cfa41cee 100644
--- a/sonar-plugin-api/src/main/java/org/sonar/api/SonarRuntime.java
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/SonarRuntime.java
@@ -19,6 +19,8 @@
*/
package org.sonar.api;
+import javax.annotation.concurrent.Immutable;
+
import org.sonar.api.batch.ScannerSide;
import org.sonar.api.batch.sensor.Sensor;
import org.sonar.api.batch.sensor.SensorContext;
@@ -141,6 +143,7 @@ import org.sonarsource.api.sonarlint.SonarLintSide;
@ServerSide
@ComputeEngineSide
@SonarLintSide
+@Immutable
public interface SonarRuntime {
/**
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/bootstrap/ProjectBuilder.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/bootstrap/ProjectBuilder.java
index eac17aefa31..7d557241ee0 100644
--- a/sonar-plugin-api/src/main/java/org/sonar/api/batch/bootstrap/ProjectBuilder.java
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/batch/bootstrap/ProjectBuilder.java
@@ -33,17 +33,20 @@ import org.sonar.api.batch.ScannerSide;
* <li>Change project metadata like description or source directories.</li>
* </ul>
*
+ * @deprecated since 6.5. It won't be possible to manipulate the project's structure.
* @since 2.9
*/
@ScannerSide
@InstantiationStrategy(InstantiationStrategy.PER_BATCH)
@ExtensionPoint
+@Deprecated
public abstract class ProjectBuilder {
/**
* Plugins can use the implementation {@link org.sonar.api.batch.bootstrap.internal.ProjectBuilderContext}
* for their unit tests.
*/
+ @Deprecated
public interface Context {
ProjectReactor projectReactor();
}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/bootstrap/ProjectDefinition.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/bootstrap/ProjectDefinition.java
index 19cc04bdaaf..c3c75506131 100644
--- a/sonar-plugin-api/src/main/java/org/sonar/api/batch/bootstrap/ProjectDefinition.java
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/batch/bootstrap/ProjectDefinition.java
@@ -36,6 +36,8 @@ import org.sonar.api.CoreProperties;
* {@link org.sonar.api.batch.bootstrap.ProjectBuilder extension point} and must not be used
* by other standard extensions.
*
+ * Since 6.5, plugins should no longer manipulate the project's structure.
+ *
* @since 2.9
*/
public class ProjectDefinition {
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/bootstrap/ProjectReactor.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/bootstrap/ProjectReactor.java
index 46ea9aba68a..8aba5c679a2 100644
--- a/sonar-plugin-api/src/main/java/org/sonar/api/batch/bootstrap/ProjectReactor.java
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/batch/bootstrap/ProjectReactor.java
@@ -26,8 +26,11 @@ import java.util.List;
/**
* Mutable project definitions that can be modified by {@link ProjectBuilder} extensions.
+ *
+ * @deprecated since 6.5 plugins should no longer modify the project's structure
* @since 2.9
*/
+@Deprecated
@ScannerSide
public class ProjectReactor implements ProjectKey {
@@ -41,7 +44,7 @@ public class ProjectReactor implements ProjectKey {
}
public List<ProjectDefinition> getProjects() {
- return collectProjects(root, new ArrayList<ProjectDefinition>());
+ return collectProjects(root, new ArrayList<>());
}
/**
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/bootstrap/internal/ProjectBuilderContext.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/bootstrap/internal/ProjectBuilderContext.java
index 6d2024037ba..b3093aa7011 100644
--- a/sonar-plugin-api/src/main/java/org/sonar/api/batch/bootstrap/internal/ProjectBuilderContext.java
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/batch/bootstrap/internal/ProjectBuilderContext.java
@@ -27,8 +27,10 @@ import org.sonar.api.batch.bootstrap.ProjectReactor;
* Context that is passed to {@link org.sonar.api.batch.bootstrap.ProjectBuilder} as parameter.
* Important - plugins must use this class only for unit test needs.
*
+ * @deprecated since 6.5
* @since 3.7
*/
+@Deprecated
public class ProjectBuilderContext implements ProjectBuilder.Context {
private final ProjectReactor reactor;
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/debt/DebtRemediationFunction.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/debt/DebtRemediationFunction.java
index a0b2e24404d..cbfc1368779 100644
--- a/sonar-plugin-api/src/main/java/org/sonar/api/batch/debt/DebtRemediationFunction.java
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/batch/debt/DebtRemediationFunction.java
@@ -22,12 +22,14 @@ package org.sonar.api.batch.debt;
import javax.annotation.CheckForNull;
import javax.annotation.Nullable;
import org.sonar.api.utils.Duration;
+import javax.annotation.concurrent.Immutable;
/**
* @since 4.3
* @deprecated since 6.5 debt model will soon be unavailable on batch side
*/
@Deprecated
+@Immutable
public class DebtRemediationFunction {
public enum Type {
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/InputModule.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/InputModule.java
index 373ba890055..c1aaf9fa36f 100644
--- a/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/InputModule.java
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/InputModule.java
@@ -19,6 +19,9 @@
*/
package org.sonar.api.batch.fs;
+
+import javax.annotation.concurrent.Immutable;
+
import org.sonar.api.batch.sensor.SensorContext;
/**
@@ -26,5 +29,6 @@ import org.sonar.api.batch.sensor.SensorContext;
*
* @since 5.2
*/
+@Immutable
public interface InputModule extends InputComponent {
}
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 d526e7f57cc..b3a0dbbe9ee 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
@@ -24,8 +24,11 @@ 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 javax.annotation.concurrent.Immutable;
+
import org.sonar.api.batch.fs.IndexedFile;
import org.sonar.api.batch.fs.InputFile.Type;
import org.sonar.api.utils.PathUtils;
@@ -33,35 +36,34 @@ import org.sonar.api.utils.PathUtils;
/**
* @since 6.3
*/
+@Immutable
public class DefaultIndexedFile extends DefaultInputComponent implements IndexedFile {
private final String relativePath;
private final String moduleKey;
private final Path moduleBaseDir;
- private String language;
+ private final String language;
private final Type type;
+ private final Path path;
/**
* Testing purposes only!
*/
- public DefaultIndexedFile(String moduleKey, Path moduleBaseDir, String relativePath) {
- this(moduleKey, moduleBaseDir, relativePath, TestInputFileBuilder.nextBatchId());
+ public DefaultIndexedFile(String moduleKey, Path moduleBaseDir, String relativePath, @Nullable String language) {
+ this(moduleKey, moduleBaseDir, relativePath, language, TestInputFileBuilder.nextBatchId());
}
- public DefaultIndexedFile(String moduleKey, Path moduleBaseDir, String relativePath, int batchId) {
- this(moduleKey, moduleBaseDir, relativePath, Type.MAIN, batchId);
+ public DefaultIndexedFile(String moduleKey, Path moduleBaseDir, String relativePath, @Nullable String language, int batchId) {
+ this(moduleKey, moduleBaseDir, relativePath, Type.MAIN, language, batchId);
}
- public DefaultIndexedFile(String moduleKey, Path moduleBaseDir, String relativePath, Type type, int batchId) {
+ public DefaultIndexedFile(String moduleKey, Path moduleBaseDir, String relativePath, Type type, @Nullable String language, int batchId) {
super(batchId);
this.moduleKey = moduleKey;
this.relativePath = PathUtils.sanitize(relativePath);
this.moduleBaseDir = moduleBaseDir.normalize();
this.type = type;
- }
-
- public DefaultIndexedFile setLanguage(@Nullable String language) {
this.language = language;
- return this;
+ this.path = this.moduleBaseDir.resolve(this.relativePath);
}
@Override
@@ -81,7 +83,7 @@ public class DefaultIndexedFile extends DefaultInputComponent implements Indexed
@Override
public Path path() {
- return moduleBaseDir.resolve(relativePath);
+ return path;
}
@Override
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 ff881a6f75d..b3ea8263c0d 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
@@ -47,12 +47,13 @@ public class DefaultInputFile extends DefaultInputComponent implements InputFile
private static final int DEFAULT_BUFFER_SIZE = 1024 * 4;
private final DefaultIndexedFile indexedFile;
+ private final String contents;
private final Consumer<DefaultInputFile> metadataGenerator;
+
private Status status;
private Charset charset;
private Metadata metadata;
private boolean publish;
- private String contents;
public DefaultInputFile(DefaultIndexedFile indexedFile, Consumer<DefaultInputFile> metadataGenerator) {
this(indexedFile, metadataGenerator, null);
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/DefaultInputModule.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/DefaultInputModule.java
index ac8eced2bbd..1165e54d494 100644
--- a/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/DefaultInputModule.java
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/DefaultInputModule.java
@@ -19,13 +19,36 @@
*/
package org.sonar.api.batch.fs.internal;
+import java.io.File;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import javax.annotation.CheckForNull;
+import javax.annotation.concurrent.Immutable;
+
import org.sonar.api.batch.bootstrap.ProjectDefinition;
import org.sonar.api.batch.fs.InputModule;
/**
* @since 5.2
*/
+@Immutable
public class DefaultInputModule extends DefaultInputComponent implements InputModule {
+ private final File baseDir;
+ private final File workDir;
+ private final String name;
+ private final String version;
+ private final String originalName;
+ private final String originalVersion;
+ private final String description;
+ private final String keyWithBranch;
+ private final String branch;
+ private final List<String> sources;
+ private final List<String> tests;
+ private final Map<String, String> properties;
private final String moduleKey;
private final ProjectDefinition definition;
@@ -36,9 +59,29 @@ public class DefaultInputModule extends DefaultInputComponent implements InputMo
public DefaultInputModule(String moduleKey) {
this(ProjectDefinition.create().setKey(moduleKey), TestInputFileBuilder.nextBatchId());
}
+
+ /**
+ * For testing only!
+ */
+ public DefaultInputModule(ProjectDefinition definition) {
+ this(definition, TestInputFileBuilder.nextBatchId());
+ }
public DefaultInputModule(ProjectDefinition definition, int batchId) {
super(batchId);
+ this.baseDir = definition.getBaseDir();
+ this.workDir = definition.getWorkDir();
+ this.name = definition.getName();
+ this.originalName = definition.getOriginalName();
+ this.version = definition.getVersion();
+ this.originalVersion = definition.getOriginalVersion();
+ this.description = definition.getDescription();
+ this.keyWithBranch = definition.getKeyWithBranch();
+ this.branch = definition.getBranch();
+ this.sources = Collections.unmodifiableList(new ArrayList<>(definition.sources()));
+ this.tests = Collections.unmodifiableList(new ArrayList<>(definition.tests()));
+ this.properties = Collections.unmodifiableMap(new HashMap<>(definition.properties()));
+
this.definition = definition;
this.moduleKey = definition.getKey();
}
@@ -59,5 +102,59 @@ public class DefaultInputModule extends DefaultInputComponent implements InputMo
public ProjectDefinition definition() {
return definition;
}
+
+ public File getBaseDir() {
+ return baseDir;
+ }
+
+ public File getWorkDir() {
+ return workDir;
+ }
+
+ public String getKeyWithBranch() {
+ return keyWithBranch;
+ }
+
+ @CheckForNull
+ public String getBranch() {
+ return branch;
+ }
+
+ public Map<String, String> properties() {
+ return properties;
+ }
+
+ @CheckForNull
+ public String getOriginalVersion() {
+ return originalVersion;
+ }
+
+ public String getVersion() {
+ return version;
+ }
+
+ @CheckForNull
+ public String getOriginalName() {
+ return originalName;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public String getDescription() {
+ return description;
+ }
+
+ /**
+ * @return Source files and folders.
+ */
+ public List<String> sources() {
+ return sources;
+ }
+
+ public List<String> tests() {
+ return tests;
+ }
}
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 f444c26bd0d..4a96ce61591 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
@@ -20,252 +20,34 @@
package org.sonar.api.batch.fs.internal;
import java.io.BufferedReader;
-import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
-import java.nio.ByteBuffer;
-import java.nio.CharBuffer;
-import java.nio.charset.CharacterCodingException;
import java.nio.charset.Charset;
-import java.nio.charset.CharsetEncoder;
-import java.nio.charset.CodingErrorAction;
import java.nio.charset.StandardCharsets;
-import java.security.MessageDigest;
-import javax.annotation.CheckForNull;
import javax.annotation.Nullable;
+import javax.annotation.concurrent.Immutable;
-import org.apache.commons.codec.binary.Hex;
-import org.apache.commons.codec.digest.DigestUtils;
-import org.sonar.api.CoreProperties;
import org.sonar.api.batch.ScannerSide;
import org.sonar.api.batch.fs.InputFile;
-import org.sonar.api.utils.log.Logger;
-import org.sonar.api.utils.log.Loggers;
+import org.sonar.api.batch.fs.internal.charhandler.CharHandler;
+import org.sonar.api.batch.fs.internal.charhandler.FileHashComputer;
+import org.sonar.api.batch.fs.internal.charhandler.LineCounter;
+import org.sonar.api.batch.fs.internal.charhandler.LineHashComputer;
+import org.sonar.api.batch.fs.internal.charhandler.LineOffsetCounter;
/**
* Computes hash of files. Ends of Lines are ignored, so files with
* same content but different EOL encoding have the same hash.
*/
@ScannerSide
+@Immutable
public class FileMetadata {
-
- private static final Logger LOG = Loggers.get(FileMetadata.class);
-
private static final char LINE_FEED = '\n';
private static final char CARRIAGE_RETURN = '\r';
- public abstract static class CharHandler {
-
- protected void handleAll(char c) {
- }
-
- protected void handleIgnoreEoL(char c) {
- }
-
- protected void newLine() {
- }
-
- protected void eof() {
- }
- }
-
- private static class LineCounter extends CharHandler {
- private int lines = 1;
- private int nonBlankLines = 0;
- private boolean blankLine = true;
- boolean alreadyLoggedInvalidCharacter = false;
- private final String filePath;
- private final Charset encoding;
-
- LineCounter(String filePath, Charset encoding) {
- this.filePath = filePath;
- this.encoding = encoding;
- }
-
- @Override
- protected void handleAll(char c) {
- if (!alreadyLoggedInvalidCharacter && c == '\ufffd') {
- LOG.warn("Invalid character encountered in file {} at line {} for encoding {}. Please fix file content or configure the encoding to be used using property '{}'.", filePath,
- lines, encoding, CoreProperties.ENCODING_PROPERTY);
- alreadyLoggedInvalidCharacter = true;
- }
- }
-
- @Override
- protected void newLine() {
- lines++;
- if (!blankLine) {
- nonBlankLines++;
- }
- blankLine = true;
- }
-
- @Override
- protected void handleIgnoreEoL(char c) {
- if (!Character.isWhitespace(c)) {
- blankLine = false;
- }
- }
-
- @Override
- protected void eof() {
- if (!blankLine) {
- nonBlankLines++;
- }
- }
-
- public int lines() {
- return lines;
- }
-
- public int nonBlankLines() {
- return nonBlankLines;
- }
-
- }
-
- private static class FileHashComputer extends CharHandler {
- private MessageDigest globalMd5Digest = DigestUtils.getMd5Digest();
- private StringBuilder sb = new StringBuilder();
- private final CharsetEncoder encoder;
- private final String filePath;
-
- public FileHashComputer(String filePath) {
- encoder = StandardCharsets.UTF_8.newEncoder()
- .onMalformedInput(CodingErrorAction.REPLACE)
- .onUnmappableCharacter(CodingErrorAction.REPLACE);
- this.filePath = filePath;
- }
-
- @Override
- protected void handleIgnoreEoL(char c) {
- sb.append(c);
- }
-
- @Override
- protected void newLine() {
- sb.append(LINE_FEED);
- processBuffer();
- sb.setLength(0);
- }
-
- @Override
- protected void eof() {
- if (sb.length() > 0) {
- processBuffer();
- }
- }
-
- private void processBuffer() {
- try {
- if (sb.length() > 0) {
- ByteBuffer encoded = encoder.encode(CharBuffer.wrap(sb));
- globalMd5Digest.update(encoded.array(), 0, encoded.limit());
- }
- } catch (CharacterCodingException e) {
- throw new IllegalStateException("Error encoding line hash in file: " + filePath, e);
- }
- }
-
- @CheckForNull
- public String getHash() {
- return Hex.encodeHexString(globalMd5Digest.digest());
- }
- }
-
- private static class LineHashComputer extends CharHandler {
- private final MessageDigest lineMd5Digest = DigestUtils.getMd5Digest();
- private final CharsetEncoder encoder;
- private final StringBuilder sb = new StringBuilder();
- private final LineHashConsumer consumer;
- private final File file;
- private int line = 1;
-
- public LineHashComputer(LineHashConsumer consumer, File f) {
- this.consumer = consumer;
- this.file = f;
- this.encoder = StandardCharsets.UTF_8.newEncoder()
- .onMalformedInput(CodingErrorAction.REPLACE)
- .onUnmappableCharacter(CodingErrorAction.REPLACE);
- }
-
- @Override
- protected void handleIgnoreEoL(char c) {
- if (!Character.isWhitespace(c)) {
- sb.append(c);
- }
- }
-
- @Override
- protected void newLine() {
- processBuffer();
- sb.setLength(0);
- line++;
- }
-
- @Override
- protected void eof() {
- if (this.line > 0) {
- processBuffer();
- }
- }
-
- private void processBuffer() {
- try {
- if (sb.length() > 0) {
- ByteBuffer encoded = encoder.encode(CharBuffer.wrap(sb));
- lineMd5Digest.update(encoded.array(), 0, encoded.limit());
- consumer.consume(line, lineMd5Digest.digest());
- }
- } catch (CharacterCodingException e) {
- throw new IllegalStateException("Error encoding line hash in file: " + file.getAbsolutePath(), e);
- }
- }
- }
-
- private static class LineOffsetCounter extends CharHandler {
- private long currentOriginalOffset = 0;
- private IntArrayList originalLineOffsets = new IntArrayList();
- private long lastValidOffset = 0;
-
- public LineOffsetCounter() {
- originalLineOffsets.add(0);
- }
-
- @Override
- protected void handleAll(char c) {
- currentOriginalOffset++;
- }
-
- @Override
- protected void newLine() {
- if (currentOriginalOffset > Integer.MAX_VALUE) {
- throw new IllegalStateException("File is too big: " + currentOriginalOffset);
- }
- originalLineOffsets.add((int) currentOriginalOffset);
- }
-
- @Override
- protected void eof() {
- lastValidOffset = currentOriginalOffset;
- }
-
- public int[] getOriginalLineOffsets() {
- return originalLineOffsets.trimAndGet();
- }
-
- public int getLastValidOffset() {
- if (lastValidOffset > Integer.MAX_VALUE) {
- throw new IllegalStateException("File is too big: " + lastValidOffset);
- }
- return (int) lastValidOffset;
- }
-
- }
-
/**
* Compute hash of a file ignoring line ends differences.
* Maximum performance is needed.
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/InputModuleHierarchy.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/InputModuleHierarchy.java
index 468a052f507..b258fb04dd2 100644
--- a/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/InputModuleHierarchy.java
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/InputModuleHierarchy.java
@@ -22,10 +22,11 @@ package org.sonar.api.batch.fs.internal;
import java.util.Collection;
import javax.annotation.CheckForNull;
+import javax.annotation.concurrent.Immutable;
import org.sonar.api.batch.fs.InputModule;
-import org.sonar.api.batch.fs.internal.DefaultInputModule;
+@Immutable
public interface InputModuleHierarchy {
DefaultInputModule root();
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/Metadata.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/Metadata.java
index 71d79d1007a..9323c73b062 100644
--- a/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/Metadata.java
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/Metadata.java
@@ -19,6 +19,11 @@
*/
package org.sonar.api.batch.fs.internal;
+import java.util.Arrays;
+
+import javax.annotation.concurrent.Immutable;
+
+@Immutable
public class Metadata {
private final int lines;
private final int nonBlankLines;
@@ -30,7 +35,7 @@ public class Metadata {
this.lines = lines;
this.nonBlankLines = nonBlankLines;
this.hash = hash;
- this.originalLineOffsets = originalLineOffsets;
+ this.originalLineOffsets = Arrays.copyOf(originalLineOffsets, originalLineOffsets.length);
this.lastValidOffset = lastValidOffset;
}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/PathPattern.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/PathPattern.java
index 556ce3f5de0..5ec259f0021 100644
--- a/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/PathPattern.java
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/PathPattern.java
@@ -19,11 +19,13 @@
*/
package org.sonar.api.batch.fs.internal;
+import javax.annotation.concurrent.ThreadSafe;
+
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.lang.StringUtils;
-import org.sonar.api.batch.fs.IndexedFile;
import org.sonar.api.utils.WildcardPattern;
+@ThreadSafe
public abstract class PathPattern {
final WildcardPattern pattern;
@@ -32,9 +34,9 @@ public abstract class PathPattern {
this.pattern = WildcardPattern.create(pattern);
}
- public abstract boolean match(IndexedFile inputFile);
+ public abstract boolean match(String absolutePath, String relativePath);
- public abstract boolean match(IndexedFile inputFile, boolean caseSensitiveFileExtension);
+ public abstract boolean match(String absolutePath, String relativePath, boolean caseSensitiveFileExtension);
public static PathPattern create(String s) {
String trimmed = StringUtils.trim(s);
@@ -58,15 +60,15 @@ public abstract class PathPattern {
}
@Override
- public boolean match(IndexedFile inputFile) {
- return match(inputFile, true);
+ public boolean match(String absolutePath, String relativePath) {
+ return match(absolutePath, relativePath, true);
}
@Override
- public boolean match(IndexedFile inputFile, boolean caseSensitiveFileExtension) {
- String path = inputFile.absolutePath();
+ public boolean match(String absolutePath, String relativePath, boolean caseSensitiveFileExtension) {
+ String path = absolutePath;
if (!caseSensitiveFileExtension) {
- String extension = sanitizeExtension(FilenameUtils.getExtension(inputFile.file().getName()));
+ String extension = sanitizeExtension(FilenameUtils.getExtension(relativePath));
if (StringUtils.isNotBlank(extension)) {
path = StringUtils.removeEndIgnoreCase(path, extension);
path = path + extension;
@@ -90,15 +92,15 @@ public abstract class PathPattern {
}
@Override
- public boolean match(IndexedFile inputFile) {
- return match(inputFile, true);
+ public boolean match(String absolutePath, String relativePath) {
+ return match(absolutePath, relativePath, true);
}
@Override
- public boolean match(IndexedFile inputFile, boolean caseSensitiveFileExtension) {
- String path = inputFile.relativePath();
+ public boolean match(String absolutePath, String relativePath, boolean caseSensitiveFileExtension) {
+ String path = relativePath;
if (!caseSensitiveFileExtension) {
- String extension = sanitizeExtension(FilenameUtils.getExtension(inputFile.file().getName()));
+ String extension = sanitizeExtension(FilenameUtils.getExtension(relativePath));
if (StringUtils.isNotBlank(extension)) {
path = StringUtils.removeEndIgnoreCase(path, extension);
path = path + extension;
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/PathPatternPredicate.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/PathPatternPredicate.java
index 400fd64f37b..e6b4a9246e3 100644
--- a/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/PathPatternPredicate.java
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/PathPatternPredicate.java
@@ -34,7 +34,7 @@ class PathPatternPredicate extends AbstractFilePredicate {
@Override
public boolean apply(InputFile f) {
- return pattern.match(f);
+ return pattern.match(f.absolutePath(), f.relativePath());
}
}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/TestInputFileBuilder.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/TestInputFileBuilder.java
index 6fc584fdfbc..fa86a9e17e6 100644
--- a/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/TestInputFileBuilder.java
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/TestInputFileBuilder.java
@@ -66,7 +66,7 @@ public class TestInputFileBuilder {
private int lastValidOffset = -1;
private String hash;
private int nonBlankLines;
- private int[] originalLineOffsets;
+ private int[] originalLineOffsets = new int[0];
private boolean publish = true;
private String contents;
@@ -194,8 +194,7 @@ public class TestInputFileBuilder {
}
public DefaultInputFile build() {
- DefaultIndexedFile indexedFile = new DefaultIndexedFile(moduleKey, moduleBaseDir, relativePath, type, id);
- indexedFile.setLanguage(language);
+ DefaultIndexedFile indexedFile = new DefaultIndexedFile(moduleKey, moduleBaseDir, relativePath, type, language, id);
DefaultInputFile inputFile = new DefaultInputFile(indexedFile,
f -> f.setMetadata(new Metadata(lines, nonBlankLines, hash, originalLineOffsets, lastValidOffset)),
contents);
@@ -206,8 +205,11 @@ public class TestInputFileBuilder {
}
public static DefaultInputModule newDefaultInputModule(String moduleKey, File baseDir) {
- ProjectDefinition definition = ProjectDefinition.create().setKey(moduleKey);
- definition.setBaseDir(baseDir);
- return new DefaultInputModule(definition, TestInputFileBuilder.nextBatchId());
+ ProjectDefinition definition = ProjectDefinition.create().setKey(moduleKey).setBaseDir(baseDir);
+ return newDefaultInputModule(definition);
+ }
+
+ public static DefaultInputModule newDefaultInputModule(ProjectDefinition projectDefinition) {
+ return new DefaultInputModule(projectDefinition, TestInputFileBuilder.nextBatchId());
}
}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/charhandler/CharHandler.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/charhandler/CharHandler.java
new file mode 100644
index 00000000000..76941323906
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/charhandler/CharHandler.java
@@ -0,0 +1,35 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program 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.
+ *
+ * This program 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.api.batch.fs.internal.charhandler;
+
+public abstract class CharHandler {
+
+ public void handleAll(char c) {
+ }
+
+ public void handleIgnoreEoL(char c) {
+ }
+
+ public void newLine() {
+ }
+
+ public void eof() {
+ }
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/charhandler/FileHashComputer.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/charhandler/FileHashComputer.java
new file mode 100644
index 00000000000..d1bfa79207e
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/charhandler/FileHashComputer.java
@@ -0,0 +1,85 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program 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.
+ *
+ * This program 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.api.batch.fs.internal.charhandler;
+
+import java.nio.ByteBuffer;
+import java.nio.CharBuffer;
+import java.nio.charset.CharacterCodingException;
+import java.nio.charset.CharsetEncoder;
+import java.nio.charset.CodingErrorAction;
+import java.nio.charset.StandardCharsets;
+import java.security.MessageDigest;
+
+import javax.annotation.CheckForNull;
+
+import org.apache.commons.codec.binary.Hex;
+import org.apache.commons.codec.digest.DigestUtils;
+
+public class FileHashComputer extends CharHandler {
+ private static final char LINE_FEED = '\n';
+
+
+ private MessageDigest globalMd5Digest = DigestUtils.getMd5Digest();
+ private StringBuilder sb = new StringBuilder();
+ private final CharsetEncoder encoder;
+ private final String filePath;
+
+ public FileHashComputer(String filePath) {
+ encoder = StandardCharsets.UTF_8.newEncoder()
+ .onMalformedInput(CodingErrorAction.REPLACE)
+ .onUnmappableCharacter(CodingErrorAction.REPLACE);
+ this.filePath = filePath;
+ }
+
+ @Override
+ public void handleIgnoreEoL(char c) {
+ sb.append(c);
+ }
+
+ @Override
+ public void newLine() {
+ sb.append(LINE_FEED);
+ processBuffer();
+ sb.setLength(0);
+ }
+
+ @Override
+ public void eof() {
+ if (sb.length() > 0) {
+ processBuffer();
+ }
+ }
+
+ private void processBuffer() {
+ try {
+ if (sb.length() > 0) {
+ ByteBuffer encoded = encoder.encode(CharBuffer.wrap(sb));
+ globalMd5Digest.update(encoded.array(), 0, encoded.limit());
+ }
+ } catch (CharacterCodingException e) {
+ throw new IllegalStateException("Error encoding line hash in file: " + filePath, e);
+ }
+ }
+
+ @CheckForNull
+ public String getHash() {
+ return Hex.encodeHexString(globalMd5Digest.digest());
+ }
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/IntArrayList.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/charhandler/IntArrayList.java
index 913c0a2433b..2bdcfb6a852 100644
--- a/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/IntArrayList.java
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/charhandler/IntArrayList.java
@@ -17,7 +17,7 @@
* 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.api.batch.fs.internal;
+package org.sonar.api.batch.fs.internal.charhandler;
import java.util.Arrays;
import java.util.Collection;
@@ -72,11 +72,12 @@ class IntArrayList {
}
private void ensureCapacityInternal(int minCapacity) {
+ int capacity = minCapacity;
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
- minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
+ capacity = Math.max(DEFAULT_CAPACITY, minCapacity);
}
- ensureExplicitCapacity(minCapacity);
+ ensureExplicitCapacity(capacity);
}
private void ensureExplicitCapacity(int minCapacity) {
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/charhandler/LineCounter.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/charhandler/LineCounter.java
new file mode 100644
index 00000000000..c17a867f295
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/charhandler/LineCounter.java
@@ -0,0 +1,83 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program 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.
+ *
+ * This program 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.api.batch.fs.internal.charhandler;
+
+import java.nio.charset.Charset;
+
+import org.sonar.api.CoreProperties;
+import org.sonar.api.utils.log.Logger;
+import org.sonar.api.utils.log.Loggers;
+
+public class LineCounter extends CharHandler {
+ private static final Logger LOG = Loggers.get(LineCounter.class);
+
+ private int lines = 1;
+ private int nonBlankLines = 0;
+ private boolean blankLine = true;
+ boolean alreadyLoggedInvalidCharacter = false;
+ private final String filePath;
+ private final Charset encoding;
+
+ public LineCounter(String filePath, Charset encoding) {
+ this.filePath = filePath;
+ this.encoding = encoding;
+ }
+
+ @Override
+ public void handleAll(char c) {
+ if (!alreadyLoggedInvalidCharacter && c == '\ufffd') {
+ LOG.warn("Invalid character encountered in file {} at line {} for encoding {}. Please fix file content or configure the encoding to be used using property '{}'.", filePath,
+ lines, encoding, CoreProperties.ENCODING_PROPERTY);
+ alreadyLoggedInvalidCharacter = true;
+ }
+ }
+
+ @Override
+ public void newLine() {
+ lines++;
+ if (!blankLine) {
+ nonBlankLines++;
+ }
+ blankLine = true;
+ }
+
+ @Override
+ public void handleIgnoreEoL(char c) {
+ if (!Character.isWhitespace(c)) {
+ blankLine = false;
+ }
+ }
+
+ @Override
+ public void eof() {
+ if (!blankLine) {
+ nonBlankLines++;
+ }
+ }
+
+ public int lines() {
+ return lines;
+ }
+
+ public int nonBlankLines() {
+ return nonBlankLines;
+ }
+
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/charhandler/LineHashComputer.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/charhandler/LineHashComputer.java
new file mode 100644
index 00000000000..f371c71f5a1
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/charhandler/LineHashComputer.java
@@ -0,0 +1,82 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program 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.
+ *
+ * This program 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.api.batch.fs.internal.charhandler;
+
+import java.io.File;
+import java.nio.ByteBuffer;
+import java.nio.CharBuffer;
+import java.nio.charset.CharacterCodingException;
+import java.nio.charset.CharsetEncoder;
+import java.nio.charset.CodingErrorAction;
+import java.nio.charset.StandardCharsets;
+import java.security.MessageDigest;
+
+import org.apache.commons.codec.digest.DigestUtils;
+import org.sonar.api.batch.fs.internal.FileMetadata.LineHashConsumer;
+
+public class LineHashComputer extends CharHandler {
+ private final MessageDigest lineMd5Digest = DigestUtils.getMd5Digest();
+ private final CharsetEncoder encoder;
+ private final StringBuilder sb = new StringBuilder();
+ private final LineHashConsumer consumer;
+ private final File file;
+ private int line = 1;
+
+ public LineHashComputer(LineHashConsumer consumer, File f) {
+ this.consumer = consumer;
+ this.file = f;
+ this.encoder = StandardCharsets.UTF_8.newEncoder()
+ .onMalformedInput(CodingErrorAction.REPLACE)
+ .onUnmappableCharacter(CodingErrorAction.REPLACE);
+ }
+
+ @Override
+ public void handleIgnoreEoL(char c) {
+ if (!Character.isWhitespace(c)) {
+ sb.append(c);
+ }
+ }
+
+ @Override
+ public void newLine() {
+ processBuffer();
+ sb.setLength(0);
+ line++;
+ }
+
+ @Override
+ public void eof() {
+ if (this.line > 0) {
+ processBuffer();
+ }
+ }
+
+ private void processBuffer() {
+ try {
+ if (sb.length() > 0) {
+ ByteBuffer encoded = encoder.encode(CharBuffer.wrap(sb));
+ lineMd5Digest.update(encoded.array(), 0, encoded.limit());
+ consumer.consume(line, lineMd5Digest.digest());
+ }
+ } catch (CharacterCodingException e) {
+ throw new IllegalStateException("Error encoding line hash in file: " + file.getAbsolutePath(), e);
+ }
+ }
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/charhandler/LineOffsetCounter.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/charhandler/LineOffsetCounter.java
new file mode 100644
index 00000000000..cf39d16267f
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/charhandler/LineOffsetCounter.java
@@ -0,0 +1,60 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program 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.
+ *
+ * This program 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.api.batch.fs.internal.charhandler;
+
+public class LineOffsetCounter extends CharHandler {
+ private long currentOriginalOffset = 0;
+ private IntArrayList originalLineOffsets = new IntArrayList();
+ private long lastValidOffset = 0;
+
+ public LineOffsetCounter() {
+ originalLineOffsets.add(0);
+ }
+
+ @Override
+ public void handleAll(char c) {
+ currentOriginalOffset++;
+ }
+
+ @Override
+ public void newLine() {
+ if (currentOriginalOffset > Integer.MAX_VALUE) {
+ throw new IllegalStateException("File is too big: " + currentOriginalOffset);
+ }
+ originalLineOffsets.add((int) currentOriginalOffset);
+ }
+
+ @Override
+ public void eof() {
+ lastValidOffset = currentOriginalOffset;
+ }
+
+ public int[] getOriginalLineOffsets() {
+ return originalLineOffsets.trimAndGet();
+ }
+
+ public int getLastValidOffset() {
+ if (lastValidOffset > Integer.MAX_VALUE) {
+ throw new IllegalStateException("File is too big: " + lastValidOffset);
+ }
+ return (int) lastValidOffset;
+ }
+
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/charhandler/package-info.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/charhandler/package-info.java
new file mode 100644
index 00000000000..b8d3c63fcff
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/charhandler/package-info.java
@@ -0,0 +1,24 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program 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.
+ *
+ * This program 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.
+ */
+@ParametersAreNonnullByDefault
+package org.sonar.api.batch.fs.internal.charhandler;
+
+import javax.annotation.ParametersAreNonnullByDefault;
+
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/measure/MetricFinder.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/measure/MetricFinder.java
index 7e8dffb5903..c51c5ddf969 100644
--- a/sonar-plugin-api/src/main/java/org/sonar/api/batch/measure/MetricFinder.java
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/batch/measure/MetricFinder.java
@@ -23,12 +23,15 @@ import java.io.Serializable;
import java.util.Collection;
import java.util.List;
import javax.annotation.CheckForNull;
+import javax.annotation.concurrent.ThreadSafe;
+
import org.sonar.api.batch.ScannerSide;
/**
* @since 4.5
*/
@ScannerSide
+@ThreadSafe
public interface MetricFinder {
@CheckForNull
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/rule/ActiveRule.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/rule/ActiveRule.java
index bf96a7a1103..c0df514efc0 100644
--- a/sonar-plugin-api/src/main/java/org/sonar/api/batch/rule/ActiveRule.java
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/batch/rule/ActiveRule.java
@@ -21,12 +21,15 @@ package org.sonar.api.batch.rule;
import java.util.Map;
import javax.annotation.CheckForNull;
+import javax.annotation.concurrent.Immutable;
+
import org.sonar.api.rule.RuleKey;
/**
* Configuration of a rule activated on a Quality profile
* @since 4.2
*/
+@Immutable
public interface ActiveRule {
RuleKey ruleKey();
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/rule/ActiveRules.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/rule/ActiveRules.java
index 1ea2b013ed7..f25c75a5af8 100644
--- a/sonar-plugin-api/src/main/java/org/sonar/api/batch/rule/ActiveRules.java
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/batch/rule/ActiveRules.java
@@ -23,6 +23,7 @@ import org.sonar.api.batch.ScannerSide;
import org.sonar.api.rule.RuleKey;
import javax.annotation.CheckForNull;
+import javax.annotation.concurrent.Immutable;
import java.util.Collection;
@@ -35,6 +36,7 @@ import java.util.Collection;
*
* @since 4.2
*/
+@Immutable
@ScannerSide
public interface ActiveRules {
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/rule/RuleParam.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/rule/RuleParam.java
index 7c06501dfbe..4781bb39fdd 100644
--- a/sonar-plugin-api/src/main/java/org/sonar/api/batch/rule/RuleParam.java
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/batch/rule/RuleParam.java
@@ -19,10 +19,14 @@
*/
package org.sonar.api.batch.rule;
+import javax.annotation.concurrent.Immutable;
+
/**
* @since 4.2
*/
+@Immutable
public interface RuleParam {
String key();
+
String description();
}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/rule/Rules.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/rule/Rules.java
index 20b755c6fc7..c73cc867eee 100644
--- a/sonar-plugin-api/src/main/java/org/sonar/api/batch/rule/Rules.java
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/batch/rule/Rules.java
@@ -23,6 +23,7 @@ import org.sonar.api.batch.ScannerSide;
import org.sonar.api.rule.RuleKey;
import javax.annotation.CheckForNull;
+import javax.annotation.concurrent.Immutable;
import java.util.Collection;
@@ -33,6 +34,7 @@ import java.util.Collection;
* @since 4.2
*/
@ScannerSide
+@Immutable
public interface Rules {
/**
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/rule/internal/DefaultActiveRules.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/rule/internal/DefaultActiveRules.java
index 9f4fd4bdbb0..981ebbbedde 100644
--- a/sonar-plugin-api/src/main/java/org/sonar/api/batch/rule/internal/DefaultActiveRules.java
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/batch/rule/internal/DefaultActiveRules.java
@@ -20,7 +20,6 @@
package org.sonar.api.batch.rule.internal;
import com.google.common.collect.ImmutableListMultimap;
-import com.google.common.collect.ListMultimap;
import org.sonar.api.batch.rule.ActiveRule;
import org.sonar.api.batch.rule.ActiveRules;
import org.sonar.api.rule.RuleKey;
@@ -28,35 +27,32 @@ import org.sonar.api.rule.RuleKey;
import javax.annotation.concurrent.Immutable;
import java.util.Collection;
+import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
@Immutable
public class DefaultActiveRules implements ActiveRules {
-
- // TODO use disk-backed cache (persistit) instead of full in-memory cache ?
- private final ListMultimap<String, ActiveRule> activeRulesByRepository;
+ private final ImmutableListMultimap<String, ActiveRule> activeRulesByRepository;
private final Map<String, Map<String, ActiveRule>> activeRulesByRepositoryAndKey = new HashMap<>();
private final Map<String, Map<String, ActiveRule>> activeRulesByRepositoryAndInternalKey = new HashMap<>();
- private final ListMultimap<String, ActiveRule> activeRulesByLanguage;
+ private final ImmutableListMultimap<String, ActiveRule> activeRulesByLanguage;
public DefaultActiveRules(Collection<NewActiveRule> newActiveRules) {
ImmutableListMultimap.Builder<String, ActiveRule> repoBuilder = ImmutableListMultimap.builder();
ImmutableListMultimap.Builder<String, ActiveRule> langBuilder = ImmutableListMultimap.builder();
for (NewActiveRule newAR : newActiveRules) {
DefaultActiveRule ar = new DefaultActiveRule(newAR);
- repoBuilder.put(ar.ruleKey().repository(), ar);
+ String repo = ar.ruleKey().repository();
+ repoBuilder.put(repo, ar);
if (ar.language() != null) {
langBuilder.put(ar.language(), ar);
}
- if (!activeRulesByRepositoryAndKey.containsKey(ar.ruleKey().repository())) {
- activeRulesByRepositoryAndKey.put(ar.ruleKey().repository(), new HashMap<String, ActiveRule>());
- activeRulesByRepositoryAndInternalKey.put(ar.ruleKey().repository(), new HashMap<String, ActiveRule>());
- }
- activeRulesByRepositoryAndKey.get(ar.ruleKey().repository()).put(ar.ruleKey().rule(), ar);
+
+ activeRulesByRepositoryAndKey.computeIfAbsent(repo, r -> new HashMap<>()).put(ar.ruleKey().rule(), ar);
String internalKey = ar.internalKey();
if (internalKey != null) {
- activeRulesByRepositoryAndInternalKey.get(ar.ruleKey().repository()).put(internalKey, ar);
+ activeRulesByRepositoryAndInternalKey.computeIfAbsent(repo, r -> new HashMap<>()).put(internalKey, ar);
}
}
activeRulesByRepository = repoBuilder.build();
@@ -65,11 +61,8 @@ public class DefaultActiveRules implements ActiveRules {
@Override
public ActiveRule find(RuleKey ruleKey) {
- Map<String, ActiveRule> map = activeRulesByRepositoryAndKey.get(ruleKey.repository());
- if(map != null) {
- return map.get(ruleKey.rule());
- }
- return null;
+ return activeRulesByRepositoryAndKey.getOrDefault(ruleKey.repository(), Collections.emptyMap())
+ .get(ruleKey.rule());
}
@Override
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/rule/internal/DefaultRules.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/rule/internal/DefaultRules.java
index 37795039e83..1b46101fe24 100644
--- a/sonar-plugin-api/src/main/java/org/sonar/api/batch/rule/internal/DefaultRules.java
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/batch/rule/internal/DefaultRules.java
@@ -25,7 +25,6 @@ import com.google.common.collect.HashBasedTable;
import org.sonar.api.batch.rule.Rule;
import com.google.common.collect.Table;
import com.google.common.collect.ImmutableListMultimap;
-import com.google.common.collect.ListMultimap;
import org.apache.commons.lang.StringUtils;
import org.sonar.api.batch.rule.Rules;
import org.sonar.api.rule.RuleKey;
@@ -39,9 +38,7 @@ import java.util.List;
@Immutable
class DefaultRules implements Rules {
-
- // TODO use disk-backed cache (persistit) instead of full in-memory cache ?
- private final ListMultimap<String, Rule> rulesByRepository;
+ private final ImmutableListMultimap<String, Rule> rulesByRepository;
private final ImmutableTable<String, String, List<Rule>> rulesByRepositoryAndInternalKey;
DefaultRules(Collection<NewRule> newRules) {
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/cpd/internal/DefaultCpdTokens.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/cpd/internal/DefaultCpdTokens.java
index abdb03c9ecc..88e9c1d6b13 100644
--- a/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/cpd/internal/DefaultCpdTokens.java
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/cpd/internal/DefaultCpdTokens.java
@@ -57,7 +57,7 @@ public class DefaultCpdTokens extends DefaultStorable implements NewCpdTokens {
this.inputFile = requireNonNull(inputFile, "file can't be null");
String[] cpdExclusions = config.getStringArray(CoreProperties.CPD_EXCLUSIONS);
for (PathPattern cpdExclusion : PathPattern.create(cpdExclusions)) {
- if (cpdExclusion.match(inputFile)) {
+ if (cpdExclusion.match(inputFile.absolutePath(), inputFile.relativePath())) {
this.excluded = true;
}
}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/resources/Project.java b/sonar-plugin-api/src/main/java/org/sonar/api/resources/Project.java
index 4cb9ded0ef5..65752a177c6 100644
--- a/sonar-plugin-api/src/main/java/org/sonar/api/resources/Project.java
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/resources/Project.java
@@ -28,6 +28,7 @@ import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.builder.ToStringBuilder;
import org.sonar.api.batch.bootstrap.ProjectDefinition;
import org.sonar.api.batch.fs.InputModule;
+import org.sonar.api.batch.fs.internal.DefaultInputModule;
import org.sonar.api.component.Component;
import org.sonar.api.scan.filesystem.PathResolver;
@@ -39,6 +40,10 @@ import org.sonar.api.scan.filesystem.PathResolver;
public class Project extends Resource implements Component {
private final ProjectDefinition definition;
+ public Project(DefaultInputModule module) {
+ this(module.definition());
+ }
+
public Project(ProjectDefinition definition) {
this.definition = definition;
this.setKey(definition.getKey());
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/scan/filesystem/PathResolver.java b/sonar-plugin-api/src/main/java/org/sonar/api/scan/filesystem/PathResolver.java
index fdc1dd19afd..17698835e8e 100644
--- a/sonar-plugin-api/src/main/java/org/sonar/api/scan/filesystem/PathResolver.java
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/scan/filesystem/PathResolver.java
@@ -25,6 +25,8 @@ import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import javax.annotation.CheckForNull;
+import javax.annotation.concurrent.Immutable;
+
import org.apache.commons.io.FilenameUtils;
import org.sonar.api.batch.ScannerSide;
import org.sonar.api.utils.PathUtils;
@@ -35,6 +37,7 @@ import static java.util.stream.Collectors.joining;
* @since 3.5
*/
@ScannerSide
+@Immutable
public class PathResolver {
public File relativeFile(File dir, String path) {
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/scan/issue/filter/FilterableIssue.java b/sonar-plugin-api/src/main/java/org/sonar/api/scan/issue/filter/FilterableIssue.java
index 6928b75e036..76e1a74ec29 100644
--- a/sonar-plugin-api/src/main/java/org/sonar/api/scan/issue/filter/FilterableIssue.java
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/scan/issue/filter/FilterableIssue.java
@@ -22,12 +22,14 @@ package org.sonar.api.scan.issue.filter;
import java.util.Date;
import javax.annotation.CheckForNull;
+import javax.annotation.concurrent.ThreadSafe;
import org.sonar.api.rule.RuleKey;
/**
* @since 5.3
*/
+@ThreadSafe
public interface FilterableIssue {
String componentKey();
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/scan/issue/filter/IssueFilter.java b/sonar-plugin-api/src/main/java/org/sonar/api/scan/issue/filter/IssueFilter.java
index 230ad24605d..0c2847f7a1e 100644
--- a/sonar-plugin-api/src/main/java/org/sonar/api/scan/issue/filter/IssueFilter.java
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/scan/issue/filter/IssueFilter.java
@@ -19,6 +19,9 @@
*/
package org.sonar.api.scan.issue.filter;
+
+import javax.annotation.concurrent.ThreadSafe;
+
import org.sonar.api.ExtensionPoint;
import org.sonar.api.batch.ScannerSide;
import org.sonarsource.api.sonarlint.SonarLintSide;
@@ -27,6 +30,7 @@ import org.sonarsource.api.sonarlint.SonarLintSide;
@SonarLintSide
@ExtensionPoint
@FunctionalInterface
+@ThreadSafe
/**
* @since 5.3
*/
@@ -40,6 +44,9 @@ public interface IssueFilter {
* </ul>
* The <code>chain</code> parameter allows for fine control of the filtering logic: it is each filter's duty to either pass the issue to the next filter, by calling
* the {@link IssueFilterChain#accept} method, or return directly if the issue has to be accepted or not
+ *
+ * Implementations should be thread safe.
+ *
* @param issue the issue being filtered
* @param chain the rest of the filters
* @return <code>true</code> to accept the issue, <code>false</code> to reject it, {@link IssueFilterChain#accept} to let the other filters decide.
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/scan/issue/filter/IssueFilterChain.java b/sonar-plugin-api/src/main/java/org/sonar/api/scan/issue/filter/IssueFilterChain.java
index aab918618ce..02b6943b63f 100644
--- a/sonar-plugin-api/src/main/java/org/sonar/api/scan/issue/filter/IssueFilterChain.java
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/scan/issue/filter/IssueFilterChain.java
@@ -19,6 +19,8 @@
*/
package org.sonar.api.scan.issue.filter;
+import javax.annotation.concurrent.ThreadSafe;
+
/**
* A filter chain is an object provided to issues filters for fine control over the filtering logic. Each filter has the choice to:
* <ul>
@@ -29,6 +31,7 @@ package org.sonar.api.scan.issue.filter;
*
* @since 5.3
*/
+@ThreadSafe
public interface IssueFilterChain {
/**
* Called by a filter to let downstream filters decide the fate of the issue
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/utils/WildcardPattern.java b/sonar-plugin-api/src/main/java/org/sonar/api/utils/WildcardPattern.java
index a1a0f064569..310e788d36d 100644
--- a/sonar-plugin-api/src/main/java/org/sonar/api/utils/WildcardPattern.java
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/utils/WildcardPattern.java
@@ -19,10 +19,13 @@
*/
package org.sonar.api.utils;
+import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.regex.Pattern;
import javax.annotation.Nullable;
+import javax.annotation.concurrent.ThreadSafe;
+
import org.apache.commons.lang.StringUtils;
/**
@@ -54,12 +57,12 @@ import org.apache.commons.lang.StringUtils;
* <a href="https://github.com/JetBrains/intellij-community/blob/idea/107.743/platform/util/src/com/intellij/openapi/util/io/FileUtil.java#L847">FileUtil</a>
* from IntelliJ OpenAPI.
*
- *
* @since 1.10
*/
+@ThreadSafe
public class WildcardPattern {
- private static final Map<String, WildcardPattern> CACHE = new HashMap<>();
+ private static final Map<String, WildcardPattern> CACHE = Collections.synchronizedMap(new HashMap<>());
private static final String SPECIAL_CHARS = "()[]^$.{}+|";
private Pattern pattern;
@@ -196,11 +199,6 @@ public class WildcardPattern {
*/
public static WildcardPattern create(String pattern, String directorySeparator) {
String key = pattern + directorySeparator;
- WildcardPattern wildcardPattern = CACHE.get(key);
- if (wildcardPattern == null) {
- wildcardPattern = new WildcardPattern(pattern, directorySeparator);
- CACHE.put(key, wildcardPattern);
- }
- return wildcardPattern;
+ return CACHE.computeIfAbsent(key, k -> new WildcardPattern(pattern, directorySeparator));
}
}