@@ -29,6 +29,7 @@ import org.sonar.xoo.lang.TestCaseSensor; | |||
import org.sonar.xoo.lang.XooTokenizerSensor; | |||
import org.sonar.xoo.rule.ChecksSensor; | |||
import org.sonar.xoo.rule.CreateIssueByInternalKeySensor; | |||
import org.sonar.xoo.rule.DeprecatedResourceApiSensor; | |||
import org.sonar.xoo.rule.OneIssueOnDirPerFileSensor; | |||
import org.sonar.xoo.rule.OneIssuePerLineSensor; | |||
import org.sonar.xoo.rule.RandomAccessSensor; | |||
@@ -76,6 +77,7 @@ public class XooPlugin extends SonarPlugin { | |||
DependencySensor.class, | |||
ChecksSensor.class, | |||
RandomAccessSensor.class, | |||
DeprecatedResourceApiSensor.class, | |||
OneIssuePerLineSensor.class, | |||
OneIssueOnDirPerFileSensor.class, |
@@ -0,0 +1,85 @@ | |||
/* | |||
* 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.xoo.rule; | |||
import org.sonar.api.batch.Sensor; | |||
import org.sonar.api.batch.rule.ActiveRules; | |||
import org.sonar.api.component.ResourcePerspectives; | |||
import org.sonar.api.issue.Issuable; | |||
import org.sonar.api.resources.Directory; | |||
import org.sonar.api.resources.Project; | |||
import org.sonar.api.rule.RuleKey; | |||
import org.sonar.api.scan.filesystem.FileQuery; | |||
import org.sonar.api.scan.filesystem.ModuleFileSystem; | |||
import org.sonar.api.scan.filesystem.PathResolver; | |||
import org.sonar.xoo.Xoo; | |||
import java.io.File; | |||
public class DeprecatedResourceApiSensor implements Sensor { | |||
public static final String RULE_KEY = "DeprecatedResourceApi"; | |||
private final ModuleFileSystem fileSystem; | |||
private final ResourcePerspectives perspectives; | |||
private final ActiveRules activeRules; | |||
public DeprecatedResourceApiSensor(ModuleFileSystem fileSystem, ResourcePerspectives perspectives, ActiveRules activeRules) { | |||
this.fileSystem = fileSystem; | |||
this.perspectives = perspectives; | |||
this.activeRules = activeRules; | |||
} | |||
@Override | |||
public boolean shouldExecuteOnProject(Project project) { | |||
return !fileSystem.files(FileQuery.onMain().onLanguage(Xoo.KEY)).isEmpty() && activeRules.find(RuleKey.of(XooRulesDefinition.XOO_REPOSITORY, RULE_KEY)) != null; | |||
} | |||
@Override | |||
public void analyse(Project module, org.sonar.api.batch.SensorContext context) { | |||
createIssueOnDir(new Directory("")); | |||
File src = module.getFileSystem().getSourceDirs().get(0); | |||
for (File f : fileSystem.files(FileQuery.onMain().onLanguage(Xoo.KEY))) { | |||
String relativePathFromSourceDir = new PathResolver().relativePath(src, f); | |||
org.sonar.api.resources.File sonarFile = new org.sonar.api.resources.File(relativePathFromSourceDir); | |||
Issuable issuable = perspectives.as(Issuable.class, sonarFile); | |||
issuable.addIssue(issuable.newIssueBuilder() | |||
.ruleKey(RuleKey.of(XooRulesDefinition.XOO_REPOSITORY, RULE_KEY)) | |||
.message("Issue created using deprecated API") | |||
.line(1) | |||
.build()); | |||
sonarFile = context.getResource(sonarFile); | |||
Directory parent = sonarFile.getParent(); | |||
createIssueOnDir(parent); | |||
} | |||
} | |||
private Directory createIssueOnDir(Directory dir) { | |||
Issuable issuable = perspectives.as(Issuable.class, dir); | |||
issuable.addIssue(issuable.newIssueBuilder() | |||
.ruleKey(RuleKey.of(XooRulesDefinition.XOO_REPOSITORY, RULE_KEY)) | |||
.message("Issue created using deprecated API") | |||
.build()); | |||
return dir; | |||
} | |||
} |
@@ -27,6 +27,6 @@ public class XooPluginTest { | |||
@Test | |||
public void provide_extensions() { | |||
assertThat(new XooPlugin().getExtensions()).hasSize(20); | |||
assertThat(new XooPlugin().getExtensions()).hasSize(21); | |||
} | |||
} |
@@ -38,6 +38,7 @@ import org.sonar.api.measures.CoreMetrics; | |||
import org.sonar.api.measures.Measure; | |||
import org.sonar.api.measures.MeasuresFilter; | |||
import org.sonar.api.measures.MeasuresFilters; | |||
import org.sonar.api.resources.Directory; | |||
import org.sonar.api.resources.File; | |||
import org.sonar.api.resources.Project; | |||
import org.sonar.api.resources.ProjectLink; | |||
@@ -615,6 +616,38 @@ public class DefaultIndex extends SonarIndex { | |||
if (StringUtils.isNotBlank(reference.getKey())) { | |||
return buckets.get(reference); | |||
} | |||
String relativePathFromSourceDir = null; | |||
boolean isTest = false; | |||
boolean isDir = false; | |||
if (reference instanceof File) { | |||
File referenceFile = (File) reference; | |||
isTest = Qualifiers.UNIT_TEST_FILE.equals(referenceFile.getQualifier()); | |||
relativePathFromSourceDir = referenceFile.relativePathFromSourceDir(); | |||
} else if (reference instanceof Directory) { | |||
isDir = true; | |||
Directory referenceDir = (Directory) reference; | |||
relativePathFromSourceDir = referenceDir.relativePathFromSourceDir(); | |||
if (Directory.ROOT.equals(relativePathFromSourceDir)) { | |||
relativePathFromSourceDir = ""; | |||
} | |||
} | |||
if (relativePathFromSourceDir != null) { | |||
// Resolve using deprecated key | |||
List<java.io.File> dirs; | |||
if (isTest) { | |||
dirs = getProject().getFileSystem().getTestDirs(); | |||
} else { | |||
dirs = getProject().getFileSystem().getSourceDirs(); | |||
} | |||
for (java.io.File src : dirs) { | |||
java.io.File abs = new java.io.File(src, relativePathFromSourceDir); | |||
Bucket b = getBucket(isDir ? Directory.fromIOFile(abs, getProject()) : File.fromIOFile(abs, getProject())); | |||
if (b != null) { | |||
return b; | |||
} | |||
} | |||
} | |||
return null; | |||
} | |||
@@ -0,0 +1,88 @@ | |||
/* | |||
* 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.mediumtest.deprecated; | |||
import com.google.common.collect.ImmutableMap; | |||
import org.apache.commons.io.FileUtils; | |||
import org.junit.After; | |||
import org.junit.Before; | |||
import org.junit.Test; | |||
import org.junit.rules.TemporaryFolder; | |||
import org.sonar.batch.mediumtest.BatchMediumTester; | |||
import org.sonar.batch.mediumtest.TaskResult; | |||
import org.sonar.batch.protocol.input.ActiveRule; | |||
import org.sonar.xoo.XooPlugin; | |||
import java.io.File; | |||
import java.io.IOException; | |||
import static org.assertj.core.api.Assertions.assertThat; | |||
public class DeprecatedApiMediumTest { | |||
@org.junit.Rule | |||
public TemporaryFolder temp = new TemporaryFolder(); | |||
public BatchMediumTester tester = BatchMediumTester.builder() | |||
.registerPlugin("xoo", new XooPlugin()) | |||
.addDefaultQProfile("xoo", "Sonar Way") | |||
.activateRule(new ActiveRule("xoo", "DeprecatedResourceApi", null, "One issue per line", "MAJOR", null, "xoo")) | |||
.build(); | |||
@Before | |||
public void prepare() { | |||
tester.start(); | |||
} | |||
@After | |||
public void stop() { | |||
tester.stop(); | |||
} | |||
@Test | |||
public void testIssueDetails() throws IOException { | |||
File baseDir = temp.newFolder(); | |||
File srcDir = new File(baseDir, "src"); | |||
srcDir.mkdir(); | |||
File xooFileInRootDir = new File(srcDir, "sample.xoo"); | |||
FileUtils.write(xooFileInRootDir, "1\n2\n3\n4\n5\n6\n7\n8\n9\n10"); | |||
File xooFileInAnotherDir = new File(srcDir, "package/sample.xoo"); | |||
FileUtils.write(xooFileInAnotherDir, "1\n2\n3\n4\n5\n6\n7\n8\n9\n10"); | |||
TaskResult result = tester.newTask() | |||
.properties(ImmutableMap.<String, String>builder() | |||
.put("sonar.task", "scan") | |||
.put("sonar.projectBaseDir", baseDir.getAbsolutePath()) | |||
.put("sonar.projectKey", "com.foo.project") | |||
.put("sonar.projectName", "Foo Project") | |||
.put("sonar.projectVersion", "1.0-SNAPSHOT") | |||
.put("sonar.projectDescription", "Description of Foo Project") | |||
.put("sonar.sources", "src") | |||
.build()) | |||
.start(); | |||
// 1 issue on root dir + 1 issue on each file + 1 issue on each file parent dir | |||
assertThat(result.issues()).hasSize(5); | |||
} | |||
} |
@@ -35,8 +35,34 @@ public class Directory extends Resource { | |||
public static final String SEPARATOR = "/"; | |||
public static final String ROOT = "[root]"; | |||
private final String relativePathFromSourceDir; | |||
Directory() { | |||
// Used by factory | |||
this.relativePathFromSourceDir = null; | |||
} | |||
/** | |||
* @deprecated since 4.2 use {@link #fromIOFile(java.io.File, Project)} | |||
*/ | |||
@Deprecated | |||
public Directory(String relativePathFromSourceDir) { | |||
this(relativePathFromSourceDir, null); | |||
} | |||
/** | |||
* @deprecated since 4.2 use {@link #fromIOFile(java.io.File, Project)} | |||
*/ | |||
@Deprecated | |||
public Directory(String relativePathFromSourceDir, Language language) { | |||
this.relativePathFromSourceDir = parseKey(relativePathFromSourceDir); | |||
} | |||
/** | |||
* Internal. | |||
*/ | |||
public String relativePathFromSourceDir() { | |||
return relativePathFromSourceDir; | |||
} | |||
@Override |
@@ -28,6 +28,8 @@ import org.sonar.api.utils.WildcardPattern; | |||
import javax.annotation.CheckForNull; | |||
import java.util.List; | |||
/** | |||
* This class is an implementation of a resource of type FILE | |||
* | |||
@@ -42,8 +44,62 @@ public class File extends Resource { | |||
private Directory parent; | |||
private String qualifier = Qualifiers.FILE; | |||
private final String relativePathFromSourceDir; | |||
private File() { | |||
// Used by factory method | |||
this.relativePathFromSourceDir = null; | |||
} | |||
/** | |||
* @deprecated since 4.2 use {@link FileSystem#inputFile(org.sonar.api.batch.fs.FilePredicate)} | |||
*/ | |||
@Deprecated | |||
public File(String relativePathFromSourceDir) { | |||
if (relativePathFromSourceDir == null) { | |||
throw new IllegalArgumentException("File key is null"); | |||
} | |||
this.relativePathFromSourceDir = parseKey(relativePathFromSourceDir); | |||
} | |||
/** | |||
* @deprecated since 4.2 use {@link FileSystem#inputFile(org.sonar.api.batch.fs.FilePredicate)} | |||
*/ | |||
@Deprecated | |||
public File(String relativeDirectoryPathFromSourceDir, String filename) { | |||
this.filename = StringUtils.trim(filename); | |||
if (StringUtils.isBlank(relativeDirectoryPathFromSourceDir)) { | |||
this.relativePathFromSourceDir = filename; | |||
} else { | |||
this.relativePathFromSourceDir = new StringBuilder().append(Directory.parseKey(relativeDirectoryPathFromSourceDir)).append(Directory.SEPARATOR).append(this.filename) | |||
.toString(); | |||
} | |||
} | |||
/** | |||
* @deprecated since 4.2 use {@link FileSystem#inputFile(org.sonar.api.batch.fs.FilePredicate)} | |||
*/ | |||
@Deprecated | |||
public File(Language language, String relativePathFromSourceDir) { | |||
this(relativePathFromSourceDir); | |||
this.language = language; | |||
} | |||
/** | |||
* Creates a File from language, directory and filename | |||
* @deprecated since 4.2 use {@link #fromIOFile(java.io.File, Project)} | |||
*/ | |||
@Deprecated | |||
public File(Language language, String relativeDirectoryPathFromSourceDir, String filename) { | |||
this(relativeDirectoryPathFromSourceDir, filename); | |||
this.language = language; | |||
} | |||
/** | |||
* Internal. | |||
*/ | |||
public String relativePathFromSourceDir() { | |||
return relativePathFromSourceDir; | |||
} | |||
/** | |||
@@ -77,6 +133,20 @@ public class File extends Resource { | |||
return matcher.match(getKey()); | |||
} | |||
/** | |||
* Creates a File from an io.file and a list of sources directories | |||
* @deprecated since 4.2 use {@link #fromIOFile(java.io.File, Project)} | |||
*/ | |||
@Deprecated | |||
@CheckForNull | |||
public static File fromIOFile(java.io.File file, List<java.io.File> sourceDirs) { | |||
PathResolver.RelativePath relativePath = new PathResolver().relativePath(sourceDirs, file); | |||
if (relativePath != null) { | |||
return new File(relativePath.path()); | |||
} | |||
return null; | |||
} | |||
/** | |||
* Creates a {@link File} from an absolute {@link java.io.File} and a module. | |||
* The returned {@link File} can be then passed for example to |