diff options
7 files changed, 305 insertions, 1 deletions
diff --git a/plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo/XooPlugin.java b/plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo/XooPlugin.java index 96c090c5dc9..db5d607938a 100644 --- a/plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo/XooPlugin.java +++ b/plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo/XooPlugin.java @@ -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, diff --git a/plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo/rule/DeprecatedResourceApiSensor.java b/plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo/rule/DeprecatedResourceApiSensor.java new file mode 100644 index 00000000000..7cb80fbf541 --- /dev/null +++ b/plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo/rule/DeprecatedResourceApiSensor.java @@ -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; + } + +} diff --git a/plugins/sonar-xoo-plugin/src/test/java/org/sonar/xoo/XooPluginTest.java b/plugins/sonar-xoo-plugin/src/test/java/org/sonar/xoo/XooPluginTest.java index c7493349640..efa35f294d5 100644 --- a/plugins/sonar-xoo-plugin/src/test/java/org/sonar/xoo/XooPluginTest.java +++ b/plugins/sonar-xoo-plugin/src/test/java/org/sonar/xoo/XooPluginTest.java @@ -27,6 +27,6 @@ public class XooPluginTest { @Test public void provide_extensions() { - assertThat(new XooPlugin().getExtensions()).hasSize(20); + assertThat(new XooPlugin().getExtensions()).hasSize(21); } } diff --git a/sonar-batch/src/main/java/org/sonar/batch/index/DefaultIndex.java b/sonar-batch/src/main/java/org/sonar/batch/index/DefaultIndex.java index 6cf8ae826f4..4cc275dc4aa 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/index/DefaultIndex.java +++ b/sonar-batch/src/main/java/org/sonar/batch/index/DefaultIndex.java @@ -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; } diff --git a/sonar-batch/src/test/java/org/sonar/batch/mediumtest/deprecated/DeprecatedApiMediumTest.java b/sonar-batch/src/test/java/org/sonar/batch/mediumtest/deprecated/DeprecatedApiMediumTest.java new file mode 100644 index 00000000000..671b5137514 --- /dev/null +++ b/sonar-batch/src/test/java/org/sonar/batch/mediumtest/deprecated/DeprecatedApiMediumTest.java @@ -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); + } + +} diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/resources/Directory.java b/sonar-plugin-api/src/main/java/org/sonar/api/resources/Directory.java index f11f2f432a8..fe72745346e 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/resources/Directory.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/resources/Directory.java @@ -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 diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/resources/File.java b/sonar-plugin-api/src/main/java/org/sonar/api/resources/File.java index afe82db8a84..393cdc5e327 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/resources/File.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/resources/File.java @@ -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; } /** @@ -78,6 +134,20 @@ public class File extends Resource { } /** + * 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 * {@link SensorContext#saveMeasure(Resource, org.sonar.api.measures.Measure)}. |