Support View in android-compiler-plugin

This commit is contained in:
Yan Zhulanow
2015-03-04 21:19:28 +03:00
parent 33a1aa342f
commit 4cc5e9248a
41 changed files with 637 additions and 60 deletions
@@ -42,10 +42,11 @@ import org.jetbrains.org.objectweb.asm.Opcodes.ACC_PUBLIC
import org.jetbrains.org.objectweb.asm.Type
import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter
private enum class AndroidClassType {
ACTIVITY
FRAGMENT
UNKNOWN
private enum class AndroidClassType(val internalClassName: String, val supportsCache: Boolean = false) {
ACTIVITY : AndroidClassType("android/app/Activity", true)
FRAGMENT : AndroidClassType("android/app/Fragment", true)
VIEW : AndroidClassType("android/view/View")
UNKNOWN : AndroidClassType("")
}
public class AndroidExpressionCodegenExtension : ExpressionCodegenExtension {
@@ -72,15 +73,10 @@ public class AndroidExpressionCodegenExtension : ExpressionCodegenExtension {
if (declarationDescriptor == null) return null
val supportsCache = when {
extensionReceiver is ClassReceiver -> true
else -> {
val source = declarationDescriptor.getSource()
if (source is KotlinSourceElement) true else false
}
}
val supportsCache = declarationDescriptor.getSource() is KotlinSourceElement
if (supportsCache) {
val androidClassType = getClassType(declarationDescriptor)
if (supportsCache && androidClassType.supportsCache) {
val className = DescriptorUtils.getFqName(declarationDescriptor).toString()
val bytecodeClassName = className.replace('.', '/')
@@ -88,15 +84,15 @@ public class AndroidExpressionCodegenExtension : ExpressionCodegenExtension {
c.v.getstatic(androidPackage.replace(".", "/") + "/R\$id", propertyDescriptor.getName().asString(), "I")
c.v.invokevirtual(bytecodeClassName, METHOD_NAME, "(I)Landroid/view/View;", false)
} else {
when (getClassType(declarationDescriptor)) {
AndroidClassType.ACTIVITY -> {
receiver.put(Type.getType("Landroid/app/Activity;"), c.v)
when (androidClassType) {
AndroidClassType.ACTIVITY, AndroidClassType.VIEW -> {
receiver.put(Type.getType("L${androidClassType.internalClassName};"), c.v)
c.v.getstatic(androidPackage.replace(".", "/") + "/R\$id", propertyDescriptor.getName().asString(), "I")
c.v.invokevirtual("android/app/Activity", "findViewById", "(I)Landroid/view/View;", false)
c.v.invokevirtual(androidClassType.internalClassName, "findViewById", "(I)Landroid/view/View;", false)
}
AndroidClassType.FRAGMENT -> {
receiver.put(Type.getType("Landroid/app/Fragment;"), c.v)
c.v.invokevirtual("android/app/Fragment", "getView", "()Landroid/view/View;", false)
receiver.put(Type.getType("L${androidClassType.internalClassName};"), c.v)
c.v.invokevirtual(androidClassType.internalClassName, "getView", "()Landroid/view/View;", false)
c.v.getstatic(androidPackage.replace(".", "/") + "/R\$id", propertyDescriptor.getName().asString(), "I")
c.v.invokevirtual("android/view/View", "findViewById", "(I)Landroid/view/View;", false)
}
@@ -114,6 +110,7 @@ public class AndroidExpressionCodegenExtension : ExpressionCodegenExtension {
fun getClassTypeInternal(name: String): AndroidClassType? = when (name) {
"android.app.Activity" -> AndroidClassType.ACTIVITY
"android.app.Fragment" -> AndroidClassType.FRAGMENT
"android.view.View" -> AndroidClassType.VIEW
else -> null
}
@@ -196,7 +193,7 @@ public class AndroidExpressionCodegenExtension : ExpressionCodegenExtension {
// Resolve View via findViewById if not in cache
iv.load(0, classType)
when (androidClassType) {
AndroidClassType.ACTIVITY -> {
AndroidClassType.ACTIVITY, AndroidClassType.VIEW -> {
loadId()
iv.invokevirtual(className, "findViewById", "(I)Landroid/view/View;", false)
}
@@ -57,11 +57,7 @@ public abstract class AndroidUIXmlProcessor(protected val project: Project) {
public class NoAndroidManifestFound : Exception("No android manifest file found in project root")
private val androidImports = listOf(
"android.app.Activity",
"android.app.Fragment",
"android.view.View",
"android.widget.*")
protected val LOG: Logger = Logger.getInstance(javaClass)
public abstract val resourceManager: AndroidResourceManager
@@ -91,31 +87,42 @@ public abstract class AndroidUIXmlProcessor(protected val project: Project) {
}
}
protected val LOG: Logger = Logger.getInstance(javaClass)
public fun parse(): List<String> {
return resourceManager.getLayoutXmlFiles().map { file ->
return resourceManager.getLayoutXmlFiles().flatMap { file ->
val widgets = parseSingleFile(file)
if (widgets.isNotEmpty()) {
val layoutPackage = file.genSyntheticPackageName()
val stringWriter = KotlinStringWriter()
stringWriter.writePackage(layoutPackage)
stringWriter.writeAndroidImports()
widgets.forEach {
stringWriter.writeSyntheticProperty("Activity", it, "findViewById(0)")
stringWriter.writeSyntheticProperty("Fragment", it, "getView().findViewById(0)")
val mainLayoutFile = renderLayoutFile(layoutPackage, widgets) {
writeSyntheticProperty("Activity", it, "findViewById(0)")
writeSyntheticProperty("Fragment", it, "getView().findViewById(0)")
}
val contents = stringWriter.toStringBuffer().toString()
contents
} else null
val viewLayoutFile = renderLayoutFile("$layoutPackage.view", widgets) {
writeSyntheticProperty("View", it, "findViewById(0)")
}
listOf(mainLayoutFile, viewLayoutFile)
} else listOf()
}.filterNotNull()
}
public fun parseToPsi(): List<JetFile>? = cachedJetFiles.getValue()
protected abstract fun parseSingleFile(file: PsiFile): Collection<AndroidWidget>
protected abstract fun parseSingleFile(file: PsiFile): List<AndroidWidget>
private fun renderLayoutFile(
packageName: String,
widgets: List<AndroidWidget>,
widgetWriter: KotlinStringWriter.(AndroidWidget) -> Unit
): String {
val stringWriter = KotlinStringWriter()
stringWriter.writePackage(packageName)
stringWriter.writeAndroidImports()
widgets.forEach { stringWriter.widgetWriter(it) }
return stringWriter.toStringBuffer().toString()
}
private fun KotlinStringWriter.writeAndroidImports() {
androidImports.forEach { writeImport(it) }
@@ -138,4 +145,12 @@ public abstract class AndroidUIXmlProcessor(protected val project: Project) {
return CachedValuesManager.getManager(project).createCachedValue(result, false)
}
default object {
private val androidImports = listOf(
"android.app.Activity",
"android.app.Fragment",
"android.view.View",
"android.widget.*")
}
}
@@ -38,8 +38,8 @@ public class CliAndroidUIXmlProcessor(
project.getExtensions(PsiTreeChangePreprocessor.EP_NAME).first { it is AndroidPsiTreeChangePreprocessor }
}
override fun parseSingleFile(file: PsiFile): Collection<AndroidWidget> {
val widgets: MutableCollection<AndroidWidget> = ArrayList()
override fun parseSingleFile(file: PsiFile): List<AndroidWidget> {
val widgets = arrayListOf<AndroidWidget>()
val handler = AndroidXmlHandler { id, clazz -> widgets.add(AndroidWidget(id, clazz)) }
try {