Name synthetic files after layout names

This commit is contained in:
Yan Zhulanow
2015-04-01 22:35:26 +03:00
parent e6fa6e9a5d
commit b4cfacbac7
36 changed files with 75 additions and 48 deletions
@@ -22,7 +22,9 @@ import org.jetbrains.kotlin.lexer.JetTokens
public object AndroidConst {
val ANDROID_USER_PACKAGE: Key<String> = Key.create<String>("ANDROID_USER_PACKAGE")
val SYNTHETIC_FILENAME: String = "ANDROIDXML"
val SYNTHETIC_FILENAME_PREFIX: String = "ANDROIDXML_"
val LAYOUT_POSTFIX: String = "_LAYOUT"
val VIEW_LAYOUT_POSTFIX: String = "_VIEW"
val SYNTHETIC_PACKAGE: String = "kotlinx.android.synthetic"
val SYNTHETIC_PACKAGE_PATH_LENGTH = SYNTHETIC_PACKAGE.count { it == '.' } + 1
@@ -54,6 +54,8 @@ import kotlin.properties.*
import com.intellij.psi.impl.*
import org.jetbrains.kotlin.types.Flexibility
public class AndroidSyntheticFile(val name: String, val contents: String)
public abstract class AndroidUIXmlProcessor(protected val project: Project) {
public class NoAndroidManifestFound : Exception("No android manifest file found in project root")
@@ -62,15 +64,16 @@ public abstract class AndroidUIXmlProcessor(protected val project: Project) {
public abstract val resourceManager: AndroidResourceManager
protected abstract val cachedSources: CachedValue<List<String>>
protected abstract val cachedSources: CachedValue<List<AndroidSyntheticFile>>
private val cachedJetFiles: CachedValue<List<JetFile>> by Delegates.lazy {
cachedValue {
val psiManager = PsiManager.getInstance(project)
val applicationPackage = resourceManager.androidModuleInfo?.applicationPackage
val jetFiles = cachedSources.getValue().mapIndexed { index, text ->
val virtualFile = LightVirtualFile(AndroidConst.SYNTHETIC_FILENAME + index + ".kt", text)
val jetFiles = cachedSources.getValue().mapIndexed { index, syntheticFile ->
val fileName = AndroidConst.SYNTHETIC_FILENAME_PREFIX + syntheticFile.name + ".kt"
val virtualFile = LightVirtualFile(fileName, syntheticFile.contents)
val jetFile = psiManager.findFile(virtualFile) as JetFile
if (applicationPackage != null) {
jetFile.putUserData(AndroidConst.ANDROID_USER_PACKAGE, applicationPackage)
@@ -82,10 +85,14 @@ public abstract class AndroidUIXmlProcessor(protected val project: Project) {
}
}
public fun parse(generateCommonFiles: Boolean = true): List<String> {
public fun parse(generateCommonFiles: Boolean = true): List<AndroidSyntheticFile> {
val commonFiles = if (generateCommonFiles) {
val clearCacheFile = renderLayoutFile(AndroidConst.SYNTHETIC_PACKAGE) {} +
renderClearCacheFunction("android.app.Activity") + renderClearCacheFunction("android.app.Fragment")
val clearCacheFile = renderSyntheticFile("clearCache") {
writePackage(AndroidConst.SYNTHETIC_PACKAGE)
writeAndroidImports()
writeClearCacheFunction("android.app.Activity")
writeClearCacheFunction("android.app.Fragment")
}
listOf(clearCacheFile,
FLEXIBLE_TYPE_FILE,
@@ -98,14 +105,15 @@ public abstract class AndroidUIXmlProcessor(protected val project: Project) {
val files = entry.getValue()
val widgets = parseLayout(files)
val layoutPackage = files[0].genSyntheticPackageName()
val layoutName = files[0].getName().substringBefore('.')
val packageName = AndroidConst.SYNTHETIC_PACKAGE + "." + files[0].getEscapedLayoutName()
val mainLayoutFile = renderLayoutFile(layoutPackage, widgets) {
val mainLayoutFile = renderLayoutFile(layoutName + AndroidConst.LAYOUT_POSTFIX, packageName, widgets) {
writeSyntheticProperty("android.app.Activity", it, "findViewById(0)")
writeSyntheticProperty("android.app.Fragment", it, "getView().findViewById(0)")
}
val viewLayoutFile = renderLayoutFile("$layoutPackage.view", widgets) {
val viewLayoutFile = renderLayoutFile(layoutName + AndroidConst.VIEW_LAYOUT_POSTFIX, "$packageName.view", widgets) {
writeSyntheticProperty("android.view.View", it, "findViewById(0)")
}
@@ -118,16 +126,23 @@ public abstract class AndroidUIXmlProcessor(protected val project: Project) {
protected abstract fun parseLayout(files: List<PsiFile>): List<AndroidWidget>
private fun renderLayoutFile(
name: String,
packageName: String,
widgets: List<AndroidWidget> = listOf(),
widgetWriter: KotlinStringWriter.(AndroidWidget) -> Unit
): String {
): AndroidSyntheticFile {
val stringWriter = KotlinStringWriter()
stringWriter.writePackage(packageName)
stringWriter.writeAndroidImports()
widgets.forEach { stringWriter.widgetWriter(it) }
return stringWriter.toStringBuffer().toString()
return AndroidSyntheticFile(name, stringWriter.toStringBuffer().toString())
}
private fun renderSyntheticFile(filename: String, init: KotlinStringWriter.() -> Unit): AndroidSyntheticFile {
val stringWriter = KotlinStringWriter()
stringWriter.init()
return AndroidSyntheticFile(filename, stringWriter.toStringBuffer().toString())
}
private fun KotlinStringWriter.writeAndroidImports() {
@@ -135,8 +150,8 @@ public abstract class AndroidUIXmlProcessor(protected val project: Project) {
writeEmptyLine()
}
private fun PsiFile.genSyntheticPackageName(): String {
return AndroidConst.SYNTHETIC_PACKAGE + "." + escapeAndroidIdentifier(getName().substringBefore('.'))
private fun PsiFile.getEscapedLayoutName(): String {
return escapeAndroidIdentifier(getName().substringBefore('.'))
}
private fun KotlinStringWriter.writeSyntheticProperty(receiver: String, widget: AndroidWidget, stubCall: String) {
@@ -149,7 +164,9 @@ public abstract class AndroidUIXmlProcessor(protected val project: Project) {
getterBody = body)
}
private fun renderClearCacheFunction(receiver: String) = "public fun $receiver.${AndroidConst.CLEAR_FUNCTION_NAME}() {}\n"
private fun KotlinStringWriter.writeClearCacheFunction(receiver: String) {
writeText("public fun $receiver.${AndroidConst.CLEAR_FUNCTION_NAME}() {}\n")
}
protected fun <T> cachedValue(result: () -> CachedValueProvider.Result<T>): CachedValue<T> {
return CachedValuesManager.getManager(project).createCachedValue(result, false)
@@ -187,13 +204,14 @@ public abstract class AndroidUIXmlProcessor(protected val project: Project) {
"android.support.v4.widget.*",
Flexibility.FLEXIBLE_TYPE_CLASSIFIER.asSingleFqName().asString())
private val FLEXIBLE_TYPE_FILE = "package $EXPLICIT_FLEXIBLE_PACKAGE\n\nclass $EXPLICIT_FLEXIBLE_CLASS_NAME<L, U>"
private val FLEXIBLE_TYPE_FILE =
AndroidSyntheticFile("ft", "package $EXPLICIT_FLEXIBLE_PACKAGE\n\nclass $EXPLICIT_FLEXIBLE_CLASS_NAME<L, U>")
private val FAKE_SUPPORT_V4_WIDGET_FILE = "package android.support.v4.widget"
private val FAKE_SUPPORT_V4_WIDGET_FILE = AndroidSyntheticFile("supportv4_widget", "package android.support.v4.widget")
private val FAKE_SUPPORT_V4_VIEW_FILE = "package android.support.v4.view"
private val FAKE_SUPPORT_V4_VIEW_FILE = AndroidSyntheticFile("supportv4_view", "package android.support.v4.view")
private val FAKE_SUPPORT_V4_APP_FILE = "package android.support.v4.app"
private val FAKE_SUPPORT_V4_APP_FILE = AndroidSyntheticFile("supportv4_app", "package android.support.v4.app")
}
}
@@ -37,7 +37,7 @@ public class CliAndroidUIXmlProcessor(
CliAndroidResourceManager(project, manifestPath, mainResDirectory)
}
override val cachedSources: CachedValue<List<String>> by Delegates.lazy {
override val cachedSources: CachedValue<List<AndroidSyntheticFile>> by Delegates.lazy {
cachedValue {
Result.create(parse(), ModificationTracker.NEVER_CHANGED)
}
@@ -69,6 +69,10 @@ class KotlinStringWriter : KotlinWriter {
body.newLine()
}
fun writeText(text: String) {
body.writeln(text)
}
override fun toStringBuffer(): StringBuffer {
ctx.absorbChildren()
return ctx.buffer
@@ -1,4 +1,4 @@
package kotlinx.android.synthetic.layout
package kotlinx.android.synthetic.test
import android.app.*
import android.view.*
@@ -1,4 +1,4 @@
package kotlinx.android.synthetic.layout.view
package kotlinx.android.synthetic.test.view
import android.app.*
import android.view.*
@@ -1,4 +1,4 @@
package kotlinx.android.synthetic.layout
package kotlinx.android.synthetic.test
import android.app.*
import android.view.*
@@ -1,4 +1,4 @@
package kotlinx.android.synthetic.layout.view
package kotlinx.android.synthetic.test.view
import android.app.*
import android.view.*
@@ -1,4 +1,4 @@
package kotlinx.android.synthetic.layout
package kotlinx.android.synthetic.test
import android.app.*
import android.view.*
@@ -1,4 +1,4 @@
package kotlinx.android.synthetic.layout.view
package kotlinx.android.synthetic.test.view
import android.app.*
import android.view.*
@@ -1,4 +1,4 @@
package kotlinx.android.synthetic.layout1
package kotlinx.android.synthetic.test1
import android.app.*
import android.view.*
@@ -1,4 +1,4 @@
package kotlinx.android.synthetic.layout1.view
package kotlinx.android.synthetic.test1.view
import android.app.*
import android.view.*
@@ -1,4 +1,4 @@
package kotlinx.android.synthetic.layout
package kotlinx.android.synthetic.test
import android.app.*
import android.view.*
@@ -1,4 +1,4 @@
package kotlinx.android.synthetic.layout.view
package kotlinx.android.synthetic.test.view
import android.app.*
import android.view.*
@@ -1,4 +1,4 @@
package kotlinx.android.synthetic.layout
package kotlinx.android.synthetic.test
import android.app.*
import android.view.*
@@ -1,4 +1,4 @@
package kotlinx.android.synthetic.layout.view
package kotlinx.android.synthetic.test.view
import android.app.*
import android.view.*
@@ -1,4 +1,4 @@
package kotlinx.android.synthetic.layout
package kotlinx.android.synthetic.test
import android.app.*
import android.view.*
@@ -1,4 +1,4 @@
package kotlinx.android.synthetic.layout.view
package kotlinx.android.synthetic.test.view
import android.app.*
import android.view.*
@@ -1,4 +1,4 @@
package kotlinx.android.synthetic.layout
package kotlinx.android.synthetic.test
import android.app.*
import android.view.*
@@ -1,4 +1,4 @@
package kotlinx.android.synthetic.layout.view
package kotlinx.android.synthetic.test.view
import android.app.*
import android.view.*
@@ -1,4 +1,4 @@
package kotlinx.android.synthetic.layout
package kotlinx.android.synthetic.test
import android.app.*
import android.view.*
@@ -1,4 +1,4 @@
package kotlinx.android.synthetic.layout.view
package kotlinx.android.synthetic.test.view
import android.app.*
import android.view.*
@@ -23,6 +23,7 @@ import org.jetbrains.kotlin.test.JetTestUtils
import org.jetbrains.kotlin.test.ConfigurationKind
import org.jetbrains.kotlin.test.TestJdkKind
import org.jetbrains.kotlin.cli.jvm.compiler.EnvironmentConfigFiles
import org.jetbrains.kotlin.lang.resolve.android.AndroidConst
import org.jetbrains.kotlin.lang.resolve.android.CliAndroidUIXmlProcessor
import org.jetbrains.kotlin.lang.resolve.android.AndroidUIXmlProcessor
import kotlin.test.*
@@ -30,19 +31,21 @@ import kotlin.test.*
public abstract class AbstractAndroidXml2KConversionTest : UsefulTestCase() {
public fun doTest(path: String) {
val jetCoreEnvironment = getEnvironment(path)
val jetCoreEnvironment = getEnvironment()
val parser = CliAndroidUIXmlProcessor(jetCoreEnvironment.project, path + "AndroidManifest.xml", path + "/res")
val actual = parser.parse(false)
val actual = parser.parse(false).toMap { it.name }
val layoutFiles = File(path).listFiles {
it.isFile() && it.name.startsWith("layout") && it.name.endsWith(".kt")
}?.sortBy { it.name } ?: listOf()
val expectedLayoutFiles = File(path).listFiles {
it.isFile() && it.name.endsWith(".kt")
}?.toMap { it.name.substringBefore(".kt") } ?: mapOf()
assertEquals(layoutFiles.size(), actual.size())
assertEquals(expectedLayoutFiles.size(), actual.size())
for ((index, file) in layoutFiles.withIndex()) {
JetTestUtils.assertEqualsToFile(file, actual[index])
for ((name, file) in expectedLayoutFiles) {
val actualContents = actual[name]
assertNotNull(actualContents, "File $name was not generated")
JetTestUtils.assertEqualsToFile(file, actualContents!!.contents)
}
}
@@ -55,7 +58,7 @@ public abstract class AbstractAndroidXml2KConversionTest : UsefulTestCase() {
}
}
private fun getEnvironment(testPath: String): KotlinCoreEnvironment {
private fun getEnvironment(): KotlinCoreEnvironment {
val configuration = JetTestUtils.compilerConfigurationForTests(ConfigurationKind.ALL, TestJdkKind.MOCK_JDK)
return KotlinCoreEnvironment.createForTests(getTestRootDisposable()!!, configuration, EnvironmentConfigFiles.JVM_CONFIG_FILES)
}
@@ -35,7 +35,7 @@ class IDEAndroidUIXmlProcessor(val module: Module) : AndroidUIXmlProcessor(modul
module.getProject().getExtensions(PsiTreeChangePreprocessor.EP_NAME).first { it is AndroidPsiTreeChangePreprocessor }
}
override val cachedSources: CachedValue<List<String>> by Delegates.lazy {
override val cachedSources: CachedValue<List<AndroidSyntheticFile>> by Delegates.lazy {
cachedValue {
Result.create(parse(), psiTreeChangePreprocessor)
}