fix xml resolving

- replace VirtualFileListener based approach with direct PsiFile reading
This commit is contained in:
Mikhail Mutcianko
2014-07-25 15:22:52 +04:00
committed by Yan Zhulanow
parent 35b1cb4fc7
commit c24fccc659
3 changed files with 33 additions and 38 deletions
@@ -40,6 +40,13 @@ import org.xml.sax.helpers.DefaultHandler
import org.xml.sax.Attributes import org.xml.sax.Attributes
import org.jetbrains.kotlin.resolve.android.AndroidConst.* import org.jetbrains.kotlin.resolve.android.AndroidConst.*
import com.intellij.psi.PsiFileFactory import com.intellij.psi.PsiFileFactory
import com.intellij.psi.util.PsiModificationTracker
import com.intellij.psi.impl.PsiModificationTrackerImpl
import java.util.Queue
import com.intellij.psi.PsiFile
import org.xml.sax.InputSource
import java.io.ByteArrayInputStream
import com.intellij.openapi.diagnostic.Log
abstract class AndroidUIXmlParser { abstract class AndroidUIXmlParser {
@@ -53,18 +60,18 @@ abstract class AndroidUIXmlParser {
"android.view.View", "android.view.View",
"android.widget.*") "android.widget.*")
abstract val searchPath: String? protected abstract val searchPath: String?
abstract val androidAppPackage: String protected abstract val androidAppPackage: String
val saxParser = initSAX() private val saxParser = initSAX()
val fileCache = HashMap<VirtualFile, String>() private val fileCache = HashMap<PsiFile, String>()
var lastCachedPsi: JetFile? = null private var lastCachedPsi: JetFile? = null
val fileModificationTime = HashMap<VirtualFile, Long>() private val fileModificationTime = HashMap<PsiFile, Long>()
val filesToProcess = ConcurrentLinkedQueue<VirtualFile>() protected val filesToProcess: Queue<PsiFile> = ConcurrentLinkedQueue()
var listenerSetUp = false protected var listenerSetUp: Boolean = false
volatile var invalidateCaches = false protected volatile var invalidateCaches: Boolean = false
public fun parseToString(): String? { public fun parseToString(): String? {
val cacheState = doParse() val cacheState = doParse()
@@ -73,6 +80,7 @@ abstract class AndroidUIXmlParser {
} }
public fun parseToPsi(project: Project): JetFile? { public fun parseToPsi(project: Project): JetFile? {
populateQueue(project)
val cacheState = doParse() val cacheState = doParse()
if (cacheState == null) return null if (cacheState == null) return null
return if (cacheState == CacheAction.MISS || lastCachedPsi == null) { return if (cacheState == CacheAction.MISS || lastCachedPsi == null) {
@@ -126,7 +134,7 @@ abstract class AndroidUIXmlParser {
return kw return kw
} }
private fun parseSingleFileWithCache(file: VirtualFile): Pair<String, CacheAction> { private fun parseSingleFileWithCache(file: PsiFile): Pair<String, CacheAction> {
val lastRecorded = fileModificationTime[file] ?: -1 val lastRecorded = fileModificationTime[file] ?: -1
if (file.getModificationStamp() > lastRecorded) if (file.getModificationStamp() > lastRecorded)
return Pair(parseSingleFile(file), CacheAction.MISS) return Pair(parseSingleFile(file), CacheAction.MISS)
@@ -134,17 +142,19 @@ abstract class AndroidUIXmlParser {
return Pair(fileCache[file]!!, CacheAction.HIT) return Pair(fileCache[file]!!, CacheAction.HIT)
} }
private fun parseSingleFile(file: VirtualFile): String { private fun parseSingleFile(file: PsiFile): String {
val ids: MutableCollection<AndroidWidget> = ArrayList() val ids: MutableCollection<AndroidWidget> = ArrayList()
val handler = AndroidXmlHandler({ id, wClass -> ids.add(AndroidWidget(id, wClass)) }) val handler = AndroidXmlHandler({ id, wClass -> ids.add(AndroidWidget(id, wClass)) })
fileModificationTime[file] = file.getModificationStamp() fileModificationTime[file] = file.getModificationStamp()
try { try {
saxParser.parse(file.getInputStream()!!, handler) val source = InputSource(ByteArrayInputStream(file.getText()!!.getBytes("utf-8")))
saxParser.parse(source, handler)
val res = produceKotlinProperties(KotlinStringWriter(), ids).toString() val res = produceKotlinProperties(KotlinStringWriter(), ids).toString()
fileCache[file] = res fileCache[file] = res
return res return res
} catch (e: Exception) { } catch (e: Exception) {
fileCache[file] = "" Log.print(e.getMessage())
invalidateCaches()
return "" return ""
} }
} }
@@ -177,6 +187,13 @@ abstract class AndroidUIXmlParser {
invalidateCaches = false invalidateCaches = false
} }
protected fun populateQueue(project: Project) {
val fileManager = VirtualFileManager.getInstance()
val watchDir = fileManager.findFileByUrl("file://" + searchPath)
val psiManager = PsiManager.getInstance(project)
filesToProcess.addAll(watchDir?.getChildren()?.toArrayList()?.map { psiManager.findFile(it) } ?.mapNotNull { it })
}
protected abstract fun lazySetup() protected abstract fun lazySetup()
protected fun readManifest(): AndroidManifest { protected fun readManifest(): AndroidManifest {
@@ -25,9 +25,7 @@ class CliAndroidUIXmlParser(val project: Project, override val searchPath: Strin
override var androidAppPackage: String = "" override var androidAppPackage: String = ""
override fun lazySetup() { override fun lazySetup() {
val fileManager = VirtualFileManager.getInstance() populateQueue(project)
val watchDir = fileManager.findFileByUrl("file://" + searchPath)
filesToProcess.addAll(watchDir?.getChildren()?.toArrayList() ?: ArrayList(0))
androidAppPackage = readManifest()._package androidAppPackage = readManifest()._package
} }
} }
@@ -23,34 +23,14 @@ import java.util.ArrayList
import com.intellij.openapi.vfs.VirtualFileAdapter import com.intellij.openapi.vfs.VirtualFileAdapter
import com.intellij.openapi.vfs.VirtualFileEvent import com.intellij.openapi.vfs.VirtualFileEvent
class IDEAndroidUIXmlParser(project: Project): AndroidUIXmlParser() { class IDEAndroidUIXmlParser(val project: Project): AndroidUIXmlParser() {
override val searchPath: String? = project.getBasePath() + "/res/layout/" override val searchPath: String? = project.getBasePath() + "/res/layout/"
override var androidAppPackage: String = "" override var androidAppPackage: String = ""
override protected fun lazySetup() { override protected fun lazySetup() {
if (listenerSetUp) return if (listenerSetUp) return
androidAppPackage = readManifest()._package androidAppPackage = readManifest()._package
val fileManager = VirtualFileManager.getInstance() populateQueue(project)
val watchDir = fileManager.findFileByUrl("file://" + searchPath)
filesToProcess.addAll(watchDir?.getChildren()?.toArrayList() ?: ArrayList(0))
fileManager.addVirtualFileListener(object : VirtualFileAdapter() {
override fun contentsChanged(event: VirtualFileEvent) {
if (event.getParent() == watchDir)
filesToProcess.add(event.getFile())
}
override fun fileCreated(event: VirtualFileEvent) {
if (event.getParent() == watchDir)
super<VirtualFileAdapter>.fileCreated(event)
}
override fun fileDeleted(event: VirtualFileEvent) {
if (event.getParent() == watchDir) {
// ignore potential synchronisation issues - it doesn't really matter if invalidation and
// file processing will be handled in different passes
invalidateCaches = true
filesToProcess.addAll(watchDir?.getChildren()?.toArrayList() ?: ArrayList(0))
}
}
})
listenerSetUp = true listenerSetUp = true
} }
} }