From a4dc6870dc6496f179f6c7eedbefbafb8a811237 Mon Sep 17 00:00:00 2001 From: Yan Zhulanow Date: Fri, 20 Mar 2015 21:24:43 +0300 Subject: [PATCH] Fix Android layout widget redeclarations --- .../resolve/android/AndroidResourceManager.kt | 7 ++-- .../resolve/android/AndroidUIXmlProcessor.kt | 26 ++++++++++++--- .../android/CliAndroidUIXmlProcessor.kt | 12 ++++--- .../lang/resolve/android/androidResources.kt | 2 +- .../simple/layoutVariants/AndroidManifest.xml | 33 +++++++++++++++++++ .../converter/simple/layoutVariants/layout.kt | 14 ++++++++ .../simple/layoutVariants/layout1.kt | 11 +++++++ .../layoutVariants/res/layout-hdpi/layout.xml | 10 ++++++ .../layoutVariants/res/layout/layout.xml | 10 ++++++ .../simple/sameIds/AndroidManifest.xml | 33 +++++++++++++++++++ .../converter/simple/sameIds/layout.kt | 20 +++++++++++ .../converter/simple/sameIds/layout1.kt | 14 ++++++++ .../simple/sameIds/res/layout/layout.xml | 25 ++++++++++++++ .../AndroidXml2KConversionTestGenerated.java | 12 +++++++ .../android/AndroidFindMemberUsagesHandler.kt | 3 +- .../android/IDEAndroidResourceManager.kt | 2 +- .../android/IDEAndroidUIXmlProcessor.kt | 11 ++++--- 17 files changed, 224 insertions(+), 21 deletions(-) create mode 100644 plugins/android-compiler-plugin/testData/android/converter/simple/layoutVariants/AndroidManifest.xml create mode 100644 plugins/android-compiler-plugin/testData/android/converter/simple/layoutVariants/layout.kt create mode 100644 plugins/android-compiler-plugin/testData/android/converter/simple/layoutVariants/layout1.kt create mode 100644 plugins/android-compiler-plugin/testData/android/converter/simple/layoutVariants/res/layout-hdpi/layout.xml create mode 100644 plugins/android-compiler-plugin/testData/android/converter/simple/layoutVariants/res/layout/layout.xml create mode 100644 plugins/android-compiler-plugin/testData/android/converter/simple/sameIds/AndroidManifest.xml create mode 100644 plugins/android-compiler-plugin/testData/android/converter/simple/sameIds/layout.kt create mode 100644 plugins/android-compiler-plugin/testData/android/converter/simple/sameIds/layout1.kt create mode 100644 plugins/android-compiler-plugin/testData/android/converter/simple/sameIds/res/layout/layout.xml diff --git a/plugins/android-compiler-plugin/src/org/jetbrains/kotlin/lang/resolve/android/AndroidResourceManager.kt b/plugins/android-compiler-plugin/src/org/jetbrains/kotlin/lang/resolve/android/AndroidResourceManager.kt index 4eca04c4260..937a32c6891 100644 --- a/plugins/android-compiler-plugin/src/org/jetbrains/kotlin/lang/resolve/android/AndroidResourceManager.kt +++ b/plugins/android-compiler-plugin/src/org/jetbrains/kotlin/lang/resolve/android/AndroidResourceManager.kt @@ -33,9 +33,9 @@ public abstract class AndroidResourceManager(val project: Project) { public open fun idToXmlAttribute(id: String): PsiElement? = null - open fun getLayoutXmlFiles(): List { + public fun getLayoutXmlFiles(): Map> { val info = androidModuleInfo - if (info == null) return listOf() + if (info == null) return mapOf() val psiManager = PsiManager.getInstance(project) val fileManager = VirtualFileManager.getInstance() @@ -61,7 +61,8 @@ public abstract class AndroidResourceManager(val project: Project) { .filter { it.getParent().getName().startsWith("layout") && it.getName().toLowerCase().endsWith(".xml") } .map { psiManager.findFile(it) } .filterNotNull() - .sortBy { it.getName() } + .groupBy { it.getName().substringBeforeLast('.') } + .mapValues { it.getValue().sortBy { it.getParent().getName().length() } } } fun getMainResDirectory(): VirtualFile? { diff --git a/plugins/android-compiler-plugin/src/org/jetbrains/kotlin/lang/resolve/android/AndroidUIXmlProcessor.kt b/plugins/android-compiler-plugin/src/org/jetbrains/kotlin/lang/resolve/android/AndroidUIXmlProcessor.kt index 511a74b6a03..5cb83191883 100644 --- a/plugins/android-compiler-plugin/src/org/jetbrains/kotlin/lang/resolve/android/AndroidUIXmlProcessor.kt +++ b/plugins/android-compiler-plugin/src/org/jetbrains/kotlin/lang/resolve/android/AndroidUIXmlProcessor.kt @@ -69,7 +69,7 @@ public abstract class AndroidUIXmlProcessor(protected val project: Project) { val psiManager = PsiManager.getInstance(project) val applicationPackage = resourceManager.androidModuleInfo?.applicationPackage - val jetFiles = cachedSources.getValue().mapIndexed { (index, text) -> + val jetFiles = cachedSources.getValue().mapIndexed { index, text -> val virtualFile = LightVirtualFile(AndroidConst.SYNTHETIC_FILENAME + index + ".kt", text) val jetFile = psiManager.findFile(virtualFile) as JetFile if (applicationPackage != null) { @@ -89,10 +89,11 @@ public abstract class AndroidUIXmlProcessor(protected val project: Project) { listOf(clearCacheFile, FLEXIBLE_TYPE_FILE) } else listOf() - return resourceManager.getLayoutXmlFiles().flatMap { file -> - val widgets = parseSingleFile(file) + return resourceManager.getLayoutXmlFiles().flatMap { entry -> + val files = entry.getValue() + val widgets = parseLayout(files) if (widgets.isNotEmpty()) { - val layoutPackage = file.genSyntheticPackageName() + val layoutPackage = files[0].genSyntheticPackageName() val mainLayoutFile = renderLayoutFile(layoutPackage, widgets) { writeSyntheticProperty("Activity", it, "findViewById(0)") @@ -110,7 +111,7 @@ public abstract class AndroidUIXmlProcessor(protected val project: Project) { public fun parseToPsi(): List? = cachedJetFiles.getValue() - protected abstract fun parseSingleFile(file: PsiFile): List + protected abstract fun parseLayout(files: List): List private fun renderLayoutFile( packageName: String, @@ -150,6 +151,21 @@ public abstract class AndroidUIXmlProcessor(protected val project: Project) { return CachedValuesManager.getManager(project).createCachedValue(result, false) } + protected fun removeDuplicates(widgets: List): List { + val widgetMap = linkedMapOf() + for (widget in widgets) { + if (widgetMap.contains(widget.id)) { + val existingElement = widgetMap.get(widget.id) + if (existingElement.className != widget.className) { + // Widgets with the same id but different types exist. + widgetMap.put(widget.id, widget.copy(className = "View")) + } + } + else widgetMap.put(widget.id, widget) + } + return widgetMap.values().toList() + } + companion object { private val EXPLICIT_FLEXIBLE_PACKAGE = Flexibility.FLEXIBLE_TYPE_CLASSIFIER.getPackageFqName().asString() private val EXPLICIT_FLEXIBLE_CLASS_NAME = Flexibility.FLEXIBLE_TYPE_CLASSIFIER.getRelativeClassName().asString() diff --git a/plugins/android-compiler-plugin/src/org/jetbrains/kotlin/lang/resolve/android/CliAndroidUIXmlProcessor.kt b/plugins/android-compiler-plugin/src/org/jetbrains/kotlin/lang/resolve/android/CliAndroidUIXmlProcessor.kt index 2b1733a8825..9e9df3a99e0 100644 --- a/plugins/android-compiler-plugin/src/org/jetbrains/kotlin/lang/resolve/android/CliAndroidUIXmlProcessor.kt +++ b/plugins/android-compiler-plugin/src/org/jetbrains/kotlin/lang/resolve/android/CliAndroidUIXmlProcessor.kt @@ -43,14 +43,16 @@ public class CliAndroidUIXmlProcessor( } } - override fun parseSingleFile(file: PsiFile): List { + override fun parseLayout(files: List): List { val widgets = arrayListOf() - val handler = AndroidXmlHandler { id, clazz -> widgets.add(AndroidWidget(id, clazz)) } + val handler = AndroidXmlHandler { id, widgetType -> widgets.add(AndroidWidget(id, widgetType)) } try { - val inputStream = ByteArrayInputStream(file.getVirtualFile().contentsToByteArray()) - resourceManager.saxParser.parse(inputStream, handler) - return widgets + for (file in files) { + val inputStream = ByteArrayInputStream(file.getVirtualFile().contentsToByteArray()) + resourceManager.saxParser.parse(inputStream, handler) + } + return removeDuplicates(widgets) } catch (e: Throwable) { LOG.error(e) diff --git a/plugins/android-compiler-plugin/src/org/jetbrains/kotlin/lang/resolve/android/androidResources.kt b/plugins/android-compiler-plugin/src/org/jetbrains/kotlin/lang/resolve/android/androidResources.kt index 04fcba0ff1f..23f2d4e4849 100644 --- a/plugins/android-compiler-plugin/src/org/jetbrains/kotlin/lang/resolve/android/androidResources.kt +++ b/plugins/android-compiler-plugin/src/org/jetbrains/kotlin/lang/resolve/android/androidResources.kt @@ -20,4 +20,4 @@ public data class AndroidModuleInfo(val applicationPackage: String, val mainResD trait AndroidResource -public class AndroidWidget(val id: String, val className: String) : AndroidResource \ No newline at end of file +public data class AndroidWidget(val id: String, val className: String) : AndroidResource \ No newline at end of file diff --git a/plugins/android-compiler-plugin/testData/android/converter/simple/layoutVariants/AndroidManifest.xml b/plugins/android-compiler-plugin/testData/android/converter/simple/layoutVariants/AndroidManifest.xml new file mode 100644 index 00000000000..580c474f5de --- /dev/null +++ b/plugins/android-compiler-plugin/testData/android/converter/simple/layoutVariants/AndroidManifest.xml @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/plugins/android-compiler-plugin/testData/android/converter/simple/layoutVariants/layout.kt b/plugins/android-compiler-plugin/testData/android/converter/simple/layoutVariants/layout.kt new file mode 100644 index 00000000000..9e1a5e9793a --- /dev/null +++ b/plugins/android-compiler-plugin/testData/android/converter/simple/layoutVariants/layout.kt @@ -0,0 +1,14 @@ +package kotlinx.android.synthetic.layout + +import android.app.Activity +import android.app.Fragment +import android.view.View +import android.widget.* +import kotlin.internal.flexible.ft + +val Activity.button: ft + get() = findViewById(0) : View + +val Fragment.button: ft + get() = getView().findViewById(0) : View + diff --git a/plugins/android-compiler-plugin/testData/android/converter/simple/layoutVariants/layout1.kt b/plugins/android-compiler-plugin/testData/android/converter/simple/layoutVariants/layout1.kt new file mode 100644 index 00000000000..44a79867803 --- /dev/null +++ b/plugins/android-compiler-plugin/testData/android/converter/simple/layoutVariants/layout1.kt @@ -0,0 +1,11 @@ +package kotlinx.android.synthetic.layout.view + +import android.app.Activity +import android.app.Fragment +import android.view.View +import android.widget.* +import kotlin.internal.flexible.ft + +val View.button: ft + get() = findViewById(0) : View + diff --git a/plugins/android-compiler-plugin/testData/android/converter/simple/layoutVariants/res/layout-hdpi/layout.xml b/plugins/android-compiler-plugin/testData/android/converter/simple/layoutVariants/res/layout-hdpi/layout.xml new file mode 100644 index 00000000000..53351ea3e23 --- /dev/null +++ b/plugins/android-compiler-plugin/testData/android/converter/simple/layoutVariants/res/layout-hdpi/layout.xml @@ -0,0 +1,10 @@ + + + + + \ No newline at end of file diff --git a/plugins/android-compiler-plugin/testData/android/converter/simple/layoutVariants/res/layout/layout.xml b/plugins/android-compiler-plugin/testData/android/converter/simple/layoutVariants/res/layout/layout.xml new file mode 100644 index 00000000000..88846af96e4 --- /dev/null +++ b/plugins/android-compiler-plugin/testData/android/converter/simple/layoutVariants/res/layout/layout.xml @@ -0,0 +1,10 @@ + + +