com.autonomousapps.model.internal.Capability.kt Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of dependency-analysis-gradle-plugin Show documentation
Show all versions of dependency-analysis-gradle-plugin Show documentation
Analyzes dependency usage in Android and JVM projects
// Copyright (c) 2024. Tony Robalik.
// SPDX-License-Identifier: Apache-2.0
package com.autonomousapps.model.internal
import com.autonomousapps.internal.utils.LexicographicIterableComparator
import com.autonomousapps.internal.utils.filterToOrderedSet
import com.autonomousapps.model.internal.intermediates.consumer.MemberAccess
import com.autonomousapps.model.internal.intermediates.producer.BinaryClass
import com.squareup.moshi.JsonClass
import dev.zacsweers.moshix.sealed.annotations.TypeLabel
@JsonClass(generateAdapter = false, generator = "sealed:type")
internal sealed class Capability : Comparable {
override fun compareTo(other: Capability): Int = javaClass.simpleName.compareTo(other.javaClass.simpleName)
/**
* This is for the JVM world, where sometimes multiple Jar files make up one component.
* Subclasses implement this to merge details in a useful way.
* It's implemented in all subclasses for completeness, although some situations might never occur in Android.
*/
abstract fun merge(other: Capability): Capability
}
@TypeLabel("linter")
@JsonClass(generateAdapter = false)
internal data class AndroidLinterCapability(
val lintRegistry: String,
/** True if this dependency contains _only_ an Android lint jar/registry. */
val isLintJar: Boolean,
) : Capability() {
override fun merge(other: Capability): Capability {
return AndroidLinterCapability(lintRegistry, isLintJar && (other as AndroidLinterCapability).isLintJar)
}
}
@TypeLabel("manifest")
@JsonClass(generateAdapter = false)
internal data class AndroidManifestCapability(
val componentMap: Map>,
) : Capability() {
enum class Component(val mapKey: String) {
ACTIVITY("activities"),
SERVICE("services"),
RECEIVER("receivers"),
PROVIDER("providers");
companion object {
internal fun of(mapKey: String): Component {
return values().find {
it.mapKey == mapKey
} ?: error("Could not find Manifest.Component for $mapKey")
}
}
}
override fun merge(other: Capability): Capability {
return AndroidManifestCapability(componentMap + (other as AndroidManifestCapability).componentMap)
}
}
@TypeLabel("asset")
@JsonClass(generateAdapter = false)
internal data class AndroidAssetCapability(
val assets: List,
) : Capability() {
override fun merge(other: Capability): Capability {
return AndroidAssetCapability(assets + (other as AndroidAssetCapability).assets)
}
}
@TypeLabel("res")
@JsonClass(generateAdapter = false)
internal data class AndroidResCapability(
val rImport: String,
val lines: List,
) : Capability() {
@JsonClass(generateAdapter = false)
data class Line(val type: String, val value: String)
override fun merge(other: Capability): Capability {
return AndroidResCapability(
rImport + (other as AndroidResCapability).rImport,
lines + other.lines
)
}
}
@TypeLabel("proc")
@JsonClass(generateAdapter = false)
internal data class AnnotationProcessorCapability(
val processor: String,
val supportedAnnotationTypes: Set,
) : Capability() {
override fun merge(other: Capability): Capability {
return AnnotationProcessorCapability(
processor, // other.processor ?
supportedAnnotationTypes + (other as AnnotationProcessorCapability).supportedAnnotationTypes
)
}
}
// @TypeLabel("binaryClass")
// @JsonClass(generateAdapter = false)
// internal data class BinaryClassCapability(
// val binaryClasses: Set,
// ) : Capability() {
//
// internal data class PartitionResult(
// val matchingClasses: Set,
// val nonMatchingClasses: Set,
// ) {
//
// companion object {
// fun empty(): PartitionResult = PartitionResult(emptySet(), emptySet())
// }
//
// class Builder {
// val matchingClasses = sortedSetOf()
// val nonMatchingClasses = sortedSetOf()
//
// fun build(): PartitionResult {
// return PartitionResult(
// matchingClasses = matchingClasses,
// nonMatchingClasses = nonMatchingClasses,
// )
// }
// }
// }
//
// override fun merge(other: Capability): Capability {
// return BinaryClassCapability(
// binaryClasses = binaryClasses + (other as BinaryClassCapability).binaryClasses,
// )
// }
//
// internal fun findMatchingClasses(memberAccess: MemberAccess): PartitionResult {
// val relevant = findRelevantBinaryClasses(memberAccess)
//
// // lenient
// if (relevant.isEmpty()) return PartitionResult.empty()
//
// return relevant
// .map { bin -> bin.partition(memberAccess) }
// .fold(PartitionResult.Builder()) { acc, (match, nonMatch) ->
// acc.apply {
// match?.let { matchingClasses.add(it) }
// nonMatch?.let { nonMatchingClasses.add(it) }
// }
// }
// .build()
// }
//
// /**
// * Example:
// * 1. [memberAccess] is for `groovy/lang/MetaClass#getProperty`.
// * 2. That method is actually provided by `groovy/lang/MetaObjectProtocol`, which `groovy/lang/MetaClass` implements.
// *
// * All of the above ("this" class, its super class, and its interfaces) are relevant for search purposes. Note we
// * don't inspect the member names for this check.
// */
// private fun findRelevantBinaryClasses(memberAccess: MemberAccess): Set {
// // direct references
// val relevant = binaryClasses.filterTo(mutableSetOf()) { bin ->
// bin.className == memberAccess.owner
// }
//
// // Walk up the class hierarchy
// fun walkUp(): Int {
// binaryClasses.filterTo(relevant) { bin ->
// bin.className in relevant.map { it.superClassName }
// || bin.className in relevant.flatMap { it.interfaces }
// }
// return relevant.size
// }
//
// // TODO(tsr): this could be more performant
// do {
// val size = relevant.size
// val newSize = walkUp()
// } while (newSize > size)
//
// return relevant
// }
//
// /**
// * Partitions and returns artificial pair of [BinaryClasses][BinaryClass]. Non-null elements indicate relevant (to
// * [memberAccess] matching and non-matching members of this `BinaryClass`. Matching members are binary-compatible; and
// * non-matching members have the same [name][com.autonomousapps.model.intermediates.producer.Member.name] but
// * incompatible [descriptors][com.autonomousapps.model.intermediates.producer.Member.descriptor], and are therefore
// * binary-incompatible.
// *
// * nb: We don't want this as a method directly in BinaryClass because it can't safely assert the prerequisite that
// * it's only called on "relevant" classes. THIS class, however, can, via findRelevantBinaryClasses.
// */
// private fun BinaryClass.partition(memberAccess: MemberAccess): Pair {
// // There can be only one match
// val matchingFields = effectivelyPublicFields.firstOrNull { it.matches(memberAccess) }
// val matchingMethods = effectivelyPublicMethods.firstOrNull { it.matches(memberAccess) }
//
// // There can be many non-matches
// val nonMatchingFields = effectivelyPublicFields.filterToOrderedSet { it.doesNotMatch(memberAccess) }
// val nonMatchingMethods = effectivelyPublicMethods.filterToOrderedSet { it.doesNotMatch(memberAccess) }
//
// // Create a view of the binary class containing only the matching members.
// val match = if (matchingFields != null || matchingMethods != null) {
// copy(
// effectivelyPublicFields = matchingFields?.let { setOf(it) }.orEmpty(),
// effectivelyPublicMethods = matchingMethods?.let { setOf(it) }.orEmpty()
// )
// } else {
// null
// }
//
// // Create a view of the binary class containing only the non-matching members.
// val nonMatch = if (nonMatchingFields.isNotEmpty() || nonMatchingMethods.isNotEmpty()) {
// copy(
// effectivelyPublicFields = nonMatchingFields,
// effectivelyPublicMethods = nonMatchingMethods,
// )
// } else {
// null
// }
//
// return match to nonMatch
// }
// }
@TypeLabel("class")
@JsonClass(generateAdapter = false)
internal data class ClassCapability(
val classes: Set,
) : Capability() {
override fun merge(other: Capability): Capability {
return ClassCapability(classes + (other as ClassCapability).classes)
}
}
@TypeLabel("const")
@JsonClass(generateAdapter = false)
internal data class ConstantCapability(
/** Map of fully-qualified class names to constant field names. */
val constants: Map>,
/** Kotlin classes with top-level declarations. */
val ktFiles: Set,
) : Capability() {
override fun merge(other: Capability): Capability {
return ConstantCapability(
constants + (other as ConstantCapability).constants,
ktFiles + other.ktFiles
)
}
}
@TypeLabel("inferred")
@JsonClass(generateAdapter = false)
internal data class InferredCapability(
/**
* True if this dependency contains only annotations that are only needed at compile-time (`CLASS` and `SOURCE` level
* retention policies). False otherwise.
*/
val isCompileOnlyAnnotations: Boolean,
) : Capability() {
override fun merge(other: Capability): Capability {
return InferredCapability(isCompileOnlyAnnotations && (other as InferredCapability).isCompileOnlyAnnotations)
}
}
@TypeLabel("inline")
@JsonClass(generateAdapter = false)
internal data class InlineMemberCapability(
val inlineMembers: Set,
) : Capability() {
@JsonClass(generateAdapter = false)
data class InlineMember(
val packageName: String,
val inlineMembers: Set,
) : Comparable {
override fun compareTo(other: InlineMember): Int = compareBy(InlineMember::packageName)
.thenBy(LexicographicIterableComparator()) { it.inlineMembers }
.compare(this, other)
}
override fun merge(other: Capability): Capability {
return InlineMemberCapability(inlineMembers + (other as InlineMemberCapability).inlineMembers)
}
}
@TypeLabel("typealias")
@JsonClass(generateAdapter = false)
internal data class TypealiasCapability(
val typealiases: Set,
) : Capability() {
@JsonClass(generateAdapter = false)
data class Typealias(
val packageName: String,
val typealiases: Set,
) : Comparable {
override fun compareTo(other: Typealias): Int = compareBy(Typealias::packageName)
.thenBy(LexicographicIterableComparator()) { it.typealiases }
.compare(this, other)
@JsonClass(generateAdapter = false)
data class Alias(
val name: String,
val expandedType: String,
) : Comparable {
override fun compareTo(other: Alias): Int = compareBy(Alias::name)
.thenComparing(Alias::expandedType)
.compare(this, other)
}
}
override fun merge(other: Capability): Capability {
return TypealiasCapability(typealiases + (other as TypealiasCapability).typealiases)
}
}
@TypeLabel("native")
@JsonClass(generateAdapter = false)
internal data class NativeLibCapability(
val fileNames: Set,
) : Capability() {
override fun merge(other: Capability): Capability {
return NativeLibCapability(fileNames + (other as NativeLibCapability).fileNames)
}
}
@TypeLabel("service_loader")
@JsonClass(generateAdapter = false)
internal data class ServiceLoaderCapability(
val providerFile: String,
val providerClasses: Set,
) : Capability() {
override fun merge(other: Capability): Capability {
return ServiceLoaderCapability(
providerFile + (other as ServiceLoaderCapability).providerFile, providerClasses + other.providerClasses
)
}
}
@TypeLabel("security_provider")
@JsonClass(generateAdapter = false)
internal data class SecurityProviderCapability(
val securityProviders: Set,
) : Capability() {
override fun merge(other: Capability): Capability {
return SecurityProviderCapability(securityProviders + (other as SecurityProviderCapability).securityProviders)
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy