replace sax with psi based android ui xml parsing in plugin
This commit is contained in:
+18
-24
@@ -47,6 +47,8 @@ import com.intellij.psi.PsiFile
|
|||||||
import org.xml.sax.InputSource
|
import org.xml.sax.InputSource
|
||||||
import java.io.ByteArrayInputStream
|
import java.io.ByteArrayInputStream
|
||||||
import com.intellij.openapi.diagnostic.Log
|
import com.intellij.openapi.diagnostic.Log
|
||||||
|
import org.xml.sax.SAXException
|
||||||
|
import com.intellij.openapi.diagnostic.Logger
|
||||||
|
|
||||||
abstract class AndroidUIXmlParser {
|
abstract class AndroidUIXmlParser {
|
||||||
|
|
||||||
@@ -63,15 +65,22 @@ abstract class AndroidUIXmlParser {
|
|||||||
protected abstract val searchPath: String?
|
protected abstract val searchPath: String?
|
||||||
protected abstract val androidAppPackage: 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 val fileCache = HashMap<PsiFile, String>()
|
||||||
private var lastCachedPsi: JetFile? = null
|
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 val filesToProcess: Queue<PsiFile> = ConcurrentLinkedQueue()
|
||||||
protected var listenerSetUp: Boolean = false
|
protected var listenerSetUp: Boolean = false
|
||||||
protected volatile var invalidateCaches: Boolean = false
|
|
||||||
|
protected val LOG: Logger = Logger.getInstance(this.javaClass)
|
||||||
|
|
||||||
public fun parseToString(): String? {
|
public fun parseToString(): String? {
|
||||||
val cacheState = doParse()
|
val cacheState = doParse()
|
||||||
@@ -102,11 +111,6 @@ abstract class AndroidUIXmlParser {
|
|||||||
return file.extension == "xml"
|
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> {
|
private fun searchForUIXml(path: String): Collection<File> {
|
||||||
return searchForUIXml(arrayListOf(File(path)))
|
return searchForUIXml(arrayListOf(File(path)))
|
||||||
@@ -143,26 +147,17 @@ abstract class AndroidUIXmlParser {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun parseSingleFile(file: PsiFile): String {
|
private fun parseSingleFile(file: PsiFile): String {
|
||||||
val ids: MutableCollection<AndroidWidget> = ArrayList()
|
val res = parseSingleFileImpl(file)
|
||||||
val handler = AndroidXmlHandler({ id, wClass -> ids.add(AndroidWidget(id, wClass)) })
|
|
||||||
fileModificationTime[file] = file.getModificationStamp()
|
fileModificationTime[file] = file.getModificationStamp()
|
||||||
try {
|
fileCache[file] = res
|
||||||
val source = InputSource(ByteArrayInputStream(file.getText()!!.getBytes("utf-8")))
|
return res
|
||||||
saxParser.parse(source, handler)
|
|
||||||
val res = produceKotlinProperties(KotlinStringWriter(), ids).toString()
|
|
||||||
fileCache[file] = res
|
|
||||||
return res
|
|
||||||
} catch (e: Exception) {
|
|
||||||
Log.print(e.getMessage())
|
|
||||||
invalidateCaches()
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
abstract fun parseSingleFileImpl(file: PsiFile): String
|
||||||
|
|
||||||
private fun doParse(): CacheAction? {
|
private fun doParse(): CacheAction? {
|
||||||
if (searchPath == null || searchPath == "") return null
|
if (searchPath == null || searchPath == "") return null
|
||||||
lazySetup()
|
lazySetup()
|
||||||
if (invalidateCaches) invalidateCaches()
|
|
||||||
var overallCacheMiss = false
|
var overallCacheMiss = false
|
||||||
var file = filesToProcess.poll()
|
var file = filesToProcess.poll()
|
||||||
while (file != null) {
|
while (file != null) {
|
||||||
@@ -184,7 +179,6 @@ abstract class AndroidUIXmlParser {
|
|||||||
fileCache.clear()
|
fileCache.clear()
|
||||||
fileModificationTime.clear()
|
fileModificationTime.clear()
|
||||||
lastCachedPsi = null
|
lastCachedPsi = null
|
||||||
invalidateCaches = false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected fun populateQueue(project: Project) {
|
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) {
|
for (id in ids) {
|
||||||
val body = arrayListOf("return findViewById(0) as ${id.className}")
|
val body = arrayListOf("return findViewById(0) as ${id.className}")
|
||||||
kw.writeImmutableExtensionProperty(receiver = "Activity",
|
kw.writeImmutableExtensionProperty(receiver = "Activity",
|
||||||
|
|||||||
+19
@@ -19,14 +19,33 @@ package org.jetbrains.jet.lang.resolve.android
|
|||||||
import com.intellij.openapi.vfs.VirtualFileManager
|
import com.intellij.openapi.vfs.VirtualFileManager
|
||||||
import java.util.ArrayList
|
import java.util.ArrayList
|
||||||
import com.intellij.openapi.project.Project
|
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() {
|
class CliAndroidUIXmlParser(val project: Project, override val searchPath: String?): AndroidUIXmlParser() {
|
||||||
|
|
||||||
override var androidAppPackage: String = ""
|
override var androidAppPackage: String = ""
|
||||||
|
|
||||||
|
|
||||||
override fun lazySetup() {
|
override fun lazySetup() {
|
||||||
populateQueue(project)
|
populateQueue(project)
|
||||||
androidAppPackage = readManifest()._package
|
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 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
|
||||||
|
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() {
|
class IDEAndroidUIXmlParser(val project: Project): AndroidUIXmlParser() {
|
||||||
override val searchPath: String? = project.getBasePath() + "/res/layout/"
|
override val searchPath: String? = project.getBasePath() + "/res/layout/"
|
||||||
@@ -33,5 +36,11 @@ class IDEAndroidUIXmlParser(val project: Project): AndroidUIXmlParser() {
|
|||||||
populateQueue(project)
|
populateQueue(project)
|
||||||
listenerSetUp = true
|
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()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user