diff --git a/plugins/android-extensions/android-extensions-compiler/src/org/jetbrains/kotlin/android/synthetic/AndroidConst.kt b/plugins/android-extensions/android-extensions-compiler/src/org/jetbrains/kotlin/android/synthetic/AndroidConst.kt index 37bb9ef3c08..85cf7b6a773 100644 --- a/plugins/android-extensions/android-extensions-compiler/src/org/jetbrains/kotlin/android/synthetic/AndroidConst.kt +++ b/plugins/android-extensions/android-extensions-compiler/src/org/jetbrains/kotlin/android/synthetic/AndroidConst.kt @@ -16,6 +16,7 @@ package org.jetbrains.kotlin.android.synthetic +import org.jetbrains.kotlin.android.synthetic.res.ResourceIdentifier object AndroidConst { val SYNTHETIC_PACKAGE: String = "kotlinx.android.synthetic" @@ -32,9 +33,7 @@ object AndroidConst { val ID_ATTRIBUTE: String = "$ANDROID_NAMESPACE:$ID_ATTRIBUTE_NO_NAMESPACE" val CLASS_ATTRIBUTE_NO_NAMESPACE: String = "class" - val ID_DECLARATION_PREFIX = "@+id/" - val ID_USAGE_PREFIX = "@id/" - val XML_ID_PREFIXES = arrayOf(ID_DECLARATION_PREFIX, ID_USAGE_PREFIX) + val IDENTIFIER_REGEX = "^@(\\+)?(([A-Za-z0-9_\\.]+)\\:)?id\\/([A-Za-z0-9_]+)$".toRegex() val CLEAR_FUNCTION_NAME = "clearFindViewByIdCache" @@ -55,13 +54,10 @@ object AndroidConst { val FQNAME_RESOLVE_PACKAGES = listOf("android.widget", "android.webkit", "android.view") } -fun androidIdToName(id: String): String? { - for (prefix in AndroidConst.XML_ID_PREFIXES) { - if (id.startsWith(prefix)) { - return id.substring(prefix.length) - } - } - return null +fun androidIdToName(id: String): ResourceIdentifier? { + val values = AndroidConst.IDENTIFIER_REGEX.matchEntire(id)?.groupValues ?: return null + val packageName = values[3] + return ResourceIdentifier(values[4], if (packageName.isEmpty()) null else packageName) } fun isWidgetTypeIgnored(xmlType: String): Boolean { diff --git a/plugins/android-extensions/android-extensions-compiler/src/org/jetbrains/kotlin/android/synthetic/AndroidXmlHandler.kt b/plugins/android-extensions/android-extensions-compiler/src/org/jetbrains/kotlin/android/synthetic/AndroidXmlHandler.kt index 52fdfaaa492..9bcea5dbe9a 100644 --- a/plugins/android-extensions/android-extensions-compiler/src/org/jetbrains/kotlin/android/synthetic/AndroidXmlHandler.kt +++ b/plugins/android-extensions/android-extensions-compiler/src/org/jetbrains/kotlin/android/synthetic/AndroidXmlHandler.kt @@ -16,11 +16,12 @@ package org.jetbrains.kotlin.android.synthetic +import org.jetbrains.kotlin.android.synthetic.res.ResourceIdentifier import org.xml.sax.Attributes import org.xml.sax.helpers.DefaultHandler import java.util.HashMap -class AndroidXmlHandler(private val elementCallback: (String, String) -> Unit) : DefaultHandler() { +class AndroidXmlHandler(private val elementCallback: (ResourceIdentifier, String) -> Unit) : DefaultHandler() { override fun startDocument() { super.startDocument() diff --git a/plugins/android-extensions/android-extensions-compiler/src/org/jetbrains/kotlin/android/synthetic/codegen/AndroidExpressionCodegenExtension.kt b/plugins/android-extensions/android-extensions-compiler/src/org/jetbrains/kotlin/android/synthetic/codegen/AndroidExpressionCodegenExtension.kt index 9b6e12da51c..3c56a8e3efc 100644 --- a/plugins/android-extensions/android-extensions-compiler/src/org/jetbrains/kotlin/android/synthetic/codegen/AndroidExpressionCodegenExtension.kt +++ b/plugins/android-extensions/android-extensions-compiler/src/org/jetbrains/kotlin/android/synthetic/codegen/AndroidExpressionCodegenExtension.kt @@ -178,10 +178,16 @@ class AndroidExpressionCodegenExtension : ExpressionCodegenExtension { return putSelectorForFragment(v) } + val syntheticProperty = propertyDescriptor as AndroidSyntheticProperty + if (androidClassType.supportsCache && isCacheSupported(receiverDescriptor, propertyDescriptor)) { val declarationDescriptorType = typeMapper.mapType(receiverDescriptor) receiver.put(declarationDescriptorType, v) - v.getstatic(androidPackage.replace(".", "/") + "/R\$id", propertyDescriptor.name.asString(), "I") + + val resourceId = syntheticProperty.resourceId + val packageName = resourceId.packageName ?: androidPackage + v.getstatic(packageName.replace(".", "/") + "/R\$id", resourceId.name, "I") + v.invokevirtual(declarationDescriptorType.internalName, CACHED_FIND_VIEW_BY_ID_METHOD_NAME, "(I)Landroid/view/View;", false) } else { diff --git a/plugins/android-extensions/android-extensions-compiler/src/org/jetbrains/kotlin/android/synthetic/descriptors/AndroidSyntheticPackageFragmentDescriptor.kt b/plugins/android-extensions/android-extensions-compiler/src/org/jetbrains/kotlin/android/synthetic/descriptors/AndroidSyntheticPackageFragmentDescriptor.kt index 5c7e9e6272c..9d92ad06873 100644 --- a/plugins/android-extensions/android-extensions-compiler/src/org/jetbrains/kotlin/android/synthetic/descriptors/AndroidSyntheticPackageFragmentDescriptor.kt +++ b/plugins/android-extensions/android-extensions-compiler/src/org/jetbrains/kotlin/android/synthetic/descriptors/AndroidSyntheticPackageFragmentDescriptor.kt @@ -60,8 +60,10 @@ class AndroidSyntheticPackageFragmentDescriptor( when (resource) { is AndroidResource.Widget -> { val resolvedWidget = resource.resolve(module) - for (receiver in widgetReceivers) { - properties += genPropertyForWidget(packageFragmentDescriptor, receiver, resolvedWidget, context) + if (resolvedWidget != null) { + for (receiver in widgetReceivers) { + properties += genPropertyForWidget(packageFragmentDescriptor, receiver, resolvedWidget, context) + } } } is AndroidResource.Fragment -> if (!packageData.forView) { diff --git a/plugins/android-extensions/android-extensions-compiler/src/org/jetbrains/kotlin/android/synthetic/res/AndroidLayoutXmlFileManager.kt b/plugins/android-extensions/android-extensions-compiler/src/org/jetbrains/kotlin/android/synthetic/res/AndroidLayoutXmlFileManager.kt index 651d13edfab..0e731a9e927 100644 --- a/plugins/android-extensions/android-extensions-compiler/src/org/jetbrains/kotlin/android/synthetic/res/AndroidLayoutXmlFileManager.kt +++ b/plugins/android-extensions/android-extensions-compiler/src/org/jetbrains/kotlin/android/synthetic/res/AndroidLayoutXmlFileManager.kt @@ -27,7 +27,6 @@ import com.intellij.psi.PsiManager import org.jetbrains.kotlin.android.synthetic.AndroidConst import org.jetbrains.kotlin.descriptors.ModuleDescriptor import org.jetbrains.kotlin.descriptors.PropertyDescriptor -import org.jetbrains.kotlin.psi.KtProperty import java.util.* class AndroidVariantData(val variant: AndroidVariant, private val layouts: Map>): Map> by layouts @@ -91,7 +90,7 @@ abstract class AndroidLayoutXmlFileManager(val project: Project) { protected abstract fun doExtractResources(files: List, module: ModuleDescriptor): List - protected fun parseAndroidResource(id: String, tag: String, sourceElement: PsiElement?): AndroidResource { + protected fun parseAndroidResource(id: ResourceIdentifier, tag: String, sourceElement: PsiElement?): AndroidResource { return when (tag) { "fragment" -> AndroidResource.Fragment(id, sourceElement) "include" -> AndroidResource.Widget(id, AndroidConst.VIEW_FQNAME, sourceElement) @@ -104,20 +103,20 @@ abstract class AndroidLayoutXmlFileManager(val project: Project) { val resourcesToExclude = hashSetOf() for (res in resources) { - if (resourceMap.contains(res.id)) { - val existing = resourceMap[res.id]!! + if (resourceMap.contains(res.id.name)) { + val existing = resourceMap[res.id.name]!! - if (!res.sameClass(existing)) { - resourcesToExclude.add(res.id) + if (!res.sameClass(existing) || res.id.packageName != existing.id.packageName) { + resourcesToExclude.add(res.id.name) } else if (res is AndroidResource.Widget && existing is AndroidResource.Widget) { // Widgets with the same id but different types exist. if (res.xmlType != existing.xmlType && existing.xmlType != AndroidConst.VIEW_FQNAME) { - resourceMap.put(res.id, AndroidResource.Widget(res.id, AndroidConst.VIEW_FQNAME, res.sourceElement)) + resourceMap.put(res.id.name, AndroidResource.Widget(res.id, AndroidConst.VIEW_FQNAME, res.sourceElement)) } } } - else resourceMap.put(res.id, res) + else resourceMap.put(res.id.name, res) } resourcesToExclude.forEach { resourceMap.remove(it) } return resourceMap.values.toList() diff --git a/plugins/android-extensions/android-extensions-compiler/src/org/jetbrains/kotlin/android/synthetic/res/androidResources.kt b/plugins/android-extensions/android-extensions-compiler/src/org/jetbrains/kotlin/android/synthetic/res/androidResources.kt index 86ae42b3bb4..ec2f2af59c8 100644 --- a/plugins/android-extensions/android-extensions-compiler/src/org/jetbrains/kotlin/android/synthetic/res/androidResources.kt +++ b/plugins/android-extensions/android-extensions-compiler/src/org/jetbrains/kotlin/android/synthetic/res/androidResources.kt @@ -26,6 +26,7 @@ import org.jetbrains.kotlin.descriptors.ClassDescriptor import org.jetbrains.kotlin.descriptors.ModuleDescriptor import org.jetbrains.kotlin.name.ClassId import org.jetbrains.kotlin.name.FqName +import org.jetbrains.kotlin.name.isValidJavaFqName import org.jetbrains.kotlin.serialization.deserialization.findClassAcrossModuleDependencies class AndroidVariant(val name: String, val resDirectories: List) { @@ -43,18 +44,35 @@ class AndroidModule(val applicationPackage: String, val variants: List Unit) : XmlElementVisitor() { +class AndroidXmlVisitor(val elementCallback: (ResourceIdentifier, String, XmlAttribute) -> Unit) : XmlElementVisitor() { override fun visitElement(element: PsiElement) { element.acceptChildren(this) diff --git a/plugins/android-extensions/android-extensions-idea/src/org/jetbrains/kotlin/android/synthetic/idea/res/IDEAndroidLayoutXmlFileManager.kt b/plugins/android-extensions/android-extensions-idea/src/org/jetbrains/kotlin/android/synthetic/idea/res/IDEAndroidLayoutXmlFileManager.kt index bf88bcf24db..af0c7ddd41a 100644 --- a/plugins/android-extensions/android-extensions-idea/src/org/jetbrains/kotlin/android/synthetic/idea/res/IDEAndroidLayoutXmlFileManager.kt +++ b/plugins/android-extensions/android-extensions-idea/src/org/jetbrains/kotlin/android/synthetic/idea/res/IDEAndroidLayoutXmlFileManager.kt @@ -84,7 +84,7 @@ class IDEAndroidLayoutXmlFileManager(val module: Module) : AndroidLayoutXmlFileM val attributes = arrayListOf() val visitor = AndroidXmlVisitor { retId, wClass, valueElement -> - if (retId == propertyName) attributes.add(valueElement) + if (retId.name == propertyName) attributes.add(valueElement) } layoutFiles.forEach { it.accept(visitor) }