com.github.jengelman.gradle.plugins.shadow.relocation.SimpleRelocator.kt Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of shadow-gradle-plugin Show documentation
Show all versions of shadow-gradle-plugin Show documentation
Gradle plugin to create fat/uber JARs, apply file transforms, and relocate packages for applications and libraries. Gradle version of Maven's Shade plugin.
package com.github.jengelman.gradle.plugins.shadow.relocation
import java.util.regex.Pattern
import org.codehaus.plexus.util.SelectorUtils
import org.gradle.api.tasks.Input
import org.gradle.api.tasks.Optional
/**
* Modified from [org.apache.maven.plugins.shade.relocation.SimpleRelocator.java](https://github.com/apache/maven-shade-plugin/blob/master/src/main/java/org/apache/maven/plugins/shade/relocation/SimpleRelocator.java).
*
* @author Jason van Zyl
* @author Mauro Talevi
* @author John Engelman
*/
@CacheableRelocator
public open class SimpleRelocator @JvmOverloads constructor(
pattern: String?,
shadedPattern: String?,
includes: List? = null,
excludes: List? = null,
private val _rawString: Boolean = false,
) : Relocator {
private val _pattern: String
private val _pathPattern: String
private val _shadedPattern: String
private val _shadedPathPattern: String
private val _includes = mutableSetOf()
private val _excludes = mutableSetOf()
init {
if (_rawString) {
_pathPattern = pattern.orEmpty()
_shadedPathPattern = shadedPattern.orEmpty()
_pattern = "" // not used for raw string relocator
_shadedPattern = "" // not used for raw string relocator
} else {
if (pattern == null) {
_pattern = ""
_pathPattern = ""
} else {
_pattern = pattern.replace('/', '.')
_pathPattern = pattern.replace('.', '/')
}
if (shadedPattern == null) {
_shadedPattern = "hidden.${_pattern}"
_shadedPathPattern = "hidden/$_pathPattern"
} else {
_shadedPattern = shadedPattern.replace('/', '.')
_shadedPathPattern = shadedPattern.replace('.', '/')
}
}
_includes += normalizePatterns(includes)
_excludes += normalizePatterns(excludes)
}
@get:Input
@get:Optional
public open val pattern: String get() = _pattern
@get:Input
public open val pathPattern: String get() = _pathPattern
@get:Input
@get:Optional
public open val shadedPattern: String get() = _shadedPattern
@get:Input
public open val shadedPathPattern: String get() = _shadedPathPattern
@get:Input
public open val rawString: Boolean get() = _rawString
@get:Input
public open val includes: Set get() = _includes
@get:Input
public open val excludes: Set get() = _excludes
public open fun include(pattern: String): SimpleRelocator = apply {
_includes += normalizePatterns(listOf(pattern))
}
public open fun exclude(pattern: String): SimpleRelocator = apply {
_excludes += normalizePatterns(listOf(pattern))
}
override fun canRelocatePath(path: String): Boolean {
if (_rawString) return Pattern.compile(_pathPattern).matcher(path).find()
// If string is too short - no need to perform expensive string operations
if (path.length < _pathPattern.length) return false
val adjustedPath = if (path.endsWith(".class")) {
// Safeguard against strings containing only ".class"
if (path.length == 6) return false
path.dropLast(6)
} else {
path
}
// Allow for annoying option of an extra / on the front of a path. See MSHADE-119 comes from getClass().getResource("/a/b/c.properties").
val startIndex = if (adjustedPath.startsWith("/")) 1 else 0
val pathStartsWithPattern = adjustedPath.startsWith(_pathPattern, startIndex)
return pathStartsWithPattern && isIncluded(adjustedPath) && !isExcluded(adjustedPath)
}
override fun canRelocateClass(className: String): Boolean {
return !_rawString && !className.contains('/') && canRelocatePath(className.replace('.', '/'))
}
override fun relocatePath(context: RelocatePathContext): String {
val path = context.path
context.stats.relocate(_pathPattern, _shadedPathPattern)
return if (_rawString) {
path.replace(_pathPattern.toRegex(), _shadedPathPattern)
} else {
path.replaceFirst(_pathPattern, _shadedPathPattern)
}
}
override fun relocateClass(context: RelocateClassContext): String {
context.stats.relocate(_pathPattern, _shadedPathPattern)
return context.className.replaceFirst(_pattern, _shadedPattern)
}
override fun applyToSourceContent(sourceContent: String): String {
return if (_rawString) {
sourceContent
} else {
sourceContent.replace("\\b$_pattern".toRegex(), _shadedPattern)
}
}
private fun isIncluded(path: String): Boolean {
return _includes.isEmpty() || _includes.any { SelectorUtils.matchPath(it, path, "/", true) }
}
private fun isExcluded(path: String): Boolean {
return _excludes.any { SelectorUtils.matchPath(it, path, "/", true) }
}
private companion object {
fun normalizePatterns(patterns: Collection?) = buildSet {
patterns ?: return@buildSet
for (pattern in patterns) {
// Regex patterns don't need to be normalized and stay as is
if (pattern.startsWith(SelectorUtils.REGEX_HANDLER_PREFIX)) {
add(pattern)
continue
}
val classPattern = pattern.replace('.', '/')
add(classPattern)
if (classPattern.endsWith("/*")) {
val packagePattern = classPattern.substring(0, classPattern.lastIndexOf('/'))
add(packagePattern)
}
}
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy