Support Fragment synthetic properties

This commit is contained in:
Yan Zhulanow
2015-04-08 21:12:42 +03:00
parent d7488023d2
commit 7a685b8c56
15 changed files with 322 additions and 21 deletions
@@ -37,6 +37,7 @@ import org.jetbrains.kotlin.resolve.lazy.descriptors.LazyClassDescriptor
import org.jetbrains.kotlin.resolve.scopes.receivers.ClassReceiver
import org.jetbrains.kotlin.resolve.source.KotlinSourceElement
import org.jetbrains.kotlin.types.Flexibility
import org.jetbrains.kotlin.types.lowerIfFlexible
import org.jetbrains.org.objectweb.asm.Label
import org.jetbrains.org.objectweb.asm.Opcodes.ACC_PRIVATE
import org.jetbrains.org.objectweb.asm.Opcodes.ACC_PUBLIC
@@ -46,6 +47,7 @@ import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter
private enum class AndroidClassType(val internalClassName: String, val supportsCache: Boolean = false) {
ACTIVITY : AndroidClassType(AndroidConst.ACTIVITY_FQNAME.innerName, true)
FRAGMENT : AndroidClassType(AndroidConst.FRAGMENT_FQNAME.innerName, true)
SUPPORT_FRAGMENT_ACTIVITY : AndroidClassType(AndroidConst.SUPPORT_FRAGMENT_ACTIVITY_FQNAME.innerName, true)
SUPPORT_FRAGMENT : AndroidClassType(AndroidConst.SUPPORT_FRAGMENT_FQNAME.innerName, true)
VIEW : AndroidClassType(AndroidConst.VIEW_FQNAME.innerName)
UNKNOWN : AndroidClassType("")
@@ -135,8 +137,12 @@ public class AndroidExpressionCodegenExtension : ExpressionCodegenExtension {
) : StackValue(typeMapper.mapType(descriptor.getReturnType()!!)) {
override fun putSelector(type: Type, v: InstructionAdapter) {
if (androidClassType.supportsCache && isCacheSupported(declarationDescriptor)) {
val returnTypeString = typeMapper.mapType(descriptor.getType().lowerIfFlexible()).getClassName()
if (AndroidConst.FRAGMENT_FQNAME == returnTypeString || AndroidConst.SUPPORT_FRAGMENT_FQNAME == returnTypeString) {
return putSelectorForFragment(v)
}
if (androidClassType.supportsCache && isCacheSupported(declarationDescriptor)) {
val declarationDescriptorType = typeMapper.mapType(declarationDescriptor)
receiver.put(declarationDescriptorType, v)
v.getstatic(androidPackage.replace(".", "/") + "/R\$id", descriptor.getName().asString(), "I")
@@ -144,15 +150,15 @@ public class AndroidExpressionCodegenExtension : ExpressionCodegenExtension {
}
else {
when (androidClassType) {
AndroidClassType.ACTIVITY, AndroidClassType.VIEW -> {
AndroidClassType.ACTIVITY, AndroidClassType.SUPPORT_FRAGMENT_ACTIVITY, AndroidClassType.VIEW -> {
receiver.put(Type.getType("L${androidClassType.internalClassName};"), v)
v.getstatic(androidPackage.replace(".", "/") + "/R\$id", descriptor.getName().asString(), "I")
getResourceId(v)
v.invokevirtual(androidClassType.internalClassName, "findViewById", "(I)Landroid/view/View;", false)
}
AndroidClassType.FRAGMENT, AndroidClassType.SUPPORT_FRAGMENT -> {
receiver.put(Type.getType("L${androidClassType.internalClassName};"), v)
v.invokevirtual(androidClassType.internalClassName, "getView", "()Landroid/view/View;", false)
v.getstatic(androidPackage.replace(".", "/") + "/R\$id", descriptor.getName().asString(), "I")
getResourceId(v)
v.invokevirtual("android/view/View", "findViewById", "(I)Landroid/view/View;", false)
}
else -> throw IllegalStateException("Invalid Android class type: $androidClassType") // Should never occur
@@ -161,6 +167,35 @@ public class AndroidExpressionCodegenExtension : ExpressionCodegenExtension {
v.checkcast(this.type)
}
private fun putSelectorForFragment(v: InstructionAdapter) {
receiver.put(Type.getType("L${androidClassType.internalClassName};"), v)
when (androidClassType) {
AndroidClassType.ACTIVITY, AndroidClassType.FRAGMENT -> {
v.invokevirtual(androidClassType.internalClassName, "getFragmentManager", "()Landroid/app/FragmentManager;", false)
getResourceId(v)
v.invokevirtual("android/app/FragmentManager", "findFragmentById", "(I)Landroid/app/Fragment;", false)
}
AndroidClassType.SUPPORT_FRAGMENT -> {
v.invokevirtual(androidClassType.internalClassName, "getFragmentManager", "()Landroid/support/v4/app/FragmentManager;", false)
getResourceId(v)
v.invokevirtual("android/support/v4/app/FragmentManager", "findFragmentById", "(I)Landroid/support/v4/app/Fragment;", false)
}
AndroidClassType.SUPPORT_FRAGMENT_ACTIVITY -> {
v.invokevirtual(androidClassType.internalClassName, "getSupportFragmentManager", "()Landroid/support/v4/app/FragmentManager;", false)
getResourceId(v)
v.invokevirtual("android/support/v4/app/FragmentManager", "findFragmentById", "(I)Landroid/support/v4/app/Fragment;", false)
}
else -> throw IllegalStateException("Invalid Android class type: $androidClassType") // Should never occur
}
v.checkcast(this.type)
}
fun getResourceId(v: InstructionAdapter) {
v.getstatic(androidPackage.replace(".", "/") + "/R\$id", descriptor.getName().asString(), "I")
}
}
private fun CallableDescriptor.getAndroidPackage(): String? {
@@ -175,6 +210,7 @@ public class AndroidExpressionCodegenExtension : ExpressionCodegenExtension {
fun getClassTypeInternal(name: String): AndroidClassType? = when (name) {
AndroidConst.ACTIVITY_FQNAME -> AndroidClassType.ACTIVITY
AndroidConst.FRAGMENT_FQNAME -> AndroidClassType.FRAGMENT
AndroidConst.SUPPORT_FRAGMENT_ACTIVITY_FQNAME -> AndroidClassType.SUPPORT_FRAGMENT_ACTIVITY
AndroidConst.SUPPORT_FRAGMENT_FQNAME -> AndroidClassType.SUPPORT_FRAGMENT
AndroidConst.VIEW_FQNAME -> AndroidClassType.VIEW
else -> null
@@ -292,7 +328,7 @@ public class AndroidExpressionCodegenExtension : ExpressionCodegenExtension {
// Resolve View via findViewById if not in cache
iv.load(0, classType)
when (androidClassType) {
AndroidClassType.ACTIVITY, AndroidClassType.VIEW -> {
AndroidClassType.ACTIVITY, AndroidClassType.SUPPORT_FRAGMENT_ACTIVITY, AndroidClassType.VIEW -> {
loadId()
iv.invokevirtual(className, "findViewById", "(I)Landroid/view/View;", false)
}
@@ -46,8 +46,9 @@ public object AndroidConst {
val FRAGMENT_FQNAME = "android.app.Fragment"
val SUPPORT_V4_PACKAGE = "android.support.v4"
val SUPPORT_FRAGMENT_FQNAME = "$SUPPORT_V4_PACKAGE.app.Fragment"
val SUPPORT_FRAGMENT_ACTIVITY_FQNAME = "$SUPPORT_V4_PACKAGE.app.FragmentActivity"
val IGNORED_XML_WIDGET_TYPES = setOf("requestFocus", "merge", "tag", "check", "fragment")
val IGNORED_XML_WIDGET_TYPES = setOf("requestFocus", "merge", "tag", "check")
val ESCAPED_IDENTIFIERS = (JetTokens.KEYWORDS.getTypes() + JetTokens.SOFT_KEYWORDS.getTypes())
.map { it as? JetKeywordToken }.filterNotNull().map { it.getValue() }.toSet()
@@ -66,6 +66,7 @@ public abstract class AndroidUIXmlProcessor(protected val project: Project) {
protected abstract val cachedSources: CachedValue<List<AndroidSyntheticFile>>
//MAKE CONSTANT (or abstract)
var supportV4 = false
private val cachedJetFiles: CachedValue<List<JetFile>> by Delegates.lazy {
@@ -141,9 +142,10 @@ public abstract class AndroidUIXmlProcessor(protected val project: Project) {
writeAndroidImports()
for (res in resources) {
properties(res).forEach {
if (supportV4 || !it.first.startsWith(AndroidConst.SUPPORT_V4_PACKAGE)) {
writeSyntheticProperty(it.first, res, it.second)
properties(res).forEach { property ->
// Comment
if (supportV4 || !isFromSupportV4Package(property.first)) {
writeSyntheticProperty(property.first, res, property.second)
}
}
}
@@ -162,12 +164,13 @@ public abstract class AndroidUIXmlProcessor(protected val project: Project) {
}
private fun KotlinStringWriter.writeSyntheticProperty(receiver: String, widget: AndroidResource, stubCall: String) {
val cast = if (widget.className != "View") " as? ${widget.className}" else ""
// extract startsWith() to fun
val className = if (isFromSupportV4Package(receiver)) widget.supportClassName() else widget.className
val cast = if (widget.className != "View") " as? $className" else ""
val body = arrayListOf("return $stubCall$cast")
val type = widget.className
writeImmutableExtensionProperty(receiver,
name = widget.id,
retType = "$EXPLICIT_FLEXIBLE_CLASS_NAME<$type, $type?>",
retType = "$EXPLICIT_FLEXIBLE_CLASS_NAME<$className, $className?>",
getterBody = body)
}
@@ -179,6 +182,10 @@ public abstract class AndroidUIXmlProcessor(protected val project: Project) {
return CachedValuesManager.getManager(project).createCachedValue(result, false)
}
private fun isFromSupportV4Package(fqName: String): Boolean {
return fqName.startsWith(AndroidConst.SUPPORT_V4_PACKAGE)
}
protected fun removeDuplicates(resources: List<AndroidResource>): List<AndroidResource> {
val resourceMap = linkedMapOf<String, AndroidResource>()
val resourcesToExclude = hashSetOf<String>()
@@ -26,25 +26,41 @@ public abstract class AndroidResource(val id: String) {
public open val viewProperties: List<Pair<String, String>> = listOf()
public open fun sameClass(other: AndroidResource): Boolean = false
public open fun supportClassName(): String = className
}
public class AndroidWidget(id: String, override val className: String) : AndroidResource(id) {
override val mainProperties = listOf(
"android.app.Activity" to "findViewById(0)",
"android.app.Fragment" to "getView().findViewById(0)",
"android.support.v4.app.Fragment" to "getView().findViewById(0)")
private companion object {
val MAIN_PROPERTIES = listOf(
AndroidConst.ACTIVITY_FQNAME to "findViewById(0)",
AndroidConst.FRAGMENT_FQNAME to "getView().findViewById(0)",
AndroidConst.SUPPORT_FRAGMENT_FQNAME to "getView().findViewById(0)")
override val viewProperties = listOf("android.view.View" to "findViewById(0)")
val VIEW_PROPERTIES = listOf("android.view.View" to "findViewById(0)")
}
override val mainProperties = MAIN_PROPERTIES
override val viewProperties = VIEW_PROPERTIES
override fun sameClass(other: AndroidResource) = other is AndroidWidget
}
public class AndroidFragment(id: String) : AndroidResource(id) {
override val className = "Fragment"
private companion object {
val MAIN_PROPERTIES = listOf(
AndroidConst.ACTIVITY_FQNAME to "getFragmentManager().findFragmentById(0)",
AndroidConst.FRAGMENT_FQNAME to "getFragmentManager().findFragmentById(0)",
AndroidConst.SUPPORT_FRAGMENT_FQNAME to "getFragmentManager().findFragmentById(0)",
AndroidConst.SUPPORT_FRAGMENT_ACTIVITY_FQNAME to "getSupportFragmentManager().findFragmentById(0)")
}
override val mainProperties = listOf(
"android.app.Activity" to "getFragmentManager().findFragmentById(0)",
"android.app.Fragment" to "getActivity().getFragmentManager().findFragmentById(0)")
override val className = AndroidConst.FRAGMENT_FQNAME
override val mainProperties = MAIN_PROPERTIES
override fun sameClass(other: AndroidResource) = other is AndroidFragment
override fun supportClassName() = AndroidConst.SUPPORT_FRAGMENT_FQNAME
}