@@ -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()); | |||
} | |||
} |
@@ -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); | |||
} |
@@ -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); |
@@ -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; |
@@ -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); | |||
@@ -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> | |||
@@ -574,6 +575,11 @@ | |||
<artifactId>sonar-plugin-api</artifactId> | |||
<version>${project.version}</version> | |||
</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> |
@@ -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> |
@@ -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); |
@@ -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; |
@@ -33,7 +33,7 @@ public interface InputFile extends Serializable { | |||
MAIN, TEST | |||
} | |||
/** | |||
/** | |||
* Status regarding previous analysis | |||
*/ | |||
enum Status { |
@@ -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 |
@@ -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 |
@@ -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)); | |||
} |
@@ -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); | |||
} | |||
} | |||
} |
@@ -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 + "]"; | |||
} | |||
} | |||
@@ -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 { | |||
@@ -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 |
@@ -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()); | |||
} | |||
} |
@@ -21,3 +21,4 @@ | |||
package org.sonar.api.batch.fs.internal; | |||
import javax.annotation.ParametersAreNonnullByDefault; | |||
@@ -21,3 +21,4 @@ | |||
package org.sonar.api.batch.fs; | |||
import javax.annotation.ParametersAreNonnullByDefault; | |||
@@ -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 { | |||
} |
@@ -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 { | |||
} |
@@ -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(); | |||
} |
@@ -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); | |||
} |
@@ -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); | |||
} |
@@ -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); | |||
} | |||
} | |||
} |
@@ -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; | |||
} | |||
} | |||
} |
@@ -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; |
@@ -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 + "]"; | |||
} | |||
} |
@@ -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; |
@@ -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; |
@@ -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); | |||
} | |||
} |
@@ -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(); | |||
} | |||
} |
@@ -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(); | |||
} | |||
} |
@@ -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; | |||
} | |||
} |
@@ -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; |
@@ -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); | |||
} | |||
} |
@@ -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; |
@@ -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(); | |||
} |
@@ -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; |
@@ -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; | |||
} | |||
} |
@@ -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); | |||
} | |||
} |
@@ -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; |
@@ -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(); | |||
} | |||
} |
@@ -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(); | |||
} | |||
@@ -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(); |
@@ -40,6 +40,10 @@ | |||
<groupId>org.codehaus.sonar</groupId> | |||
<artifactId>sonar-java-api</artifactId> | |||
</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> |
@@ -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); | |||
} | |||
@@ -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) { |
@@ -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; | |||
} |
@@ -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); | |||
} |
@@ -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(); | |||
} | |||
} |
@@ -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 { |
@@ -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() { |
@@ -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; | |||
} | |||
@@ -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)); | |||
} | |||
@@ -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; |
@@ -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); | |||
} |
@@ -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()); | |||
} |
@@ -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); |
@@ -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) { |
@@ -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; | |||
} | |||
} |
@@ -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(); | |||
} |
@@ -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; |
@@ -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); |
@@ -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()); |
@@ -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() { |
@@ -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(); | |||
} | |||
@@ -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); |
@@ -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 { | |||
@@ -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()); |
@@ -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); | |||
} | |||
@@ -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()); | |||
} | |||
} |
@@ -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; | |||
} | |||
} |
@@ -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); | |||
} |
@@ -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; |
@@ -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); | |||
} | |||
} | |||
} |
@@ -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)) { |
@@ -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); | |||
} | |||
}); | |||
} |
@@ -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, |
@@ -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) |
@@ -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); | |||
} | |||
} |
@@ -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()); | |||
} | |||
} | |||
} |
@@ -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(); | |||
} |
@@ -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) { |
@@ -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 |
@@ -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); |
@@ -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()); |