Java代碼分析器(三): 以強大的屬性描述符寫出通用代碼

另載於 http://www.qingjingjie.com/blogs/4java

上篇介紹的形形色色的語法元素大概讓人眼花繚亂了,並且每種元素都對應一個Java類。知道是一回事,使用就是另外一回事了,這麼多個類,要給每一個類寫對應的處理代碼,不勝其煩。ASTVisitor雖然能自動遍歷語法樹,可是並不能幫你處理每一種結點。git

好在JDT提供了更加抽象的屬性描述符(property descriptor),寥寥幾個類就能掌控全部Java語法。用術語來講,上篇的那些類屬於異構AST,本篇講的是同構AST。github

對任何AST結點均可調用方法structuralPropertiesForType(),你會獲得List<StructuralPropertyDescriptor>,其中每一項都表明這個結點所屬的類的一個結構性字段(就是跟AST有關的字段)。json

StructuralPropertyDescriptor 是一個抽象類,有三個子類:SimplePropertyDescriptor, ChildPropertyDescriptor, ChildListPropertyDescriptor。這些東西是元數據,用來描述各類語法元素的固有結構,使用它們有種在用Java反射的感受。scala

SimplePropertyDescriptor 表示這個字段存放的不是AST結點,而是個值,多是int, String,Operator之類的,SimplePropertyDescriptor.valueType 能告訴咱們這個值是什麼類型。code

ChildPropertyDescriptor 表示這個字段存放的是一個AST結點,好比咱們解析了一個class,獲得typeDeclaration結點,而後調用typeDeclaration.structuralPropertiesForType(),獲得的list中有一項就是typeName的描述符,嗯,就是AbstractTypeDeclaration類的typeName字段,字段類型爲SimpleName。blog

ChildListPropertyDescriptor 表示這個字段存放的是一組AST結點! 好比AbstractTypeDeclaration擁有一組bodyDeclarations,而CompilationUnit則擁有一組imports。bodyDeclarations和imports都是List!ip

有了描述符能作什麼呢? 能夠自由訪問一棵語法樹了。get

咱們來想象一個流程:你有一個java文件,你把它交給JDT的parser,解析出一個CompilationUnit cu,也就是一棵語法樹的根結點。調用cu.structuralPropertiesForType(),獲得描述符的list,循環遍歷list,對每一個描述符prop,用instanceof判斷具體類型(總共就3個類型),分別作"不一樣處理"。it

不一樣處理:instanceof操做發現某個描述符是ChildListPropertyDescriptor, 因而你把描述符強轉(cast)成該類型,調用prop.getId()獲得"imports",哦,是imports字段啊,調用prop.getElementType()獲得ImportDeclaration.class,確認了這一發現。而後你調用cu.getStructuralProperty(prop)獲得一個object,你知道它實際是List<ImportDeclaration>,所以你將它強轉爲這個List類型,遍歷它,對每一個ImportDeclaration,調用getName().getFullyQualifiedName(),就獲得了每一個import的名稱。(固然,對ImportDeclaration也能夠僞裝不知道其類型,也用元數據來操控之)

由此你就完成了一個分析流程。由於不用關心具體的結點類型,因此你能夠方便地進行一些宏觀、抽象的分析。

最後提供一段我用Scala寫的代碼供參考(50行就能把任意Java代碼結構轉換成JSON輸出, 使用了lift json庫):
https://github.com/sorra/Lanka/blob/fa52cdaa2f94aadfcc29f8be2711a88da3c8cbb3/src/sorra/lanka/json/MetaConversion.scala利用強大的屬性描述符,寫出通用的JSON轉換代碼,避免了給每一個結點類寫對應的JSON轉換代碼(幾十種結點類,要死啊)。

相關文章
相關標籤/搜索