From d557fa8d5dd4ec4b74d7a786a97d53af1b99796c Mon Sep 17 00:00:00 2001 From: Mikhail Mutcianko Date: Fri, 18 Jul 2014 18:08:14 +0400 Subject: [PATCH] event based file processing - use VirtualFileListener to wait for ui xml file updates - optimize imports --- .../resolve/android/AndroidUIXmlParser.kt | 61 +++++++++++++++---- .../resolve/android/CliAndroidUIXmlParser.kt | 2 - 2 files changed, 50 insertions(+), 13 deletions(-) diff --git a/compiler/frontend.android/src/org/jetbrains/jet/lang/resolve/android/AndroidUIXmlParser.kt b/compiler/frontend.android/src/org/jetbrains/jet/lang/resolve/android/AndroidUIXmlParser.kt index af84a8b4220..946a12b37bc 100644 --- a/compiler/frontend.android/src/org/jetbrains/jet/lang/resolve/android/AndroidUIXmlParser.kt +++ b/compiler/frontend.android/src/org/jetbrains/jet/lang/resolve/android/AndroidUIXmlParser.kt @@ -10,6 +10,11 @@ import org.jetbrains.kotlin.psi.JetFile import javax.xml.parsers.SAXParser import java.util.HashMap import com.intellij.openapi.vfs.VirtualFile +import com.intellij.openapi.vfs.VirtualFileManager +import com.intellij.openapi.vfs.VirtualFileAdapter +import com.intellij.openapi.vfs.VirtualFileEvent +import com.intellij.openapi.project.Project +import java.util.concurrent.ConcurrentLinkedQueue abstract class AndroidUIXmlParser { @@ -24,9 +29,13 @@ abstract class AndroidUIXmlParser { val saxParser = initSAX() - val fileCache = HashMap() + val fileCache = HashMap() var lastCachedPsi: JetFile? = null - val fileModificationTime = HashMap() + val fileModificationTime = HashMap() + + val filesToProcess = ConcurrentLinkedQueue() + var listenerSetUp = false + volatile var invalidateCaches = false public fun parseToString(): String? { val cacheState = doParse() @@ -37,7 +46,7 @@ abstract class AndroidUIXmlParser { public fun parseToPsi(project: Project): JetFile? { val cacheState = doParse() if (cacheState == null) return null - return if (cacheState === CacheAction.MISS) { + return if (cacheState == CacheAction.MISS || lastCachedPsi == null) { try { val psiFile = JetPsiFactory(project).createFile(renderString()) lastCachedPsi = psiFile @@ -84,20 +93,20 @@ abstract class AndroidUIXmlParser { return kw } - private fun parseSingleFileWithCache(file: File): Pair { + private fun parseSingleFileWithCache(file: VirtualFile): Pair { val lastRecorded = fileModificationTime[file] ?: -1 - if (file.lastModified() > lastRecorded) + if (file.getModificationStamp() > lastRecorded) return Pair(parseSingleFile(file), CacheAction.MISS) else return Pair(fileCache[file]!!, CacheAction.HIT) } - private fun parseSingleFile(file: File): String { + private fun parseSingleFile(file: VirtualFile): String { val ids: MutableCollection = ArrayList() val handler = AndroidXmlHandler({ id, wClass -> ids.add(AndroidWidget(id, wClass)) }) - fileModificationTime[file] = file.lastModified() + fileModificationTime[file] = file.getModificationStamp() try { - saxParser.parse(FileInputStream(file), handler) + saxParser.parse(file.getInputStream()!!, handler) val res = produceKotlinProperties(KotlinStringWriter(), ids).toString() fileCache[file] = res return res @@ -109,11 +118,14 @@ abstract class AndroidUIXmlParser { private fun doParse(): CacheAction? { if (searchPath == null || searchPath == "") return null - val files = searchForUIXml(searchPath!!) + lazySetupFileListener() + if (invalidateCaches) invalidateCaches() var overallCacheMiss = false - for (file in files) { - val res = parseSingleFileWithCache(file) + var file = filesToProcess.poll() + while (file != null) { + val res = parseSingleFileWithCache(file!!) overallCacheMiss = overallCacheMiss or (res.second == CacheAction.MISS) + file = filesToProcess.poll() } return if (overallCacheMiss) CacheAction.MISS else CacheAction.HIT } @@ -129,6 +141,33 @@ abstract class AndroidUIXmlParser { fileCache.clear() fileModificationTime.clear() lastCachedPsi = null + invalidateCaches = false + } + + private fun lazySetupFileListener() { + if (listenerSetUp) return + val fileManager = VirtualFileManager.getInstance() + 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.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 } private fun produceKotlinProperties(kw: KotlinStringWriter, ids: Collection): StringBuffer { diff --git a/compiler/frontend.android/src/org/jetbrains/jet/lang/resolve/android/CliAndroidUIXmlParser.kt b/compiler/frontend.android/src/org/jetbrains/jet/lang/resolve/android/CliAndroidUIXmlParser.kt index 0d4254cabb5..dc5f98b1214 100644 --- a/compiler/frontend.android/src/org/jetbrains/jet/lang/resolve/android/CliAndroidUIXmlParser.kt +++ b/compiler/frontend.android/src/org/jetbrains/jet/lang/resolve/android/CliAndroidUIXmlParser.kt @@ -1,6 +1,4 @@ package org.jetbrains.jet.lang.resolve.android -import com.intellij.openapi.project.Project - class CliAndroidUIXmlParser(override val searchPath: String?): AndroidUIXmlParser()