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/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..3163088ae65
--- /dev/null
+++ b/sonar-java-plugin/src/main/resources/org/sonar/l10n/java/rules/java/Agentic_way_profile.json
@@ -0,0 +1,472 @@
+{
+ "name": "AI Quality Profile",
+ "ruleKeys": [
+ "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"
+ ]
+}
\ 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
new file mode 100644
index 00000000000..582d05a2d6f
--- /dev/null
+++ b/sonar-java-plugin/src/test/java/org/sonar/plugins/java/JavaAgenticWayProfileTest.java
@@ -0,0 +1,198 @@
+/*
+ * 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.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;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+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();
+ 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(467)
+ .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"
+ ));
+ }
+
+ @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);
+ }
+}
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);
}