<commit_before>package com.cognifide.gradle.aem.pkg

import com.cognifide.gradle.aem.api.AemConfig
import com.cognifide.gradle.aem.api.AemException
import com.cognifide.gradle.aem.api.AemTask
import com.cognifide.gradle.aem.internal.Patterns
import com.cognifide.gradle.aem.internal.PropertyParser
import org.apache.commons.io.FileUtils
import org.gradle.api.Project
import org.gradle.api.file.CopySpec
import org.gradle.api.file.DuplicatesStrategy
import org.gradle.api.tasks.*
import org.gradle.api.tasks.bundling.Zip
import org.jsoup.Jsoup
import org.jsoup.nodes.Element
import org.jsoup.parser.Parser
import java.io.File

open class ComposeTask : Zip(), AemTask {

    companion object {
        val NAME = "aemCompose"

        val DEPENDENCIES_SUFFIX = ".dependencies"
    }

    @Nested
    final override val config = AemConfig.of(project)

    @InputDirectory
    val vaultDir = AemTask.temporaryDir(project, PrepareTask.NAME, PackagePlugin.VLT_PATH)

    @Internal
    val filterRoots = mutableSetOf<Element>()

    @get:Input
    val filterRootsProp: String
        get() = filterRoots.joinToString(config.vaultLineSeparatorString) { it.toString() }

    @Internal
    var filterRootDefault = { subproject: Project, subconfig: AemConfig ->
        Element("<filter root=\"${subconfig.bundlePath}\"/>")
    }

    @Internal
    val propertyParser = PropertyParser(project)

    @Internal
    private var bundleCollectors: List<() -> Unit> = mutableListOf()

    @Internal
    private var contentCollectors: List<() -> Unit> = mutableListOf()

    @Internal
    private var archiveName: String? = null

    @Input
    val bundleIncludes = mutableMapOf<String, List<String>>()

    @Internal
    private val bundleResolvers = mutableMapOf<String, () -> Unit>()

    @Internal
    var fileFilter: ((CopySpec) -> Unit) = { spec ->
        spec.exclude(config.filesExcluded)
        spec.eachFile({ fileDetail ->
            val path = fileDetail.relativeSourcePath.pathString
            if (Patterns.wildcard(path, config.filesExpanded)) {
                fileDetail.filter({ line -> propertyParser.expandPackage(line, fileProperties, path) })
            }
        })
    }

    @get:Internal
    val fileProperties
        get() = mapOf(
                "filters" to filterRoots,
                "filterRoots" to filterRootsProp
        )

    init {
        description = "Composes CRX package from JCR content and built OSGi bundles"
        group = AemTask.GROUP

        duplicatesStrategy = DuplicatesStrategy.WARN
        isZip64 = true

        // Include itself by default
        includeProject(project)
        includeVault(vaultDir)

        // Evaluate inclusion above and other cross project inclusions
        project.gradle.projectsEvaluated({
            fromBundles()
            fromContents()
        })
    }

    private fun fromBundles() {
        bundleCollectors.onEach { it() }
    }

    private fun fromContents() {
        contentCollectors.onEach { it() }
    }

    fun includeProject(projectPath: String) {
        includeProject(findProject(projectPath))
    }

    fun includeProject(project: Project) {
        includeContent(project)
        includeBundles(project)
    }

    fun includeSubprojects() {
        includeProjects("${project.path}:*")
    }

    fun includeProjects(pathFilter: String) {
        project.gradle.afterProject { subproject ->
            if (subproject != project
                    && subproject.plugins.hasPlugin(PackagePlugin.ID)
                    && (pathFilter.isBlank() || Patterns.wildcard(subproject.path, pathFilter))) {
                includeProject(subproject)
            }
        }
    }

    fun includeProjects(projectPaths: Collection<String>) {
        projectPaths.onEach { includeProject(it) }
    }

    fun includeBundles(projectPath: String) {
        includeBundlesAtPath(findProject(projectPath))
    }

    fun includeBundles(project: Project) {
        includeBundlesAtPath(project)
    }

