replace sax with psi based android ui xml parsing in plugin

This commit is contained in:
dedoz
2014-07-26 21:29:35 +04:00
committed by Yan Zhulanow
parent c24fccc659
commit 9899e78329
4 changed files with 89 additions and 24 deletions
@@ -47,6 +47,8 @@ import com.intellij.psi.PsiFile
import org.xml.sax.InputSource
import java.io.ByteArrayInputStream
import com.intellij.openapi.diagnostic.Log
import org.xml.sax.SAXException
import com.intellij.openapi.diagnostic.Logger
abstract class AndroidUIXmlParser {
@@ -63,15 +65,22 @@ abstract class AndroidUIXmlParser {
protected abstract val searchPath: String?
protected abstract val androidAppPackage: String
private val saxParser = initSAX()
protected val saxParser: SAXParser = initSAX()
protected fun initSAX(): SAXParser {
val saxFactory = SAXParserFactory.newInstance()
saxFactory?.setNamespaceAware(true)
return saxFactory!!.newSAXParser()
}
private val fileCache = HashMap<PsiFile, String>()
private var lastCachedPsi: JetFile? = null
private val fileModificationTime = HashMap<PsiFile, Long>()
protected val fileModificationTime: HashMap<PsiFile, Long> = HashMap()
protected val filesToProcess: Queue<PsiFile> = ConcurrentLinkedQueue()
protected var listenerSetUp: Boolean = false
protected volatile var invalidateCaches: Boolean = false
protected val LOG: Logger = Logger.getInstance(this.javaClass)
public fun parseToString(): String? {
val cacheState = doParse()
@@ -102,11 +111,6 @@ abstract class AndroidUIXmlParser {
return file.extension == "xml"
}
private fun initSAX(): SAXParser {
val saxFactory = SAXParserFactory.newInstance()
saxFactory?.setNamespaceAware(true)
return saxFactory!!.newSAXParser()
}
private fun searchForUIXml(path: String): Collection<File> {
return searchForUIXml(arrayListOf(File(path)))
@@ -143,26 +147,17 @@ abstract class AndroidUIXmlParser {
}
private fun parseSingleFile(file: PsiFile): String {
val ids: MutableCollection<AndroidWidget> = ArrayList()
val handler = AndroidXmlHandler({ id, wClass -> ids.add(AndroidWidget(id, wClass)) })
val res = parseSingleFileImpl(file)
fileModificationTime[file] = file.getModificationStamp()
try {
val source = InputSource(ByteArrayInputStream(file.getText()!!.getBytes("utf-8")))
saxParser.parse(source, handler)
val res = produceKotlinProperties(KotlinStringWriter(), ids).toString()
fileCache[file] = res
return res
} catch (e: Exception) {
Log.print(e.getMessage())
invalidateCaches()
return ""
}
fileCache[file] = res
return res
}
abstract fun parseSingleFileImpl(file: PsiFile): String
private fun doParse(): CacheAction? {
if (searchPath == null || searchPath == "") return null
lazySetup()
if (invalidateCaches) invalidateCaches()
var overallCacheMiss = false
var file = filesToProcess.poll()
while (file != null) {
@@ -184,7 +179,6 @@ abstract class AndroidUIXmlParser {
fileCache.clear()
fileModificationTime.clear()
lastCachedPsi = null
invalidateCaches = false
}
protected fun populateQueue(project: Project) {
@@ -216,7 +210,7 @@ abstract class AndroidUIXmlParser {
}
}
private fun produceKotlinProperties(kw: KotlinStringWriter, ids: Collection<AndroidWidget>): StringBuffer {
protected fun produceKotlinProperties(kw: KotlinStringWriter, ids: Collection<AndroidWidget>): StringBuffer {
for (id in ids) {
val body = arrayListOf("return findViewById(0) as ${id.className}")
kw.writeImmutableExtensionProperty(receiver = "Activity",
@@ -19,14 +19,33 @@ package org.jetbrains.jet.lang.resolve.android
import com.intellij.openapi.vfs.VirtualFileManager
import java.util.ArrayList
import com.intellij.openapi.project.Project
import com.intellij.psi.PsiFile
import java.io.ByteArrayInputStream
import org.xml.sax.InputSource
import javax.xml.parsers.SAXParser
import javax.xml.parsers.SAXParserFactory
class CliAndroidUIXmlParser(val project: Project, override val searchPath: String?): AndroidUIXmlParser() {
override var androidAppPackage: String = ""
override fun lazySetup() {
populateQueue(project)
androidAppPackage = readManifest()._package
}
override fun parseSingleFileImpl(file: PsiFile): String {
val ids: MutableCollection<AndroidWidget> = ArrayList()
val handler = AndroidXmlHandler({ id, wClass -> ids.add(AndroidWidget(id, wClass)) })
try {
val source = InputSource(ByteArrayInputStream(file.getText()!!.getBytes("utf-8")))
saxParser.parse(source, handler)
return produceKotlinProperties(KotlinStringWriter(), ids).toString()
} catch (e: Throwable) {
LOG.error(e)
return ""
}
}
}
@@ -0,0 +1,43 @@
/*
* Copyright 2010-2014 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.jet.plugin.android
import com.intellij.psi.XmlElementVisitor
import com.intellij.psi.PsiElement
import com.intellij.psi.xml.XmlElement
import com.intellij.psi.xml.XmlTag
class AndroidXmlVisitor(val elementCallback: (String, String)-> Unit): XmlElementVisitor() {
override fun visitElement(element: PsiElement) {
element.acceptChildren(this)
}
override fun visitXmlElement(element: XmlElement?) {
element?.acceptChildren(this)
}
override fun visitXmlTag(tag: XmlTag?) {
val idPrefix = "@+id/"
val attribute = tag?.getAttribute("android:id")
if (attribute != null && attribute.getValue() != null) {
elementCallback(attribute.getValue()!!.replace(idPrefix, ""), tag!!.getLocalName())
}
tag?.acceptChildren(this)
}
}
@@ -22,6 +22,9 @@ import com.intellij.openapi.vfs.VirtualFileManager
import java.util.ArrayList
import com.intellij.openapi.vfs.VirtualFileAdapter
import com.intellij.openapi.vfs.VirtualFileEvent
import com.intellij.psi.PsiFile
import org.jetbrains.jet.lang.resolve.android.AndroidWidget
import org.jetbrains.jet.lang.resolve.android.KotlinStringWriter
class IDEAndroidUIXmlParser(val project: Project): AndroidUIXmlParser() {
override val searchPath: String? = project.getBasePath() + "/res/layout/"
@@ -33,5 +36,11 @@ class IDEAndroidUIXmlParser(val project: Project): AndroidUIXmlParser() {
populateQueue(project)
listenerSetUp = true
}
override fun parseSingleFileImpl(file: PsiFile): String {
val ids: MutableCollection<AndroidWidget> = ArrayList()
file.accept(AndroidXmlVisitor({ id, wClass -> ids.add(AndroidWidget(id, wClass))}))
return produceKotlinProperties(KotlinStringWriter(), ids).toString()
}
}