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

com.airbnb.epoxy.processor.DataBindingModuleLookup.kt Maven / Gradle / Ivy

package com.airbnb.epoxy.processor

import androidx.room.compiler.processing.XProcessingEnv
import androidx.room.compiler.processing.XTypeElement
import com.airbnb.epoxy.processor.resourcescanning.ResourceScanner
import com.squareup.javapoet.ClassName
import kotlin.math.min

class DataBindingModuleLookup(
    private val environment: XProcessingEnv,
    private val logger: Logger,
    private val resourceProcessor: ResourceScanner
) {
    fun getModuleName(element: XTypeElement): String {
        val packageName = element.packageName

        // First we try to get the module name by looking at what R classes were found when processing
        // layout annotations. This may find nothing if no layouts were given as annotation params
        var moduleName: String? = getModuleNameViaResources(packageName)
        if (moduleName == null) {
            // If the first approach fails, we try to guess at the R class for the module and look up
            // the class to see if it exists. This can fail if this model's package name does not
            // include the module name as a prefix (convention makes this unlikely.)
            moduleName = getModuleNameViaGuessing(packageName)
        }

        if (moduleName == null) {
            logger.logError("Could not find module name for DataBinding BR class.")
            // Fallback to using the package name so we can at least try to generate and compile something
            moduleName = packageName
        }

        return moduleName
    }

    /**
     * Attempts to get the module name of the given package. We can do this because the package name
     * of an R class is the module. Generally only one R class is used and we can just use that module
     * name, but it is possible to have multiple R classes. In that case we compare the package names
     * to find what is the most similar.
     *
     *
     * We need to get the module name to know the path of the BR class for data binding.
     */
    private fun getModuleNameViaResources(packageName: String): String {
        val rClasses = resourceProcessor.rClassNames
        if (rClasses.isEmpty()) {
            return packageName
        }
        if (rClasses.size == 1) {
            // Common case
            return rClasses[0].packageName()
        }

        // Generally the only R class used should be the app's. It is possible to use other R classes
        // though, like Android's. In that case we figure out the most likely match by comparing the
        // package name.
        //  For example we might have "com.airbnb.epoxy.R" and "android.R"
        val packageNames = packageName.split("\\.").toTypedArray()
        var bestMatch: ClassName? = null
        val bestNumMatches = -1
        for (rClass in rClasses) {
            val rModuleNames = rClass.packageName().split("\\.").toTypedArray()
            var numNameMatches = 0
            for (i in 0 until min(packageNames.size, rModuleNames.size)) {
                if (packageNames[i] == rModuleNames[i]) {
                    numNameMatches++
                } else {
                    break
                }
            }
            if (numNameMatches > bestNumMatches) {
                bestMatch = rClass
            }
        }
        return bestMatch!!.packageName()
    }

    /**
     * Attempts to get the android module that is currently being processed.. We can do this because
     * the package name of an R class is the module name. So, we take any element in the module,
     *
     *
     * We need to get the module name to know the path of the BR class for data binding.
     */
    private fun getModuleNameViaGuessing(packageName: String): String? {
        val packageNameParts = packageName.split("\\.").toTypedArray()
        var moduleName = ""
        for (i in packageNameParts.indices) {
            moduleName += packageNameParts[i]
            val rClass = environment.findType("$moduleName.R")
            moduleName += if (rClass != null) {
                return moduleName
            } else {
                "."
            }
        }
        return null
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy