From 618bc5086fa76784784df9205308908619a0125e Mon Sep 17 00:00:00 2001 From: Jamie Shiell Date: Mon, 18 Nov 2024 15:59:35 +0000 Subject: [PATCH] Add basic support for grouping results by severity (#3) --- CHANGELOG.md | 2 +- .../checkstyle/actions/GroupBySeverity.java | 10 ++ .../toolwindow/FileGroupTreeInfo.java | 17 +++ .../toolwindow/FileResultTreeInfo.java | 47 ------- .../checkstyle/toolwindow/GroupTreeInfo.java | 51 ++++++++ .../toolwindow/PackageGroupTreeInfo.java | 17 +++ .../toolwindow/PackageTreeInfo.java | 47 ------- .../checkstyle/toolwindow/ResultGrouping.java | 3 +- .../toolwindow/ResultTreeModel.java | 118 ++++++++++++------ .../toolwindow/SeverityGroupTreeInfo.java | 28 +++++ .../toolwindow/ToggleableTreeNode.java | 7 ++ src/main/resources/META-INF/plugin.xml | 8 +- .../checkstyle/CheckStyleBundle.properties | 2 + 13 files changed, 221 insertions(+), 136 deletions(-) create mode 100644 src/main/java/org/infernus/idea/checkstyle/actions/GroupBySeverity.java create mode 100644 src/main/java/org/infernus/idea/checkstyle/toolwindow/FileGroupTreeInfo.java delete mode 100644 src/main/java/org/infernus/idea/checkstyle/toolwindow/FileResultTreeInfo.java create mode 100644 src/main/java/org/infernus/idea/checkstyle/toolwindow/GroupTreeInfo.java create mode 100644 src/main/java/org/infernus/idea/checkstyle/toolwindow/PackageGroupTreeInfo.java delete mode 100644 src/main/java/org/infernus/idea/checkstyle/toolwindow/PackageTreeInfo.java create mode 100644 src/main/java/org/infernus/idea/checkstyle/toolwindow/SeverityGroupTreeInfo.java diff --git a/CHANGELOG.md b/CHANGELOG.md index f961a697..a61da252 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,7 +1,7 @@ # CheckStyle-IDEA Changelog -* **5.99.0** New: Added option to group results by package (#3). +* **5.99.0** New: Added option to group results by package and severity (#3). * **5.98.0** New: Added Checkstyle 10.20.1. * **5.97.0** Fixed: Refactored code to fix exception around API dependencies at initialisation (#655). * **5.97.0** New: Added Checkstyle 10.19.0. diff --git a/src/main/java/org/infernus/idea/checkstyle/actions/GroupBySeverity.java b/src/main/java/org/infernus/idea/checkstyle/actions/GroupBySeverity.java new file mode 100644 index 00000000..9c72c081 --- /dev/null +++ b/src/main/java/org/infernus/idea/checkstyle/actions/GroupBySeverity.java @@ -0,0 +1,10 @@ +package org.infernus.idea.checkstyle.actions; + +import org.infernus.idea.checkstyle.toolwindow.ResultGrouping; + +public class GroupBySeverity extends GroupingAction { + + public GroupBySeverity() { + super(ResultGrouping.BY_SEVERITY); + } +} diff --git a/src/main/java/org/infernus/idea/checkstyle/toolwindow/FileGroupTreeInfo.java b/src/main/java/org/infernus/idea/checkstyle/toolwindow/FileGroupTreeInfo.java new file mode 100644 index 00000000..1b39ca6f --- /dev/null +++ b/src/main/java/org/infernus/idea/checkstyle/toolwindow/FileGroupTreeInfo.java @@ -0,0 +1,17 @@ +package org.infernus.idea.checkstyle.toolwindow; + +import com.intellij.icons.AllIcons; + +public class FileGroupTreeInfo extends GroupTreeInfo { + + /** + * Construct a file node. + * + * @param fileName the name of the file. + * @param problemCount the number of problems in the file. + */ + public FileGroupTreeInfo(final String fileName, final int problemCount) { + super(fileName, "file", AllIcons.FileTypes.Java, problemCount); + } + +} diff --git a/src/main/java/org/infernus/idea/checkstyle/toolwindow/FileResultTreeInfo.java b/src/main/java/org/infernus/idea/checkstyle/toolwindow/FileResultTreeInfo.java deleted file mode 100644 index 12fc0e53..00000000 --- a/src/main/java/org/infernus/idea/checkstyle/toolwindow/FileResultTreeInfo.java +++ /dev/null @@ -1,47 +0,0 @@ -package org.infernus.idea.checkstyle.toolwindow; - -import com.intellij.icons.AllIcons; -import org.infernus.idea.checkstyle.CheckStyleBundle; - -public class FileResultTreeInfo extends ResultTreeNode { - - private final String fileName; - private final int totalProblems; - private int visibleProblems; - - /** - * Construct a file node. - * - * @param fileName the name of the file. - * @param problemCount the number of problems in the file. - */ - public FileResultTreeInfo(final String fileName, final int problemCount) { - super(CheckStyleBundle.message("plugin.results.scan-file-result", fileName, problemCount)); - - if (fileName == null) { - throw new IllegalArgumentException("Filename may not be null"); - } - - this.fileName = fileName; - this.totalProblems = problemCount; - this.visibleProblems = problemCount; - - updateTextForFileNode(); - - setIcon(AllIcons.FileTypes.Java); - } - - private void updateTextForFileNode() { - if (totalProblems == visibleProblems) { - setText(CheckStyleBundle.message("plugin.results.scan-file-result", fileName, totalProblems)); - } else { - setText(CheckStyleBundle.message("plugin.results.scan-file-result.filtered", fileName, visibleProblems, totalProblems - visibleProblems)); - } - } - - void setVisibleProblems(final int visibleProblems) { - this.visibleProblems = visibleProblems; - - updateTextForFileNode(); - } -} diff --git a/src/main/java/org/infernus/idea/checkstyle/toolwindow/GroupTreeInfo.java b/src/main/java/org/infernus/idea/checkstyle/toolwindow/GroupTreeInfo.java new file mode 100644 index 00000000..fc8b1967 --- /dev/null +++ b/src/main/java/org/infernus/idea/checkstyle/toolwindow/GroupTreeInfo.java @@ -0,0 +1,51 @@ +package org.infernus.idea.checkstyle.toolwindow; + +import org.infernus.idea.checkstyle.CheckStyleBundle; +import org.jetbrains.annotations.NotNull; + +import javax.swing.*; + +abstract class GroupTreeInfo extends ResultTreeNode { + + private final String name; + private final String groupId; + private final int totalProblems; + private int visibleProblems; + + /** + * Construct a group node. + * + * @param name the name of the group. + * @param groupId the ID used as part of message lookup. + * @param icon the icon of the group. + * @param problemCount the number of problems in the group. + */ + public GroupTreeInfo(@NotNull final String name, + @NotNull final String groupId, + @NotNull final Icon icon, + final int problemCount) { + super(CheckStyleBundle.message("plugin.results.scan-" + groupId + "-result", name, problemCount)); + + this.name = name; + this.groupId = groupId; + this.totalProblems = problemCount; + this.visibleProblems = problemCount; + + updateDisplayText(); + setIcon(icon); + } + + private void updateDisplayText() { + if (totalProblems == visibleProblems) { + setText(CheckStyleBundle.message("plugin.results.scan-" + groupId + "-result", name, totalProblems)); + } else { + setText(CheckStyleBundle.message("plugin.results.scan-" + groupId + "-result.filtered", name, visibleProblems, totalProblems - visibleProblems)); + } + } + + void setVisibleProblems(final int visibleProblems) { + this.visibleProblems = visibleProblems; + + updateDisplayText(); + } +} diff --git a/src/main/java/org/infernus/idea/checkstyle/toolwindow/PackageGroupTreeInfo.java b/src/main/java/org/infernus/idea/checkstyle/toolwindow/PackageGroupTreeInfo.java new file mode 100644 index 00000000..dcf4b4eb --- /dev/null +++ b/src/main/java/org/infernus/idea/checkstyle/toolwindow/PackageGroupTreeInfo.java @@ -0,0 +1,17 @@ +package org.infernus.idea.checkstyle.toolwindow; + +import com.intellij.icons.AllIcons; + +public class PackageGroupTreeInfo extends GroupTreeInfo { + + /** + * Construct a package node. + * + * @param packageName the name of the package. + * @param problemCount the number of problems in the file. + */ + public PackageGroupTreeInfo(final String packageName, final int problemCount) { + super(packageName, "package", AllIcons.Nodes.Package, problemCount); + } + +} diff --git a/src/main/java/org/infernus/idea/checkstyle/toolwindow/PackageTreeInfo.java b/src/main/java/org/infernus/idea/checkstyle/toolwindow/PackageTreeInfo.java deleted file mode 100644 index fa411460..00000000 --- a/src/main/java/org/infernus/idea/checkstyle/toolwindow/PackageTreeInfo.java +++ /dev/null @@ -1,47 +0,0 @@ -package org.infernus.idea.checkstyle.toolwindow; - -import com.intellij.icons.AllIcons; -import org.infernus.idea.checkstyle.CheckStyleBundle; - -public class PackageTreeInfo extends ResultTreeNode { - - private final String packageName; - private final int totalProblems; - private int visibleProblems; - - /** - * Construct a package node. - * - * @param packageName the name of the package. - * @param problemCount the number of problems in the file. - */ - public PackageTreeInfo(final String packageName, final int problemCount) { - super(CheckStyleBundle.message("plugin.results.scan-file-result", packageName, problemCount)); - - if (packageName == null) { - throw new IllegalArgumentException("Package name may not be null"); - } - - this.packageName = packageName; - this.totalProblems = problemCount; - this.visibleProblems = problemCount; - - updateDisplayText(); - - setIcon(AllIcons.Nodes.Package); - } - - private void updateDisplayText() { - if (totalProblems == visibleProblems) { - setText(CheckStyleBundle.message("plugin.results.scan-package-result", packageName, totalProblems)); - } else { - setText(CheckStyleBundle.message("plugin.results.scan-package-result.filtered", packageName, visibleProblems, totalProblems - visibleProblems)); - } - } - - void setVisibleProblems(final int visibleProblems) { - this.visibleProblems = visibleProblems; - - updateDisplayText(); - } -} diff --git a/src/main/java/org/infernus/idea/checkstyle/toolwindow/ResultGrouping.java b/src/main/java/org/infernus/idea/checkstyle/toolwindow/ResultGrouping.java index 1f9912f6..32a880a3 100644 --- a/src/main/java/org/infernus/idea/checkstyle/toolwindow/ResultGrouping.java +++ b/src/main/java/org/infernus/idea/checkstyle/toolwindow/ResultGrouping.java @@ -3,6 +3,7 @@ public enum ResultGrouping { BY_FILE, - BY_PACKAGE + BY_PACKAGE, + BY_SEVERITY } diff --git a/src/main/java/org/infernus/idea/checkstyle/toolwindow/ResultTreeModel.java b/src/main/java/org/infernus/idea/checkstyle/toolwindow/ResultTreeModel.java index f245c5a5..596d492e 100644 --- a/src/main/java/org/infernus/idea/checkstyle/toolwindow/ResultTreeModel.java +++ b/src/main/java/org/infernus/idea/checkstyle/toolwindow/ResultTreeModel.java @@ -84,8 +84,9 @@ private void rebuildTree() { visibleRootNode.removeAllChildren(); switch (grouping) { - case BY_FILE -> groupResultsByFile(); case BY_PACKAGE -> groupResultsByPackage(); + case BY_SEVERITY -> groupResultsBySeverity(); + default -> groupResultsByFile(); } filterDisplayedTree(); @@ -125,12 +126,8 @@ private void filterNodeAndChildren(final ToggleableTreeNode node) { filterNodeAndChildren(childNode); } - if (node.getUserObject() instanceof FileResultTreeInfo fileResultTreeInfo) { - fileResultTreeInfo.setVisibleProblems(node.getChildCount()); - nodeShouldBeVisible = node.getChildCount() > 0; - - } else if (node.getUserObject() instanceof PackageTreeInfo packageTreeInfo) { - packageTreeInfo.setVisibleProblems(node.getChildCount()); + if (node.getUserObject() instanceof GroupTreeInfo groupTreeInfo) { + groupTreeInfo.setVisibleProblems(node.getChildCount()); nodeShouldBeVisible = node.getChildCount() > 0; } else if (node.getUserObject() instanceof ProblemResultTreeInfo problemResultTreeInfo) { @@ -166,10 +163,40 @@ public void setModel(@NotNull final Map> results, } private void groupResultsByFile() { - int problemCount = createFileNodes(sortByFileName(lastResults), visibleRootNode); + int problemCount = createFileNodes(sortByFileName(lastResults), lastResults, visibleRootNode); setRootMessage(problemCount); } + private int createFileNodes(final List sortedFiles, + final Map> problemsForAllFiles, + final ToggleableTreeNode parentNode) { + int problemCount = 0; + for (final PsiFile file : sortedFiles) { + final var fileNode = new ToggleableTreeNode(); + final var problems = problemsForAllFiles.getOrDefault(file, emptyList()); + + int childProblemCount = 0; + for (final Problem problem : problems) { + if (problem.severityLevel() != SeverityLevel.Ignore) { + final var problemInfo = new ProblemResultTreeInfo(file, problem); + fileNode.add(new ToggleableTreeNode(problemInfo)); + + ++childProblemCount; + } + } + + if (childProblemCount > 0) { + var nodeObject = new FileGroupTreeInfo(file.getName(), childProblemCount); + fileNode.setUserObject(nodeObject); + + parentNode.add(fileNode); + } + + problemCount += childProblemCount; + } + return problemCount; + } + private List sortByFileName(final Map> results) { if (results == null || results.isEmpty()) { return emptyList(); @@ -186,9 +213,9 @@ private void groupResultsByPackage() { for (String packageName : groupedByPackage.keySet()) { final var packageNode = new ToggleableTreeNode(); - var childProblemCount = createFileNodes(groupedByPackage.getOrDefault(packageName, emptyList()), packageNode); + var childProblemCount = createFileNodes(groupedByPackage.getOrDefault(packageName, emptyList()), lastResults, packageNode); if (childProblemCount > 0) { - final var packageInfo = new PackageTreeInfo(packageName, childProblemCount); + final var packageInfo = new PackageGroupTreeInfo(packageName, childProblemCount); packageNode.setUserObject(packageInfo); visibleRootNode.add(packageNode); } @@ -199,35 +226,6 @@ private void groupResultsByPackage() { setRootMessage(problemCount); } - private int createFileNodes(final List files, - final ToggleableTreeNode parentNode) { - int problemCount = 0; - for (final PsiFile file : files) { - final var fileNode = new ToggleableTreeNode(); - final var problems = lastResults.getOrDefault(file, emptyList()); - - int childProblemCount = 0; - for (final Problem problem : problems) { - if (problem.severityLevel() != SeverityLevel.Ignore) { - final var problemInfo = new ProblemResultTreeInfo(file, problem); - fileNode.add(new ToggleableTreeNode(problemInfo)); - - ++childProblemCount; - } - } - - if (childProblemCount > 0) { - var nodeObject = new FileResultTreeInfo(file.getName(), childProblemCount); - fileNode.setUserObject(nodeObject); - - parentNode.add(fileNode); - } - - problemCount += childProblemCount; - } - return problemCount; - } - private SortedMap> groupByPackageName(final Map> results) { if (results == null || results.isEmpty()) { return Collections.emptySortedMap(); @@ -247,6 +245,48 @@ private SortedMap> groupByPackageName(final Map 0) { + final var packageInfo = new SeverityGroupTreeInfo(severityLevel, childProblemCount); + severityNode.setUserObject(packageInfo); + visibleRootNode.add(severityNode); + } + + problemCount += childProblemCount; + } + + setRootMessage(problemCount); + } + + private SortedMap>> groupBySeverity(final Map> results) { + if (results == null || results.isEmpty()) { + return Collections.emptySortedMap(); + } + var severities = List.of(SeverityLevel.Error, SeverityLevel.Warning, SeverityLevel.Info); + + var groupedBySeverity = new TreeMap>>(); + severities.forEach(severityLevel -> groupedBySeverity.put(severityLevel, new HashMap<>())); + + for (var resultFile : results.keySet()) { + var problems = results.get(resultFile); + + for (SeverityLevel severityLevel : severities) { + groupedBySeverity.get(severityLevel).put( + resultFile, + problems.stream().filter(item -> item.severityLevel() == severityLevel).toList()); + } + } + return groupedBySeverity; + } + private void setRootMessage(final int problemCount) { if (problemCount == 0) { setRootMessage("plugin.results.scan-no-results"); diff --git a/src/main/java/org/infernus/idea/checkstyle/toolwindow/SeverityGroupTreeInfo.java b/src/main/java/org/infernus/idea/checkstyle/toolwindow/SeverityGroupTreeInfo.java new file mode 100644 index 00000000..dc7f103f --- /dev/null +++ b/src/main/java/org/infernus/idea/checkstyle/toolwindow/SeverityGroupTreeInfo.java @@ -0,0 +1,28 @@ +package org.infernus.idea.checkstyle.toolwindow; + +import com.intellij.icons.AllIcons; +import org.infernus.idea.checkstyle.csapi.SeverityLevel; + +import javax.swing.*; + +public class SeverityGroupTreeInfo extends GroupTreeInfo { + + /** + * Construct a severity node. + * + * @param severityLevel the severity level. + * @param problemCount the number of problems at this severity. + */ + public SeverityGroupTreeInfo(final SeverityLevel severityLevel, final int problemCount) { + super(severityLevel.name(), "file", iconForSeverity(severityLevel), problemCount); + } + + private static Icon iconForSeverity(final SeverityLevel severityLevel) { + return switch (severityLevel) { + case Error -> AllIcons.General.Error; + case Warning -> AllIcons.General.Warning; + default -> AllIcons.General.Information; + }; + } + +} diff --git a/src/main/java/org/infernus/idea/checkstyle/toolwindow/ToggleableTreeNode.java b/src/main/java/org/infernus/idea/checkstyle/toolwindow/ToggleableTreeNode.java index 63e98719..19743696 100644 --- a/src/main/java/org/infernus/idea/checkstyle/toolwindow/ToggleableTreeNode.java +++ b/src/main/java/org/infernus/idea/checkstyle/toolwindow/ToggleableTreeNode.java @@ -43,6 +43,13 @@ List getAllChildren() { return Collections.emptyList(); } + @Override + public void removeAllChildren() { + if (children != null) { + children.clear(); + } + } + @Override public TreeNode getChildAt(final int index) { if (children == null) { diff --git a/src/main/resources/META-INF/plugin.xml b/src/main/resources/META-INF/plugin.xml index f3ea3787..52503968 100644 --- a/src/main/resources/META-INF/plugin.xml +++ b/src/main/resources/META-INF/plugin.xml @@ -25,7 +25,7 @@ -
  • 5.99.0: Added option to group results by package (#3).
  • +
  • 5.99.0: Added option to group results by package and severity (#3).
  • 5.98.0: New: Added Checkstyle 10.20.1.
  • 5.97.0: Fixed: Refactored code to fix exception around API dependencies at initialisation (#655).
  • 5.97.0: New: Added Checkstyle 10.19.0.
  • 1 @@ -168,6 +168,12 @@ text="Group Results By Package" description="Group the displayed results by their package" icon="/actions/GroupByPackage.svg"/> + + diff --git a/src/main/resources/org/infernus/idea/checkstyle/CheckStyleBundle.properties b/src/main/resources/org/infernus/idea/checkstyle/CheckStyleBundle.properties index 5af2859d..2189c5fd 100644 --- a/src/main/resources/org/infernus/idea/checkstyle/CheckStyleBundle.properties +++ b/src/main/resources/org/infernus/idea/checkstyle/CheckStyleBundle.properties @@ -21,6 +21,8 @@ plugin.results.scan-no-results=Checkstyle found no problems in the file(s) plugin.results.scan-results=Checkstyle found {0} item(s) in {1} file(s) plugin.results.scan-package-result={0} : {1} item(s) plugin.results.scan-package-result.filtered={0} : {1} item(s), {2} more hidden +plugin.results.scan-severity-result={0} : {1} item(s) +plugin.results.scan-severity-result.filtered={0} : {1} item(s), {2} more hidden plugin.results.scan-file-result={0} : {1} item(s) plugin.results.scan-file-result.filtered={0} : {1} item(s), {2} more hidden plugin.results.file-result={1} ({2}:{3}) [{4}]