Skip to content

Commit cd9d1c7

Browse files
authored
Merge pull request #897 from uni-bremen-agst/885-static-analyser-framework-checkstyle-support
885 static analyser framework checkstyle support Closes #885
2 parents 31b82ac + 87e1c75 commit cd9d1c7

31 files changed

+66195
-309
lines changed

Assets/SEE/DataModel/DG/IO/CheckstyleIndexNodeStrategy.cs

Lines changed: 345 additions & 0 deletions
Large diffs are not rendered by default.
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
fileFormatVersion: 2
2+
guid: 5ab25e7b3b2faed46958fdec3e51d922
Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
using System.Collections.Generic;
2+
3+
namespace SEE.DataModel.DG.IO
4+
{
5+
/// <summary>
6+
/// Contains parser configuration and node-indexing helpers for importing external analysis reports.
7+
/// Parsing configuration for Checkstyle XML reports.
8+
/// </summary>
9+
/// <remarks>
10+
/// Supported nodes:
11+
/// <list type="bullet">
12+
/// <item><description><c>&lt;file&gt;</c>: File-level aggregation (counts by severity).</description></item>
13+
/// <item><description><c>&lt;error&gt;</c>: Individual violations (line/column + rendered message).</description></item>
14+
/// </list>
15+
///
16+
/// Key idea:
17+
/// Metrics are configured per context (see <see cref="XPathMapping.MetricsByContext"/>),
18+
/// so the parser does not need XPath hacks that force irrelevant metrics to evaluate to NaN.
19+
/// </remarks>
20+
internal sealed class CheckstyleParsingConfig : ParsingConfig
21+
{
22+
/// <summary>
23+
/// Context name for individual Checkstyle violations (<c>&lt;error&gt;</c> nodes).
24+
/// </summary>
25+
private const string errorContext = "error";
26+
27+
/// <summary>
28+
/// Context name for file-level aggregation (<c>&lt;file&gt;</c> nodes).
29+
/// </summary>
30+
private const string fileContext = "file";
31+
32+
/// <summary>
33+
/// Initializes a new instance of the <see cref="CheckstyleParsingConfig"/> class.
34+
/// </summary>
35+
public CheckstyleParsingConfig()
36+
{
37+
ToolId = "Checkstyle";
38+
39+
// Used by CheckstyleIndexNodeStrategy to trim absolute paths down to package/class.
40+
SourceRootMarker = "src/main/java/";
41+
42+
XPathMapping = new XPathMapping
43+
{
44+
// Parse both file-level and error-level nodes.
45+
SearchedNodes = $"//{fileContext}|//{errorContext}",
46+
47+
// Build a stable "full path" identifier for each node.
48+
// For Checkstyle, the report contains absolute file paths in @name.
49+
PathBuilders = new Dictionary<string, string>
50+
{
51+
[fileContext] = "string(@name)",
52+
[errorContext] = "string(ancestor::file/@name)",
53+
},
54+
55+
// File name extraction (optional for CheckstyleIndexNodeStrategy, but useful for other strategies).
56+
// We keep this context-first to avoid tag-name ambiguity.
57+
FileName = new Dictionary<string, string>
58+
{
59+
// The file element has the file path in @name.
60+
[fileContext] = "string(@name)",
61+
// The error element inherits the file path from its ancestor <file>.
62+
[errorContext] = "string(ancestor::file/@name)",
63+
},
64+
65+
// Location is only meaningful for <error>, but parsing it globally is safe:
66+
// for <file> these attributes are missing and are simply ignored.
67+
LocationMapping = new Dictionary<string, string>
68+
{
69+
["StartLine"] = "string(@line)",
70+
["StartColumn"] = "string(@column)",
71+
},
72+
73+
// Context-specific metrics.
74+
MetricsByContext = new Dictionary<string, Dictionary<string, string>>
75+
{
76+
[fileContext] = new Dictionary<string, string>
77+
{
78+
// Count errors by severity within this file.
79+
// count(error[@severity='warning']) counts all child <error> elements.
80+
["Aggregated.WarningCount"] = "string(count(error[@severity='warning']))",
81+
["Aggregated.ErrorCount"] = "string(count(error[@severity='error']))",
82+
["Aggregated.InfoCount"] = "string(count(error[@severity='info']))",
83+
84+
// Total violations for this file.
85+
["Aggregated.ViolationCount"] = "string(count(error))",
86+
},
87+
88+
[errorContext] = new Dictionary<string, string>
89+
{
90+
// A short, human-friendly issue string.
91+
// Using substring(...) with a large length keeps this robust even if message is empty.
92+
["ContextLevel.Issue"] =
93+
"substring(concat('Line ', @line, ': [', @severity, '] ', @message), 1, 10000)",
94+
},
95+
},
96+
};
97+
}
98+
99+
/// <summary>
100+
/// Creates the parser implementation that can read Checkstyle reports using this configuration.
101+
/// </summary>
102+
/// <returns>A report parser configured for Checkstyle XML input.</returns>
103+
internal override IReportParser CreateParser()
104+
{
105+
return new XmlReportParser(this);
106+
}
107+
108+
/// <summary>
109+
/// Creates the index-node strategy that maps findings to the corresponding graph node identifiers.
110+
/// </summary>
111+
/// <returns>An index node strategy suitable for Checkstyle file paths.</returns>
112+
public override IIndexNodeStrategy CreateIndexNodeStrategy()
113+
{
114+
return new CheckstyleIndexNodeStrategy(this);
115+
}
116+
}
117+
}
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
fileFormatVersion: 2
2+
guid: ebed831a38cc0ef438ca848e2657b541

