88 "strings"
99)
1010
11+ //go:embed pmd/default-ruleset.xml
12+ var defaultPMDRuleset string
13+
1114// Parameter represents a rule parameter
1215type Parameter struct {
1316 Name string
@@ -30,14 +33,9 @@ type RuleSet struct {
3033}
3134
3235// DeprecatedReferences maps deprecated pattern IDs to their new versions
33- var DeprecatedReferences = map [string ]string {}
34-
35- func CreatePmd6Config (configuration []domain.PatternConfiguration ) string {
36- return createPmdConfigGeneric (configuration , pmd6Header , convertPatternIDToPMD , false )
37- }
38-
39- func CreatePmd7Config (configuration []domain.PatternConfiguration ) string {
40- return createPmdConfigGeneric (configuration , pmd7Header , convertPatternIDToPMD , true )
36+ var DeprecatedReferences = map [string ]string {
37+ // Add deprecated pattern mappings here
38+ // Example: "rulesets_java_design_ExcessiveClassLength": "category_java_design_ExcessiveClassLength",
4139}
4240
4341// prefixPatternID adds the appropriate prefix to a pattern ID
@@ -61,74 +59,128 @@ func prefixPatternID(patternID string) string {
6159 }
6260}
6361
64- func convertPatternIDToPMD (patternID string , isPmd7 bool ) (string , error ) {
65- // Normalize deprecated patterns
62+ // convertPatternIDToPMD converts a Codacy pattern ID to PMD format
63+ func convertPatternIDToPMD (patternID string ) (string , error ) {
64+ // Check if this is a deprecated pattern
6665 if newID , ok := DeprecatedReferences [patternID ]; ok {
6766 patternID = newID
6867 }
6968
70- // Normalize version-specific prefixes
71- if isPmd7 {
72- patternID = strings .TrimPrefix (patternID , "PMD7_" )
73- } else {
74- patternID = strings .TrimPrefix (patternID , "PMD_" )
75- }
69+ // Handle both formats:
70+ // 1. "java/design/NPathComplexity"
71+ // 2. "PMD_category_java_design_NPathComplexity"
72+ // 3. "PMD_category_apex_security_ApexSharingViolations"
73+ // 4. "PMD_category_plsql_errorprone_TO_TIMESTAMPWithoutDateFormat"
7674
7775 var parts []string
78-
79- // Handle different ID formats
8076 if strings .Contains (patternID , "/" ) {
81- // Already in path form: lang/category/rule
8277 parts = strings .Split (patternID , "/" )
83- if len (parts ) < 3 {
84- return "" , fmt .Errorf ("invalid PMD path pattern ID: %s" , patternID )
85- }
86- lang , category := parts [0 ], parts [1 ]
87- rule := strings .Join (parts [2 :], "/" )
88- return fmt .Sprintf ("rulesets/%s/%s.xml/%s" , lang , category , rule ), nil
89- }
90-
91- // Handle underscore-based Codacy pattern IDs
92- parts = strings .Split (patternID , "_" )
93- if isPmd7 {
94- // Expect category_lang_category_rule
95- if len (parts ) < 4 || parts [0 ] != "category" {
96- return "" , fmt .Errorf ("invalid PMD 7 pattern ID: %s" , patternID )
97- }
98- lang := parts [1 ]
99- category := parts [2 ]
100- rule := strings .Join (parts [3 :], "_" )
101- return fmt .Sprintf ("category/%s/%s.xml/%s" , lang , category , rule ), nil
10278 } else {
103- // PMD 6: expect lang_category_rule
79+ // Remove PMD_ prefix if present
80+ id := strings .TrimPrefix (patternID , "PMD_" )
81+ // Split by underscore and remove "category" if present
82+ parts = strings .Split (id , "_" )
10483 if parts [0 ] == "category" {
10584 parts = parts [1 :]
10685 }
107- if len (parts ) < 3 {
108- return "" , fmt .Errorf ("invalid PMD 6 pattern ID: %s" , patternID )
86+ }
87+
88+ if len (parts ) < 3 {
89+ return "" , fmt .Errorf ("invalid pattern ID format: %s" , patternID )
90+ }
91+
92+ // Extract language, category, and rule
93+ language := parts [0 ] // java, apex, etc.
94+ category := parts [1 ] // design, security, etc.
95+ rule := parts [2 ] // rule name
96+
97+ // If there are more parts, combine them with the rule name
98+ if len (parts ) > 3 {
99+ rule = strings .Join (parts [2 :], "_" )
100+ }
101+
102+ return fmt .Sprintf ("category/%s/%s.xml/%s" , language , category , rule ), nil
103+ }
104+
105+ // convertPatternIDToPMD7 converts a Codacy pattern ID to PMD 7 format
106+ func convertPatternIDToPMD7 (patternID string ) (string , error ) {
107+ // Strip deprecated mappings
108+ if newID , ok := DeprecatedReferences [patternID ]; ok {
109+ patternID = newID
110+ }
111+
112+ // Remove "PMD7_" prefix if present
113+ patternID = strings .TrimPrefix (patternID , "PMD7_" )
114+
115+ // Now handle patternIDs like: category_java_bestpractices_UnusedLocalVariable
116+ parts := strings .Split (patternID , "_" )
117+
118+ if len (parts ) < 4 || parts [0 ] != "category" {
119+ return "" , fmt .Errorf ("invalid PMD 7 pattern ID: %s" , patternID )
120+ }
121+
122+ lang := parts [1 ]
123+ category := parts [2 ]
124+ rule := strings .Join (parts [3 :], "_" )
125+
126+ return fmt .Sprintf ("category/%s/%s.xml/%s" , lang , category , rule ), nil
127+ }
128+
129+ // generateRuleXML generates XML for a single rule
130+ func generateRuleXML (rule Rule ) (string , error ) {
131+ pmdRef , err := convertPatternIDToPMD (rule .PatternID )
132+ if err != nil {
133+ return "" , err
134+ }
135+
136+ if len (rule .Parameters ) == 0 {
137+ return fmt .Sprintf (` <rule ref="%s"/>` , pmdRef ), nil
138+ }
139+
140+ // Generate rule with parameters
141+ var params strings.Builder
142+ for _ , param := range rule .Parameters {
143+ // Skip enabled and version parameters, but include all others
144+ if param .Name != "enabled" && param .Name != "version" {
145+ params .WriteString (fmt .Sprintf (`
146+ <property name="%s" value="%s"/>` , param .Name , param .Value ))
109147 }
110- lang := parts [0 ]
111- category := parts [1 ]
112- rule := strings .Join (parts [2 :], "_" )
113- return fmt .Sprintf ("rulesets/%s/%s.xml/%s" , lang , category , rule ), nil
114148 }
149+
150+ // If no parameters left after filtering, just output the rule without properties
151+ if params .Len () == 0 {
152+ return fmt .Sprintf (` <rule ref="%s"/>` , pmdRef ), nil
153+ }
154+
155+ result := fmt .Sprintf (` <rule ref="%s">
156+ <properties>%s
157+ </properties>
158+ </rule>` , pmdRef , params .String ())
159+
160+ return result , nil
115161}
116162
163+ // Add PMD 7 header
164+ var pmd7Header = `<?xml version="1.0" encoding="UTF-8"?>
165+ <ruleset name="Codacy PMD 7 Ruleset"
166+ xmlns="https://pmd.github.io/ruleset/2.0.0"
167+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
168+ xsi:schemaLocation="https://pmd.github.io/ruleset/2.0.0 https://pmd.github.io/schemas/pmd-7.0.0.xsd">
169+ <description>Codacy PMD 7 Ruleset</description>`
170+
117171// createPmdConfigGeneric abstracts the config creation for both PMD and PMD7
118172func createPmdConfigGeneric (
119173 configuration []domain.PatternConfiguration ,
120174 header string ,
121- convertPatternID func (string , bool ) (string , error ),
175+ convertPatternID func (string ) (string , error ),
122176 isPMD7 bool ,
123177) string {
124178 if len (configuration ) == 0 {
125179 // Fallback empty ruleset for PMD 7, or default for PMD 6
126180 if header == pmd7Header {
127181 return header + `</ruleset>`
128182 }
129-
130- return pmd6Header + `</ruleset>`
131-
183+ return defaultPMDRuleset
132184 }
133185
134186 var rules []Rule
@@ -183,7 +235,7 @@ func createPmdConfigGeneric(
183235 continue
184236 }
185237
186- ref , err := convertPatternID (rule .PatternID , isPMD7 )
238+ ref , err := convertPatternID (rule .PatternID )
187239 if err != nil {
188240 continue
189241 }
@@ -216,17 +268,18 @@ func createPmdConfigGeneric(
216268 return rulesetXML .String ()
217269}
218270
219- var pmd7Header = `<?xml version="1.0" encoding="UTF-8"?>
220- <ruleset name="Codacy PMD 7 Ruleset"
221- xmlns="https://pmd.github.io/ruleset/2.0.0"
222- xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
223- xsi:schemaLocation="https://pmd.github.io/ruleset/2.0.0 https://pmd.github.io/schemas/pmd-7.0.0.xsd">
224- <description>Codacy PMD 7 Ruleset</description>`
225-
226- var pmd6Header = `<?xml version="1.0"?>
271+ // CreatePmdConfig creates a PMD 6 configuration from the provided tool configuration
272+ func CreatePmdConfig (configuration []domain.PatternConfiguration ) string {
273+ return createPmdConfigGeneric (configuration , `<?xml version="1.0"?>
227274<ruleset name="Codacy PMD Ruleset"
228- xmlns="http://pmd.sourceforge.net/ruleset/2.0.0"
229- xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
230- xsi:schemaLocation="http://pmd.sourceforge.net/ruleset/2.0.0 https://pmd.sourceforge.io/ruleset_2_0_0.xsd">
231- <description>Codacy PMD Ruleset</description>
232- `
275+ xmlns="http://pmd.sourceforge.net/ruleset/2.0.0"
276+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
277+ xsi:schemaLocation="http://pmd.sourceforge.net/ruleset/2.0.0 https://pmd.sourceforge.io/ruleset_2_0_0.xsd">
278+ <description>Codacy PMD Ruleset</description>
279+
280+ ` , convertPatternIDToPMD , false )
281+ }
282+
283+ func CreatePmd7Config (configuration []domain.PatternConfiguration ) string {
284+ return createPmdConfigGeneric (configuration , pmd7Header , convertPatternIDToPMD7 , true )
285+ }
0 commit comments