From 31312fcebeeb176ce296e71f65b3ed2edcfef7a9 Mon Sep 17 00:00:00 2001 From: Dorian Burihabwa Date: Fri, 20 Mar 2026 11:57:56 +0100 Subject: [PATCH 1/3] SONARJAVA-6205 Add an agentic focused quality profile for Java --- .../plugins/java/JavaAgenticWayProfile.java | 52 ++ .../org/sonar/plugins/java/JavaPlugin.java | 1 + .../plugins/java/JavaSonarWayProfile.java | 16 +- .../plugins/java/QualityProfileUtils.java | 52 ++ .../java/rules/java/Agentic_way_profile.json | 473 ++++++++++++++++++ .../java/JavaAgenticWayProfileTest.java | 107 ++++ .../sonar/plugins/java/JavaPluginTest.java | 4 +- 7 files changed, 692 insertions(+), 13 deletions(-) create mode 100644 sonar-java-plugin/src/main/java/org/sonar/plugins/java/JavaAgenticWayProfile.java create mode 100644 sonar-java-plugin/src/main/java/org/sonar/plugins/java/QualityProfileUtils.java create mode 100644 sonar-java-plugin/src/main/resources/org/sonar/l10n/java/rules/java/Agentic_way_profile.json create mode 100644 sonar-java-plugin/src/test/java/org/sonar/plugins/java/JavaAgenticWayProfileTest.java diff --git a/sonar-java-plugin/src/main/java/org/sonar/plugins/java/JavaAgenticWayProfile.java b/sonar-java-plugin/src/main/java/org/sonar/plugins/java/JavaAgenticWayProfile.java new file mode 100644 index 00000000000..67efdb9e5bc --- /dev/null +++ b/sonar-java-plugin/src/main/java/org/sonar/plugins/java/JavaAgenticWayProfile.java @@ -0,0 +1,52 @@ +/* + * SonarQube Java + * Copyright (C) 2012-2025 SonarSource Sàrl + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the Sonar Source-Available License Version 1, as published by SonarSource SA. + * + * This program 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 Sonar Source-Available License for more details. + * + * You should have received a copy of the Sonar Source-Available License + * along with this program; if not, see https://sonarsource.com/license/ssal/ + */ +package org.sonar.plugins.java; + +import java.util.Set; +import javax.annotation.Nullable; +import org.sonar.api.rule.RuleKey; +import org.sonar.api.server.profile.BuiltInQualityProfilesDefinition; +import org.sonar.plugins.java.api.ProfileRegistrar; +import org.sonarsource.api.sonarlint.SonarLintSide; + +@SonarLintSide +public class JavaAgenticWayProfile implements BuiltInQualityProfilesDefinition { + private final ProfileRegistrar[] profileRegistrars; + + /** + * Constructor used by Pico container (SC) when no ProfileRegistrar are available + */ + public JavaAgenticWayProfile() { + this(null); + } + + public JavaAgenticWayProfile(@Nullable ProfileRegistrar[] profileRegistrars) { + this.profileRegistrars = profileRegistrars; + } + + @Override + public void define(Context context) { + NewBuiltInQualityProfile agenticWay = context.createBuiltInQualityProfile("AI Quality Profile", Java.KEY); + Set ruleKeys = QualityProfileUtils.registerRulesFromJson( + "/org/sonar/l10n/java/rules/java/Agentic_way_profile.json", + this.profileRegistrars + ); + + ruleKeys.forEach(ruleKey -> agenticWay.activateRule(ruleKey.repository(), ruleKey.rule())); + agenticWay.done(); + } +} diff --git a/sonar-java-plugin/src/main/java/org/sonar/plugins/java/JavaPlugin.java b/sonar-java-plugin/src/main/java/org/sonar/plugins/java/JavaPlugin.java index c1f64a5613c..7cd9a3788cc 100644 --- a/sonar-java-plugin/src/main/java/org/sonar/plugins/java/JavaPlugin.java +++ b/sonar-java-plugin/src/main/java/org/sonar/plugins/java/JavaPlugin.java @@ -62,6 +62,7 @@ public void define(Context context) { list.addAll(SurefireExtensions.getExtensions()); list.add(DroppedPropertiesSensor.class); list.add(JavaSonarWayProfile.class); + list.add(JavaAgenticWayProfile.class); list.add(ClasspathForMain.class); ExternalReportExtensions.define(context); diff --git a/sonar-java-plugin/src/main/java/org/sonar/plugins/java/JavaSonarWayProfile.java b/sonar-java-plugin/src/main/java/org/sonar/plugins/java/JavaSonarWayProfile.java index c2b47e553de..4f183d4ba4f 100644 --- a/sonar-java-plugin/src/main/java/org/sonar/plugins/java/JavaSonarWayProfile.java +++ b/sonar-java-plugin/src/main/java/org/sonar/plugins/java/JavaSonarWayProfile.java @@ -26,10 +26,8 @@ import org.slf4j.LoggerFactory; import org.sonar.api.rule.RuleKey; import org.sonar.api.server.profile.BuiltInQualityProfilesDefinition; -import org.sonar.java.GeneratedCheckList; import org.sonar.java.annotations.VisibleForTesting; import org.sonar.plugins.java.api.ProfileRegistrar; -import org.sonarsource.analyzer.commons.BuiltInQualityProfileJsonLoader; import org.sonarsource.api.sonarlint.SonarLintSide; /** @@ -66,12 +64,10 @@ public JavaSonarWayProfile(@Nullable ProfileRegistrar[] profileRegistrars) { @Override public void define(Context context) { NewBuiltInQualityProfile sonarWay = context.createBuiltInQualityProfile("Sonar way", Java.KEY); - Set ruleKeys = new HashSet<>(sonarJavaSonarWayRuleKeys()); - if (profileRegistrars != null) { - for (ProfileRegistrar profileRegistrar : profileRegistrars) { - profileRegistrar.register(ruleKeys::addAll); - } - } + Set ruleKeys = QualityProfileUtils.registerRulesFromJson( + SONAR_WAY_PATH, + this.profileRegistrars + ); // Former activation mechanism, it should be removed once sonar-security and sonar-dataflow-bug-detection // support the new mechanism: @@ -89,9 +85,7 @@ public void define(Context context) { } static Set sonarJavaSonarWayRuleKeys() { - return BuiltInQualityProfileJsonLoader.loadActiveKeysFromJsonProfile(SONAR_WAY_PATH).stream() - .map(rule -> RuleKey.of(GeneratedCheckList.REPOSITORY_KEY, rule)) - .collect(Collectors.toSet()); + return QualityProfileUtils.loadRuleKeys(SONAR_WAY_PATH); } @VisibleForTesting diff --git a/sonar-java-plugin/src/main/java/org/sonar/plugins/java/QualityProfileUtils.java b/sonar-java-plugin/src/main/java/org/sonar/plugins/java/QualityProfileUtils.java new file mode 100644 index 00000000000..ac768d9eded --- /dev/null +++ b/sonar-java-plugin/src/main/java/org/sonar/plugins/java/QualityProfileUtils.java @@ -0,0 +1,52 @@ +/* + * SonarQube Java + * Copyright (C) 2012-2025 SonarSource Sàrl + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the Sonar Source-Available License Version 1, as published by SonarSource SA. + * + * This program 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 Sonar Source-Available License for more details. + * + * You should have received a copy of the Sonar Source-Available License + * along with this program; if not, see https://sonarsource.com/license/ssal/ + */ +package org.sonar.plugins.java; + +import java.util.HashSet; +import java.util.Set; +import java.util.stream.Collectors; +import javax.annotation.Nullable; +import org.sonar.api.rule.RuleKey; +import org.sonar.java.GeneratedCheckList; +import org.sonar.plugins.java.api.ProfileRegistrar; +import org.sonarsource.analyzer.commons.BuiltInQualityProfileJsonLoader; + +class QualityProfileUtils { + private QualityProfileUtils() { + /* This utility class should not be instantiated */ + } + + static Set registerRulesFromJson( + String pathToJsonProfile, + @Nullable ProfileRegistrar[] profileRegistrars) { + + Set ruleKeys = new HashSet<>(loadRuleKeys(pathToJsonProfile)); + if (profileRegistrars != null) { + for (ProfileRegistrar profileRegistrar : profileRegistrars) { + profileRegistrar.register(ruleKeys::addAll); + } + } + + return ruleKeys; + } + + static Set loadRuleKeys(final String pathToJsonProfile) { + return BuiltInQualityProfileJsonLoader.loadActiveKeysFromJsonProfile(pathToJsonProfile).stream() + .map(rule -> RuleKey.of(GeneratedCheckList.REPOSITORY_KEY, rule)) + .collect(Collectors.toSet()); + } +} diff --git a/sonar-java-plugin/src/main/resources/org/sonar/l10n/java/rules/java/Agentic_way_profile.json b/sonar-java-plugin/src/main/resources/org/sonar/l10n/java/rules/java/Agentic_way_profile.json new file mode 100644 index 00000000000..a13126eaaa7 --- /dev/null +++ b/sonar-java-plugin/src/main/resources/org/sonar/l10n/java/rules/java/Agentic_way_profile.json @@ -0,0 +1,473 @@ +{ + "name": "AI Quality Profile", + "ruleKeys": [ + "S100", + "S106", + "S107", + "S108", + "S112", + "S119", + "S120", + "S125", + "S127", + "S128", + "S131", + "S135", + "S899", + "S1065", + "S1068", + "S1111", + "S1113", + "S1116", + "S1117", + "S1118", + "S1119", + "S1121", + "S1123", + "S1125", + "S1126", + "S1128", + "S1130", + "S1133", + "S1134", + "S1135", + "S1141", + "S1143", + "S1144", + "S1149", + "S1150", + "S1153", + "S1155", + "S1157", + "S1158", + "S1161", + "S1163", + "S1165", + "S1168", + "S1170", + "S1171", + "S1172", + "S1174", + "S1175", + "S1181", + "S1182", + "S1185", + "S1186", + "S1190", + "S1191", + "S1192", + "S1193", + "S1199", + "S1201", + "S1206", + "S1210", + "S1214", + "S1215", + "S1217", + "S1219", + "S1220", + "S1221", + "S1223", + "S1226", + "S1264", + "S1301", + "S1313", + "S1317", + "S1319", + "S1444", + "S1450", + "S1452", + "S1479", + "S1481", + "S1488", + "S1596", + "S1598", + "S1602", + "S1604", + "S1607", + "S1612", + "S1640", + "S1643", + "S1656", + "S1700", + "S1751", + "S1764", + "S1844", + "S1845", + "S1849", + "S1854", + "S1858", + "S1860", + "S1862", + "S1871", + "S1872", + "S1874", + "S1905", + "S1940", + "S1948", + "S1989", + "S1994", + "S2053", + "S2055", + "S2060", + "S2061", + "S2062", + "S2065", + "S2066", + "S2068", + "S2077", + "S2092", + "S2093", + "S2094", + "S2097", + "S2109", + "S2110", + "S2111", + "S2112", + "S2114", + "S2115", + "S2116", + "S2118", + "S2119", + "S2121", + "S2122", + "S2123", + "S2127", + "S2129", + "S2130", + "S2133", + "S2134", + "S2139", + "S2140", + "S2142", + "S2147", + "S2151", + "S2153", + "S2154", + "S2157", + "S2159", + "S2160", + "S2166", + "S2167", + "S2168", + "S2175", + "S2176", + "S2177", + "S2178", + "S2183", + "S2184", + "S2185", + "S2186", + "S2187", + "S2188", + "S2200", + "S2201", + "S2204", + "S2209", + "S2225", + "S2226", + "S2229", + "S2230", + "S2232", + "S2234", + "S2235", + "S2236", + "S2245", + "S2251", + "S2252", + "S2254", + "S2257", + "S2272", + "S2273", + "S2274", + "S2275", + "S2276", + "S2293", + "S2326", + "S2386", + "S2387", + "S2388", + "S2390", + "S2437", + "S2438", + "S2440", + "S2441", + "S2442", + "S2445", + "S2446", + "S2447", + "S2479", + "S2612", + "S2629", + "S2638", + "S2639", + "S2674", + "S2675", + "S2676", + "S2677", + "S2681", + "S2692", + "S2695", + "S2696", + "S2699", + "S2718", + "S2737", + "S2757", + "S2761", + "S2786", + "S2789", + "S2864", + "S2885", + "S2886", + "S2924", + "S2925", + "S2970", + "S2975", + "S3008", + "S3010", + "S3011", + "S3012", + "S3014", + "S3020", + "S3024", + "S3033", + "S3034", + "S3038", + "S3039", + "S3042", + "S3046", + "S3051", + "S3063", + "S3064", + "S3066", + "S3067", + "S3077", + "S3078", + "S3252", + "S3305", + "S3329", + "S3330", + "S3346", + "S3358", + "S3398", + "S3400", + "S3415", + "S3416", + "S3436", + "S3457", + "S3551", + "S3577", + "S3599", + "S3626", + "S3631", + "S3740", + "S3751", + "S3752", + "S3753", + "S3776", + "S3864", + "S3878", + "S3923", + "S3972", + "S3973", + "S3981", + "S3984", + "S3985", + "S3986", + "S4030", + "S4032", + "S4034", + "S4036", + "S4042", + "S4065", + "S4087", + "S4143", + "S4144", + "S4201", + "S4274", + "S4275", + "S4276", + "S4347", + "S4348", + "S4349", + "S4351", + "S4423", + "S4425", + "S4426", + "S4433", + "S4434", + "S4454", + "S4488", + "S4502", + "S4507", + "S4512", + "S4517", + "S4524", + "S4544", + "S4601", + "S4602", + "S4635", + "S4682", + "S4684", + "S4738", + "S4790", + "S4830", + "S4970", + "S4973", + "S5042", + "S5122", + "S5164", + "S5247", + "S5301", + "S5320", + "S5322", + "S5324", + "S5332", + "S5344", + "S5361", + "S5443", + "S5445", + "S5527", + "S5542", + "S5547", + "S5659", + "S5679", + "S5689", + "S5693", + "S5738", + "S5776", + "S5777", + "S5778", + "S5779", + "S5783", + "S5785", + "S5790", + "S5803", + "S5804", + "S5808", + "S5810", + "S5826", + "S5831", + "S5833", + "S5838", + "S5842", + "S5843", + "S5845", + "S5846", + "S5850", + "S5852", + "S5855", + "S5856", + "S5863", + "S5866", + "S5868", + "S5876", + "S5917", + "S5960", + "S5967", + "S5969", + "S5994", + "S5996", + "S5998", + "S6001", + "S6002", + "S6068", + "S6070", + "S6103", + "S6104", + "S6126", + "S6201", + "S6202", + "S6203", + "S6204", + "S6205", + "S6206", + "S6207", + "S6208", + "S6209", + "S6216", + "S6218", + "S6243", + "S6262", + "S6263", + "S6288", + "S6293", + "S6301", + "S6326", + "S6331", + "S6353", + "S6355", + "S6362", + "S6363", + "S6395", + "S6396", + "S6397", + "S6418", + "S6432", + "S6437", + "S6485", + "S6539", + "S6541", + "S6548", + "S6806", + "S6809", + "S6810", + "S6814", + "S6816", + "S6817", + "S6818", + "S6831", + "S6838", + "S6856", + "S6857", + "S6862", + "S6863", + "S6876", + "S6877", + "S6878", + "S6880", + "S6881", + "S6885", + "S6889", + "S6901", + "S6905", + "S6906", + "S6909", + "S6913", + "S6915", + "S6916", + "S7158", + "S7177", + "S7178", + "S7179", + "S7180", + "S7183", + "S7184", + "S7185", + "S7186", + "S7190", + "S7409", + "S7435", + "S7466", + "S7467", + "S7474", + "S7475", + "S7476", + "S7477", + "S7478", + "S7479", + "S7481", + "S7482", + "S7629", + "S8346", + "S8432", + "S8433", + "S8444", + "S8445", + "S8446", + "S8447", + "S8450", + "S8465", + "S8469" + ] +} diff --git a/sonar-java-plugin/src/test/java/org/sonar/plugins/java/JavaAgenticWayProfileTest.java b/sonar-java-plugin/src/test/java/org/sonar/plugins/java/JavaAgenticWayProfileTest.java new file mode 100644 index 00000000000..1de94c21e9a --- /dev/null +++ b/sonar-java-plugin/src/test/java/org/sonar/plugins/java/JavaAgenticWayProfileTest.java @@ -0,0 +1,107 @@ +/* + * SonarQube Java + * Copyright (C) 2012-2025 SonarSource Sàrl + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the Sonar Source-Available License Version 1, as published by SonarSource SA. + * + * This program 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 Sonar Source-Available License for more details. + * + * You should have received a copy of the Sonar Source-Available License + * along with this program; if not, see https://sonarsource.com/license/ssal/ + */ +package org.sonar.plugins.java; + + +import java.util.List; +import java.util.Map; +import org.junit.jupiter.api.Test; +import org.sonar.api.server.profile.BuiltInQualityProfilesDefinition; + +import static org.assertj.core.api.Assertions.assertThat; + +class JavaAgenticWayProfileTest { + + @Test + void profile_is_registered_as_expected() { + JavaAgenticWayProfile profile = new JavaAgenticWayProfile(); + BuiltInQualityProfilesDefinition.Context context = new BuiltInQualityProfilesDefinition.Context(); + profile.define(context); + + Map> profilesPerLanguages = context.profilesByLanguageAndName(); + assertThat(profilesPerLanguages).containsOnlyKeys("java"); + assertThat(profilesPerLanguages.get("java")).containsOnlyKeys("AI Quality Profile"); + + BuiltInQualityProfilesDefinition.BuiltInQualityProfile actualProfile = profilesPerLanguages.get("java").get("AI Quality Profile"); + assertThat(actualProfile.isDefault()).isFalse(); + assertThat(actualProfile.rules()) + .hasSize(468) + .extracting(BuiltInQualityProfilesDefinition.BuiltInActiveRule::ruleKey) + .doesNotContainAnyElementsOf(List.of( + "S101", + "S110", + "S114", + "S115", + "S116", + "S117", + "S1066", + "S1075", + "S1104", + "S1110", + "S1124", + "S1195", + "S1197", + "S1611", + "S1659", + "S1710", + "S4719", + "S4838", + "S4925", + "S4929", + "S4968", + "S4977", + "S5261", + "S5329", + "S5411", + "S5413", + "S5663", + "S5664", + "S5665", + "S5669", + "S5786", + "S5841", + "S5853", + "S5854", + "S5857", + "S5860", + "S5869", + "S5958", + "S5961", + "S5973", + "S5976", + "S5993", + "S6019", + "S6035", + "S6213", + "S6217", + "S6219", + "S6241", + "S6242", + "S6244", + "S6246", + "S6804", + "S6813", + "S6829", + "S6830", + "S6832", + "S6833", + "S6837", + "S6912", + "S8491" + )); + } +} diff --git a/sonar-java-plugin/src/test/java/org/sonar/plugins/java/JavaPluginTest.java b/sonar-java-plugin/src/test/java/org/sonar/plugins/java/JavaPluginTest.java index eb700626422..06031c47465 100644 --- a/sonar-java-plugin/src/test/java/org/sonar/plugins/java/JavaPluginTest.java +++ b/sonar-java-plugin/src/test/java/org/sonar/plugins/java/JavaPluginTest.java @@ -51,7 +51,7 @@ void sonarqube_9_9_extensions() { Plugin.Context context = new Plugin.Context(sqCommunity); javaPlugin.define(context); assertThat(context.getExtensions()) - .hasSize(36) + .hasSize(37) .doesNotContain(Jasper.class); } @@ -61,7 +61,7 @@ void sonarqube_9_9_commercial_extensions() { Plugin.Context context = new Plugin.Context(sqEnterprise); javaPlugin.define(context); assertThat(context.getExtensions()) - .hasSize(37) + .hasSize(38) .contains(Jasper.class); } From 472d1cc0be1776a13b0f628e32c66ef1141ab99e Mon Sep 17 00:00:00 2001 From: Dorian Burihabwa Date: Mon, 23 Mar 2026 17:56:41 +0100 Subject: [PATCH 2/3] SONARJAVA-6205 Add test script to generate profile from CSV file --- pom.xml | 5 + sonar-java-plugin/pom.xml | 5 + .../java/JavaAgenticWayProfileTest.java | 91 +++++++++++++++++++ 3 files changed, 101 insertions(+) diff --git a/pom.xml b/pom.xml index 3fd1a140b57..fd0fa8f0522 100644 --- a/pom.xml +++ b/pom.xml @@ -280,6 +280,11 @@ commons-lang3 3.20.0 + + org.apache.commons + commons-csv + 1.14.1 + org.springframework spring-expression diff --git a/sonar-java-plugin/pom.xml b/sonar-java-plugin/pom.xml index e8818a56508..7640a1028ab 100644 --- a/sonar-java-plugin/pom.xml +++ b/sonar-java-plugin/pom.xml @@ -137,6 +137,11 @@ ${project.version} test + + org.apache.commons + commons-csv + test + diff --git a/sonar-java-plugin/src/test/java/org/sonar/plugins/java/JavaAgenticWayProfileTest.java b/sonar-java-plugin/src/test/java/org/sonar/plugins/java/JavaAgenticWayProfileTest.java index 1de94c21e9a..b698eab4f2c 100644 --- a/sonar-java-plugin/src/test/java/org/sonar/plugins/java/JavaAgenticWayProfileTest.java +++ b/sonar-java-plugin/src/test/java/org/sonar/plugins/java/JavaAgenticWayProfileTest.java @@ -17,8 +17,19 @@ package org.sonar.plugins.java; +import java.io.BufferedWriter; +import java.io.FileReader; +import java.io.FileWriter; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; import java.util.List; import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; +import org.apache.commons.csv.CSVFormat; +import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import org.sonar.api.server.profile.BuiltInQualityProfilesDefinition; @@ -26,6 +37,8 @@ class JavaAgenticWayProfileTest { + private static final Path RULE_DESCRIPTION_DIRECTORY = Path.of("sonar-java-plugin", "src", "main", "resources", "org", "sonar", "l10n", "java", "rules", "java"); + @Test void profile_is_registered_as_expected() { JavaAgenticWayProfile profile = new JavaAgenticWayProfile(); @@ -104,4 +117,82 @@ void profile_is_registered_as_expected() { "S8491" )); } + + @Disabled("This method should be used as a utility method to generate a quality profile from a CSV file") + void generate_ai_quality_profile() throws IOException { + generate( + Path.of("Path", "to", "your", "input.csv"), + RULE_DESCRIPTION_DIRECTORY.resolve("Agentic_way_profile.json"), + "AI Quality Profile" + ); + } + + public static Path generate(Path input, Path output, String profileName) throws IOException { + CSVFormat csvFormat = CSVFormat.DEFAULT.builder() + .setHeader() + .setSkipHeaderRecord(true) + .get(); + + Set keysOfImplementedRules = getImplementedRuleKeys(); + + String ruleKeys; + try (FileReader in = new FileReader(input.toFile(), StandardCharsets.UTF_8)) { + ruleKeys = csvFormat.parse(in).stream() + // Filter rules that have not been classified as AI and Sonar Way + .filter(ruleRecord -> "AI and Sonar way".equalsIgnoreCase(ruleRecord.get("classification_status"))) + // Recover keys + .map(ruleRecord -> ruleRecord.get("ruleid")) + // Filter out keys that do not have a sonar-java implementation + .filter(keysOfImplementedRules::contains) + // Sort keys + .sorted(JavaAgenticWayProfileTest::compareRuleKeys) + // Surround with double quotes for output in JSON document + .map(" \"%s\""::formatted) + // Comma and line separate rule keys + .collect(Collectors.joining(",%s".formatted(System.lineSeparator()))); + } + try ( + FileWriter out = new FileWriter(output.toFile(), StandardCharsets.UTF_8); + BufferedWriter writer = new BufferedWriter(out) + ) { + writer.write("{"); + writer.newLine(); + writer.write(" \"name\": \"%s\",".formatted(profileName)); + writer.newLine(); + writer.write(" \"ruleKeys\": ["); + writer.newLine(); + writer.write(ruleKeys); + writer.newLine(); + writer.write(" ]"); + writer.newLine(); + writer.write("}"); + } + return output; + + } + + private static Set getImplementedRuleKeys() throws IOException { + if (!Files.isDirectory(RULE_DESCRIPTION_DIRECTORY)) { + throw new IllegalStateException("This should not happen!"); + } + return Files.list(RULE_DESCRIPTION_DIRECTORY) + .filter(path -> !path.endsWith("_profile.json")) + .map(path -> { + String fileName = path.getFileName().toString(); + return fileName.substring(0, fileName.lastIndexOf('.')); + }) + .collect(Collectors.toSet()); + } + + private static Integer getSortingKey(String ruleKey) { + try { + return Integer.parseInt(ruleKey.substring(1)); + } catch (NumberFormatException ignored) { + return Integer.MIN_VALUE; + } + } + + private static int compareRuleKeys(String first, String second) { + return getSortingKey(first) - getSortingKey(second); + } } From 35e7ca44f99271391feccbb105dc726f66d7e587 Mon Sep 17 00:00:00 2001 From: Dorian Burihabwa Date: Mon, 23 Mar 2026 17:57:45 +0100 Subject: [PATCH 3/3] SONARJAVA-6205 Remove S100 from AI quality profile --- .../org/sonar/l10n/java/rules/java/Agentic_way_profile.json | 3 +-- .../java/org/sonar/plugins/java/JavaAgenticWayProfileTest.java | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/sonar-java-plugin/src/main/resources/org/sonar/l10n/java/rules/java/Agentic_way_profile.json b/sonar-java-plugin/src/main/resources/org/sonar/l10n/java/rules/java/Agentic_way_profile.json index a13126eaaa7..3163088ae65 100644 --- a/sonar-java-plugin/src/main/resources/org/sonar/l10n/java/rules/java/Agentic_way_profile.json +++ b/sonar-java-plugin/src/main/resources/org/sonar/l10n/java/rules/java/Agentic_way_profile.json @@ -1,7 +1,6 @@ { "name": "AI Quality Profile", "ruleKeys": [ - "S100", "S106", "S107", "S108", @@ -470,4 +469,4 @@ "S8465", "S8469" ] -} +} \ No newline at end of file diff --git a/sonar-java-plugin/src/test/java/org/sonar/plugins/java/JavaAgenticWayProfileTest.java b/sonar-java-plugin/src/test/java/org/sonar/plugins/java/JavaAgenticWayProfileTest.java index b698eab4f2c..582d05a2d6f 100644 --- a/sonar-java-plugin/src/test/java/org/sonar/plugins/java/JavaAgenticWayProfileTest.java +++ b/sonar-java-plugin/src/test/java/org/sonar/plugins/java/JavaAgenticWayProfileTest.java @@ -52,7 +52,7 @@ void profile_is_registered_as_expected() { BuiltInQualityProfilesDefinition.BuiltInQualityProfile actualProfile = profilesPerLanguages.get("java").get("AI Quality Profile"); assertThat(actualProfile.isDefault()).isFalse(); assertThat(actualProfile.rules()) - .hasSize(468) + .hasSize(467) .extracting(BuiltInQualityProfilesDefinition.BuiltInActiveRule::ruleKey) .doesNotContainAnyElementsOf(List.of( "S101",