All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.jetbrains.kotlin.android.synthetic.res.AndroidLayoutXmlFileManager.kt Maven / Gradle / Ivy

There is a newer version: 2.1.20-Beta1
Show newest version
/*
 * Copyright 2010-2015 JetBrains s.r.o.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.jetbrains.kotlin.android.synthetic.res

import org.jetbrains.kotlin.com.intellij.openapi.module.Module
import org.jetbrains.kotlin.com.intellij.openapi.module.ModuleServiceManager
import org.jetbrains.kotlin.com.intellij.openapi.project.Project
import org.jetbrains.kotlin.com.intellij.openapi.vfs.VirtualFile
import org.jetbrains.kotlin.com.intellij.openapi.vfs.VirtualFileManager
import org.jetbrains.kotlin.com.intellij.psi.PsiElement
import org.jetbrains.kotlin.com.intellij.psi.PsiFile
import org.jetbrains.kotlin.com.intellij.psi.PsiManager
import org.jetbrains.kotlin.android.synthetic.AndroidConst
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
import java.util.*

class AndroidVariantData(val variant: AndroidVariant, private val layouts: Map>): Map> by layouts
class AndroidModuleData(val module: AndroidModule, private val variants: List): Iterable by variants {
    companion object {
        val EMPTY = AndroidModuleData(AndroidModule("android", listOf()), listOf())
    }
}

abstract class AndroidLayoutXmlFileManager(val project: Project) {

    abstract val androidModule: AndroidModule?

    open fun propertyToXmlAttributes(propertyDescriptor: PropertyDescriptor): List = listOf()

    open fun getModuleData(): AndroidModuleData {
        val androidModule = androidModule ?: return AndroidModuleData.EMPTY
        return AndroidModuleData(androidModule, androidModule.variants.map { getVariantData(it) })
    }

    fun getVariantData(variant: AndroidVariant): AndroidVariantData {
        val psiManager = PsiManager.getInstance(project)
        val fileManager = VirtualFileManager.getInstance()

        fun VirtualFile.getAllChildren(): List {
            val allChildren = arrayListOf()
            val currentChildren = children ?: emptyArray()
            for (child in currentChildren) {
                if (child.isDirectory) {
                    allChildren.addAll(child.getAllChildren())
                }
                else {
                    allChildren.add(child)
                }
            }
            return allChildren
        }

        val resDirectories = variant.resDirectories.map { fileManager.findFileByUrl("file://$it") }
        val allChildren = resDirectories.flatMap { it?.getAllChildren() ?: listOf() }

        val allLayoutFiles = allChildren.filter { it.parent.name.startsWith("layout") && it.name.toLowerCase().endsWith(".xml") }
        val allLayoutPsiFiles = allLayoutFiles.fold(ArrayList(allLayoutFiles.size)) { list, file ->
            val psiFile = psiManager.findFile(file)
            if (psiFile != null && psiFile.parent != null) {
                list += psiFile
            }
            list
        }

        val layoutNameToXmlFiles = allLayoutPsiFiles
                .groupBy { it.name.substringBeforeLast('.') }
                .mapValues { it.value.sortedBy { it.parent!!.name.length } }

        return AndroidVariantData(variant, layoutNameToXmlFiles)
    }

    fun extractResources(files: List, module: ModuleDescriptor): List {
        return filterDuplicates(doExtractResources(files, module))
    }

    protected abstract fun doExtractResources(files: List, module: ModuleDescriptor): List

    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)
            else -> AndroidResource.Widget(id, tag, sourceElement)
        }
    }

    private fun filterDuplicates(resources: List): List {
        val resourceMap = linkedMapOf()
        val resourcesToExclude = hashSetOf()

        for (res in resources) {
            if (resourceMap.contains(res.id.name)) {
                val existing = resourceMap[res.id.name]!!

                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.name, AndroidResource.Widget(res.id, AndroidConst.VIEW_FQNAME, res.sourceElement))
                    }
                }
            }
            else resourceMap.put(res.id.name, res)
        }
        resourcesToExclude.forEach { resourceMap.remove(it) }
        return resourceMap.values.toList()
    }


    companion object {
        fun getInstance(module: Module): AndroidLayoutXmlFileManager? {
            val service = ModuleServiceManager.getService(module, AndroidLayoutXmlFileManager::class.java)
            return service ?: module.getComponent(AndroidLayoutXmlFileManager::class.java)
        }
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy