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

com.github.jengelman.gradle.plugins.shadow.relocation.SimpleRelocator.kt Maven / Gradle / Ivy

Go to download

Gradle plugin to create fat/uber JARs, apply file transforms, and relocate packages for applications and libraries. Gradle version of Maven's Shade plugin.

There is a newer version: 9.0.0-beta4
Show newest version
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