21
21
22
22
import org .slf4j .Logger ;
23
23
import org .slf4j .LoggerFactory ;
24
+ import org .sonar .api .batch .SensorContext ;
25
+ import org .sonar .api .batch .fs .FileSystem ;
26
+ import org .sonar .api .batch .fs .InputFile ;
27
+ import org .sonar .api .component .ResourcePerspectives ;
28
+ import org .sonar .api .issue .Issuable ;
29
+ import org .sonar .api .issue .Issue ;
24
30
import org .sonar .api .measures .CoreMetrics ;
25
31
import org .sonar .api .measures .Measure ;
26
32
import org .sonar .api .measures .PersistenceMode ;
27
33
import org .sonar .api .measures .RangeDistributionBuilder ;
34
+ import org .sonar .api .profiles .RulesProfile ;
35
+ import org .sonar .api .resources .Resource ;
36
+ import org .sonar .api .rule .RuleKey ;
37
+ import org .sonar .api .rules .ActiveRule ;
28
38
import org .w3c .dom .Document ;
29
39
import org .w3c .dom .Element ;
30
40
import org .w3c .dom .Node ;
@@ -67,23 +77,34 @@ public final class LizardReportParser {
67
77
private static final Number [] FUNCTIONS_DISTRIB_BOTTOM_LIMITS = {1 , 2 , 4 , 6 , 8 , 10 , 12 , 20 , 30 };
68
78
private static final Number [] FILES_DISTRIB_BOTTOM_LIMITS = {0 , 5 , 10 , 20 , 30 , 60 , 90 };
69
79
70
- private LizardReportParser () {
71
- // Prevent outside instantiation
80
+ private final FileSystem fileSystem ;
81
+ private final ResourcePerspectives resourcePerspectives ;
82
+ private final RulesProfile rulesProfile ;
83
+ private final SensorContext sensorContext ;
84
+
85
+ private LizardReportParser (final FileSystem fileSystem , final ResourcePerspectives resourcePerspectives ,
86
+ final RulesProfile rulesProfile , final SensorContext sensorContext ) {
87
+ this .fileSystem = fileSystem ;
88
+ this .resourcePerspectives = resourcePerspectives ;
89
+ this .rulesProfile = rulesProfile ;
90
+ this .sensorContext = sensorContext ;
72
91
}
73
92
74
93
/**
75
94
* @param xmlFile lizard xml report
76
95
* @return Map containing as key the name of the file and as value a list containing the measures for that file
77
96
*/
78
97
@ CheckForNull
79
- public static Map <String , List <Measure >> parseReport (final File xmlFile ) {
98
+ public static Map <String , List <Measure >> parseReport (final FileSystem fileSystem ,
99
+ final ResourcePerspectives resourcePerspectives , final RulesProfile rulesProfile ,
100
+ final SensorContext sensorContext , final File xmlFile ) {
80
101
Map <String , List <Measure >> result = null ;
81
102
DocumentBuilderFactory factory = DocumentBuilderFactory .newInstance ();
82
103
83
104
try {
84
105
DocumentBuilder builder = factory .newDocumentBuilder ();
85
106
Document document = builder .parse (xmlFile );
86
- result = new LizardReportParser ().parseFile (document );
107
+ result = new LizardReportParser (fileSystem , resourcePerspectives , rulesProfile , sensorContext ).parseFile (document );
87
108
} catch (final FileNotFoundException e ) {
88
109
LOGGER .error ("Lizard Report not found {}" , xmlFile , e );
89
110
} catch (final IOException | ParserConfigurationException | SAXException e ) {
@@ -197,6 +218,7 @@ private void addComplexityFunctionMeasures(Map<String, List<Measure>> reportMeas
197
218
complexityDistribution .add (func .getCyclomaticComplexity ());
198
219
count ++;
199
220
complexityInFunctions += func .getCyclomaticComplexity ();
221
+ createFunctionComplexityIssue (entry .getKey (), func );
200
222
}
201
223
}
202
224
@@ -205,23 +227,110 @@ private void addComplexityFunctionMeasures(Map<String, List<Measure>> reportMeas
205
227
for (Measure m : entry .getValue ()) {
206
228
if (m .getMetric ().getKey ().equalsIgnoreCase (CoreMetrics .FILE_COMPLEXITY .getKey ())) {
207
229
complex = m .getValue ();
230
+ createFileComplexityIssue (entry .getKey (), (int ) complex );
208
231
break ;
209
232
}
210
233
}
211
234
212
235
double complexMean = complex / (double ) count ;
213
- entry .getValue ().addAll (buildFuncionMeasuresList (complexMean , complexityInFunctions , complexityDistribution ));
236
+ entry .getValue ().addAll (buildFunctionMeasuresList (complexMean , complexityInFunctions , complexityDistribution ));
214
237
}
215
238
}
216
239
}
217
240
241
+ private void createFileComplexityIssue (String fileName , int complexity ) {
242
+ ActiveRule activeRule = rulesProfile .getActiveRule (
243
+ LizardRulesDefinition .REPOSITORY_KEY ,
244
+ LizardRulesDefinition .FILE_CYCLOMATIC_COMPLEXITY_RULE_KEY );
245
+
246
+ if (activeRule == null ) {
247
+ // Rule is not active
248
+ return ;
249
+ }
250
+
251
+ int threshold = Integer .parseInt (activeRule .getParameter (LizardRulesDefinition .FILE_CYCLOMATIC_COMPLEXITY_PARAM_KEY ));
252
+
253
+ if (complexity <= threshold ) {
254
+ // Complexity is lower or equal to the defined threshold
255
+ return ;
256
+ }
257
+
258
+ final InputFile inputFile = fileSystem .inputFile (fileSystem .predicates ().hasPath (fileName ));
259
+ final Resource resource = inputFile == null ? null : sensorContext .getResource (inputFile );
260
+
261
+ if (resource == null ) {
262
+ LOGGER .debug ("Skipping file (not found in index): {}" , fileName );
263
+ return ;
264
+ }
265
+
266
+ Issuable issuable = resourcePerspectives .as (Issuable .class , resource );
267
+
268
+ if (issuable != null ) {
269
+ Issue issue = issuable .newIssueBuilder ()
270
+ .ruleKey (RuleKey .of (LizardRulesDefinition .REPOSITORY_KEY , LizardRulesDefinition .FILE_CYCLOMATIC_COMPLEXITY_RULE_KEY ))
271
+ .message (String .format ("The Cyclomatic Complexity of this file \" %s\" is %d which is greater than %d authorized." , fileName , complexity , threshold ))
272
+ .effortToFix ((double ) (complexity - threshold ))
273
+ .build ();
274
+
275
+ issuable .addIssue (issue );
276
+ }
277
+ }
278
+
279
+ private void createFunctionComplexityIssue (String fileName , ObjCFunction func ) {
280
+ ActiveRule activeRule = rulesProfile .getActiveRule (
281
+ LizardRulesDefinition .REPOSITORY_KEY ,
282
+ LizardRulesDefinition .FUNCTION_CYCLOMATIC_COMPLEXITY_RULE_KEY );
283
+
284
+ if (activeRule == null ) {
285
+ // Rule is not active
286
+ return ;
287
+ }
288
+
289
+ int complexity = func .getCyclomaticComplexity ();
290
+ int threshold = Integer .parseInt (activeRule .getParameter (LizardRulesDefinition .FUNCTION_CYCLOMATIC_COMPLEXITY_PARAM_KEY ));
291
+
292
+ if (complexity <= threshold ) {
293
+ // Complexity is lower or equal to the defined threshold
294
+ return ;
295
+ }
296
+
297
+ final InputFile inputFile = fileSystem .inputFile (fileSystem .predicates ().hasPath (fileName ));
298
+ final Resource resource = inputFile == null ? null : sensorContext .getResource (inputFile );
299
+
300
+ if (resource == null ) {
301
+ LOGGER .debug ("Skipping file (not found in index): {}" , fileName );
302
+ return ;
303
+ }
304
+
305
+ Issuable issuable = resourcePerspectives .as (Issuable .class , resource );
306
+
307
+ if (issuable != null ) {
308
+ String name = func .getName ();
309
+
310
+ int lastColonIndex = name .lastIndexOf (':' );
311
+ Integer lineNumber = lastColonIndex == -1 ? null : Integer .valueOf (name .substring (lastColonIndex + 1 ));
312
+
313
+ int atIndex = name .indexOf (" at " );
314
+ String functionName = atIndex == -1 ? name : name .substring (0 , atIndex );
315
+
316
+ Issue issue = issuable .newIssueBuilder ()
317
+ .ruleKey (RuleKey .of (LizardRulesDefinition .REPOSITORY_KEY , LizardRulesDefinition .FUNCTION_CYCLOMATIC_COMPLEXITY_RULE_KEY ))
318
+ .message (String .format ("The Cyclomatic Complexity of this function \" %s\" is %d which is greater than %d authorized." , functionName , complexity , threshold ))
319
+ .line (lineNumber )
320
+ .effortToFix ((double ) (complexity - threshold ))
321
+ .build ();
322
+
323
+ issuable .addIssue (issue );
324
+ }
325
+ }
326
+
218
327
/**
219
328
* @param complexMean average complexity per function in a file
220
329
* @param complexityInFunctions Entire complexity in functions
221
330
* @param builder Builder ready to build FUNCTION_COMPLEXITY_DISTRIBUTION
222
331
* @return list of Measures containing FUNCTION_COMPLEXITY_DISTRIBUTION, FUNCTION_COMPLEXITY and COMPLEXITY_IN_FUNCTIONS
223
332
*/
224
- public List <Measure > buildFuncionMeasuresList (double complexMean , int complexityInFunctions ,
333
+ public List <Measure > buildFunctionMeasuresList (double complexMean , int complexityInFunctions ,
225
334
RangeDistributionBuilder builder ) {
226
335
List <Measure > list = new ArrayList <>();
227
336
list .add (new Measure (CoreMetrics .FUNCTION_COMPLEXITY , complexMean ));
0 commit comments