2121
2222import org .slf4j .Logger ;
2323import 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 ;
2430import org .sonar .api .measures .CoreMetrics ;
2531import org .sonar .api .measures .Measure ;
2632import org .sonar .api .measures .PersistenceMode ;
2733import 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 ;
2838import org .w3c .dom .Document ;
2939import org .w3c .dom .Element ;
3040import org .w3c .dom .Node ;
@@ -67,23 +77,34 @@ public final class LizardReportParser {
6777 private static final Number [] FUNCTIONS_DISTRIB_BOTTOM_LIMITS = {1 , 2 , 4 , 6 , 8 , 10 , 12 , 20 , 30 };
6878 private static final Number [] FILES_DISTRIB_BOTTOM_LIMITS = {0 , 5 , 10 , 20 , 30 , 60 , 90 };
6979
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 ;
7291 }
7392
7493 /**
7594 * @param xmlFile lizard xml report
7695 * @return Map containing as key the name of the file and as value a list containing the measures for that file
7796 */
7897 @ 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 ) {
80101 Map <String , List <Measure >> result = null ;
81102 DocumentBuilderFactory factory = DocumentBuilderFactory .newInstance ();
82103
83104 try {
84105 DocumentBuilder builder = factory .newDocumentBuilder ();
85106 Document document = builder .parse (xmlFile );
86- result = new LizardReportParser ().parseFile (document );
107+ result = new LizardReportParser (fileSystem , resourcePerspectives , rulesProfile , sensorContext ).parseFile (document );
87108 } catch (final FileNotFoundException e ) {
88109 LOGGER .error ("Lizard Report not found {}" , xmlFile , e );
89110 } catch (final IOException | ParserConfigurationException | SAXException e ) {
@@ -197,6 +218,7 @@ private void addComplexityFunctionMeasures(Map<String, List<Measure>> reportMeas
197218 complexityDistribution .add (func .getCyclomaticComplexity ());
198219 count ++;
199220 complexityInFunctions += func .getCyclomaticComplexity ();
221+ createFunctionComplexityIssue (entry .getKey (), func );
200222 }
201223 }
202224
@@ -205,23 +227,110 @@ private void addComplexityFunctionMeasures(Map<String, List<Measure>> reportMeas
205227 for (Measure m : entry .getValue ()) {
206228 if (m .getMetric ().getKey ().equalsIgnoreCase (CoreMetrics .FILE_COMPLEXITY .getKey ())) {
207229 complex = m .getValue ();
230+ createFileComplexityIssue (entry .getKey (), (int ) complex );
208231 break ;
209232 }
210233 }
211234
212235 double complexMean = complex / (double ) count ;
213- entry .getValue ().addAll (buildFuncionMeasuresList (complexMean , complexityInFunctions , complexityDistribution ));
236+ entry .getValue ().addAll (buildFunctionMeasuresList (complexMean , complexityInFunctions , complexityDistribution ));
214237 }
215238 }
216239 }
217240
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+
218327 /**
219328 * @param complexMean average complexity per function in a file
220329 * @param complexityInFunctions Entire complexity in functions
221330 * @param builder Builder ready to build FUNCTION_COMPLEXITY_DISTRIBUTION
222331 * @return list of Measures containing FUNCTION_COMPLEXITY_DISTRIBUTION, FUNCTION_COMPLEXITY and COMPLEXITY_IN_FUNCTIONS
223332 */
224- public List <Measure > buildFuncionMeasuresList (double complexMean , int complexityInFunctions ,
333+ public List <Measure > buildFunctionMeasuresList (double complexMean , int complexityInFunctions ,
225334 RangeDistributionBuilder builder ) {
226335 List <Measure > list = new ArrayList <>();
227336 list .add (new Measure (CoreMetrics .FUNCTION_COMPLEXITY , complexMean ));
0 commit comments