上一篇我们讲解怎麽产生目标 parser 的 parse
方法,这篇来讲解 generator 的内部结构,这会用到上篇提到的 getParseFuncSpec
。我们的 generator 会先继承一个基底类别,叫做 ParserGenerator ,里面是包含一些 generator 的基础方法和流程。
const val METHOD_GET_ATTR_OR_NULL = "getAttributeOrNull"
const val METHOD_GET_ELEMENT_BY_TAG = "getElementByTag"
class KotlinParserGenerator(
private val element: Element,
private val isRoot: Boolean,
logger: Logger
) : ParserGenerator(logger) {
private val outputClass = ClassName(element.getPackage(), element.simpleName.toString())
private val exceptionClass = ClassName("java.lang", "IllegalStateException")
private val docBuilderFactoryClass = ClassName("javax.xml.parsers", "DocumentBuilderFactory")
private val elementClassName = ClassName("org.w3c.dom", "Element")
private val listClassName = ClassName("java.util", "ArrayList")
private val getAttributeOrNullMemberName = MemberName(extensionFullPath, METHOD_GET_ATTR_OR_NULL)
private val getElementByTagMemberName = MemberName(extensionFullPath, METHOD_GET_ELEMENT_BY_TAG)
override fun generate(): FileSpec {
val generatedClassName = "${element.simpleName}$PARSER_SUFFIX"
return FileSpec.builder(GENERATOR_PACKAGE, generatedClassName)
.addType(getObjectTypeSpec(generatedClassName))
.build()
}
private fun getObjectTypeSpec(className: String): TypeSpec {
val builder = TypeSpec.objectBuilder(className)
val outputClassName = element.simpleName.toString()
if (isRoot) {
builder.addFunction(getParseFuncSpec())
}
return builder
.addFunction(getClassFunSpec(element, outputClassName, builder))
.build()
}
private fun getParseFuncSpec(): FunSpec {
// 略
}
private fun getClassFunSpec(
rootElement: Element,
outputClassName: String,
objectBuilder: TypeSpec.Builder
): FunSpec {
// 略
}
}
在 annotation processor 侦测到 annotation 元素时,就会呼叫 parser generator 帮它产生对应的程序码,只要放入相对应的 Element
就可以,而在建构仔我们看到的 isRoot
指的是这个 element 是不是 channel tag 。之後,generator 的 generate
方法就会被呼叫,把用 KotlinPoet 产生的程序码写入档案中。上方程序码里的 getClassFunSpec
就是用来针对每个被标注 @RssTag
的类别产生对应的 parser 程序码,也是本篇的重点。要怎麽针对它产生对应的 parser 程序码?我们先想想产生出来的程序码应该要长怎麽样。假设我们有一个 data class RssItem
。
@RssTag(name = "item")
data class RssItem(
val title: String?,
val author: String?,
val guid: TestGuid?
): Serializable
@RssTag(name = "guid")
data class TestGuid(
@RssAttribute
val isPermaLink: Boolean?
): Serializable
那它产生出来的程序码,预计要长成这样:
object RssItemParser {
fun Element.getItem(): RssItem {
// #1 Value Statement
val titleTitle: String? = readString("title")
val titleItunesTitle: String? = readString("itunes:title")
val titleGoogleplayTitle: String? = readString("googleplay:title")
val authorAuthor: String? = readString("author")
val authorItunesAuthor: String? = readString("itunes:author")
val authorGoogleplayAuthor: String? = readString("googleplay:author")
val guidGuid: TestGuid? = getElementByTag("guid")?.getGuid()
val guidItunesGuid: TestGuid? = getElementByTag("itunes:guid")?.getGuid()
val guidGoogleplayGuid: TestGuid? = getElementByTag("googleplay:guid")?.getGuid()
// #2 Use class constructor
return RssItem(
title = titleTitle ?: titleItunesTitle ?: titleGoogleplayTitle,
author = authorAuthor ?: authorItunesAuthor ?: authorGoogleplayAuthor,
guid = guidGuid ?: guidItunesGuid ?: guidGoogleplayGuid
)
}
}
为了要产生上面的程序码,我们可以把步骤拆成四个:
ParseData
ParseData
整理好的资讯来产生 value 宣告的程序码。(上面程序码标注 #1 的部分)下篇文章我会各别把这四个步骤用程序码来讲解他们的实作。
<<: AI ninja project [day 25] QLattice -- 基础分类
前言 工作了好一段时间後,直到那次处理了OOM(Out Of Memory)问题,才发现JDK内有很...
全球有超过 42% 的网站使用 WordPress 架设,WordPress 适合架设部落格、小型企...
除了监听事件外,jQuery也提供了定义好的动态效果函式,让开发者直接使用,并透过传入相关参数,去自...
Colab连结 虽然 Tensorflow 提供了几个预训练模型让我们可以很快的完成训练任务,但是有...
Linux 系列的主流发行版主要分为Red Hat Linux(包含CentOS和RHEL)和Ubu...