| package org.apache.maven.plugins.checkstyle; |
| |
| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| */ |
| |
| import java.io.File; |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.Collections; |
| import java.util.List; |
| import java.util.ResourceBundle; |
| |
| import org.apache.maven.doxia.sink.Sink; |
| import org.apache.maven.doxia.sink.SinkEventAttributeSet; |
| import org.apache.maven.doxia.sink.SinkEventAttributes; |
| import org.apache.maven.doxia.tools.SiteTool; |
| import org.apache.maven.plugin.logging.Log; |
| import org.apache.maven.plugin.logging.SystemStreamLog; |
| import org.apache.maven.plugins.checkstyle.exec.CheckstyleResults; |
| import org.codehaus.plexus.util.StringUtils; |
| |
| import com.puppycrawl.tools.checkstyle.api.AuditEvent; |
| import com.puppycrawl.tools.checkstyle.api.CheckstyleException; |
| import com.puppycrawl.tools.checkstyle.api.Configuration; |
| import com.puppycrawl.tools.checkstyle.api.SeverityLevel; |
| |
| /** |
| * Generate a report based on CheckstyleResults. |
| * |
| * @version $Id$ |
| */ |
| public class CheckstyleReportGenerator |
| { |
| private Log log; |
| |
| private final File basedir; |
| |
| private final ResourceBundle bundle; |
| |
| private final Sink sink; |
| |
| private SeverityLevel severityLevel; |
| |
| private Configuration checkstyleConfig; |
| |
| private boolean enableRulesSummary; |
| |
| private boolean enableSeveritySummary; |
| |
| private boolean enableFilesSummary; |
| |
| private boolean enableRSS; |
| |
| private final SiteTool siteTool; |
| |
| private String xrefLocation; |
| |
| private List<String> treeWalkerNames = Collections.singletonList( "TreeWalker" ); |
| |
| private final IconTool iconTool; |
| |
| private final String ruleset; |
| |
| public CheckstyleReportGenerator( Sink sink, ResourceBundle bundle, File basedir, SiteTool siteTool, |
| String ruleset ) |
| { |
| this.bundle = bundle; |
| |
| this.sink = sink; |
| |
| this.basedir = basedir; |
| |
| this.siteTool = siteTool; |
| |
| this.ruleset = ruleset; |
| |
| this.enableRulesSummary = true; |
| this.enableSeveritySummary = true; |
| this.enableFilesSummary = true; |
| this.enableRSS = true; |
| this.iconTool = new IconTool( sink, bundle ); |
| } |
| |
| public Log getLog() |
| { |
| if ( this.log == null ) |
| { |
| this.log = new SystemStreamLog(); |
| } |
| return this.log; |
| } |
| |
| public void setLog( Log log ) |
| { |
| this.log = log; |
| } |
| |
| private String getTitle() |
| { |
| String title; |
| |
| if ( getSeverityLevel() == null ) |
| { |
| title = bundle.getString( "report.checkstyle.title" ); |
| } |
| else |
| { |
| title = bundle.getString( "report.checkstyle.severity_title" ) + severityLevel.getName(); |
| } |
| |
| return title; |
| } |
| |
| public void generateReport( CheckstyleResults results ) |
| { |
| doHeading(); |
| |
| if ( getSeverityLevel() == null ) |
| { |
| if ( enableSeveritySummary ) |
| { |
| doSeveritySummary( results ); |
| } |
| |
| if ( enableFilesSummary ) |
| { |
| doFilesSummary( results ); |
| } |
| |
| if ( enableRulesSummary ) |
| { |
| doRulesSummary( results ); |
| } |
| } |
| |
| doDetails( results ); |
| sink.body_(); |
| sink.flush(); |
| sink.close(); |
| } |
| |
| private void doHeading() |
| { |
| sink.head(); |
| sink.title(); |
| sink.text( getTitle() ); |
| sink.title_(); |
| sink.head_(); |
| |
| sink.body(); |
| |
| sink.section1(); |
| sink.sectionTitle1(); |
| sink.text( getTitle() ); |
| sink.sectionTitle1_(); |
| |
| sink.paragraph(); |
| sink.text( bundle.getString( "report.checkstyle.checkstylelink" ) + " " ); |
| sink.link( "http://checkstyle.sourceforge.net/" ); |
| sink.text( "Checkstyle" ); |
| sink.link_(); |
| String version = getCheckstyleVersion(); |
| if ( version != null ) |
| { |
| sink.text( " " ); |
| sink.text( version ); |
| } |
| sink.text( " " ); |
| sink.text( String.format( bundle.getString( "report.checkstyle.ruleset" ), ruleset ) ); |
| sink.text( "." ); |
| |
| if ( enableRSS ) |
| { |
| sink.nonBreakingSpace(); |
| sink.link( "checkstyle.rss" ); |
| sink.figure(); |
| sink.figureCaption(); |
| sink.text( "rss feed" ); |
| sink.figureCaption_(); |
| sink.figureGraphics( "images/rss.png" ); |
| sink.figure_(); |
| sink.link_(); |
| } |
| |
| sink.paragraph_(); |
| sink.section1_(); |
| } |
| |
| /** |
| * Get the value of the specified attribute from the Checkstyle configuration. |
| * If parentConfigurations is non-null and non-empty, the parent |
| * configurations are searched if the attribute cannot be found in the |
| * current configuration. If the attribute is still not found, the |
| * specified default value will be returned. |
| * |
| * @param config The current Checkstyle configuration |
| * @param parentConfiguration The configuration of the parent of the current configuration |
| * @param attributeName The name of the attribute |
| * @param defaultValue The default value to use if the attribute cannot be found in any configuration |
| * @return The value of the specified attribute |
| */ |
| private String getConfigAttribute( Configuration config, ChainedItem<Configuration> parentConfiguration, |
| String attributeName, String defaultValue ) |
| { |
| String ret; |
| try |
| { |
| ret = config.getAttribute( attributeName ); |
| } |
| catch ( CheckstyleException e ) |
| { |
| // Try to find the attribute in a parent, if there are any |
| if ( parentConfiguration != null ) |
| { |
| ret = |
| getConfigAttribute( parentConfiguration.value, parentConfiguration.parent, attributeName, |
| defaultValue ); |
| } |
| else |
| { |
| ret = defaultValue; |
| } |
| } |
| return ret; |
| } |
| |
| /** |
| * Create the rules summary section of the report. |
| * |
| * @param results The results to summarize |
| */ |
| private void doRulesSummary( CheckstyleResults results ) |
| { |
| if ( checkstyleConfig == null ) |
| { |
| return; |
| } |
| |
| sink.section1(); |
| sink.sectionTitle1(); |
| sink.text( bundle.getString( "report.checkstyle.rules" ) ); |
| sink.sectionTitle1_(); |
| |
| sink.table(); |
| |
| sink.tableRow(); |
| sink.tableHeaderCell(); |
| sink.text( bundle.getString( "report.checkstyle.rule.category" ) ); |
| sink.tableHeaderCell_(); |
| |
| sink.tableHeaderCell(); |
| sink.text( bundle.getString( "report.checkstyle.rule" ) ); |
| sink.tableHeaderCell_(); |
| |
| sink.tableHeaderCell(); |
| sink.text( bundle.getString( "report.checkstyle.violations" ) ); |
| sink.tableHeaderCell_(); |
| |
| sink.tableHeaderCell(); |
| sink.text( bundle.getString( "report.checkstyle.column.severity" ) ); |
| sink.tableHeaderCell_(); |
| |
| sink.tableRow_(); |
| |
| // Top level should be the checker. |
| if ( "checker".equalsIgnoreCase( checkstyleConfig.getName() ) ) |
| { |
| String category = null; |
| for ( ConfReference ref: sortConfiguration( results ) ) |
| { |
| doRuleRow( ref, results, category ); |
| |
| category = ref.category; |
| } |
| } |
| else |
| { |
| sink.tableRow(); |
| sink.tableCell(); |
| sink.text( bundle.getString( "report.checkstyle.norule" ) ); |
| sink.tableCell_(); |
| sink.tableRow_(); |
| } |
| |
| sink.table_(); |
| |
| sink.section1_(); |
| } |
| |
| /** |
| * Create a summary for one Checkstyle rule. |
| * |
| * @param ref The configuration reference for the row |
| * @param results The results to summarize |
| * @param previousCategory The previous row's category |
| */ |
| private void doRuleRow( ConfReference ref, CheckstyleResults results, String previousCategory ) |
| { |
| Configuration checkerConfig = ref.configuration; |
| ChainedItem<Configuration> parentConfiguration = ref.parentConfiguration; |
| String ruleName = checkerConfig.getName(); |
| |
| sink.tableRow(); |
| |
| // column 1: rule category |
| sink.tableCell(); |
| String category = ref.category; |
| if ( !category.equals( previousCategory ) ) |
| { |
| sink.text( category ); |
| } |
| sink.tableCell_(); |
| |
| // column 2: Rule name + configured attributes |
| sink.tableCell(); |
| if ( !"extension".equals( category ) ) |
| { |
| sink.link( "http://checkstyle.sourceforge.net/config_" + category + ".html#" + ruleName ); |
| sink.text( ruleName ); |
| sink.link_(); |
| } |
| else |
| { |
| sink.text( ruleName ); |
| } |
| |
| List<String> attribnames = new ArrayList<>( Arrays.asList( checkerConfig.getAttributeNames() ) ); |
| attribnames.remove( "severity" ); // special value (deserves unique column) |
| if ( !attribnames.isEmpty() ) |
| { |
| sink.list(); |
| for ( String name : attribnames ) |
| { |
| sink.listItem(); |
| |
| sink.text( name ); |
| |
| String value = getConfigAttribute( checkerConfig, null, name, "" ); |
| // special case, Header.header and RegexpHeader.header |
| if ( "header".equals( name ) && ( "Header".equals( ruleName ) || "RegexpHeader".equals( ruleName ) ) ) |
| { |
| String[] lines = StringUtils.split( value, "\\n" ); |
| int linenum = 1; |
| for ( String line : lines ) |
| { |
| sink.lineBreak(); |
| sink.rawText( "<span style=\"color: gray\">" ); |
| sink.text( linenum + ":" ); |
| sink.rawText( "</span>" ); |
| sink.nonBreakingSpace(); |
| sink.monospaced(); |
| sink.text( line ); |
| sink.monospaced_(); |
| linenum++; |
| } |
| } |
| else if ( "headerFile".equals( name ) && "RegexpHeader".equals( ruleName ) ) |
| { |
| sink.text( ": " ); |
| sink.monospaced(); |
| sink.text( "\"" ); |
| if ( basedir != null ) |
| { |
| // Make the headerFile value relative to ${basedir} |
| String path = siteTool.getRelativePath( value, basedir.getAbsolutePath() ); |
| sink.text( path.replace( '\\', '/' ) ); |
| } |
| else |
| { |
| sink.text( value ); |
| } |
| sink.text( "\"" ); |
| sink.monospaced_(); |
| } |
| else |
| { |
| sink.text( ": " ); |
| sink.monospaced(); |
| sink.text( "\"" ); |
| sink.text( value ); |
| sink.text( "\"" ); |
| sink.monospaced_(); |
| } |
| sink.listItem_(); |
| } |
| sink.list_(); |
| } |
| |
| sink.tableCell_(); |
| |
| // column 3: rule violation count |
| sink.tableCell(); |
| sink.text( String.valueOf( ref.violations ) ); |
| sink.tableCell_(); |
| |
| // column 4: severity |
| sink.tableCell(); |
| // Grab the severity from the rule configuration, this time use error as default value |
| // Also pass along all parent configurations, so that we can try to find the severity there |
| String severity = getConfigAttribute( checkerConfig, parentConfiguration, "severity", "error" ); |
| iconTool.iconSeverity( severity, IconTool.TEXT_SIMPLE ); |
| sink.tableCell_(); |
| |
| sink.tableRow_(); |
| } |
| |
| /** |
| * Check if a violation matches a rule. |
| * |
| * @param event the violation to check |
| * @param ruleName The name of the rule |
| * @param expectedMessage A message that, if it's not null, will be matched to the message from the violation |
| * @param expectedSeverity A severity that, if it's not null, will be matched to the severity from the violation |
| * @return The number of rule violations |
| */ |
| public boolean matchRule( AuditEvent event, String ruleName, String expectedMessage, String expectedSeverity ) |
| { |
| if ( !ruleName.equals( RuleUtil.getName( event ) ) ) |
| { |
| return false; |
| } |
| |
| // check message too, for those that have a specific one. |
| // like GenericIllegalRegexp and Regexp |
| if ( expectedMessage != null ) |
| { |
| // event.getMessage() uses java.text.MessageFormat in its implementation. |
| // Read MessageFormat Javadoc about single quote: |
| // http://java.sun.com/j2se/1.4.2/docs/api/java/text/MessageFormat.html |
| String msgWithoutSingleQuote = StringUtils.replace( expectedMessage, "'", "" ); |
| |
| if ( ! ( expectedMessage.equals( event.getMessage() ) |
| || msgWithoutSingleQuote.equals( event.getMessage() ) ) ) |
| { |
| return false; |
| } |
| } |
| // Check the severity. This helps to distinguish between |
| // different configurations for the same rule, where each |
| // configuration has a different severity, like JavadocMethod. |
| // See also https://issues.apache.org/jira/browse/MCHECKSTYLE-41 |
| if ( expectedSeverity != null ) |
| { |
| if ( !expectedSeverity.equals( event.getSeverityLevel().getName() ) ) |
| { |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| private void doSeveritySummary( CheckstyleResults results ) |
| { |
| sink.section1(); |
| sink.sectionTitle1(); |
| sink.text( bundle.getString( "report.checkstyle.summary" ) ); |
| sink.sectionTitle1_(); |
| |
| sink.table(); |
| |
| sink.tableRow(); |
| sink.tableHeaderCell(); |
| sink.text( bundle.getString( "report.checkstyle.files" ) ); |
| sink.tableHeaderCell_(); |
| |
| sink.tableHeaderCell(); |
| iconTool.iconInfo( IconTool.TEXT_TITLE ); |
| sink.tableHeaderCell_(); |
| |
| sink.tableHeaderCell(); |
| iconTool.iconWarning( IconTool.TEXT_TITLE ); |
| sink.tableHeaderCell_(); |
| |
| sink.tableHeaderCell(); |
| iconTool.iconError( IconTool.TEXT_TITLE ); |
| sink.tableHeaderCell_(); |
| sink.tableRow_(); |
| |
| sink.tableRow(); |
| sink.tableCell(); |
| sink.text( String.valueOf( results.getFileCount() ) ); |
| sink.tableCell_(); |
| sink.tableCell(); |
| sink.text( String.valueOf( results.getSeverityCount( SeverityLevel.INFO ) ) ); |
| sink.tableCell_(); |
| sink.tableCell(); |
| sink.text( String.valueOf( results.getSeverityCount( SeverityLevel.WARNING ) ) ); |
| sink.tableCell_(); |
| sink.tableCell(); |
| sink.text( String.valueOf( results.getSeverityCount( SeverityLevel.ERROR ) ) ); |
| sink.tableCell_(); |
| sink.tableRow_(); |
| |
| sink.table_(); |
| |
| sink.section1_(); |
| } |
| |
| private void doFilesSummary( CheckstyleResults results ) |
| { |
| sink.section1(); |
| sink.sectionTitle1(); |
| sink.text( bundle.getString( "report.checkstyle.files" ) ); |
| sink.sectionTitle1_(); |
| |
| sink.table(); |
| |
| sink.tableRow(); |
| sink.tableHeaderCell(); |
| sink.text( bundle.getString( "report.checkstyle.file" ) ); |
| sink.tableHeaderCell_(); |
| sink.tableHeaderCell(); |
| iconTool.iconInfo( IconTool.TEXT_ABBREV ); |
| sink.tableHeaderCell_(); |
| sink.tableHeaderCell(); |
| iconTool.iconWarning( IconTool.TEXT_ABBREV ); |
| sink.tableHeaderCell_(); |
| sink.tableHeaderCell(); |
| iconTool.iconError( IconTool.TEXT_ABBREV ); |
| sink.tableHeaderCell_(); |
| sink.tableRow_(); |
| |
| // Sort the files before writing them to the report |
| List<String> fileList = new ArrayList<>( results.getFiles().keySet() ); |
| Collections.sort( fileList ); |
| |
| for ( String filename : fileList ) |
| { |
| List<AuditEvent> violations = results.getFileViolations( filename ); |
| if ( violations.isEmpty() ) |
| { |
| // skip files without violations |
| continue; |
| } |
| |
| sink.tableRow(); |
| |
| sink.tableCell(); |
| sink.link( "#" + filename.replace( '/', '.' ) ); |
| sink.text( filename ); |
| sink.link_(); |
| sink.tableCell_(); |
| |
| sink.tableCell(); |
| sink.text( String.valueOf( results.getSeverityCount( violations, SeverityLevel.INFO ) ) ); |
| sink.tableCell_(); |
| |
| sink.tableCell(); |
| sink.text( String.valueOf( results.getSeverityCount( violations, SeverityLevel.WARNING ) ) ); |
| sink.tableCell_(); |
| |
| sink.tableCell(); |
| sink.text( String.valueOf( results.getSeverityCount( violations, SeverityLevel.ERROR ) ) ); |
| sink.tableCell_(); |
| |
| sink.tableRow_(); |
| } |
| |
| sink.table_(); |
| sink.section1_(); |
| } |
| |
| private void doDetails( CheckstyleResults results ) |
| { |
| |
| sink.section1(); |
| sink.sectionTitle1(); |
| sink.text( bundle.getString( "report.checkstyle.details" ) ); |
| sink.sectionTitle1_(); |
| |
| // Sort the files before writing their details to the report |
| List<String> fileList = new ArrayList<>( results.getFiles().keySet() ); |
| Collections.sort( fileList ); |
| |
| for ( String file : fileList ) |
| { |
| List<AuditEvent> violations = results.getFileViolations( file ); |
| |
| if ( violations.isEmpty() ) |
| { |
| // skip files without violations |
| continue; |
| } |
| |
| sink.section2(); |
| SinkEventAttributes attrs = new SinkEventAttributeSet(); |
| attrs.addAttribute( SinkEventAttributes.ID, file.replace( '/', '.' ) ); |
| sink.sectionTitle( Sink.SECTION_LEVEL_2, attrs ); |
| sink.text( file ); |
| sink.sectionTitle_( Sink.SECTION_LEVEL_2 ); |
| |
| sink.table(); |
| sink.tableRow(); |
| sink.tableHeaderCell(); |
| sink.text( bundle.getString( "report.checkstyle.column.severity" ) ); |
| sink.tableHeaderCell_(); |
| sink.tableHeaderCell(); |
| sink.text( bundle.getString( "report.checkstyle.rule.category" ) ); |
| sink.tableHeaderCell_(); |
| sink.tableHeaderCell(); |
| sink.text( bundle.getString( "report.checkstyle.rule" ) ); |
| sink.tableHeaderCell_(); |
| sink.tableHeaderCell(); |
| sink.text( bundle.getString( "report.checkstyle.column.message" ) ); |
| sink.tableHeaderCell_(); |
| sink.tableHeaderCell(); |
| sink.text( bundle.getString( "report.checkstyle.column.line" ) ); |
| sink.tableHeaderCell_(); |
| sink.tableRow_(); |
| |
| doFileEvents( violations, file ); |
| |
| sink.table_(); |
| sink.section2_(); |
| } |
| |
| sink.section1_(); |
| } |
| |
| private void doFileEvents( List<AuditEvent> eventList, String filename ) |
| { |
| for ( AuditEvent event : eventList ) |
| { |
| SeverityLevel level = event.getSeverityLevel(); |
| |
| if ( ( getSeverityLevel() != null ) && !( getSeverityLevel() != level ) ) |
| { |
| continue; |
| } |
| |
| sink.tableRow(); |
| |
| sink.tableCell(); |
| iconTool.iconSeverity( level.getName(), IconTool.TEXT_SIMPLE ); |
| sink.tableCell_(); |
| |
| sink.tableCell(); |
| String category = RuleUtil.getCategory( event ); |
| if ( category != null ) |
| { |
| sink.text( category ); |
| } |
| sink.tableCell_(); |
| |
| sink.tableCell(); |
| String ruleName = RuleUtil.getName( event ); |
| if ( ruleName != null ) |
| { |
| sink.text( ruleName ); |
| } |
| sink.tableCell_(); |
| |
| sink.tableCell(); |
| sink.text( event.getMessage() ); |
| sink.tableCell_(); |
| |
| sink.tableCell(); |
| |
| int line = event.getLine(); |
| if ( getXrefLocation() != null && line != 0 ) |
| { |
| sink.link( getXrefLocation() + "/" + filename.replaceAll( "\\.java$", ".html" ) + "#L" |
| + line ); |
| sink.text( String.valueOf( line ) ); |
| sink.link_(); |
| } |
| else if ( line != 0 ) |
| { |
| sink.text( String.valueOf( line ) ); |
| } |
| sink.tableCell_(); |
| |
| sink.tableRow_(); |
| } |
| } |
| |
| public SeverityLevel getSeverityLevel() |
| { |
| return severityLevel; |
| } |
| |
| public void setSeverityLevel( SeverityLevel severityLevel ) |
| { |
| this.severityLevel = severityLevel; |
| } |
| |
| public boolean isEnableRulesSummary() |
| { |
| return enableRulesSummary; |
| } |
| |
| public void setEnableRulesSummary( boolean enableRulesSummary ) |
| { |
| this.enableRulesSummary = enableRulesSummary; |
| } |
| |
| public boolean isEnableSeveritySummary() |
| { |
| return enableSeveritySummary; |
| } |
| |
| public void setEnableSeveritySummary( boolean enableSeveritySummary ) |
| { |
| this.enableSeveritySummary = enableSeveritySummary; |
| } |
| |
| public boolean isEnableFilesSummary() |
| { |
| return enableFilesSummary; |
| } |
| |
| public void setEnableFilesSummary( boolean enableFilesSummary ) |
| { |
| this.enableFilesSummary = enableFilesSummary; |
| } |
| |
| public boolean isEnableRSS() |
| { |
| return enableRSS; |
| } |
| |
| public void setEnableRSS( boolean enableRSS ) |
| { |
| this.enableRSS = enableRSS; |
| } |
| |
| public String getXrefLocation() |
| { |
| return xrefLocation; |
| } |
| |
| public void setXrefLocation( String xrefLocation ) |
| { |
| this.xrefLocation = xrefLocation; |
| } |
| |
| public Configuration getCheckstyleConfig() |
| { |
| return checkstyleConfig; |
| } |
| |
| public void setCheckstyleConfig( Configuration config ) |
| { |
| this.checkstyleConfig = config; |
| } |
| |
| public void setTreeWalkerNames( List<String> treeWalkerNames ) |
| { |
| this.treeWalkerNames = treeWalkerNames; |
| } |
| |
| public List<String> getTreeWalkerNames() |
| { |
| return treeWalkerNames; |
| } |
| |
| /** |
| * Get the effective Checkstyle version at runtime. |
| * @return the MANIFEST implementation version of Checkstyle API package (can be <code>null</code>) |
| */ |
| private String getCheckstyleVersion() |
| { |
| Package checkstyleApiPackage = Configuration.class.getPackage(); |
| |
| return ( checkstyleApiPackage == null ) ? null : checkstyleApiPackage.getImplementationVersion(); |
| } |
| |
| public List<ConfReference> sortConfiguration( CheckstyleResults results ) |
| { |
| List<ConfReference> result = new ArrayList<>(); |
| |
| sortConfiguration( result, checkstyleConfig, null, results ); |
| |
| Collections.sort( result ); |
| |
| return result; |
| } |
| |
| private void sortConfiguration( List<ConfReference> result, Configuration config, |
| ChainedItem<Configuration> parent, CheckstyleResults results ) |
| { |
| for ( Configuration childConfig : config.getChildren() ) |
| { |
| String ruleName = childConfig.getName(); |
| |
| if ( treeWalkerNames.contains( ruleName ) ) |
| { |
| // special sub-case: TreeWalker is the parent of multiple rules, not an effective rule |
| sortConfiguration( result, childConfig, new ChainedItem<>( config, parent ), results ); |
| } |
| else |
| { |
| String fixedmessage = getConfigAttribute( childConfig, null, "message", null ); |
| // Grab the severity from the rule configuration, use "error" as default value (as is |
| // done where the rule severity is displayed otherwise we miscount error violations) |
| String configSeverity = getConfigAttribute( childConfig, null, "severity", "error" ); |
| |
| // count rule violations |
| long violations = 0; |
| AuditEvent lastMatchedEvent = null; |
| for ( List<AuditEvent> errors : results.getFiles().values() ) |
| { |
| for ( AuditEvent event : errors ) |
| { |
| if ( matchRule( event, ruleName, fixedmessage, configSeverity ) ) |
| { |
| lastMatchedEvent = event; |
| violations++; |
| } |
| } |
| } |
| |
| if ( violations > 0 ) // forget rules without violations |
| { |
| String category = RuleUtil.getCategory( lastMatchedEvent ); |
| |
| result.add( new ConfReference( category, childConfig, parent, violations, result.size() ) ); |
| } |
| } |
| } |
| } |
| |
| private static class ConfReference |
| implements Comparable<ConfReference> |
| { |
| private final String category; |
| private final Configuration configuration; |
| private final ChainedItem<Configuration> parentConfiguration; |
| private final long violations; |
| private final int count; |
| |
| ConfReference( String category, Configuration configuration, |
| ChainedItem<Configuration> parentConfiguration, long violations, int count ) |
| { |
| this.category = category; |
| this.configuration = configuration; |
| this.parentConfiguration = parentConfiguration; |
| this.violations = violations; |
| this.count = count; |
| } |
| |
| public int compareTo( ConfReference o ) |
| { |
| int compare = category.compareTo( o.category ); |
| if ( compare == 0 ) |
| { |
| compare = configuration.getName().compareTo( o.configuration.getName() ); |
| } |
| return ( compare == 0 ) ? ( o.count - count ) : compare; |
| } |
| } |
| |
| private static class ChainedItem<T> |
| { |
| private final ChainedItem<T> parent; |
| |
| private final T value; |
| |
| ChainedItem( T value, ChainedItem<T> parent ) |
| { |
| this.parent = parent; |
| this.value = value; |
| } |
| } |
| |
| } |