<commit_before>package ru.vyarus.gradle.plugin.quality

import com.github.spotbugs.snom.SpotBugsTask
import groovy.transform.CompileStatic
import groovy.transform.TypeCheckingMode
import org.gradle.api.Plugin
import org.gradle.api.Project
import org.gradle.api.Task
import org.gradle.api.execution.TaskExecutionGraph
import org.gradle.api.plugins.GroovyPlugin
import org.gradle.api.plugins.JavaPlugin
import org.gradle.api.plugins.quality.*
import org.gradle.api.reporting.Report
import org.gradle.api.tasks.SourceSet
import org.gradle.api.tasks.SourceTask
import org.gradle.api.tasks.TaskProvider
import org.gradle.api.tasks.TaskState
import org.gradle.api.tasks.compile.JavaCompile
import org.gradle.process.CommandLineArgumentProvider
import org.gradle.util.GradleVersion
import ru.vyarus.gradle.plugin.quality.report.*
import ru.vyarus.gradle.plugin.quality.spotbugs.CustomSpotBugsPlugin
import ru.vyarus.gradle.plugin.quality.task.InitQualityConfigTask
import ru.vyarus.gradle.plugin.quality.util.CpdUtils
import ru.vyarus.gradle.plugin.quality.util.DurationFormatter
import ru.vyarus.gradle.plugin.quality.util.SpotbugsExclusionConfigProvider
import ru.vyarus.gradle.plugin.quality.util.SpotbugsUtils

/**
 * Quality plugin enables and configures quality plugins for java and groovy projects.
 * Plugin must be registered after java or groovy plugins, otherwise wil do nothing.
 * <p>
 * Java project is detected by presence of java sources. In this case Checkstyle, PMD and Spotbugs plugins are
 * activated. If quality plugins applied manually, they would be configured too (even if auto detection didn't
 * recognize related sources). Also, additional javac lint options are activated to show more warnings
 * during compilation.
 * <p>
 * If groovy plugin enabled, CodeNarc plugin activated.
 * <p>
 * All plugins are configured to produce xml and html reports. For spotbugs html report
 * generated manually. All plugins violations are printed into console in unified format which makes console
 * output good enough for fixing violations.
 * <p>
 * Plugin may be configured with 'quality' closure. See {@link QualityExtension} for configuration options.
 * <p>
 * By default, plugin use bundled quality plugins configurations. These configs could be copied into project
 * with 'initQualityConfig' task (into quality.configDir directory). These custom configs will be used in
 * priority with fallback to default config if config not found.
 * <p>
 * Special tasks registered for each source set: checkQualityMain, checkQualityTest etc.
 * Tasks group registered quality plugins tasks for specific source set. This allows running quality plugins
 * directly without tests (comparing to using 'check' task). Also, allows running quality plugins on source sets
 * not enabled for main 'check' (example case: run quality checks for tests (time to time)). These tasks may be
 * used even when quality tasks are disabled ({@code quality.enabled = false}).
 *
 * @author <NAME>
 * @since 12.11.2015
 * @see CodeNarcPlugin
 * @see CheckstylePlugin
 * @see PmdPlugin
 * @see com.github.spotbugs.snom.SpotBugsPlugin
 * @see de.aaschmid.gradle.plugins.cpd.CpdPlugin
 */
@CompileStatic
class QualityPlugin implements Plugin<Project> {

    private static final String QUALITY_TASK = 'checkQuality'

    @Override
    void apply(Project project) {
        // activated only when java plugin is enabled
        project.plugins.withType(JavaPlugin) {
            QualityExtension extension = project.extensions.create('quality', QualityExtension, project)
            addInitConfigTask(project)

            project.afterEvaluate {
                configureGroupingTasks(project)

                Context context = createContext(project, extension)
                ConfigLoader configLoader = new ConfigLoader(project)

                configureJavac(project, extension)
                applyCheckstyle(project, extension, configLoader, context.registerJavaPlugins)
                applyPMD(project, extension, configLoader, context.registerJavaPlugins)
                applySpotbugs(project, extension, configLoader, context.registerJavaPlugins)
                configureAnimalSniffer(project, extension)
                configureCpdPlugin(project, extension, configLoader,
                        !context.registerJavaPlugins && context.registerGroovyPlugins)
                applyCodeNarc(project, extension, configLoader, context.registerGroovyPlugins)
            }
        }
    }

    private void addInitConfigTask(Project project) {
        project.tasks.register('initQualityConfig', InitQualityConfigTask)
    }

