Resolve Android widget class fq names properly (KT-8790)
This commit is contained in:
+6
-6
@@ -21,6 +21,10 @@ import com.intellij.openapi.components.ServiceManager
|
|||||||
import com.intellij.openapi.project.Project
|
import com.intellij.openapi.project.Project
|
||||||
import org.jetbrains.kotlin.analyzer.ModuleInfo
|
import org.jetbrains.kotlin.analyzer.ModuleInfo
|
||||||
import org.jetbrains.kotlin.android.synthetic.codegen.AndroidExpressionCodegenExtension
|
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.codegen.extensions.ExpressionCodegenExtension
|
||||||
import org.jetbrains.kotlin.compiler.plugin.CliOption
|
import org.jetbrains.kotlin.compiler.plugin.CliOption
|
||||||
import org.jetbrains.kotlin.compiler.plugin.CliOptionProcessingException
|
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.CompilerConfiguration
|
||||||
import org.jetbrains.kotlin.config.CompilerConfigurationKey
|
import org.jetbrains.kotlin.config.CompilerConfigurationKey
|
||||||
import org.jetbrains.kotlin.extensions.ExternalDeclarationsProvider
|
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
|
import org.jetbrains.kotlin.psi.JetFile
|
||||||
|
|
||||||
public object AndroidConfigurationKeys {
|
public object AndroidConfigurationKeys {
|
||||||
@@ -70,8 +70,8 @@ public class AndroidCommandLineProcessor : CommandLineProcessor {
|
|||||||
|
|
||||||
public class CliAndroidDeclarationsProvider(private val project: Project) : ExternalDeclarationsProvider {
|
public class CliAndroidDeclarationsProvider(private val project: Project) : ExternalDeclarationsProvider {
|
||||||
override fun getExternalDeclarations(moduleInfo: ModuleInfo?): Collection<JetFile> {
|
override fun getExternalDeclarations(moduleInfo: ModuleInfo?): Collection<JetFile> {
|
||||||
val parser = ServiceManager.getService(project, javaClass<SyntheticFileGenerator>())
|
val parser = ServiceManager.getService(project, javaClass<SyntheticFileGenerator>()) as? CliSyntheticFileGenerator
|
||||||
return parser.getSyntheticFiles() ?: listOf()
|
return parser?.getSyntheticFiles() ?: listOf()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+9
-4
@@ -55,6 +55,8 @@ public object AndroidConst {
|
|||||||
|
|
||||||
val ESCAPED_IDENTIFIERS = (JetTokens.KEYWORDS.getTypes() + JetTokens.SOFT_KEYWORDS.getTypes())
|
val ESCAPED_IDENTIFIERS = (JetTokens.KEYWORDS.getTypes() + JetTokens.SOFT_KEYWORDS.getTypes())
|
||||||
.map { it as? JetKeywordToken }.filterNotNull().map { it.getValue() }.toSet()
|
.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
|
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
|
return if (id in AndroidConst.ESCAPED_IDENTIFIERS) "`$id`" else id
|
||||||
}
|
}
|
||||||
|
|
||||||
public fun parseAndroidResource(id: String, type: String): AndroidResource {
|
public fun parseAndroidResource(id: String, tag: String, fqNameResolver: (String) -> String?): AndroidResource {
|
||||||
return when (type) {
|
return when (tag) {
|
||||||
"fragment" -> AndroidFragment(id)
|
"fragment" -> AndroidFragment(id)
|
||||||
"include" -> AndroidWidget(id, "View")
|
"include" -> AndroidWidget(id, AndroidConst.VIEW_FQNAME)
|
||||||
else -> AndroidWidget(id, type)
|
else -> {
|
||||||
|
val fqName = fqNameResolver(tag) ?: AndroidConst.VIEW_FQNAME
|
||||||
|
AndroidWidget(id, fqName)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+1
-1
@@ -16,6 +16,7 @@
|
|||||||
|
|
||||||
package org.jetbrains.kotlin.android.synthetic.codegen
|
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.ClassBuilder
|
||||||
import org.jetbrains.kotlin.codegen.FunctionCodegen
|
import org.jetbrains.kotlin.codegen.FunctionCodegen
|
||||||
import org.jetbrains.kotlin.codegen.StackValue
|
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.GenerationState
|
||||||
import org.jetbrains.kotlin.codegen.state.JetTypeMapper
|
import org.jetbrains.kotlin.codegen.state.JetTypeMapper
|
||||||
import org.jetbrains.kotlin.descriptors.*
|
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.load.java.lazy.descriptors.LazyJavaClassDescriptor
|
||||||
import org.jetbrains.kotlin.psi.JetClassOrObject
|
import org.jetbrains.kotlin.psi.JetClassOrObject
|
||||||
import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils
|
import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils
|
||||||
|
|||||||
+30
-18
@@ -16,23 +16,31 @@
|
|||||||
|
|
||||||
package org.jetbrains.kotlin.android.synthetic.res
|
package org.jetbrains.kotlin.android.synthetic.res
|
||||||
|
|
||||||
|
import com.intellij.openapi.diagnostic.Logger
|
||||||
import com.intellij.openapi.project.Project
|
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.PsiFile
|
||||||
import com.intellij.psi.util.CachedValue
|
|
||||||
import com.intellij.psi.util.CachedValueProvider.Result
|
|
||||||
import java.io.ByteArrayInputStream
|
import java.io.ByteArrayInputStream
|
||||||
import org.jetbrains.kotlin.android.synthetic.AndroidXmlHandler
|
import org.jetbrains.kotlin.android.synthetic.AndroidXmlHandler
|
||||||
import org.jetbrains.kotlin.android.synthetic.parseAndroidResource
|
import org.jetbrains.kotlin.android.synthetic.parseAndroidResource
|
||||||
|
import com.intellij.psi.search.GlobalSearchScope
|
||||||
|
import org.jetbrains.kotlin.psi.JetFile
|
||||||
import kotlin.properties.Delegates
|
import kotlin.properties.Delegates
|
||||||
|
|
||||||
public class CliSyntheticFileGenerator(
|
public open class CliSyntheticFileGenerator(
|
||||||
project: Project,
|
project: Project,
|
||||||
private val manifestPath: String,
|
private val manifestPath: String,
|
||||||
private val resDirectories: List<String>,
|
private val resDirectories: List<String>,
|
||||||
private val supportV4: Boolean
|
private val supportV4: Boolean
|
||||||
) : SyntheticFileGenerator(project) {
|
) : 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 {
|
override fun supportV4(): Boolean {
|
||||||
return supportV4
|
return supportV4
|
||||||
}
|
}
|
||||||
@@ -41,27 +49,31 @@ public class CliSyntheticFileGenerator(
|
|||||||
CliAndroidLayoutXmlFileManager(project, manifestPath, resDirectories)
|
CliAndroidLayoutXmlFileManager(project, manifestPath, resDirectories)
|
||||||
}
|
}
|
||||||
|
|
||||||
override val cachedSources: CachedValue<List<AndroidSyntheticFile>> by lazy {
|
public override fun getSyntheticFiles(): List<JetFile> = cachedJetFiles
|
||||||
cachedValue {
|
|
||||||
Result.create(generateSyntheticFiles(), ModificationTracker.NEVER_CHANGED)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun extractLayoutResources(files: List<PsiFile>): List<AndroidResource> {
|
override fun extractLayoutResources(files: List<PsiFile>, scope: GlobalSearchScope): List<AndroidResource> {
|
||||||
val resources = arrayListOf<AndroidResource>()
|
val resources = arrayListOf<AndroidResource>()
|
||||||
val handler = AndroidXmlHandler { id, widgetType -> resources.add(parseAndroidResource(id, widgetType)) }
|
|
||||||
|
|
||||||
try {
|
val handler = AndroidXmlHandler { id, tag ->
|
||||||
for (file in files) {
|
resources += parseAndroidResource(id, tag) { tag ->
|
||||||
|
resolveFqClassNameForView(javaPsiFacade, scope, tag)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (file in files) {
|
||||||
|
try {
|
||||||
val inputStream = ByteArrayInputStream(file.getVirtualFile().contentsToByteArray())
|
val inputStream = ByteArrayInputStream(file.getVirtualFile().contentsToByteArray())
|
||||||
layoutXmlFileManager.saxParser.parse(inputStream, handler)
|
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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+60
-48
@@ -16,10 +16,11 @@
|
|||||||
|
|
||||||
package org.jetbrains.kotlin.android.synthetic.res
|
package org.jetbrains.kotlin.android.synthetic.res
|
||||||
|
|
||||||
import com.intellij.openapi.diagnostic.Logger
|
|
||||||
import com.intellij.openapi.project.Project
|
import com.intellij.openapi.project.Project
|
||||||
|
import com.intellij.psi.JavaPsiFacade
|
||||||
import com.intellij.psi.PsiFile
|
import com.intellij.psi.PsiFile
|
||||||
import com.intellij.psi.PsiManager
|
import com.intellij.psi.PsiManager
|
||||||
|
import com.intellij.psi.search.GlobalSearchScope
|
||||||
import com.intellij.psi.util.CachedValue
|
import com.intellij.psi.util.CachedValue
|
||||||
import com.intellij.psi.util.CachedValueProvider.Result
|
import com.intellij.psi.util.CachedValueProvider.Result
|
||||||
import com.intellij.psi.util.CachedValuesManager
|
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")
|
public class NoAndroidManifestFound : Exception("No android manifest file found in project root")
|
||||||
|
|
||||||
protected val LOG: Logger = Logger.getInstance(javaClass)
|
|
||||||
|
|
||||||
public abstract val layoutXmlFileManager: AndroidLayoutXmlFileManager
|
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
|
protected abstract fun supportV4(): Boolean
|
||||||
|
|
||||||
public fun generateSyntheticFiles(generateCommonFiles: Boolean = true): List<AndroidSyntheticFile> {
|
public abstract fun getSyntheticFiles(): List<JetFile>
|
||||||
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
|
|
||||||
|
|
||||||
listOf(clearCacheFile,
|
protected fun generateSyntheticFiles(generateCommonFiles: Boolean = true, scope: GlobalSearchScope): List<AndroidSyntheticFile> {
|
||||||
FLEXIBLE_TYPE_FILE,
|
val commonFiles = if (generateCommonFiles) generateCommonFiles() else listOf()
|
||||||
FAKE_SUPPORT_V4_APP_FILE,
|
|
||||||
FAKE_SUPPORT_V4_VIEW_FILE,
|
|
||||||
FAKE_SUPPORT_V4_WIDGET_FILE)
|
|
||||||
} else listOf()
|
|
||||||
|
|
||||||
return layoutXmlFileManager.getLayoutXmlFiles().flatMap { entry ->
|
return layoutXmlFileManager.getLayoutXmlFiles().flatMap { entry ->
|
||||||
val files = entry.getValue()
|
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 mainLayoutFile = renderMainLayoutFile(layoutName, resources)
|
||||||
val viewLayoutFile = renderViewLayoutFile(layoutName, resources)
|
val viewLayoutFile = renderViewLayoutFile(layoutName, resources)
|
||||||
@@ -94,9 +59,25 @@ public abstract class SyntheticFileGenerator(protected val project: Project) {
|
|||||||
}.filterNotNull() + commonFiles
|
}.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 {
|
private fun renderMainLayoutFile(layoutName: String, resources: List<AndroidResource>): AndroidSyntheticFile {
|
||||||
return renderLayoutFile(layoutName + AndroidConst.LAYOUT_POSTFIX,
|
return renderLayoutFile(layoutName + AndroidConst.LAYOUT_POSTFIX,
|
||||||
@@ -140,13 +121,13 @@ public abstract class SyntheticFileGenerator(protected val project: Project) {
|
|||||||
writeEmptyLine()
|
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
|
// extract startsWith() to fun
|
||||||
val className = if (isFromSupportV4Package(receiver)) widget.supportClassName else widget.className
|
val className = if (isFromSupportV4Package(receiver)) resource.supportClassName else resource.className
|
||||||
val cast = if (widget.className != "View") " as? $className" else ""
|
val cast = if (resource.className != "View") " as? $className" else ""
|
||||||
val body = arrayListOf("return $stubCall$cast")
|
val body = arrayListOf("return $stubCall$cast")
|
||||||
writeImmutableExtensionProperty(receiver,
|
writeImmutableExtensionProperty(receiver,
|
||||||
name = widget.id,
|
name = resource.id,
|
||||||
retType = "$EXPLICIT_FLEXIBLE_CLASS_NAME<$className, $className?>",
|
retType = "$EXPLICIT_FLEXIBLE_CLASS_NAME<$className, $className?>",
|
||||||
getterBody = body)
|
getterBody = body)
|
||||||
}
|
}
|
||||||
@@ -163,6 +144,22 @@ public abstract class SyntheticFileGenerator(protected val project: Project) {
|
|||||||
return fqName.startsWith(AndroidConst.SUPPORT_V4_PACKAGE)
|
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> {
|
protected fun filterDuplicates(resources: List<AndroidResource>): List<AndroidResource> {
|
||||||
val resourceMap = linkedMapOf<String, AndroidResource>()
|
val resourceMap = linkedMapOf<String, AndroidResource>()
|
||||||
val resourcesToExclude = hashSetOf<String>()
|
val resourcesToExclude = hashSetOf<String>()
|
||||||
@@ -184,6 +181,21 @@ public abstract class SyntheticFileGenerator(protected val project: Project) {
|
|||||||
return resourceMap.values().toList()
|
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 {
|
companion object {
|
||||||
private val EXPLICIT_FLEXIBLE_PACKAGE = Flexibility.FLEXIBLE_TYPE_CLASSIFIER.packageFqName.asString()
|
private val EXPLICIT_FLEXIBLE_PACKAGE = Flexibility.FLEXIBLE_TYPE_CLASSIFIER.packageFqName.asString()
|
||||||
private val EXPLICIT_FLEXIBLE_CLASS_NAME = Flexibility.FLEXIBLE_TYPE_CLASSIFIER.relativeClassName.asString()
|
private val EXPLICIT_FLEXIBLE_CLASS_NAME = Flexibility.FLEXIBLE_TYPE_CLASSIFIER.relativeClassName.asString()
|
||||||
|
|||||||
+17
-12
@@ -16,17 +16,18 @@
|
|||||||
|
|
||||||
package org.jetbrains.kotlin.lang.resolve.android.test
|
package org.jetbrains.kotlin.lang.resolve.android.test
|
||||||
|
|
||||||
|
import com.intellij.openapi.module.ModuleManager
|
||||||
import com.intellij.testFramework.UsefulTestCase
|
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 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() {
|
public abstract class AbstractAndroidXml2KConversionTest : UsefulTestCase() {
|
||||||
|
|
||||||
@@ -35,10 +36,14 @@ public abstract class AbstractAndroidXml2KConversionTest : UsefulTestCase() {
|
|||||||
|
|
||||||
val jetCoreEnvironment = getEnvironment()
|
val jetCoreEnvironment = getEnvironment()
|
||||||
val layoutPaths = getResPaths(path)
|
val layoutPaths = getResPaths(path)
|
||||||
val parser = CliSyntheticFileGenerator(jetCoreEnvironment.project, path + "AndroidManifest.xml", layoutPaths)
|
val supportV4 = testDirectory.name.startsWith("support")
|
||||||
parser.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 {
|
val expectedLayoutFiles = testDirectory.listFiles {
|
||||||
it.isFile() && it.name.endsWith(".kt")
|
it.isFile() && it.name.endsWith(".kt")
|
||||||
|
|||||||
+20
-13
@@ -16,20 +16,19 @@
|
|||||||
|
|
||||||
package org.jetbrains.kotlin.lang.resolve.android.test
|
package org.jetbrains.kotlin.lang.resolve.android.test
|
||||||
|
|
||||||
import com.intellij.openapi.extensions.Extensions
|
|
||||||
import com.intellij.openapi.project.Project
|
import com.intellij.openapi.project.Project
|
||||||
import com.intellij.psi.impl.PsiTreeChangePreprocessor
|
import com.intellij.psi.search.GlobalSearchScope
|
||||||
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.testFramework.UsefulTestCase
|
import com.intellij.testFramework.UsefulTestCase
|
||||||
import org.jetbrains.kotlin.analyzer.ModuleInfo
|
import org.jetbrains.kotlin.analyzer.ModuleInfo
|
||||||
import org.jetbrains.kotlin.psi.JetFile
|
import org.jetbrains.kotlin.android.synthetic.AndroidConfigurationKeys
|
||||||
import org.jetbrains.kotlin.config.CompilerConfiguration
|
import org.jetbrains.kotlin.android.synthetic.codegen.AndroidExpressionCodegenExtension
|
||||||
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment
|
|
||||||
import org.jetbrains.kotlin.cli.jvm.compiler.EnvironmentConfigFiles
|
|
||||||
import org.jetbrains.kotlin.android.synthetic.res.CliSyntheticFileGenerator
|
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
|
import java.io.File
|
||||||
|
|
||||||
private class AndroidTestExternalDeclarationsProvider(
|
private class AndroidTestExternalDeclarationsProvider(
|
||||||
@@ -39,12 +38,20 @@ private class AndroidTestExternalDeclarationsProvider(
|
|||||||
val supportV4: Boolean
|
val supportV4: Boolean
|
||||||
) : ExternalDeclarationsProvider {
|
) : ExternalDeclarationsProvider {
|
||||||
override fun getExternalDeclarations(moduleInfo: ModuleInfo?): Collection<JetFile> {
|
override fun getExternalDeclarations(moduleInfo: ModuleInfo?): Collection<JetFile> {
|
||||||
val parser = CliSyntheticFileGenerator(project, manifestPath, resPaths)
|
val parser = CliSyntheticFileGeneratorForConversionTest(project, manifestPath, resPaths, supportV4)
|
||||||
parser.supportV4 = supportV4
|
return parser.getSyntheticFiles()
|
||||||
return parser.getSyntheticFiles() ?: listOf()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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(
|
fun UsefulTestCase.createAndroidTestEnvironment(
|
||||||
configuration: CompilerConfiguration,
|
configuration: CompilerConfiguration,
|
||||||
resPaths: List<String>,
|
resPaths: List<String>,
|
||||||
|
|||||||
+3
-2
@@ -19,6 +19,7 @@ package org.jetbrains.kotlin.android.synthetic.idea
|
|||||||
import com.intellij.openapi.module.ModuleServiceManager
|
import com.intellij.openapi.module.ModuleServiceManager
|
||||||
import com.intellij.openapi.project.Project
|
import com.intellij.openapi.project.Project
|
||||||
import org.jetbrains.kotlin.analyzer.ModuleInfo
|
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.android.synthetic.res.SyntheticFileGenerator
|
||||||
import org.jetbrains.kotlin.extensions.ExternalDeclarationsProvider
|
import org.jetbrains.kotlin.extensions.ExternalDeclarationsProvider
|
||||||
import org.jetbrains.kotlin.idea.caches.resolve.ModuleSourceInfo
|
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()
|
if (moduleInfo !is ModuleSourceInfo) return listOf()
|
||||||
|
|
||||||
val module = moduleInfo.module
|
val module = moduleInfo.module
|
||||||
val parser = ModuleServiceManager.getService(module, javaClass<SyntheticFileGenerator>())!!
|
val parser = ModuleServiceManager.getService(module, javaClass<SyntheticFileGenerator>()) as? IDESyntheticFileGenerator
|
||||||
val syntheticFiles = parser.getSyntheticFiles()
|
val syntheticFiles = parser?.getSyntheticFiles()
|
||||||
syntheticFiles?.forEach { it.moduleInfo = moduleInfo }
|
syntheticFiles?.forEach { it.moduleInfo = moduleInfo }
|
||||||
|
|
||||||
return syntheticFiles ?: listOf()
|
return syntheticFiles ?: listOf()
|
||||||
|
|||||||
+4
-4
@@ -21,11 +21,11 @@ import com.intellij.psi.PsiElement
|
|||||||
import org.jetbrains.android.facet.AndroidFacet
|
import org.jetbrains.android.facet.AndroidFacet
|
||||||
import org.jetbrains.kotlin.android.synthetic.AndroidConst
|
import org.jetbrains.kotlin.android.synthetic.AndroidConst
|
||||||
import org.jetbrains.kotlin.android.synthetic.idea.AndroidXmlVisitor
|
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.AndroidLayoutXmlFileManager
|
||||||
|
import org.jetbrains.kotlin.android.synthetic.res.AndroidModuleInfo
|
||||||
import org.jetbrains.kotlin.psi.JetProperty
|
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() }
|
override val androidModuleInfo: AndroidModuleInfo? by lazy { module.androidFacet?.toAndroidModuleInfo() }
|
||||||
|
|
||||||
@@ -37,7 +37,7 @@ public class IDEAndroidLayoutXmlFileManager(val module: Module) : AndroidLayoutX
|
|||||||
val layoutFiles = getLayoutXmlFiles()[layoutPackageName]
|
val layoutFiles = getLayoutXmlFiles()[layoutPackageName]
|
||||||
if (layoutFiles == null || layoutFiles.isEmpty()) return listOf()
|
if (layoutFiles == null || layoutFiles.isEmpty()) return listOf()
|
||||||
|
|
||||||
val propertyName = property.getName()
|
val propertyName = property.name
|
||||||
|
|
||||||
val attributes = arrayListOf<PsiElement>()
|
val attributes = arrayListOf<PsiElement>()
|
||||||
val visitor = AndroidXmlVisitor { retId, wClass, valueElement ->
|
val visitor = AndroidXmlVisitor { retId, wClass, valueElement ->
|
||||||
@@ -53,7 +53,7 @@ public class IDEAndroidLayoutXmlFileManager(val module: Module) : AndroidLayoutX
|
|||||||
|
|
||||||
private fun AndroidFacet.toAndroidModuleInfo(): AndroidModuleInfo? {
|
private fun AndroidFacet.toAndroidModuleInfo(): AndroidModuleInfo? {
|
||||||
val applicationPackage = manifest?.getPackage()?.toString()
|
val applicationPackage = manifest?.getPackage()?.toString()
|
||||||
val mainResDirectories = getAllResourceDirectories().map { it.getPath() }
|
val mainResDirectories = allResourceDirectories.map { it.path }
|
||||||
|
|
||||||
return if (applicationPackage != null) {
|
return if (applicationPackage != null) {
|
||||||
AndroidModuleInfo(applicationPackage, mainResDirectories)
|
AndroidModuleInfo(applicationPackage, mainResDirectories)
|
||||||
|
|||||||
+17
-11
@@ -20,6 +20,7 @@ import com.intellij.openapi.module.Module
|
|||||||
import com.intellij.psi.JavaPsiFacade
|
import com.intellij.psi.JavaPsiFacade
|
||||||
import com.intellij.psi.PsiFile
|
import com.intellij.psi.PsiFile
|
||||||
import com.intellij.psi.impl.PsiTreeChangePreprocessor
|
import com.intellij.psi.impl.PsiTreeChangePreprocessor
|
||||||
|
import com.intellij.psi.search.GlobalSearchScope
|
||||||
import com.intellij.psi.util.CachedValue
|
import com.intellij.psi.util.CachedValue
|
||||||
import com.intellij.psi.util.CachedValueProvider.Result
|
import com.intellij.psi.util.CachedValueProvider.Result
|
||||||
import org.jetbrains.kotlin.android.synthetic.AndroidConst
|
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.idea.AndroidXmlVisitor
|
||||||
import org.jetbrains.kotlin.android.synthetic.parseAndroidResource
|
import org.jetbrains.kotlin.android.synthetic.parseAndroidResource
|
||||||
import org.jetbrains.kotlin.android.synthetic.res.AndroidResource
|
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.android.synthetic.res.SyntheticFileGenerator
|
||||||
|
import org.jetbrains.kotlin.psi.JetFile
|
||||||
|
|
||||||
class IDESyntheticFileGenerator(val module: Module) : SyntheticFileGenerator(module.project) {
|
class IDESyntheticFileGenerator(val module: Module) : SyntheticFileGenerator(module.project) {
|
||||||
|
|
||||||
private val supportV4: Boolean
|
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 {
|
init {
|
||||||
val scope = module.getModuleWithDependenciesAndLibrariesScope(false)
|
supportV4 = javaPsiFacade.findClasses(AndroidConst.SUPPORT_FRAGMENT_FQNAME, moduleScope).isNotEmpty()
|
||||||
supportV4 = JavaPsiFacade.getInstance(module.getProject())
|
|
||||||
.findClasses(AndroidConst.SUPPORT_FRAGMENT_FQNAME, scope).isNotEmpty()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun supportV4() = supportV4
|
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 }
|
module.project.getExtensions(PsiTreeChangePreprocessor.EP_NAME).first { it is AndroidPsiTreeChangePreprocessor }
|
||||||
}
|
}
|
||||||
|
|
||||||
override val cachedSources: CachedValue<List<AndroidSyntheticFile>> by lazy {
|
public override fun getSyntheticFiles(): List<JetFile> = cachedJetFiles.value
|
||||||
cachedValue {
|
|
||||||
Result.create(generateSyntheticFiles(), psiTreeChangePreprocessor)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun extractLayoutResources(files: List<PsiFile>): List<AndroidResource> {
|
override fun extractLayoutResources(files: List<PsiFile>, scope: GlobalSearchScope): List<AndroidResource> {
|
||||||
val widgets = arrayListOf<AndroidResource>()
|
val widgets = arrayListOf<AndroidResource>()
|
||||||
val visitor = AndroidXmlVisitor { id, widgetType, attribute ->
|
val visitor = AndroidXmlVisitor { id, widgetType, attribute ->
|
||||||
widgets.add(parseAndroidResource(id, widgetType))
|
widgets += parseAndroidResource(id, widgetType) {
|
||||||
|
resolveFqClassNameForView(javaPsiFacade, scope, it)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
files.forEach { it.accept(visitor) }
|
files.forEach { it.accept(visitor) }
|
||||||
|
|||||||
+4
-4
@@ -16,10 +16,10 @@
|
|||||||
|
|
||||||
package org.jetbrains.kotlin.android
|
package org.jetbrains.kotlin.android
|
||||||
|
|
||||||
import org.jetbrains.kotlin.android.synthetic.res.CliSyntheticFileGenerator
|
|
||||||
import com.intellij.openapi.module.ModuleManager
|
import com.intellij.openapi.module.ModuleManager
|
||||||
import org.jetbrains.kotlin.android.synthetic.idea.TestConst
|
import org.jetbrains.kotlin.android.synthetic.idea.TestConst
|
||||||
import org.jetbrains.kotlin.android.synthetic.idea.res.IDESyntheticFileGenerator
|
import org.jetbrains.kotlin.android.synthetic.idea.res.IDESyntheticFileGenerator
|
||||||
|
import org.jetbrains.kotlin.android.synthetic.res.CliSyntheticFileGenerator
|
||||||
import kotlin.test.assertEquals
|
import kotlin.test.assertEquals
|
||||||
|
|
||||||
public abstract class AbstractParserResultEqualityTest : KotlinAndroidTestCase() {
|
public abstract class AbstractParserResultEqualityTest : KotlinAndroidTestCase() {
|
||||||
@@ -31,11 +31,11 @@ public abstract class AbstractParserResultEqualityTest : KotlinAndroidTestCase()
|
|||||||
"$path${it.name}/"
|
"$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 ideParser = IDESyntheticFileGenerator(ModuleManager.getInstance(project).getModules()[0])
|
||||||
|
|
||||||
val cliResult = cliParser.getSyntheticFiles()!!.joinToString("\n\n")
|
val cliResult = cliParser.getSyntheticFiles().joinToString("\n\n")
|
||||||
val ideResult = ideParser.getSyntheticFiles()!!.joinToString("\n\n")
|
val ideResult = ideParser.getSyntheticFiles().joinToString("\n\n")
|
||||||
|
|
||||||
assertEquals(cliResult, ideResult)
|
assertEquals(cliResult, ideResult)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user