com.autonomousapps.internal.ManifestParser.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.internal
import com.autonomousapps.internal.utils.buildDocument
import com.autonomousapps.internal.utils.document.mapToSet
import org.w3c.dom.Document
import org.w3c.dom.Element
import java.io.File
internal class ManifestParser(
/** The namespace, or package name, as set by the Android DSL. May be empty. */
private val namespace: String
) {
class ParseResult(
val packageName: String,
/** The value of the `android:name` attribute. May be empty. */
val applicationName: String,
/** The value of the `android:theme` attribute. May be empty. */
val theme: String,
val components: Map>
)
/**
* The purpose of [allComponents] is to assist in the migration from the old to the new model.
*/
@Throws(ManifestParseException::class)
fun parse(manifest: File, allComponents: Boolean = false): ParseResult {
val document = buildDocument(manifest)
val packageName = packageName(manifest, document)
val application = application(document)
val applicationName = application?.getAttribute("android:name") ?: ""
val theme = application?.attributes?.getNamedItem("android:theme")
?.nodeValue?.substringAfter("@style/").orEmpty()
val services = application?.componentNames(Manifest.Component.SERVICE, packageName) ?: emptySet()
val providers = application?.componentNames(Manifest.Component.PROVIDER, packageName) ?: emptySet()
val activities = application?.componentNames(Manifest.Component.ACTIVITY, packageName) ?: emptySet()
val receivers = application?.componentNames(Manifest.Component.RECEIVER, packageName) ?: emptySet()
val componentsMapping = mutableMapOf>()
// "service" is enough to catch LeakCanary, and "provider" makes sense in principle. Trying not to be too aggressive.
if (services.isNotEmpty()) componentsMapping[Manifest.Component.SERVICE.mapKey] = services
if (providers.isNotEmpty()) componentsMapping[Manifest.Component.PROVIDER.mapKey] = providers
if (allComponents) {
if (activities.isNotEmpty()) componentsMapping[Manifest.Component.ACTIVITY.mapKey] = activities
if (receivers.isNotEmpty()) componentsMapping[Manifest.Component.RECEIVER.mapKey] = receivers
}
return ParseResult(
packageName = packageName,
applicationName = applicationName,
theme = theme,
components = componentsMapping
)
}
private fun application(document: Document): Element? {
val elements = document.getElementsByTagName("application")
return if (elements.length > 0) {
elements.item(0) as Element
} else {
null
}
}
// https://github.com/autonomousapps/dependency-analysis-android-gradle-plugin/issues/700
private fun packageName(manifest: File, document: Document): String {
return namespace.ifEmpty {
runCatching {
document.getElementsByTagName("manifest").item(0)
.attributes
.getNamedItem("package")
.nodeValue
}.getOrElse { t ->
throw if (t is NullPointerException) {
ManifestParseException(
"${manifest.path} has no 'package' attribute. You should use 'android.namespace' to set the package name " +
"and remove the 'package' attribute from the main manifest, since that attribute is set for removal " +
"with AGP 8.0.",
t
)
} else {
t
}
}
}
}
private fun Element.componentNames(
component: Manifest.Component,
packageName: String
): Set {
return getElementsByTagName(component.tagName)
.mapToSet {
it.attributes.getNamedItem(component.attrName).nodeValue.withPackageName(
packageName
)
}
}
private fun String.withPackageName(packageName: String): String {
return if (startsWith(".")) {
// item name is relative, so prefix with the package name
"$packageName$this"
} else {
// item name is absolute, so use it as-is
this
}
}
internal class ManifestParseException(
msg: String, cause: Throwable
) : RuntimeException(msg, cause)
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy