From 9899e78329a5cac47d8ffc4c09635ce1a4819491 Mon Sep 17 00:00:00 2001 From: dedoz Date: Sat, 26 Jul 2014 21:29:35 +0400 Subject: [PATCH] replace sax with psi based android ui xml parsing in plugin --- .../resolve/android/AndroidUIXmlParser.kt | 42 ++++++++---------- .../resolve/android/CliAndroidUIXmlParser.kt | 19 ++++++++ .../jet/plugin/android/AndroidXmlVisitor.kt | 43 +++++++++++++++++++ .../plugin/android/IDEAndroidUIXmlParser.kt | 9 ++++ 4 files changed, 89 insertions(+), 24 deletions(-) create mode 100644 idea/src/org/jetbrains/jet/plugin/android/AndroidXmlVisitor.kt 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 5f37504ee88..8d22e098e29 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 @@ -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() private var lastCachedPsi: JetFile? = null - private val fileModificationTime = HashMap() + protected val fileModificationTime: HashMap = HashMap() protected val filesToProcess: Queue = 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 { return searchForUIXml(arrayListOf(File(path))) @@ -143,26 +147,17 @@ abstract class AndroidUIXmlParser { } private fun parseSingleFile(file: PsiFile): String { - val ids: MutableCollection = 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): StringBuffer { + protected fun produceKotlinProperties(kw: KotlinStringWriter, ids: Collection): StringBuffer { for (id in ids) { val body = arrayListOf("return findViewById(0) as ${id.className}") kw.writeImmutableExtensionProperty(receiver = "Activity", 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 8b1c195208e..4dcb058c153 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 @@ -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 = 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 "" + } + } } diff --git a/idea/src/org/jetbrains/jet/plugin/android/AndroidXmlVisitor.kt b/idea/src/org/jetbrains/jet/plugin/android/AndroidXmlVisitor.kt new file mode 100644 index 00000000000..03b9ac33684 --- /dev/null +++ b/idea/src/org/jetbrains/jet/plugin/android/AndroidXmlVisitor.kt @@ -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) + } +} + diff --git a/idea/src/org/jetbrains/jet/plugin/android/IDEAndroidUIXmlParser.kt b/idea/src/org/jetbrains/jet/plugin/android/IDEAndroidUIXmlParser.kt index 30df9b02db8..96e9ad2646e 100644 --- a/idea/src/org/jetbrains/jet/plugin/android/IDEAndroidUIXmlParser.kt +++ b/idea/src/org/jetbrains/jet/plugin/android/IDEAndroidUIXmlParser.kt @@ -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 = ArrayList() + file.accept(AndroidXmlVisitor({ id, wClass -> ids.add(AndroidWidget(id, wClass))})) + return produceKotlinProperties(KotlinStringWriter(), ids).toString() + } }