All Downloads are FREE. Search and download functionalities are using the official Maven repository.

name.remal.gradle_plugins.dsl.utils.readJavaModuleName.kt Maven / Gradle / Ivy

There is a newer version: 1.9.2
Show newest version
package name.remal.gradle_plugins.dsl.utils

import name.remal.ASM_API
import name.remal.accept
import org.objectweb.asm.ClassReader
import org.objectweb.asm.ClassVisitor
import org.objectweb.asm.ModuleVisitor
import org.objectweb.asm.Opcodes
import java.io.File
import java.lang.reflect.Field
import java.util.jar.Manifest
import java.util.zip.ZipEntry
import java.util.zip.ZipFile
import java.util.zip.ZipFile.OPEN_READ

@Suppress("ComplexMethod", "NestedBlockDepth")
fun readJavaModuleName(artifactFile: File): String? {
    val file = artifactFile.absoluteFile
    try {
        if (file.isFile) {
            ZipFile(file, OPEN_READ).use { zipFile ->
                MODULE_INFO_PATHS.forEach { moduleInfoPath ->
                    zipFile.getEntry(moduleInfoPath)?.takeUnless(ZipEntry::isDirectory)?.let(zipFile::getInputStream)?.use { inputStream ->
                        try {
                            val bytecode = inputStream.readBytes()
                            val classReader = ClassReader(bytecode)
                            val visitor = ModuleNameVisitor()
                            classReader.accept(visitor)
                            visitor.moduleName?.let { return it }
                        } catch (e: Throwable) {
                            getGradleLogger(::readJavaModuleName.name).warn("Error processing $file!/$moduleInfoPath", e)
                        }
                    }
                }

                zipFile.getEntry(MANIFEST_PATH)?.takeUnless(ZipEntry::isDirectory)?.let(zipFile::getInputStream)?.use { inputStream ->
                    try {
                        Manifest(inputStream).mainAttributes?.getValue(MODULE_NAME_MANIFEST_ATTRIBUTE)?.let { return it }
                    } catch (e: Throwable) {
                        getGradleLogger(::readJavaModuleName.name).warn("Error processing $file!/$MANIFEST_PATH", e)
                    }
                }
            }

            return file.autoJavaModuleName

        } else if (file.isDirectory) {
            MODULE_INFO_PATHS.forEach { moduleInfoPath ->
                val moduleInfoFile = file.resolve(moduleInfoPath)
                if (moduleInfoFile.isFile) {
                    try {
                        val bytecode = moduleInfoFile.readBytes()
                        val classReader = ClassReader(bytecode)
                        val visitor = ModuleNameVisitor()
                        classReader.accept(visitor)
                        visitor.moduleName?.let { return it }
                    } catch (e: Throwable) {
                        getGradleLogger(::readJavaModuleName.name).warn("Error processing $moduleInfoFile", e)
                    }
                }
            }

            file.resolve(MANIFEST_PATH).takeIf(File::isFile)?.let { manifestFile ->
                try {
                    manifestFile.inputStream().use { inputStream ->
                        Manifest(inputStream).mainAttributes?.getValue(MODULE_NAME_MANIFEST_ATTRIBUTE)?.let { return it }
                    }
                } catch (e: Throwable) {
                    getGradleLogger(::readJavaModuleName.name).warn("Error processing $manifestFile", e)
                }
            }
        }
    } catch (e: Throwable) {
        getGradleLogger(::readJavaModuleName.name).warn("Error processing $file", e)
    }

    return null
}

private val DASH_VERSION_REGEX = Regex("-(\\d+(\\..*|$))")
private val NON_ALPHANUM_REGEX = Regex("[^A-Za-z0-9]")
private val REPEATING_DOTS = Regex("\\.{2,}")
private val File.autoJavaModuleName: String
    get() {
        var name = this.nameWithoutExtension
        name = name.replace(DASH_VERSION_REGEX, "")
        name = name.replace(NON_ALPHANUM_REGEX, ".")
        name = name.replace(REPEATING_DOTS, ".").trim('.')
        return name
    }


private class ModuleNameVisitor : ClassVisitor(ASM_API) {

    var moduleName: String? = null

    override fun visitModule(name: String?, access: Int, version: String?): ModuleVisitor? {
        moduleName = name
        return null
    }

}

private val MULTI_RELEASE_JAR_VERSIONS: List = Opcodes::class.java.fields.asSequence()
    .filter { it.type == Int::class.java }
    .filter { it.name.startsWith("V") && !it.name.contains('_') }
    .map(Field::getName)
    .mapNotNull { it.substring(1).toIntOrNull() }
    .toList()
    .sorted()

val MODULE_INFO_PATHS: List = listOf("module-info.class") + MULTI_RELEASE_JAR_VERSIONS.map { "META-INF/versions/$it/module-info.class" }

private const val MANIFEST_PATH = "META-INF/MANIFEST.MF"
const val MODULE_NAME_MANIFEST_ATTRIBUTE = "Automatic-Module-Name"




© 2015 - 2024 Weber Informatics LLC | Privacy Policy