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

com.autonomousapps.internal.models.kt Maven / Gradle / Ivy

There is a newer version: 2.0.2
Show newest version
package com.autonomousapps.internal

import com.autonomousapps.internal.asm.Opcodes
import com.autonomousapps.internal.utils.efficient
import com.autonomousapps.internal.utils.filterNotToSet
import com.autonomousapps.internal.utils.mapToSet
import com.squareup.moshi.JsonClass
import java.lang.annotation.RetentionPolicy
import java.util.regex.Pattern

/** Metadata from an Android manifest. */
data class Manifest(
  /** The package name per ``. */
  val packageName: String,
  /** A map of component type to components. */
  val componentMap: Map>
) {

  internal enum class Component(val tagName: String, val mapKey: String) {
    ACTIVITY("activity", "activities"),
    SERVICE("service", "services"),
    RECEIVER("receiver", "receivers"),
    PROVIDER("provider", "providers");

    val attrName = "android:name"
  }
}

data class AnalyzedClass(
  val className: String,
  val outerClassName: String?,
  val superClassName: String?,
  val retentionPolicy: RetentionPolicy?,
  /**
   * Ignoring constructors and static initializers. Such a class will not prejudice the compileOnly
   * algorithm against declaring the containing jar "annotations-only". See for example
   * `org.jetbrains.annotations.ApiStatus`. This outer class only exists as a sort of "namespace"
   * for the annotations it contains.
   */
  val hasNoMembers: Boolean,
  val access: Access,
  val methods: Set,
  val innerClasses: Set,
  val constantFields: Set
) : Comparable {

  constructor(
    className: String,
    outerClassName: String?,
    superClassName: String?,
    retentionPolicy: String?,
    isAnnotation: Boolean,
    hasNoMembers: Boolean,
    access: Access,
    methods: Set,
    innerClasses: Set,
    constantClasses: Set
  ) : this(
    className = className,
    outerClassName = outerClassName,
    superClassName = superClassName,
    retentionPolicy = fromString(retentionPolicy, isAnnotation),
    hasNoMembers = hasNoMembers,
    access = access,
    methods = methods,
    innerClasses = innerClasses,
    constantFields = constantClasses
  )

  companion object {
    fun fromString(name: String?, isAnnotation: Boolean): RetentionPolicy? = when {
      RetentionPolicy.CLASS.name == name -> RetentionPolicy.CLASS
      RetentionPolicy.SOURCE.name == name -> RetentionPolicy.SOURCE
      RetentionPolicy.RUNTIME.name == name -> RetentionPolicy.RUNTIME
      // Default if RetentionPolicy is not specified.
      isAnnotation -> RetentionPolicy.CLASS
      else -> null
    }
  }

  override fun compareTo(other: AnalyzedClass): Int = className.compareTo(other.className)
}

enum class Access {
  PUBLIC,
  PROTECTED,
  PRIVATE,
  PACKAGE_PRIVATE;

  companion object {
    fun fromInt(access: Int): Access {
      return when {
        isPublic(access) -> PUBLIC
        isProtected(access) -> PROTECTED
        isPrivate(access) -> PRIVATE
        isPackagePrivate(access) -> PACKAGE_PRIVATE
        else -> throw IllegalArgumentException("Access <$access> is an unknown value")
      }
    }

    private fun isPackagePrivate(access: Int): Boolean =
      !isPublic(access) && !isPrivate(access) && !isProtected(access)

    private fun isPublic(access: Int): Boolean = access and Opcodes.ACC_PUBLIC != 0

    private fun isPrivate(access: Int): Boolean = access and Opcodes.ACC_PRIVATE != 0

    private fun isProtected(access: Int): Boolean = access and Opcodes.ACC_PROTECTED != 0
  }
}

data class Method internal constructor(val types: Set) {

  constructor(descriptor: String) : this(findTypes(descriptor))

  companion object {
    private val DESCRIPTOR = Pattern.compile("L(.+?);")

    private fun findTypes(descriptor: String): Set {
      val types = sortedSetOf()
      val m = DESCRIPTOR.matcher(descriptor)
      while (m.find()) {
        types.add(m.group(1))
      }
      return types.efficient()
    }
  }
}

@JsonClass(generateAdapter = false)
internal data class AbiExclusions(
  val annotationExclusions: Set = emptySet(),
  val classExclusions: Set = emptySet(),
  val pathExclusions: Set = emptySet()
) {

  @Transient
  private val annotationRegexes = annotationExclusions.mapToSet(String::toRegex)

  @Transient
  private val classRegexes = classExclusions.mapToSet(String::toRegex)

  @Transient
  private val pathRegexes = pathExclusions.mapToSet(String::toRegex)

  fun excludesAnnotation(fqcn: String): Boolean = annotationRegexes.any { it.containsMatchIn(fqcn.dotty()) }
  fun excludesClass(fqcn: String) = classRegexes.any { it.containsMatchIn(fqcn.dotty()) }
  fun excludesPath(path: String) = pathRegexes.any { it.containsMatchIn(path.dotty()) }

  // The user-facing regex expects FQCNs to be delimited with dots, not slashes
  private fun String.dotty() = replace('/', '.')

  companion object {
    val NONE = AbiExclusions()
  }
}

@JsonClass(generateAdapter = false)
internal data class UsagesExclusions(
  val classExclusions: Set = emptySet()
) {

  @Transient
  private val classRegexes = classExclusions.mapToSet(String::toRegex)

  private fun excludesClass(fqcn: String) = classRegexes.any { it.containsMatchIn(fqcn.dotty()) }

  fun excludeClassesFromSet(fqcn: Set): Set {
    return fqcn.filterNotToSet { excludesClass(it) }
  }

  // The user-facing regex expects FQCNs to be delimited with dots, not slashes
  private fun String.dotty() = replace('/', '.')

  companion object {
    val NONE = UsagesExclusions()
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy