在 compile time 的时候,不像我们一般再开发的时候很容易的去 log 一些我们要的资讯,这边我们必须要透过 processor 提供的 Messager
才能进行 log ,这个 log 的内容会显示在 IDE 的 build console 内。
我们也可以自己用一个 class 去封 Messager
,像是这样:
class Logger(private val log: Messager) {
fun log(msg: String) {
log.printMessage(Diagnostic.Kind.NOTE, "$msg\r\n")
}
fun warn(msg: String) {
log.printMessage(Diagnostic.Kind.WARNING, "$msg\r\n")
}
fun error(msg: String, element: Element) {
log.printMessage(Diagnostic.Kind.ERROR, "$msg\r\n", element)
}
}
之後就可以直接使用方法来 log 不同层级的资讯!
Extension 是帮助我们在产生 parser 程序码的过程中会用到的一些工具,我们可以从前几篇在实作 DOM parser 的时候,有用到的东西去做规划。
readString
getAttribute
getElementByTag
toBoolean
於是我们可以知道我们想要产生的类别大概长怎样,我们就可以先在编辑器先写上想产生的类别:
object ParserExtensions {
fun Element.readString(name: String, parentTag: String? = null): String? {
val nodeList = getElementsByTagName(name)
if (parentTag == null) {
return nodeList.item(0)?.textContent
} else {
for (i in 0 until nodeList.length) {
val e = nodeList.item(i) as? Element ?: continue
val parent = e.parentNode as? Element
if (parent?.tagName != parentTag) continue
return e.textContent
}
return null
}
}
fun Element.getAttributeOrNull(tag: String): String? {
val attr = getAttribute(tag) ?: return null
return if (attr.isEmpty() || attr.isBlank()) null else attr
}
fun Element.getElementByTag(tag: String): Element? {
val nodeList = getElementsByTagName(tag)
if (nodeList.length == 0) return null
return nodeList.item(0) as? Element
}
fun String.toBooleanOrNull(): Boolean? = when (toLowerCase()) {
"true", "yes" -> true
"no", "false" -> false
else -> null
}
}
程序码的细节可以参考前几篇的 "使用 DOM Parser" ,里面有针对 Element
的介绍,这边就不再赘述。接下来就可以勾勒出 KotlinExtensionGenerator
的大概结构:
class KotlinExtensionGenerator(
private val logger: Logger
) : ExtensionGenerator() {
private val elementClass = ClassName("org.w3c.dom", "Element")
override fun generate(): FileSpec {
logger.log("Generating $EXTENSION_NAME for Kotlin.")
return FileSpec.builder(GENERATOR_PACKAGE,EXTENSION_NAME)
.addType(
TypeSpec.objectBuilder(EXTENSION_NAME)
.addFunction(getReadStringFunSpec())
.addFunction(getAttributeOrNullFunSpec())
.addFunction(getElementByTagFunSpec())
.addFunction(getBooleanConversionFunSpec())
.build()
)
.build()
}
private fun getReadStringFunSpec() {
// 略
}
private fun getAttributeOrNullFunSpec() {
// 略
}
private fun getElementByTagFunSpec() {
// 略
}
}
generate 的方法里主要定义了 ParserExtension
会有几个方法与它的型态,这边是定义成 object 。里面的 fun spec 就比较没什麽特别的,都可以用简单的 KotlinPoet 语法完成,如果有兴趣的朋友可以参考这边。这个类别还有一个 getBooleanConversionFunSpec
还没有被提到,因为它是父类别 ExtensionGenerator
的方法,主要是产生 boolean 值转换的方法,对应到 ParserExtension
里的 toBooleanOrNull
方法。这个抽象类别同时也被 ExtensionGenerator
继承。
abstract class ExtensionGenerator : Generator {
protected fun getBooleanConversionFunSpec() = FunSpec.builder(METHOD_TO_BOOLEAN)
.receiver(String::class)
.addCode(
"""
|return when (toLowerCase()) {
|${TAB}"true", "yes" -> true
|${TAB}"no", "false" -> false
|${TAB}else -> null
|}
""".trimMargin()
)
.returns(Boolean::class.asTypeName().copy(nullable = true))
.build()
}
实作到这里,我们可以产生一些 extension 在 parser 的 generator 里面可以用,下篇我们会提到以 DOM parser 为基底产生的 parser 。
微软也做游戏机!这种在现在听来理所当然的事情、发生在 20 年前、其实还是让人感到挺震惊的。而当时诞...
前言 「眼前的黑不是黑,你说的白是什麽白」-- 你是我的眼(萧煌奇) 没错,并非所有图片都是在理想的...
DAY04 建立 Datastore 和 Dataset (下) 今天我们就要把昨天建立好的 dat...
昨天我们提到了该如何撰写 JSX,今天就来学习怎麽建立元件,来把同类型的 React element...
前言 在之前我有写一篇关於资料库的ACID分享RDBMS资料库基本原则 假如我们系统是一个多执行续高...