    @CompileStatic(TypeCheckingMode.SKIP)
    private void configureGroupingTasks(Project project) {
        // create checkQualityMain, checkQualityTest (quality tasks for all source sets)
        // using all source sets and not just declared in extension to be able to run quality plugins
        // on source sets which are not included in check task run (e.g. run quality on tests time to time)
        project.sourceSets.each { SourceSet set ->
            project.tasks.register(set.getTaskName(QUALITY_TASK, null)) {
                it.with {
                    group = 'verification'
                    description = "Run quality plugins for $set.name source set"
                }
            }
        }
    }

    private void configureJavac(Project project, QualityExtension extension) {
        if (!extension.lintOptions) {
            return
        }
        project.tasks.withType(JavaCompile).configureEach { JavaCompile t ->
            t.options.compilerArgumentProviders
                    .add({ extension.lintOptions.collect { "-Xlint:$it" as String } } as CommandLineArgumentProvider)
        }
    }

    @CompileStatic(TypeCheckingMode.SKIP)
    @SuppressWarnings(['MethodSize', 'NestedBlockDepth'])
    private void applyCheckstyle(Project project, QualityExtension extension, ConfigLoader configLoader,
                                 boolean register) {
        configurePlugin(project,
                extension.checkstyle,
                register,
                CheckstylePlugin) {
            project.configure(project) {
                checkstyle {
                    showViolations = false
                    toolVersion = extension.checkstyleVersion
                    ignoreFailures = !extension.strict
                    // this may be custom user file (gradle/config/checkstyle/checkstyle.xml) or default one
                    // (in different location)
                    configFile = configLoader.resolveCheckstyleConfig(false)
                    // this is required for ${config_loc} variable, but this will ALWAYS point to
                    // gradle/config/checkstyle/ (or other configured config location dir) because custom
                    // configuration files may be only there
                    configDirectory = configLoader.resolveCheckstyleConfigDir()
                    sourceSets = extension.sourceSets
                }
                if (extension.checkstyleBackport) {
                    repositories {
                        maven {
                            url 'https://rnveach.github.io/checkstyle-backport-jre8/maven2/'
                            // use custom repository ONLY for checkstyle (just in case)
                            content { includeGroup 'com.puppycrawl.tools' }
                        }
                    }
                    dependencies {
                        checkstyle "com.puppycrawl.tools:checkstyle-backport-jre8:${extension.checkstyleVersion}:all"
                    }<commit_msg>use non-shaded checkstyle-backport dependency<commit_after>package ru.vyarus.gradle.plugin.quality

import com.github.spotbugs.snom.SpotBugsTask
import groovy.transform.CompileStatic
import groovy.transform.TypeCheckingMode
import org.gradle.api.Plugin
import org.gradle.api.Project
import org.gradle.api.Task
import org.gradle.api.execution.TaskExecutionGraph
import org.gradle.api.plugins.GroovyPlugin
import org.gradle.api.plugins.JavaPlugin
import org.gradle.api.plugins.quality.*
import org.gradle.api.reporting.Report
import org.gradle.api.tasks.SourceSet
import org.gradle.api.tasks.SourceTask
import org.gradle.api.tasks.TaskProvider
import org.gradle.api.tasks.TaskState
import org.gradle.api.tasks.compile.JavaCompile
import org.gradle.process.CommandLineArgumentProvider
import org.gradle.util.GradleVersion
import ru.vyarus.gradle.plugin.quality.report.*
import ru.vyarus.gradle.plugin.quality.spotbugs.CustomSpotBugsPlugin
import ru.vyarus.gradle.plugin.quality.task.InitQualityConfigTask
import ru.vyarus.gradle.plugin.quality.util.CpdUtils
import ru.vyarus.gradle.plugin.quality.util.DurationFormatter
import ru.vyarus.gradle.plugin.quality.util.SpotbugsExclusionConfigProvider
import ru.vyarus.gradle.plugin.quality.util.SpotbugsUtils

/**
 * Quality plugin enables and configures quality plugins for java and groovy projects.
 * Plugin must be registered after java or groovy plugins, otherwise wil do nothing.
 * <p>
 * Java project is detected by presence of java sources. In this case Checkstyle, PMD and Spotbugs plugins are
 * activated. If quality plugins applied manually, they would be configured too (even if auto detection didn't
 * recognize related sources). Also, additional javac lint options are activated to show more warnings
 * during compilation.
 * <p>
 * If groovy plugin enabled, CodeNarc plugin activated.
 * <p>
 * All plugins are configured to produce xml and html reports. For spotbugs html report
 * generated manually. All plugins violations are printed into console in unified format which makes console
 * output good enough for fixing violations.
 * <p>
 * Plugin may be configured with 'quality' closure. See {@link QualityExtension} for configuration options.
 * <p>
 * By default, plugin use bundled quality plugins configurations. These configs could be copied into project
 * with 'initQualityConfig' task (into quality.configDir directory). These custom configs will be used in
 * priority with fallback to default config if config not found.
 * <p>
 * Special tasks registered for each source set: checkQualityMain, checkQualityTest etc.
 * Tasks group registered quality plugins tasks for specific source set. This allows running quality plugins
 * directly without tests (comparing to using 'check' task). Also, allows running quality plugins on source sets
 * not enabled for main 'check' (example case: run quality checks for tests (time to time)). These tasks may be
 * used even when quality tasks are disabled ({@code quality.enabled = false}).
 *
 * @author <NAME>
 * @since 12.11.2015
 * @see CodeNarcPlugin
 * @see CheckstylePlugin
 * @see PmdPlugin
 * @see com.github.spotbugs.snom.SpotBugsPlugin
 * @see de.aaschmid.gradle.plugins.cpd.CpdPlugin
 */
@CompileStatic
class QualityPlugin implements Plugin<Project> {

    private static final String QUALITY_TASK = 'checkQuality'

    @Override
    void apply(Project project) {
        // activated only when java plugin is enabled
        project.plugins.withType(JavaPlugin) {
            QualityExtension extension = project.extensions.create('quality', QualityExtension, project)
            addInitConfigTask(project)

            project.afterEvaluate {
                configureGroupingTasks(project)

                Context context = createContext(project, extension)
                ConfigLoader configLoader = new ConfigLoader(project)

                configureJavac(project, extension)
                applyCheckstyle(project, extension, configLoader, context.registerJavaPlugins)
                applyPMD(project, extension, configLoader, context.registerJavaPlugins)
                applySpotbugs(project, extension, configLoader, context.registerJavaPlugins)
                configureAnimalSniffer(project, extension)
                configureCpdPlugin(project, extension, configLoader,
                        !context.registerJavaPlugins && context.registerGroovyPlugins)
                applyCodeNarc(project, extension, configLoader, context.registerGroovyPlugins)
            }
        }
    }

    private void addInitConfigTask(Project project) {
        project.tasks.register('initQualityConfig', InitQualityConfigTask)
    }

    @CompileStatic(TypeCheckingMode.SKIP)
    private void configureGroupingTasks(Project project) {
        // create checkQualityMain, checkQualityTest (quality tasks for all source sets)
        // using all source sets and not just declared in extension to be able to run quality plugins
        // on source sets which are not included in check task run (e.g. run quality on tests time to time)
        project.sourceSets.each { SourceSet set ->
            project.tasks.register(set.getTaskName(QUALITY_TASK, null)) {
                it.with {
                    group = 'verification'
                    description = "Run quality plugins for $set.name source set"
                }
            }
        }
    }

    private void configureJavac(Project project, QualityExtension extension) {
        if (!extension.lintOptions) {
            return
        }
        project.tasks.withType(JavaCompile).configureEach { JavaCompile t ->
            t.options.compilerArgumentProviders
                    .add({ extension.lintOptions.collect { "-Xlint:$it" as String } } as CommandLineArgumentProvider)
        }
    }

    @CompileStatic(TypeCheckingMode.SKIP)
    @SuppressWarnings(['MethodSize', 'NestedBlockDepth'])
    private void applyCheckstyle(Project project, QualityExtension extension, ConfigLoader configLoader,
                                 boolean register) {
        configurePlugin(project,
                extension.checkstyle,
                register,
                CheckstylePlugin) {
            project.configure(project) {
                checkstyle {
                    showViolations = false
                    toolVersion = extension.checkstyleVersion
                    ignoreFailures = !extension.strict
                    // this may be custom user file (gradle/config/checkstyle/checkstyle.xml) or default one
                    // (in different location)
                    configFile = configLoader.resolveCheckstyleConfig(false)
                    // this is required for ${config_loc} variable, but this will ALWAYS point to
                    // gradle/config/checkstyle/ (or other configured config location dir) because custom
                    // configuration files may be only there
                    configDirectory = configLoader.resolveCheckstyleConfigDir()
                    sourceSets = extension.sourceSets
                }
                if (extension.checkstyleBackport) {
                    repositories {
                        maven {
                            url 'https://rnveach.github.io/checkstyle-backport-jre8/maven2/'
                            // use custom repository ONLY for checkstyle (just in case)
                            content { includeGroup 'com.puppycrawl.tools' }
                        }
                    }
                    dependencies {
                        checkstyle "com.puppycrawl.tools:checkstyle-backport-jre8:${extension.checkstyleVersion}"
                    }
{'fraction_non_alphanumeric': 0.07133516411664705, 'fraction_numerical': 0.0014384726036354126, 'mean_word_length': 2.6837668593448942, 'pattern_counts': {'":': 0, '<': 19, '<?xml version=': 0, '>': 23, 'https://': 2, 'lorem ipsum': 0, 'www.': 0, 'xml': 4}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 4, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4230920', 'n_tokens_mistral': 3881, 'n_tokens_neox': 3579, 'n_words': 1119}