Java 限制SonarQube中每行相同检查的问题

Java 限制SonarQube中每行相同检查的问题,java,plugins,sonarqube,Java,Plugins,Sonarqube,我正在使用Java插件API为SonarQube 5.6.6开发一个插件。我创建了一些自定义规则(检查),其中一个在同一行中多次报告相同的问题。这是有意义的,因为该行多次出现相同的错误,但是,有没有办法限制这一点,以便SonarQube只显示该行中的问题 图像(和代码)胜于雄辩,因此我将展示一个示例:一个检查,它在每次检测到新类时都报告一个问题 @Rule(key = "foo_key", name = "Foo issue", description = "Foo issue", priori

我正在使用Java插件API为SonarQube 5.6.6开发一个插件。我创建了一些自定义规则(检查),其中一个在同一行中多次报告相同的问题。这是有意义的,因为该行多次出现相同的错误,但是,有没有办法限制这一点,以便SonarQube只显示该行中的问题

图像(和代码)胜于雄辩,因此我将展示一个示例:一个检查,它在每次检测到新类时都报告一个问题

@Rule(key = "foo_key", name = "Foo issue", description = "Foo issue", priority = Priority.INFO)
public class FooCheck extends IssuableSubscriptionVisitor {

    @Override
    public List<Kind> nodesToVisit() {
        return ImmutableList.of(Kind.NEW_CLASS);
    }

    @Override
    public void visitNode(Tree tree) {
        reportIssue(tree, "New class!");
    }

}
然而,这是一个解决方案,仅此检查。我想知道是否有一个通用的方法告诉SonarQube不要在每行显示相同检查的几个问题


谢谢。

您可以在报告问题之前获取
节点的行号,将其保存到全局变量
lastLineReported
或报告行列表中。然后,用一个简单的
if
语句以两种方式进行检查:

1-使用lastLineReported变量:

if(lastLineReported != currentLine) {
        lastLineReported = currentLine;
        reportIssue(tree, "New class!");
    }
if(!reportedLines.contains(currentLine)) {
        reportedLines.add(currentLine);
        reportIssue(tree, "New class!");
    }
2-使用报告的行列表:

if(lastLineReported != currentLine) {
        lastLineReported = currentLine;
        reportIssue(tree, "New class!");
    }
if(!reportedLines.contains(currentLine)) {
        reportedLines.add(currentLine);
        reportIssue(tree, "New class!");
    }
基于此,我将提供完整的解决方案:

关键是接口
Tree
org.sonar.plugins.java.api.Tree.Tree
)有一个方法
firstToken()
,它返回一个
SyntaxToken
,这是另一个接口,它有一个方法
行()。因此,我们可以调用
tree.firstToken().line()
来获取我们正在访问的节点的行,在我们第一次报告该行上的问题时保存它,并在以后访问节点时检查它们的行是否已经报告了问题

注意:我们不能在集合中静态保存这些行,因为每次访问要分析的每个源代码文件的节点时,都会共享该集合的值。相反,我们必须将每一行与正在分析的文件一起保存。如果我们没有这样做,并且在文件a的X行中创建了一个问题,如果文件B在其X行中有问题,则不会创建此问题

@Rule(key = "foo_key", name = "Foo issue", description = "Foo issue", priority = Priority.INFO)
public class FooCheck extends IssuableSubscriptionVisitor {

    private static final Map<String, Collection<Integer>> linesWithIssuesByClass = new HashMap<>();

    @Override
    public List<Kind> nodesToVisit() {
        return ImmutableList.of(Kind.NEW_CLASS);
    }

    @Override
    public void visitNode(Tree tree) {
        if (lineAlreadyHasThisIssue(tree)) {
            return;
        }

        reportIssue(tree);
    }

    private boolean lineAlreadyHasThisIssue(Tree tree) {
        if (tree.firstToken() != null) {
            final String classname = getFullyQualifiedNameOfClassOf(tree);
            final int line = tree.firstToken().line();

            return linesWithIssuesByClass.containsKey(classname)
                    && linesWithIssuesByClass.get(classname).contains(line);
        }

        return false;
    }

    private void reportIssue(Tree tree) {
        if (tree.firstToken() != null) {
            final String classname = getFullyQualifiedNameOfClassOf(tree);
            final int line = tree.firstToken().line();

            if (!linesWithIssuesByClass.containsKey(classname)) {
                linesWithIssuesByClass.put(classname, new ArrayList<>());
            }

            linesWithIssuesByClass.get(classname).add(line);
        }

        reportIssue(tree, "New class!");
    }

    private String getFullyQualifiedNameOfClassOf(Tree tree) {
        Tree parent = tree.parent();

        while (parent != null) {
            final Tree grandparent = parent.parent();

            if (parent.is(Kind.CLASS) && grandparent != null && grandparent.is(Kind.COMPILATION_UNIT)) {
                final String packageName = getPackageName((CompilationUnitTree) grandparent);

                return packageName.isEmpty()
                        ? getClassName((ClassTree) parent)
                        : packageName + '.' + getClassName((ClassTree) parent);
            }

            parent = parent.parent();
        }

        return "";
    }

    private String getPackageName(CompilationUnitTree compilationUnitTree) {
        final PackageDeclarationTree packageDeclarationTree = compilationUnitTree.packageDeclaration();
        if (packageDeclarationTree == null) {
            return "";
        }

        return packageDeclarationTree.packageName().toString();
    }

    private String getClassName(ClassTree classTree) {
        final IdentifierTree simpleName = classTree.simpleName();
        return simpleName == null
                ? ""
                : simpleName.toString();
    }

}
虽然
Tree
有另一个方法
lastToken()
返回另一个带有行的
SyntaxToken
,但我们应该调用
firstToken()
,因为我们的节点可能是这样的多行:

Foo foo = new Foo(
        new Bar()
);

lastToken().line()
在每次访问节点时都会有不同的值,而
firstToken().line()
不会。

多亏了你的回答,我可以解决我的问题,所以我投了更高的票。:)为了完整起见,我编写了另一个答案来指定与SonarQube接口相关的代码。