Fix Android layout widget redeclarations

This commit is contained in:
Yan Zhulanow
2015-03-20 21:24:43 +03:00
parent c40598b3e9
commit a4dc6870dc
17 changed files with 224 additions and 21 deletions
@@ -33,9 +33,9 @@ public abstract class AndroidResourceManager(val project: Project) {
public open fun idToXmlAttribute(id: String): PsiElement? = null
open fun getLayoutXmlFiles(): List<PsiFile> {
public fun getLayoutXmlFiles(): Map<String, List<PsiFile>> {
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? {
@@ -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<JetFile>? = cachedJetFiles.getValue()
protected abstract fun parseSingleFile(file: PsiFile): List<AndroidWidget>
protected abstract fun parseLayout(files: List<PsiFile>): List<AndroidWidget>
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<AndroidWidget>): List<AndroidWidget> {
val widgetMap = linkedMapOf<String, AndroidWidget>()
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()
@@ -43,14 +43,16 @@ public class CliAndroidUIXmlProcessor(
}
}
override fun parseSingleFile(file: PsiFile): List<AndroidWidget> {
override fun parseLayout(files: List<PsiFile>): List<AndroidWidget> {
val widgets = arrayListOf<AndroidWidget>()
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)
@@ -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
public data class AndroidWidget(val id: String, val className: String) : AndroidResource