diff options
author | Julien HENRY <julien.henry@sonarsource.com> | 2014-06-12 09:38:46 +0200 |
---|---|---|
committer | Julien HENRY <julien.henry@sonarsource.com> | 2014-06-16 09:42:46 +0200 |
commit | d35dc73824f38e3585b44d8b7ee9df867bccec5b (patch) | |
tree | 1248c5da0843b6218c677f1a375a82e3bb67b383 | |
parent | 74e76cbd9f07045561a219efe49f220253508ddb (diff) | |
download | sonarqube-d35dc73824f38e3585b44d8b7ee9df867bccec5b.tar.gz sonarqube-d35dc73824f38e3585b44d8b7ee9df867bccec5b.zip |
SONAR-5389 New Analyzer API
158 files changed, 3786 insertions, 1084 deletions
diff --git a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/UserManagedMetrics.java b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/UserManagedMetrics.java index cf5996477f6..1bc1d597a6b 100644 --- a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/UserManagedMetrics.java +++ b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/UserManagedMetrics.java @@ -29,24 +29,24 @@ public final class UserManagedMetrics implements Metrics { private static final String DOMAIN = "Management"; public List<Metric> getMetrics() { - return ImmutableList.of( - new Metric.Builder("burned_budget", "Burned budget", Metric.ValueType.FLOAT) - .setDirection(Metric.DIRECTION_NONE) - .setQualitative(false) - .setDomain(DOMAIN) - .setUserManaged(true) - .create(), - new Metric.Builder("business_value", "Business value", Metric.ValueType.FLOAT) - .setDirection(Metric.DIRECTION_BETTER) - .setQualitative(true) - .setDomain(DOMAIN) - .setUserManaged(true) - .create(), - new Metric.Builder("team_size", "Team size", Metric.ValueType.INT) - .setDirection(Metric.DIRECTION_NONE) - .setQualitative(false) - .setDomain(DOMAIN) - .setUserManaged(true) - .create()); + return ImmutableList.<Metric>of( + new Metric.Builder("burned_budget", "Burned budget", Metric.ValueType.FLOAT) + .setDirection(Metric.DIRECTION_NONE) + .setQualitative(false) + .setDomain(DOMAIN) + .setUserManaged(true) + .create(), + new Metric.Builder("business_value", "Business value", Metric.ValueType.FLOAT) + .setDirection(Metric.DIRECTION_BETTER) + .setQualitative(true) + .setDomain(DOMAIN) + .setUserManaged(true) + .create(), + new Metric.Builder("team_size", "Team size", Metric.ValueType.INT) + .setDirection(Metric.DIRECTION_NONE) + .setQualitative(false) + .setDomain(DOMAIN) + .setUserManaged(true) + .create()); } } diff --git a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/sensors/FileHashSensor.java b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/sensors/FileHashSensor.java index 3e758761c18..b1c5dac5147 100644 --- a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/sensors/FileHashSensor.java +++ b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/sensors/FileHashSensor.java @@ -23,7 +23,7 @@ import com.google.common.collect.Maps; import org.sonar.api.batch.Sensor; import org.sonar.api.batch.SensorContext; import org.sonar.api.batch.fs.InputFile; -import org.sonar.api.batch.fs.internal.DefaultInputFile; +import org.sonar.api.batch.fs.internal.DeprecatedDefaultInputFile; import org.sonar.api.resources.Project; import org.sonar.api.utils.KeyValueFormat; import org.sonar.batch.index.ComponentDataCache; @@ -59,7 +59,7 @@ public final class FileHashSensor implements Sensor { public void analyse(Project project, SensorContext context) { Map<String, String> map = Maps.newHashMap(); for (InputFile inputFile : fileCache.byModule(project.key())) { - String hash = ((DefaultInputFile) inputFile).hash(); + String hash = ((DeprecatedDefaultInputFile) inputFile).hash(); if (hash != null) { map.put(inputFile.relativePath(), hash); } diff --git a/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/sensors/FileHashSensorTest.java b/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/sensors/FileHashSensorTest.java index 4db5159ed48..29f5e64cce9 100644 --- a/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/sensors/FileHashSensorTest.java +++ b/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/sensors/FileHashSensorTest.java @@ -28,7 +28,7 @@ import org.junit.rules.ExpectedException; import org.junit.rules.TemporaryFolder; import org.sonar.api.batch.SensorContext; import org.sonar.api.batch.fs.InputFile; -import org.sonar.api.batch.fs.internal.DefaultInputFile; +import org.sonar.api.batch.fs.internal.DeprecatedDefaultInputFile; import org.sonar.api.resources.Project; import org.sonar.batch.index.ComponentDataCache; import org.sonar.batch.scan.filesystem.InputFileCache; @@ -58,8 +58,8 @@ public class FileHashSensorTest { @Test public void store_file_hashes() throws Exception { when(fileCache.byModule("struts")).thenReturn(Lists.<InputFile>newArrayList( - new DefaultInputFile("src/Foo.java").setFile(temp.newFile()).setHash("ABC"), - new DefaultInputFile("src/Bar.java").setFile(temp.newFile()).setHash("DEF"))); + new DeprecatedDefaultInputFile("src/Foo.java").setFile(temp.newFile()).setHash("ABC"), + new DeprecatedDefaultInputFile("src/Bar.java").setFile(temp.newFile()).setHash("DEF"))); SensorContext sensorContext = mock(SensorContext.class); sensor.analyse(project, sensorContext); @@ -72,8 +72,8 @@ public class FileHashSensorTest { public void store_file_hashes_for_branches() throws Exception { project = new Project("struts", "branch-2.x", "Struts 2.x"); when(fileCache.byModule("struts:branch-2.x")).thenReturn(Lists.<InputFile>newArrayList( - new DefaultInputFile("src/Foo.java").setFile(temp.newFile()).setHash("ABC"), - new DefaultInputFile("src/Bar.java").setFile(temp.newFile()).setHash("DEF"))); + new DeprecatedDefaultInputFile("src/Foo.java").setFile(temp.newFile()).setHash("ABC"), + new DeprecatedDefaultInputFile("src/Bar.java").setFile(temp.newFile()).setHash("DEF"))); SensorContext sensorContext = mock(SensorContext.class); sensor.analyse(project, sensorContext); diff --git a/plugins/sonar-cpd-plugin/src/main/java/org/sonar/plugins/cpd/SonarBridgeEngine.java b/plugins/sonar-cpd-plugin/src/main/java/org/sonar/plugins/cpd/SonarBridgeEngine.java index 7c24a89bb3a..df4bc036115 100644 --- a/plugins/sonar-cpd-plugin/src/main/java/org/sonar/plugins/cpd/SonarBridgeEngine.java +++ b/plugins/sonar-cpd-plugin/src/main/java/org/sonar/plugins/cpd/SonarBridgeEngine.java @@ -31,7 +31,7 @@ import org.sonar.api.batch.SensorContext; import org.sonar.api.batch.fs.FilePredicates; import org.sonar.api.batch.fs.FileSystem; import org.sonar.api.batch.fs.InputFile; -import org.sonar.api.batch.fs.internal.DefaultInputFile; +import org.sonar.api.batch.fs.internal.DeprecatedDefaultInputFile; import org.sonar.api.config.Settings; import org.sonar.api.resources.Project; import org.sonar.api.utils.SonarException; @@ -106,7 +106,7 @@ public class SonarBridgeEngine extends CpdEngine { TokenizerBridge bridge = new TokenizerBridge(mapping.getTokenizer(), fs.encoding().name(), getBlockSize(project, languageKey)); for (InputFile inputFile : sourceFiles) { LOG.debug("Populating index from {}", inputFile); - String resourceEffectiveKey = ((DefaultInputFile) inputFile).key(); + String resourceEffectiveKey = ((DeprecatedDefaultInputFile) inputFile).key(); List<Block> blocks = bridge.chunk(resourceEffectiveKey, inputFile.file()); index.insert(inputFile, blocks); } @@ -118,7 +118,7 @@ public class SonarBridgeEngine extends CpdEngine { try { for (InputFile inputFile : sourceFiles) { LOG.debug("Detection of duplications for {}", inputFile); - String resourceEffectiveKey = ((DefaultInputFile) inputFile).key(); + String resourceEffectiveKey = ((DeprecatedDefaultInputFile) inputFile).key(); Collection<Block> fileBlocks = index.getByInputFile(inputFile, resourceEffectiveKey); Iterable<CloneGroup> filtered; diff --git a/plugins/sonar-cpd-plugin/src/main/java/org/sonar/plugins/cpd/SonarEngine.java b/plugins/sonar-cpd-plugin/src/main/java/org/sonar/plugins/cpd/SonarEngine.java index 8255053db31..130206427fc 100644 --- a/plugins/sonar-cpd-plugin/src/main/java/org/sonar/plugins/cpd/SonarEngine.java +++ b/plugins/sonar-cpd-plugin/src/main/java/org/sonar/plugins/cpd/SonarEngine.java @@ -31,7 +31,7 @@ import org.sonar.api.batch.SensorContext; import org.sonar.api.batch.fs.FilePredicates; import org.sonar.api.batch.fs.FileSystem; import org.sonar.api.batch.fs.InputFile; -import org.sonar.api.batch.fs.internal.DefaultInputFile; +import org.sonar.api.batch.fs.internal.DeprecatedDefaultInputFile; import org.sonar.api.config.Settings; import org.sonar.api.measures.CoreMetrics; import org.sonar.api.measures.Measure; @@ -115,7 +115,7 @@ public class SonarEngine extends CpdEngine { for (InputFile inputFile : sourceFiles) { LOG.debug("Populating index from {}", inputFile); - String resourceEffectiveKey = ((DefaultInputFile) inputFile).key(); + String resourceEffectiveKey = ((DeprecatedDefaultInputFile) inputFile).key(); List<Statement> statements; @@ -141,7 +141,7 @@ public class SonarEngine extends CpdEngine { try { for (InputFile inputFile : sourceFiles) { LOG.debug("Detection of duplications for {}", inputFile); - String resourceEffectiveKey = ((DefaultInputFile) inputFile).key(); + String resourceEffectiveKey = ((DeprecatedDefaultInputFile) inputFile).key(); Collection<Block> fileBlocks = index.getByInputFile(inputFile, resourceEffectiveKey); diff --git a/plugins/sonar-cpd-plugin/src/test/java/org/sonar/plugins/cpd/SonarEngineTest.java b/plugins/sonar-cpd-plugin/src/test/java/org/sonar/plugins/cpd/SonarEngineTest.java index ac2be64ac60..c8551a74166 100644 --- a/plugins/sonar-cpd-plugin/src/test/java/org/sonar/plugins/cpd/SonarEngineTest.java +++ b/plugins/sonar-cpd-plugin/src/test/java/org/sonar/plugins/cpd/SonarEngineTest.java @@ -25,7 +25,7 @@ import org.junit.Test; import org.junit.rules.TemporaryFolder; import org.sonar.api.batch.SensorContext; import org.sonar.api.batch.fs.InputFile; -import org.sonar.api.batch.fs.internal.DefaultInputFile; +import org.sonar.api.batch.fs.internal.DeprecatedDefaultInputFile; import org.sonar.api.measures.CoreMetrics; import org.sonar.api.test.IsMeasure; import org.sonar.duplications.index.CloneGroup; @@ -46,11 +46,11 @@ public class SonarEngineTest { public TemporaryFolder temp = new TemporaryFolder(); SensorContext context = mock(SensorContext.class); - DefaultInputFile inputFile; + DeprecatedDefaultInputFile inputFile; @Before public void before() throws IOException { - inputFile = new DefaultInputFile("src/main/java/Foo.java"); + inputFile = new DeprecatedDefaultInputFile("src/main/java/Foo.java"); inputFile.setFile(temp.newFile("Foo.java")); } @@ -138,7 +138,7 @@ public class SonarEngineTest { @Test public void shouldEscapeXmlEntities() throws IOException { - InputFile csharpFile = new DefaultInputFile("Loads/File Loads/Subs & Reds/SubsRedsDelivery.cs") + InputFile csharpFile = new DeprecatedDefaultInputFile("Loads/File Loads/Subs & Reds/SubsRedsDelivery.cs") .setFile(temp.newFile("SubsRedsDelivery.cs")); List<CloneGroup> groups = Arrays.asList(newCloneGroup( new ClonePart("Loads/File Loads/Subs & Reds/SubsRedsDelivery.cs", 0, 5, 204), @@ -14,6 +14,7 @@ <modules> <module>sonar-application</module> <module>sonar-batch</module> + <module>sonar-batch-plugin-api</module> <module>sonar-batch-maven-compat</module> <module>sonar-check-api</module> <module>sonar-colorizer</module> @@ -576,6 +577,11 @@ </dependency> <dependency> <groupId>org.codehaus.sonar</groupId> + <artifactId>sonar-batch-plugin-api</artifactId> + <version>${project.version}</version> + </dependency> + <dependency> + <groupId>org.codehaus.sonar</groupId> <artifactId>sonar-update-center-common</artifactId> <version>${sonarUpdateCenter.version}</version> </dependency> diff --git a/sonar-batch-plugin-api/pom.xml b/sonar-batch-plugin-api/pom.xml new file mode 100644 index 00000000000..f90ff635f9c --- /dev/null +++ b/sonar-batch-plugin-api/pom.xml @@ -0,0 +1,34 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + <modelVersion>4.0.0</modelVersion> + + <parent> + <groupId>org.codehaus.sonar</groupId> + <artifactId>sonar</artifactId> + <version>4.4-SNAPSHOT</version> + </parent> + + <artifactId>sonar-batch-plugin-api</artifactId> + <packaging>jar</packaging> + + <name>SonarQube :: Batch Plugin API</name> + + <dependencies> + <dependency> + <groupId>com.google.code.findbugs</groupId> + <artifactId>jsr305</artifactId> + </dependency> + + <dependency> + <groupId>junit</groupId> + <artifactId>junit</artifactId> + </dependency> + <dependency> + <groupId>org.easytesting</groupId> + <artifactId>fest-assert</artifactId> + </dependency> + </dependencies> + + +</project> diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/FilePredicate.java b/sonar-batch-plugin-api/src/main/java/org/sonar/api/batch/fs/FilePredicate.java index 830859ed3ba..830859ed3ba 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/FilePredicate.java +++ b/sonar-batch-plugin-api/src/main/java/org/sonar/api/batch/fs/FilePredicate.java diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/FilePredicates.java b/sonar-batch-plugin-api/src/main/java/org/sonar/api/batch/fs/FilePredicates.java index dd9f14833ca..65a2e19d6a5 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/FilePredicates.java +++ b/sonar-batch-plugin-api/src/main/java/org/sonar/api/batch/fs/FilePredicates.java @@ -64,6 +64,8 @@ public interface FilePredicates { FilePredicate hasLanguages(Collection<String> languages); + FilePredicate hasLanguages(String... languages); + FilePredicate hasStatus(InputFile.Status status); FilePredicate hasType(InputFile.Type type); diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/FileSystem.java b/sonar-batch-plugin-api/src/main/java/org/sonar/api/batch/fs/FileSystem.java index 5172eb8d542..0c050f038c2 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/FileSystem.java +++ b/sonar-batch-plugin-api/src/main/java/org/sonar/api/batch/fs/FileSystem.java @@ -19,9 +19,10 @@ */ package org.sonar.api.batch.fs; -import org.sonar.api.BatchComponent; +import org.sonar.batch.api.BatchComponent; import javax.annotation.CheckForNull; + import java.io.File; import java.nio.charset.Charset; import java.util.SortedSet; diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/InputFile.java b/sonar-batch-plugin-api/src/main/java/org/sonar/api/batch/fs/InputFile.java index e4728a44408..83e9697f15f 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/InputFile.java +++ b/sonar-batch-plugin-api/src/main/java/org/sonar/api/batch/fs/InputFile.java @@ -33,7 +33,7 @@ public interface InputFile extends Serializable { MAIN, TEST } - /** + /** * Status regarding previous analysis */ enum Status { diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/InputFileFilter.java b/sonar-batch-plugin-api/src/main/java/org/sonar/api/batch/fs/InputFileFilter.java index 1ece5081290..166af9ea64b 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/InputFileFilter.java +++ b/sonar-batch-plugin-api/src/main/java/org/sonar/api/batch/fs/InputFileFilter.java @@ -19,7 +19,7 @@ */ package org.sonar.api.batch.fs; -import org.sonar.api.BatchExtension; +import org.sonar.batch.api.BatchExtension; /** * Extension point to exclude some files from inspection diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/AbsolutePathPredicate.java b/sonar-batch-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/AbsolutePathPredicate.java index 8e943a0f5f8..367c1eabdeb 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/AbsolutePathPredicate.java +++ b/sonar-batch-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/AbsolutePathPredicate.java @@ -19,9 +19,9 @@ */ package org.sonar.api.batch.fs.internal; -import org.apache.commons.io.FilenameUtils; import org.sonar.api.batch.fs.FilePredicate; import org.sonar.api.batch.fs.InputFile; +import org.sonar.batch.api.internal.FilenameUtils; /** * @since 4.2 @@ -31,7 +31,7 @@ class AbsolutePathPredicate implements FilePredicate { private final String path; AbsolutePathPredicate(String path) { - this.path = FilenameUtils.normalize(path, true); + this.path = FilenameUtils.normalize(path); } @Override diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/AndPredicate.java b/sonar-batch-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/AndPredicate.java index 0494ed524dc..0494ed524dc 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/AndPredicate.java +++ b/sonar-batch-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/AndPredicate.java diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/DefaultFilePredicates.java b/sonar-batch-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/DefaultFilePredicates.java index 52bb75ad9b2..8036d90a3ea 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/DefaultFilePredicates.java +++ b/sonar-batch-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/DefaultFilePredicates.java @@ -19,12 +19,12 @@ */ package org.sonar.api.batch.fs.internal; -import com.google.common.collect.Lists; import org.sonar.api.batch.fs.FilePredicate; import org.sonar.api.batch.fs.FilePredicates; import org.sonar.api.batch.fs.InputFile; import java.io.File; +import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.List; @@ -115,7 +115,15 @@ public class DefaultFilePredicates implements FilePredicates { } public FilePredicate hasLanguages(Collection<String> languages) { - List<FilePredicate> list = Lists.newArrayList(); + List<FilePredicate> list = new ArrayList<FilePredicate>(); + for (String language : languages) { + list.add(hasLanguage(language)); + } + return or(list); + } + + public FilePredicate hasLanguages(String... languages) { + List<FilePredicate> list = new ArrayList<FilePredicate>(); for (String language : languages) { list.add(hasLanguage(language)); } diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/DefaultFileSystem.java b/sonar-batch-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/DefaultFileSystem.java index 3a97f46f757..89abb9e3ccf 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/DefaultFileSystem.java +++ b/sonar-batch-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/DefaultFileSystem.java @@ -19,27 +19,25 @@ */ package org.sonar.api.batch.fs.internal; -import com.google.common.base.Function; -import com.google.common.base.Preconditions; -import com.google.common.base.Predicate; -import com.google.common.collect.Iterables; -import com.google.common.collect.Lists; -import com.google.common.collect.Maps; -import com.google.common.collect.Sets; import org.sonar.api.batch.fs.FilePredicate; import org.sonar.api.batch.fs.FilePredicates; import org.sonar.api.batch.fs.FileSystem; import org.sonar.api.batch.fs.InputFile; +import org.sonar.batch.api.internal.Preconditions; import javax.annotation.CheckForNull; import javax.annotation.Nullable; + import java.io.File; import java.nio.charset.Charset; +import java.util.ArrayList; +import java.util.Collection; import java.util.Collections; -import java.util.List; +import java.util.HashMap; +import java.util.Iterator; import java.util.Map; -import java.util.NoSuchElementException; import java.util.SortedSet; +import java.util.TreeSet; /** * @since 4.2 @@ -47,7 +45,7 @@ import java.util.SortedSet; public class DefaultFileSystem implements FileSystem { private final Cache cache; - private final SortedSet<String> languages = Sets.newTreeSet(); + private final SortedSet<String> languages = new TreeSet<String>(); private File baseDir, workDir; private Charset encoding; private final FilePredicates predicates = new DefaultFilePredicates(); @@ -101,39 +99,70 @@ public class DefaultFileSystem implements FileSystem { @Override public InputFile inputFile(FilePredicate predicate) { doPreloadFiles(); - if (predicate instanceof UniqueIndexPredicate) { - return cache.inputFile((UniqueIndexPredicate) predicate); + if (predicate instanceof RelativePathPredicate) { + return cache.inputFile((RelativePathPredicate) predicate); } - try { - Iterable<InputFile> files = inputFiles(predicate); - return Iterables.getOnlyElement(files); - } catch (NoSuchElementException e) { - // contrary to guava, return null if iterable is empty + Iterable<InputFile> files = inputFiles(predicate); + Iterator<InputFile> iterator = files.iterator(); + if (!iterator.hasNext()) { return null; } + InputFile first = iterator.next(); + if (!iterator.hasNext()) { + return first; + } + + StringBuilder sb = new StringBuilder(); + sb.append("expected one element but was: <" + first); + for (int i = 0; i < 4 && iterator.hasNext(); i++) { + sb.append(", " + iterator.next()); + } + if (iterator.hasNext()) { + sb.append(", ..."); + } + sb.append('>'); + + throw new IllegalArgumentException(sb.toString()); + } @Override public Iterable<InputFile> inputFiles(FilePredicate predicate) { doPreloadFiles(); - return Iterables.filter(cache.inputFiles(), new GuavaPredicate(predicate)); + return filter(cache.inputFiles(), predicate); } @Override public boolean hasFiles(FilePredicate predicate) { doPreloadFiles(); - return Iterables.indexOf(cache.inputFiles(), new GuavaPredicate(predicate)) >= 0; + for (InputFile element : cache.inputFiles()) { + if (predicate.apply(element)) { + return true; + } + } + return false; } @Override public Iterable<File> files(FilePredicate predicate) { doPreloadFiles(); - return Iterables.transform(inputFiles(predicate), new Function<InputFile, File>() { - @Override - public File apply(@Nullable InputFile input) { - return input == null ? null : input.file(); + Collection<File> result = new ArrayList<File>(); + for (InputFile element : inputFiles(predicate)) { + if (predicate.apply(element)) { + result.add(element.file()); } - }); + } + return result; + } + + public static Collection<InputFile> filter(Iterable<InputFile> target, FilePredicate predicate) { + Collection<InputFile> result = new ArrayList<InputFile>(); + for (InputFile element : target) { + if (predicate.apply(element)) { + result.add(element); + } + } + return result; } /** @@ -179,17 +208,12 @@ public class DefaultFileSystem implements FileSystem { protected abstract Iterable<InputFile> inputFiles(); @CheckForNull - protected abstract InputFile inputFile(UniqueIndexPredicate predicate); + protected abstract InputFile inputFile(RelativePathPredicate predicate); protected abstract void doAdd(InputFile inputFile); - protected abstract void doIndex(String indexId, Object value, InputFile inputFile); - final void add(InputFile inputFile) { doAdd(inputFile); - for (FileIndex index : FileIndex.ALL) { - doIndex(index.id(), index.valueOf(inputFile), inputFile); - } } } @@ -197,49 +221,22 @@ public class DefaultFileSystem implements FileSystem { * Used only for testing */ private static class MapCache extends Cache { - private final List<InputFile> files = Lists.newArrayList(); - private final Map<String, Map<Object, InputFile>> fileMap = Maps.newHashMap(); + private final Map<String, InputFile> fileMap = new HashMap<String, InputFile>(); @Override public Iterable<InputFile> inputFiles() { - return Lists.newArrayList(files); + return new ArrayList<InputFile>(fileMap.values()); } @Override - public InputFile inputFile(UniqueIndexPredicate predicate) { - Map<Object, InputFile> byAttr = fileMap.get(predicate.indexId()); - if (byAttr != null) { - return byAttr.get(predicate.value()); - } - return null; + public InputFile inputFile(RelativePathPredicate predicate) { + return fileMap.get(predicate.path()); } @Override protected void doAdd(InputFile inputFile) { - files.add(inputFile); - } - - @Override - protected void doIndex(String indexId, Object value, InputFile inputFile) { - Map<Object, InputFile> attrValues = fileMap.get(indexId); - if (attrValues == null) { - attrValues = Maps.newHashMap(); - fileMap.put(indexId, attrValues); - } - attrValues.put(value, inputFile); + fileMap.put(inputFile.relativePath(), inputFile); } } - private static class GuavaPredicate implements Predicate<InputFile> { - private final FilePredicate predicate; - - private GuavaPredicate(FilePredicate predicate) { - this.predicate = predicate; - } - - @Override - public boolean apply(@Nullable InputFile input) { - return input != null && predicate.apply(input); - } - } } diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/DefaultInputFile.java b/sonar-batch-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/DefaultInputFile.java index 888ae7ac313..5767d74a374 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/DefaultInputFile.java +++ b/sonar-batch-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/DefaultInputFile.java @@ -19,17 +19,18 @@ */ package org.sonar.api.batch.fs.internal; -import org.apache.commons.io.FilenameUtils; import org.sonar.api.batch.fs.InputFile; -import org.sonar.api.utils.PathUtils; +import org.sonar.batch.api.internal.FilenameUtils; import javax.annotation.CheckForNull; -import java.io.*; + +import java.io.File; +import java.io.Serializable; /** * @since 4.2 */ -public class DefaultInputFile implements InputFile, org.sonar.api.resources.InputFile, Serializable { +public class DefaultInputFile implements InputFile, Serializable { private final String relativePath; private String absolutePath; @@ -39,13 +40,9 @@ public class DefaultInputFile implements InputFile, org.sonar.api.resources.Inpu private String hash; private int lines; private String key; - private String deprecatedKey; - private String sourceDirAbsolutePath; - private String pathRelativeToSourceDir; - private String basedir; public DefaultInputFile(String relativePath) { - this.relativePath = FilenameUtils.normalize(relativePath, true); + this.relativePath = FilenameUtils.normalize(relativePath); } @Override @@ -120,7 +117,7 @@ public class DefaultInputFile implements InputFile, org.sonar.api.resources.Inpu } public DefaultInputFile setAbsolutePath(String s) { - this.absolutePath = FilenameUtils.normalize(s, true); + this.absolutePath = FilenameUtils.normalize(s); return this; } @@ -159,85 +156,12 @@ public class DefaultInputFile implements InputFile, org.sonar.api.resources.Inpu return this; } - /** - * Key used before version 4.2. It can be different than {@link #key} on Java files. - */ - public String deprecatedKey() { - return deprecatedKey; - } - - public DefaultInputFile setDeprecatedKey(String s) { - this.deprecatedKey = s; - return this; - } - - /** - * Used only for backward-compatibility. Meaningless since version 4.2. - */ - public String sourceDirAbsolutePath() { - return sourceDirAbsolutePath; - } - - public DefaultInputFile setSourceDirAbsolutePath(String s) { - this.sourceDirAbsolutePath = FilenameUtils.normalize(s, true); - return this; - } - - /** - * Used only for backward-compatibility. Meaningless since version 4.2. - */ - - public String pathRelativeToSourceDir() { - return pathRelativeToSourceDir; - } - - public DefaultInputFile setPathRelativeToSourceDir(String s) { - this.pathRelativeToSourceDir = FilenameUtils.normalize(s, true); - return this; - } - - /** - * @deprecated in 4.2. Replaced by {@link org.sonar.api.batch.fs.FileSystem#baseDir()} - */ - @Deprecated - @Override - public File getFileBaseDir() { - return new File(basedir); - } - - public void setBasedir(File basedir) { - this.basedir = PathUtils.sanitize(basedir.getAbsolutePath()); - } - - /** - * @deprecated in 4.2. Use {@link #file()} - */ - @Deprecated - @Override - public File getFile() { - return file(); - } - - /** - * @deprecated in 4.2. Use {@link #relativePath()} - */ - @Deprecated - @Override - public String getRelativePath() { - return pathRelativeToSourceDir; - } - - @Override - public InputStream getInputStream() throws FileNotFoundException { - return new BufferedInputStream(new FileInputStream(file())); - } - @Override public boolean equals(Object o) { if (this == o) { return true; } - if (o == null || getClass() != o.getClass()) { + if (!(o instanceof DefaultInputFile)) { return false; } @@ -255,6 +179,3 @@ public class DefaultInputFile implements InputFile, org.sonar.api.resources.Inpu return "[relative=" + relativePath + ", abs=" + absolutePath + "]"; } } - - - diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/FalsePredicate.java b/sonar-batch-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/FalsePredicate.java index b092f848946..b092f848946 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/FalsePredicate.java +++ b/sonar-batch-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/FalsePredicate.java diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/LanguagePredicate.java b/sonar-batch-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/LanguagePredicate.java index 77d2f74887a..77d2f74887a 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/LanguagePredicate.java +++ b/sonar-batch-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/LanguagePredicate.java diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/NotPredicate.java b/sonar-batch-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/NotPredicate.java index 5f2359ee166..5f2359ee166 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/NotPredicate.java +++ b/sonar-batch-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/NotPredicate.java diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/OrPredicate.java b/sonar-batch-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/OrPredicate.java index a887c631cf2..a887c631cf2 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/OrPredicate.java +++ b/sonar-batch-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/OrPredicate.java diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/PathPattern.java b/sonar-batch-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/PathPattern.java index c1b53b68a40..cdcfd8e2028 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/PathPattern.java +++ b/sonar-batch-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/PathPattern.java @@ -19,10 +19,10 @@ */ package org.sonar.api.batch.fs.internal; -import org.apache.commons.io.FilenameUtils; -import org.apache.commons.lang.StringUtils; import org.sonar.api.batch.fs.InputFile; -import org.sonar.api.utils.WildcardPattern; +import org.sonar.batch.api.internal.FilenameUtils; +import org.sonar.batch.api.internal.StringUtils; +import org.sonar.batch.api.internal.WildcardPattern; public abstract class PathPattern { diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/PathPatternPredicate.java b/sonar-batch-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/PathPatternPredicate.java index c2402a43cfe..0cf614bcb09 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/PathPatternPredicate.java +++ b/sonar-batch-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/PathPatternPredicate.java @@ -21,7 +21,6 @@ package org.sonar.api.batch.fs.internal; import org.sonar.api.batch.fs.FilePredicate; import org.sonar.api.batch.fs.InputFile; -import org.sonar.api.batch.fs.internal.PathPattern; /** * @since 4.2 diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/RelativePathPredicate.java b/sonar-batch-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/RelativePathPredicate.java index 22aed6e09a5..d0c4a3d3fba 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/RelativePathPredicate.java +++ b/sonar-batch-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/RelativePathPredicate.java @@ -19,34 +19,28 @@ */ package org.sonar.api.batch.fs.internal; -import org.apache.commons.io.FilenameUtils; import org.sonar.api.batch.fs.FilePredicate; import org.sonar.api.batch.fs.InputFile; +import org.sonar.batch.api.internal.FilenameUtils; /** * @since 4.2 */ -class RelativePathPredicate implements FilePredicate, UniqueIndexPredicate { +public class RelativePathPredicate implements FilePredicate { private final String path; RelativePathPredicate(String path) { - this.path = FilenameUtils.normalize(path, true); + this.path = FilenameUtils.normalize(path); } - @Override - public boolean apply(InputFile f) { - return path.equals(f.relativePath()); - } - - @Override - public Object value() { + public String path() { return path; } @Override - public String indexId() { - return RelativePathIndex.ID; + public boolean apply(InputFile f) { + return path.equals(f.relativePath()); } } diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/StatusPredicate.java b/sonar-batch-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/StatusPredicate.java index fe7c934071e..fe7c934071e 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/StatusPredicate.java +++ b/sonar-batch-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/StatusPredicate.java diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/TruePredicate.java b/sonar-batch-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/TruePredicate.java index ec2aebd624d..ec2aebd624d 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/TruePredicate.java +++ b/sonar-batch-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/TruePredicate.java diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/TypePredicate.java b/sonar-batch-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/TypePredicate.java index ac8b6d5fe8d..ac8b6d5fe8d 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/TypePredicate.java +++ b/sonar-batch-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/TypePredicate.java diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/package-info.java b/sonar-batch-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/package-info.java index 8fac5590440..713fe6131ca 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/package-info.java +++ b/sonar-batch-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/package-info.java @@ -21,3 +21,4 @@ package org.sonar.api.batch.fs.internal; import javax.annotation.ParametersAreNonnullByDefault; + diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/package-info.java b/sonar-batch-plugin-api/src/main/java/org/sonar/api/batch/fs/package-info.java index 197da9925ac..c97db582333 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/package-info.java +++ b/sonar-batch-plugin-api/src/main/java/org/sonar/api/batch/fs/package-info.java @@ -21,3 +21,4 @@ package org.sonar.api.batch.fs; import javax.annotation.ParametersAreNonnullByDefault; + diff --git a/sonar-batch-plugin-api/src/main/java/org/sonar/batch/api/BatchComponent.java b/sonar-batch-plugin-api/src/main/java/org/sonar/batch/api/BatchComponent.java new file mode 100644 index 00000000000..d48e2253190 --- /dev/null +++ b/sonar-batch-plugin-api/src/main/java/org/sonar/batch/api/BatchComponent.java @@ -0,0 +1,29 @@ +/* + * 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.api; + +/** + * Dependency Injection : all the classes implementing this interface are available in the batch IoC container. + * Just add a parameter to the constructor of your component. + * + * @since 4.4 + */ +public interface BatchComponent { +} diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/UniqueIndexPredicate.java b/sonar-batch-plugin-api/src/main/java/org/sonar/batch/api/BatchExtension.java index 8b7404d2571..1a774a16f5d 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/UniqueIndexPredicate.java +++ b/sonar-batch-plugin-api/src/main/java/org/sonar/batch/api/BatchExtension.java @@ -17,15 +17,14 @@ * 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.batch.api; + /** - * @since 4.2 + * Batch extension point. + * + * @since 4.4 */ -public interface UniqueIndexPredicate { - - String indexId(); - - Object value(); +public interface BatchExtension extends BatchComponent { } diff --git a/sonar-batch-plugin-api/src/main/java/org/sonar/batch/api/InstantiationStrategy.java b/sonar-batch-plugin-api/src/main/java/org/sonar/batch/api/InstantiationStrategy.java new file mode 100644 index 00000000000..521c6ddd61f --- /dev/null +++ b/sonar-batch-plugin-api/src/main/java/org/sonar/batch/api/InstantiationStrategy.java @@ -0,0 +1,46 @@ +/* + * 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.api; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Define instantiation strategy of batch extensions. If an extension is not annotated, then default value + * is {@link #PER_PROJECT}. + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.TYPE) +public @interface InstantiationStrategy { + + /** + * Shared extension. Lifecycle is the full analysis. + */ + String PER_BATCH = "PER_BATCH"; + + /** + * Created and initialized for each project and sub-project (a project is a module in Maven terminology). + */ + String PER_PROJECT = "PER_PROJECT"; + + String value(); +} diff --git a/sonar-batch-plugin-api/src/main/java/org/sonar/batch/api/analyzer/Analyzer.java b/sonar-batch-plugin-api/src/main/java/org/sonar/batch/api/analyzer/Analyzer.java new file mode 100644 index 00000000000..612354560a3 --- /dev/null +++ b/sonar-batch-plugin-api/src/main/java/org/sonar/batch/api/analyzer/Analyzer.java @@ -0,0 +1,51 @@ +/* + * 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.api.analyzer; + +import org.sonar.batch.api.BatchExtension; + +/** + * <p> + * An Analyzer is invoked once during the analysis of a project. The analyzer can parse a flat file, connect to a web server... Analyzers are + * used to add measure and issues at file level. + * </p> + * + * <p> + * For example the Cobertura Analyzer parses Cobertura report and saves the first-level of measures on files. + * </p> + * + * @since 4.4 + */ +public interface Analyzer extends BatchExtension { + + /** + * Describe what this analyzer is doing. + * @return + */ + AnalyzerDescriptor describe(); + + /** + * The method that is going to be run when the analyzer is called + * + * @param context the context + */ + void analyse(AnalyzerContext context); + +} diff --git a/sonar-batch-plugin-api/src/main/java/org/sonar/batch/api/analyzer/AnalyzerContext.java b/sonar-batch-plugin-api/src/main/java/org/sonar/batch/api/analyzer/AnalyzerContext.java new file mode 100644 index 00000000000..e29dfaf711d --- /dev/null +++ b/sonar-batch-plugin-api/src/main/java/org/sonar/batch/api/analyzer/AnalyzerContext.java @@ -0,0 +1,80 @@ +/* + * 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.api.analyzer; + +import org.sonar.api.batch.fs.InputFile; +import org.sonar.batch.api.analyzer.issue.AnalyzerIssue; +import org.sonar.batch.api.analyzer.measure.AnalyzerMeasure; +import org.sonar.batch.api.measures.Metric; + +import javax.annotation.CheckForNull; + +import java.io.Serializable; +import java.util.Collection; + +/** + * @since 4.4 + */ +public interface AnalyzerContext { + + // ----------- MEASURES -------------- + + /** + * Find a project measure. + */ + @CheckForNull + AnalyzerMeasure<?> getMeasure(String metricKey); + + /** + * Find a project measure. + */ + @CheckForNull + <G extends Serializable> AnalyzerMeasure<G> getMeasure(Metric<G> metric); + + /** + * Find a file measure. + */ + @CheckForNull + AnalyzerMeasure<?> getMeasure(InputFile file, String metricKey); + + /** + * Find a file measure. + */ + @CheckForNull + <G extends Serializable> AnalyzerMeasure<G> getMeasure(InputFile file, Metric<G> metric); + + /** + * Add a measure. + */ + void addMeasure(AnalyzerMeasure<?> measure); + + // ----------- ISSUES -------------- + + /** + * Add an issue. + */ + void addIssue(AnalyzerIssue issue); + + /** + * Add a list of issues. + */ + void addIssues(Collection<AnalyzerIssue> issues); + +} diff --git a/sonar-batch-plugin-api/src/main/java/org/sonar/batch/api/analyzer/AnalyzerDescriptor.java b/sonar-batch-plugin-api/src/main/java/org/sonar/batch/api/analyzer/AnalyzerDescriptor.java new file mode 100644 index 00000000000..0b97200dd10 --- /dev/null +++ b/sonar-batch-plugin-api/src/main/java/org/sonar/batch/api/analyzer/AnalyzerDescriptor.java @@ -0,0 +1,107 @@ +/* + * 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.api.analyzer; + +import org.sonar.api.batch.fs.InputFile; +import org.sonar.batch.api.measures.Metric; + +import java.util.Arrays; +import java.util.Collection; + +public class AnalyzerDescriptor { + + private final String name; + private final Metric<?>[] dependsOn; + private final Metric<?>[] provides; + private final String[] languages; + private final InputFile.Type[] types; + + private AnalyzerDescriptor(Builder builder) { + this.name = builder.name; + this.dependsOn = builder.dependsOn != null ? builder.dependsOn : new Metric<?>[0]; + this.provides = builder.provides != null ? builder.provides : new Metric<?>[0]; + this.languages = builder.languages != null ? builder.languages : new String[0]; + this.types = builder.types; + } + + public String name() { + return name; + } + + public Metric<?>[] dependsOn() { + return dependsOn; + } + + public Metric<?>[] provides() { + return provides; + } + + public Collection<String> languages() { + return Arrays.asList(languages); + } + + public InputFile.Type[] types() { + return types; + } + + public static Builder builder() { + return new Builder(); + } + + public static class Builder { + + private String name; + private Metric<?>[] dependsOn; + private Metric<?>[] provides; + private String[] languages; + private InputFile.Type[] types; + + public Builder name(String name) { + this.name = name; + return this; + } + + public Builder dependsOn(Metric<?>... metrics) { + this.dependsOn = metrics; + return this; + } + + public Builder provides(Metric<?>... metrics) { + this.provides = metrics; + return this; + } + + public Builder runOnLanguages(String... languageKeys) { + this.languages = languageKeys; + return this; + } + + public Builder runOnTypes(InputFile.Type... types) { + this.types = types; + return this; + } + + public AnalyzerDescriptor build() { + return new AnalyzerDescriptor(this); + } + + } + +} diff --git a/sonar-batch-plugin-api/src/main/java/org/sonar/batch/api/analyzer/issue/AnalyzerIssue.java b/sonar-batch-plugin-api/src/main/java/org/sonar/batch/api/analyzer/issue/AnalyzerIssue.java new file mode 100644 index 00000000000..f39d88493fe --- /dev/null +++ b/sonar-batch-plugin-api/src/main/java/org/sonar/batch/api/analyzer/issue/AnalyzerIssue.java @@ -0,0 +1,126 @@ +/* + * 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.api.analyzer.issue; + +import org.sonar.api.batch.fs.InputFile; +import org.sonar.batch.api.analyzer.Analyzer; +import org.sonar.batch.api.internal.Preconditions; +import org.sonar.batch.api.rules.RuleKey; + +import javax.annotation.Nullable; + +/** + * Issue reported by an {@link Analyzer} + * + * @since 4.4 + */ +public class AnalyzerIssue { + + private final InputFile inputFile; + private final RuleKey ruleKey; + private final String message; + private final Integer line; + private final Double effortToFix; + + private AnalyzerIssue(Builder builder) { + this.inputFile = builder.file; + this.ruleKey = builder.ruleKey; + this.message = builder.message; + this.line = builder.line; + this.effortToFix = builder.effortToFix; + } + + public static Builder builder() { + return new Builder(); + } + + @Nullable + public InputFile inputFile() { + return inputFile; + } + + public RuleKey ruleKey() { + return ruleKey; + } + + public String message() { + return message; + } + + public Integer line() { + return line; + } + + @Nullable + public Double effortToFix() { + return effortToFix; + } + + public static class Builder { + + private Boolean onProject = null; + private InputFile file; + private RuleKey ruleKey; + private String message; + private Integer line; + private Double effortToFix; + + public AnalyzerIssue build() { + return new AnalyzerIssue(this); + } + + public Builder ruleKey(RuleKey ruleKey) { + this.ruleKey = ruleKey; + return this; + } + + public Builder onFile(InputFile file) { + Preconditions.checkState(onProject == null, "onFile or onProject can be called only once"); + Preconditions.checkNotNull(file, "InputFile should be non null"); + this.file = file; + this.onProject = false; + return this; + } + + public Builder onProject() { + Preconditions.checkState(onProject == null, "onFile or onProject can be called only once"); + this.file = null; + this.onProject = true; + return this; + } + + public Builder atLine(int line) { + this.line = line; + return this; + } + + public Builder effortToFix(@Nullable Double effortToFix) { + this.effortToFix = effortToFix; + return this; + } + + public Builder message(String message) { + this.message = message; + return this; + } + + } + +} diff --git a/sonar-batch-plugin-api/src/main/java/org/sonar/batch/api/analyzer/issue/package-info.java b/sonar-batch-plugin-api/src/main/java/org/sonar/batch/api/analyzer/issue/package-info.java new file mode 100644 index 00000000000..70a6600e6d1 --- /dev/null +++ b/sonar-batch-plugin-api/src/main/java/org/sonar/batch/api/analyzer/issue/package-info.java @@ -0,0 +1,21 @@ +/* + * 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. + */ +@javax.annotation.ParametersAreNonnullByDefault +package org.sonar.batch.api.analyzer.issue;
\ No newline at end of file diff --git a/sonar-batch-plugin-api/src/main/java/org/sonar/batch/api/analyzer/measure/AnalyzerMeasure.java b/sonar-batch-plugin-api/src/main/java/org/sonar/batch/api/analyzer/measure/AnalyzerMeasure.java new file mode 100644 index 00000000000..3d74dca1b4c --- /dev/null +++ b/sonar-batch-plugin-api/src/main/java/org/sonar/batch/api/analyzer/measure/AnalyzerMeasure.java @@ -0,0 +1,129 @@ +/* + * 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.api.analyzer.measure; + +import org.sonar.api.batch.fs.InputFile; +import org.sonar.batch.api.internal.Preconditions; +import org.sonar.batch.api.measures.Metric; + +import javax.annotation.Nullable; + +import java.io.Serializable; + +public class AnalyzerMeasure<G extends Serializable> implements Serializable { + + private final InputFile inputFile; + private final String metricKey; + private final G value; + + private AnalyzerMeasure(Builder<G> builder) { + Preconditions.checkNotNull(builder.value, "Measure value can't be null"); + Preconditions.checkNotNull(builder.metricKey, "Measure metricKey can't be null"); + this.inputFile = builder.file; + this.metricKey = builder.metricKey; + this.value = builder.value; + } + + @Nullable + public InputFile inputFile() { + return inputFile; + } + + public String metricKey() { + return metricKey; + } + + public Serializable value() { + return value; + } + + public static <G extends Serializable> Builder<G> builder() { + return new Builder<G>(); + } + + public static class Builder<G extends Serializable> { + + private Boolean onProject = null; + private InputFile file; + private String metricKey; + private G value; + + public Builder<G> onFile(InputFile file) { + Preconditions.checkState(onProject == null, "onFile or onProject can be called only once"); + Preconditions.checkNotNull(file, "InputFile should be non null"); + this.file = file; + this.onProject = false; + return this; + } + + public Builder<G> onProject() { + Preconditions.checkState(onProject == null, "onFile or onProject can be called only once"); + this.file = null; + this.onProject = true; + return this; + } + + private Builder<G> metricKey(String metricKey) { + Preconditions.checkState(metricKey != null, "Metric already defined"); + this.metricKey = metricKey; + return this; + } + + public Builder<G> forMetric(Metric<G> metric) { + return metricKey(metric.key()); + } + + public Builder<G> withValue(G value) { + Preconditions.checkState(value != null, "Measure value already defined"); + Preconditions.checkNotNull(value, "Measure value can't be null"); + this.value = value; + return this; + } + + public AnalyzerMeasure<G> build() { + return new AnalyzerMeasure<G>(this); + } + } + + @Override + public boolean equals(Object obj) { + if (!(obj instanceof AnalyzerMeasure)) { + return false; + } + AnalyzerMeasure<?> other = (AnalyzerMeasure<?>) obj; + return metricKey.equals(other.metricKey) + && value.equals(other.value) + && (inputFile == null ? other.inputFile == null : inputFile.equals(other.inputFile)); + } + + @Override + public int hashCode() { + return metricKey.hashCode() + + value.hashCode() + + (inputFile != null ? inputFile.hashCode() : 0); + } + + @Override + public String toString() { + return "AnalyzerMeasure[" + (inputFile != null ? "inputFile=" + inputFile.toString() : "onProject") + + ",metricKey=" + metricKey + ",value=" + value + "]"; + } + +} diff --git a/sonar-batch-plugin-api/src/main/java/org/sonar/batch/api/analyzer/measure/package-info.java b/sonar-batch-plugin-api/src/main/java/org/sonar/batch/api/analyzer/measure/package-info.java new file mode 100644 index 00000000000..b490662460a --- /dev/null +++ b/sonar-batch-plugin-api/src/main/java/org/sonar/batch/api/analyzer/measure/package-info.java @@ -0,0 +1,21 @@ +/* + * 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. + */ +@javax.annotation.ParametersAreNonnullByDefault +package org.sonar.batch.api.analyzer.measure;
\ No newline at end of file diff --git a/sonar-batch-plugin-api/src/main/java/org/sonar/batch/api/analyzer/package-info.java b/sonar-batch-plugin-api/src/main/java/org/sonar/batch/api/analyzer/package-info.java new file mode 100644 index 00000000000..5b72f8c6315 --- /dev/null +++ b/sonar-batch-plugin-api/src/main/java/org/sonar/batch/api/analyzer/package-info.java @@ -0,0 +1,21 @@ +/* + * 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. + */ +@javax.annotation.ParametersAreNonnullByDefault +package org.sonar.batch.api.analyzer;
\ No newline at end of file diff --git a/sonar-batch-plugin-api/src/main/java/org/sonar/batch/api/internal/FilenameUtils.java b/sonar-batch-plugin-api/src/main/java/org/sonar/batch/api/internal/FilenameUtils.java new file mode 100644 index 00000000000..0461255879b --- /dev/null +++ b/sonar-batch-plugin-api/src/main/java/org/sonar/batch/api/internal/FilenameUtils.java @@ -0,0 +1,244 @@ +/* + * 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.api.internal; + +import java.io.File; + +/** + * Copied from commons io + * + */ +public class FilenameUtils { + + public static final char EXTENSION_SEPARATOR = '.'; + + private static final char UNIX_SEPARATOR = '/'; + + /** + * The Windows separator character. + */ + private static final char WINDOWS_SEPARATOR = '\\'; + + /** + * The system separator character. + */ + private static final char SYSTEM_SEPARATOR = File.separatorChar; + + /** + * The separator character that is the opposite of the system separator. + */ + private static final char OTHER_SEPARATOR; + static { + if (isSystemWindows()) { + OTHER_SEPARATOR = UNIX_SEPARATOR; + } else { + OTHER_SEPARATOR = WINDOWS_SEPARATOR; + } + } + + static boolean isSystemWindows() { + return SYSTEM_SEPARATOR == WINDOWS_SEPARATOR; + } + + public static String normalize(String filename) { + return doNormalize(filename, UNIX_SEPARATOR, true); + } + + private static String doNormalize(String filename, char separator, boolean keepSeparator) { + if (filename == null) { + return null; + } + int size = filename.length(); + if (size == 0) { + return filename; + } + int prefix = getPrefixLength(filename); + if (prefix < 0) { + return null; + } + + char[] array = new char[size + 2]; // +1 for possible extra slash, +2 for arraycopy + filename.getChars(0, filename.length(), array, 0); + + // fix separators throughout + char otherSeparator = separator == SYSTEM_SEPARATOR ? OTHER_SEPARATOR : SYSTEM_SEPARATOR; + for (int i = 0; i < array.length; i++) { + if (array[i] == otherSeparator) { + array[i] = separator; + } + } + + // add extra separator on the end to simplify code below + boolean lastIsDirectory = true; + if (array[size - 1] != separator) { + array[size++] = separator; + lastIsDirectory = false; + } + + // adjoining slashes + for (int i = prefix + 1; i < size; i++) { + if (array[i] == separator && array[i - 1] == separator) { + System.arraycopy(array, i, array, i - 1, size - i); + size--; + i--; + } + } + + // dot slash + for (int i = prefix + 1; i < size; i++) { + if (array[i] == separator && array[i - 1] == '.' && + (i == prefix + 1 || array[i - 2] == separator)) { + if (i == size - 1) { + lastIsDirectory = true; + } + System.arraycopy(array, i + 1, array, i - 1, size - i); + size -= 2; + i--; + } + } + + // double dot slash + outer: for (int i = prefix + 2; i < size; i++) { + if (array[i] == separator && array[i - 1] == '.' && array[i - 2] == '.' && + (i == prefix + 2 || array[i - 3] == separator)) { + if (i == prefix + 2) { + return null; + } + if (i == size - 1) { + lastIsDirectory = true; + } + int j; + for (j = i - 4; j >= prefix; j--) { + if (array[j] == separator) { + // remove b/../ from a/b/../c + System.arraycopy(array, i + 1, array, j + 1, size - i); + size -= i - j; + i = j + 1; + continue outer; + } + } + // remove a/../ from a/../c + System.arraycopy(array, i + 1, array, prefix, size - i); + size -= i + 1 - prefix; + i = prefix + 1; + } + } + + if (size <= 0) { // should never be less than 0 + return ""; + } + if (size <= prefix) { // should never be less than prefix + return new String(array, 0, size); + } + if (lastIsDirectory && keepSeparator) { + return new String(array, 0, size); // keep trailing separator + } + return new String(array, 0, size - 1); // lose trailing separator + } + + public static int getPrefixLength(String filename) { + if (filename == null) { + return -1; + } + int len = filename.length(); + if (len == 0) { + return 0; + } + char ch0 = filename.charAt(0); + if (ch0 == ':') { + return -1; + } + if (len == 1) { + if (ch0 == '~') { + return 2; // return a length greater than the input + } + return isSeparator(ch0) ? 1 : 0; + } else { + if (ch0 == '~') { + int posUnix = filename.indexOf(UNIX_SEPARATOR, 1); + int posWin = filename.indexOf(WINDOWS_SEPARATOR, 1); + if (posUnix == -1 && posWin == -1) { + return len + 1; // return a length greater than the input + } + posUnix = posUnix == -1 ? posWin : posUnix; + posWin = posWin == -1 ? posUnix : posWin; + return Math.min(posUnix, posWin) + 1; + } + char ch1 = filename.charAt(1); + if (ch1 == ':') { + ch0 = Character.toUpperCase(ch0); + if (ch0 >= 'A' && ch0 <= 'Z') { + if (len == 2 || isSeparator(filename.charAt(2)) == false) { + return 2; + } + return 3; + } + return -1; + + } else if (isSeparator(ch0) && isSeparator(ch1)) { + int posUnix = filename.indexOf(UNIX_SEPARATOR, 2); + int posWin = filename.indexOf(WINDOWS_SEPARATOR, 2); + if (posUnix == -1 && posWin == -1 || posUnix == 2 || posWin == 2) { + return -1; + } + posUnix = posUnix == -1 ? posWin : posUnix; + posWin = posWin == -1 ? posUnix : posWin; + return Math.min(posUnix, posWin) + 1; + } else { + return isSeparator(ch0) ? 1 : 0; + } + } + } + + private static boolean isSeparator(char ch) { + return ch == UNIX_SEPARATOR || ch == WINDOWS_SEPARATOR; + } + + public static String getExtension(String filename) { + if (filename == null) { + return null; + } + int index = indexOfExtension(filename); + if (index == -1) { + return ""; + } else { + return filename.substring(index + 1); + } + } + + public static int indexOfExtension(String filename) { + if (filename == null) { + return -1; + } + int extensionPos = filename.lastIndexOf(EXTENSION_SEPARATOR); + int lastSeparator = indexOfLastSeparator(filename); + return lastSeparator > extensionPos ? -1 : extensionPos; + } + + public static int indexOfLastSeparator(String filename) { + if (filename == null) { + return -1; + } + int lastUnixPos = filename.lastIndexOf(UNIX_SEPARATOR); + int lastWindowsPos = filename.lastIndexOf(WINDOWS_SEPARATOR); + return Math.max(lastUnixPos, lastWindowsPos); + } + +} diff --git a/sonar-batch-plugin-api/src/main/java/org/sonar/batch/api/internal/Preconditions.java b/sonar-batch-plugin-api/src/main/java/org/sonar/batch/api/internal/Preconditions.java new file mode 100644 index 00000000000..75ac73f46f3 --- /dev/null +++ b/sonar-batch-plugin-api/src/main/java/org/sonar/batch/api/internal/Preconditions.java @@ -0,0 +1,413 @@ +/* + * 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.api.internal; + +import javax.annotation.Nullable; + +/** + * Copied from Guava + */ +public final class Preconditions { + private Preconditions() { + } + + /** + * Ensures the truth of an expression involving one or more parameters to the + * calling method. + * + * @param expression a boolean expression + * @throws IllegalArgumentException if {@code expression} is false + */ + public static void checkArgument(boolean expression) { + if (!expression) { + throw new IllegalArgumentException(); + } + } + + /** + * Ensures the truth of an expression involving one or more parameters to the + * calling method. + * + * @param expression a boolean expression + * @param errorMessage the exception message to use if the check fails; will + * be converted to a string using {@link String#valueOf(Object)} + * @throws IllegalArgumentException if {@code expression} is false + */ + public static void checkArgument( + boolean expression, @Nullable Object errorMessage) { + if (!expression) { + throw new IllegalArgumentException(String.valueOf(errorMessage)); + } + } + + /** + * Ensures the truth of an expression involving one or more parameters to the + * calling method. + * + * @param expression a boolean expression + * @param errorMessageTemplate a template for the exception message should the + * check fail. The message is formed by replacing each {@code %s} + * placeholder in the template with an argument. These are matched by + * position - the first {@code %s} gets {@code errorMessageArgs[0]}, etc. + * Unmatched arguments will be appended to the formatted message in square + * braces. Unmatched placeholders will be left as-is. + * @param errorMessageArgs the arguments to be substituted into the message + * template. Arguments are converted to strings using + * {@link String#valueOf(Object)}. + * @throws IllegalArgumentException if {@code expression} is false + * @throws NullPointerException if the check fails and either {@code + * errorMessageTemplate} or {@code errorMessageArgs} is null (don't let + * this happen) + */ + public static void checkArgument(boolean expression, + @Nullable String errorMessageTemplate, + @Nullable Object... errorMessageArgs) { + if (!expression) { + throw new IllegalArgumentException( + format(errorMessageTemplate, errorMessageArgs)); + } + } + + /** + * Ensures the truth of an expression involving the state of the calling + * instance, but not involving any parameters to the calling method. + * + * @param expression a boolean expression + * @throws IllegalStateException if {@code expression} is false + */ + public static void checkState(boolean expression) { + if (!expression) { + throw new IllegalStateException(); + } + } + + /** + * Ensures the truth of an expression involving the state of the calling + * instance, but not involving any parameters to the calling method. + * + * @param expression a boolean expression + * @param errorMessage the exception message to use if the check fails; will + * be converted to a string using {@link String#valueOf(Object)} + * @throws IllegalStateException if {@code expression} is false + */ + public static void checkState( + boolean expression, @Nullable Object errorMessage) { + if (!expression) { + throw new IllegalStateException(String.valueOf(errorMessage)); + } + } + + /** + * Ensures the truth of an expression involving the state of the calling + * instance, but not involving any parameters to the calling method. + * + * @param expression a boolean expression + * @param errorMessageTemplate a template for the exception message should the + * check fail. The message is formed by replacing each {@code %s} + * placeholder in the template with an argument. These are matched by + * position - the first {@code %s} gets {@code errorMessageArgs[0]}, etc. + * Unmatched arguments will be appended to the formatted message in square + * braces. Unmatched placeholders will be left as-is. + * @param errorMessageArgs the arguments to be substituted into the message + * template. Arguments are converted to strings using + * {@link String#valueOf(Object)}. + * @throws IllegalStateException if {@code expression} is false + * @throws NullPointerException if the check fails and either {@code + * errorMessageTemplate} or {@code errorMessageArgs} is null (don't let + * this happen) + */ + public static void checkState(boolean expression, + @Nullable String errorMessageTemplate, + @Nullable Object... errorMessageArgs) { + if (!expression) { + throw new IllegalStateException( + format(errorMessageTemplate, errorMessageArgs)); + } + } + + /** + * Ensures that an object reference passed as a parameter to the calling + * method is not null. + * + * @param reference an object reference + * @return the non-null reference that was validated + * @throws NullPointerException if {@code reference} is null + */ + public static <T> T checkNotNull(T reference) { + if (reference == null) { + throw new NullPointerException(); + } + return reference; + } + + /** + * Ensures that an object reference passed as a parameter to the calling + * method is not null. + * + * @param reference an object reference + * @param errorMessage the exception message to use if the check fails; will + * be converted to a string using {@link String#valueOf(Object)} + * @return the non-null reference that was validated + * @throws NullPointerException if {@code reference} is null + */ + public static <T> T checkNotNull(T reference, @Nullable Object errorMessage) { + if (reference == null) { + throw new NullPointerException(String.valueOf(errorMessage)); + } + return reference; + } + + /** + * Ensures that an object reference passed as a parameter to the calling + * method is not null. + * + * @param reference an object reference + * @param errorMessageTemplate a template for the exception message should the + * check fail. The message is formed by replacing each {@code %s} + * placeholder in the template with an argument. These are matched by + * position - the first {@code %s} gets {@code errorMessageArgs[0]}, etc. + * Unmatched arguments will be appended to the formatted message in square + * braces. Unmatched placeholders will be left as-is. + * @param errorMessageArgs the arguments to be substituted into the message + * template. Arguments are converted to strings using + * {@link String#valueOf(Object)}. + * @return the non-null reference that was validated + * @throws NullPointerException if {@code reference} is null + */ + public static <T> T checkNotNull(T reference, + @Nullable String errorMessageTemplate, + @Nullable Object... errorMessageArgs) { + if (reference == null) { + // If either of these parameters is null, the right thing happens anyway + throw new NullPointerException( + format(errorMessageTemplate, errorMessageArgs)); + } + return reference; + } + + /* + * All recent hotspots (as of 2009) *really* like to have the natural code + * + * if (guardExpression) { + * throw new BadException(messageExpression); + * } + * + * refactored so that messageExpression is moved to a separate + * String-returning method. + * + * if (guardExpression) { + * throw new BadException(badMsg(...)); + * } + * + * The alternative natural refactorings into void or Exception-returning + * methods are much slower. This is a big deal - we're talking factors of + * 2-8 in microbenchmarks, not just 10-20%. (This is a hotspot optimizer + * bug, which should be fixed, but that's a separate, big project). + * + * The coding pattern above is heavily used in java.util, e.g. in ArrayList. + * There is a RangeCheckMicroBenchmark in the JDK that was used to test this. + * + * But the methods in this class want to throw different exceptions, + * depending on the args, so it appears that this pattern is not directly + * applicable. But we can use the ridiculous, devious trick of throwing an + * exception in the middle of the construction of another exception. + * Hotspot is fine with that. + */ + + /** + * Ensures that {@code index} specifies a valid <i>element</i> in an array, + * list or string of size {@code size}. An element index may range from zero, + * inclusive, to {@code size}, exclusive. + * + * @param index a user-supplied index identifying an element of an array, list + * or string + * @param size the size of that array, list or string + * @return the value of {@code index} + * @throws IndexOutOfBoundsException if {@code index} is negative or is not + * less than {@code size} + * @throws IllegalArgumentException if {@code size} is negative + */ + public static int checkElementIndex(int index, int size) { + return checkElementIndex(index, size, "index"); + } + + /** + * Ensures that {@code index} specifies a valid <i>element</i> in an array, + * list or string of size {@code size}. An element index may range from zero, + * inclusive, to {@code size}, exclusive. + * + * @param index a user-supplied index identifying an element of an array, list + * or string + * @param size the size of that array, list or string + * @param desc the text to use to describe this index in an error message + * @return the value of {@code index} + * @throws IndexOutOfBoundsException if {@code index} is negative or is not + * less than {@code size} + * @throws IllegalArgumentException if {@code size} is negative + */ + public static int checkElementIndex( + int index, int size, @Nullable String desc) { + // Carefully optimized for execution by hotspot (explanatory comment above) + if (index < 0 || index >= size) { + throw new IndexOutOfBoundsException(badElementIndex(index, size, desc)); + } + return index; + } + + private static String badElementIndex(int index, int size, String desc) { + if (index < 0) { + return format("%s (%s) must not be negative", desc, index); + } else if (size < 0) { + throw new IllegalArgumentException("negative size: " + size); + } else { // index >= size + return format("%s (%s) must be less than size (%s)", desc, index, size); + } + } + + /** + * Ensures that {@code index} specifies a valid <i>position</i> in an array, + * list or string of size {@code size}. A position index may range from zero + * to {@code size}, inclusive. + * + * @param index a user-supplied index identifying a position in an array, list + * or string + * @param size the size of that array, list or string + * @return the value of {@code index} + * @throws IndexOutOfBoundsException if {@code index} is negative or is + * greater than {@code size} + * @throws IllegalArgumentException if {@code size} is negative + */ + public static int checkPositionIndex(int index, int size) { + return checkPositionIndex(index, size, "index"); + } + + /** + * Ensures that {@code index} specifies a valid <i>position</i> in an array, + * list or string of size {@code size}. A position index may range from zero + * to {@code size}, inclusive. + * + * @param index a user-supplied index identifying a position in an array, list + * or string + * @param size the size of that array, list or string + * @param desc the text to use to describe this index in an error message + * @return the value of {@code index} + * @throws IndexOutOfBoundsException if {@code index} is negative or is + * greater than {@code size} + * @throws IllegalArgumentException if {@code size} is negative + */ + public static int checkPositionIndex( + int index, int size, @Nullable String desc) { + // Carefully optimized for execution by hotspot (explanatory comment above) + if (index < 0 || index > size) { + throw new IndexOutOfBoundsException(badPositionIndex(index, size, desc)); + } + return index; + } + + private static String badPositionIndex(int index, int size, String desc) { + if (index < 0) { + return format("%s (%s) must not be negative", desc, index); + } else if (size < 0) { + throw new IllegalArgumentException("negative size: " + size); + } else { // index > size + return format("%s (%s) must not be greater than size (%s)", + desc, index, size); + } + } + + /** + * Ensures that {@code start} and {@code end} specify a valid <i>positions</i> + * in an array, list or string of size {@code size}, and are in order. A + * position index may range from zero to {@code size}, inclusive. + * + * @param start a user-supplied index identifying a starting position in an + * array, list or string + * @param end a user-supplied index identifying a ending position in an array, + * list or string + * @param size the size of that array, list or string + * @throws IndexOutOfBoundsException if either index is negative or is + * greater than {@code size}, or if {@code end} is less than {@code start} + * @throws IllegalArgumentException if {@code size} is negative + */ + public static void checkPositionIndexes(int start, int end, int size) { + // Carefully optimized for execution by hotspot (explanatory comment above) + if (start < 0 || end < start || end > size) { + throw new IndexOutOfBoundsException(badPositionIndexes(start, end, size)); + } + } + + private static String badPositionIndexes(int start, int end, int size) { + if (start < 0 || start > size) { + return badPositionIndex(start, size, "start index"); + } + if (end < 0 || end > size) { + return badPositionIndex(end, size, "end index"); + } + // end < start + return format("end index (%s) must not be less than start index (%s)", + end, start); + } + + /** + * Substitutes each {@code %s} in {@code template} with an argument. These + * are matched by position - the first {@code %s} gets {@code args[0]}, etc. + * If there are more arguments than placeholders, the unmatched arguments will + * be appended to the end of the formatted message in square braces. + * + * @param template a non-null string containing 0 or more {@code %s} + * placeholders. + * @param args the arguments to be substituted into the message + * template. Arguments are converted to strings using + * {@link String#valueOf(Object)}. Arguments can be null. + */ + static String format(String template, + @Nullable Object... args) { + template = String.valueOf(template); // null -> "null" + + // start substituting the arguments into the '%s' placeholders + StringBuilder builder = new StringBuilder( + template.length() + 16 * args.length); + int templateStart = 0; + int i = 0; + while (i < args.length) { + int placeholderStart = template.indexOf("%s", templateStart); + if (placeholderStart == -1) { + break; + } + builder.append(template.substring(templateStart, placeholderStart)); + builder.append(args[i++]); + templateStart = placeholderStart + 2; + } + builder.append(template.substring(templateStart)); + + // if we run out of placeholders, append the extra args in square braces + if (i < args.length) { + builder.append(" ["); + builder.append(args[i++]); + while (i < args.length) { + builder.append(", "); + builder.append(args[i++]); + } + builder.append(']'); + } + + return builder.toString(); + } +} diff --git a/sonar-batch-plugin-api/src/main/java/org/sonar/batch/api/internal/StringUtils.java b/sonar-batch-plugin-api/src/main/java/org/sonar/batch/api/internal/StringUtils.java new file mode 100644 index 00000000000..ab279ae34e5 --- /dev/null +++ b/sonar-batch-plugin-api/src/main/java/org/sonar/batch/api/internal/StringUtils.java @@ -0,0 +1,140 @@ +/* + * 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.api.internal; + +/** + * Copied from commons lang + */ +public class StringUtils { + + public static final String EMPTY = ""; + + public static String trim(String str) { + return str == null ? null : str.trim(); + } + + public static String removeStart(String str, String remove) { + if (isEmpty(str) || isEmpty(remove)) { + return str; + } + if (str.startsWith(remove)) { + return str.substring(remove.length()); + } + return str; + } + + public static String removeEnd(String str, String remove) { + if (isEmpty(str) || isEmpty(remove)) { + return str; + } + if (str.endsWith(remove)) { + return str.substring(0, str.length() - remove.length()); + } + return str; + } + + public static boolean isEmpty(String str) { + return str == null || str.length() == 0; + } + + public static boolean startsWithIgnoreCase(String str, String prefix) { + return startsWith(str, prefix, true); + } + + private static boolean startsWith(String str, String prefix, boolean ignoreCase) { + if (str == null || prefix == null) { + return (str == null && prefix == null); + } + if (prefix.length() > str.length()) { + return false; + } + return str.regionMatches(ignoreCase, 0, prefix, 0, prefix.length()); + } + + public static String substring(String str, int start) { + if (str == null) { + return null; + } + + // handle negatives, which means last n characters + if (start < 0) { + start = str.length() + start; // remember start is negative + } + + if (start < 0) { + start = 0; + } + if (start > str.length()) { + return EMPTY; + } + + return str.substring(start); + } + + public static boolean isBlank(String str) { + int strLen; + if (str == null || (strLen = str.length()) == 0) { + return true; + } + for (int i = 0; i < strLen; i++) { + if ((Character.isWhitespace(str.charAt(i)) == false)) { + return false; + } + } + return true; + } + + public static boolean isNotBlank(String str) { + return !StringUtils.isBlank(str); + } + + public static String removeEndIgnoreCase(String str, String remove) { + if (isEmpty(str) || isEmpty(remove)) { + return str; + } + if (endsWithIgnoreCase(str, remove)) { + return str.substring(0, str.length() - remove.length()); + } + return str; + } + + public static boolean endsWithIgnoreCase(String str, String suffix) { + return endsWith(str, suffix, true); + } + + private static boolean endsWith(String str, String suffix, boolean ignoreCase) { + if (str == null || suffix == null) { + return (str == null && suffix == null); + } + if (suffix.length() > str.length()) { + return false; + } + int strOffset = str.length() - suffix.length(); + return str.regionMatches(ignoreCase, strOffset, suffix, 0, suffix.length()); + } + + public static String lowerCase(String str) { + if (str == null) { + return null; + } + return str.toLowerCase(); + } + +} diff --git a/sonar-batch-plugin-api/src/main/java/org/sonar/batch/api/internal/WildcardPattern.java b/sonar-batch-plugin-api/src/main/java/org/sonar/batch/api/internal/WildcardPattern.java new file mode 100644 index 00000000000..f179fd73571 --- /dev/null +++ b/sonar-batch-plugin-api/src/main/java/org/sonar/batch/api/internal/WildcardPattern.java @@ -0,0 +1,205 @@ +/* + * 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.api.internal; + +import java.util.HashMap; +import java.util.Map; +import java.util.regex.Pattern; + +/** + * Implementation of Ant-style matching patterns. + * Contrary to other implementations (like AntPathMatcher from Spring Framework) it is based on {@link Pattern Java Regular Expressions}. + * To increase performance it holds an internal cache of all processed patterns. + * <p> + * Following rules are applied: + * <ul> + * <li>? matches single character</li> + * <li>* matches zero or more characters</li> + * <li>** matches zero or more 'directories'</li> + * </ul> + * </p> + * <p> + * Some examples of patterns: + * <ul> + * <li><code>org/T?st.java</code> - matches <code>org/Test.java</code> and also <code>org/Tost.java</code></li> + * <li><code>org/*.java</code> - matches all <code>.java</code> files in the <code>org</code> directory, + * e.g. <code>org/Foo.java</code> or <code>org/Bar.java</code></li> + * <li><code>org/**</code> - matches all files underneath the <code>org</code> directory, + * e.g. <code>org/Foo.java</code> or <code>org/foo/bar.jsp</code></li> + * <li><code>org/**/Test.java</code> - matches all <code>Test.java</code> files underneath the <code>org</code> directory, + * e.g. <code>org/Test.java</code> or <code>org/foo/Test.java</code> or <code>org/foo/bar/Test.java</code></li> + * <li><code>org/**/*.java</code> - matches all <code>.java</code> files underneath the <code>org</code> directory, + * e.g. <code>org/Foo.java</code> or <code>org/foo/Bar.java</code> or <code>org/foo/bar/Baz.java</code></li> + * </ul> + * </p> + * <p> + * Another implementation, which is also based on Java Regular Expressions, can be found in + * <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. + * </p> + * + */ +public class WildcardPattern { + + private static final Map<String, WildcardPattern> CACHE = new HashMap<String, WildcardPattern>(); + private static final String SPECIAL_CHARS = "()[]^$.{}+|"; + + private Pattern pattern; + private String stringRepresentation; + + protected WildcardPattern(String pattern, String directorySeparator) { + this.stringRepresentation = pattern; + this.pattern = Pattern.compile(toRegexp(pattern, directorySeparator)); + } + + private static String toRegexp(String antPattern, String directorySeparator) { + final String escapedDirectorySeparator = '\\' + directorySeparator; + + final StringBuilder sb = new StringBuilder(antPattern.length()); + + sb.append('^'); + + int i = antPattern.startsWith("/") || antPattern.startsWith("\\") ? 1 : 0; + while (i < antPattern.length()) { + final char ch = antPattern.charAt(i); + + if (SPECIAL_CHARS.indexOf(ch) != -1) { + // Escape regexp-specific characters + sb.append('\\').append(ch); + } else if (ch == '*') { + if (i + 1 < antPattern.length() && antPattern.charAt(i + 1) == '*') { + // Double asterisk + // Zero or more directories + if (i + 2 < antPattern.length() && isSlash(antPattern.charAt(i + 2))) { + sb.append("(?:.*").append(escapedDirectorySeparator).append("|)"); + i += 2; + } else { + sb.append(".*"); + i += 1; + } + } else { + // Single asterisk + // Zero or more characters excluding directory separator + sb.append("[^").append(escapedDirectorySeparator).append("]*?"); + } + } else if (ch == '?') { + // Any single character excluding directory separator + sb.append("[^").append(escapedDirectorySeparator).append("]"); + } else if (isSlash(ch)) { + // Directory separator + sb.append(escapedDirectorySeparator); + } else { + // Single character + sb.append(ch); + } + + i++; + } + + sb.append('$'); + + return sb.toString(); + } + + private static boolean isSlash(char ch) { + return ch == '/' || ch == '\\'; + } + + /** + * Returns string representation of this pattern. + * + * @since 2.5 + */ + @Override + public String toString() { + return stringRepresentation; + } + + /** + * Returns true if specified value matches this pattern. + */ + public boolean match(String value) { + value = StringUtils.removeStart(value, "/"); + value = StringUtils.removeEnd(value, "/"); + return pattern.matcher(value).matches(); + } + + /** + * Returns true if specified value matches one of specified patterns. + * + * @since 2.4 + */ + public static boolean match(WildcardPattern[] patterns, String value) { + for (WildcardPattern pattern : patterns) { + if (pattern.match(value)) { + return true; + } + } + return false; + } + + /** + * Creates pattern with "/" as a directory separator. + * + * @see #create(String, String) + */ + public static WildcardPattern create(String pattern) { + return create(pattern, "/"); + } + + /** + * Creates array of patterns with "/" as a directory separator. + * + * @see #create(String, String) + */ + public static WildcardPattern[] create(String[] patterns) { + if (patterns == null) { + return new WildcardPattern[0]; + } + WildcardPattern[] exclusionPAtterns = new WildcardPattern[patterns.length]; + for (int i = 0; i < patterns.length; i++) { + exclusionPAtterns[i] = create(patterns[i]); + } + return exclusionPAtterns; + } + + /** + * Creates pattern with specified separator for directories. + * <p> + * This is used to match Java-classes, i.e. <code>org.foo.Bar</code> against <code>org/**</code>. + * <b>However usage of character other than "/" as a directory separator is misleading and should be avoided, + * so method {@link #create(String)} is preferred over this one.</b> + * </p> + * <p> + * Also note that no matter whether forward or backward slashes were used in the <code>antPattern</code> + * the returned pattern will use <code>directorySeparator</code>. + * Thus to match Windows-style path "dir\file.ext" against pattern "dir/file.ext" normalization should be performed. + * </p> + */ + 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; + } +} diff --git a/sonar-batch-plugin-api/src/main/java/org/sonar/batch/api/internal/package-info.java b/sonar-batch-plugin-api/src/main/java/org/sonar/batch/api/internal/package-info.java new file mode 100644 index 00000000000..9870b99bffc --- /dev/null +++ b/sonar-batch-plugin-api/src/main/java/org/sonar/batch/api/internal/package-info.java @@ -0,0 +1,21 @@ +/* + * 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. + */ +@javax.annotation.ParametersAreNonnullByDefault +package org.sonar.batch.api.internal;
\ No newline at end of file diff --git a/sonar-batch-plugin-api/src/main/java/org/sonar/batch/api/languages/Language.java b/sonar-batch-plugin-api/src/main/java/org/sonar/batch/api/languages/Language.java new file mode 100644 index 00000000000..7dd6c8b4e6b --- /dev/null +++ b/sonar-batch-plugin-api/src/main/java/org/sonar/batch/api/languages/Language.java @@ -0,0 +1,57 @@ +/* + * 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.api.languages; + +import java.util.Arrays; +import java.util.Collection; + +public final class Language { + + private final String key, name; + private final String[] fileSuffixes; + + public Language(String key, String name, String... fileSuffixes) { + this.key = key; + this.name = name; + this.fileSuffixes = fileSuffixes; + } + + /** + * For example "java". + */ + public String key() { + return key; + } + + /** + * For example "Java" + */ + public String name() { + return name; + } + + /** + * For example ["jav", "java"]. + */ + public Collection<String> fileSuffixes() { + return Arrays.asList(fileSuffixes); + } + +} diff --git a/sonar-batch-plugin-api/src/main/java/org/sonar/batch/api/languages/package-info.java b/sonar-batch-plugin-api/src/main/java/org/sonar/batch/api/languages/package-info.java new file mode 100644 index 00000000000..a36d1c13de5 --- /dev/null +++ b/sonar-batch-plugin-api/src/main/java/org/sonar/batch/api/languages/package-info.java @@ -0,0 +1,21 @@ +/* + * 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. + */ +@javax.annotation.ParametersAreNonnullByDefault +package org.sonar.batch.api.languages;
\ No newline at end of file diff --git a/sonar-batch-plugin-api/src/main/java/org/sonar/batch/api/measures/Metric.java b/sonar-batch-plugin-api/src/main/java/org/sonar/batch/api/measures/Metric.java new file mode 100644 index 00000000000..52dc17c1718 --- /dev/null +++ b/sonar-batch-plugin-api/src/main/java/org/sonar/batch/api/measures/Metric.java @@ -0,0 +1,29 @@ +/* + * 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.api.measures; + +import java.io.Serializable; + +public interface Metric<G extends Serializable> { + + Class<G> type(); + + String key(); +} diff --git a/sonar-batch-plugin-api/src/main/java/org/sonar/batch/api/measures/package-info.java b/sonar-batch-plugin-api/src/main/java/org/sonar/batch/api/measures/package-info.java new file mode 100644 index 00000000000..e684b549c5f --- /dev/null +++ b/sonar-batch-plugin-api/src/main/java/org/sonar/batch/api/measures/package-info.java @@ -0,0 +1,21 @@ +/* + * 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. + */ +@javax.annotation.ParametersAreNonnullByDefault +package org.sonar.batch.api.measures;
\ No newline at end of file diff --git a/sonar-batch-plugin-api/src/main/java/org/sonar/batch/api/rules/QProfile.java b/sonar-batch-plugin-api/src/main/java/org/sonar/batch/api/rules/QProfile.java new file mode 100644 index 00000000000..1d074e105f2 --- /dev/null +++ b/sonar-batch-plugin-api/src/main/java/org/sonar/batch/api/rules/QProfile.java @@ -0,0 +1,43 @@ +/* + * 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.api.rules; + +public class QProfile { + private final String name, language; + private final Integer version; + + public QProfile(String name, String language, Integer version) { + this.name = name; + this.language = language; + this.version = version; + } + + public String name() { + return name; + } + + public String language() { + return language; + } + + public Integer version() { + return version; + } +} diff --git a/sonar-batch-plugin-api/src/main/java/org/sonar/batch/api/rules/RuleKey.java b/sonar-batch-plugin-api/src/main/java/org/sonar/batch/api/rules/RuleKey.java new file mode 100644 index 00000000000..3af1337a752 --- /dev/null +++ b/sonar-batch-plugin-api/src/main/java/org/sonar/batch/api/rules/RuleKey.java @@ -0,0 +1,90 @@ +/* + * 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.api.rules; + +import java.io.Serializable; + +/** + * Key of a rule. Unique among all the rule repositories. + * + * @since 3.6 + */ +public class RuleKey implements Serializable { + private final String repository, rule; + + protected RuleKey(String repositoryKey, String ruleKey) { + this.repository = repositoryKey; + this.rule = ruleKey; + } + + /** + * Create a key. Parameters are NOT null. + */ + public static RuleKey of(String repository, String rule) { + return new RuleKey(repository, rule); + } + + /** + * Never null + */ + public String repository() { + return repository; + } + + /** + * Never null + */ + public String rule() { + return rule; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + RuleKey ruleKey = (RuleKey) o; + if (!repository.equals(ruleKey.repository)) { + return false; + } + if (!rule.equals(ruleKey.rule)) { + return false; + } + return true; + } + + @Override + public int hashCode() { + int result = repository.hashCode(); + result = 31 * result + rule.hashCode(); + return result; + } + + /** + * Format is "repository:rule", for example "squid:AvoidCycle" + */ + @Override + public String toString() { + return String.format("%s:%s", repository, rule); + } +} diff --git a/sonar-batch-plugin-api/src/main/java/org/sonar/batch/api/rules/package-info.java b/sonar-batch-plugin-api/src/main/java/org/sonar/batch/api/rules/package-info.java new file mode 100644 index 00000000000..d91eb3485c9 --- /dev/null +++ b/sonar-batch-plugin-api/src/main/java/org/sonar/batch/api/rules/package-info.java @@ -0,0 +1,21 @@ +/* + * 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. + */ +@javax.annotation.ParametersAreNonnullByDefault +package org.sonar.batch.api.rules;
\ No newline at end of file diff --git a/sonar-plugin-api/src/test/java/org/sonar/api/batch/fs/internal/DefaultFilePredicatesTest.java b/sonar-batch-plugin-api/src/test/java/org/sonar/api/batch/fs/internal/DefaultFilePredicatesTest.java index e78bff01ce7..95888fecfe9 100644 --- a/sonar-plugin-api/src/test/java/org/sonar/api/batch/fs/internal/DefaultFilePredicatesTest.java +++ b/sonar-batch-plugin-api/src/test/java/org/sonar/api/batch/fs/internal/DefaultFilePredicatesTest.java @@ -19,7 +19,6 @@ */ package org.sonar.api.batch.fs.internal; -import org.apache.commons.io.FilenameUtils; import org.fest.assertions.Assertions; import org.junit.Before; import org.junit.Rule; @@ -71,9 +70,9 @@ public class DefaultFilePredicatesTest { @Test public void matches_inclusion_patterns() throws Exception { - assertThat(predicates.matchesPathPatterns(new String[]{"src/other/**.java", "src/main/**/Action.java"}).apply(javaFile)).isTrue(); - assertThat(predicates.matchesPathPatterns(new String[]{}).apply(javaFile)).isTrue(); - assertThat(predicates.matchesPathPatterns(new String[]{"src/other/**.java", "src/**/*.php"}).apply(javaFile)).isFalse(); + assertThat(predicates.matchesPathPatterns(new String[] {"src/other/**.java", "src/main/**/Action.java"}).apply(javaFile)).isTrue(); + assertThat(predicates.matchesPathPatterns(new String[] {}).apply(javaFile)).isTrue(); + assertThat(predicates.matchesPathPatterns(new String[] {"src/other/**.java", "src/**/*.php"}).apply(javaFile)).isFalse(); } @Test @@ -85,9 +84,9 @@ public class DefaultFilePredicatesTest { @Test public void does_not_match_exclusion_patterns() throws Exception { - assertThat(predicates.doesNotMatchPathPatterns(new String[]{}).apply(javaFile)).isTrue(); - assertThat(predicates.doesNotMatchPathPatterns(new String[]{"src/other/**.java", "src/**/*.php"}).apply(javaFile)).isTrue(); - assertThat(predicates.doesNotMatchPathPatterns(new String[]{"src/other/**.java", "src/main/**/Action.java"}).apply(javaFile)).isFalse(); + assertThat(predicates.doesNotMatchPathPatterns(new String[] {}).apply(javaFile)).isTrue(); + assertThat(predicates.doesNotMatchPathPatterns(new String[] {"src/other/**.java", "src/**/*.php"}).apply(javaFile)).isTrue(); + assertThat(predicates.doesNotMatchPathPatterns(new String[] {"src/other/**.java", "src/main/**/Action.java"}).apply(javaFile)).isFalse(); } @Test @@ -107,7 +106,7 @@ public class DefaultFilePredicatesTest { public void has_absolute_path() throws Exception { String path = javaFile.file().getAbsolutePath(); assertThat(predicates.hasAbsolutePath(path).apply(javaFile)).isTrue(); - assertThat(predicates.hasAbsolutePath(FilenameUtils.separatorsToWindows(path)).apply(javaFile)).isTrue(); + assertThat(predicates.hasAbsolutePath(path.replaceAll("/", "\\\\")).apply(javaFile)).isTrue(); assertThat(predicates.hasAbsolutePath(temp.newFile().getAbsolutePath()).apply(javaFile)).isFalse(); assertThat(predicates.hasAbsolutePath("src/main/java/struts/Action.java").apply(javaFile)).isFalse(); @@ -130,7 +129,6 @@ public class DefaultFilePredicatesTest { // relative file assertThat(predicates.is(new File(javaFile.relativePath())).apply(javaFile)).isTrue(); - // absolute file assertThat(predicates.is(javaFile.file()).apply(javaFile)).isTrue(); assertThat(predicates.is(javaFile.file().getAbsoluteFile()).apply(javaFile)).isTrue(); @@ -187,8 +185,8 @@ public class DefaultFilePredicatesTest { assertThat(predicates.and(Arrays.asList(predicates.all(), predicates.none())).apply(javaFile)).isFalse(); // array - assertThat(predicates.and(new FilePredicate[]{predicates.all(), predicates.all()}).apply(javaFile)).isTrue(); - assertThat(predicates.and(new FilePredicate[]{predicates.all(), predicates.none()}).apply(javaFile)).isFalse(); + assertThat(predicates.and(new FilePredicate[] {predicates.all(), predicates.all()}).apply(javaFile)).isTrue(); + assertThat(predicates.and(new FilePredicate[] {predicates.all(), predicates.none()}).apply(javaFile)).isFalse(); } @Test @@ -210,8 +208,8 @@ public class DefaultFilePredicatesTest { assertThat(predicates.or(Arrays.asList(predicates.none(), predicates.none())).apply(javaFile)).isFalse(); // array - assertThat(predicates.or(new FilePredicate[]{predicates.all(), predicates.all()}).apply(javaFile)).isTrue(); - assertThat(predicates.or(new FilePredicate[]{predicates.all(), predicates.none()}).apply(javaFile)).isTrue(); - assertThat(predicates.or(new FilePredicate[]{predicates.none(), predicates.none()}).apply(javaFile)).isFalse(); + assertThat(predicates.or(new FilePredicate[] {predicates.all(), predicates.all()}).apply(javaFile)).isTrue(); + assertThat(predicates.or(new FilePredicate[] {predicates.all(), predicates.none()}).apply(javaFile)).isTrue(); + assertThat(predicates.or(new FilePredicate[] {predicates.none(), predicates.none()}).apply(javaFile)).isFalse(); } } diff --git a/sonar-plugin-api/src/test/java/org/sonar/api/batch/fs/internal/DefaultFileSystemTest.java b/sonar-batch-plugin-api/src/test/java/org/sonar/api/batch/fs/internal/DefaultFileSystemTest.java index aa898e908c5..678fdc5687b 100644 --- a/sonar-plugin-api/src/test/java/org/sonar/api/batch/fs/internal/DefaultFileSystemTest.java +++ b/sonar-batch-plugin-api/src/test/java/org/sonar/api/batch/fs/internal/DefaultFileSystemTest.java @@ -19,7 +19,6 @@ */ package org.sonar.api.batch.fs.internal; -import com.google.common.base.Charsets; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; @@ -60,8 +59,8 @@ public class DefaultFileSystemTest { assertThat(fs.isDefaultJvmEncoding()).isTrue(); assertThat(fs.encoding()).isEqualTo(Charset.defaultCharset()); - fs.setEncoding(Charsets.ISO_8859_1); - assertThat(fs.encoding()).isEqualTo(Charsets.ISO_8859_1); + fs.setEncoding(Charset.forName("ISO-8859-1")); + assertThat(fs.encoding()).isEqualTo(Charset.forName("ISO-8859-1")); assertThat(fs.isDefaultJvmEncoding()).isFalse(); } diff --git a/sonar-plugin-api/src/test/java/org/sonar/api/batch/fs/internal/DefaultInputFileTest.java b/sonar-batch-plugin-api/src/test/java/org/sonar/api/batch/fs/internal/DefaultInputFileTest.java index 3148a2dc4c8..b96821ffd69 100644 --- a/sonar-plugin-api/src/test/java/org/sonar/api/batch/fs/internal/DefaultInputFileTest.java +++ b/sonar-batch-plugin-api/src/test/java/org/sonar/api/batch/fs/internal/DefaultInputFileTest.java @@ -37,18 +37,14 @@ public class DefaultInputFileTest { public void test() throws Exception { DefaultInputFile inputFile = new DefaultInputFile("src/Foo.php") .setFile(temp.newFile("Foo.php")) - .setDeprecatedKey("deprecated") .setKey("ABCDE") .setHash("1234") .setLines(42) .setLanguage("php") .setStatus(InputFile.Status.ADDED) - .setType(InputFile.Type.TEST) - .setPathRelativeToSourceDir("Foo.php"); + .setType(InputFile.Type.TEST); assertThat(inputFile.relativePath()).isEqualTo("src/Foo.php"); - // deprecated method is different -> path relative to source dir - assertThat(inputFile.getRelativePath()).isEqualTo("Foo.php"); assertThat(new File(inputFile.relativePath())).isRelative(); assertThat(inputFile.absolutePath()).endsWith("Foo.php"); assertThat(new File(inputFile.absolutePath())).isAbsolute(); diff --git a/sonar-plugin-api/src/test/java/org/sonar/api/batch/fs/internal/PathPatternTest.java b/sonar-batch-plugin-api/src/test/java/org/sonar/api/batch/fs/internal/PathPatternTest.java index 01d6dabd0fb..01d6dabd0fb 100644 --- a/sonar-plugin-api/src/test/java/org/sonar/api/batch/fs/internal/PathPatternTest.java +++ b/sonar-batch-plugin-api/src/test/java/org/sonar/api/batch/fs/internal/PathPatternTest.java diff --git a/sonar-batch/pom.xml b/sonar-batch/pom.xml index db45a80e535..1db1f35f0e9 100644 --- a/sonar-batch/pom.xml +++ b/sonar-batch/pom.xml @@ -42,6 +42,10 @@ </dependency> <dependency> <groupId>org.codehaus.sonar</groupId> + <artifactId>sonar-batch-plugin-api</artifactId> + </dependency> + <dependency> + <groupId>org.codehaus.sonar</groupId> <artifactId>sonar-plugin-api</artifactId> <exclusions> <exclusion> diff --git a/sonar-batch/src/main/java/org/sonar/batch/DefaultSensorContext.java b/sonar-batch/src/main/java/org/sonar/batch/DefaultSensorContext.java index 4d71b95e470..089b2a3c58f 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/DefaultSensorContext.java +++ b/sonar-batch/src/main/java/org/sonar/batch/DefaultSensorContext.java @@ -30,11 +30,16 @@ import org.sonar.api.design.Dependency; import org.sonar.api.measures.Measure; import org.sonar.api.measures.MeasuresFilter; import org.sonar.api.measures.Metric; -import org.sonar.api.resources.*; +import org.sonar.api.resources.File; +import org.sonar.api.resources.Project; +import org.sonar.api.resources.ProjectLink; +import org.sonar.api.resources.Qualifiers; +import org.sonar.api.resources.Resource; import org.sonar.api.rules.Violation; import org.sonar.api.utils.SonarException; import org.sonar.core.measure.MeasurementFilters; +import java.io.Serializable; import java.util.Collection; import java.util.Date; import java.util.List; @@ -110,7 +115,7 @@ public class DefaultSensorContext implements SensorContext { } @Override - public Measure getMeasure(Metric metric) { + public <G extends Serializable> Measure<G> getMeasure(Metric<G> metric) { return index.getMeasure(project, metric); } @@ -130,7 +135,7 @@ public class DefaultSensorContext implements SensorContext { } @Override - public Measure getMeasure(Resource resource, Metric metric) { + public <G extends Serializable> Measure<G> getMeasure(Resource resource, Metric<G> metric) { return index.getMeasure(resource, metric); } diff --git a/sonar-batch/src/main/java/org/sonar/batch/bootstrap/AnalysisMode.java b/sonar-batch/src/main/java/org/sonar/batch/bootstrap/AnalysisMode.java index 4415122c3f6..89640219562 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/bootstrap/AnalysisMode.java +++ b/sonar-batch/src/main/java/org/sonar/batch/bootstrap/AnalysisMode.java @@ -38,6 +38,7 @@ public class AnalysisMode implements BatchComponent { private boolean preview; private boolean incremental; private int previewReadTimeoutSec; + private boolean sensorMode; public AnalysisMode(BootstrapProperties bootstrapProps) { init(bootstrapProps); @@ -51,20 +52,28 @@ public class AnalysisMode implements BatchComponent { return incremental; } + public boolean isSensorMode() { + return sensorMode; + } + private void init(BootstrapProperties bootstrapProps) { if (bootstrapProps.properties().containsKey(CoreProperties.DRY_RUN)) { LOG.warn(MessageFormat.format("Property {0} is deprecated. Please use {1} instead.", CoreProperties.DRY_RUN, CoreProperties.ANALYSIS_MODE)); preview = "true".equals(bootstrapProps.property(CoreProperties.DRY_RUN)); incremental = false; + sensorMode = false; } else { String mode = bootstrapProps.property(CoreProperties.ANALYSIS_MODE); preview = CoreProperties.ANALYSIS_MODE_PREVIEW.equals(mode); incremental = CoreProperties.ANALYSIS_MODE_INCREMENTAL.equals(mode); + sensorMode = CoreProperties.ANALYSIS_MODE_SENSOR.equals(mode); } if (incremental) { LOG.info("Incremental mode"); } else if (preview) { LOG.info("Preview mode"); + } else if (sensorMode) { + LOG.info("Sensor mode"); } // To stay compatible with plugins that use the old property to check mode if (incremental || preview) { diff --git a/sonar-batch/src/main/java/org/sonar/batch/bootstrap/BatchComponents.java b/sonar-batch/src/main/java/org/sonar/batch/bootstrap/BatchComponents.java index 02c98554fa6..0391110d3a5 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/bootstrap/BatchComponents.java +++ b/sonar-batch/src/main/java/org/sonar/batch/bootstrap/BatchComponents.java @@ -20,9 +20,6 @@ package org.sonar.batch.bootstrap; import com.google.common.collect.Lists; -import org.sonar.batch.debt.DebtDecorator; -import org.sonar.batch.debt.IssueChangelogDebtCalculator; -import org.sonar.batch.debt.NewDebtDecorator; import org.sonar.batch.maven.DefaultMavenPluginExecutor; import org.sonar.batch.maven.MavenProjectBootstrapper; import org.sonar.batch.maven.MavenProjectBuilder; @@ -36,11 +33,8 @@ public class BatchComponents { public static Collection all() { List components = Lists.newArrayList( // Maven - MavenProjectBootstrapper.class, DefaultMavenPluginExecutor.class, MavenProjectConverter.class, MavenProjectBuilder.class, - - // Debt - IssueChangelogDebtCalculator.class, DebtDecorator.class, NewDebtDecorator.class - ); + MavenProjectBootstrapper.class, DefaultMavenPluginExecutor.class, MavenProjectConverter.class, MavenProjectBuilder.class + ); components.addAll(CorePropertyDefinitions.all()); return components; } diff --git a/sonar-batch/src/main/java/org/sonar/batch/bootstrap/BatchExtensionDictionnary.java b/sonar-batch/src/main/java/org/sonar/batch/bootstrap/BatchExtensionDictionnary.java index 7cbf67570b1..538e3091e42 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/bootstrap/BatchExtensionDictionnary.java +++ b/sonar-batch/src/main/java/org/sonar/batch/bootstrap/BatchExtensionDictionnary.java @@ -21,10 +21,14 @@ package org.sonar.batch.bootstrap; import com.google.common.collect.Lists; import org.apache.commons.lang.ClassUtils; -import org.sonar.api.BatchExtension; import org.sonar.api.batch.CheckProject; +import org.sonar.api.batch.Sensor; +import org.sonar.api.batch.fs.FileSystem; import org.sonar.api.platform.ComponentContainer; import org.sonar.api.resources.Project; +import org.sonar.batch.api.analyzer.Analyzer; +import org.sonar.batch.api.analyzer.AnalyzerContext; +import org.sonar.batch.scan.SensorWrapper; import java.util.Collection; import java.util.List; @@ -34,8 +38,13 @@ import java.util.List; */ public class BatchExtensionDictionnary extends org.sonar.api.batch.BatchExtensionDictionnary { - public BatchExtensionDictionnary(ComponentContainer componentContainer) { + private FileSystem fs; + private AnalyzerContext context; + + public BatchExtensionDictionnary(ComponentContainer componentContainer, FileSystem fs, AnalyzerContext context) { super(componentContainer); + this.fs = fs; + this.context = context; } public <T> Collection<T> select(Class<T> type, Project project, boolean sort, ExtensionMatcher matcher) { @@ -48,7 +57,10 @@ public class BatchExtensionDictionnary extends org.sonar.api.batch.BatchExtensio private <T> List<T> getFilteredExtensions(Class<T> type, Project project, ExtensionMatcher matcher) { List<T> result = Lists.newArrayList(); - for (BatchExtension extension : getExtensions()) { + for (Object extension : getExtensions()) { + if (type == Sensor.class && extension instanceof Analyzer) { + extension = new SensorWrapper((Analyzer) extension, context, fs); + } if (shouldKeep(type, extension, project, matcher)) { result.add((T) extension); } @@ -57,7 +69,9 @@ public class BatchExtensionDictionnary extends org.sonar.api.batch.BatchExtensio } private boolean shouldKeep(Class type, Object extension, Project project, ExtensionMatcher matcher) { - boolean keep = ClassUtils.isAssignable(extension.getClass(), type) && (matcher == null || matcher.accept(extension)); + boolean keep = (ClassUtils.isAssignable(extension.getClass(), type) + || (type == Sensor.class && ClassUtils.isAssignable(extension.getClass(), Analyzer.class))) + && (matcher == null || matcher.accept(extension)); if (keep && project != null && ClassUtils.isAssignable(extension.getClass(), CheckProject.class)) { keep = ((CheckProject) extension).shouldExecuteOnProject(project); } diff --git a/sonar-batch/src/main/java/org/sonar/batch/bootstrap/BootstrapContainer.java b/sonar-batch/src/main/java/org/sonar/batch/bootstrap/BootstrapContainer.java index 693be2183a2..5d7e836786b 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/bootstrap/BootstrapContainer.java +++ b/sonar-batch/src/main/java/org/sonar/batch/bootstrap/BootstrapContainer.java @@ -39,6 +39,10 @@ import org.sonar.batch.components.PastSnapshotFinderByDays; import org.sonar.batch.components.PastSnapshotFinderByPreviousAnalysis; import org.sonar.batch.components.PastSnapshotFinderByPreviousVersion; import org.sonar.batch.components.PastSnapshotFinderByVersion; +import org.sonar.batch.debt.DebtModelProvider; +import org.sonar.batch.rule.RulesProvider; +import org.sonar.batch.rules.DefaultQProfileReferential; +import org.sonar.batch.rules.QProfilesReferential; import org.sonar.batch.settings.DefaultSettingsReferential; import org.sonar.batch.settings.SettingsReferential; import org.sonar.core.cluster.NullQueue; @@ -83,8 +87,8 @@ public class BootstrapContainer extends ComponentContainer { addBootstrapComponents(); if (!sensorMode) { addDatabaseComponents(); + addCoreComponents(); } - addCoreComponents(); } private void addBootstrapComponents() { @@ -118,6 +122,9 @@ public class BootstrapContainer extends ComponentContainer { if (getComponentByType(MetricFinder.class) == null) { add(CacheMetricFinder.class); } + if (getComponentByType(QProfilesReferential.class) == null) { + add(DefaultQProfileReferential.class); + } } private void addDatabaseComponents() { @@ -156,7 +163,9 @@ public class BootstrapContainer extends ComponentContainer { PastSnapshotFinderByPreviousVersion.class, PastMeasuresLoader.class, PastSnapshotFinder.class, - Durations.class); + Durations.class, + new DebtModelProvider(), + new RulesProvider()); } @Override @@ -172,8 +181,8 @@ public class BootstrapContainer extends ComponentContainer { } } - public void executeTask(Map<String, String> taskProperties) { - new TaskContainer(this, taskProperties).execute(); + public void executeTask(Map<String, String> taskProperties, Object... components) { + new TaskContainer(this, taskProperties, components).execute(); } } diff --git a/sonar-batch/src/main/java/org/sonar/batch/bootstrap/ExtensionInstaller.java b/sonar-batch/src/main/java/org/sonar/batch/bootstrap/ExtensionInstaller.java index bb2b2e431f2..b7088cf4c01 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/bootstrap/ExtensionInstaller.java +++ b/sonar-batch/src/main/java/org/sonar/batch/bootstrap/ExtensionInstaller.java @@ -43,11 +43,10 @@ public class ExtensionInstaller { } public ExtensionInstaller install(ComponentContainer container, ExtensionMatcher matcher) { - boolean preview = analysisMode.isPreview(); // core components for (Object o : BatchComponents.all()) { - doInstall(container, matcher, null, preview, o); + doInstall(container, matcher, null, o); } // plugin extensions @@ -55,7 +54,7 @@ public class ExtensionInstaller { PluginMetadata metadata = entry.getKey(); Plugin plugin = entry.getValue(); for (Object extension : plugin.getExtensions()) { - doInstall(container, matcher, metadata, preview, extension); + doInstall(container, matcher, metadata, extension); } } List<ExtensionProvider> providers = container.getComponentsByType(ExtensionProvider.class); @@ -63,18 +62,18 @@ public class ExtensionInstaller { Object object = provider.provide(); if (object instanceof Iterable) { for (Object extension : (Iterable) object) { - doInstall(container, matcher, null, preview, extension); + doInstall(container, matcher, null, extension); } } else { - doInstall(container, matcher, null, preview, object); + doInstall(container, matcher, null, object); } } return this; } - private void doInstall(ComponentContainer container, ExtensionMatcher matcher, @Nullable PluginMetadata metadata, boolean dryRun, Object extension) { + private void doInstall(ComponentContainer container, ExtensionMatcher matcher, @Nullable PluginMetadata metadata, Object extension) { if (ExtensionUtils.supportsEnvironment(extension, env) - && (!dryRun || ExtensionUtils.supportsPreview(extension)) + && (!analysisMode.isPreview() || ExtensionUtils.supportsPreview(extension)) && matcher.accept(extension)) { container.addExtension(metadata, extension); } else { diff --git a/sonar-batch/src/main/java/org/sonar/batch/bootstrap/TaskContainer.java b/sonar-batch/src/main/java/org/sonar/batch/bootstrap/TaskContainer.java index 86122e7dec4..e7fd8427a2d 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/bootstrap/TaskContainer.java +++ b/sonar-batch/src/main/java/org/sonar/batch/bootstrap/TaskContainer.java @@ -41,10 +41,12 @@ import java.util.Map; public class TaskContainer extends ComponentContainer { private final Map<String, String> taskProperties; + private final Object[] components; - public TaskContainer(ComponentContainer parent, Map<String, String> taskProperties) { + public TaskContainer(ComponentContainer parent, Map<String, String> taskProperties, Object... components) { super(parent); this.taskProperties = taskProperties; + this.components = components; } @Override @@ -52,6 +54,9 @@ public class TaskContainer extends ComponentContainer { installCoreTasks(); installTaskExtensions(); installComponentsUsingTaskExtensions(); + for (Object component : components) { + add(component); + } } void installCoreTasks() { diff --git a/sonar-batch/src/main/java/org/sonar/batch/bootstrapper/Batch.java b/sonar-batch/src/main/java/org/sonar/batch/bootstrapper/Batch.java index 972450aebde..ac107141bc3 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/bootstrapper/Batch.java +++ b/sonar-batch/src/main/java/org/sonar/batch/bootstrapper/Batch.java @@ -87,12 +87,12 @@ public final class Batch { /** * @since 4.4 */ - public Batch executeTask(Map<String, String> taskProperties) { + public Batch executeTask(Map<String, String> taskProperties, Object... components) { if (!started) { throw new IllegalStateException("Batch is not started. Unable to execute task."); } - bootstrapContainer.executeTask(taskProperties); + bootstrapContainer.executeTask(taskProperties, components); return this; } diff --git a/sonar-batch/src/main/java/org/sonar/batch/index/DefaultIndex.java b/sonar-batch/src/main/java/org/sonar/batch/index/DefaultIndex.java index 44db4e53f73..ae75f47f2a4 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/index/DefaultIndex.java +++ b/sonar-batch/src/main/java/org/sonar/batch/index/DefaultIndex.java @@ -178,7 +178,7 @@ public class DefaultIndex extends SonarIndex { } @Override - public Measure getMeasure(Resource resource, Metric metric) { + public Measure getMeasure(Resource resource, org.sonar.batch.api.measures.Metric<?> metric) { return getMeasures(resource, MeasuresFilters.metric(metric)); } diff --git a/sonar-batch/src/main/java/org/sonar/batch/index/ResourceKeyMigration.java b/sonar-batch/src/main/java/org/sonar/batch/index/ResourceKeyMigration.java index 99eeb05cac9..4e169391d11 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/index/ResourceKeyMigration.java +++ b/sonar-batch/src/main/java/org/sonar/batch/index/ResourceKeyMigration.java @@ -26,7 +26,7 @@ import org.slf4j.LoggerFactory; import org.sonar.api.BatchComponent; import org.sonar.api.batch.fs.FileSystem; import org.sonar.api.batch.fs.InputFile; -import org.sonar.api.batch.fs.internal.DefaultInputFile; +import org.sonar.api.batch.fs.internal.DeprecatedDefaultInputFile; import org.sonar.api.database.DatabaseSession; import org.sonar.api.database.model.ResourceModel; import org.sonar.api.resources.Directory; @@ -80,7 +80,7 @@ public class ResourceKeyMigration implements BatchComponent { Map<String, InputFile> deprecatedTestKeyMapper = new HashMap<String, InputFile>(); Map<String, String> deprecatedDirectoryKeyMapper = new HashMap<String, String>(); for (InputFile inputFile : inputFiles) { - String deprecatedKey = ((DefaultInputFile) inputFile).deprecatedKey(); + String deprecatedKey = ((DeprecatedDefaultInputFile) inputFile).deprecatedKey(); if (deprecatedKey != null) { if (InputFile.Type.TEST == inputFile.type() && !deprecatedTestKeyMapper.containsKey(deprecatedKey)) { deprecatedTestKeyMapper.put(deprecatedKey, inputFile); @@ -109,7 +109,7 @@ public class ResourceKeyMigration implements BatchComponent { boolean isTest = Qualifiers.UNIT_TEST_FILE.equals(resourceModel.getQualifier()); InputFile matchedFile = findInputFile(deprecatedFileKeyMapper, deprecatedTestKeyMapper, oldEffectiveKey, isTest); if (matchedFile != null) { - String newEffectiveKey = ((DefaultInputFile) matchedFile).key(); + String newEffectiveKey = ((DeprecatedDefaultInputFile) matchedFile).key(); // Now compute migration of the parent dir String oldKey = StringUtils.substringAfterLast(oldEffectiveKey, ":"); Resource sonarFile; diff --git a/sonar-batch/src/main/java/org/sonar/batch/issue/IssueFilters.java b/sonar-batch/src/main/java/org/sonar/batch/issue/IssueFilters.java index e896729663a..79b576d3398 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/issue/IssueFilters.java +++ b/sonar-batch/src/main/java/org/sonar/batch/issue/IssueFilters.java @@ -53,15 +53,34 @@ public class IssueFilters implements BatchExtension { this(deprecatedFilters, deprecatedViolations, new org.sonar.api.issue.IssueFilter[0]); } + /** + * Used by scan2 + */ + public IssueFilters(org.sonar.api.issue.IssueFilter[] exclusionFilters, IssueFilter[] filters) { + this(null, null, exclusionFilters, filters); + } + + public IssueFilters(org.sonar.api.issue.IssueFilter[] exclusionFilters) { + this(null, null, exclusionFilters, new IssueFilter[0]); + } + + public IssueFilters(IssueFilter[] filters) { + this(null, null, new org.sonar.api.issue.IssueFilter[0], filters); + } + + public IssueFilters() { + this(null, null, new org.sonar.api.issue.IssueFilter[0], new IssueFilter[0]); + } + public boolean accept(DefaultIssue issue, @Nullable Violation violation) { - if(new DefaultIssueFilterChain(filters).accept(issue)) { + if (new DefaultIssueFilterChain(filters).accept(issue)) { // Apply deprecated rules only if filter chain accepts the current issue for (org.sonar.api.issue.IssueFilter filter : exclusionFilters) { if (!filter.accept(issue)) { return false; } } - if (!deprecatedFilters.isEmpty()) { + if (deprecatedFilters != null && !deprecatedFilters.isEmpty()) { Violation v = violation != null ? violation : deprecatedViolations.toViolation(issue); return !deprecatedFilters.isIgnored(v); } diff --git a/sonar-batch/src/main/java/org/sonar/batch/issue/ModuleIssues.java b/sonar-batch/src/main/java/org/sonar/batch/issue/ModuleIssues.java index 6f46e2cc7a9..1e880a6b31d 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/issue/ModuleIssues.java +++ b/sonar-batch/src/main/java/org/sonar/batch/issue/ModuleIssues.java @@ -47,7 +47,7 @@ public class ModuleIssues { private final Project project; private final IssueFilters filters; - public ModuleIssues(ActiveRules activeRules, Rules rules, IssueCache cache, Project project, IssueFilters filters) { + public ModuleIssues(ActiveRules activeRules, Rules rules, IssueCache cache, @Nullable Project project, IssueFilters filters) { this.activeRules = activeRules; this.rules = rules; this.cache = cache; @@ -55,6 +55,13 @@ public class ModuleIssues { this.filters = filters; } + /** + * Used by scan2 + */ + public ModuleIssues(ActiveRules activeRules, Rules rules, IssueCache cache, IssueFilters filters) { + this(activeRules, rules, cache, null, filters); + } + public boolean initAndAddIssue(DefaultIssue issue) { return initAndAddIssue(issue, null); } @@ -67,6 +74,7 @@ public class ModuleIssues { private DefaultIssue newIssue(Violation violation) { return (DefaultIssue) new DefaultIssueBuilder() .componentKey(violation.getResource().getEffectiveKey()) + // Project can be null but Violation not used by scan2 .projectKey(project.getRoot().getEffectiveKey()) .ruleKey(RuleKey.of(violation.getRule().getRepositoryKey(), violation.getRule().getKey())) .effortToFix(violation.getCost()) @@ -107,8 +115,10 @@ public class ModuleIssues { if (Strings.isNullOrEmpty(issue.message())) { issue.setMessage(rule.name()); } - issue.setCreationDate(project.getAnalysisDate()); - issue.setUpdateDate(project.getAnalysisDate()); + if (project != null) { + issue.setCreationDate(project.getAnalysisDate()); + issue.setUpdateDate(project.getAnalysisDate()); + } if (issue.severity() == null) { issue.setSeverity(activeRule.severity()); } diff --git a/sonar-batch/src/main/java/org/sonar/batch/issue/ignore/scanner/IssueExclusionsLoader.java b/sonar-batch/src/main/java/org/sonar/batch/issue/ignore/scanner/IssueExclusionsLoader.java index 1dad68550bb..5f3e9c308e8 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/issue/ignore/scanner/IssueExclusionsLoader.java +++ b/sonar-batch/src/main/java/org/sonar/batch/issue/ignore/scanner/IssueExclusionsLoader.java @@ -22,7 +22,7 @@ package org.sonar.batch.issue.ignore.scanner; import org.sonar.api.batch.fs.FileSystem; import org.sonar.api.batch.fs.InputFile; -import org.sonar.api.batch.fs.internal.DefaultInputFile; +import org.sonar.api.batch.fs.internal.DeprecatedDefaultInputFile; import org.sonar.api.resources.Project; import org.sonar.api.utils.SonarException; import org.sonar.batch.issue.ignore.pattern.IssueExclusionPatternInitializer; @@ -59,7 +59,7 @@ public final class IssueExclusionsLoader { for (InputFile inputFile : fileSystem.inputFiles(fileSystem.predicates().all())) { try { - String componentEffectiveKey = ((DefaultInputFile) inputFile).key(); + String componentEffectiveKey = ((DeprecatedDefaultInputFile) inputFile).key(); if (componentEffectiveKey != null) { String path = inputFile.relativePath(); inclusionPatternInitializer.initializePatternsForPath(path, componentEffectiveKey); diff --git a/sonar-batch/src/main/java/org/sonar/batch/language/LanguageDistributionDecorator.java b/sonar-batch/src/main/java/org/sonar/batch/language/LanguageDistributionDecorator.java index ca77b7c239c..fcada30d053 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/language/LanguageDistributionDecorator.java +++ b/sonar-batch/src/main/java/org/sonar/batch/language/LanguageDistributionDecorator.java @@ -44,14 +44,14 @@ public class LanguageDistributionDecorator implements Decorator { @DependsUpon public List<Metric> dependsUponMetrics() { - return ImmutableList.of(CoreMetrics.LINES); + return ImmutableList.<Metric>of(CoreMetrics.LINES); } @DependedUpon public List<Metric> generatesMetrics() { return ImmutableList.of( CoreMetrics.NCLOC_LANGUAGE_DISTRIBUTION - ); + ); } public void decorate(Resource resource, DecoratorContext context) { diff --git a/sonar-batch/src/main/java/org/sonar/batch/languages/DeprecatedLanguagesReferential.java b/sonar-batch/src/main/java/org/sonar/batch/languages/DeprecatedLanguagesReferential.java new file mode 100644 index 00000000000..1084b7d49a2 --- /dev/null +++ b/sonar-batch/src/main/java/org/sonar/batch/languages/DeprecatedLanguagesReferential.java @@ -0,0 +1,63 @@ +/* + * 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.languages; + +import org.sonar.api.resources.Languages; +import org.sonar.batch.api.languages.Language; + +import javax.annotation.CheckForNull; + +import java.util.ArrayList; +import java.util.Collection; + +/** + * Languages referential using {@link Languages} + * @since 4.4 + */ +public class DeprecatedLanguagesReferential implements LanguagesReferential { + + private Languages languages; + + public DeprecatedLanguagesReferential(Languages languages) { + this.languages = languages; + } + + /** + * Get language. + */ + @CheckForNull + public Language get(String languageKey) { + org.sonar.api.resources.Language language = languages.get(languageKey); + return language != null ? new Language(language.getKey(), language.getName(), language.getFileSuffixes()) : null; + } + + /** + * Get list of all supported languages. + */ + public Collection<Language> all() { + org.sonar.api.resources.Language[] all = languages.all(); + Collection<Language> result = new ArrayList<Language>(all.length); + for (org.sonar.api.resources.Language language : all) { + result.add(new Language(language.getKey(), language.getName(), language.getFileSuffixes())); + } + return result; + } + +} diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/FileIndex.java b/sonar-batch/src/main/java/org/sonar/batch/languages/LanguagesReferential.java index 4c61809fa3c..bf9fcd00de5 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/FileIndex.java +++ b/sonar-batch/src/main/java/org/sonar/batch/languages/LanguagesReferential.java @@ -17,23 +17,30 @@ * 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.batch.languages; -import com.google.common.collect.ImmutableList; -import org.sonar.api.batch.fs.InputFile; +import org.sonar.api.BatchComponent; +import org.sonar.batch.api.languages.Language; import javax.annotation.CheckForNull; -import java.util.List; -// Accepted to support both InputFile and InputDir as long as indexes are on the same attributes -public interface FileIndex { +import java.util.Collection; - // Currently only a single index is supported - List<FileIndex> ALL = ImmutableList.<FileIndex>of(new RelativePathIndex()); +/** + * Languages referential + * @since 4.4 + */ +public interface LanguagesReferential extends BatchComponent { + /** + * Get language. + */ @CheckForNull - Object valueOf(InputFile f); + Language get(String languageKey); - String id(); + /** + * Get list of all supported languages. + */ + Collection<Language> all(); } diff --git a/sonar-batch/src/main/java/org/sonar/batch/languages/package-info.java b/sonar-batch/src/main/java/org/sonar/batch/languages/package-info.java new file mode 100644 index 00000000000..5ce5bc10019 --- /dev/null +++ b/sonar-batch/src/main/java/org/sonar/batch/languages/package-info.java @@ -0,0 +1,20 @@ +/* + * 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.languages;
\ No newline at end of file diff --git a/sonar-batch/src/main/java/org/sonar/batch/phases/SensorMatcher.java b/sonar-batch/src/main/java/org/sonar/batch/phases/SensorMatcher.java index 2ee73f61dfb..6328ac77a46 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/phases/SensorMatcher.java +++ b/sonar-batch/src/main/java/org/sonar/batch/phases/SensorMatcher.java @@ -19,12 +19,12 @@ */ package org.sonar.batch.phases; -import org.sonar.batch.bootstrap.ExtensionMatcher; - import org.apache.commons.lang.ClassUtils; import org.sonar.api.BatchExtension; import org.sonar.api.batch.InstantiationStrategy; import org.sonar.api.batch.Sensor; +import org.sonar.batch.api.analyzer.Analyzer; +import org.sonar.batch.bootstrap.ExtensionMatcher; /** * Allow to filter sensors that will be executed. @@ -36,7 +36,8 @@ public abstract class SensorMatcher implements BatchExtension, ExtensionMatcher @Override public final boolean accept(Object extension) { - return ClassUtils.isAssignable(extension.getClass(), Sensor.class) && acceptSensor((Sensor) extension); + return ClassUtils.isAssignable(extension.getClass(), Sensor.class) + && acceptSensor((Sensor) extension) || ClassUtils.isAssignable(extension.getClass(), Analyzer.class); } public abstract boolean acceptSensor(Sensor sensor); diff --git a/sonar-batch/src/main/java/org/sonar/batch/rule/ActiveRulesProvider.java b/sonar-batch/src/main/java/org/sonar/batch/rule/ActiveRulesProvider.java index 95762afd79d..a7aea325d48 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/rule/ActiveRulesProvider.java +++ b/sonar-batch/src/main/java/org/sonar/batch/rule/ActiveRulesProvider.java @@ -28,6 +28,8 @@ import org.sonar.api.batch.rule.internal.NewActiveRule; import org.sonar.api.rules.Rule; import org.sonar.api.rules.RuleFinder; import org.sonar.api.rules.RuleParam; +import org.sonar.batch.api.rules.QProfile; +import org.sonar.batch.rules.QProfileWithId; import org.sonar.core.qualityprofile.db.ActiveRuleDao; import org.sonar.core.qualityprofile.db.ActiveRuleDto; import org.sonar.core.qualityprofile.db.ActiveRuleParamDto; @@ -49,13 +51,14 @@ public class ActiveRulesProvider extends ProviderAdapter { private ActiveRules load(ModuleQProfiles qProfiles, ActiveRuleDao dao, RuleFinder ruleFinder) { ActiveRulesBuilder builder = new ActiveRulesBuilder(); - for (ModuleQProfiles.QProfile qProfile : qProfiles.findAll()) { + for (QProfile qProfile : qProfiles.findAll()) { + QProfileWithId qProfileWithId = (QProfileWithId) qProfile; ListMultimap<Integer, ActiveRuleParamDto> paramDtosByActiveRuleId = ArrayListMultimap.create(); - for (ActiveRuleParamDto dto : dao.selectParamsByProfileId(qProfile.id())) { + for (ActiveRuleParamDto dto : dao.selectParamsByProfileId(qProfileWithId.id())) { paramDtosByActiveRuleId.put(dto.getActiveRuleId(), dto); } - for (ActiveRuleDto activeDto : dao.selectByProfileId(qProfile.id())) { + for (ActiveRuleDto activeDto : dao.selectByProfileId(qProfileWithId.id())) { Rule rule = ruleFinder.findById(activeDto.getRulId()); if (rule != null) { NewActiveRule newActiveRule = builder.activate(rule.ruleKey()); diff --git a/sonar-batch/src/main/java/org/sonar/batch/rule/ModuleQProfiles.java b/sonar-batch/src/main/java/org/sonar/batch/rule/ModuleQProfiles.java index fb5027d923b..674fbb59a56 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/rule/ModuleQProfiles.java +++ b/sonar-batch/src/main/java/org/sonar/batch/rule/ModuleQProfiles.java @@ -23,11 +23,11 @@ import com.google.common.collect.ImmutableMap; import org.apache.commons.lang.StringUtils; import org.sonar.api.BatchComponent; import org.sonar.api.config.Settings; -import org.sonar.api.resources.Language; -import org.sonar.api.resources.Languages; import org.sonar.api.utils.MessageException; -import org.sonar.core.qualityprofile.db.QualityProfileDao; -import org.sonar.core.qualityprofile.db.QualityProfileDto; +import org.sonar.batch.api.languages.Language; +import org.sonar.batch.api.rules.QProfile; +import org.sonar.batch.languages.LanguagesReferential; +import org.sonar.batch.rules.QProfilesReferential; import javax.annotation.CheckForNull; @@ -41,55 +41,19 @@ public class ModuleQProfiles implements BatchComponent { public static final String SONAR_PROFILE_PROP = "sonar.profile"; - public static class QProfile { - private final String name, language; - private final Integer version; - private final int id; - - public QProfile(QualityProfileDto dto) { - this.id = dto.getId(); - this.name = dto.getName(); - this.language = dto.getLanguage(); - this.version = dto.getVersion(); - } - - QProfile(int id, String name, String language, Integer version) { - this.id = id; - this.name = name; - this.language = language; - this.version = version; - } - - public int id() { - return id; - } - - public String name() { - return name; - } - - public String language() { - return language; - } - - public Integer version() { - return version; - } - } - private final Map<String, QProfile> byLanguage; - public ModuleQProfiles(Settings settings, Languages languages, QualityProfileDao dao) { + public ModuleQProfiles(Settings settings, LanguagesReferential languages, QProfilesReferential qProfileRef) { ImmutableMap.Builder<String, QProfile> builder = ImmutableMap.builder(); String defaultName = settings.getString(SONAR_PROFILE_PROP); for (Language language : languages.all()) { QProfile profile = null; if (StringUtils.isNotBlank(defaultName)) { - profile = loadDefaultQProfile(dao, defaultName, language.getKey()); + profile = loadDefaultQProfile(qProfileRef, defaultName, language.key()); } if (profile == null) { - profile = loadQProfile(dao, settings, language.getKey()); + profile = loadQProfile(qProfileRef, settings, language.key()); } if (profile != null) { builder.put(profile.language(), profile); @@ -99,25 +63,21 @@ public class ModuleQProfiles implements BatchComponent { } @CheckForNull - private QProfile loadQProfile(QualityProfileDao dao, Settings settings, String language) { + private QProfile loadQProfile(QProfilesReferential qProfileRef, Settings settings, String language) { String profileName = settings.getString("sonar.profile." + language); if (profileName != null) { - QualityProfileDto dto = dao.selectByNameAndLanguage(profileName, language); + QProfile dto = qProfileRef.get(language, profileName); if (dto == null) { throw MessageException.of(String.format("Quality profile not found : '%s' on language '%s'", profileName, language)); } - return new QProfile(dto); + return dto; } return null; } @CheckForNull - private QProfile loadDefaultQProfile(QualityProfileDao dao, String profileName, String language) { - QualityProfileDto dto = dao.selectByNameAndLanguage(profileName, language); - if (dto != null) { - return new QProfile(dto); - } - return null; + private QProfile loadDefaultQProfile(QProfilesReferential qProfileRef, String profileName, String language) { + return qProfileRef.get(language, profileName); } public Collection<QProfile> findAll() { diff --git a/sonar-batch/src/main/java/org/sonar/batch/rule/QProfileEventsDecorator.java b/sonar-batch/src/main/java/org/sonar/batch/rule/QProfileEventsDecorator.java index ee67fcb31cd..e84dc342c17 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/rule/QProfileEventsDecorator.java +++ b/sonar-batch/src/main/java/org/sonar/batch/rule/QProfileEventsDecorator.java @@ -34,8 +34,7 @@ import org.sonar.api.resources.Languages; import org.sonar.api.resources.Project; import org.sonar.api.resources.Qualifiers; import org.sonar.api.resources.Resource; -import org.sonar.api.resources.ResourceUtils; -import org.sonar.batch.rule.ModuleQProfiles.QProfile; +import org.sonar.batch.rules.QProfileWithId; import org.sonar.core.qualityprofile.db.QualityProfileDao; import org.sonar.core.qualityprofile.db.QualityProfileDto; @@ -101,14 +100,14 @@ public class QProfileEventsDecorator implements Decorator { } else { pastProfileVersion = pastProfileVersionMeasure.getIntValue(); } - pastProfiles = UsedQProfiles.fromProfiles(new ModuleQProfiles.QProfile(pastProfileId, pastProfileName, pastProfileLanguage, pastProfileVersion)); + pastProfiles = UsedQProfiles.fromProfiles(new QProfileWithId(pastProfileId, pastProfileName, pastProfileLanguage, pastProfileVersion)); } // Now create appropriate events - Map<Integer, QProfile> pastProfilesById = Maps.newHashMap(pastProfiles.profilesById()); - for (QProfile profile : currentProfiles.profilesById().values()) { + Map<Integer, QProfileWithId> pastProfilesById = Maps.newHashMap(pastProfiles.profilesById()); + for (QProfileWithId profile : currentProfiles.profilesById().values()) { if (pastProfilesById.containsKey(profile.id())) { - QProfile pastProfile = pastProfilesById.get(profile.id()); + QProfileWithId pastProfile = pastProfilesById.get(profile.id()); if (pastProfile.version() < profile.version()) { // New version of the same QP usedProfile(context, profile); @@ -118,25 +117,25 @@ public class QProfileEventsDecorator implements Decorator { usedProfile(context, profile); } } - for (QProfile profile : pastProfilesById.values()) { + for (QProfileWithId profile : pastProfilesById.values()) { // Following profiles are no more used stopUsedProfile(context, profile); } } - private void stopUsedProfile(DecoratorContext context, QProfile profile) { + private void stopUsedProfile(DecoratorContext context, QProfileWithId profile) { Language language = languages.get(profile.language()); String languageName = language != null ? language.getName() : profile.language(); context.createEvent("Stop using " + format(profile) + " (" + languageName + ")", format(profile) + " no more used for " + languageName, Event.CATEGORY_PROFILE, null); } - private void usedProfile(DecoratorContext context, QProfile profile) { + private void usedProfile(DecoratorContext context, QProfileWithId profile) { Language language = languages.get(profile.language()); String languageName = language != null ? language.getName() : profile.language(); context.createEvent("Use " + format(profile) + " (" + languageName + ")", format(profile) + " used for " + languageName, Event.CATEGORY_PROFILE, null); } - private String format(QProfile profile) { + private String format(QProfileWithId profile) { return profile.name() + " version " + profile.version(); } diff --git a/sonar-batch/src/main/java/org/sonar/batch/rule/QProfileSensor.java b/sonar-batch/src/main/java/org/sonar/batch/rule/QProfileSensor.java index 0773a3c6472..620f769d8b7 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/rule/QProfileSensor.java +++ b/sonar-batch/src/main/java/org/sonar/batch/rule/QProfileSensor.java @@ -26,6 +26,7 @@ import org.sonar.api.batch.fs.FileSystem; import org.sonar.api.measures.CoreMetrics; import org.sonar.api.measures.Measure; import org.sonar.api.resources.Project; +import org.sonar.batch.rules.QProfileWithId; import org.sonar.core.qualityprofile.db.QualityProfileDao; import java.util.List; @@ -51,9 +52,9 @@ public class QProfileSensor implements Sensor { } public void analyse(Project project, SensorContext context) { - List<ModuleQProfiles.QProfile> profiles = Lists.newArrayList(); + List<QProfileWithId> profiles = Lists.newArrayList(); for (String language : fs.languages()) { - ModuleQProfiles.QProfile qProfile = moduleQProfiles.findByLanguage(language); + QProfileWithId qProfile = (QProfileWithId) moduleQProfiles.findByLanguage(language); if (qProfile != null) { dao.updateUsedColumn(qProfile.id(), true); profiles.add(qProfile); @@ -65,7 +66,7 @@ public class QProfileSensor implements Sensor { // For backward compatibility if (profiles.size() == 1) { - ModuleQProfiles.QProfile qProfile = profiles.get(0); + QProfileWithId qProfile = profiles.get(0); Measure measure = new Measure(CoreMetrics.PROFILE, qProfile.name()).setValue((double) qProfile.id()); Measure measureVersion = new Measure(CoreMetrics.PROFILE_VERSION, qProfile.version().doubleValue()); context.saveMeasure(measure); diff --git a/sonar-batch/src/main/java/org/sonar/batch/rule/QProfileVerifier.java b/sonar-batch/src/main/java/org/sonar/batch/rule/QProfileVerifier.java index ac8e51eb10e..10e110b6ee9 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/rule/QProfileVerifier.java +++ b/sonar-batch/src/main/java/org/sonar/batch/rule/QProfileVerifier.java @@ -27,7 +27,7 @@ import org.sonar.api.BatchComponent; import org.sonar.api.batch.fs.FileSystem; import org.sonar.api.config.Settings; import org.sonar.api.utils.MessageException; -import org.sonar.batch.rule.ModuleQProfiles.QProfile; +import org.sonar.batch.api.rules.QProfile; public class QProfileVerifier implements BatchComponent { diff --git a/sonar-batch/src/main/java/org/sonar/batch/rule/RulesProfileProvider.java b/sonar-batch/src/main/java/org/sonar/batch/rule/RulesProfileProvider.java index 2b778be21dc..2c5ec75a235 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/rule/RulesProfileProvider.java +++ b/sonar-batch/src/main/java/org/sonar/batch/rule/RulesProfileProvider.java @@ -30,6 +30,7 @@ import org.sonar.api.rules.ActiveRule; import org.sonar.api.rules.Rule; import org.sonar.api.rules.RuleFinder; import org.sonar.api.rules.RulePriority; +import org.sonar.batch.api.rules.QProfile; import java.util.Collection; import java.util.Map; @@ -55,8 +56,8 @@ public class RulesProfileProvider extends ProviderAdapter { } private RulesProfile loadSingleLanguageProfile(ModuleQProfiles qProfiles, ActiveRules activeRules, - RuleFinder ruleFinder, String language) { - ModuleQProfiles.QProfile qProfile = qProfiles.findByLanguage(language); + RuleFinder ruleFinder, String language) { + QProfile qProfile = qProfiles.findByLanguage(language); if (qProfile != null) { return new RulesProfileWrapper(select(qProfile, activeRules, ruleFinder)); } @@ -65,13 +66,13 @@ public class RulesProfileProvider extends ProviderAdapter { private RulesProfile loadProfiles(ModuleQProfiles qProfiles, ActiveRules activeRules, RuleFinder ruleFinder) { Collection<RulesProfile> dtos = Lists.newArrayList(); - for (ModuleQProfiles.QProfile qProfile : qProfiles.findAll()) { + for (QProfile qProfile : qProfiles.findAll()) { dtos.add(select(qProfile, activeRules, ruleFinder)); } return new RulesProfileWrapper(dtos); } - private RulesProfile select(ModuleQProfiles.QProfile qProfile, ActiveRules activeRules, RuleFinder ruleFinder) { + private RulesProfile select(QProfile qProfile, ActiveRules activeRules, RuleFinder ruleFinder) { RulesProfile deprecatedProfile = new RulesProfile(); deprecatedProfile.setVersion(qProfile.version()); deprecatedProfile.setName(qProfile.name()); diff --git a/sonar-batch/src/main/java/org/sonar/batch/rule/UsedQProfiles.java b/sonar-batch/src/main/java/org/sonar/batch/rule/UsedQProfiles.java index 1b1115ed18b..5973a74c593 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/rule/UsedQProfiles.java +++ b/sonar-batch/src/main/java/org/sonar/batch/rule/UsedQProfiles.java @@ -26,7 +26,7 @@ import com.google.gson.JsonElement; import com.google.gson.JsonObject; import com.google.gson.JsonParser; import org.sonar.api.utils.text.JsonWriter; -import org.sonar.batch.rule.ModuleQProfiles.QProfile; +import org.sonar.batch.rules.QProfileWithId; import javax.annotation.concurrent.Immutable; @@ -37,14 +37,14 @@ import java.util.Map; @Immutable public class UsedQProfiles { - private Map<Integer, ModuleQProfiles.QProfile> profilesById = Maps.newLinkedHashMap(); + private Map<Integer, QProfileWithId> profilesById = Maps.newLinkedHashMap(); private UsedQProfiles() { } - public static final UsedQProfiles fromProfiles(Iterable<QProfile> profiles) { + public static final UsedQProfiles fromProfiles(Iterable<QProfileWithId> profiles) { UsedQProfiles result = new UsedQProfiles(); - for (QProfile qProfile : profiles) { + for (QProfileWithId qProfile : profiles) { result.add(qProfile); } return result; @@ -54,7 +54,7 @@ public class UsedQProfiles { return new UsedQProfiles(); } - public static final UsedQProfiles fromProfiles(QProfile... profiles) { + public static final UsedQProfiles fromProfiles(QProfileWithId... profiles) { return fromProfiles(Arrays.asList(profiles)); } @@ -63,7 +63,7 @@ public class UsedQProfiles { JsonArray root = new JsonParser().parse(json).getAsJsonArray(); for (JsonElement elt : root) { JsonObject profile = elt.getAsJsonObject(); - result.add(new QProfile(profile.get("id").getAsInt(), profile.get("name").getAsString(), profile.get("language").getAsString(), profile.get("version").getAsInt())); + result.add(new QProfileWithId(profile.get("id").getAsInt(), profile.get("name").getAsString(), profile.get("language").getAsString(), profile.get("version").getAsInt())); } return result; } @@ -72,7 +72,7 @@ public class UsedQProfiles { StringWriter json = new StringWriter(); JsonWriter writer = JsonWriter.of(json); writer.beginArray(); - for (ModuleQProfiles.QProfile qProfile : profilesById.values()) { + for (QProfileWithId qProfile : profilesById.values()) { writer.beginObject() .prop("id", qProfile.id()) .prop("name", qProfile.name()) @@ -89,8 +89,8 @@ public class UsedQProfiles { return empty().mergeInPlace(this).mergeInPlace(other); } - private void add(ModuleQProfiles.QProfile profile) { - QProfile alreadyAdded = profilesById.get(profile.id()); + private void add(QProfileWithId profile) { + QProfileWithId alreadyAdded = profilesById.get(profile.id()); if (alreadyAdded == null // Keep only latest version || profile.version() > alreadyAdded.version()) { @@ -98,8 +98,8 @@ public class UsedQProfiles { } } - private UsedQProfiles addAll(Iterable<QProfile> profiles) { - for (QProfile profile : profiles) { + private UsedQProfiles addAll(Iterable<QProfileWithId> profiles) { + for (QProfileWithId profile : profiles) { this.add(profile); } return this; @@ -110,7 +110,7 @@ public class UsedQProfiles { return this; } - public Map<Integer, ModuleQProfiles.QProfile> profilesById() { + public Map<Integer, QProfileWithId> profilesById() { return ImmutableMap.copyOf(profilesById); } diff --git a/sonar-batch/src/main/java/org/sonar/batch/rules/DefaultQProfileReferential.java b/sonar-batch/src/main/java/org/sonar/batch/rules/DefaultQProfileReferential.java new file mode 100644 index 00000000000..936b17e63fe --- /dev/null +++ b/sonar-batch/src/main/java/org/sonar/batch/rules/DefaultQProfileReferential.java @@ -0,0 +1,46 @@ +/* + * 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.rules; + +import org.sonar.batch.api.rules.QProfile; +import org.sonar.core.qualityprofile.db.QualityProfileDao; +import org.sonar.core.qualityprofile.db.QualityProfileDto; + +/** + * @since 4.4 + */ +public class DefaultQProfileReferential implements QProfilesReferential { + + private QualityProfileDao qualityProfileDao; + + public DefaultQProfileReferential(QualityProfileDao qualityProfileDao) { + this.qualityProfileDao = qualityProfileDao; + } + + @Override + public QProfile get(String language, String name) { + QualityProfileDto dto = qualityProfileDao.selectByNameAndLanguage(name, language); + if (dto == null) { + return null; + } + return new QProfileWithId(dto.getId(), dto.getName(), dto.getLanguage(), dto.getVersion()); + } + +} diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/RelativePathIndex.java b/sonar-batch/src/main/java/org/sonar/batch/rules/QProfileWithId.java index 33facf13cf2..4d2511676a9 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/RelativePathIndex.java +++ b/sonar-batch/src/main/java/org/sonar/batch/rules/QProfileWithId.java @@ -17,23 +17,20 @@ * 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.batch.rules; -import org.sonar.api.batch.fs.InputFile; +import org.sonar.batch.api.rules.QProfile; -/** - * @since 4.2 - */ -public class RelativePathIndex implements FileIndex { - public static final String ID = "rel"; +public class QProfileWithId extends QProfile { + private final int id; - @Override - public Object valueOf(InputFile f) { - return f.relativePath(); + public QProfileWithId(int id, String name, String language, Integer version) { + super(name, language, version); + this.id = id; } - @Override - public String id() { - return ID; + public int id() { + return id; } + } diff --git a/sonar-batch/src/main/java/org/sonar/batch/rules/QProfilesReferential.java b/sonar-batch/src/main/java/org/sonar/batch/rules/QProfilesReferential.java new file mode 100644 index 00000000000..aec78dc14c8 --- /dev/null +++ b/sonar-batch/src/main/java/org/sonar/batch/rules/QProfilesReferential.java @@ -0,0 +1,39 @@ +/* + * 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.rules; + +import org.sonar.api.BatchComponent; +import org.sonar.batch.api.rules.QProfile; + +import javax.annotation.CheckForNull; + +/** + * Quality profiles referential + * @since 4.4 + */ +public interface QProfilesReferential extends BatchComponent { + + /** + * Get quality profile + */ + @CheckForNull + QProfile get(String language, String name); + +} diff --git a/sonar-batch/src/main/java/org/sonar/batch/rules/package-info.java b/sonar-batch/src/main/java/org/sonar/batch/rules/package-info.java new file mode 100644 index 00000000000..819e886c070 --- /dev/null +++ b/sonar-batch/src/main/java/org/sonar/batch/rules/package-info.java @@ -0,0 +1,21 @@ +/* + * 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. + */ +@javax.annotation.ParametersAreNonnullByDefault +package org.sonar.batch.rules;
\ No newline at end of file diff --git a/sonar-batch/src/main/java/org/sonar/batch/scan/AnalyzerContextAdaptor.java b/sonar-batch/src/main/java/org/sonar/batch/scan/AnalyzerContextAdaptor.java new file mode 100644 index 00000000000..a3f125d9c4f --- /dev/null +++ b/sonar-batch/src/main/java/org/sonar/batch/scan/AnalyzerContextAdaptor.java @@ -0,0 +1,169 @@ +/* + * 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.scan; + +import org.sonar.api.batch.SensorContext; +import org.sonar.api.batch.fs.InputFile; +import org.sonar.api.component.ResourcePerspectives; +import org.sonar.api.issue.Issuable; +import org.sonar.api.measures.Measure; +import org.sonar.api.measures.MetricFinder; +import org.sonar.api.resources.File; +import org.sonar.api.resources.Project; +import org.sonar.api.resources.Resource; +import org.sonar.api.rule.RuleKey; +import org.sonar.batch.api.analyzer.AnalyzerContext; +import org.sonar.batch.api.analyzer.issue.AnalyzerIssue; +import org.sonar.batch.api.analyzer.measure.AnalyzerMeasure; +import org.sonar.batch.api.measures.Metric; + +import java.io.Serializable; +import java.util.Collection; + +public class AnalyzerContextAdaptor implements AnalyzerContext { + + private SensorContext sensorContext; + private MetricFinder metricFinder; + private Project project; + private ResourcePerspectives perspectives; + + public AnalyzerContextAdaptor(SensorContext sensorContext, MetricFinder metricFinder, Project project, ResourcePerspectives perspectives) { + this.sensorContext = sensorContext; + this.metricFinder = metricFinder; + this.project = project; + this.perspectives = perspectives; + } + + @Override + public AnalyzerMeasure<?> getMeasure(String metricKey) { + Metric<?> m = metricFinder.findByKey(metricKey); + if (m == null) { + throw new IllegalStateException("Unknow metric with key: " + metricKey); + } + return getMeasure(m); + } + + @Override + public <G extends Serializable> AnalyzerMeasure<G> getMeasure(Metric<G> metric) { + org.sonar.api.measures.Metric<G> m = metricFinder.findByKey(metric.key()); + Measure<G> measure = sensorContext.getMeasure(m); + if (measure == null) { + return null; + } + return AnalyzerMeasure.<G>builder() + .onProject() + .forMetric(metric) + .withValue(measure.value()) + .build(); + } + + @Override + public AnalyzerMeasure<?> getMeasure(InputFile file, String metricKey) { + Metric<?> m = metricFinder.findByKey(metricKey); + if (m == null) { + throw new IllegalStateException("Unknow metric with key: " + metricKey); + } + return getMeasure(file, m); + } + + @Override + public <G extends Serializable> AnalyzerMeasure<G> getMeasure(InputFile file, Metric<G> metric) { + File fileRes = File.fromIOFile(file.file(), project); + org.sonar.api.measures.Metric<G> m = metricFinder.findByKey(metric.key()); + Measure<G> measure = sensorContext.getMeasure(fileRes, m); + if (measure == null) { + return null; + } + return AnalyzerMeasure.<G>builder() + .onFile(file) + .forMetric(metric) + .withValue(measure.value()) + .build(); + } + + @Override + public void addMeasure(AnalyzerMeasure<?> measure) { + org.sonar.api.measures.Metric<?> m = metricFinder.findByKey(measure.metricKey()); + + Measure measureToSave = new Measure(m); + switch (m.getType()) { + case BOOL: + measureToSave.setValue(Boolean.TRUE.equals(measure.value()) ? 1.0 : 0.0); + break; + case INT: + case MILLISEC: + measureToSave.setValue(Double.valueOf(((Integer) measure.value()))); + break; + case FLOAT: + case PERCENT: + case RATING: + measureToSave.setValue(((Double) measure.value())); + break; + case STRING: + case LEVEL: + case DATA: + case DISTRIB: + measureToSave.setData(((String) measure.value())); + break; + case WORK_DUR: + measureToSave.setValue(Double.valueOf(((Long) measure.value()))); + break; + default: + if (m.isNumericType()) { + measureToSave.setValue(((Double) measure.value())); + } else if (m.isDataType()) { + measureToSave.setData(((String) measure.value())); + } else { + throw new UnsupportedOperationException("Unsupported type :" + m.getType()); + } + } + if (measure.inputFile() != null) { + File fileRes = File.fromIOFile(measure.inputFile().file(), project); + sensorContext.saveMeasure(fileRes, measureToSave); + } else { + sensorContext.saveMeasure(measureToSave); + } + } + + @Override + public void addIssue(AnalyzerIssue issue) { + Resource r; + if (issue.inputFile() != null) { + r = File.fromIOFile(issue.inputFile().file(), project); + } else { + r = project; + } + Issuable issuable = perspectives.as(Issuable.class, r); + issuable.addIssue(issuable.newIssueBuilder() + .ruleKey(RuleKey.of(issue.ruleKey().repository(), issue.ruleKey().rule())) + .effortToFix(issue.effortToFix()) + .line(issue.line()) + .message(issue.message()) + .build()); + } + + @Override + public void addIssues(Collection<AnalyzerIssue> issues) { + for (AnalyzerIssue analyzerIssue : issues) { + addIssue(analyzerIssue); + } + } + +} diff --git a/sonar-batch/src/main/java/org/sonar/batch/scan/LanguageVerifier.java b/sonar-batch/src/main/java/org/sonar/batch/scan/LanguageVerifier.java index f64dd0d7219..7f32e03e98e 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/scan/LanguageVerifier.java +++ b/sonar-batch/src/main/java/org/sonar/batch/scan/LanguageVerifier.java @@ -25,9 +25,9 @@ import org.slf4j.LoggerFactory; import org.sonar.api.CoreProperties; import org.sonar.api.batch.fs.internal.DefaultFileSystem; import org.sonar.api.config.Settings; -import org.sonar.api.resources.Language; -import org.sonar.api.resources.Languages; import org.sonar.api.utils.MessageException; +import org.sonar.batch.api.languages.Language; +import org.sonar.batch.languages.LanguagesReferential; /** * Verifies that the property sonar.language is valid @@ -37,16 +37,15 @@ public class LanguageVerifier implements Startable { private static final Logger LOG = LoggerFactory.getLogger(LanguageVerifier.class); private final Settings settings; - private final Languages languages; + private final LanguagesReferential languages; private final DefaultFileSystem fs; - public LanguageVerifier(Settings settings, Languages languages, DefaultFileSystem fs) { + public LanguageVerifier(Settings settings, LanguagesReferential languages, DefaultFileSystem fs) { this.settings = settings; this.languages = languages; this.fs = fs; } - @Override public void start() { if (settings.hasKey(CoreProperties.PROJECT_LANGUAGE_PROPERTY)) { diff --git a/sonar-batch/src/main/java/org/sonar/batch/scan/ModuleScanContainer.java b/sonar-batch/src/main/java/org/sonar/batch/scan/ModuleScanContainer.java index 6e4ebd39b6a..6b1e0e1a501 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/scan/ModuleScanContainer.java +++ b/sonar-batch/src/main/java/org/sonar/batch/scan/ModuleScanContainer.java @@ -39,6 +39,9 @@ import org.sonar.batch.bootstrap.ExtensionInstaller; import org.sonar.batch.bootstrap.ExtensionMatcher; import org.sonar.batch.bootstrap.ExtensionUtils; import org.sonar.batch.components.TimeMachineConfiguration; +import org.sonar.batch.debt.DebtDecorator; +import org.sonar.batch.debt.IssueChangelogDebtCalculator; +import org.sonar.batch.debt.NewDebtDecorator; import org.sonar.batch.events.EventBus; import org.sonar.batch.index.DefaultIndex; import org.sonar.batch.index.ResourcePersister; @@ -139,6 +142,7 @@ public class ModuleScanContainer extends ComponentContainer { TimeMachineConfiguration.class, DefaultSensorContext.class, + AnalyzerContextAdaptor.class, BatchExtensionDictionnary.class, DefaultTimeMachine.class, ViolationFilters.class, @@ -178,6 +182,11 @@ public class ModuleScanContainer extends ComponentContainer { // language LanguageDistributionDecorator.class, + // Debt + IssueChangelogDebtCalculator.class, + DebtDecorator.class, + NewDebtDecorator.class, + ScanPerspectives.class); } @@ -191,7 +200,8 @@ public class ModuleScanContainer extends ComponentContainer { // Example : C# plugin adds sub-projects at runtime, even if they are not defined in root pom. return !ExtensionUtils.isMavenExtensionOnly(extension) || module.getPom() != null; } - return false; + return ExtensionUtils.isType(extension, org.sonar.batch.api.BatchComponent.class) + && ExtensionUtils.isInstantiationStrategy(extension, org.sonar.batch.api.InstantiationStrategy.PER_PROJECT); } }); } 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 b6dca14dafd..8d4dfa22df7 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 @@ -40,7 +40,6 @@ import org.sonar.batch.bootstrap.ExtensionMatcher; import org.sonar.batch.bootstrap.ExtensionUtils; import org.sonar.batch.bootstrap.MetricProvider; import org.sonar.batch.components.PeriodsDefinition; -import org.sonar.batch.debt.DebtModelProvider; import org.sonar.batch.debt.IssueChangelogDebtCalculator; import org.sonar.batch.index.Caches; import org.sonar.batch.index.ComponentDataCache; @@ -61,9 +60,9 @@ import org.sonar.batch.issue.DeprecatedViolations; import org.sonar.batch.issue.IssueCache; import org.sonar.batch.issue.IssuePersister; import org.sonar.batch.issue.ScanIssueStorage; +import org.sonar.batch.languages.DeprecatedLanguagesReferential; import org.sonar.batch.phases.GraphPersister; import org.sonar.batch.profiling.PhasesSumUpTimeProfiler; -import org.sonar.batch.rule.RulesProvider; import org.sonar.batch.scan.filesystem.InputFileCache; import org.sonar.batch.scan.maven.FakeMavenPluginExecutor; import org.sonar.batch.scan.maven.MavenPluginExecutor; @@ -101,27 +100,23 @@ public class ProjectScanContainer extends ComponentContainer { } private void projectBootstrap() { - // Old versions of bootstrappers used to pass project reactor as an extension - // so check if it is already present in parent container - ProjectReactor reactor = getComponentByType(ProjectReactor.class); + ProjectReactor reactor; + // OK, not present, so look for a custom ProjectBootstrapper + ProjectBootstrapper bootstrapper = getComponentByType(ProjectBootstrapper.class); + Settings settings = getComponentByType(Settings.class); + if (bootstrapper == null + // Starting from Maven plugin 2.3 then only DefaultProjectBootstrapper should be used. + || "true".equals(settings.getString("sonar.mojoUseRunner"))) { + // Use default SonarRunner project bootstrapper + ProjectReactorBuilder builder = getComponentByType(ProjectReactorBuilder.class); + reactor = builder.execute(); + } else { + reactor = bootstrapper.bootstrap(); + } if (reactor == null) { - // OK, not present, so look for a custom ProjectBootstrapper - ProjectBootstrapper bootstrapper = getComponentByType(ProjectBootstrapper.class); - Settings settings = getComponentByType(Settings.class); - if (bootstrapper == null - // Starting from Maven plugin 2.3 then only DefaultProjectBootstrapper should be used. - || "true".equals(settings.getString("sonar.mojoUseRunner"))) { - // Use default SonarRunner project bootstrapper - ProjectReactorBuilder builder = getComponentByType(ProjectReactorBuilder.class); - reactor = builder.execute(); - } else { - reactor = bootstrapper.bootstrap(); - } - if (reactor == null) { - throw new SonarException(bootstrapper + " has returned null as ProjectReactor"); - } - add(reactor); + throw new SonarException(bootstrapper + " has returned null as ProjectReactor"); } + add(reactor); } private void addBatchComponents() { @@ -175,15 +170,12 @@ public class ProjectScanContainer extends ComponentContainer { // lang Languages.class, + DeprecatedLanguagesReferential.class, HighlightableBuilder.class, SymbolizableBuilder.class, // technical debt DefaultTechnicalDebtModel.class, - new DebtModelProvider(), - - // rules - new RulesProvider(), // Differential periods PeriodsDefinition.class, diff --git a/sonar-batch/src/main/java/org/sonar/batch/scan/ScanTask.java b/sonar-batch/src/main/java/org/sonar/batch/scan/ScanTask.java index 1ccbb393522..33285ae2be6 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/scan/ScanTask.java +++ b/sonar-batch/src/main/java/org/sonar/batch/scan/ScanTask.java @@ -24,8 +24,10 @@ import org.sonar.api.platform.ComponentContainer; import org.sonar.api.task.Task; import org.sonar.api.task.TaskDefinition; import org.sonar.batch.DefaultProjectTree; +import org.sonar.batch.bootstrap.BootstrapProperties; import org.sonar.batch.bootstrap.TaskContainer; import org.sonar.batch.phases.Phases; +import org.sonar.batch.scan2.ProjectScanContainer; public class ScanTask implements Task { public static final TaskDefinition DEFINITION = TaskDefinition.builder() @@ -41,7 +43,12 @@ public class ScanTask implements Task { } public void execute() { - scan(new ProjectScanContainer(taskContainer)); + boolean sensorMode = CoreProperties.ANALYSIS_MODE_SENSOR.equals(taskContainer.getComponentByType(BootstrapProperties.class).property(CoreProperties.ANALYSIS_MODE)); + if (sensorMode) { + new ProjectScanContainer(taskContainer).execute(); + } else { + scan(new org.sonar.batch.scan.ProjectScanContainer(taskContainer)); + } } // Add components specific to project scan (views will use different ones) diff --git a/sonar-batch/src/main/java/org/sonar/batch/scan/SensorWrapper.java b/sonar-batch/src/main/java/org/sonar/batch/scan/SensorWrapper.java new file mode 100644 index 00000000000..ae9a25c59dc --- /dev/null +++ b/sonar-batch/src/main/java/org/sonar/batch/scan/SensorWrapper.java @@ -0,0 +1,78 @@ +/* + * 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.scan; + +import org.sonar.api.batch.DependedUpon; +import org.sonar.api.batch.DependsUpon; +import org.sonar.api.batch.Sensor; +import org.sonar.api.batch.SensorContext; +import org.sonar.api.batch.fs.FileSystem; +import org.sonar.api.resources.Project; +import org.sonar.batch.api.analyzer.Analyzer; +import org.sonar.batch.api.analyzer.AnalyzerContext; +import org.sonar.batch.api.measures.Metric; + +import java.util.Arrays; +import java.util.List; + +public class SensorWrapper implements Sensor { + + private Analyzer analyzer; + private AnalyzerContext adaptor; + private FileSystem fs; + + public SensorWrapper(Analyzer analyzer, AnalyzerContext adaptor, FileSystem fs) { + this.analyzer = analyzer; + this.adaptor = adaptor; + this.fs = fs; + } + + @DependedUpon + public List<Metric<?>> provides() { + return Arrays.asList(analyzer.describe().provides()); + } + + @DependsUpon + public List<Metric<?>> depends() { + return Arrays.asList(analyzer.describe().dependsOn()); + } + + @Override + public boolean shouldExecuteOnProject(Project project) { + if (!analyzer.describe().languages().isEmpty()) { + if (project.getLanguageKey() != null && !analyzer.describe().languages().contains(project.getLanguageKey())) { + return false; + } + boolean hasFile = false; + for (String languageKey : analyzer.describe().languages()) { + hasFile |= fs.hasFiles(fs.predicates().hasLanguage(languageKey)); + } + if (!hasFile) { + return false; + } + } + return true; + } + + @Override + public void analyse(Project module, SensorContext context) { + analyzer.analyse(adaptor); + } +} diff --git a/sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/AdditionalFilePredicates.java b/sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/AdditionalFilePredicates.java index af44e9b8783..6332d51a2ed 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/AdditionalFilePredicates.java +++ b/sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/AdditionalFilePredicates.java @@ -22,7 +22,7 @@ package org.sonar.batch.scan.filesystem; import org.apache.commons.io.FilenameUtils; import org.sonar.api.batch.fs.FilePredicate; import org.sonar.api.batch.fs.InputFile; -import org.sonar.api.batch.fs.internal.DefaultInputFile; +import org.sonar.api.batch.fs.internal.DeprecatedDefaultInputFile; /** * Additional {@link org.sonar.api.batch.fs.FilePredicate}s that are @@ -43,7 +43,7 @@ class AdditionalFilePredicates { @Override public boolean apply(InputFile f) { - return key.equals(((DefaultInputFile) f).key()); + return key.equals(((DeprecatedDefaultInputFile) f).key()); } } @@ -56,7 +56,7 @@ class AdditionalFilePredicates { @Override public boolean apply(InputFile f) { - return key.equals(((DefaultInputFile) f).deprecatedKey()); + return key.equals(((DeprecatedDefaultInputFile) f).deprecatedKey()); } } @@ -69,7 +69,7 @@ class AdditionalFilePredicates { @Override public boolean apply(InputFile f) { - return path.equals(((DefaultInputFile) f).pathRelativeToSourceDir()); + return path.equals(((DeprecatedDefaultInputFile) f).pathRelativeToSourceDir()); } } @@ -82,7 +82,7 @@ class AdditionalFilePredicates { @Override public boolean apply(InputFile f) { - return path.equals(((DefaultInputFile) f).sourceDirAbsolutePath()); + return path.equals(((DeprecatedDefaultInputFile) f).sourceDirAbsolutePath()); } } } diff --git a/sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/ComponentIndexer.java b/sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/ComponentIndexer.java index 4729d7f0361..f7f289ddf64 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/ComponentIndexer.java +++ b/sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/ComponentIndexer.java @@ -27,7 +27,7 @@ import org.sonar.api.CoreProperties; import org.sonar.api.batch.SonarIndex; import org.sonar.api.batch.fs.FileSystem; import org.sonar.api.batch.fs.InputFile; -import org.sonar.api.batch.fs.internal.DefaultInputFile; +import org.sonar.api.batch.fs.internal.DeprecatedDefaultInputFile; import org.sonar.api.config.Settings; import org.sonar.api.resources.File; import org.sonar.api.resources.Languages; @@ -65,7 +65,7 @@ public class ComponentIndexer implements BatchComponent { for (InputFile inputFile : fs.inputFiles(fs.predicates().all())) { String languageKey = inputFile.language(); boolean unitTest = InputFile.Type.TEST == inputFile.type(); - String pathFromSourceDir = ((DefaultInputFile) inputFile).pathRelativeToSourceDir(); + String pathFromSourceDir = ((DeprecatedDefaultInputFile) inputFile).pathRelativeToSourceDir(); if (pathFromSourceDir == null) { pathFromSourceDir = inputFile.relativePath(); } diff --git a/sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/DefaultModuleFileSystem.java b/sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/DefaultModuleFileSystem.java index ea5692fb4b3..7205c1c483f 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/DefaultModuleFileSystem.java +++ b/sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/DefaultModuleFileSystem.java @@ -27,13 +27,14 @@ import org.apache.commons.lang.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.sonar.api.CoreProperties; +import org.sonar.api.batch.bootstrap.ProjectDefinition; import org.sonar.api.batch.fs.FilePredicate; import org.sonar.api.batch.fs.internal.DefaultFileSystem; import org.sonar.api.config.Settings; import org.sonar.api.resources.Project; import org.sonar.api.scan.filesystem.FileQuery; import org.sonar.api.scan.filesystem.ModuleFileSystem; -import org.sonar.api.utils.SonarException; +import org.sonar.api.utils.MessageException; import javax.annotation.CheckForNull; import javax.annotation.Nullable; @@ -66,11 +67,24 @@ public class DefaultModuleFileSystem extends DefaultFileSystem implements Module private ComponentIndexer componentIndexer; private boolean initialized; - public DefaultModuleFileSystem(ModuleInputFileCache moduleInputFileCache, Project module, Settings settings, FileIndexer indexer, ModuleFileSystemInitializer initializer, + /** + * Used by scan2 + */ + public DefaultModuleFileSystem(ModuleInputFileCache moduleInputFileCache, ProjectDefinition def, Settings settings, FileIndexer indexer, ModuleFileSystemInitializer initializer) { + this(moduleInputFileCache, def.getKey(), settings, indexer, initializer, null); + } + + public DefaultModuleFileSystem(ModuleInputFileCache moduleInputFileCache, ProjectDefinition def, Project project, Settings settings, FileIndexer indexer, + ModuleFileSystemInitializer initializer, ComponentIndexer componentIndexer) { + this(moduleInputFileCache, project.getKey(), settings, indexer, initializer, componentIndexer); + } + + private DefaultModuleFileSystem(ModuleInputFileCache moduleInputFileCache, String moduleKey, Settings settings, FileIndexer indexer, ModuleFileSystemInitializer initializer, + @Nullable ComponentIndexer componentIndexer) { super(moduleInputFileCache); this.componentIndexer = componentIndexer; - this.moduleKey = module.getKey(); + this.moduleKey = moduleKey; this.settings = settings; this.indexer = indexer; if (initializer.baseDir() != null) { @@ -202,7 +216,7 @@ public class DefaultModuleFileSystem extends DefaultFileSystem implements Module public void resetDirs(File basedir, File buildDir, List<File> sourceDirs, List<File> testDirs, List<File> binaryDirs) { if (initialized) { - throw new SonarException("Module filesystem is already initialized. Modifications of filesystem are only allowed during Initializer phase."); + throw MessageException.of("Module filesystem is already initialized. Modifications of filesystem are only allowed during Initializer phase."); } setBaseDir(basedir); this.buildDir = buildDir; @@ -213,11 +227,13 @@ public class DefaultModuleFileSystem extends DefaultFileSystem implements Module public void index() { if (initialized) { - throw new SonarException("Module filesystem can only be indexed once"); + throw MessageException.of("Module filesystem can only be indexed once"); } initialized = true; indexer.index(this); - componentIndexer.execute(this); + if (componentIndexer != null) { + componentIndexer.execute(this); + } } private List<File> existingDirs(List<File> dirs) { diff --git a/sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/DeprecatedFileFilters.java b/sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/DeprecatedFileFilters.java index 8545dc4ef45..d52e19b0b0b 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/DeprecatedFileFilters.java +++ b/sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/DeprecatedFileFilters.java @@ -21,7 +21,7 @@ package org.sonar.batch.scan.filesystem; import org.sonar.api.batch.fs.InputFile; import org.sonar.api.batch.fs.InputFileFilter; -import org.sonar.api.batch.fs.internal.DefaultInputFile; +import org.sonar.api.batch.fs.internal.DeprecatedDefaultInputFile; import org.sonar.api.scan.filesystem.FileSystemFilter; import org.sonar.api.scan.filesystem.FileType; import org.sonar.api.scan.filesystem.ModuleFileSystem; @@ -72,12 +72,12 @@ public class DeprecatedFileFilters implements InputFileFilter { @Override public File relativeDir() { - return new File(((DefaultInputFile)inputFile).sourceDirAbsolutePath()); + return new File(((DeprecatedDefaultInputFile)inputFile).sourceDirAbsolutePath()); } @Override public String relativePath() { - return ((DefaultInputFile)inputFile).pathRelativeToSourceDir(); + return ((DeprecatedDefaultInputFile)inputFile).pathRelativeToSourceDir(); } @Override diff --git a/sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/FileIndexer.java b/sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/FileIndexer.java index b9d784723fa..b3142c9cc05 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/FileIndexer.java +++ b/sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/FileIndexer.java @@ -27,11 +27,12 @@ import org.apache.commons.io.filefilter.IOFileFilter; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.sonar.api.BatchComponent; +import org.sonar.api.batch.bootstrap.ProjectDefinition; import org.sonar.api.batch.fs.InputFile; import org.sonar.api.batch.fs.InputFileFilter; -import org.sonar.api.batch.fs.internal.DefaultInputFile; +import org.sonar.api.batch.fs.internal.DeprecatedDefaultInputFile; import org.sonar.api.resources.Project; -import org.sonar.api.utils.SonarException; +import org.sonar.api.utils.MessageException; import java.io.File; import java.util.Collection; @@ -44,31 +45,45 @@ import java.util.Set; */ public class FileIndexer implements BatchComponent { + private static final Logger LOG = LoggerFactory.getLogger(FileIndexer.class); + private static final IOFileFilter DIR_FILTER = FileFilterUtils.and(HiddenFileFilter.VISIBLE, FileFilterUtils.notFileFilter(FileFilterUtils.prefixFileFilter("."))); private static final IOFileFilter FILE_FILTER = HiddenFileFilter.VISIBLE; private final List<InputFileFilter> filters; private final InputFileCache fileCache; - private final Project module; + private final boolean isAggregator; private final ExclusionFilters exclusionFilters; private final InputFileBuilderFactory inputFileBuilderFactory; public FileIndexer(List<InputFileFilter> filters, ExclusionFilters exclusionFilters, InputFileBuilderFactory inputFileBuilderFactory, - InputFileCache cache, Project module) { + InputFileCache cache, Project module, ProjectDefinition def) { + this(filters, exclusionFilters, inputFileBuilderFactory, cache, !module.getModules().isEmpty()); + } + + /** + * Used by scan2 + */ + public FileIndexer(List<InputFileFilter> filters, ExclusionFilters exclusionFilters, InputFileBuilderFactory inputFileBuilderFactory, + InputFileCache cache, ProjectDefinition def) { + this(filters, exclusionFilters, inputFileBuilderFactory, cache, !def.getSubProjects().isEmpty()); + } + + private FileIndexer(List<InputFileFilter> filters, ExclusionFilters exclusionFilters, InputFileBuilderFactory inputFileBuilderFactory, + InputFileCache cache, boolean isAggregator) { this.filters = filters; this.exclusionFilters = exclusionFilters; this.inputFileBuilderFactory = inputFileBuilderFactory; this.fileCache = cache; - this.module = module; + this.isAggregator = isAggregator; } void index(DefaultModuleFileSystem fileSystem) { - Logger logger = LoggerFactory.getLogger(FileIndexer.class); - if (!module.getModules().isEmpty()) { + if (isAggregator) { // No indexing for an aggregator module return; } - logger.info("Index files"); + LOG.info("Index files"); exclusionFilters.prepare(); Progress progress = new Progress(fileCache.byModule(fileSystem.moduleKey())); @@ -93,13 +108,13 @@ public class FileIndexer implements BatchComponent { fileCache.remove(fileSystem.moduleKey(), removed); } - logger.info(String.format("%d files indexed", progress.count())); + LOG.info(String.format("%d files indexed", progress.count())); } private void indexFiles(InputFileBuilder inputFileBuilder, DefaultModuleFileSystem fileSystem, Progress progress, List<File> sourceFiles, InputFile.Type type) { for (File sourceFile : sourceFiles) { - DefaultInputFile inputFile = inputFileBuilder.create(sourceFile); + DeprecatedDefaultInputFile inputFile = inputFileBuilder.create(sourceFile); if (inputFile != null && exclusionFilters.accept(inputFile, type)) { indexFile(inputFileBuilder, fileSystem, progress, inputFile, type); } @@ -109,7 +124,7 @@ public class FileIndexer implements BatchComponent { private void indexDirectory(InputFileBuilder inputFileBuilder, DefaultModuleFileSystem fileSystem, Progress status, File dirToIndex, InputFile.Type type) { Collection<File> files = FileUtils.listFiles(dirToIndex, FILE_FILTER, DIR_FILTER); for (File file : files) { - DefaultInputFile inputFile = inputFileBuilder.create(file); + DeprecatedDefaultInputFile inputFile = inputFileBuilder.create(file); if (inputFile != null && exclusionFilters.accept(inputFile, type)) { indexFile(inputFileBuilder, fileSystem, status, inputFile, type); } @@ -117,7 +132,7 @@ public class FileIndexer implements BatchComponent { } private void indexFile(InputFileBuilder inputFileBuilder, DefaultModuleFileSystem fs, - Progress status, DefaultInputFile inputFile, InputFile.Type type) { + Progress status, DeprecatedDefaultInputFile inputFile, InputFile.Type type) { InputFile completedFile = inputFileBuilder.complete(inputFile, type); if (completedFile != null && accept(completedFile)) { fs.add(completedFile); @@ -146,7 +161,7 @@ public class FileIndexer implements BatchComponent { void markAsIndexed(InputFile inputFile) { if (indexed.contains(inputFile)) { - throw new SonarException("File " + inputFile + " can't be indexed twice. Please check that inclusion/exclusion patterns produce " + throw MessageException.of("File " + inputFile + " can't be indexed twice. Please check that inclusion/exclusion patterns produce " + "disjoint sets for main and test files"); } removed.remove(inputFile); diff --git a/sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/InputFileBuilder.java b/sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/InputFileBuilder.java index 10b888344b9..cd7844fd4de 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/InputFileBuilder.java +++ b/sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/InputFileBuilder.java @@ -24,7 +24,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.sonar.api.batch.fs.FileSystem; import org.sonar.api.batch.fs.InputFile; -import org.sonar.api.batch.fs.internal.DefaultInputFile; +import org.sonar.api.batch.fs.internal.DeprecatedDefaultInputFile; import org.sonar.api.scan.filesystem.PathResolver; import org.sonar.batch.bootstrap.AnalysisMode; import org.sonar.batch.util.DeprecatedKeyUtils; @@ -76,13 +76,13 @@ class InputFileBuilder { } @CheckForNull - DefaultInputFile create(File file) { + DeprecatedDefaultInputFile create(File file) { String relativePath = pathResolver.relativePath(fs.baseDir(), file); if (relativePath == null) { LOG.warn("File '{}' is ignored. It is not located in module basedir '{}'.", file.getAbsolutePath(), fs.baseDir()); return null; } - DefaultInputFile inputFile = new DefaultInputFile(relativePath); + DeprecatedDefaultInputFile inputFile = new DeprecatedDefaultInputFile(relativePath); inputFile.setBasedir(fs.baseDir()); inputFile.setFile(file); return inputFile; @@ -92,7 +92,7 @@ class InputFileBuilder { * Optimization to not set all InputFile data if the file is excluded from analysis. */ @CheckForNull - DefaultInputFile complete(DefaultInputFile inputFile, InputFile.Type type) { + DeprecatedDefaultInputFile complete(DeprecatedDefaultInputFile inputFile, InputFile.Type type) { inputFile.setType(type); inputFile.setKey(new StringBuilder().append(moduleKey).append(":").append(inputFile.relativePath()).toString()); inputFile.setBasedir(fs.baseDir()); @@ -112,7 +112,7 @@ class InputFileBuilder { return inputFile; } - private void fillDeprecatedData(DefaultInputFile inputFile) { + private void fillDeprecatedData(DeprecatedDefaultInputFile inputFile) { List<File> sourceDirs = InputFile.Type.MAIN == inputFile.type() ? fs.sourceDirs() : fs.testDirs(); for (File sourceDir : sourceDirs) { String sourceRelativePath = pathResolver.relativePath(sourceDir, inputFile.file()); diff --git a/sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/InputFileBuilderFactory.java b/sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/InputFileBuilderFactory.java index 90dcfc870d7..772f773cb5f 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/InputFileBuilderFactory.java +++ b/sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/InputFileBuilderFactory.java @@ -20,6 +20,7 @@ package org.sonar.batch.scan.filesystem; import org.sonar.api.BatchComponent; +import org.sonar.api.batch.bootstrap.ProjectDefinition; import org.sonar.api.resources.Project; import org.sonar.api.scan.filesystem.PathResolver; import org.sonar.batch.bootstrap.AnalysisMode; @@ -32,9 +33,22 @@ public class InputFileBuilderFactory implements BatchComponent { private final StatusDetectionFactory statusDetectionFactory; private final AnalysisMode analysisMode; - public InputFileBuilderFactory(Project moduleDef, PathResolver pathResolver, LanguageDetectionFactory langDetectionFactory, - StatusDetectionFactory statusDetectionFactory, AnalysisMode analysisMode) { - this.moduleKey = moduleDef.getEffectiveKey(); + public InputFileBuilderFactory(Project module, ProjectDefinition def, PathResolver pathResolver, LanguageDetectionFactory langDetectionFactory, + StatusDetectionFactory statusDetectionFactory, AnalysisMode analysisMode) { + this(module.getEffectiveKey(), pathResolver, langDetectionFactory, statusDetectionFactory, analysisMode); + } + + /** + * Used by scan2 + */ + public InputFileBuilderFactory(ProjectDefinition def, PathResolver pathResolver, LanguageDetectionFactory langDetectionFactory, + StatusDetectionFactory statusDetectionFactory, AnalysisMode analysisMode) { + this(def.getKey(), pathResolver, langDetectionFactory, statusDetectionFactory, analysisMode); + } + + private InputFileBuilderFactory(String effectiveKey, PathResolver pathResolver, LanguageDetectionFactory langDetectionFactory, + StatusDetectionFactory statusDetectionFactory, AnalysisMode analysisMode) { + this.moduleKey = effectiveKey; this.pathResolver = pathResolver; this.langDetectionFactory = langDetectionFactory; this.statusDetectionFactory = statusDetectionFactory; diff --git a/sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/InputFileCache.java b/sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/InputFileCache.java index 3035f115c5c..3b12d8ba5f6 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/InputFileCache.java +++ b/sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/InputFileCache.java @@ -21,8 +21,6 @@ package org.sonar.batch.scan.filesystem; import org.sonar.api.BatchComponent; import org.sonar.api.batch.fs.InputFile; -import org.sonar.api.batch.fs.internal.FileIndex; -import org.sonar.api.batch.fs.internal.RelativePathIndex; import org.sonar.batch.index.Cache; import org.sonar.batch.index.Caches; @@ -69,23 +67,8 @@ public class InputFileCache implements BatchComponent { return this; } - - public void index(String moduleKey, String indexId, Object indexValue, InputFile inputFile) { - // already indexed by relative path is already used - if (!indexId.equals(RelativePathIndex.ID)) { - // See limitation of org.sonar.batch.index.Cache -> fail - // to traverse a sub-tree, for example in order to - // have the following structure in InputFileCache : - // [index id|module key|index value] - throw new UnsupportedOperationException("Only relative path index is supported yet"); - } - } - @CheckForNull - public InputFile get(String moduleKey, String indexId, Object indexValue) { - if (!indexId.equals(RelativePathIndex.ID)) { - throw new UnsupportedOperationException("Only relative path index is supported yet"); - } - return cache.get(moduleKey, indexValue); + public InputFile get(String moduleKey, String relativePath) { + return cache.get(moduleKey, relativePath); } } diff --git a/sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/LanguageDetection.java b/sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/LanguageDetection.java index da4900f2145..7256c5592a3 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/LanguageDetection.java +++ b/sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/LanguageDetection.java @@ -29,14 +29,13 @@ import org.sonar.api.CoreProperties; import org.sonar.api.batch.fs.InputFile; import org.sonar.api.batch.fs.internal.PathPattern; import org.sonar.api.config.Settings; -import org.sonar.api.resources.Language; -import org.sonar.api.resources.Languages; import org.sonar.api.utils.MessageException; +import org.sonar.batch.api.languages.Language; +import org.sonar.batch.languages.LanguagesReferential; import javax.annotation.CheckForNull; import java.text.MessageFormat; -import java.util.Arrays; import java.util.List; import java.util.Map; @@ -54,23 +53,23 @@ class LanguageDetection { private final List<String> languagesToConsider = Lists.newArrayList(); private final String forcedLanguage; - LanguageDetection(Settings settings, Languages languages) { + LanguageDetection(Settings settings, LanguagesReferential languages) { for (Language language : languages.all()) { - String[] filePatterns = settings.getStringArray(getFileLangPatternPropKey(language.getKey())); + String[] filePatterns = settings.getStringArray(getFileLangPatternPropKey(language.key())); PathPattern[] pathPatterns = PathPattern.create(filePatterns); if (pathPatterns.length > 0) { - patternsByLanguage.put(language.getKey(), pathPatterns); + patternsByLanguage.put(language.key(), pathPatterns); } else { // If no custom language pattern is defined then fallback to suffixes declared by language - String[] patterns = Arrays.copyOf(language.getFileSuffixes(), language.getFileSuffixes().length); + String[] patterns = language.fileSuffixes().toArray(new String[language.fileSuffixes().size()]); for (int i = 0; i < patterns.length; i++) { String suffix = patterns[i]; String extension = sanitizeExtension(suffix); patterns[i] = "**/*." + extension; } PathPattern[] defaultLanguagePatterns = PathPattern.create(patterns); - patternsByLanguage.put(language.getKey(), defaultLanguagePatterns); - LOG.debug("Declared extensions of language " + language + " were converted to " + getDetails(language.getKey())); + patternsByLanguage.put(language.key(), defaultLanguagePatterns); + LOG.debug("Declared extensions of language " + language + " were converted to " + getDetails(language.key())); } } diff --git a/sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/LanguageDetectionFactory.java b/sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/LanguageDetectionFactory.java index 814b6e380ed..6ff579fd769 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/LanguageDetectionFactory.java +++ b/sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/LanguageDetectionFactory.java @@ -21,13 +21,13 @@ package org.sonar.batch.scan.filesystem; import org.sonar.api.BatchComponent; import org.sonar.api.config.Settings; -import org.sonar.api.resources.Languages; +import org.sonar.batch.languages.LanguagesReferential; public class LanguageDetectionFactory implements BatchComponent { private final Settings settings; - private final Languages languages; + private final LanguagesReferential languages; - public LanguageDetectionFactory(Settings settings, Languages languages) { + public LanguageDetectionFactory(Settings settings, LanguagesReferential languages) { this.settings = settings; this.languages = languages; } diff --git a/sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/ModuleInputFileCache.java b/sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/ModuleInputFileCache.java index 4a909f29cf4..d555074db79 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/ModuleInputFileCache.java +++ b/sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/ModuleInputFileCache.java @@ -20,9 +20,10 @@ package org.sonar.batch.scan.filesystem; import org.sonar.api.BatchComponent; +import org.sonar.api.batch.bootstrap.ProjectDefinition; import org.sonar.api.batch.fs.InputFile; -import org.sonar.api.batch.fs.internal.UniqueIndexPredicate; import org.sonar.api.batch.fs.internal.DefaultFileSystem; +import org.sonar.api.batch.fs.internal.RelativePathPredicate; import org.sonar.api.resources.Project; public class ModuleInputFileCache extends DefaultFileSystem.Cache implements BatchComponent { @@ -30,28 +31,31 @@ public class ModuleInputFileCache extends DefaultFileSystem.Cache implements Bat private final String moduleKey; private final InputFileCache projectCache; - public ModuleInputFileCache(Project module, InputFileCache projectCache) { + public ModuleInputFileCache(Project module, ProjectDefinition projectDef, InputFileCache projectCache) { this.moduleKey = module.getKey(); this.projectCache = projectCache; } + /** + * Used by scan2 + */ + public ModuleInputFileCache(ProjectDefinition projectDef, InputFileCache projectCache) { + this.moduleKey = projectDef.getKey(); + this.projectCache = projectCache; + } + @Override protected Iterable<InputFile> inputFiles() { return projectCache.byModule(moduleKey); } @Override - protected InputFile inputFile(UniqueIndexPredicate predicate) { - return projectCache.get(moduleKey, predicate.indexId(), predicate.value()); + protected InputFile inputFile(RelativePathPredicate predicate) { + return projectCache.get(moduleKey, predicate.path()); } @Override protected void doAdd(InputFile inputFile) { projectCache.put(moduleKey, inputFile); } - - @Override - protected void doIndex(String indexId, Object value, InputFile inputFile) { - projectCache.index(moduleKey, indexId, value, inputFile); - } } diff --git a/sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/StatusDetectionFactory.java b/sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/StatusDetectionFactory.java index dd1e4a247c2..53729dde592 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/StatusDetectionFactory.java +++ b/sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/StatusDetectionFactory.java @@ -21,6 +21,8 @@ package org.sonar.batch.scan.filesystem; import org.sonar.api.BatchComponent; +import java.util.Collections; + public class StatusDetectionFactory implements BatchComponent { private final PreviousFileHashLoader previousFileHashLoader; @@ -29,7 +31,14 @@ public class StatusDetectionFactory implements BatchComponent { this.previousFileHashLoader = l; } + /** + * Used by scan2 + */ + public StatusDetectionFactory() { + this.previousFileHashLoader = null; + } + StatusDetection create() { - return new StatusDetection(previousFileHashLoader.hashByRelativePath()); + return new StatusDetection(previousFileHashLoader != null ? previousFileHashLoader.hashByRelativePath() : Collections.<String, String>emptyMap()); } } diff --git a/sonar-batch/src/main/java/org/sonar/batch/scan/measure/MeasureCache.java b/sonar-batch/src/main/java/org/sonar/batch/scan/measure/MeasureCache.java index a2056966ef2..e3732308cf5 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/scan/measure/MeasureCache.java +++ b/sonar-batch/src/main/java/org/sonar/batch/scan/measure/MeasureCache.java @@ -34,11 +34,9 @@ import org.sonar.batch.index.Caches; public class MeasureCache implements BatchComponent { private final Cache<Measure> cache; - private final Cache<String> dataCache; public MeasureCache(Caches caches) { cache = caches.createCache("measures"); - dataCache = caches.createCache("measuresData"); } public Iterable<Entry<Measure>> entries() { @@ -50,7 +48,11 @@ public class MeasureCache implements BatchComponent { } public Iterable<Measure> byMetric(Resource r, String metricKey) { - return cache.values(r.getEffectiveKey(), metricKey); + return byMetric(r.getEffectiveKey(), metricKey); + } + + public Iterable<Measure> byMetric(String resourceKey, String metricKey) { + return cache.values(resourceKey, metricKey); } public MeasureCache put(Resource resource, Measure measure) { @@ -85,4 +87,5 @@ public class MeasureCache implements BatchComponent { } return sb.toString(); } + } diff --git a/sonar-batch/src/main/java/org/sonar/batch/scan/report/JsonReport.java b/sonar-batch/src/main/java/org/sonar/batch/scan/report/JsonReport.java index 20731c631a3..fe8ddc7826d 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/scan/report/JsonReport.java +++ b/sonar-batch/src/main/java/org/sonar/batch/scan/report/JsonReport.java @@ -27,7 +27,7 @@ import org.slf4j.LoggerFactory; import org.sonar.api.BatchComponent; import org.sonar.api.batch.fs.FileSystem; import org.sonar.api.batch.fs.InputFile; -import org.sonar.api.batch.fs.internal.DefaultInputFile; +import org.sonar.api.batch.fs.internal.DeprecatedDefaultInputFile; import org.sonar.api.config.Settings; import org.sonar.api.issue.internal.DefaultIssue; import org.sonar.api.platform.Server; @@ -169,7 +169,7 @@ public class JsonReport implements BatchComponent { writeJsonModuleComponents(json, rootModule); // TODO we need to dump directories for (InputFile inputFile : fileCache.all()) { - String key = ((DefaultInputFile) inputFile).key(); + String key = ((DeprecatedDefaultInputFile) inputFile).key(); json .beginObject() .prop("key", key) diff --git a/sonar-batch/src/main/java/org/sonar/batch/scan2/AnalyzersExecutor.java b/sonar-batch/src/main/java/org/sonar/batch/scan2/AnalyzersExecutor.java new file mode 100644 index 00000000000..c0ae6dbbaab --- /dev/null +++ b/sonar-batch/src/main/java/org/sonar/batch/scan2/AnalyzersExecutor.java @@ -0,0 +1,60 @@ +/* + * 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.scan2; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.sonar.api.BatchComponent; +import org.sonar.api.platform.ComponentContainer; +import org.sonar.batch.api.analyzer.Analyzer; +import org.sonar.batch.api.analyzer.AnalyzerContext; +import org.sonar.batch.api.analyzer.AnalyzerDescriptor; + +import java.util.Collection; + +public class AnalyzersExecutor implements BatchComponent { + + private static final Logger LOG = LoggerFactory.getLogger(AnalyzersExecutor.class); + + private ComponentContainer container; + + public AnalyzersExecutor(ComponentContainer container) { + this.container = container; + } + + public void execute(AnalyzerContext context) { + Collection<Analyzer> analyzers = container.getComponentsByType(Analyzer.class); + + for (Analyzer analyzer : analyzers) { + + AnalyzerDescriptor descriptor = analyzer.describe(); + + LOG.info("Execute analyzer: " + descriptor.name()); + + executeSensor(context, analyzer); + } + + } + + private void executeSensor(AnalyzerContext context, Analyzer analyzer) { + analyzer.analyse(context); + } + +} diff --git a/sonar-batch/src/main/java/org/sonar/batch/scan2/DefaultAnalyzerContext.java b/sonar-batch/src/main/java/org/sonar/batch/scan2/DefaultAnalyzerContext.java new file mode 100644 index 00000000000..253bf0cab41 --- /dev/null +++ b/sonar-batch/src/main/java/org/sonar/batch/scan2/DefaultAnalyzerContext.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.scan2; + +import org.sonar.api.batch.bootstrap.ProjectDefinition; +import org.sonar.api.batch.fs.InputFile; +import org.sonar.api.issue.internal.DefaultIssue; +import org.sonar.api.rule.RuleKey; +import org.sonar.batch.api.analyzer.AnalyzerContext; +import org.sonar.batch.api.analyzer.issue.AnalyzerIssue; +import org.sonar.batch.api.analyzer.measure.AnalyzerMeasure; +import org.sonar.batch.api.measures.Metric; +import org.sonar.batch.issue.ModuleIssues; +import org.sonar.core.component.ComponentKeys; +import org.sonar.core.issue.DefaultIssueBuilder; + +import java.io.Serializable; +import java.util.Collection; + +public class DefaultAnalyzerContext implements AnalyzerContext { + + private final MeasureCache measureCache; + private ProjectDefinition def; + private ModuleIssues moduleIssues; + + public DefaultAnalyzerContext(ProjectDefinition def, MeasureCache measureCache, ModuleIssues moduleIssues) { + this.def = def; + this.measureCache = measureCache; + this.moduleIssues = moduleIssues; + } + + @Override + public AnalyzerMeasure getMeasure(String metricKey) { + return measureCache.byMetric(def.getKey(), metricKey); + } + + @Override + public <G extends Serializable> AnalyzerMeasure<G> getMeasure(Metric<G> metric) { + return (AnalyzerMeasure<G>) measureCache.byMetric(def.getKey(), metric.key()); + } + + @Override + public AnalyzerMeasure getMeasure(InputFile file, String metricKey) { + return measureCache.byMetric(ComponentKeys.createEffectiveKey(def.getKey(), file), metricKey); + } + + @Override + public <G extends Serializable> AnalyzerMeasure<G> getMeasure(InputFile file, Metric<G> metric) { + return (AnalyzerMeasure<G>) measureCache.byMetric(ComponentKeys.createEffectiveKey(def.getKey(), file), metric.key()); + } + + @Override + public void addMeasure(org.sonar.batch.api.analyzer.measure.AnalyzerMeasure<?> measure) { + if (measure.inputFile() != null) { + measureCache.put(ComponentKeys.createEffectiveKey(def.getKey(), measure.inputFile()), measure); + } else { + measureCache.put(def.getKey(), measure); + } + } + + @Override + public void addIssue(AnalyzerIssue issue) { + DefaultIssueBuilder builder = new DefaultIssueBuilder() + .projectKey(def.getKey()); + if (issue.inputFile() != null) { + builder.componentKey(ComponentKeys.createEffectiveKey(def.getKey(), issue.inputFile())); + } else { + builder.componentKey(def.getKey()); + } + + moduleIssues.initAndAddIssue((DefaultIssue) builder + .ruleKey(RuleKey.of(issue.ruleKey().repository(), issue.ruleKey().rule())) + .message(issue.message()) + .line(issue.line()) + .effortToFix(issue.effortToFix()) + .build()); + } + + @Override + public void addIssues(Collection<AnalyzerIssue> issues) { + for (AnalyzerIssue analyzerIssue : issues) { + addIssue(analyzerIssue); + } + + } + +} diff --git a/sonar-batch/src/main/java/org/sonar/batch/scan2/MeasureCache.java b/sonar-batch/src/main/java/org/sonar/batch/scan2/MeasureCache.java new file mode 100644 index 00000000000..96ab8adc9de --- /dev/null +++ b/sonar-batch/src/main/java/org/sonar/batch/scan2/MeasureCache.java @@ -0,0 +1,65 @@ +/* + * 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.scan2; + +import com.google.common.base.Preconditions; +import org.sonar.api.BatchComponent; +import org.sonar.batch.api.analyzer.measure.AnalyzerMeasure; +import org.sonar.batch.index.Cache; +import org.sonar.batch.index.Cache.Entry; +import org.sonar.batch.index.Caches; + +/** + * Cache of all measures. This cache is shared amongst all project modules. + */ +public class MeasureCache implements BatchComponent { + + private final Cache<AnalyzerMeasure<?>> cache; + + public MeasureCache(Caches caches) { + cache = caches.createCache("measures"); + } + + public Iterable<Entry<AnalyzerMeasure<?>>> entries() { + return cache.entries(); + } + + public AnalyzerMeasure<?> byMetric(String resourceKey, String metricKey) { + return cache.get(resourceKey, metricKey); + } + + public MeasureCache put(String resourceKey, AnalyzerMeasure<?> measure) { + Preconditions.checkNotNull(resourceKey); + Preconditions.checkNotNull(measure.metricKey()); + cache.put(resourceKey, measure.metricKey(), measure); + return this; + } + + public boolean contains(String resourceKey, AnalyzerMeasure<?> measure) { + Preconditions.checkNotNull(resourceKey); + Preconditions.checkNotNull(measure); + return cache.containsKey(resourceKey, measure.metricKey()); + } + + public Iterable<AnalyzerMeasure<?>> all() { + return cache.values(); + } + +} diff --git a/sonar-batch/src/main/java/org/sonar/batch/scan2/ModuleScanContainer.java b/sonar-batch/src/main/java/org/sonar/batch/scan2/ModuleScanContainer.java index 44bcadc309c..24435c0dc55 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/scan2/ModuleScanContainer.java +++ b/sonar-batch/src/main/java/org/sonar/batch/scan2/ModuleScanContainer.java @@ -21,27 +21,17 @@ package org.sonar.batch.scan2; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.sonar.api.BatchComponent; -import org.sonar.api.batch.InstantiationStrategy; import org.sonar.api.batch.bootstrap.ProjectDefinition; import org.sonar.api.batch.rule.CheckFactory; import org.sonar.api.platform.ComponentContainer; -import org.sonar.api.resources.Project; import org.sonar.api.scan.filesystem.FileExclusions; -import org.sonar.batch.DefaultProjectClasspath; -import org.sonar.batch.DefaultSensorContext; -import org.sonar.batch.DefaultTimeMachine; -import org.sonar.batch.ProjectTree; -import org.sonar.batch.ResourceFilters; -import org.sonar.batch.ViolationFilters; +import org.sonar.batch.api.BatchComponent; +import org.sonar.batch.api.InstantiationStrategy; import org.sonar.batch.bootstrap.BatchExtensionDictionnary; import org.sonar.batch.bootstrap.ExtensionInstaller; import org.sonar.batch.bootstrap.ExtensionMatcher; import org.sonar.batch.bootstrap.ExtensionUtils; -import org.sonar.batch.components.TimeMachineConfiguration; import org.sonar.batch.events.EventBus; -import org.sonar.batch.index.DefaultIndex; -import org.sonar.batch.index.ResourcePersister; import org.sonar.batch.issue.IssuableFactory; import org.sonar.batch.issue.IssueFilters; import org.sonar.batch.issue.ModuleIssues; @@ -51,23 +41,10 @@ import org.sonar.batch.issue.ignore.pattern.IssueExclusionPatternInitializer; import org.sonar.batch.issue.ignore.pattern.IssueInclusionPatternInitializer; import org.sonar.batch.issue.ignore.scanner.IssueExclusionsLoader; import org.sonar.batch.issue.ignore.scanner.IssueExclusionsRegexpScanner; -import org.sonar.batch.language.LanguageDistributionDecorator; -import org.sonar.batch.phases.Phase2Executor; -import org.sonar.batch.phases.PhaseExecutor; -import org.sonar.batch.phases.PhasesTimeProfiler; -import org.sonar.batch.qualitygate.GenerateQualityGateEvents; -import org.sonar.batch.qualitygate.QualityGateProvider; -import org.sonar.batch.qualitygate.QualityGateVerifier; -import org.sonar.batch.rule.ActiveRulesProvider; import org.sonar.batch.rule.ModuleQProfiles; -import org.sonar.batch.rule.QProfileDecorator; -import org.sonar.batch.rule.QProfileEventsDecorator; -import org.sonar.batch.rule.QProfileSensor; import org.sonar.batch.rule.QProfileVerifier; -import org.sonar.batch.rule.RulesProfileProvider; import org.sonar.batch.scan.LanguageVerifier; import org.sonar.batch.scan.ModuleSettings; -import org.sonar.batch.scan.filesystem.ComponentIndexer; import org.sonar.batch.scan.filesystem.DefaultModuleFileSystem; import org.sonar.batch.scan.filesystem.DeprecatedFileFilters; import org.sonar.batch.scan.filesystem.ExclusionFilters; @@ -77,46 +54,34 @@ import org.sonar.batch.scan.filesystem.InputFileBuilderFactory; import org.sonar.batch.scan.filesystem.LanguageDetectionFactory; import org.sonar.batch.scan.filesystem.ModuleFileSystemInitializer; import org.sonar.batch.scan.filesystem.ModuleInputFileCache; -import org.sonar.batch.scan.filesystem.PreviousFileHashLoader; -import org.sonar.batch.scan.filesystem.ProjectFileSystemAdapter; import org.sonar.batch.scan.filesystem.StatusDetectionFactory; -import org.sonar.batch.scan.report.JsonReport; -import org.sonar.core.component.ScanPerspectives; -import org.sonar.core.measure.MeasurementFilters; public class ModuleScanContainer extends ComponentContainer { private static final Logger LOG = LoggerFactory.getLogger(ModuleScanContainer.class); - private final Project module; + private final ProjectDefinition moduleDefinition; - public ModuleScanContainer(ProjectScanContainer parent, Project module) { + public ModuleScanContainer(ProjectScanContainer parent, ProjectDefinition moduleDefinition) { super(parent); - this.module = module; + this.moduleDefinition = moduleDefinition; } @Override protected void doBeforeStart() { - LOG.info("------------- Scan {}", module.getName()); + LOG.info("------------- Scan {}", moduleDefinition.getName()); addCoreComponents(); addExtensions(); } private void addCoreComponents() { - ProjectDefinition moduleDefinition = getComponentByType(ProjectTree.class).getProjectDefinition(module); add( moduleDefinition, - module.getConfiguration(), - module, - ModuleSettings.class); + ModuleSettings.class, - // hack to initialize commons-configuration before ExtensionProviders - getComponentByType(ModuleSettings.class); - - add( EventBus.class, Phase2Executor.class, - PhasesTimeProfiler.class, Phase2Executor.getPhaseClasses(), moduleDefinition.getContainerExtensions(), + AnalyzersExecutor.class, // file system ModuleInputFileCache.class, @@ -126,46 +91,21 @@ public class ModuleScanContainer extends ComponentContainer { InputFileBuilderFactory.class, StatusDetectionFactory.class, LanguageDetectionFactory.class, - PreviousFileHashLoader.class, FileIndexer.class, - ComponentIndexer.class, LanguageVerifier.class, FileSystemLogger.class, - DefaultProjectClasspath.class, DefaultModuleFileSystem.class, ModuleFileSystemInitializer.class, - ProjectFileSystemAdapter.class, QProfileVerifier.class, - // the Snapshot component will be removed when asynchronous measures are improved (required for AsynchronousMeasureSensor) - getComponentByType(ResourcePersister.class).getSnapshot(module), - - TimeMachineConfiguration.class, - DefaultSensorContext.class, + DefaultAnalyzerContext.class, BatchExtensionDictionnary.class, - DefaultTimeMachine.class, - ViolationFilters.class, IssueFilters.class, - MeasurementFilters.class, - ResourceFilters.class, - - // quality gates - new QualityGateProvider(), - QualityGateVerifier.class, - GenerateQualityGateEvents.class, // rules ModuleQProfiles.class, - new ActiveRulesProvider(), - new RulesProfileProvider(), - QProfileSensor.class, - QProfileDecorator.class, - QProfileEventsDecorator.class, CheckFactory.class, - // report - JsonReport.class, - // issues IssuableFactory.class, ModuleIssues.class, @@ -176,36 +116,22 @@ public class ModuleScanContainer extends ComponentContainer { IssueExclusionsRegexpScanner.class, IssueExclusionsLoader.class, EnforceIssuesFilter.class, - IgnoreIssuesFilter.class, - - // language - LanguageDistributionDecorator.class, - - ScanPerspectives.class); + IgnoreIssuesFilter.class); } private void addExtensions() { ExtensionInstaller installer = getComponentByType(ExtensionInstaller.class); installer.install(this, new ExtensionMatcher() { public boolean accept(Object extension) { - if (ExtensionUtils.isType(extension, BatchComponent.class) && ExtensionUtils.isInstantiationStrategy(extension, InstantiationStrategy.PER_PROJECT)) { - // Special use-case: the extension point ProjectBuilder is used in a Maven environment to define some - // new sub-projects without pom. - // Example : C# plugin adds sub-projects at runtime, even if they are not defined in root pom. - return !ExtensionUtils.isMavenExtensionOnly(extension) || module.getPom() != null; - } - return false; + return ExtensionUtils.isType(extension, BatchComponent.class) + && ExtensionUtils.isInstantiationStrategy(extension, InstantiationStrategy.PER_PROJECT); } }); } @Override protected void doAfterStart() { - DefaultIndex index = getComponentByType(DefaultIndex.class); - index.setCurrentProject(module, - getComponentByType(ModuleIssues.class)); - - getComponentByType(PhaseExecutor.class).execute(module); + getComponentByType(Phase2Executor.class).execute(moduleDefinition); } } diff --git a/sonar-batch/src/main/java/org/sonar/batch/phases/Phase2Executor.java b/sonar-batch/src/main/java/org/sonar/batch/scan2/Phase2Executor.java index 87d1348ae3b..fcbec9bf4b4 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/phases/Phase2Executor.java +++ b/sonar-batch/src/main/java/org/sonar/batch/scan2/Phase2Executor.java @@ -17,19 +17,18 @@ * 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.phases; +package org.sonar.batch.scan2; import com.google.common.collect.Lists; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.sonar.api.batch.SensorContext; -import org.sonar.api.resources.Project; -import org.sonar.batch.events.EventBus; +import org.sonar.api.batch.bootstrap.ProjectDefinition; +import org.sonar.batch.api.analyzer.AnalyzerContext; import org.sonar.batch.issue.ignore.scanner.IssueExclusionsLoader; +import org.sonar.batch.phases.SensorsExecutor; import org.sonar.batch.rule.QProfileVerifier; import org.sonar.batch.scan.filesystem.DefaultModuleFileSystem; import org.sonar.batch.scan.filesystem.FileSystemLogger; -import org.sonar.batch.scan.maven.MavenPluginsConfigurator; import java.util.Collection; @@ -37,32 +36,19 @@ public final class Phase2Executor { public static final Logger LOGGER = LoggerFactory.getLogger(Phase2Executor.class); - private final EventBus eventBus; - private final Phases phases; - private final MavenPluginsConfigurator mavenPluginsConfigurator; - private final InitializersExecutor initializersExecutor; - private final SensorsExecutor sensorsExecutor; - private final SensorContext sensorContext; - private final ProjectInitializer pi; + private final AnalyzersExecutor analyzersExecutor; + private final AnalyzerContext analyzerContext; private final FileSystemLogger fsLogger; private final DefaultModuleFileSystem fs; private final QProfileVerifier profileVerifier; private final IssueExclusionsLoader issueExclusionsLoader; - public Phase2Executor(Phases phases, - MavenPluginsConfigurator mavenPluginsConfigurator, InitializersExecutor initializersExecutor, - SensorsExecutor sensorsExecutor, - SensorContext sensorContext, - EventBus eventBus, ProjectInitializer pi, + public Phase2Executor(AnalyzersExecutor analyzersExecutor, + AnalyzerContext analyzerContext, FileSystemLogger fsLogger, DefaultModuleFileSystem fs, QProfileVerifier profileVerifier, IssueExclusionsLoader issueExclusionsLoader) { - this.phases = phases; - this.mavenPluginsConfigurator = mavenPluginsConfigurator; - this.initializersExecutor = initializersExecutor; - this.sensorsExecutor = sensorsExecutor; - this.sensorContext = sensorContext; - this.eventBus = eventBus; - this.pi = pi; + this.analyzersExecutor = analyzersExecutor; + this.analyzerContext = analyzerContext; this.fsLogger = fsLogger; this.fs = fs; this.profileVerifier = profileVerifier; @@ -70,20 +56,14 @@ public final class Phase2Executor { } public static Collection<Class> getPhaseClasses() { - return Lists.<Class>newArrayList(SensorsExecutor.class, InitializersExecutor.class, ProjectInitializer.class); + return Lists.<Class>newArrayList(SensorsExecutor.class); } /** * Executed on each module */ - public void execute(Project module) { - pi.execute(module); - - eventBus.fireEvent(new ProjectAnalysisEvent(module, true)); - - executeMavenPhase(module); - - executeInitializersPhase(); + public void execute(ProjectDefinition moduleDefinition) { + fsLogger.log(); // Index and lock the filesystem fs.index(); @@ -94,23 +74,6 @@ public final class Phase2Executor { // Initialize issue exclusions issueExclusionsLoader.execute(); - sensorsExecutor.execute(sensorContext); - - eventBus.fireEvent(new ProjectAnalysisEvent(module, false)); - } - - private void executeInitializersPhase() { - if (phases.isEnabled(Phases.Phase.INIT)) { - initializersExecutor.execute(); - fsLogger.log(); - } - } - - private void executeMavenPhase(Project module) { - if (phases.isEnabled(Phases.Phase.MAVEN)) { - eventBus.fireEvent(new MavenPhaseEvent(true)); - mavenPluginsConfigurator.execute(module); - eventBus.fireEvent(new MavenPhaseEvent(false)); - } + analyzersExecutor.execute(analyzerContext); } } diff --git a/sonar-batch/src/main/java/org/sonar/batch/scan2/ProjectScanContainer.java b/sonar-batch/src/main/java/org/sonar/batch/scan2/ProjectScanContainer.java index 3ca92f4bc5b..6a3d9bfcfac 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/scan2/ProjectScanContainer.java +++ b/sonar-batch/src/main/java/org/sonar/batch/scan2/ProjectScanContainer.java @@ -20,41 +20,25 @@ package org.sonar.batch.scan2; import com.google.common.annotations.VisibleForTesting; -import org.sonar.api.BatchComponent; import org.sonar.api.CoreProperties; -import org.sonar.api.batch.InstantiationStrategy; import org.sonar.api.batch.bootstrap.ProjectBootstrapper; +import org.sonar.api.batch.bootstrap.ProjectDefinition; import org.sonar.api.batch.bootstrap.ProjectReactor; import org.sonar.api.config.Settings; import org.sonar.api.platform.ComponentContainer; -import org.sonar.api.resources.Languages; -import org.sonar.api.resources.Project; import org.sonar.api.scan.filesystem.PathResolver; import org.sonar.api.utils.SonarException; -import org.sonar.batch.DefaultFileLinesContextFactory; -import org.sonar.batch.ProjectConfigurator; -import org.sonar.batch.ProjectTree; +import org.sonar.batch.api.BatchComponent; +import org.sonar.batch.api.InstantiationStrategy; import org.sonar.batch.bootstrap.ExtensionInstaller; import org.sonar.batch.bootstrap.ExtensionMatcher; import org.sonar.batch.bootstrap.ExtensionUtils; -import org.sonar.batch.bootstrap.MetricProvider; -import org.sonar.batch.components.PeriodsDefinition; -import org.sonar.batch.debt.DebtModelProvider; import org.sonar.batch.debt.IssueChangelogDebtCalculator; import org.sonar.batch.index.Caches; import org.sonar.batch.index.ComponentDataCache; import org.sonar.batch.index.ComponentDataPersister; -import org.sonar.batch.index.DefaultIndex; -import org.sonar.batch.index.DefaultPersistenceManager; -import org.sonar.batch.index.DefaultResourcePersister; -import org.sonar.batch.index.DependencyPersister; -import org.sonar.batch.index.EventPersister; -import org.sonar.batch.index.LinkPersister; -import org.sonar.batch.index.MeasurePersister; import org.sonar.batch.index.ResourceCache; -import org.sonar.batch.index.ResourceKeyMigration; import org.sonar.batch.index.SnapshotCache; -import org.sonar.batch.index.SourcePersister; import org.sonar.batch.issue.DefaultProjectIssues; import org.sonar.batch.issue.DeprecatedViolations; import org.sonar.batch.issue.IssueCache; @@ -62,13 +46,10 @@ import org.sonar.batch.issue.IssuePersister; import org.sonar.batch.issue.ScanIssueStorage; import org.sonar.batch.phases.GraphPersister; import org.sonar.batch.profiling.PhasesSumUpTimeProfiler; -import org.sonar.batch.rule.RulesProvider; import org.sonar.batch.scan.ProjectReactorBuilder; -import org.sonar.batch.scan.ProjectSettingsReady; import org.sonar.batch.scan.filesystem.InputFileCache; import org.sonar.batch.scan.maven.FakeMavenPluginExecutor; import org.sonar.batch.scan.maven.MavenPluginExecutor; -import org.sonar.batch.scan.measure.MeasureCache; import org.sonar.batch.source.HighlightableBuilder; import org.sonar.batch.source.SymbolizableBuilder; import org.sonar.core.component.ScanGraph; @@ -76,13 +57,10 @@ import org.sonar.core.issue.IssueNotifications; import org.sonar.core.issue.IssueUpdater; import org.sonar.core.issue.workflow.FunctionExecutor; import org.sonar.core.issue.workflow.IssueWorkflow; -import org.sonar.core.notification.DefaultNotificationManager; -import org.sonar.core.technicaldebt.DefaultTechnicalDebtModel; import org.sonar.core.test.TestPlanBuilder; import org.sonar.core.test.TestPlanPerspectiveLoader; import org.sonar.core.test.TestableBuilder; import org.sonar.core.test.TestablePerspectiveLoader; -import org.sonar.core.user.DefaultUserFinder; public class ProjectScanContainer extends ComponentContainer { public ProjectScanContainer(ComponentContainer taskContainer) { @@ -102,50 +80,32 @@ public class ProjectScanContainer extends ComponentContainer { } private void projectBootstrap() { - // Old versions of bootstrappers used to pass project reactor as an extension - // so check if it is already present in parent container - ProjectReactor reactor = getComponentByType(ProjectReactor.class); + ProjectReactor reactor; + ProjectBootstrapper bootstrapper = getComponentByType(ProjectBootstrapper.class); + Settings settings = getComponentByType(Settings.class); + if (bootstrapper == null + // Starting from Maven plugin 2.3 then only DefaultProjectBootstrapper should be used. + || "true".equals(settings.getString("sonar.mojoUseRunner"))) { + // Use default SonarRunner project bootstrapper + ProjectReactorBuilder builder = getComponentByType(ProjectReactorBuilder.class); + reactor = builder.execute(); + } else { + reactor = bootstrapper.bootstrap(); + } if (reactor == null) { - // OK, not present, so look for a custom ProjectBootstrapper - ProjectBootstrapper bootstrapper = getComponentByType(ProjectBootstrapper.class); - Settings settings = getComponentByType(Settings.class); - if (bootstrapper == null - // Starting from Maven plugin 2.3 then only DefaultProjectBootstrapper should be used. - || "true".equals(settings.getString("sonar.mojoUseRunner"))) { - // Use default SonarRunner project bootstrapper - ProjectReactorBuilder builder = getComponentByType(ProjectReactorBuilder.class); - reactor = builder.execute(); - } else { - reactor = bootstrapper.bootstrap(); - } - if (reactor == null) { - throw new SonarException(bootstrapper + " has returned null as ProjectReactor"); - } - add(reactor); + throw new SonarException(bootstrapper + " has returned null as ProjectReactor"); } + add(reactor); } private void addBatchComponents() { add( - DefaultPersistenceManager.class, - DependencyPersister.class, - EventPersister.class, - LinkPersister.class, - MeasurePersister.class, - DefaultResourcePersister.class, - SourcePersister.class, - DefaultNotificationManager.class, - MetricProvider.class, - ProjectConfigurator.class, - DefaultIndex.class, - ResourceKeyMigration.class, - DefaultFileLinesContextFactory.class, Caches.class, SnapshotCache.class, ResourceCache.class, ComponentDataCache.class, ComponentDataPersister.class, - DefaultUserFinder.class, + MeasureCache.class, // file system InputFileCache.class, @@ -172,24 +132,10 @@ public class ProjectScanContainer extends ComponentContainer { GraphPersister.class, // lang - Languages.class, HighlightableBuilder.class, SymbolizableBuilder.class, - // technical debt - DefaultTechnicalDebtModel.class, - new DebtModelProvider(), - - // rules - new RulesProvider(), - - // Differential periods - PeriodsDefinition.class, - - // Measures - MeasureCache.class, - - ProjectSettingsReady.class); + ScanTaskObservers.class); } private void fixMavenExecutor() { @@ -204,19 +150,20 @@ public class ProjectScanContainer extends ComponentContainer { @Override protected void doAfterStart() { - ProjectTree tree = getComponentByType(ProjectTree.class); - scanRecursively(tree.getRootProject()); + ProjectReactor tree = getComponentByType(ProjectReactor.class); + scanRecursively(tree.getRoot()); + getComponentByType(ScanTaskObservers.class).notifyEndOfScanTask(); } - private void scanRecursively(Project module) { - for (Project subModules : module.getModules()) { + private void scanRecursively(ProjectDefinition module) { + for (ProjectDefinition subModules : module.getSubProjects()) { scanRecursively(subModules); } scan(module); } @VisibleForTesting - void scan(Project module) { + void scan(ProjectDefinition module) { new ModuleScanContainer(this, module).execute(); } diff --git a/sonar-batch/src/main/java/org/sonar/batch/scan2/ScanTaskObserver.java b/sonar-batch/src/main/java/org/sonar/batch/scan2/ScanTaskObserver.java new file mode 100644 index 00000000000..89f5c7694cc --- /dev/null +++ b/sonar-batch/src/main/java/org/sonar/batch/scan2/ScanTaskObserver.java @@ -0,0 +1,28 @@ +/* + * 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.scan2; + +import org.sonar.batch.api.BatchExtension; + +public interface ScanTaskObserver extends BatchExtension { + + void scanTaskCompleted(ProjectScanContainer container); + +} diff --git a/sonar-batch/src/main/java/org/sonar/batch/scan2/ScanTaskObservers.java b/sonar-batch/src/main/java/org/sonar/batch/scan2/ScanTaskObservers.java new file mode 100644 index 00000000000..6dc8fa6fd40 --- /dev/null +++ b/sonar-batch/src/main/java/org/sonar/batch/scan2/ScanTaskObservers.java @@ -0,0 +1,42 @@ +/* + * 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.scan2; + +public class ScanTaskObservers { + + private ScanTaskObserver[] observers; + private ProjectScanContainer projectScanContainer; + + public ScanTaskObservers(ProjectScanContainer projectScanContainer, ScanTaskObserver... observers) { + this.projectScanContainer = projectScanContainer; + this.observers = observers; + } + + public ScanTaskObservers(ProjectScanContainer projectScanContainer) { + this(projectScanContainer, new ScanTaskObserver[0]); + } + + public void notifyEndOfScanTask() { + for (ScanTaskObserver scanTaskObserver : observers) { + scanTaskObserver.scanTaskCompleted(projectScanContainer); + } + } + +} diff --git a/sonar-batch/src/main/java/org/sonar/batch/scan2/package-info.java b/sonar-batch/src/main/java/org/sonar/batch/scan2/package-info.java new file mode 100644 index 00000000000..d5d167bf737 --- /dev/null +++ b/sonar-batch/src/main/java/org/sonar/batch/scan2/package-info.java @@ -0,0 +1,21 @@ +/* + * 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. + */ +@javax.annotation.ParametersAreNonnullByDefault +package org.sonar.batch.scan2;
\ No newline at end of file diff --git a/sonar-batch/src/main/java/org/sonar/batch/settings/package-info.java b/sonar-batch/src/main/java/org/sonar/batch/settings/package-info.java new file mode 100644 index 00000000000..6d2df16d3ab --- /dev/null +++ b/sonar-batch/src/main/java/org/sonar/batch/settings/package-info.java @@ -0,0 +1,21 @@ +/* + * 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. + */ +@javax.annotation.ParametersAreNonnullByDefault +package org.sonar.batch.settings;
\ No newline at end of file diff --git a/sonar-batch/src/test/java/org/sonar/batch/bootstrap/BatchExtensionDictionnaryTest.java b/sonar-batch/src/test/java/org/sonar/batch/bootstrap/BatchExtensionDictionnaryTest.java index 79af9782d6d..d7f5df2e761 100644 --- a/sonar-batch/src/test/java/org/sonar/batch/bootstrap/BatchExtensionDictionnaryTest.java +++ b/sonar-batch/src/test/java/org/sonar/batch/bootstrap/BatchExtensionDictionnaryTest.java @@ -23,14 +23,17 @@ import org.junit.Test; import org.sonar.api.BatchExtension; import org.sonar.api.batch.Sensor; import org.sonar.api.batch.SensorContext; +import org.sonar.api.batch.fs.FileSystem; import org.sonar.api.platform.ComponentContainer; import org.sonar.api.resources.Project; +import org.sonar.batch.api.analyzer.AnalyzerContext; import java.util.Collection; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertThat; import static org.junit.internal.matchers.IsCollectionContaining.hasItem; +import static org.mockito.Mockito.mock; public class BatchExtensionDictionnaryTest { @@ -39,7 +42,7 @@ public class BatchExtensionDictionnaryTest { for (BatchExtension extension : extensions) { iocContainer.addSingleton(extension); } - return new BatchExtensionDictionnary(iocContainer); + return new BatchExtensionDictionnary(iocContainer, mock(FileSystem.class), mock(AnalyzerContext.class)); } @Test diff --git a/sonar-batch/src/test/java/org/sonar/batch/bootstrap/MetricProviderTest.java b/sonar-batch/src/test/java/org/sonar/batch/bootstrap/MetricProviderTest.java index 37b6736a89e..1af98413155 100644 --- a/sonar-batch/src/test/java/org/sonar/batch/bootstrap/MetricProviderTest.java +++ b/sonar-batch/src/test/java/org/sonar/batch/bootstrap/MetricProviderTest.java @@ -41,12 +41,12 @@ public class MetricProviderTest { @Test public void should_provide_plugin_metrics() { - Metrics factory = new Metrics(){ + Metrics factory = new Metrics() { public List<Metric> getMetrics() { - return Arrays.asList(new Metric.Builder("custom", "Custom", Metric.ValueType.FLOAT).create()); + return Arrays.<Metric>asList(new Metric.Builder("custom", "Custom", Metric.ValueType.FLOAT).create()); } }; - MetricProvider provider = new MetricProvider(new Metrics[]{factory}); + MetricProvider provider = new MetricProvider(new Metrics[] {factory}); List<Metric> metrics = provider.provide(); assertThat(metrics.size()).isEqualTo(1 + CoreMetrics.getMetrics().size()); diff --git a/sonar-batch/src/test/java/org/sonar/batch/index/ResourceKeyMigrationTest.java b/sonar-batch/src/test/java/org/sonar/batch/index/ResourceKeyMigrationTest.java index 564a6845a04..1fe27bc91b1 100644 --- a/sonar-batch/src/test/java/org/sonar/batch/index/ResourceKeyMigrationTest.java +++ b/sonar-batch/src/test/java/org/sonar/batch/index/ResourceKeyMigrationTest.java @@ -28,6 +28,7 @@ import org.junit.rules.TemporaryFolder; import org.slf4j.Logger; import org.sonar.api.batch.fs.InputFile; import org.sonar.api.batch.fs.internal.DefaultInputFile; +import org.sonar.api.batch.fs.internal.DeprecatedDefaultInputFile; import org.sonar.api.resources.Project; import org.sonar.jpa.test.AbstractDbUnitTestCase; @@ -86,9 +87,10 @@ public class ResourceKeyMigrationTest extends AbstractDbUnitTestCase { File file = new File(baseDir, path); String effectiveKey = module.getKey() + ":" + path; String deprecatedEffectiveKey = module.getKey() + ":" + deprecatedKey; - return new DefaultInputFile(path).setFile(file) - .setKey(effectiveKey) + return new DeprecatedDefaultInputFile(path) .setDeprecatedKey(deprecatedEffectiveKey) + .setFile(file) + .setKey(effectiveKey) .setType(isTest ? InputFile.Type.TEST : InputFile.Type.MAIN); } @@ -99,7 +101,7 @@ public class ResourceKeyMigrationTest extends AbstractDbUnitTestCase { Logger logger = mock(Logger.class); ResourceKeyMigration migration = new ResourceKeyMigration(getSession(), logger); migration.checkIfMigrationNeeded(multiModuleProject); - + migration.migrateIfNeeded(javaModule, javaInputFiles); migration.migrateIfNeeded(phpModule, phpInputFiles); @@ -110,7 +112,7 @@ public class ResourceKeyMigrationTest extends AbstractDbUnitTestCase { verify(logger).info("Component {} changed to {}", "b:org/foo", "b:src/main/java/org/foo"); verify(logger).info("Component {} changed to {}", "b:[root]", "b:src/main/java"); - checkTables("shouldMigrateResourceKeys", new String[]{"build_date", "created_at"}, "projects"); + checkTables("shouldMigrateResourceKeys", new String[] {"build_date", "created_at"}, "projects"); } private static Project newProject(String key, String language) { diff --git a/sonar-batch/src/test/java/org/sonar/batch/issue/ignore/scanner/IssueExclusionsLoaderTest.java b/sonar-batch/src/test/java/org/sonar/batch/issue/ignore/scanner/IssueExclusionsLoaderTest.java index 065601b9a91..5b97df2c7f5 100644 --- a/sonar-batch/src/test/java/org/sonar/batch/issue/ignore/scanner/IssueExclusionsLoaderTest.java +++ b/sonar-batch/src/test/java/org/sonar/batch/issue/ignore/scanner/IssueExclusionsLoaderTest.java @@ -29,7 +29,7 @@ import org.mockito.Mock; import org.mockito.MockitoAnnotations; import org.sonar.api.batch.fs.InputFile; import org.sonar.api.batch.fs.internal.DefaultFileSystem; -import org.sonar.api.batch.fs.internal.DefaultInputFile; +import org.sonar.api.batch.fs.internal.DeprecatedDefaultInputFile; import org.sonar.api.utils.SonarException; import org.sonar.batch.issue.ignore.pattern.IssueExclusionPatternInitializer; import org.sonar.batch.issue.ignore.pattern.IssueInclusionPatternInitializer; @@ -101,12 +101,12 @@ public class IssueExclusionsLoaderTest { @Test public void shouldAnalyzeProject() throws IOException { File javaFile1 = new File(baseDir, "src/main/java/Foo.java"); - fs.add(new DefaultInputFile("src/main/java/Foo.java") + fs.add(new DeprecatedDefaultInputFile("src/main/java/Foo.java") .setFile(javaFile1) .setType(InputFile.Type.MAIN) .setKey("polop:src/main/java/Foo.java")); File javaTestFile1 = new File(baseDir, "src/test/java/FooTest.java"); - fs.add(new DefaultInputFile("src/test/java/FooTest.java") + fs.add(new DeprecatedDefaultInputFile("src/test/java/FooTest.java") .setFile(javaTestFile1) .setType(InputFile.Type.TEST) .setKey("polop:src/test/java/FooTest.java")); @@ -126,12 +126,12 @@ public class IssueExclusionsLoaderTest { @Test public void shouldAnalyseFilesOnlyWhenRegexConfigured() throws IOException { File javaFile1 = new File(baseDir, "src/main/java/Foo.java"); - fs.add(new DefaultInputFile("src/main/java/Foo.java") + fs.add(new DeprecatedDefaultInputFile("src/main/java/Foo.java") .setFile(javaFile1) .setType(InputFile.Type.MAIN) .setKey("polop:src/main/java/Foo.java")); File javaTestFile1 = new File(baseDir, "src/test/java/FooTest.java"); - fs.add(new DefaultInputFile("src/test/java/FooTest.java") + fs.add(new DeprecatedDefaultInputFile("src/test/java/FooTest.java") .setFile(javaTestFile1) .setType(InputFile.Type.TEST) .setKey("polop:src/test/java/FooTest.java")); @@ -149,7 +149,7 @@ public class IssueExclusionsLoaderTest { @Test public void shouldReportFailure() throws IOException { File phpFile1 = new File(baseDir, "src/Foo.php"); - fs.add(new DefaultInputFile("src/Foo.php") + fs.add(new DeprecatedDefaultInputFile("src/Foo.php") .setFile(phpFile1) .setType(InputFile.Type.MAIN) .setKey("polop:src/Foo.php")); diff --git a/sonar-batch/src/test/java/org/sonar/batch/medium/Scan2MediumTest.java b/sonar-batch/src/test/java/org/sonar/batch/medium/Scan2MediumTest.java deleted file mode 100644 index ae4a9ec02bb..00000000000 --- a/sonar-batch/src/test/java/org/sonar/batch/medium/Scan2MediumTest.java +++ /dev/null @@ -1,168 +0,0 @@ -/* - * 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.medium; - -import com.google.common.collect.BiMap; -import com.google.common.collect.HashBiMap; -import com.google.common.collect.ImmutableMap; -import com.google.common.collect.Lists; -import com.google.common.collect.Maps; -import org.junit.Test; -import org.sonar.api.measures.Metric; -import org.sonar.api.measures.MetricFinder; -import org.sonar.api.rule.RuleKey; -import org.sonar.api.rules.Rule; -import org.sonar.api.rules.RuleFinder; -import org.sonar.api.rules.RuleQuery; -import org.sonar.batch.bootstrap.PluginsReferential; -import org.sonar.batch.bootstrapper.Batch; -import org.sonar.batch.bootstrapper.EnvironmentInformation; -import org.sonar.batch.settings.SettingsReferential; -import org.sonar.core.plugins.RemotePlugin; - -import java.io.File; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -public class Scan2MediumTest { - - @Test - public void mediumTest() { - Batch batch = Batch.builder() - .setEnableLoggingConfiguration(true) - .addComponent(new EnvironmentInformation("mediumTest", "1.0")) - .addComponent(new MockSettingsReferential()) - .addComponent(new MockPluginsReferential()) - .addComponent(new MockMetricFinder()) - .addComponent(new MockRuleFinder()) - .setBootstrapProperties(ImmutableMap.of("sonar.analysis.mode", "sensor")) - .build(); - - batch.start(); - - // batch.executeTask(ImmutableMap.<String, String>builder().put("sonar.task", "scan").build()); - - batch.stop(); - } - - private static class MockSettingsReferential implements SettingsReferential { - - private Map<String, String> globalSettings = new HashMap<String, String>(); - private Map<String, Map<String, String>> projectSettings = new HashMap<String, Map<String, String>>(); - - @Override - public Map<String, String> globalSettings() { - return globalSettings; - } - - @Override - public Map<String, String> projectSettings(String projectKey) { - return projectSettings.containsKey(projectKey) ? projectSettings.get(projectKey) : Collections.<String, String>emptyMap(); - } - - } - - private static class MockPluginsReferential implements PluginsReferential { - - private List<RemotePlugin> pluginList = new ArrayList<RemotePlugin>(); - private Map<RemotePlugin, File> pluginFiles = new HashMap<RemotePlugin, File>(); - - @Override - public List<RemotePlugin> pluginList() { - return pluginList; - } - - @Override - public File pluginFile(RemotePlugin remote) { - return pluginFiles.get(remote); - } - - } - - private static class MockMetricFinder implements MetricFinder { - - private Map<String, Metric> metricsByKey = Maps.newLinkedHashMap(); - private Map<Integer, Metric> metricsById = Maps.newLinkedHashMap(); - - @Override - public Metric findById(int metricId) { - return metricsById.get(metricId); - } - - @Override - public Metric findByKey(String key) { - return metricsByKey.get(key); - } - - @Override - public Collection<Metric> findAll(List<String> metricKeys) { - List<Metric> result = Lists.newLinkedList(); - for (String metricKey : metricKeys) { - Metric metric = findByKey(metricKey); - if (metric != null) { - result.add(metric); - } - } - return result; - } - - @Override - public Collection<Metric> findAll() { - return metricsByKey.values(); - } - - } - - private static class MockRuleFinder implements RuleFinder { - private BiMap<Integer, Rule> rulesById = HashBiMap.create(); - private Map<String, Map<String, Rule>> rulesByRepoKeyAndRuleKey = Maps.newHashMap(); - - @Override - public Rule findById(int ruleId) { - return rulesById.get(ruleId); - } - - @Override - public Rule findByKey(String repositoryKey, String ruleKey) { - Map<String, Rule> repository = rulesByRepoKeyAndRuleKey.get(repositoryKey); - return repository != null ? repository.get(ruleKey) : null; - } - - @Override - public Rule findByKey(RuleKey key) { - return findByKey(key.repository(), key.rule()); - } - - @Override - public Rule find(RuleQuery query) { - throw new UnsupportedOperationException(); - } - - @Override - public Collection<Rule> findAll(RuleQuery query) { - throw new UnsupportedOperationException(); - } - } - -} diff --git a/sonar-batch/src/test/java/org/sonar/batch/rule/ActiveRulesProviderTest.java b/sonar-batch/src/test/java/org/sonar/batch/rule/ActiveRulesProviderTest.java index c2039e71bf2..205fe2e8a8c 100644 --- a/sonar-batch/src/test/java/org/sonar/batch/rule/ActiveRulesProviderTest.java +++ b/sonar-batch/src/test/java/org/sonar/batch/rule/ActiveRulesProviderTest.java @@ -27,9 +27,10 @@ import org.sonar.api.rule.RuleKey; import org.sonar.api.rule.Severity; import org.sonar.api.rules.Rule; import org.sonar.api.rules.RuleFinder; +import org.sonar.batch.api.rules.QProfile; +import org.sonar.batch.rules.QProfileWithId; import org.sonar.core.persistence.AbstractDaoTestCase; import org.sonar.core.qualityprofile.db.ActiveRuleDao; -import org.sonar.core.qualityprofile.db.QualityProfileDao; import java.util.Arrays; @@ -53,13 +54,12 @@ public class ActiveRulesProviderTest extends AbstractDaoTestCase { @Test public void build_active_rules() throws Exception { setupData("shared"); - QualityProfileDao profileDao = new QualityProfileDao(getMyBatis()); - when(qProfiles.findAll()).thenReturn(Arrays.asList( + when(qProfiles.findAll()).thenReturn(Arrays.<QProfile>asList( // 1 rule is enabled on java with severity INFO - new ModuleQProfiles.QProfile(profileDao.selectById(2)), + new QProfileWithId(2, "Java Two", "java", 20), // 1 rule is enabled on php with severity BLOCKER - new ModuleQProfiles.QProfile(profileDao.selectById(3)) - )); + new QProfileWithId(3, "Php One", "php", 30) + )); ActiveRulesProvider provider = new ActiveRulesProvider(); ActiveRuleDao activeRuleDao = new ActiveRuleDao(getMyBatis()); diff --git a/sonar-batch/src/test/java/org/sonar/batch/rule/ModuleQProfilesTest.java b/sonar-batch/src/test/java/org/sonar/batch/rule/ModuleQProfilesTest.java index 99b39d19986..6b3102206f3 100644 --- a/sonar-batch/src/test/java/org/sonar/batch/rule/ModuleQProfilesTest.java +++ b/sonar-batch/src/test/java/org/sonar/batch/rule/ModuleQProfilesTest.java @@ -25,6 +25,12 @@ import org.sonar.api.config.Settings; import org.sonar.api.resources.Language; import org.sonar.api.resources.Languages; import org.sonar.api.utils.MessageException; +import org.sonar.batch.api.rules.QProfile; +import org.sonar.batch.languages.DeprecatedLanguagesReferential; +import org.sonar.batch.languages.LanguagesReferential; +import org.sonar.batch.rules.DefaultQProfileReferential; +import org.sonar.batch.rules.QProfilesReferential; +import org.sonar.batch.rules.QProfileWithId; import org.sonar.core.persistence.AbstractDaoTestCase; import org.sonar.core.qualityprofile.db.QualityProfileDao; @@ -35,31 +41,32 @@ import static org.fest.assertions.Fail.fail; public class ModuleQProfilesTest extends AbstractDaoTestCase { - Languages languages = new Languages(new SimpleLanguage("java"), new SimpleLanguage("php")); + LanguagesReferential languages = new DeprecatedLanguagesReferential(new Languages(new SimpleLanguage("java"), new SimpleLanguage("php"))); Settings settings = new Settings(); @Test public void find_profiles() throws Exception { setupData("shared"); QualityProfileDao dao = new QualityProfileDao(getMyBatis()); + QProfilesReferential ref = new DefaultQProfileReferential(dao); settings.setProperty("sonar.profile.java", "Java Two"); settings.setProperty("sonar.profile.abap", "Abap One"); settings.setProperty("sonar.profile.php", "Php One"); - ModuleQProfiles moduleQProfiles = new ModuleQProfiles(settings, languages, dao); - List<ModuleQProfiles.QProfile> qProfiles = Lists.newArrayList(moduleQProfiles.findAll()); + ModuleQProfiles moduleQProfiles = new ModuleQProfiles(settings, languages, ref); + List<QProfile> qProfiles = Lists.newArrayList(moduleQProfiles.findAll()); assertThat(qProfiles).hasSize(2); assertThat(moduleQProfiles.findByLanguage("java")).isNotNull(); assertThat(moduleQProfiles.findByLanguage("php")).isNotNull(); assertThat(moduleQProfiles.findByLanguage("abap")).isNull(); - ModuleQProfiles.QProfile javaProfile = qProfiles.get(0); + QProfileWithId javaProfile = (QProfileWithId) qProfiles.get(0); assertThat(javaProfile.id()).isEqualTo(2); assertThat(javaProfile.name()).isEqualTo("Java Two"); assertThat(javaProfile.language()).isEqualTo("java"); assertThat(javaProfile.version()).isEqualTo(20); - ModuleQProfiles.QProfile phpProfile = qProfiles.get(1); + QProfileWithId phpProfile = (QProfileWithId) qProfiles.get(1); assertThat(phpProfile.id()).isEqualTo(3); assertThat(phpProfile.name()).isEqualTo("Php One"); assertThat(phpProfile.language()).isEqualTo("php"); @@ -71,22 +78,23 @@ public class ModuleQProfilesTest extends AbstractDaoTestCase { public void use_sonar_profile_property() throws Exception { setupData("shared"); QualityProfileDao dao = new QualityProfileDao(getMyBatis()); + QProfilesReferential ref = new DefaultQProfileReferential(dao); settings.setProperty("sonar.profile", "Java Two"); settings.setProperty("sonar.profile.php", "Php One"); - ModuleQProfiles moduleQProfiles = new ModuleQProfiles(settings, languages, dao); - List<ModuleQProfiles.QProfile> qProfiles = Lists.newArrayList(moduleQProfiles.findAll()); + ModuleQProfiles moduleQProfiles = new ModuleQProfiles(settings, languages, ref); + List<QProfile> qProfiles = Lists.newArrayList(moduleQProfiles.findAll()); assertThat(qProfiles).hasSize(2); - ModuleQProfiles.QProfile javaProfile = qProfiles.get(0); + QProfileWithId javaProfile = (QProfileWithId) qProfiles.get(0); assertThat(javaProfile.id()).isEqualTo(2); assertThat(javaProfile.name()).isEqualTo("Java Two"); assertThat(javaProfile.language()).isEqualTo("java"); assertThat(javaProfile.version()).isEqualTo(20); // Fallback to sonar.profile.php if no match for sonar.profile - ModuleQProfiles.QProfile phpProfile = qProfiles.get(1); + QProfileWithId phpProfile = (QProfileWithId) qProfiles.get(1); assertThat(phpProfile.id()).isEqualTo(3); assertThat(phpProfile.name()).isEqualTo("Php One"); assertThat(phpProfile.language()).isEqualTo("php"); @@ -98,12 +106,13 @@ public class ModuleQProfilesTest extends AbstractDaoTestCase { public void fail_if_unknown_profile() throws Exception { setupData("shared"); QualityProfileDao dao = new QualityProfileDao(getMyBatis()); + QProfilesReferential ref = new DefaultQProfileReferential(dao); settings.setProperty("sonar.profile.java", "Unknown"); settings.setProperty("sonar.profile.php", "Php One"); try { - new ModuleQProfiles(settings, languages, dao); + new ModuleQProfiles(settings, languages, ref); fail(); } catch (MessageException e) { assertThat(e).hasMessage("Quality profile not found : 'Unknown' on language 'java'"); diff --git a/sonar-batch/src/test/java/org/sonar/batch/rule/QProfileSensorTest.java b/sonar-batch/src/test/java/org/sonar/batch/rule/QProfileSensorTest.java index a074f7185df..04ba7b1c251 100644 --- a/sonar-batch/src/test/java/org/sonar/batch/rule/QProfileSensorTest.java +++ b/sonar-batch/src/test/java/org/sonar/batch/rule/QProfileSensorTest.java @@ -25,6 +25,8 @@ import org.sonar.api.batch.fs.internal.DefaultFileSystem; import org.sonar.api.measures.CoreMetrics; import org.sonar.api.resources.Project; import org.sonar.api.test.IsMeasure; +import org.sonar.batch.api.rules.QProfile; +import org.sonar.batch.rules.QProfileWithId; import org.sonar.core.persistence.AbstractDaoTestCase; import org.sonar.core.qualityprofile.db.QualityProfileDao; @@ -54,7 +56,7 @@ public class QProfileSensorTest extends AbstractDaoTestCase { public void no_qprofiles() throws Exception { setupData("shared"); QualityProfileDao dao = new QualityProfileDao(getMyBatis()); - when(moduleQProfiles.findAll()).thenReturn(Collections.<ModuleQProfiles.QProfile>emptyList()); + when(moduleQProfiles.findAll()).thenReturn(Collections.<QProfile>emptyList()); QProfileSensor sensor = new QProfileSensor(moduleQProfiles, fs, dao); assertThat(sensor.shouldExecuteOnProject(project)).isTrue(); @@ -69,8 +71,8 @@ public class QProfileSensorTest extends AbstractDaoTestCase { setupData("shared"); QualityProfileDao dao = new QualityProfileDao(getMyBatis()); - when(moduleQProfiles.findByLanguage("java")).thenReturn(new ModuleQProfiles.QProfile(dao.selectById(2))); - when(moduleQProfiles.findByLanguage("php")).thenReturn(new ModuleQProfiles.QProfile(dao.selectById(3))); + when(moduleQProfiles.findByLanguage("java")).thenReturn(new QProfileWithId(2, "Java Two", "java", 20)); + when(moduleQProfiles.findByLanguage("php")).thenReturn(new QProfileWithId(3, "Php One", "php", 30)); when(moduleQProfiles.findByLanguage("abap")).thenReturn(null); fs.addLanguages("java", "php", "abap"); @@ -86,8 +88,8 @@ public class QProfileSensorTest extends AbstractDaoTestCase { setupData("shared"); QualityProfileDao dao = new QualityProfileDao(getMyBatis()); - when(moduleQProfiles.findByLanguage("java")).thenReturn(new ModuleQProfiles.QProfile(dao.selectById(2))); - when(moduleQProfiles.findByLanguage("php")).thenReturn(new ModuleQProfiles.QProfile(dao.selectById(3))); + when(moduleQProfiles.findByLanguage("java")).thenReturn(new QProfileWithId(2, "Java Two", "java", 20)); + when(moduleQProfiles.findByLanguage("php")).thenReturn(new QProfileWithId(3, "Php One", "php", 30)); when(moduleQProfiles.findByLanguage("abap")).thenReturn(null); fs.addLanguages("java"); @@ -106,8 +108,8 @@ public class QProfileSensorTest extends AbstractDaoTestCase { setupData("shared"); QualityProfileDao dao = new QualityProfileDao(getMyBatis()); - when(moduleQProfiles.findByLanguage("java")).thenReturn(new ModuleQProfiles.QProfile(dao.selectById(2))); - when(moduleQProfiles.findByLanguage("php")).thenReturn(new ModuleQProfiles.QProfile(dao.selectById(3))); + when(moduleQProfiles.findByLanguage("java")).thenReturn(new QProfileWithId(2, "Java Two", "java", 20)); + when(moduleQProfiles.findByLanguage("php")).thenReturn(new QProfileWithId(3, "Php One", "php", 30)); when(moduleQProfiles.findByLanguage("abap")).thenReturn(null); fs.addLanguages("java", "php"); diff --git a/sonar-batch/src/test/java/org/sonar/batch/rule/QProfileVerifierTest.java b/sonar-batch/src/test/java/org/sonar/batch/rule/QProfileVerifierTest.java index 0fd8c58d040..483b6c7e397 100644 --- a/sonar-batch/src/test/java/org/sonar/batch/rule/QProfileVerifierTest.java +++ b/sonar-batch/src/test/java/org/sonar/batch/rule/QProfileVerifierTest.java @@ -28,9 +28,11 @@ import org.sonar.api.batch.fs.internal.DefaultFileSystem; import org.sonar.api.config.Settings; import org.sonar.api.profiles.RulesProfile; import org.sonar.api.utils.MessageException; -import org.sonar.batch.rule.ModuleQProfiles.QProfile; +import org.sonar.batch.rules.QProfileWithId; -import static org.mockito.Mockito.*; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; public class QProfileVerifierTest { @@ -46,11 +48,11 @@ public class QProfileVerifierTest { @Before public void before() { profiles = mock(ModuleQProfiles.class); - QProfile javaProfile = mock(QProfile.class); + QProfileWithId javaProfile = mock(QProfileWithId.class); when(javaProfile.name()).thenReturn("My Java profile"); javaRulesProfile = mock(RulesProfile.class); when(profiles.findByLanguage("java")).thenReturn(javaProfile); - QProfile cobolProfile = mock(QProfile.class); + QProfileWithId cobolProfile = mock(QProfileWithId.class); when(cobolProfile.name()).thenReturn("My Cobol profile"); cobolRulesProfile = mock(RulesProfile.class); when(profiles.findByLanguage("cobol")).thenReturn(cobolProfile); diff --git a/sonar-batch/src/test/java/org/sonar/batch/rule/RulesProfileProviderTest.java b/sonar-batch/src/test/java/org/sonar/batch/rule/RulesProfileProviderTest.java index 3a980631b1c..789f4a35485 100644 --- a/sonar-batch/src/test/java/org/sonar/batch/rule/RulesProfileProviderTest.java +++ b/sonar-batch/src/test/java/org/sonar/batch/rule/RulesProfileProviderTest.java @@ -24,6 +24,8 @@ import org.sonar.api.batch.rule.ActiveRules; import org.sonar.api.config.Settings; import org.sonar.api.profiles.RulesProfile; import org.sonar.api.rules.RuleFinder; +import org.sonar.batch.api.rules.QProfile; +import org.sonar.batch.rules.QProfileWithId; import java.util.Arrays; @@ -42,8 +44,8 @@ public class RulesProfileProviderTest { @Test public void merge_profiles() throws Exception { - ModuleQProfiles.QProfile qProfile = new ModuleQProfiles.QProfile(33, "Sonar way", "java", 12); - when(qProfiles.findAll()).thenReturn(Arrays.asList(qProfile)); + QProfileWithId qProfile = new QProfileWithId(33, "Sonar way", "java", 12); + when(qProfiles.findAll()).thenReturn(Arrays.<QProfile>asList(qProfile)); RulesProfile profile = provider.provide(qProfiles, activeRules, ruleFinder, settings); @@ -64,7 +66,7 @@ public class RulesProfileProviderTest { public void keep_compatibility_with_single_language_projects() throws Exception { settings.setProperty("sonar.language", "java"); - ModuleQProfiles.QProfile qProfile = new ModuleQProfiles.QProfile(33, "Sonar way", "java", 12); + QProfileWithId qProfile = new QProfileWithId(33, "Sonar way", "java", 12); when(qProfiles.findByLanguage("java")).thenReturn(qProfile); RulesProfile profile = provider.provide(qProfiles, activeRules, ruleFinder, settings); diff --git a/sonar-batch/src/test/java/org/sonar/batch/rule/UsedQProfilesTest.java b/sonar-batch/src/test/java/org/sonar/batch/rule/UsedQProfilesTest.java index b3cab37a325..1e9d47db0ec 100644 --- a/sonar-batch/src/test/java/org/sonar/batch/rule/UsedQProfilesTest.java +++ b/sonar-batch/src/test/java/org/sonar/batch/rule/UsedQProfilesTest.java @@ -20,6 +20,7 @@ package org.sonar.batch.rule; import org.junit.Test; +import org.sonar.batch.rules.QProfileWithId; import static org.fest.assertions.Assertions.assertThat; @@ -28,8 +29,8 @@ public class UsedQProfilesTest { @Test public void serialization() throws Exception { - ModuleQProfiles.QProfile java = new ModuleQProfiles.QProfile(1, "Sonar Way", "java", 1); - ModuleQProfiles.QProfile php = new ModuleQProfiles.QProfile(2, "Sonar Way", "php", 1); + QProfileWithId java = new QProfileWithId(1, "Sonar Way", "java", 1); + QProfileWithId php = new QProfileWithId(2, "Sonar Way", "php", 1); UsedQProfiles used = UsedQProfiles.fromProfiles(java, php); assertThat(used.toJSON()).isEqualTo( diff --git a/sonar-batch/src/test/java/org/sonar/batch/scan/LanguageVerifierTest.java b/sonar-batch/src/test/java/org/sonar/batch/scan/LanguageVerifierTest.java index 8ea87446d82..30b6b571883 100644 --- a/sonar-batch/src/test/java/org/sonar/batch/scan/LanguageVerifierTest.java +++ b/sonar-batch/src/test/java/org/sonar/batch/scan/LanguageVerifierTest.java @@ -27,13 +27,15 @@ import org.sonar.api.config.Settings; import org.sonar.api.resources.Java; import org.sonar.api.resources.Languages; import org.sonar.api.utils.MessageException; +import org.sonar.batch.languages.DeprecatedLanguagesReferential; +import org.sonar.batch.languages.LanguagesReferential; import static org.fest.assertions.Assertions.assertThat; public class LanguageVerifierTest { Settings settings = new Settings(); - Languages languages = new Languages(Java.INSTANCE); + LanguagesReferential languages = new DeprecatedLanguagesReferential(new Languages(Java.INSTANCE)); DefaultFileSystem fs = new DefaultFileSystem(); @Rule diff --git a/sonar-batch/src/test/java/org/sonar/batch/scan/filesystem/AdditionalFilePredicatesTest.java b/sonar-batch/src/test/java/org/sonar/batch/scan/filesystem/AdditionalFilePredicatesTest.java index 86121955485..0bb4b243226 100644 --- a/sonar-batch/src/test/java/org/sonar/batch/scan/filesystem/AdditionalFilePredicatesTest.java +++ b/sonar-batch/src/test/java/org/sonar/batch/scan/filesystem/AdditionalFilePredicatesTest.java @@ -24,6 +24,7 @@ import org.junit.Test; import org.junit.rules.TemporaryFolder; import org.sonar.api.batch.fs.FilePredicate; import org.sonar.api.batch.fs.internal.DefaultInputFile; +import org.sonar.api.batch.fs.internal.DeprecatedDefaultInputFile; import java.io.File; @@ -38,10 +39,10 @@ public class AdditionalFilePredicatesTest { public void key() throws Exception { FilePredicate predicate = new AdditionalFilePredicates.KeyPredicate("struts:Action.java"); - DefaultInputFile inputFile = new DefaultInputFile("Action.java").setKey("struts:Action.java"); + DefaultInputFile inputFile = new DeprecatedDefaultInputFile("Action.java").setKey("struts:Action.java"); assertThat(predicate.apply(inputFile)).isTrue(); - inputFile = new DefaultInputFile("Filter.java").setKey("struts:Filter.java"); + inputFile = new DeprecatedDefaultInputFile("Filter.java").setKey("struts:Filter.java"); assertThat(predicate.apply(inputFile)).isFalse(); } @@ -49,10 +50,10 @@ public class AdditionalFilePredicatesTest { public void deprecated_key() throws Exception { FilePredicate predicate = new AdditionalFilePredicates.DeprecatedKeyPredicate("struts:Action.java"); - DefaultInputFile inputFile = new DefaultInputFile("Action.java").setDeprecatedKey("struts:Action.java"); + DeprecatedDefaultInputFile inputFile = new DeprecatedDefaultInputFile("Action.java").setDeprecatedKey("struts:Action.java"); assertThat(predicate.apply(inputFile)).isTrue(); - inputFile = new DefaultInputFile("Filter.java").setDeprecatedKey("struts:Filter.java"); + inputFile = new DeprecatedDefaultInputFile("Filter.java").setDeprecatedKey("struts:Filter.java"); assertThat(predicate.apply(inputFile)).isFalse(); } @@ -61,10 +62,10 @@ public class AdditionalFilePredicatesTest { File dir = temp.newFolder(); FilePredicate predicate = new AdditionalFilePredicates.SourceDirPredicate(dir.getAbsolutePath()); - DefaultInputFile inputFile = new DefaultInputFile("Action.java").setSourceDirAbsolutePath(dir.getAbsolutePath()); + DeprecatedDefaultInputFile inputFile = new DeprecatedDefaultInputFile("Action.java").setSourceDirAbsolutePath(dir.getAbsolutePath()); assertThat(predicate.apply(inputFile)).isTrue(); - inputFile = new DefaultInputFile("Filter.java").setSourceDirAbsolutePath(temp.newFolder().getAbsolutePath()); + inputFile = new DeprecatedDefaultInputFile("Filter.java").setSourceDirAbsolutePath(temp.newFolder().getAbsolutePath()); assertThat(predicate.apply(inputFile)).isFalse(); } @@ -72,10 +73,10 @@ public class AdditionalFilePredicatesTest { public void path_relative_to_source_dir() throws Exception { FilePredicate predicate = new AdditionalFilePredicates.SourceRelativePathPredicate("foo/Bar.php"); - DefaultInputFile inputFile = new DefaultInputFile("src/php/foo/Bar.php").setPathRelativeToSourceDir("foo/Bar.php"); + DeprecatedDefaultInputFile inputFile = new DeprecatedDefaultInputFile("src/php/foo/Bar.php").setPathRelativeToSourceDir("foo/Bar.php"); assertThat(predicate.apply(inputFile)).isTrue(); - inputFile = new DefaultInputFile("foo/Bar.php").setPathRelativeToSourceDir("Bar.php"); + inputFile = new DeprecatedDefaultInputFile("foo/Bar.php").setPathRelativeToSourceDir("Bar.php"); assertThat(predicate.apply(inputFile)).isFalse(); } } diff --git a/sonar-batch/src/test/java/org/sonar/batch/scan/filesystem/ComponentIndexerTest.java b/sonar-batch/src/test/java/org/sonar/batch/scan/filesystem/ComponentIndexerTest.java index 4ae7bf09d6c..122b9c618de 100644 --- a/sonar-batch/src/test/java/org/sonar/batch/scan/filesystem/ComponentIndexerTest.java +++ b/sonar-batch/src/test/java/org/sonar/batch/scan/filesystem/ComponentIndexerTest.java @@ -33,6 +33,7 @@ import org.sonar.api.batch.SonarIndex; import org.sonar.api.batch.fs.InputFile; import org.sonar.api.batch.fs.internal.DefaultFileSystem; import org.sonar.api.batch.fs.internal.DefaultInputFile; +import org.sonar.api.batch.fs.internal.DeprecatedDefaultInputFile; import org.sonar.api.config.Settings; import org.sonar.api.resources.AbstractLanguage; import org.sonar.api.resources.Java; @@ -160,8 +161,9 @@ public class ComponentIndexerTest { File javaFile1 = new File(baseDir, "src/main/java/foo/bar/Foo.java"); FileUtils.write(javaFile1, "\uFEFFpublic class Test", Charsets.UTF_8); - fs.add(new DefaultInputFile("src/main/java/foo/bar/Foo.java").setFile(javaFile1) + fs.add(new DeprecatedDefaultInputFile("src/main/java/foo/bar/Foo.java") .setPathRelativeToSourceDir("foo/bar/Foo.java") + .setFile(javaFile1) .setLanguage("java")); Languages languages = new Languages(Java.INSTANCE); ComponentIndexer indexer = new ComponentIndexer(project, languages, sonarIndex, settings, mock(ResourceKeyMigration.class)); @@ -184,9 +186,9 @@ public class ComponentIndexerTest { File javaFile1 = new File(baseDir, "src/main/java/foo/bar/Foo.java"); FileUtils.copyFile(getFile(testFile), javaFile1); - fs.add(new DefaultInputFile("src/main/java/foo/bar/Foo.java") - .setFile(javaFile1) + fs.add(new DeprecatedDefaultInputFile("src/main/java/foo/bar/Foo.java") .setPathRelativeToSourceDir("foo/bar/Foo.java") + .setFile(javaFile1) .setLanguage("java")); Languages languages = new Languages(Java.INSTANCE); ComponentIndexer indexer = new ComponentIndexer(project, languages, sonarIndex, settings, mock(ResourceKeyMigration.class)); @@ -210,8 +212,9 @@ public class ComponentIndexerTest { private DefaultInputFile newInputFile(String path, String content, String sourceRelativePath, String languageKey, boolean unitTest) throws IOException { File file = new File(baseDir, path); FileUtils.write(file, content); - return new DefaultInputFile(path).setFile(file) + return new DeprecatedDefaultInputFile(path) .setPathRelativeToSourceDir(sourceRelativePath) + .setFile(file) .setLanguage(languageKey) .setType(unitTest ? InputFile.Type.TEST : InputFile.Type.MAIN); } diff --git a/sonar-batch/src/test/java/org/sonar/batch/scan/filesystem/DefaultModuleFileSystemTest.java b/sonar-batch/src/test/java/org/sonar/batch/scan/filesystem/DefaultModuleFileSystemTest.java index 594a777095d..0de8ac80c56 100644 --- a/sonar-batch/src/test/java/org/sonar/batch/scan/filesystem/DefaultModuleFileSystemTest.java +++ b/sonar-batch/src/test/java/org/sonar/batch/scan/filesystem/DefaultModuleFileSystemTest.java @@ -26,8 +26,9 @@ import org.junit.rules.ExpectedException; import org.junit.rules.TemporaryFolder; import org.mockito.Mockito; import org.sonar.api.CoreProperties; +import org.sonar.api.batch.bootstrap.ProjectDefinition; import org.sonar.api.batch.fs.InputFile; -import org.sonar.api.batch.fs.internal.DefaultInputFile; +import org.sonar.api.batch.fs.internal.DeprecatedDefaultInputFile; import org.sonar.api.config.Settings; import org.sonar.api.resources.Project; @@ -58,13 +59,13 @@ public class DefaultModuleFileSystemTest { @Test public void test_equals_and_hashCode() throws Exception { - DefaultModuleFileSystem foo1 = new DefaultModuleFileSystem(moduleInputFileCache, + DefaultModuleFileSystem foo1 = new DefaultModuleFileSystem(moduleInputFileCache, ProjectDefinition.create(), new Project("foo"), settings, fileIndexer, initializer, componentIndexer); - DefaultModuleFileSystem foo2 = new DefaultModuleFileSystem(moduleInputFileCache, + DefaultModuleFileSystem foo2 = new DefaultModuleFileSystem(moduleInputFileCache, ProjectDefinition.create(), new Project("foo"), settings, fileIndexer, initializer, componentIndexer); - DefaultModuleFileSystem bar = new DefaultModuleFileSystem(moduleInputFileCache, + DefaultModuleFileSystem bar = new DefaultModuleFileSystem(moduleInputFileCache, ProjectDefinition.create(), new Project("bar"), settings, fileIndexer, initializer, componentIndexer); - DefaultModuleFileSystem branch = new DefaultModuleFileSystem(moduleInputFileCache, + DefaultModuleFileSystem branch = new DefaultModuleFileSystem(moduleInputFileCache, ProjectDefinition.create(), new Project("bar", "branch", "My project"), settings, fileIndexer, initializer, componentIndexer); assertThat(foo1.moduleKey()).isEqualTo("foo"); @@ -79,7 +80,7 @@ public class DefaultModuleFileSystemTest { @Test public void default_source_encoding() { - DefaultModuleFileSystem fs = new DefaultModuleFileSystem(moduleInputFileCache, + DefaultModuleFileSystem fs = new DefaultModuleFileSystem(moduleInputFileCache, ProjectDefinition.create(), new Project("foo"), settings, fileIndexer, initializer, componentIndexer); assertThat(fs.sourceCharset()).isEqualTo(Charset.defaultCharset()); @@ -89,7 +90,7 @@ public class DefaultModuleFileSystemTest { @Test public void source_encoding_is_set() { settings.setProperty(CoreProperties.ENCODING_PROPERTY, "Cp1124"); - DefaultModuleFileSystem fs = new DefaultModuleFileSystem(moduleInputFileCache, + DefaultModuleFileSystem fs = new DefaultModuleFileSystem(moduleInputFileCache, ProjectDefinition.create(), new Project("foo"), settings, fileIndexer, initializer, componentIndexer); assertThat(fs.encoding()).isEqualTo(Charset.forName("Cp1124")); @@ -115,7 +116,7 @@ public class DefaultModuleFileSystemTest { when(initializer.additionalSourceFiles()).thenReturn(Arrays.asList(additionalFile)); when(initializer.additionalTestFiles()).thenReturn(Arrays.asList(additionalTest)); - DefaultModuleFileSystem fs = new DefaultModuleFileSystem(moduleInputFileCache, + DefaultModuleFileSystem fs = new DefaultModuleFileSystem(moduleInputFileCache, ProjectDefinition.create(), new Project("foo"), settings, fileIndexer, initializer, componentIndexer); assertThat(fs.baseDir().getCanonicalPath()).isEqualTo(basedir.getCanonicalPath()); @@ -135,7 +136,7 @@ public class DefaultModuleFileSystemTest { when(initializer.workingDir()).thenReturn(basedir); when(initializer.sourceDirs()).thenReturn(Arrays.asList(new File(basedir, "src/main/java"))); - DefaultModuleFileSystem fs = new DefaultModuleFileSystem(moduleInputFileCache, + DefaultModuleFileSystem fs = new DefaultModuleFileSystem(moduleInputFileCache, ProjectDefinition.create(), new Project("foo"), settings, fileIndexer, initializer, componentIndexer); File existingDir = temp.newFolder("new_folder"); @@ -156,12 +157,12 @@ public class DefaultModuleFileSystemTest { @Test public void should_search_input_files() throws Exception { - DefaultModuleFileSystem fs = new DefaultModuleFileSystem(moduleInputFileCache, + DefaultModuleFileSystem fs = new DefaultModuleFileSystem(moduleInputFileCache, ProjectDefinition.create(), new Project("foo"), settings, fileIndexer, initializer, componentIndexer); File mainFile = temp.newFile(); - InputFile mainInput = new DefaultInputFile("Main.java").setFile(mainFile).setType(InputFile.Type.MAIN); - InputFile testInput = new DefaultInputFile("Test.java").setFile(temp.newFile()).setType(InputFile.Type.TEST); + InputFile mainInput = new DeprecatedDefaultInputFile("Main.java").setFile(mainFile).setType(InputFile.Type.MAIN); + InputFile testInput = new DeprecatedDefaultInputFile("Test.java").setFile(temp.newFile()).setType(InputFile.Type.TEST); when(moduleInputFileCache.inputFiles()).thenReturn(Lists.newArrayList(mainInput, testInput)); fs.index(); @@ -174,7 +175,7 @@ public class DefaultModuleFileSystemTest { @Test public void should_index() throws Exception { - DefaultModuleFileSystem fs = new DefaultModuleFileSystem(moduleInputFileCache, + DefaultModuleFileSystem fs = new DefaultModuleFileSystem(moduleInputFileCache, ProjectDefinition.create(), new Project("foo"), settings, fileIndexer, initializer, componentIndexer); verifyZeroInteractions(fileIndexer); diff --git a/sonar-batch/src/test/java/org/sonar/batch/scan/filesystem/DeprecatedFileFiltersTest.java b/sonar-batch/src/test/java/org/sonar/batch/scan/filesystem/DeprecatedFileFiltersTest.java index cc909035c1a..02f314e6b71 100644 --- a/sonar-batch/src/test/java/org/sonar/batch/scan/filesystem/DeprecatedFileFiltersTest.java +++ b/sonar-batch/src/test/java/org/sonar/batch/scan/filesystem/DeprecatedFileFiltersTest.java @@ -25,7 +25,7 @@ import org.junit.Test; import org.junit.rules.TemporaryFolder; import org.mockito.ArgumentCaptor; import org.sonar.api.batch.fs.InputFile; -import org.sonar.api.batch.fs.internal.DefaultInputFile; +import org.sonar.api.batch.fs.internal.DeprecatedDefaultInputFile; import org.sonar.api.scan.filesystem.FileSystemFilter; import org.sonar.api.scan.filesystem.FileType; @@ -34,7 +34,9 @@ import java.io.File; import static org.fest.assertions.Assertions.assertThat; import static org.mockito.Matchers.any; import static org.mockito.Matchers.eq; -import static org.mockito.Mockito.*; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; public class DeprecatedFileFiltersTest { @@ -47,7 +49,7 @@ public class DeprecatedFileFiltersTest { public void no_filters() throws Exception { DeprecatedFileFilters filters = new DeprecatedFileFilters(); - InputFile inputFile = new DefaultInputFile("src/main/java/Foo.java").setFile(temp.newFile()); + InputFile inputFile = new DeprecatedDefaultInputFile("src/main/java/Foo.java").setFile(temp.newFile()); assertThat(filters.accept(inputFile)).isTrue(); } @@ -57,11 +59,11 @@ public class DeprecatedFileFiltersTest { File basedir = temp.newFolder(); File file = temp.newFile(); - InputFile inputFile = new DefaultInputFile("src/main/java/Foo.java") - .setFile(file) - .setType(InputFile.Type.MAIN) + InputFile inputFile = new DeprecatedDefaultInputFile("src/main/java/Foo.java") .setSourceDirAbsolutePath(new File(basedir, "src/main/java").getAbsolutePath()) - .setPathRelativeToSourceDir("Foo.java"); + .setPathRelativeToSourceDir("Foo.java") + .setFile(file) + .setType(InputFile.Type.MAIN); when(filter.accept(eq(file), any(DeprecatedFileFilters.DeprecatedContext.class))).thenReturn(false); assertThat(filters.accept(inputFile)).isFalse(); diff --git a/sonar-batch/src/test/java/org/sonar/batch/scan/filesystem/ExclusionFiltersTest.java b/sonar-batch/src/test/java/org/sonar/batch/scan/filesystem/ExclusionFiltersTest.java index d84fdce8563..6255bd6629b 100644 --- a/sonar-batch/src/test/java/org/sonar/batch/scan/filesystem/ExclusionFiltersTest.java +++ b/sonar-batch/src/test/java/org/sonar/batch/scan/filesystem/ExclusionFiltersTest.java @@ -63,7 +63,6 @@ public class ExclusionFiltersTest { assertThat(filter.accept(inputFile, InputFile.Type.MAIN)).isFalse(); } - @Test public void match_at_least_one_inclusion() throws IOException { Settings settings = new Settings(); diff --git a/sonar-batch/src/test/java/org/sonar/batch/scan/filesystem/InputFileBuilderFactoryTest.java b/sonar-batch/src/test/java/org/sonar/batch/scan/filesystem/InputFileBuilderFactoryTest.java index 268f007e1b9..e38081b62be 100644 --- a/sonar-batch/src/test/java/org/sonar/batch/scan/filesystem/InputFileBuilderFactoryTest.java +++ b/sonar-batch/src/test/java/org/sonar/batch/scan/filesystem/InputFileBuilderFactoryTest.java @@ -21,6 +21,7 @@ package org.sonar.batch.scan.filesystem; import org.junit.Test; import org.mockito.Mockito; +import org.sonar.api.batch.bootstrap.ProjectDefinition; import org.sonar.api.resources.Project; import org.sonar.api.scan.filesystem.PathResolver; import org.sonar.batch.bootstrap.AnalysisMode; @@ -39,7 +40,7 @@ public class InputFileBuilderFactoryTest { AnalysisMode analysisMode = mock(AnalysisMode.class); InputFileBuilderFactory factory = new InputFileBuilderFactory( - project, pathResolver, langDetectionFactory, + project, ProjectDefinition.create(), pathResolver, langDetectionFactory, statusDetectionFactory, analysisMode); InputFileBuilder builder = factory.create(fs); diff --git a/sonar-batch/src/test/java/org/sonar/batch/scan/filesystem/InputFileBuilderTest.java b/sonar-batch/src/test/java/org/sonar/batch/scan/filesystem/InputFileBuilderTest.java index 5193101ff38..067f1f99d58 100644 --- a/sonar-batch/src/test/java/org/sonar/batch/scan/filesystem/InputFileBuilderTest.java +++ b/sonar-batch/src/test/java/org/sonar/batch/scan/filesystem/InputFileBuilderTest.java @@ -25,7 +25,7 @@ import org.junit.Rule; import org.junit.Test; import org.junit.rules.TemporaryFolder; import org.sonar.api.batch.fs.InputFile; -import org.sonar.api.batch.fs.internal.DefaultInputFile; +import org.sonar.api.batch.fs.internal.DeprecatedDefaultInputFile; import org.sonar.api.scan.filesystem.PathResolver; import org.sonar.api.utils.PathUtils; import org.sonar.batch.bootstrap.AnalysisMode; @@ -67,7 +67,7 @@ public class InputFileBuilderTest { InputFileBuilder builder = new InputFileBuilder("struts", new PathResolver(), langDetection, statusDetection, fs, analysisMode); - DefaultInputFile inputFile = builder.create(srcFile); + DeprecatedDefaultInputFile inputFile = builder.create(srcFile); inputFile = builder.complete(inputFile, InputFile.Type.MAIN); assertThat(inputFile.type()).isEqualTo(InputFile.Type.MAIN); @@ -93,7 +93,7 @@ public class InputFileBuilderTest { InputFileBuilder builder = new InputFileBuilder("struts", new PathResolver(), langDetection, statusDetection, fs, analysisMode); - DefaultInputFile inputFile = builder.create(srcFile); + DeprecatedDefaultInputFile inputFile = builder.create(srcFile); assertThat(inputFile).isNull(); } @@ -113,7 +113,7 @@ public class InputFileBuilderTest { InputFileBuilder builder = new InputFileBuilder("struts", new PathResolver(), langDetection, statusDetection, fs, analysisMode); - DefaultInputFile inputFile = builder.create(srcFile); + DeprecatedDefaultInputFile inputFile = builder.create(srcFile); inputFile = builder.complete(inputFile, InputFile.Type.MAIN); assertThat(inputFile).isNull(); @@ -140,7 +140,7 @@ public class InputFileBuilderTest { InputFileBuilder builder = new InputFileBuilder("struts", new PathResolver(), langDetection, statusDetection, fs, analysisMode); - DefaultInputFile inputFile = builder.create(srcFile); + DeprecatedDefaultInputFile inputFile = builder.create(srcFile); inputFile = builder.complete(inputFile, InputFile.Type.MAIN); assertThat(inputFile.pathRelativeToSourceDir()).isEqualTo("foo/Bar.java"); @@ -169,7 +169,7 @@ public class InputFileBuilderTest { InputFileBuilder builder = new InputFileBuilder("struts", new PathResolver(), langDetection, statusDetection, fs, analysisMode); - DefaultInputFile inputFile = builder.create(srcFile); + DeprecatedDefaultInputFile inputFile = builder.create(srcFile); inputFile = builder.complete(inputFile, InputFile.Type.MAIN); assertThat(inputFile.pathRelativeToSourceDir()).isEqualTo("foo/Bar.php"); diff --git a/sonar-batch/src/test/java/org/sonar/batch/scan/filesystem/InputFileCacheTest.java b/sonar-batch/src/test/java/org/sonar/batch/scan/filesystem/InputFileCacheTest.java index 1dedd839828..3da4eeba2c0 100644 --- a/sonar-batch/src/test/java/org/sonar/batch/scan/filesystem/InputFileCacheTest.java +++ b/sonar-batch/src/test/java/org/sonar/batch/scan/filesystem/InputFileCacheTest.java @@ -26,12 +26,11 @@ import org.junit.Test; import org.junit.rules.TemporaryFolder; import org.sonar.api.batch.fs.InputFile; import org.sonar.api.batch.fs.internal.DefaultInputFile; -import org.sonar.api.batch.fs.internal.RelativePathIndex; +import org.sonar.api.batch.fs.internal.DeprecatedDefaultInputFile; import org.sonar.batch.index.Caches; import org.sonar.batch.index.CachesTest; import static org.fest.assertions.Assertions.assertThat; -import static org.fest.assertions.Fail.fail; public class InputFileCacheTest { @@ -56,10 +55,9 @@ public class InputFileCacheTest { InputFileCache cache = new InputFileCache(caches); DefaultInputFile fooFile = new DefaultInputFile("src/main/java/Foo.java").setFile(temp.newFile("Foo.java")); cache.put("struts", fooFile); - cache.put("struts-core", new DefaultInputFile("src/main/java/Bar.java").setFile(temp.newFile("Bar.java"))); + cache.put("struts-core", new DeprecatedDefaultInputFile("src/main/java/Bar.java").setFile(temp.newFile("Bar.java"))); - // index by relative path is automatically fed - assertThat(cache.get("struts", RelativePathIndex.ID, "src/main/java/Foo.java").relativePath()) + assertThat(cache.get("struts", "src/main/java/Foo.java").relativePath()) .isEqualTo("src/main/java/Foo.java"); assertThat(cache.byModule("struts")).hasSize(1); @@ -78,23 +76,4 @@ public class InputFileCacheTest { assertThat(cache.all()).hasSize(1); } - @Test - public void only_relative_path_index_is_supported() throws Exception { - InputFileCache cache = new InputFileCache(caches); - DefaultInputFile input = new DefaultInputFile("src/main/java/Foo.java").setFile(temp.newFile("Foo.java")); - - try { - cache.index("struts", "unsupported-index", "index-value", input); - fail(); - } catch (UnsupportedOperationException e) { - assertThat(e).hasMessage("Only relative path index is supported yet"); - } - - try { - cache.get("struts", "unsupported-index", "index-value"); - fail(); - } catch (UnsupportedOperationException e) { - assertThat(e).hasMessage("Only relative path index is supported yet"); - } - } } diff --git a/sonar-batch/src/test/java/org/sonar/batch/scan/filesystem/LanguageDetectionFactoryTest.java b/sonar-batch/src/test/java/org/sonar/batch/scan/filesystem/LanguageDetectionFactoryTest.java index 35d09f661d6..cda01e16881 100644 --- a/sonar-batch/src/test/java/org/sonar/batch/scan/filesystem/LanguageDetectionFactoryTest.java +++ b/sonar-batch/src/test/java/org/sonar/batch/scan/filesystem/LanguageDetectionFactoryTest.java @@ -23,13 +23,15 @@ import org.junit.Test; import org.sonar.api.config.Settings; import org.sonar.api.resources.Java; import org.sonar.api.resources.Languages; +import org.sonar.batch.languages.DeprecatedLanguagesReferential; +import org.sonar.batch.languages.LanguagesReferential; import static org.fest.assertions.Assertions.assertThat; public class LanguageDetectionFactoryTest { @Test public void testCreate() throws Exception { - Languages languages = new Languages(Java.INSTANCE); + LanguagesReferential languages = new DeprecatedLanguagesReferential(new Languages(Java.INSTANCE)); LanguageDetectionFactory factory = new LanguageDetectionFactory(new Settings(), languages); LanguageDetection languageDetection = factory.create(); assertThat(languageDetection).isNotNull(); diff --git a/sonar-batch/src/test/java/org/sonar/batch/scan/filesystem/LanguageDetectionTest.java b/sonar-batch/src/test/java/org/sonar/batch/scan/filesystem/LanguageDetectionTest.java index f1500b541c2..7c6eb7ff4f2 100644 --- a/sonar-batch/src/test/java/org/sonar/batch/scan/filesystem/LanguageDetectionTest.java +++ b/sonar-batch/src/test/java/org/sonar/batch/scan/filesystem/LanguageDetectionTest.java @@ -30,6 +30,8 @@ import org.sonar.api.config.Settings; import org.sonar.api.resources.Language; import org.sonar.api.resources.Languages; import org.sonar.api.utils.MessageException; +import org.sonar.batch.languages.DeprecatedLanguagesReferential; +import org.sonar.batch.languages.LanguagesReferential; import java.io.File; import java.io.IOException; @@ -56,7 +58,7 @@ public class LanguageDetectionTest { @Test public void search_by_file_extension() throws Exception { - Languages languages = new Languages(new MockLanguage("java", "java", "jav"), new MockLanguage("cobol", "cbl", "cob")); + LanguagesReferential languages = new DeprecatedLanguagesReferential(new Languages(new MockLanguage("java", "java", "jav"), new MockLanguage("cobol", "cbl", "cob"))); LanguageDetection detection = new LanguageDetection(new Settings(), languages); assertThat(detection.language(newInputFile("Foo.java"))).isEqualTo("java"); @@ -74,13 +76,13 @@ public class LanguageDetectionTest { @Test public void should_not_fail_if_no_language() throws Exception { - LanguageDetection detection = spy(new LanguageDetection(new Settings(), new Languages())); + LanguageDetection detection = spy(new LanguageDetection(new Settings(), new DeprecatedLanguagesReferential(new Languages()))); assertThat(detection.language(newInputFile("Foo.java"))).isNull(); } @Test public void plugin_can_declare_a_file_extension_twice_for_case_sensitivity() throws Exception { - Languages languages = new Languages(new MockLanguage("abap", "abap", "ABAP")); + LanguagesReferential languages = new DeprecatedLanguagesReferential(new Languages(new MockLanguage("abap", "abap", "ABAP"))); LanguageDetection detection = new LanguageDetection(new Settings(), languages); assertThat(detection.language(newInputFile("abc.abap"))).isEqualTo("abap"); @@ -90,7 +92,7 @@ public class LanguageDetectionTest { public void language_with_no_extension() throws Exception { // abap does not declare any file extensions. // When analyzing an ABAP project, then all source files must be parsed. - Languages languages = new Languages(new MockLanguage("java", "java"), new MockLanguage("abap")); + LanguagesReferential languages = new DeprecatedLanguagesReferential(new Languages(new MockLanguage("java", "java"), new MockLanguage("abap"))); // No side-effect on non-ABAP projects LanguageDetection detection = new LanguageDetection(new Settings(), languages); @@ -108,7 +110,7 @@ public class LanguageDetectionTest { @Test public void force_language_using_deprecated_property() throws Exception { - Languages languages = new Languages(new MockLanguage("java", "java"), new MockLanguage("php", "php")); + LanguagesReferential languages = new DeprecatedLanguagesReferential(new Languages(new MockLanguage("java", "java"), new MockLanguage("php", "php"))); Settings settings = new Settings(); settings.setProperty(CoreProperties.PROJECT_LANGUAGE_PROPERTY, "java"); @@ -124,7 +126,7 @@ public class LanguageDetectionTest { thrown.expect(MessageException.class); thrown.expectMessage("No language is installed with key 'unknown'. Please update property 'sonar.language'"); - Languages languages = new Languages(new MockLanguage("java", "java"), new MockLanguage("php", "php")); + LanguagesReferential languages = new DeprecatedLanguagesReferential(new Languages(new MockLanguage("java", "java"), new MockLanguage("php", "php"))); Settings settings = new Settings(); settings.setProperty(CoreProperties.PROJECT_LANGUAGE_PROPERTY, "unknown"); new LanguageDetection(settings, languages); @@ -132,7 +134,7 @@ public class LanguageDetectionTest { @Test public void fail_if_conflicting_language_suffix() throws Exception { - Languages languages = new Languages(new MockLanguage("xml", "xhtml"), new MockLanguage("web", "xhtml")); + LanguagesReferential languages = new DeprecatedLanguagesReferential(new Languages(new MockLanguage("xml", "xhtml"), new MockLanguage("web", "xhtml"))); LanguageDetection detection = new LanguageDetection(new Settings(), languages); try { detection.language(newInputFile("abc.xhtml")); @@ -147,7 +149,7 @@ public class LanguageDetectionTest { @Test public void solve_conflict_using_filepattern() throws Exception { - Languages languages = new Languages(new MockLanguage("xml", "xhtml"), new MockLanguage("web", "xhtml")); + LanguagesReferential languages = new DeprecatedLanguagesReferential(new Languages(new MockLanguage("xml", "xhtml"), new MockLanguage("web", "xhtml"))); Settings settings = new Settings(); settings.setProperty("sonar.lang.patterns.xml", "xml/**"); @@ -159,7 +161,7 @@ public class LanguageDetectionTest { @Test public void fail_if_conflicting_filepattern() throws Exception { - Languages languages = new Languages(new MockLanguage("abap", "abap"), new MockLanguage("cobol", "cobol")); + LanguagesReferential languages = new DeprecatedLanguagesReferential(new Languages(new MockLanguage("abap", "abap"), new MockLanguage("cobol", "cobol"))); Settings settings = new Settings(); settings.setProperty("sonar.lang.patterns.abap", "*.abap,*.txt"); settings.setProperty("sonar.lang.patterns.cobol", "*.cobol,*.txt"); diff --git a/sonar-batch/src/test/java/org/sonar/batch/scan/report/JsonReportTest.java b/sonar-batch/src/test/java/org/sonar/batch/scan/report/JsonReportTest.java index c232c3344f5..6f4f306a0c5 100644 --- a/sonar-batch/src/test/java/org/sonar/batch/scan/report/JsonReportTest.java +++ b/sonar-batch/src/test/java/org/sonar/batch/scan/report/JsonReportTest.java @@ -29,7 +29,7 @@ import org.junit.rules.TemporaryFolder; import org.skyscreamer.jsonassert.JSONAssert; import org.sonar.api.batch.fs.InputFile; import org.sonar.api.batch.fs.internal.DefaultFileSystem; -import org.sonar.api.batch.fs.internal.DefaultInputFile; +import org.sonar.api.batch.fs.internal.DeprecatedDefaultInputFile; import org.sonar.api.config.Settings; import org.sonar.api.issue.Issue; import org.sonar.api.issue.internal.DefaultIssue; @@ -85,7 +85,7 @@ public class JsonReportTest { mode = mock(AnalysisMode.class); when(mode.isPreview()).thenReturn(true); userFinder = mock(UserFinder.class); - DefaultInputFile inputFile = new DefaultInputFile("src/main/java/org/apache/struts/Action.java"); + DeprecatedDefaultInputFile inputFile = new DeprecatedDefaultInputFile("src/main/java/org/apache/struts/Action.java"); inputFile.setKey("struts:src/main/java/org/apache/struts/Action.java"); inputFile.setStatus(InputFile.Status.CHANGED); InputFileCache fileCache = mock(InputFileCache.class); diff --git a/sonar-batch/src/test/resources/org/sonar/batch/medium/simple-project/sonar-project.properties b/sonar-batch/src/test/resources/org/sonar/batch/medium/simple-project/sonar-project.properties new file mode 100644 index 00000000000..69ccd8d1411 --- /dev/null +++ b/sonar-batch/src/test/resources/org/sonar/batch/medium/simple-project/sonar-project.properties @@ -0,0 +1,7 @@ +sonar.projectKey=com.foo.project +sonar.projectName=Foo Project +sonar.projectVersion=1.0-SNAPSHOT +sonar.projectDescription=Description of Foo Project + +sonar.sources=sources +sonar.libraries=libs/*.txt diff --git a/sonar-batch/src/test/resources/org/sonar/batch/medium/simple-project/sources/Fake.java b/sonar-batch/src/test/resources/org/sonar/batch/medium/simple-project/sources/Fake.java new file mode 100644 index 00000000000..b2e6462a3f9 --- /dev/null +++ b/sonar-batch/src/test/resources/org/sonar/batch/medium/simple-project/sources/Fake.java @@ -0,0 +1 @@ +Fake diff --git a/sonar-core/src/main/java/org/sonar/core/component/ComponentKeys.java b/sonar-core/src/main/java/org/sonar/core/component/ComponentKeys.java index 25d1a66d4b5..ed2e5abdd7f 100644 --- a/sonar-core/src/main/java/org/sonar/core/component/ComponentKeys.java +++ b/sonar-core/src/main/java/org/sonar/core/component/ComponentKeys.java @@ -20,6 +20,7 @@ package org.sonar.core.component; import org.apache.commons.lang.StringUtils; +import org.sonar.api.batch.fs.InputFile; import org.sonar.api.database.model.ResourceModel; import org.sonar.api.resources.Project; import org.sonar.api.resources.Resource; @@ -63,6 +64,15 @@ public final class ComponentKeys { return key; } + public static String createEffectiveKey(String projectKey, InputFile inputFile) { + // not a project nor a library + return new StringBuilder(ResourceModel.KEY_SIZE) + .append(projectKey) + .append(':') + .append(inputFile.relativePath()) + .toString(); + } + /** * <p>Test if given parameter is valid for a project/module. Valid format is:</p> * <ul> diff --git a/sonar-deprecated/src/main/java/org/sonar/api/batch/BatchExtensionDictionnary.java b/sonar-deprecated/src/main/java/org/sonar/api/batch/BatchExtensionDictionnary.java index bc1deda33bb..c951512e8f6 100644 --- a/sonar-deprecated/src/main/java/org/sonar/api/batch/BatchExtensionDictionnary.java +++ b/sonar-deprecated/src/main/java/org/sonar/api/batch/BatchExtensionDictionnary.java @@ -30,11 +30,13 @@ import org.sonar.api.platform.ComponentContainer; import org.sonar.api.resources.Project; import org.sonar.api.utils.AnnotationUtils; import org.sonar.api.utils.dag.DirectAcyclicGraph; +import org.sonar.batch.api.analyzer.Analyzer; import java.lang.annotation.Annotation; import java.lang.reflect.Array; import java.lang.reflect.Method; import java.lang.reflect.Modifier; +import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.List; @@ -66,7 +68,7 @@ public class BatchExtensionDictionnary { public Collection<MavenPluginHandler> selectMavenPluginHandlers(Project project) { List<DependsUponMavenPlugin> selectedExtensions = Lists.newArrayList(); - for (BatchExtension extension : getExtensions()) { + for (Object extension : getExtensions()) { if (ClassUtils.isAssignable(extension.getClass(), DependsUponMavenPlugin.class)) { selectedExtensions.add((DependsUponMavenPlugin) extension); } @@ -88,22 +90,23 @@ public class BatchExtensionDictionnary { return handlers; } - protected List<BatchExtension> getExtensions() { - List<BatchExtension> extensions = Lists.newArrayList(); + protected List<Object> getExtensions() { + List<Object> extensions = Lists.newArrayList(); completeBatchExtensions(componentContainer, extensions); return extensions; } - private static void completeBatchExtensions(ComponentContainer container, List<BatchExtension> extensions) { + private static void completeBatchExtensions(ComponentContainer container, List<Object> extensions) { if (container != null) { extensions.addAll(container.getComponentsByType(BatchExtension.class)); + extensions.addAll(container.getComponentsByType(org.sonar.batch.api.BatchExtension.class)); completeBatchExtensions(container.getParent(), extensions); } } private <T> List<T> getFilteredExtensions(Class<T> type, Project project) { List<T> result = Lists.newArrayList(); - for (BatchExtension extension : getExtensions()) { + for (Object extension : getExtensions()) { if (shouldKeep(type, extension, project)) { result.add((T) extension); } @@ -140,15 +143,25 @@ public class BatchExtensionDictionnary { /** * Extension dependencies */ - private <T> List getDependencies(T extension) { - return evaluateAnnotatedClasses(extension, DependsUpon.class); + private <T> List<Object> getDependencies(T extension) { + List<Object> result = new ArrayList<Object>(); + result.addAll(evaluateAnnotatedClasses(extension, DependsUpon.class)); + if (ClassUtils.isAssignable(extension.getClass(), Analyzer.class)) { + result.addAll(Arrays.asList(((Analyzer) extension).describe().dependsOn())); + } + return result; } /** * Objects that depend upon this extension. */ - public <T> List getDependents(T extension) { - return evaluateAnnotatedClasses(extension, DependedUpon.class); + public <T> List<Object> getDependents(T extension) { + List<Object> result = new ArrayList<Object>(); + result.addAll(evaluateAnnotatedClasses(extension, DependedUpon.class)); + if (ClassUtils.isAssignable(extension.getClass(), Analyzer.class)) { + result.addAll(Arrays.asList(((Analyzer) extension).describe().provides())); + } + return result; } private void completePhaseDependencies(DirectAcyclicGraph dag, Object extension) { @@ -163,7 +176,7 @@ public class BatchExtensionDictionnary { } } - protected List evaluateAnnotatedClasses(Object extension, Class<? extends Annotation> annotation) { + protected List<Object> evaluateAnnotatedClasses(Object extension, Class<? extends Annotation> annotation) { List<Object> results = Lists.newArrayList(); Class aClass = extension.getClass(); while (aClass != null) { diff --git a/sonar-plugin-api/pom.xml b/sonar-plugin-api/pom.xml index 09d12366d3f..165e9fc5eaf 100644 --- a/sonar-plugin-api/pom.xml +++ b/sonar-plugin-api/pom.xml @@ -29,6 +29,10 @@ </dependency> <dependency> <groupId>org.codehaus.sonar</groupId> + <artifactId>sonar-batch-plugin-api</artifactId> + </dependency> + <dependency> + <groupId>org.codehaus.sonar</groupId> <artifactId>sonar-colorizer</artifactId> </dependency> <dependency> diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/InstantiationStrategy.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/InstantiationStrategy.java index dcd215cd4c7..5cfea413fc5 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/batch/InstantiationStrategy.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/batch/InstantiationStrategy.java @@ -27,6 +27,7 @@ import java.lang.annotation.Target; /** * Define instantiation strategy of batch extensions. If an extension is not annotated, then default value * is {@link org.sonar.api.batch.InstantiationStrategy#PER_PROJECT}. + * @since 4.4 */ @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/SensorContext.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/SensorContext.java index bbe0af3c3d2..ccdec2dbf2f 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/batch/SensorContext.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/batch/SensorContext.java @@ -30,6 +30,7 @@ import org.sonar.api.rules.Violation; import javax.annotation.CheckForNull; +import java.io.Serializable; import java.util.Collection; import java.util.Date; import java.util.List; @@ -104,7 +105,7 @@ public interface SensorContext { /** * Find a project measure */ - Measure getMeasure(Metric metric); + <G extends Serializable> Measure<G> getMeasure(Metric<G> metric); /** * All measures of the project. Never return null. @@ -126,7 +127,7 @@ public interface SensorContext { /** * Find a measure for this project */ - Measure getMeasure(Resource resource, Metric metric); + <G extends Serializable> Measure<G> getMeasure(Resource resource, Metric<G> metric); /** * Key is updated when saving the resource. diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/SonarIndex.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/SonarIndex.java index 4eb61021ca5..1be238923ae 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/batch/SonarIndex.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/batch/SonarIndex.java @@ -22,7 +22,6 @@ package org.sonar.api.batch; import org.sonar.api.design.Dependency; import org.sonar.api.measures.Measure; import org.sonar.api.measures.MeasuresFilter; -import org.sonar.api.measures.Metric; import org.sonar.api.resources.Project; import org.sonar.api.resources.ProjectLink; import org.sonar.api.resources.Resource; @@ -121,7 +120,7 @@ public abstract class SonarIndex implements DirectedGraphAccessor<Resource, Depe public abstract Resource addResource(Resource resource); @CheckForNull - public abstract Measure getMeasure(Resource resource, Metric metric); + public abstract Measure getMeasure(Resource resource, org.sonar.batch.api.measures.Metric<?> metric); @CheckForNull public abstract <M> M getMeasures(Resource resource, MeasuresFilter<M> filter); diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/DeprecatedDefaultInputFile.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/DeprecatedDefaultInputFile.java new file mode 100644 index 00000000000..fad4e6d43ce --- /dev/null +++ b/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/DeprecatedDefaultInputFile.java @@ -0,0 +1,116 @@ +/* + * 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.api.batch.fs.internal; + +import org.sonar.batch.api.internal.FilenameUtils; + +import java.io.BufferedInputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.InputStream; + +/** + * @since 4.2 + */ +public class DeprecatedDefaultInputFile extends DefaultInputFile implements org.sonar.api.resources.InputFile { + + private String basedir; + private String deprecatedKey; + private String sourceDirAbsolutePath; + private String pathRelativeToSourceDir; + + public DeprecatedDefaultInputFile(String relativePath) { + super(relativePath); + } + + /** + * @deprecated in 4.2. Replaced by {@link org.sonar.api.batch.fs.FileSystem#baseDir()} + */ + @Deprecated + @Override + public File getFileBaseDir() { + return new File(basedir); + } + + public void setBasedir(File basedir) { + this.basedir = FilenameUtils.normalize(basedir.getAbsolutePath()); + } + + /** + * @deprecated in 4.2. Use {@link #file()} + */ + @Deprecated + @Override + public File getFile() { + return file(); + } + + /** + * @deprecated in 4.2. Use {@link #relativePath()} + */ + @Deprecated + @Override + public String getRelativePath() { + return pathRelativeToSourceDir; + } + + /** + * Key used before version 4.2. It can be different than {@link #key} on Java files. + */ + public String deprecatedKey() { + return deprecatedKey; + } + + public DeprecatedDefaultInputFile setDeprecatedKey(String s) { + this.deprecatedKey = s; + return this; + } + + /** + * Used only for backward-compatibility. Meaningless since version 4.2. + */ + public String sourceDirAbsolutePath() { + return sourceDirAbsolutePath; + } + + public DeprecatedDefaultInputFile setSourceDirAbsolutePath(String s) { + this.sourceDirAbsolutePath = FilenameUtils.normalize(s); + return this; + } + + /** + * Used only for backward-compatibility. Meaningless since version 4.2. + */ + + public String pathRelativeToSourceDir() { + return pathRelativeToSourceDir; + } + + public DeprecatedDefaultInputFile setPathRelativeToSourceDir(String s) { + this.pathRelativeToSourceDir = FilenameUtils.normalize(s); + return this; + } + + @Override + public InputStream getInputStream() throws FileNotFoundException { + return new BufferedInputStream(new FileInputStream(file())); + } +} diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/measures/CoreMetrics.java b/sonar-plugin-api/src/main/java/org/sonar/api/measures/CoreMetrics.java index 025f27fc511..f94a4345af2 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/measures/CoreMetrics.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/measures/CoreMetrics.java @@ -61,7 +61,7 @@ public final class CoreMetrics { public static String DOMAIN_TECHNICAL_DEBT = "Technical Debt"; public static final String LINES_KEY = "lines"; - public static final Metric LINES = new Metric.Builder(LINES_KEY, "Lines", Metric.ValueType.INT) + public static final Metric<Integer> LINES = new Metric.Builder(LINES_KEY, "Lines", Metric.ValueType.INT) .setDescription("Lines") .setDirection(Metric.DIRECTION_WORST) .setQualitative(false) diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/measures/Measure.java b/sonar-plugin-api/src/main/java/org/sonar/api/measures/Measure.java index 3d38e52aea8..b6a90add399 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/measures/Measure.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/measures/Measure.java @@ -38,7 +38,7 @@ import java.util.Date; * * @since 1.10 */ -public class Measure implements Serializable { +public class Measure<G extends Serializable> implements Serializable { private static final String INDEX_SHOULD_BE_IN_RANGE_FROM_1_TO_5 = "Index should be in range from 1 to 5"; protected static final int MAX_TEXT_SIZE = 96; @@ -49,7 +49,7 @@ public class Measure implements Serializable { public static final int DEFAULT_PRECISION = 1; protected String metricKey; - protected Metric metric; + protected Metric<G> metric; protected Double value; protected String data; protected String description; @@ -238,6 +238,38 @@ public class Measure implements Serializable { } /** + * For internal use. + */ + public G value() { + switch (getMetric().getType()) { + case BOOL: + return (G) Boolean.valueOf(1.0 == value); + case INT: + case MILLISEC: + return (G) Integer.valueOf(value.intValue()); + case FLOAT: + case PERCENT: + case RATING: + return (G) value; + case STRING: + case LEVEL: + case DATA: + case DISTRIB: + return (G) getData(); + case WORK_DUR: + return (G) Long.valueOf(value.longValue()); + default: + if (getMetric().isNumericType()) { + return (G) getValue(); + } else if (getMetric().isDataType()) { + return (G) getData(); + } else { + throw new UnsupportedOperationException("Unsupported type :" + getMetric().getType()); + } + } + } + + /** * @return the value of the measure as an int */ public Integer getIntValue() { @@ -692,4 +724,5 @@ public class Measure implements Serializable { public String toString() { return ReflectionToStringBuilder.toString(this); } + } diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/measures/MeasuresFilters.java b/sonar-plugin-api/src/main/java/org/sonar/api/measures/MeasuresFilters.java index f11a225e668..c51973b79b7 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/measures/MeasuresFilters.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/measures/MeasuresFilters.java @@ -51,8 +51,8 @@ public final class MeasuresFilters { }; } - public static MeasuresFilter<Measure> metric(final Metric metric) { - return metric(metric.getKey()); + public static MeasuresFilter<Measure> metric(final org.sonar.batch.api.measures.Metric<?> metric) { + return metric(metric.key()); } public static MeasuresFilter<Measure> metric(final String metricKey) { diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/measures/Metric.java b/sonar-plugin-api/src/main/java/org/sonar/api/measures/Metric.java index bc809c88884..9ea0288fd0a 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/measures/Metric.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/measures/Metric.java @@ -46,7 +46,7 @@ import java.io.Serializable; @Table(name = "metrics") @Entity(name = "Metric") @InstantiationStrategy(InstantiationStrategy.PER_BATCH) -public class Metric implements ServerExtension, BatchExtension, Serializable { +public class Metric<G extends Serializable> implements ServerExtension, BatchExtension, Serializable, org.sonar.batch.api.measures.Metric<G> { /** * A metric bigger value means a degradation @@ -62,7 +62,28 @@ public class Metric implements ServerExtension, BatchExtension, Serializable { public static final int DIRECTION_NONE = 0; public enum ValueType { - INT, FLOAT, PERCENT, BOOL, STRING, MILLISEC, DATA, LEVEL, DISTRIB, RATING, WORK_DUR + INT(Integer.class), + FLOAT(Double.class), + PERCENT(Double.class), + BOOL(Boolean.class), + STRING(String.class), + MILLISEC(Integer.class), + DATA(String.class), + LEVEL(String.class), + DISTRIB(String.class), + RATING(String.class), + WORK_DUR(Long.class); + + private final Class measureJavaType; + + private ValueType(Class measureJavaType) { + this.measureJavaType = measureJavaType; + } + + private Class measureJavaType() { + return measureJavaType; + } + } public enum Level { @@ -211,10 +232,8 @@ public class Metric implements ServerExtension, BatchExtension, Serializable { * @param qualitative whether the metric is qualitative * @param domain the metric domain * @param userManaged whether the metric is user managed - * @deprecated since 2.7 use the {@link Builder} factory. */ - @Deprecated - public Metric(String key, String name, String description, ValueType type, Integer direction, Boolean qualitative, @Nullable String domain, + private Metric(String key, String name, String description, ValueType type, Integer direction, Boolean qualitative, @Nullable String domain, boolean userManaged) { this.key = key; this.description = description; @@ -232,41 +251,6 @@ public class Metric implements ServerExtension, BatchExtension, Serializable { } /** - * Creates a fully qualified metric. This defaults some values: - * <ul> - * <li>origin : Origin.JAV</li> - * <li>enabled : true</li> - * <li>userManaged : true</li> - * </ul> - * - * @param key the metric key - * @param name the metric name - * @param type the metric type - * @param direction the metric direction - * @param qualitative whether the metric is qualitative - * @param domain the metric domain - * @param formula the metric formula - * @deprecated since 2.7 use the {@link Builder} factory. - */ - @Deprecated - public Metric(String key, String name, ValueType type, Integer direction, Boolean qualitative, String domain, Formula formula) { - this.key = key; - this.name = name; - this.type = type; - this.direction = direction; - this.domain = domain; - this.qualitative = qualitative; - this.origin = Origin.JAV; - this.enabled = true; - this.userManaged = false; - this.formula = formula; - if (ValueType.PERCENT.equals(this.type)) { - this.bestValue = (direction == DIRECTION_BETTER ? 100.0 : 0.0); - this.worstValue = (direction == DIRECTION_BETTER ? 0.0 : 100.0); - } - } - - /** * For internal use only */ public Integer getId() { @@ -803,12 +787,22 @@ public class Metric implements ServerExtension, BatchExtension, Serializable { * * @return a new {@link Metric} object */ - public Metric create() { + public <G extends Serializable> Metric<G> create() { if (ValueType.PERCENT.equals(this.type)) { this.bestValue = (direction == DIRECTION_BETTER ? 100.0 : 0.0); this.worstValue = (direction == DIRECTION_BETTER ? 0.0 : 100.0); } - return new Metric(this); + return new Metric<G>(this); } } + + @Override + public String key() { + return getKey(); + } + + @Override + public Class<G> type() { + return getType().measureJavaType(); + } } diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/resources/ProjectFileSystem.java b/sonar-plugin-api/src/main/java/org/sonar/api/resources/ProjectFileSystem.java index 41ed8f6ff8a..944b1278a6a 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/resources/ProjectFileSystem.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/resources/ProjectFileSystem.java @@ -28,7 +28,7 @@ import java.util.List; /** * @since 1.10 - * @deprecated replaced by {@link org.sonar.api.scan.filesystem.ModuleFileSystem} in 3.5 + * @deprecated since 3.5 replaced by {@link org.sonar.api.scan.filesystem.ModuleFileSystem} */ @Deprecated public interface ProjectFileSystem extends BatchComponent { diff --git a/sonar-plugin-api/src/test/java/org/sonar/api/batch/AbstractSumChildrenDecoratorTest.java b/sonar-plugin-api/src/test/java/org/sonar/api/batch/AbstractSumChildrenDecoratorTest.java index 67baa3cbfe0..9f6de72da31 100644 --- a/sonar-plugin-api/src/test/java/org/sonar/api/batch/AbstractSumChildrenDecoratorTest.java +++ b/sonar-plugin-api/src/test/java/org/sonar/api/batch/AbstractSumChildrenDecoratorTest.java @@ -41,8 +41,8 @@ public class AbstractSumChildrenDecoratorTest { public void sumChildren() { DecoratorContext context = mock(DecoratorContext.class); when(context.getChildrenMeasures(CoreMetrics.LINES)).thenReturn(Arrays.<Measure>asList( - new Measure(CoreMetrics.LINES, 100.0), - new Measure(CoreMetrics.LINES, 50.0))); + new Measure(CoreMetrics.LINES, 100.0), + new Measure(CoreMetrics.LINES, 50.0))); create(false).decorate(null, context); @@ -75,7 +75,7 @@ public class AbstractSumChildrenDecoratorTest { @Override @DependedUpon public List<Metric> generatesMetrics() { - return Arrays.asList(CoreMetrics.LINES); + return Arrays.<Metric>asList(CoreMetrics.LINES); } @Override diff --git a/sonar-plugin-api/src/test/java/org/sonar/api/batch/TimeMachineQueryTest.java b/sonar-plugin-api/src/test/java/org/sonar/api/batch/TimeMachineQueryTest.java index 7831cb2975e..1b4908c728e 100644 --- a/sonar-plugin-api/src/test/java/org/sonar/api/batch/TimeMachineQueryTest.java +++ b/sonar-plugin-api/src/test/java/org/sonar/api/batch/TimeMachineQueryTest.java @@ -21,6 +21,7 @@ package org.sonar.api.batch; import org.junit.Test; import org.sonar.api.measures.CoreMetrics; +import org.sonar.api.measures.Metric; import java.util.Arrays; @@ -30,7 +31,7 @@ public class TimeMachineQueryTest { @Test public void setNullMetrics() { - TimeMachineQuery query = new TimeMachineQuery(null).setMetrics(Arrays.asList(CoreMetrics.LINES)); + TimeMachineQuery query = new TimeMachineQuery(null).setMetrics(Arrays.<Metric>asList(CoreMetrics.LINES)); assertThat(query.getMetrics()).contains(CoreMetrics.LINES); query.unsetMetrics(); diff --git a/sonar-plugin-api/src/test/java/org/sonar/api/batch/fs/internal/DeprecatedDefaultInputFileTest.java b/sonar-plugin-api/src/test/java/org/sonar/api/batch/fs/internal/DeprecatedDefaultInputFileTest.java new file mode 100644 index 00000000000..321efa8b681 --- /dev/null +++ b/sonar-plugin-api/src/test/java/org/sonar/api/batch/fs/internal/DeprecatedDefaultInputFileTest.java @@ -0,0 +1,84 @@ +package org.sonar.api.batch.fs.internal; + +/* + * 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. + */ + +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; +import org.sonar.api.batch.fs.InputFile; + +import java.io.File; + +import static org.fest.assertions.Assertions.assertThat; + +public class DeprecatedDefaultInputFileTest { + + @Rule + public TemporaryFolder temp = new TemporaryFolder(); + + @Test + public void test() throws Exception { + DeprecatedDefaultInputFile inputFile = (DeprecatedDefaultInputFile) new DeprecatedDefaultInputFile("src/Foo.php") + .setPathRelativeToSourceDir("Foo.php") + .setDeprecatedKey("deprecated") + .setFile(temp.newFile("Foo.php")) + .setKey("ABCDE") + .setHash("1234") + .setLines(42) + .setLanguage("php") + .setStatus(InputFile.Status.ADDED) + .setType(InputFile.Type.TEST); + + assertThat(inputFile.relativePath()).isEqualTo("src/Foo.php"); + // deprecated method is different -> path relative to source dir + assertThat(inputFile.getRelativePath()).isEqualTo("Foo.php"); + assertThat(new File(inputFile.relativePath())).isRelative(); + assertThat(inputFile.absolutePath()).endsWith("Foo.php"); + assertThat(new File(inputFile.absolutePath())).isAbsolute(); + assertThat(inputFile.language()).isEqualTo("php"); + assertThat(inputFile.status()).isEqualTo(InputFile.Status.ADDED); + assertThat(inputFile.type()).isEqualTo(InputFile.Type.TEST); + assertThat(inputFile.lines()).isEqualTo(42); + assertThat(inputFile.hash()).isEqualTo("1234"); + } + + @Test + public void test_equals_and_hashcode() throws Exception { + DefaultInputFile f1 = new DefaultInputFile("src/Foo.php"); + DefaultInputFile f1a = new DefaultInputFile("src/Foo.php"); + DefaultInputFile f2 = new DefaultInputFile("src/Bar.php"); + + assertThat(f1).isEqualTo(f1); + assertThat(f1).isEqualTo(f1a); + assertThat(f1).isNotEqualTo(f2); + assertThat(f1.equals("foo")).isFalse(); + assertThat(f1.equals(null)).isFalse(); + + assertThat(f1.hashCode()).isEqualTo(f1.hashCode()); + assertThat(f1.hashCode()).isEqualTo(f1a.hashCode()); + } + + @Test + public void test_toString() throws Exception { + DefaultInputFile file = new DefaultInputFile("src/Foo.php").setAbsolutePath("/path/to/src/Foo.php"); + assertThat(file.toString()).isEqualTo("[relative=src/Foo.php, abs=/path/to/src/Foo.php]"); + } +} diff --git a/sonar-server/src/test/java/org/sonar/server/startup/RegisterMetricsTest.java b/sonar-server/src/test/java/org/sonar/server/startup/RegisterMetricsTest.java index 2fa115a8626..9e375adfd29 100644 --- a/sonar-server/src/test/java/org/sonar/server/startup/RegisterMetricsTest.java +++ b/sonar-server/src/test/java/org/sonar/server/startup/RegisterMetricsTest.java @@ -19,6 +19,7 @@ */ package org.sonar.server.startup; +import com.google.common.collect.Lists; import org.junit.Test; import org.sonar.api.measures.CoreMetrics; import org.sonar.api.measures.Metric; @@ -30,7 +31,6 @@ import org.sonar.jpa.test.AbstractDbUnitTestCase; import java.util.Arrays; import java.util.List; -import static com.google.common.collect.Lists.newArrayList; import static org.fest.assertions.Assertions.assertThat; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; @@ -67,7 +67,7 @@ public class RegisterMetricsTest extends AbstractDbUnitTestCase { setupData("shouldUpdateIfAlreadyExists"); RegisterMetrics synchronizer = new RegisterMetrics(new MeasuresDao(getSession()), mock(QualityGateConditionDao.class), new Metrics[0]); - synchronizer.register(newArrayList(new Metric.Builder("key", "new short name", Metric.ValueType.FLOAT) + synchronizer.register(Lists.<Metric>newArrayList(new Metric.Builder("key", "new short name", Metric.ValueType.FLOAT) .setDescription("new description") .setDirection(-1) .setQualitative(true) @@ -81,7 +81,7 @@ public class RegisterMetricsTest extends AbstractDbUnitTestCase { @Test public void shouldAddUserManagesMetric() { Metrics metrics = mock(Metrics.class); - when(metrics.getMetrics()).thenReturn(newArrayList(new Metric.Builder("key", "new short name", Metric.ValueType.FLOAT) + when(metrics.getMetrics()).thenReturn(Lists.<Metric>newArrayList(new Metric.Builder("key", "new short name", Metric.ValueType.FLOAT) .setDescription("new description") .setDirection(-1) .setQualitative(true) @@ -90,7 +90,7 @@ public class RegisterMetricsTest extends AbstractDbUnitTestCase { .create())); MeasuresDao measuresDao = new MeasuresDao(getSession()); - RegisterMetrics loader = new RegisterMetrics(measuresDao, mock(QualityGateConditionDao.class), new Metrics[]{metrics}); + RegisterMetrics loader = new RegisterMetrics(measuresDao, mock(QualityGateConditionDao.class), new Metrics[] {metrics}); List<Metric> result = loader.getMetricsRepositories(); assertThat(result).hasSize(1); @@ -101,7 +101,7 @@ public class RegisterMetricsTest extends AbstractDbUnitTestCase { setupData("shouldNotUpdateUserManagesMetricIfAlreadyExists"); Metrics metrics = mock(Metrics.class); - when(metrics.getMetrics()).thenReturn(newArrayList(new Metric.Builder("key", "new short name", Metric.ValueType.FLOAT) + when(metrics.getMetrics()).thenReturn(Lists.<Metric>newArrayList(new Metric.Builder("key", "new short name", Metric.ValueType.FLOAT) .setDescription("new description") .setDirection(-1) .setQualitative(true) @@ -110,7 +110,7 @@ public class RegisterMetricsTest extends AbstractDbUnitTestCase { .create())); MeasuresDao measuresDao = new MeasuresDao(getSession()); - RegisterMetrics loader = new RegisterMetrics(measuresDao, mock(QualityGateConditionDao.class), new Metrics[]{metrics}); + RegisterMetrics loader = new RegisterMetrics(measuresDao, mock(QualityGateConditionDao.class), new Metrics[] {metrics}); List<Metric> result = loader.getMetricsRepositories(); assertThat(result).isEmpty(); |