    fun includeBundles(projectPath: String, runMode: String) {
        includeBundlesAtPath(findProject(projectPath), runMode = runMode)
    }

    fun mergeBundles(projectPath: String) {
        includeBundlesAtPath(findProject(projectPath))
    }

    fun mergeBundles(projectPath: String, runMode: String) {
        includeBundlesAtPath(findProject(projectPath), runMode = runMode)
    }

    fun includeBundlesAtPath(projectPath: String, installPath: String) {
        includeBundlesAtPath(findProject(projectPath), installPath)
    }

    fun includeBundlesAtPath(project: Project, installPath: String? = null, runMode: String? = null) {
        bundleCollectors += {
            val config = AemConfig.of(project)

            dependProject(project, config.dependBundlesTaskNames)

            var effectiveInstallPath = if (!installPath.isNullOrBlank()) {
                installPath
            } else {
                AemConfig.of(project).bundlePath
            }

            if (!runMode.isNullOrBlank()) {
                effectiveInstallPath = "$effectiveInstallPath.$runMode"
            }

            val collector = BundleCollector(project)
            val includes = collector.all

            if (includes.isNotEmpty()) {
                val resolutionPath = project.path.replace(":", "/").removeSurrounding("/")
                val resolutionDir = AemTask.temporaryDir(this.project, NAME, resolutionPath)
                val resolver = { collector.allJars.forEach { FileUtils.copyFileToDirectory(it, resolutionDir) } }

                bundleIncludes[project.name] = includes
                bundleResolvers[project.name] = resolver

                if (includes.isNotEmpty()) {
                    into("${PackagePlugin.JCR_ROOT}/$effectiveInstallPath") { spec ->
                        spec.from(resolutionDir)
                        fileFilter(spec)
                    }
                }
            }<commit_msg>Bundles subdir<commit_after>package com.cognifide.gradle.aem.pkg

import com.cognifide.gradle.aem.api.AemConfig
import com.cognifide.gradle.aem.api.AemException
import com.cognifide.gradle.aem.api.AemTask
import com.cognifide.gradle.aem.internal.Patterns
import com.cognifide.gradle.aem.internal.PropertyParser
import org.apache.commons.io.FileUtils
import org.gradle.api.Project
import org.gradle.api.file.CopySpec
import org.gradle.api.file.DuplicatesStrategy
import org.gradle.api.tasks.*
import org.gradle.api.tasks.bundling.Zip
import org.jsoup.Jsoup
import org.jsoup.nodes.Element
import org.jsoup.parser.Parser
import java.io.File

open class ComposeTask : Zip(), AemTask {

    companion object {
        val NAME = "aemCompose"

        val DEPENDENCIES_SUFFIX = ".dependencies"
    }

    @Nested
    final override val config = AemConfig.of(project)

    @InputDirectory
    val vaultDir = AemTask.temporaryDir(project, PrepareTask.NAME, PackagePlugin.VLT_PATH)

    @Internal
    val filterRoots = mutableSetOf<Element>()

    @get:Input
    val filterRootsProp: String
        get() = filterRoots.joinToString(config.vaultLineSeparatorString) { it.toString() }

    @Internal
    var filterRootDefault = { subproject: Project, subconfig: AemConfig ->
        Element("<filter root=\"${subconfig.bundlePath}\"/>")
    }

    @Internal
    val propertyParser = PropertyParser(project)

    @Internal
    private var bundleCollectors: List<() -> Unit> = mutableListOf()

    @Internal
    private var contentCollectors: List<() -> Unit> = mutableListOf()

    @Internal
    private var archiveName: String? = null

    @Input
    val bundleIncludes = mutableMapOf<String, List<String>>()

    @Internal
    private val bundleResolvers = mutableMapOf<String, () -> Unit>()

    @Internal
    var fileFilter: ((CopySpec) -> Unit) = { spec ->
        spec.exclude(config.filesExcluded)
        spec.eachFile({ fileDetail ->
            val path = fileDetail.relativeSourcePath.pathString
            if (Patterns.wildcard(path, config.filesExpanded)) {
                fileDetail.filter({ line -> propertyParser.expandPackage(line, fileProperties, path) })
            }
        })
    }

    @get:Internal
    val fileProperties
        get() = mapOf(
                "filters" to filterRoots,
                "filterRoots" to filterRootsProp
        )

    init {
        description = "Composes CRX package from JCR content and built OSGi bundles"
        group = AemTask.GROUP

        duplicatesStrategy = DuplicatesStrategy.WARN
        isZip64 = true

        // Include itself by default
        includeProject(project)
        includeVault(vaultDir)

        // Evaluate inclusion above and other cross project inclusions
        project.gradle.projectsEvaluated({
            fromBundles()
            fromContents()
        })
    }

    private fun fromBundles() {
        bundleCollectors.onEach { it() }
    }

    private fun fromContents() {
        contentCollectors.onEach { it() }
    }

    fun includeProject(projectPath: String) {
        includeProject(findProject(projectPath))
    }

    fun includeProject(project: Project) {
        includeContent(project)
        includeBundles(project)
    }

    fun includeSubprojects() {
        includeProjects("${project.path}:*")
    }

    fun includeProjects(pathFilter: String) {
        project.gradle.afterProject { subproject ->
            if (subproject != project
                    && subproject.plugins.hasPlugin(PackagePlugin.ID)
                    && (pathFilter.isBlank() || Patterns.wildcard(subproject.path, pathFilter))) {
                includeProject(subproject)
            }
        }
    }

    fun includeProjects(projectPaths: Collection<String>) {
        projectPaths.onEach { includeProject(it) }
    }

    fun includeBundles(projectPath: String) {
        includeBundlesAtPath(findProject(projectPath))
    }

    fun includeBundles(project: Project) {
        includeBundlesAtPath(project)
    }

    fun includeBundles(projectPath: String, runMode: String) {
        includeBundlesAtPath(findProject(projectPath), runMode = runMode)
    }

    fun mergeBundles(projectPath: String) {
        includeBundlesAtPath(findProject(projectPath))
    }

    fun mergeBundles(projectPath: String, runMode: String) {
        includeBundlesAtPath(findProject(projectPath), runMode = runMode)
    }

    fun includeBundlesAtPath(projectPath: String, installPath: String) {
        includeBundlesAtPath(findProject(projectPath), installPath)
    }

    fun includeBundlesAtPath(project: Project, installPath: String? = null, runMode: String? = null) {
        bundleCollectors += {
            val config = AemConfig.of(project)

            dependProject(project, config.dependBundlesTaskNames)

            var effectiveInstallPath = if (!installPath.isNullOrBlank()) {
                installPath
            } else {
                AemConfig.of(project).bundlePath
            }

            if (!runMode.isNullOrBlank()) {
                effectiveInstallPath = "$effectiveInstallPath.$runMode"
            }

            val collector = BundleCollector(project)
            val includes = collector.all

            if (includes.isNotEmpty()) {
                val resolutionPath = "bundles/${project.path.replace(":", "/").removeSurrounding("/")}"
                val resolutionDir = AemTask.temporaryDir(this.project, NAME, resolutionPath)
                val resolver = { collector.allJars.forEach { FileUtils.copyFileToDirectory(it, resolutionDir) } }

                bundleIncludes[project.name] = includes
                bundleResolvers[project.name] = resolver

                if (includes.isNotEmpty()) {
                    into("${PackagePlugin.JCR_ROOT}/$effectiveInstallPath") { spec ->
                        spec.from(resolutionDir)
                        fileFilter(spec)
                    }
                }
            }
{'fraction_non_alphanumeric': 0.0977785257489061, 'fraction_numerical': 0.0003365870077415012, 'mean_word_length': 2.494560423404881, 'pattern_counts': {'":': 2, '<': 19, '<?xml version=': 0, '>': 39, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6814186', 'n_tokens_mistral': 3444, 'n_tokens_neox': 3263, 'n_words': 623}