Assets/SEE/DataModel/DG/IO/Finding.cs

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,22 +3,27 @@
33
namespace SEE.DataModel.DG.IO
44
{
55
/// <summary>
6-
/// Represents a single parsed element together with its metrics and optional location data.
6+
/// Represents a single parsed report element together with its metrics and optional location data.
77
/// </summary>
88
public class Finding
99
{
1010
/// <summary>
11-
/// Normalized path that can be mapped back to a node in a <see cref="Graph"/>.
11+
/// Normalized identifier that can be mapped back to a node in a <see cref="Graph"/>.
1212
/// </summary>
13+
/// <remarks>Preconditions: Should not be null or whitespace if the finding is meant to be applied to a graph.</remarks>
1314
public string FullPath { get; set; }
1415

1516
/// <summary>
16-
/// Name of the source file. Can be empty if <see cref="FullPath"/> contains the file name.
17+
/// Name or path of the source file.
1718
/// </summary>
19+
/// <remarks>
20+
/// May be empty if <see cref="FullPath"/> already contains the file name or if the report format does not
21+
/// expose a file name separately.
22+
/// </remarks>
1823
public string FileName { get; set; }
1924

2025
/// <summary>
21-
/// Context or tag name under which the finding was reported (for example, class, method).
26+
/// Context or tag name under which the finding was reported (for example, class, method, file).
2227
/// </summary>
2328
public string Context { get; set; }
2429

@@ -30,7 +35,7 @@ public class Finding
3035
/// <summary>
3136
/// Metric values emitted for this finding, keyed by a descriptive string.
3237
/// </summary>
33-
public Dictionary<string, string?> Metrics { get; set; }
34-
= new Dictionary<string, string?>();
38+
/// <remarks>Preconditions: Never null.</remarks>
39+
public Dictionary<string, string> Metrics { get; set; } = new Dictionary<string, string>();
3540
}
3641
}

Assets/SEE/DataModel/DG/IO/IIndexNodeStrategy.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ namespace SEE.DataModel.DG.IO
1919
/// 3. <see cref="FindingPathToMainType"/> resolves to the main type for indexing purposes
2020
/// 4. <see cref="NodeIdToMainType"/> provides consistent main type resolution from graph nodes
2121
///
22-
/// Example implementations: JavaIndexNodeStrategy, CSharpIndexNodeStrategy
22+
/// Example implementations:<see cref="JaCoCoIndexNodeStrategy"/> <see cref="CheckstyleIndexNodeStrategy"/>
2323
/// </remarks>
2424
public interface IIndexNodeStrategy
2525
{

Assets/SEE/DataModel/DG/IO/JavaIndexNodeStrategy.cs renamed to Assets/SEE/DataModel/DG/IO/JaCoCoIndexNodeStrategy.cs

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
using System;
1+
using SEE.Utils;
2+
using System;
23
using System.Collections.Generic;
34
using System.IO;
45

@@ -8,10 +9,9 @@ namespace SEE.DataModel.DG.IO
89
/// Strategy for normalizing Java code element identifiers to main type paths.
910
/// Handles JaCoCo-specific conventions including package separators, inner classes, and methods.
1011
/// </summary>
11-
public sealed class JavaIndexNodeStrategy : IIndexNodeStrategy
12+
public sealed class JaCoCoIndexNodeStrategy : IIndexNodeStrategy
1213
{
1314
// JaCoCo and Java conventions
14-
private const char JavaPathSeparator = '/';
1515
private const char NodeIdSeparator = '.';
1616
private const char InnerClassDelimiter = '$';
1717
private const char MethodDelimiter = '#';
@@ -29,7 +29,7 @@ public string FindingPathToNodeId(string fullPath)
2929
}
3030

3131
// 1. replacing path separators
32-
fullPath = fullPath.Replace(JavaPathSeparator, NodeIdSeparator);
32+
fullPath = fullPath.Replace(Filenames.UnixDirectorySeparator, NodeIdSeparator).Replace(Filenames.WindowsDirectorySeparator, NodeIdSeparator);
3333

3434
bool isMethod = fullPath.IndexOf(MethodDelimiter) > -1;
3535

@@ -57,8 +57,8 @@ public string FindingPathToMainType(string fullPath, string fileName)
5757
return null;
5858
}
5959

60-
// Step 1: Convert path separators: '/' → '.'
61-
string normalized = fullPath.Replace(JavaPathSeparator, NodeIdSeparator);
60+
// Step 1: Convert path separators: '/' → '.' and '\\' -> '.'
61+
string normalized = Filenames.ReplaceDirectorySeparators(fullPath, NodeIdSeparator);
6262

6363
// Step 2: Remove everything from first inner class or method delimiter onwards
6464
normalized = RemoveInnerTypesAndMethods(normalized);
@@ -86,7 +86,7 @@ public string NodeIdToMainType(Node node)
8686
}
8787

8888
// For methods: recurse to parent type
89-
if (node.Type == "Method")
89+
if (node.Type == NodeTypes.Method)
9090
{
9191
return node.Parent != null ? NodeIdToMainType(node.Parent) : null;
9292
}
@@ -108,10 +108,10 @@ public string NodeIdToMainType(Node node)
108108
/// </summary>
109109
private static readonly HashSet<string> TypeNodeTypes = new()
110110
{
111-
"Class",
112-
"Interface",
113-
"Class_Template",
114-
"Interface_Template"
111+
NodeTypes.Class,
112+
NodeTypes.Interface,
113+
NodeTypes.ClassTemplate,
114+
NodeTypes.InterfaceTemplate
115115
};
116116

117117
private static bool IsTypeNode(string nodeType)

Assets/SEE/DataModel/DG/IO/JavaIndexNodeStrategy.cs.meta renamed to Assets/SEE/DataModel/DG/IO/JaCoCoIndexNodeStrategy.cs.meta

File renamed without changes.

0 commit comments

Comments
 (0)