Resolve Android widget class fq names properly (KT-8790)

This commit is contained in:
Yan Zhulanow
2015-08-20 15:11:01 +03:00
parent 8577f3e91b
commit e0bd532b7a
11 changed files with 171 additions and 123 deletions
@@ -21,6 +21,10 @@ import com.intellij.openapi.components.ServiceManager
import com.intellij.openapi.project.Project
import org.jetbrains.kotlin.analyzer.ModuleInfo
import org.jetbrains.kotlin.android.synthetic.codegen.AndroidExpressionCodegenExtension
import org.jetbrains.kotlin.android.synthetic.res.AndroidLayoutXmlFileManager
import org.jetbrains.kotlin.android.synthetic.res.CliAndroidLayoutXmlFileManager
import org.jetbrains.kotlin.android.synthetic.res.CliSyntheticFileGenerator
import org.jetbrains.kotlin.android.synthetic.res.SyntheticFileGenerator
import org.jetbrains.kotlin.codegen.extensions.ExpressionCodegenExtension
import org.jetbrains.kotlin.compiler.plugin.CliOption
import org.jetbrains.kotlin.compiler.plugin.CliOptionProcessingException
@@ -29,10 +33,6 @@ import org.jetbrains.kotlin.compiler.plugin.ComponentRegistrar
import org.jetbrains.kotlin.config.CompilerConfiguration
import org.jetbrains.kotlin.config.CompilerConfigurationKey
import org.jetbrains.kotlin.extensions.ExternalDeclarationsProvider
import org.jetbrains.kotlin.android.synthetic.res.AndroidLayoutXmlFileManager
import org.jetbrains.kotlin.android.synthetic.res.SyntheticFileGenerator
import org.jetbrains.kotlin.android.synthetic.res.CliAndroidLayoutXmlFileManager
import org.jetbrains.kotlin.android.synthetic.res.CliSyntheticFileGenerator
import org.jetbrains.kotlin.psi.JetFile
public object AndroidConfigurationKeys {
@@ -70,8 +70,8 @@ public class AndroidCommandLineProcessor : CommandLineProcessor {
public class CliAndroidDeclarationsProvider(private val project: Project) : ExternalDeclarationsProvider {
override fun getExternalDeclarations(moduleInfo: ModuleInfo?): Collection<JetFile> {
val parser = ServiceManager.getService(project, javaClass<SyntheticFileGenerator>())
return parser.getSyntheticFiles() ?: listOf()
val parser = ServiceManager.getService(project, javaClass<SyntheticFileGenerator>()) as? CliSyntheticFileGenerator
return parser?.getSyntheticFiles() ?: listOf()
}
}
@@ -55,6 +55,8 @@ public object AndroidConst {
val ESCAPED_IDENTIFIERS = (JetTokens.KEYWORDS.getTypes() + JetTokens.SOFT_KEYWORDS.getTypes())
.map { it as? JetKeywordToken }.filterNotNull().map { it.getValue() }.toSet()
val FQNAME_RESOLVE_PACKAGES = listOf("android.widget", "android.webkit", "android.view")
}
public fun nameToIdDeclaration(name: String): String = AndroidConst.ID_DECLARATION_PREFIX + name
@@ -74,10 +76,13 @@ fun escapeAndroidIdentifier(id: String): String {
return if (id in AndroidConst.ESCAPED_IDENTIFIERS) "`$id`" else id
}
public fun parseAndroidResource(id: String, type: String): AndroidResource {
return when (type) {
public fun parseAndroidResource(id: String, tag: String, fqNameResolver: (String) -> String?): AndroidResource {
return when (tag) {
"fragment" -> AndroidFragment(id)
"include" -> AndroidWidget(id, "View")
else -> AndroidWidget(id, type)
"include" -> AndroidWidget(id, AndroidConst.VIEW_FQNAME)
else -> {
val fqName = fqNameResolver(tag) ?: AndroidConst.VIEW_FQNAME
AndroidWidget(id, fqName)
}
}
}
@@ -16,6 +16,7 @@
package org.jetbrains.kotlin.android.synthetic.codegen
import org.jetbrains.kotlin.android.synthetic.AndroidConst
import org.jetbrains.kotlin.codegen.ClassBuilder
import org.jetbrains.kotlin.codegen.FunctionCodegen
import org.jetbrains.kotlin.codegen.StackValue
@@ -23,7 +24,6 @@ import org.jetbrains.kotlin.codegen.extensions.ExpressionCodegenExtension
import org.jetbrains.kotlin.codegen.state.GenerationState
import org.jetbrains.kotlin.codegen.state.JetTypeMapper
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.android.synthetic.AndroidConst
import org.jetbrains.kotlin.load.java.lazy.descriptors.LazyJavaClassDescriptor
import org.jetbrains.kotlin.psi.JetClassOrObject
import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils
@@ -16,23 +16,31 @@
package org.jetbrains.kotlin.android.synthetic.res
import com.intellij.openapi.diagnostic.Logger
import com.intellij.openapi.project.Project
import com.intellij.openapi.util.ModificationTracker
import com.intellij.psi.JavaPsiFacade
import com.intellij.psi.PsiFile
import com.intellij.psi.util.CachedValue
import com.intellij.psi.util.CachedValueProvider.Result
import java.io.ByteArrayInputStream
import org.jetbrains.kotlin.android.synthetic.AndroidXmlHandler
import org.jetbrains.kotlin.android.synthetic.parseAndroidResource
import com.intellij.psi.search.GlobalSearchScope
import org.jetbrains.kotlin.psi.JetFile
import kotlin.properties.Delegates
public class CliSyntheticFileGenerator(
public open class CliSyntheticFileGenerator(
project: Project,
private val manifestPath: String,
private val resDirectories: List<String>,
private val supportV4: Boolean
) : SyntheticFileGenerator(project) {
private val javaPsiFacade: JavaPsiFacade by lazy { JavaPsiFacade.getInstance(project) }
private val projectScope: GlobalSearchScope by lazy { GlobalSearchScope.allScope(project) }
private val cachedJetFiles by lazy {
generateSyntheticJetFiles(generateSyntheticFiles(true, projectScope))
}
override fun supportV4(): Boolean {
return supportV4
}
@@ -41,27 +49,31 @@ public class CliSyntheticFileGenerator(
CliAndroidLayoutXmlFileManager(project, manifestPath, resDirectories)
}
override val cachedSources: CachedValue<List<AndroidSyntheticFile>> by lazy {
cachedValue {
Result.create(generateSyntheticFiles(), ModificationTracker.NEVER_CHANGED)
}
}
public override fun getSyntheticFiles(): List<JetFile> = cachedJetFiles
override fun extractLayoutResources(files: List<PsiFile>): List<AndroidResource> {
override fun extractLayoutResources(files: List<PsiFile>, scope: GlobalSearchScope): List<AndroidResource> {
val resources = arrayListOf<AndroidResource>()
val handler = AndroidXmlHandler { id, widgetType -> resources.add(parseAndroidResource(id, widgetType)) }
try {
for (file in files) {
val handler = AndroidXmlHandler { id, tag ->
resources += parseAndroidResource(id, tag) { tag ->
resolveFqClassNameForView(javaPsiFacade, scope, tag)
}
}
for (file in files) {
try {
val inputStream = ByteArrayInputStream(file.getVirtualFile().contentsToByteArray())
layoutXmlFileManager.saxParser.parse(inputStream, handler)
} catch (e: Throwable) {
LOG.error(e)
}
return filterDuplicates(resources)
}
catch (e: Throwable) {
LOG.error(e)
return listOf()
}
return filterDuplicates(resources)
}
private companion object {
private val LOG: Logger = Logger.getInstance(javaClass)
}
}
@@ -16,10 +16,11 @@
package org.jetbrains.kotlin.android.synthetic.res
import com.intellij.openapi.diagnostic.Logger
import com.intellij.openapi.project.Project
import com.intellij.psi.JavaPsiFacade
import com.intellij.psi.PsiFile
import com.intellij.psi.PsiManager
import com.intellij.psi.search.GlobalSearchScope
import com.intellij.psi.util.CachedValue
import com.intellij.psi.util.CachedValueProvider.Result
import com.intellij.psi.util.CachedValuesManager
@@ -36,56 +37,20 @@ public abstract class SyntheticFileGenerator(protected val project: Project) {
public class NoAndroidManifestFound : Exception("No android manifest file found in project root")
protected val LOG: Logger = Logger.getInstance(javaClass)
public abstract val layoutXmlFileManager: AndroidLayoutXmlFileManager
protected abstract val cachedSources: CachedValue<List<AndroidSyntheticFile>>
private val cachedJetFiles: CachedValue<List<JetFile>> by lazy {
cachedValue {
val psiManager = PsiManager.getInstance(project)
val applicationPackage = layoutXmlFileManager.androidModuleInfo?.applicationPackage
val jetFiles = cachedSources.value.mapIndexed { index, syntheticFile ->
val fileName = AndroidConst.SYNTHETIC_FILENAME_PREFIX + syntheticFile.name + ".kt"
val virtualFile = LightVirtualFile(fileName, syntheticFile.contents)
val jetFile = psiManager.findFile(virtualFile) as JetFile
if (applicationPackage != null) {
jetFile.putUserData(AndroidConst.ANDROID_USER_PACKAGE, applicationPackage)
}
jetFile
}
Result.create(jetFiles, cachedSources)
}
}
protected abstract fun supportV4(): Boolean
public fun generateSyntheticFiles(generateCommonFiles: Boolean = true): List<AndroidSyntheticFile> {
val commonFiles = if (generateCommonFiles) {
val renderSyntheticFile = renderSyntheticFile("clearCache") {
writePackage(AndroidConst.SYNTHETIC_PACKAGE)
writeAndroidImports()
writeClearCacheFunction(AndroidConst.ACTIVITY_FQNAME)
writeClearCacheFunction(AndroidConst.FRAGMENT_FQNAME)
if (supportV4()) writeClearCacheFunction(AndroidConst.SUPPORT_FRAGMENT_FQNAME)
}
val clearCacheFile = renderSyntheticFile
public abstract fun getSyntheticFiles(): List<JetFile>
listOf(clearCacheFile,
FLEXIBLE_TYPE_FILE,
FAKE_SUPPORT_V4_APP_FILE,
FAKE_SUPPORT_V4_VIEW_FILE,
FAKE_SUPPORT_V4_WIDGET_FILE)
} else listOf()
protected fun generateSyntheticFiles(generateCommonFiles: Boolean = true, scope: GlobalSearchScope): List<AndroidSyntheticFile> {
val commonFiles = if (generateCommonFiles) generateCommonFiles() else listOf()
return layoutXmlFileManager.getLayoutXmlFiles().flatMap { entry ->
val files = entry.getValue()
val resources = extractLayoutResources(files)
val resources = extractLayoutResources(files, scope)
val layoutName = files[0].name.substringBefore('.')
val layoutName = entry.getKey()
val mainLayoutFile = renderMainLayoutFile(layoutName, resources)
val viewLayoutFile = renderViewLayoutFile(layoutName, resources)
@@ -94,9 +59,25 @@ public abstract class SyntheticFileGenerator(protected val project: Project) {
}.filterNotNull() + commonFiles
}
public fun getSyntheticFiles(): List<JetFile>? = cachedJetFiles.value
private fun generateCommonFiles(): List<AndroidSyntheticFile> {
val renderSyntheticFile = renderSyntheticFile("clearCache") {
writePackage(AndroidConst.SYNTHETIC_PACKAGE)
writeAndroidImports()
writeClearCacheFunction(AndroidConst.ACTIVITY_FQNAME)
writeClearCacheFunction(AndroidConst.FRAGMENT_FQNAME)
if (supportV4()) writeClearCacheFunction(AndroidConst.SUPPORT_FRAGMENT_FQNAME)
}
val clearCacheFile = renderSyntheticFile
protected abstract fun extractLayoutResources(files: List<PsiFile>): List<AndroidResource>
return listOf(
clearCacheFile,
FLEXIBLE_TYPE_FILE,
FAKE_SUPPORT_V4_APP_FILE,
FAKE_SUPPORT_V4_VIEW_FILE,
FAKE_SUPPORT_V4_WIDGET_FILE)
}
protected abstract fun extractLayoutResources(files: List<PsiFile>, scope: GlobalSearchScope): List<AndroidResource>
private fun renderMainLayoutFile(layoutName: String, resources: List<AndroidResource>): AndroidSyntheticFile {
return renderLayoutFile(layoutName + AndroidConst.LAYOUT_POSTFIX,
@@ -140,13 +121,13 @@ public abstract class SyntheticFileGenerator(protected val project: Project) {
writeEmptyLine()
}
private fun KotlinStringWriter.writeSyntheticProperty(receiver: String, widget: AndroidResource, stubCall: String) {
private fun KotlinStringWriter.writeSyntheticProperty(receiver: String, resource: AndroidResource, stubCall: String) {
// extract startsWith() to fun
val className = if (isFromSupportV4Package(receiver)) widget.supportClassName else widget.className
val cast = if (widget.className != "View") " as? $className" else ""
val className = if (isFromSupportV4Package(receiver)) resource.supportClassName else resource.className
val cast = if (resource.className != "View") " as? $className" else ""
val body = arrayListOf("return $stubCall$cast")
writeImmutableExtensionProperty(receiver,
name = widget.id,
name = resource.id,
retType = "$EXPLICIT_FLEXIBLE_CLASS_NAME<$className, $className?>",
getterBody = body)
}
@@ -163,6 +144,22 @@ public abstract class SyntheticFileGenerator(protected val project: Project) {
return fqName.startsWith(AndroidConst.SUPPORT_V4_PACKAGE)
}
protected fun resolveFqClassNameForView(javaPsiFacade: JavaPsiFacade, scope: GlobalSearchScope, tag: String): String? {
if (tag.contains('.')) {
if (javaPsiFacade.findClass(tag, scope) == null) {
return null
}
return tag
}
for (pkg in AndroidConst.FQNAME_RESOLVE_PACKAGES) {
val fqName = "$pkg.$tag"
if (javaPsiFacade.findClass(fqName, scope) != null) {
return fqName
}
}
return null
}
protected fun filterDuplicates(resources: List<AndroidResource>): List<AndroidResource> {
val resourceMap = linkedMapOf<String, AndroidResource>()
val resourcesToExclude = hashSetOf<String>()
@@ -184,6 +181,21 @@ public abstract class SyntheticFileGenerator(protected val project: Project) {
return resourceMap.values().toList()
}
protected fun generateSyntheticJetFiles(files: List<AndroidSyntheticFile>): List<JetFile> {
val psiManager = PsiManager.getInstance(project)
val applicationPackage = layoutXmlFileManager.androidModuleInfo?.applicationPackage
return files.mapIndexed { index, syntheticFile ->
val fileName = AndroidConst.SYNTHETIC_FILENAME_PREFIX + syntheticFile.name + ".kt"
val virtualFile = LightVirtualFile(fileName, syntheticFile.contents)
val jetFile = psiManager.findFile(virtualFile) as JetFile
if (applicationPackage != null) {
jetFile.putUserData(AndroidConst.ANDROID_USER_PACKAGE, applicationPackage)
}
jetFile
}
}
companion object {
private val EXPLICIT_FLEXIBLE_PACKAGE = Flexibility.FLEXIBLE_TYPE_CLASSIFIER.packageFqName.asString()
private val EXPLICIT_FLEXIBLE_CLASS_NAME = Flexibility.FLEXIBLE_TYPE_CLASSIFIER.relativeClassName.asString()
@@ -16,17 +16,18 @@
package org.jetbrains.kotlin.lang.resolve.android.test
import com.intellij.openapi.module.ModuleManager
import com.intellij.testFramework.UsefulTestCase
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment
import java.io.File
import org.jetbrains.kotlin.test.JetTestUtils
import org.jetbrains.kotlin.test.ConfigurationKind
import org.jetbrains.kotlin.test.TestJdkKind
import org.jetbrains.kotlin.cli.jvm.compiler.EnvironmentConfigFiles
import org.jetbrains.kotlin.android.synthetic.AndroidConst
import org.jetbrains.kotlin.android.synthetic.res.CliSyntheticFileGenerator
import org.jetbrains.kotlin.android.synthetic.res.SyntheticFileGenerator
import kotlin.test.*
import org.jetbrains.kotlin.cli.jvm.compiler.EnvironmentConfigFiles
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment
import org.jetbrains.kotlin.test.ConfigurationKind
import org.jetbrains.kotlin.test.JetTestUtils
import org.jetbrains.kotlin.test.TestJdkKind
import java.io.File
import kotlin.test.assertEquals
import kotlin.test.assertNotNull
import kotlin.test.fail
public abstract class AbstractAndroidXml2KConversionTest : UsefulTestCase() {
@@ -35,10 +36,14 @@ public abstract class AbstractAndroidXml2KConversionTest : UsefulTestCase() {
val jetCoreEnvironment = getEnvironment()
val layoutPaths = getResPaths(path)
val parser = CliSyntheticFileGenerator(jetCoreEnvironment.project, path + "AndroidManifest.xml", layoutPaths)
parser.supportV4 = testDirectory.name.startsWith("support")
val supportV4 = testDirectory.name.startsWith("support")
val parser = CliSyntheticFileGeneratorForConversionTest(
jetCoreEnvironment.project, path + "AndroidManifest.xml", layoutPaths, supportV4)
val actual = parser.generateSyntheticFiles(false).toMap { it.name }
val module = ModuleManager.getInstance(jetCoreEnvironment.project).modules.first()
val moduleScope = module.getModuleWithDependenciesAndLibrariesScope(false)
val actual = parser.gen(moduleScope).toMap { it.name }
val expectedLayoutFiles = testDirectory.listFiles {
it.isFile() && it.name.endsWith(".kt")
@@ -16,20 +16,19 @@
package org.jetbrains.kotlin.lang.resolve.android.test
import com.intellij.openapi.extensions.Extensions
import com.intellij.openapi.project.Project
import com.intellij.psi.impl.PsiTreeChangePreprocessor
import org.jetbrains.kotlin.extensions.ExternalDeclarationsProvider
import org.jetbrains.kotlin.android.synthetic.AndroidConfigurationKeys
import org.jetbrains.kotlin.codegen.extensions.ExpressionCodegenExtension
import org.jetbrains.kotlin.android.synthetic.codegen.AndroidExpressionCodegenExtension
import com.intellij.psi.search.GlobalSearchScope
import com.intellij.testFramework.UsefulTestCase
import org.jetbrains.kotlin.analyzer.ModuleInfo
import org.jetbrains.kotlin.psi.JetFile
import org.jetbrains.kotlin.config.CompilerConfiguration
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment
import org.jetbrains.kotlin.cli.jvm.compiler.EnvironmentConfigFiles
import org.jetbrains.kotlin.android.synthetic.AndroidConfigurationKeys
import org.jetbrains.kotlin.android.synthetic.codegen.AndroidExpressionCodegenExtension
import org.jetbrains.kotlin.android.synthetic.res.CliSyntheticFileGenerator
import org.jetbrains.kotlin.cli.jvm.compiler.EnvironmentConfigFiles
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment
import org.jetbrains.kotlin.codegen.extensions.ExpressionCodegenExtension
import org.jetbrains.kotlin.config.CompilerConfiguration
import org.jetbrains.kotlin.extensions.ExternalDeclarationsProvider
import org.jetbrains.kotlin.psi.JetFile
import java.io.File
private class AndroidTestExternalDeclarationsProvider(
@@ -39,12 +38,20 @@ private class AndroidTestExternalDeclarationsProvider(
val supportV4: Boolean
) : ExternalDeclarationsProvider {
override fun getExternalDeclarations(moduleInfo: ModuleInfo?): Collection<JetFile> {
val parser = CliSyntheticFileGenerator(project, manifestPath, resPaths)
parser.supportV4 = supportV4
return parser.getSyntheticFiles() ?: listOf()
val parser = CliSyntheticFileGeneratorForConversionTest(project, manifestPath, resPaths, supportV4)
return parser.getSyntheticFiles()
}
}
class CliSyntheticFileGeneratorForConversionTest(
project: Project,
manifestPath: String,
resDirectories: List<String>,
supportV4: Boolean
) : CliSyntheticFileGenerator(project, manifestPath, resDirectories, supportV4) {
fun gen(scope: GlobalSearchScope) = generateSyntheticFiles(false, scope)
}
fun UsefulTestCase.createAndroidTestEnvironment(
configuration: CompilerConfiguration,
resPaths: List<String>,
@@ -19,6 +19,7 @@ package org.jetbrains.kotlin.android.synthetic.idea
import com.intellij.openapi.module.ModuleServiceManager
import com.intellij.openapi.project.Project
import org.jetbrains.kotlin.analyzer.ModuleInfo
import org.jetbrains.kotlin.android.synthetic.idea.res.IDESyntheticFileGenerator
import org.jetbrains.kotlin.android.synthetic.res.SyntheticFileGenerator
import org.jetbrains.kotlin.extensions.ExternalDeclarationsProvider
import org.jetbrains.kotlin.idea.caches.resolve.ModuleSourceInfo
@@ -30,8 +31,8 @@ public class IDEAndroidExternalDeclarationsProvider(private val project: Project
if (moduleInfo !is ModuleSourceInfo) return listOf()
val module = moduleInfo.module
val parser = ModuleServiceManager.getService(module, javaClass<SyntheticFileGenerator>())!!
val syntheticFiles = parser.getSyntheticFiles()
val parser = ModuleServiceManager.getService(module, javaClass<SyntheticFileGenerator>()) as? IDESyntheticFileGenerator
val syntheticFiles = parser?.getSyntheticFiles()
syntheticFiles?.forEach { it.moduleInfo = moduleInfo }
return syntheticFiles ?: listOf()
@@ -21,11 +21,11 @@ import com.intellij.psi.PsiElement
import org.jetbrains.android.facet.AndroidFacet
import org.jetbrains.kotlin.android.synthetic.AndroidConst
import org.jetbrains.kotlin.android.synthetic.idea.AndroidXmlVisitor
import org.jetbrains.kotlin.android.synthetic.res.AndroidModuleInfo
import org.jetbrains.kotlin.android.synthetic.res.AndroidLayoutXmlFileManager
import org.jetbrains.kotlin.android.synthetic.res.AndroidModuleInfo
import org.jetbrains.kotlin.psi.JetProperty
public class IDEAndroidLayoutXmlFileManager(val module: Module) : AndroidLayoutXmlFileManager(module.getProject()) {
public class IDEAndroidLayoutXmlFileManager(val module: Module) : AndroidLayoutXmlFileManager(module.project) {
override val androidModuleInfo: AndroidModuleInfo? by lazy { module.androidFacet?.toAndroidModuleInfo() }
@@ -37,7 +37,7 @@ public class IDEAndroidLayoutXmlFileManager(val module: Module) : AndroidLayoutX
val layoutFiles = getLayoutXmlFiles()[layoutPackageName]
if (layoutFiles == null || layoutFiles.isEmpty()) return listOf()
val propertyName = property.getName()
val propertyName = property.name
val attributes = arrayListOf<PsiElement>()
val visitor = AndroidXmlVisitor { retId, wClass, valueElement ->
@@ -53,7 +53,7 @@ public class IDEAndroidLayoutXmlFileManager(val module: Module) : AndroidLayoutX
private fun AndroidFacet.toAndroidModuleInfo(): AndroidModuleInfo? {
val applicationPackage = manifest?.getPackage()?.toString()
val mainResDirectories = getAllResourceDirectories().map { it.getPath() }
val mainResDirectories = allResourceDirectories.map { it.path }
return if (applicationPackage != null) {
AndroidModuleInfo(applicationPackage, mainResDirectories)
@@ -20,6 +20,7 @@ import com.intellij.openapi.module.Module
import com.intellij.psi.JavaPsiFacade
import com.intellij.psi.PsiFile
import com.intellij.psi.impl.PsiTreeChangePreprocessor
import com.intellij.psi.search.GlobalSearchScope
import com.intellij.psi.util.CachedValue
import com.intellij.psi.util.CachedValueProvider.Result
import org.jetbrains.kotlin.android.synthetic.AndroidConst
@@ -27,17 +28,24 @@ import org.jetbrains.kotlin.android.synthetic.idea.AndroidPsiTreeChangePreproces
import org.jetbrains.kotlin.android.synthetic.idea.AndroidXmlVisitor
import org.jetbrains.kotlin.android.synthetic.parseAndroidResource
import org.jetbrains.kotlin.android.synthetic.res.AndroidResource
import org.jetbrains.kotlin.android.synthetic.res.AndroidSyntheticFile
import org.jetbrains.kotlin.android.synthetic.res.SyntheticFileGenerator
import org.jetbrains.kotlin.psi.JetFile
class IDESyntheticFileGenerator(val module: Module) : SyntheticFileGenerator(module.project) {
private val supportV4: Boolean
private val javaPsiFacade: JavaPsiFacade by lazy { JavaPsiFacade.getInstance(module.project) }
private val moduleScope: GlobalSearchScope by lazy { module.getModuleWithDependenciesAndLibrariesScope(false) }
private val cachedJetFiles: CachedValue<List<JetFile>> by lazy {
cachedValue {
Result.create(generateSyntheticJetFiles(generateSyntheticFiles(true, moduleScope)), psiTreeChangePreprocessor)
}
}
init {
val scope = module.getModuleWithDependenciesAndLibrariesScope(false)
supportV4 = JavaPsiFacade.getInstance(module.getProject())
.findClasses(AndroidConst.SUPPORT_FRAGMENT_FQNAME, scope).isNotEmpty()
supportV4 = javaPsiFacade.findClasses(AndroidConst.SUPPORT_FRAGMENT_FQNAME, moduleScope).isNotEmpty()
}
override fun supportV4() = supportV4
@@ -48,16 +56,14 @@ class IDESyntheticFileGenerator(val module: Module) : SyntheticFileGenerator(mod
module.project.getExtensions(PsiTreeChangePreprocessor.EP_NAME).first { it is AndroidPsiTreeChangePreprocessor }
}
override val cachedSources: CachedValue<List<AndroidSyntheticFile>> by lazy {
cachedValue {
Result.create(generateSyntheticFiles(), psiTreeChangePreprocessor)
}
}
public override fun getSyntheticFiles(): List<JetFile> = cachedJetFiles.value
override fun extractLayoutResources(files: List<PsiFile>): List<AndroidResource> {
override fun extractLayoutResources(files: List<PsiFile>, scope: GlobalSearchScope): List<AndroidResource> {
val widgets = arrayListOf<AndroidResource>()
val visitor = AndroidXmlVisitor { id, widgetType, attribute ->
widgets.add(parseAndroidResource(id, widgetType))
widgets += parseAndroidResource(id, widgetType) {
resolveFqClassNameForView(javaPsiFacade, scope, it)
}
}
files.forEach { it.accept(visitor) }
@@ -16,10 +16,10 @@
package org.jetbrains.kotlin.android
import org.jetbrains.kotlin.android.synthetic.res.CliSyntheticFileGenerator
import com.intellij.openapi.module.ModuleManager
import org.jetbrains.kotlin.android.synthetic.idea.TestConst
import org.jetbrains.kotlin.android.synthetic.idea.res.IDESyntheticFileGenerator
import org.jetbrains.kotlin.android.synthetic.res.CliSyntheticFileGenerator
import kotlin.test.assertEquals
public abstract class AbstractParserResultEqualityTest : KotlinAndroidTestCase() {
@@ -31,11 +31,11 @@ public abstract class AbstractParserResultEqualityTest : KotlinAndroidTestCase()
"$path${it.name}/"
}
val cliParser = CliSyntheticFileGenerator(project, path + "../AndroidManifest.xml", resDirs, false)
val cliParser = CliSyntheticFileGenerator(project, "$path../AndroidManifest.xml", resDirs, false)
val ideParser = IDESyntheticFileGenerator(ModuleManager.getInstance(project).getModules()[0])
val cliResult = cliParser.getSyntheticFiles()!!.joinToString("\n\n")
val ideResult = ideParser.getSyntheticFiles()!!.joinToString("\n\n")
val cliResult = cliParser.getSyntheticFiles().joinToString("\n\n")
val ideResult = ideParser.getSyntheticFiles().joinToString("\n\n")
assertEquals(cliResult, ideResult)
}