[go: nahoru, domu]

blob: 0b784c2ef49f1199c45c691494f40003c08381ae [file] [log] [blame]
/*
* Copyright 2020 The Android Open Source Project
*
* 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 androidx.ui.material.icons.generator
import com.squareup.kotlinpoet.CodeBlock
import com.squareup.kotlinpoet.FileSpec
import com.squareup.kotlinpoet.FunSpec
import com.squareup.kotlinpoet.ParameterizedTypeName.Companion.parameterizedBy
import com.squareup.kotlinpoet.PropertySpec
import com.squareup.kotlinpoet.asClassName
import com.squareup.kotlinpoet.asTypeName
import com.squareup.kotlinpoet.buildCodeBlock
import java.io.File
import kotlin.reflect.KProperty0
/**
* Generates a list named `AllIcons` that contains pairs mapping a [KProperty0] of the generated
* icon to the name of the corresponding XML drawable. This is used so we can run tests comparing
* the generated icon against the original source drawable.
*
* @property icons the list of [Icon]s to generate the manifest from
*/
class IconTestingManifestGenerator(private val icons: List<Icon>) {
/**
* Generates the list and writes it to [outputSrcDirectory].
*/
fun generateTo(outputSrcDirectory: File) {
val propertyNames: MutableList<String> = mutableListOf()
// Split up this list by themes, otherwise we get a Method too large exception.
// We will then generate another file that returns the result of concatenating the list
// for each theme.
icons
.groupBy { it.theme }
.map { (theme, icons) ->
val propertyName = "${theme.themeClassName}Icons"
propertyNames += propertyName
theme to generateListOfIconsForTheme(propertyName, theme, icons)
}
.forEach { (theme, fileSpec) ->
// KotlinPoet bans wildcard imports, and we run into class compilation errors
// (too large a file?) if we add all the imports individually, so let's just add
// the imports to each file manually.
val wildcardImport = "import androidx.ui.material.icons.${theme.themePackageName}.*"
fileSpec.writeToWithCopyright(outputSrcDirectory) { fileContent ->
fileContent.replace(
"import androidx.ui.material.icons.Icons",
"import androidx.ui.material.icons.Icons\n$wildcardImport"
)
}
}
val mainGetter = FunSpec.getterBuilder()
.addStatement("return " + propertyNames.joinToString(" + "))
.build()
FileSpec.builder("androidx.ui.material.icons.test", "AllIcons")
.addProperty(
PropertySpec.builder("AllIcons", type = listOfIconsType)
.getter(mainGetter)
.build()
).setIndent().build().writeToWithCopyright(outputSrcDirectory)
}
}
/**
* Generates a Kotlin file with a list containing all icons of the given [theme].
*
* @param propertyName the name of the top level property that we should generate the list under
* @param theme the theme that we are generating the file for
* @param allIcons a list containing all icons that we will filter to match [theme]
*/
private fun generateListOfIconsForTheme(
propertyName: String,
theme: IconTheme,
allIcons: List<Icon>
): FileSpec {
val icons = allIcons.filter { it.theme == theme }
val iconStatements = icons.toStatements()
return FileSpec.builder(PackageNames.MaterialIconsTestPackage.packageName, propertyName)
.addProperty(
PropertySpec.builder(propertyName, type = listOfIconsType)
.initializer(buildCodeBlock {
addStatement("listOf(")
indent()
iconStatements.forEach { add(it) }
unindent()
addStatement(")")
})
.build()
).setIndent().build()
}
/**
* @return a list of [CodeBlock] representing all the statements for the body of the list.
* For example, one statement would look like `(Icons.Filled::Menu) to menu`.
*/
private fun List<Icon>.toStatements(): List<CodeBlock> {
return mapIndexed { index, icon ->
buildCodeBlock {
val iconFunctionReference = "(%T.${icon.theme.themeClassName}::${icon.kotlinName})"
val text = "$iconFunctionReference to \"${icon.xmlFileName}\""
addStatement(if (index != size - 1) "$text," else text, ClassNames.Icons)
}
}
}
private val kPropertyType =
(KProperty0::class).asClassName().parameterizedBy(ClassNames.VectorAsset)
private val pairType = (Pair::class).asClassName().parameterizedBy(
kPropertyType,
(String::class).asTypeName()
)
private val listOfIconsType = (List::class).asClassName().parameterizedBy(pairType)