name.remal.gradle_plugins.dsl.artifact.Artifact.kt Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of gradle-plugins-kotlin-dsl Show documentation
Show all versions of gradle-plugins-kotlin-dsl Show documentation
Remal Gradle plugins: gradle-plugins-kotlin-dsl
package name.remal.gradle_plugins.dsl.artifact
import com.google.common.collect.MultimapBuilder
import com.google.common.collect.SetMultimap
import name.remal.*
import name.remal.gradle_plugins.dsl.cache.BaseCache
import name.remal.gradle_plugins.dsl.utils.PathMatcher
import org.objectweb.asm.ClassReader
import org.objectweb.asm.ClassReader.*
import org.objectweb.asm.Opcodes.ACC_ANNOTATION
import org.objectweb.asm.Type.getType
import org.objectweb.asm.tree.AnnotationNode
import org.objectweb.asm.tree.ClassNode
import java.io.*
import java.nio.charset.StandardCharsets.UTF_8
import java.util.*
import java.util.regex.Pattern
import java.util.zip.ZipException
import java.util.zip.ZipFile
import java.util.zip.ZipInputStream
import kotlin.collections.set
import kotlin.text.isNotEmpty
import kotlin.text.trim
class Artifact(file: File) : BaseHasEntries() {
companion object {
private val PATH_SPLITTER = Pattern.compile("[/\\\\]")
}
val file: File = file.absoluteFile
override val entryNames: Set by lazy {
val result = sortedSetOf()
if (file.isFile) {
try {
ZipFile(file).use { zipFile ->
val entries = zipFile.entries()
while (entries.hasMoreElements()) {
val entry = entries.nextElement()
if (entry.isDirectory) continue
result += entry.name
}
}
} catch (e: ZipException) {
// do nothing
}
} else if (file.isDirectory) {
file.walk().filter { file != it }.filter(File::isFile).forEach { resourceFile ->
result += resourceFile.relativeTo(file).path.replace(File.separatorChar, '/')
}
}
return@lazy result
}
override fun openStream(entryName: String): InputStream {
if (file.isFile) {
val zipFile = ZipFile(file)
val entry = zipFile.getEntry(entryName) ?: throw ArtifactEntryNotFoundException("Artifact entry not found: $entryName")
val inputStream = zipFile.getInputStream(entry)
return object : BufferedInputStream(inputStream) {
override fun close() {
super.close()
zipFile.close()
}
}
} else if (file.isDirectory) {
try {
return File(file, entryName).inputStream()
} catch (e: FileNotFoundException) {
throw ArtifactEntryNotFoundException("Artifact entry not found: $entryName", e)
}
} else {
throw ArtifactFileNotFoundException("Artifact file not found: $file")
}
}
@Suppress("ComplexMethod")
override fun forEachEntry(pattern: String?, action: (entry: HasEntries.Entry) -> Unit) {
val matcher = pattern.nullIfEmpty()?.let { PathMatcher(it) }
if (file.isFile) {
FileInputStream(file).use { fileInput ->
ZipInputStream(fileInput).use { zipInput ->
while (true) {
val entry = zipInput.nextEntry ?: break
if (entry.isDirectory) continue
val entryName = entry.name
if (matcher == null || matcher.matches(entryName)) {
action(HasEntries.Entry(entryName, { zipInput }))
}
}
}
}
} else if (file.isDirectory) {
file.walk().filter { file != it }.forEach { entryFile ->
val entryName = entryFile.relativeTo(file).invariantSeparatorsPath
if (matcher == null || matcher.matches(entryName)) {
if (entryFile.isFile) {
var streamToClose: InputStream? = null
try {
action(HasEntries.Entry(entryName, { FileInputStream(entryFile).also { streamToClose = it } }))
} finally {
streamToClose?.close()
}
}
}
}
}
}
private val annotationsInfo: AnnotationsInfo by lazy {
AnnotationsInfoPersistenceCache.read()?.get(file)?.let { return@lazy it }
return@lazy AnnotationsInfo().also { result ->
forEachEntry("**/*.class") { classEntry ->
val classNode = ClassNode().also {
val bytecode = classEntry.inputStream.readBytes()
ClassReader(bytecode).accept(it, SKIP_CODE or SKIP_DEBUG or SKIP_FRAMES)
}
if (0 == (classNode.access and ACC_ANNOTATION)) return@forEachEntry
result.annotationClassNames.add(classNode.name.replace('/', '.'))
emptyStream()
.plus(classNode.visibleAnnotations?.stream() ?: emptyStream())
.plus(classNode.invisibleAnnotations?.stream() ?: emptyStream())
.forEach { annotationNode ->
val annotationClassName = getType(annotationNode.desc).className
result.annotationsMapping.put(annotationClassName, classNode.name.replace('/', '.'))
}
}
AnnotationsInfoPersistenceCache.write(
(AnnotationsInfoPersistenceCache.read()?.toMutableMap() ?: mutableMapOf())
.also { it[file] = result }
)
}
}
override val annotationClassNames: Set by lazy { annotationsInfo.annotationClassNames }
override val annotationsMapping: Map> by lazy {
annotationsInfo.annotationsMapping
.asMap().uncheckedCast