Do not use JavaPsiFacade in android-compiler-plugin

This commit is contained in:
Yan Zhulanow
2015-08-20 19:45:01 +03:00
parent d0d846ccfc
commit 0f5d87957d
5 changed files with 47 additions and 43 deletions
@@ -18,14 +18,14 @@ package org.jetbrains.kotlin.android.synthetic.res
import com.intellij.openapi.diagnostic.Logger 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.PsiElementFinder
import com.intellij.psi.PsiFile import com.intellij.psi.PsiFile
import java.io.ByteArrayInputStream import java.io.ByteArrayInputStream
import com.intellij.psi.impl.PsiElementFinderImpl
import com.intellij.psi.search.GlobalSearchScope
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 org.jetbrains.kotlin.psi.JetFile
import kotlin.properties.Delegates
public open class CliSyntheticFileGenerator( public open class CliSyntheticFileGenerator(
project: Project, project: Project,
@@ -33,32 +33,30 @@ public open class CliSyntheticFileGenerator(
private val resDirectories: List<String> private val resDirectories: List<String>
) : 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 { private val cachedJetFiles by lazy {
val supportV4 = supportV4Available(javaPsiFacade, projectScope) val supportV4 = supportV4Available()
generateSyntheticJetFiles(generateSyntheticFiles(true, projectScope, supportV4))
generateSyntheticJetFiles(generateSyntheticFiles(true, supportV4))
} }
override val layoutXmlFileManager: CliAndroidLayoutXmlFileManager by Delegates.lazy { override val layoutXmlFileManager: CliAndroidLayoutXmlFileManager by lazy {
CliAndroidLayoutXmlFileManager(project, manifestPath, resDirectories) CliAndroidLayoutXmlFileManager(project, manifestPath, resDirectories)
} }
public override fun getSyntheticFiles(): List<JetFile> = cachedJetFiles public override fun getSyntheticFiles(): List<JetFile> = cachedJetFiles
override fun extractLayoutResources(files: List<PsiFile>, scope: GlobalSearchScope): List<AndroidResource> { override fun extractLayoutResources(files: List<PsiFile>): List<AndroidResource> {
val resources = arrayListOf<AndroidResource>() val resources = arrayListOf<AndroidResource>()
val handler = AndroidXmlHandler { id, tag -> val handler = AndroidXmlHandler { id, tag ->
resources += parseAndroidResource(id, tag) { tag -> resources += parseAndroidResource(id, tag) { tag ->
resolveFqClassNameForView(javaPsiFacade, scope, tag) resolveFqClassNameForView(tag)
} }
} }
for (file in files) { for (file in files) {
try { try {
val inputStream = ByteArrayInputStream(file.getVirtualFile().contentsToByteArray()) val inputStream = ByteArrayInputStream(file.virtualFile.contentsToByteArray())
layoutXmlFileManager.saxParser.parse(inputStream, handler) layoutXmlFileManager.saxParser.parse(inputStream, handler)
} catch (e: Throwable) { } catch (e: Throwable) {
LOG.error(e) LOG.error(e)
@@ -68,6 +66,17 @@ public open class CliSyntheticFileGenerator(
return filterDuplicates(resources) return filterDuplicates(resources)
} }
override fun checkIfClassExist(fqName: String): Boolean {
val scope = GlobalSearchScope.allScope(project)
val psiElementFinders = project.getExtensions(PsiElementFinder.EP_NAME).filter { it is PsiElementFinderImpl }
for (finder in psiElementFinders) {
val clazz = finder.findClass(fqName, scope)
if (clazz != null) return true
}
return false
}
private companion object { private companion object {
private val LOG: Logger = Logger.getInstance(javaClass) private val LOG: Logger = Logger.getInstance(javaClass)
} }
@@ -17,10 +17,9 @@
package org.jetbrains.kotlin.android.synthetic.res package org.jetbrains.kotlin.android.synthetic.res
import com.intellij.openapi.project.Project import com.intellij.openapi.project.Project
import com.intellij.psi.JavaPsiFacade import com.intellij.psi.PsiClass
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
@@ -46,16 +45,12 @@ public abstract class SyntheticFileGenerator(protected val project: Project) {
public abstract fun getSyntheticFiles(): List<JetFile> public abstract fun getSyntheticFiles(): List<JetFile>
protected fun generateSyntheticFiles( protected fun generateSyntheticFiles(generateCommonFiles: Boolean, supportV4: Boolean): List<AndroidSyntheticFile> {
generateCommonFiles: Boolean,
scope: GlobalSearchScope,
supportV4: Boolean
): List<AndroidSyntheticFile> {
val commonFiles = if (generateCommonFiles) generateCommonFiles(supportV4) else listOf() val commonFiles = if (generateCommonFiles) generateCommonFiles(supportV4) else listOf()
return layoutXmlFileManager.getLayoutXmlFiles().flatMap { entry -> return layoutXmlFileManager.getLayoutXmlFiles().flatMap { entry ->
val files = entry.getValue() val files = entry.getValue()
val resources = extractLayoutResources(files, scope) val resources = extractLayoutResources(files)
val layoutName = entry.getKey() val layoutName = entry.getKey()
@@ -81,7 +76,9 @@ public abstract class SyntheticFileGenerator(protected val project: Project) {
return listOf(clearCacheFile, TOOLS_FILE) return listOf(clearCacheFile, TOOLS_FILE)
} }
protected abstract fun extractLayoutResources(files: List<PsiFile>, scope: GlobalSearchScope): List<AndroidResource> protected abstract fun extractLayoutResources(files: List<PsiFile>): List<AndroidResource>
protected abstract fun checkIfClassExist(fqName: String): Boolean
private fun renderMainLayoutFile(layoutName: String, resources: List<AndroidResource>, supportV4: Boolean): AndroidSyntheticFile { private fun renderMainLayoutFile(layoutName: String, resources: List<AndroidResource>, supportV4: Boolean): AndroidSyntheticFile {
return renderLayoutFile(layoutName + AndroidConst.LAYOUT_POSTFIX, escapeAndroidIdentifier(layoutName), resources) { return renderLayoutFile(layoutName + AndroidConst.LAYOUT_POSTFIX, escapeAndroidIdentifier(layoutName), resources) {
@@ -152,25 +149,23 @@ 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? { protected fun resolveFqClassNameForView(tag: String): String? {
if (tag.contains('.')) { if (tag.contains('.')) {
if (javaPsiFacade.findClass(tag, scope) == null) { if (!checkIfClassExist(tag)) {
return null return null
} }
return tag return tag
} }
for (pkg in AndroidConst.FQNAME_RESOLVE_PACKAGES) { for (pkg in AndroidConst.FQNAME_RESOLVE_PACKAGES) {
val fqName = "$pkg.$tag" val fqName = "$pkg.$tag"
if (javaPsiFacade.findClass(fqName, scope) != null) { if (checkIfClassExist(fqName)) {
return fqName return fqName
} }
} }
return null return null
} }
protected fun supportV4Available(javaPsiFacade: JavaPsiFacade, scope: GlobalSearchScope): Boolean { protected fun supportV4Available(): Boolean = checkIfClassExist(AndroidConst.SUPPORT_FRAGMENT_FQNAME)
return javaPsiFacade.findClasses(AndroidConst.SUPPORT_FRAGMENT_FQNAME, scope).isNotEmpty()
}
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>()
@@ -40,10 +40,7 @@ public abstract class AbstractAndroidXml2KConversionTest : UsefulTestCase() {
val parser = CliSyntheticFileGeneratorForConversionTest( val parser = CliSyntheticFileGeneratorForConversionTest(
jetCoreEnvironment.project, path + "AndroidManifest.xml", layoutPaths, supportV4) jetCoreEnvironment.project, path + "AndroidManifest.xml", layoutPaths, supportV4)
val module = ModuleManager.getInstance(jetCoreEnvironment.project).modules.first() val actual = parser.gen().toMap { it.name }
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")
@@ -17,7 +17,6 @@
package org.jetbrains.kotlin.lang.resolve.android.test package org.jetbrains.kotlin.lang.resolve.android.test
import com.intellij.openapi.project.Project import com.intellij.openapi.project.Project
import com.intellij.psi.search.GlobalSearchScope
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.android.synthetic.AndroidConfigurationKeys import org.jetbrains.kotlin.android.synthetic.AndroidConfigurationKeys
@@ -49,7 +48,7 @@ class CliSyntheticFileGeneratorForConversionTest(
resDirectories: List<String>, resDirectories: List<String>,
private val supportV4: Boolean private val supportV4: Boolean
) : CliSyntheticFileGenerator(project, manifestPath, resDirectories) { ) : CliSyntheticFileGenerator(project, manifestPath, resDirectories) {
fun gen(scope: GlobalSearchScope) = generateSyntheticFiles(false, scope, supportV4) fun gen() = generateSyntheticFiles(false, supportV4)
} }
fun UsefulTestCase.createAndroidTestEnvironment( fun UsefulTestCase.createAndroidTestEnvironment(
@@ -20,9 +20,9 @@ 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.idea.AndroidPsiTreeChangePreprocessor import org.jetbrains.kotlin.android.synthetic.idea.AndroidPsiTreeChangePreprocessor
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
@@ -32,13 +32,10 @@ import org.jetbrains.kotlin.psi.JetFile
class IDESyntheticFileGenerator(val module: Module) : SyntheticFileGenerator(module.project) { class IDESyntheticFileGenerator(val module: Module) : SyntheticFileGenerator(module.project) {
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 { private val cachedJetFiles: CachedValue<List<JetFile>> by lazy {
cachedValue { cachedValue {
val supportV4 = supportV4Available(javaPsiFacade, moduleScope) val supportV4 = supportV4Available()
Result.create(generateSyntheticJetFiles(generateSyntheticFiles(true, moduleScope, supportV4)), psiTreeChangePreprocessor) Result.create(generateSyntheticJetFiles(generateSyntheticFiles(true, supportV4)), psiTreeChangePreprocessor)
} }
} }
@@ -48,13 +45,16 @@ 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 }
} }
public override fun getSyntheticFiles(): List<JetFile> = cachedJetFiles.value public override fun getSyntheticFiles(): List<JetFile> {
if (!checkIfClassExist(AndroidConst.VIEW_FQNAME)) return listOf()
return cachedJetFiles.value
}
override fun extractLayoutResources(files: List<PsiFile>, scope: GlobalSearchScope): List<AndroidResource> { override fun extractLayoutResources(files: List<PsiFile>): List<AndroidResource> {
val widgets = arrayListOf<AndroidResource>() val widgets = arrayListOf<AndroidResource>()
val visitor = AndroidXmlVisitor { id, widgetType, attribute -> val visitor = AndroidXmlVisitor { id, widgetType, attribute ->
widgets += parseAndroidResource(id, widgetType) { widgets += parseAndroidResource(id, widgetType) { tag ->
resolveFqClassNameForView(javaPsiFacade, scope, it) resolveFqClassNameForView(tag)
} }
} }
@@ -62,4 +62,8 @@ class IDESyntheticFileGenerator(val module: Module) : SyntheticFileGenerator(mod
return filterDuplicates(widgets) return filterDuplicates(widgets)
} }
override fun checkIfClassExist(fqName: String): Boolean {
val moduleScope = module.getModuleWithDependenciesAndLibrariesScope(false)
return JavaPsiFacade.getInstance(module.project).findClass(fqName, moduleScope) != null
}
} }