Skip to content

Commit 61e1240

Browse files
SONARPY-237 Use sonar-analyzer-commons instead of squidbridge to define RulesDefinition (SonarSource#111)
1 parent c5b9831 commit 61e1240

7 files changed

Lines changed: 53 additions & 127 deletions

File tree

pom.xml

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,11 @@
134134
</exclusion>
135135
</exclusions>
136136
</dependency>
137+
<dependency>
138+
<groupId>org.sonarsource.analyzer-commons</groupId>
139+
<artifactId>sonar-analyzer-commons</artifactId>
140+
<version>1.3.0.116</version>
141+
</dependency>
137142
<dependency>
138143
<groupId>org.codehaus.staxmate</groupId>
139144
<artifactId>staxmate</artifactId>
@@ -169,11 +174,6 @@
169174
<artifactId>logback-classic</artifactId>
170175
<version>${logback.version}</version>
171176
</dependency>
172-
<dependency>
173-
<groupId>com.google.code.gson</groupId>
174-
<artifactId>gson</artifactId>
175-
<version>2.6.2</version>
176-
</dependency>
177177

178178
<!-- test dependencies -->
179179
<dependency>

python-checks/src/main/java/org/sonar/python/checks/CommentRegularExpressionCheck.java

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,10 +26,8 @@
2626
import org.sonar.check.Rule;
2727
import org.sonar.check.RuleProperty;
2828
import org.sonar.python.PythonCheck;
29-
import org.sonar.squidbridge.annotations.RuleTemplate;
3029

3130
@Rule(key = CommentRegularExpressionCheck.CHECK_KEY)
32-
@RuleTemplate
3331
public class CommentRegularExpressionCheck extends PythonCheck {
3432
public static final String CHECK_KEY = "CommentRegularExpression";
3533
private static final String DEFAULT_REGULAR_EXPRESSION = "";

python-checks/src/main/java/org/sonar/python/checks/XPathCheck.java

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,10 +26,8 @@
2626
import org.sonar.check.Rule;
2727
import org.sonar.check.RuleProperty;
2828
import org.sonar.python.PythonCheck;
29-
import org.sonar.squidbridge.annotations.RuleTemplate;
3029

3130
@Rule(key = XPathCheck.CHECK_KEY)
32-
@RuleTemplate
3331
public class XPathCheck extends PythonCheck {
3432
public static final String CHECK_KEY = "XPath";
3533
private static final String DEFAULT_XPATH_QUERY = "";

sonar-python-plugin/pom.xml

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -39,12 +39,12 @@
3939
<version>${project.version}</version>
4040
</dependency>
4141
<dependency>
42-
<groupId>org.sonarsource.sslr</groupId>
43-
<artifactId>sslr-testing-harness</artifactId>
42+
<groupId>org.sonarsource.analyzer-commons</groupId>
43+
<artifactId>sonar-analyzer-commons</artifactId>
4444
</dependency>
4545
<dependency>
46-
<groupId>com.google.code.gson</groupId>
47-
<artifactId>gson</artifactId>
46+
<groupId>org.sonarsource.sslr</groupId>
47+
<artifactId>sslr-testing-harness</artifactId>
4848
</dependency>
4949
<dependency>
5050
<groupId>commons-lang</groupId>
@@ -117,8 +117,8 @@
117117
<configuration>
118118
<rules>
119119
<requireFilesSize>
120-
<minsize>4000000</minsize>
121-
<maxsize>4100000</maxsize>
120+
<minsize>3700000</minsize>
121+
<maxsize>4000000</maxsize>
122122
<files>
123123
<file>${project.build.directory}/${project.build.finalName}.jar</file>
124124
</files>

sonar-python-plugin/src/main/java/org/sonar/plugins/python/PythonProfile.java

Lines changed: 5 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -19,21 +19,17 @@
1919
*/
2020
package org.sonar.plugins.python;
2121

22-
import com.google.common.io.Resources;
23-
import com.google.gson.Gson;
24-
import java.io.IOException;
25-
import java.net.URL;
26-
import java.nio.charset.StandardCharsets;
27-
import java.util.Set;
2822
import org.sonar.api.profiles.ProfileDefinition;
2923
import org.sonar.api.profiles.RulesProfile;
30-
import org.sonar.api.rules.Rule;
3124
import org.sonar.api.rules.RuleFinder;
3225
import org.sonar.api.utils.ValidationMessages;
3326
import org.sonar.python.checks.CheckList;
27+
import org.sonarsource.analyzer.commons.ProfileDefinitionReader;
3428

3529
public class PythonProfile extends ProfileDefinition {
3630

31+
static final String PROFILE_LOCATION = "org/sonar/l10n/py/rules/python/Sonar_way_profile.json";
32+
3733
private final RuleFinder ruleFinder;
3834

3935
public PythonProfile(RuleFinder ruleFinder) {
@@ -43,29 +39,9 @@ public PythonProfile(RuleFinder ruleFinder) {
4339
@Override
4440
public RulesProfile createProfile(ValidationMessages messages) {
4541
RulesProfile profile = RulesProfile.create(CheckList.SONAR_WAY_PROFILE, Python.KEY);
46-
47-
loadActiveKeysFromJsonProfile(profile);
42+
ProfileDefinitionReader definitionReader = new ProfileDefinitionReader(ruleFinder);
43+
definitionReader.activateRules(profile, CheckList.REPOSITORY_KEY, PROFILE_LOCATION);
4844
return profile;
4945
}
5046

51-
private void loadActiveKeysFromJsonProfile(RulesProfile rulesProfile) {
52-
for (String ruleKey : activatedRuleKeys()) {
53-
Rule rule = ruleFinder.findByKey(CheckList.REPOSITORY_KEY, ruleKey);
54-
rulesProfile.activateRule(rule, null);
55-
}
56-
}
57-
58-
public static Set<String> activatedRuleKeys() {
59-
URL profileUrl = PythonProfile.class.getResource("/org/sonar/l10n/py/rules/python/Sonar_way_profile.json");
60-
try {
61-
Gson gson = new Gson();
62-
return gson.fromJson(Resources.toString(profileUrl, StandardCharsets.UTF_8), Profile.class).ruleKeys;
63-
} catch (IOException e) {
64-
throw new IllegalStateException("Failed to read: " + profileUrl, e);
65-
}
66-
}
67-
68-
private static class Profile {
69-
Set<String> ruleKeys;
70-
}
7147
}

sonar-python-plugin/src/main/java/org/sonar/plugins/python/PythonRuleRepository.java

Lines changed: 22 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -19,27 +19,25 @@
1919
*/
2020
package org.sonar.plugins.python;
2121

22-
import com.google.common.io.Resources;
23-
import com.google.gson.Gson;
24-
import java.io.IOException;
25-
import java.net.URL;
26-
import java.nio.charset.StandardCharsets;
27-
import java.util.Locale;
22+
import java.util.Arrays;
23+
import java.util.HashSet;
24+
import java.util.List;
2825
import java.util.Set;
29-
import javax.annotation.Nullable;
30-
import org.sonar.api.rule.RuleStatus;
31-
import org.sonar.api.rules.RuleType;
32-
import org.sonar.api.server.debt.DebtRemediationFunction;
26+
import java.util.stream.Collectors;
27+
import java.util.stream.StreamSupport;
3328
import org.sonar.api.server.rule.RulesDefinition;
3429
import org.sonar.api.utils.Version;
3530
import org.sonar.python.checks.CheckList;
36-
import org.sonar.squidbridge.annotations.AnnotationBasedRulesDefinition;
31+
import org.sonarsource.analyzer.commons.RuleMetadataLoader;
3732

3833
public class PythonRuleRepository implements RulesDefinition {
3934

4035
private static final String REPOSITORY_NAME = "SonarAnalyzer";
4136

42-
private final Gson gson = new Gson();
37+
private static final String RESOURCE_FOLDER = "org/sonar/l10n/py/rules/python";
38+
39+
private static final Set<String> TEMPLATE_RULE_KEYS = new HashSet<>(Arrays.asList("XPath", "CommentRegularExpression"));
40+
4341
private final Version sonarRuntimeVersion;
4442

4543
public PythonRuleRepository(Version sonarRuntimeVersion) {
@@ -52,85 +50,26 @@ public void define(Context context) {
5250
.createRepository(CheckList.REPOSITORY_KEY, Python.KEY)
5351
.setName(REPOSITORY_NAME);
5452

55-
new AnnotationBasedRulesDefinition(repository, "py").addRuleClasses(false, CheckList.getChecks());
56-
57-
for (NewRule rule : repository.rules()) {
58-
addMetadata(rule, rule.key());
59-
}
53+
getRuleMetadataLoader().addRulesByAnnotatedClass(repository, getCheckClasses());
6054

61-
setupDefaultActivatedRules(repository);
55+
repository.rules().stream()
56+
.filter(rule -> TEMPLATE_RULE_KEYS.contains(rule.key()))
57+
.forEach(rule -> rule.setTemplate(true));
6258

6359
repository.done();
6460
}
6561

66-
private void setupDefaultActivatedRules(NewRepository repository) {
67-
boolean shouldSetupSonarLintProfile = sonarRuntimeVersion.isGreaterThanOrEqual(Version.parse("6.0"));
68-
if (shouldSetupSonarLintProfile) {
69-
Set<String> activatedRuleKeys = PythonProfile.activatedRuleKeys();
70-
for (NewRule rule : repository.rules()) {
71-
rule.setActivatedByDefault(activatedRuleKeys.contains(rule.key()));
72-
}
73-
}
74-
}
75-
76-
@Nullable
77-
private static String readRuleDefinitionResource(String fileName) {
78-
URL resource = PythonRuleRepository.class.getResource("/org/sonar/l10n/py/rules/python/" + fileName);
79-
if (resource == null) {
80-
return null;
81-
}
82-
try {
83-
return Resources.toString(resource, StandardCharsets.UTF_8);
84-
} catch (IOException e) {
85-
throw new IllegalStateException("Failed to read: " + resource, e);
86-
}
87-
}
88-
89-
private void addMetadata(NewRule rule, String metadataKey) {
90-
rule.setHtmlDescription(readRuleDefinitionResource(metadataKey + ".html"));
91-
String json = readRuleDefinitionResource(metadataKey + ".json");
92-
if (json != null) {
93-
RuleMetadata metadata = gson.fromJson(json, RuleMetadata.class);
94-
rule.setSeverity(metadata.defaultSeverity.toUpperCase(Locale.US));
95-
rule.setName(metadata.title);
96-
rule.setTags(metadata.tags);
97-
rule.setType(RuleType.valueOf(metadata.type));
98-
rule.setStatus(RuleStatus.valueOf(metadata.status.toUpperCase(Locale.US)));
99-
100-
if (metadata.remediation != null) {
101-
// metadata.remediation is null for template rules
102-
rule.setDebtRemediationFunction(metadata.remediation.remediationFunction(rule.debtRemediationFunctions()));
103-
rule.setGapDescription(metadata.remediation.linearDesc);
104-
}
62+
private RuleMetadataLoader getRuleMetadataLoader() {
63+
if (sonarRuntimeVersion.isGreaterThanOrEqual(Version.create(6, 0))) {
64+
return new RuleMetadataLoader(RESOURCE_FOLDER, PythonProfile.PROFILE_LOCATION);
65+
} else {
66+
return new RuleMetadataLoader(RESOURCE_FOLDER);
10567
}
10668
}
10769

108-
private static class RuleMetadata {
109-
String title;
110-
String status;
111-
String type;
112-
@Nullable
113-
Remediation remediation;
114-
115-
String[] tags;
116-
String defaultSeverity;
70+
private static List<Class> getCheckClasses() {
71+
return StreamSupport.stream(CheckList.getChecks().spliterator(), false)
72+
.collect(Collectors.toList());
11773
}
11874

119-
private static class Remediation {
120-
String func;
121-
String constantCost;
122-
String linearDesc;
123-
String linearOffset;
124-
String linearFactor;
125-
126-
private DebtRemediationFunction remediationFunction(DebtRemediationFunctions drf) {
127-
if (func.startsWith("Constant")) {
128-
return drf.constantPerIssue(constantCost.replace("mn", "min"));
129-
}
130-
if ("Linear".equals(func)) {
131-
return drf.linear(linearFactor.replace("mn", "min"));
132-
}
133-
return drf.linearWithOffset(linearFactor.replace("mn", "min"), linearOffset.replace("mn", "min"));
134-
}
135-
}
13675
}

sonar-python-plugin/src/test/java/org/sonar/plugins/python/PythonRuleRepositoryTest.java

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,21 @@ public void sonarlint() {
5555
assertThat(repository.rule("BackticksUsage").activatedByDefault()).isTrue();
5656
}
5757

58+
@Test
59+
public void ruleTemplates() {
60+
RulesDefinition.Repository repository = buildRepository(Version.parse("6.7"));
61+
assertThat(repository.rule("S100").template()).isFalse();
62+
assertThat(repository.rule("CommentRegularExpression").template()).isTrue();
63+
assertThat(repository.rule("XPath").template()).isTrue();
64+
65+
long templateCount = repository.rules().stream()
66+
.map(RulesDefinition.Rule::template)
67+
.filter(Boolean::booleanValue)
68+
.count();
69+
assertThat(repository.rules().size()).isGreaterThan(50);
70+
assertThat(templateCount).isEqualTo(2);
71+
}
72+
5873
private RulesDefinition.Repository buildRepository(Version sonarRuntimeVersion) {
5974
PythonRuleRepository ruleRepository = new PythonRuleRepository(sonarRuntimeVersion);
6075
RulesDefinition.Context context = new RulesDefinition.Context();

0 commit comments

Comments
 (0)