org.codenarc.CodeNarcRunner.groovy Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of CodeNarc Show documentation
Show all versions of CodeNarc Show documentation
The CodeNarc project provides a static analysis tool for Groovy code.
/*
* Copyright 2009 the original author or authors.
*
* Licensed 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.
*/
package org.codenarc
import org.codenarc.analyzer.SourceAnalyzer
import org.codenarc.plugin.CodeNarcPlugin
import org.codenarc.plugin.FileViolations
import org.codenarc.report.ReportWriter
import org.codenarc.results.FileResults
import org.codenarc.results.Results
import org.codenarc.rule.Rule
import org.codenarc.ruleregistry.RuleRegistryInitializer
import org.codenarc.ruleset.*
/**
* Helper class to run CodeNarc.
*
* The following properties must be configured before invoking the execute()
method:
*
* ruleSetFiles
- The path to the Groovy or XML RuleSet definition files, relative to the classpath. This can be a
* single file path, or multiple paths separated by commas.
* sourceAnalyzer
- An instance of a org.codenarc.analyzer.SourceAnalyzer
implementation.
* reportWriters
- The list of ReportWriter
instances. A report is generated
* for each element in this list. At least one ReportWriter
must be configured.
*
*
* NOTE: This is an internal class. Its API is subject to change.
*
* @author Chris Mair
*/
class CodeNarcRunner {
public static final String PLUGINS_PROPERTY = 'org.codenarc.plugins'
public static final String STANDARD_PLUGIN_CLASSES = 'org.codenarc.plugin.disablerules.DisableRulesInCommentsPlugin'
String ruleSetFiles
String ruleSetString
SourceAnalyzer sourceAnalyzer
List reportWriters = []
String propertiesFilename
protected final List plugins = []
protected RuleSetConfigurer ruleSetConfigurer = new PropertiesFileRuleSetConfigurer()
/**
* The main entry point for this class. Runs CodeNarc and returns the results. Processing steps include:
*
* - Register standard CodeNarcPlugins.
* - Register any CodeNarcPlugins specified by the "org.codenarc.plugins" system property.
* - Call initialize() for each registered CodeNarcPlugin.
* - Parse the
ruleSetFiles
or ruleSetString
property to create a RuleSet.
* - ruleSetFiles: Each path may be optionally prefixed by
* any of the valid java.net.URL prefixes, such as "file:" (to load from a relative or absolute filesystem path),
* or "http:". If it is a URL, its path may be optionally URL-encoded. That can be useful if the path contains
* any problematic characters, such as comma (',') or hash ('#'). See {@link URLEncoder#encode(java.lang.String, java.lang.String)}.
* - ruleSetString: string containing a ruleSet in JSON format (if set, ruleSetFiles will be ignored)
*
* - Call processRules(List
) for each registered CodeNarcPlugin
* - Call PropertiesFileRuleSetConfigurer to configure the RuleSet from the "codenarc.properties" file (or propertiesFilename if set).
* - Apply the configured
SourceAnalyzer
.
* - Call processViolationsForFile(FileViolations) for each file with violations
* - Call processReports(List
) for each registered CodeNarcPlugin
* - Generate a report for each configured
ReportWriter
.
* - Return the
Results
object representing the analysis results.
*
* @returns the Results
object containing the results of the CodeNarc analysis.
*/
@SuppressWarnings('Println')
Results execute() {
assert ruleSetFiles || ruleSetString, 'The ruleSetFiles or ruleSetString property must be set'
assert sourceAnalyzer, 'The sourceAnalyzer property must be set to a valid SourceAnalyzer'
def startTime = System.currentTimeMillis()
initializeRuleRegistry()
initializePlugins()
def ruleSet = buildRuleSet()
def results = sourceAnalyzer.analyze(ruleSet)
applyPluginsProcessViolationsForAllFiles(results)
String countsText = buildCountsText(results)
def elapsedTime = System.currentTimeMillis() - startTime
def analysisContext = new AnalysisContext(ruleSet:ruleSet, sourceDirectories:sourceAnalyzer.sourceDirectories)
writeReports(analysisContext, results)
def resultsMessage = 'CodeNarc completed: ' + countsText + " ${elapsedTime}ms"
println resultsMessage
results
}
void registerPlugin(CodeNarcPlugin plugin) {
assert plugin
this.plugins << plugin
}
List getPlugins() {
return plugins
}
void registerPluginsForClassNames(String pluginClassNames) {
if (pluginClassNames) {
def classNamesList = pluginClassNames.tokenize(',')*.trim()
classNamesList.each { className -> registerPluginForClassName(className) }
}
}
// Helper methods
private void initializeRuleRegistry() {
new RuleRegistryInitializer().initializeRuleRegistry()
}
private void initializePlugins() {
initializeStandardPlugins()
initializePluginsFromSystemProperty()
this.plugins.each { plugin -> plugin.initialize() }
}
private RuleSet buildRuleSet() {
RuleSet initialRuleSet = createInitialRuleSet()
List rules = applyPluginsProcessRules(initialRuleSet)
RuleSet ruleSet = new ListRuleSet(rules)
ruleSetConfigurer.configure(ruleSet, propertiesFilename)
return ruleSet
}
private List applyPluginsProcessRules(RuleSet ruleSet) {
List rules = new ArrayList(ruleSet.getRules()) // need it mutable
this.plugins.each { plugin -> plugin.processRules(rules) }
return rules
}
private void applyPluginsProcessViolationsForAllFiles(Results results) {
if (plugins) {
if (results.isFile()) {
applyPluginsProcessViolationsForFile(results)
}
results.children.each { childResults -> applyPluginsProcessViolationsForAllFiles(childResults) }
}
}
private void applyPluginsProcessViolationsForFile(FileResults fileResults) {
FileViolations fileViolations = new FileViolations(fileResults)
this.plugins.each { plugin ->
plugin.processViolationsForFile(fileViolations)
}
}
/**
* Create and return the RuleSet that provides the source of Rules to be applied.
* The returned RuleSet may aggregate multiple underlying RuleSets.
* @return a single RuleSet
*/
protected RuleSet createInitialRuleSet() {
(ruleSetString) ?
createInitialRuleSetFromString() :
createInitialRuleSetFromFiles()
}
private RuleSet createInitialRuleSetFromString() {
return RuleSetUtil.loadRuleSetFromString(ruleSetString)
}
// Create ruleset from XML, JSON or Groovy files
private RuleSet createInitialRuleSetFromFiles() {
def paths = ruleSetFiles.tokenize(',')
def newRuleSet = new CompositeRuleSet()
paths.each { path ->
def ruleSet = RuleSetUtil.loadRuleSetFile(path.trim())
newRuleSet.addRuleSet(ruleSet)
}
newRuleSet
}
private void writeReports(AnalysisContext analysisContext, Results results) {
this.plugins.each { plugin -> plugin.processReports(reportWriters) }
reportWriters.each { reportWriter ->
reportWriter.writeReport(analysisContext, results)
}
}
private void initializeStandardPlugins() {
registerPluginsForClassNames(STANDARD_PLUGIN_CLASSES)
}
private void initializePluginsFromSystemProperty() {
String pluginClassNames = System.getProperty(PLUGINS_PROPERTY)
registerPluginsForClassNames(pluginClassNames)
}
private void registerPluginForClassName(String className) {
def pluginClass = getClass().classLoader.loadClass(className)
assert CodeNarcPlugin.isAssignableFrom(pluginClass), "The className [$className] is not a CodeNarcPlugin"
def plugin = pluginClass.newInstance()
registerPlugin(plugin)
}
private String buildCountsText(Results results) {
def p1 = results.getNumberOfViolationsWithPriority(1, true)
def p2 = results.getNumberOfViolationsWithPriority(2, true)
def p3 = results.getNumberOfViolationsWithPriority(3, true)
return "(p1=$p1; p2=$p2; p3=$p3)"
}
}