怎樣分析 Java 代碼以進行修改? JDT 提供了幾個工具來幫助您分析代碼
本文有意選擇了最簡單的 IScanner 接口進行演示
它的作用域也最有限
這個接口屬於 JDT 工具箱
可以通過 JDT 的 ToolFactory 類訪問它
其 createScanner 方法返回一個掃描程序
該掃描程序會簡化對一串 Java 代碼作標記的工作
它不處理任何特別困難的操作
只是對所返回的標記進行簡單的解析和分類
例如
它指出下一個標記是 public 關鍵字
其後的標記是一個標識符
再後面的標記是左圓括號
等等
隨後
只有當您希望分析一小段代碼(您明確理解想要在這段代碼中得到什麼)時
這個掃描程序才是合適的
您決不會使用掃描程序分析整個 Java 源代碼
因為您會轉而使用一些對編譯器迷而言十分熟悉的工具
JDT 的抽象語法樹(Abstract Syntax Tree
AST)框架
與簡單的掃描程序不同
AST 理解語言元素(它們不再只是
標記
)之間的關系
它可以識別象局部變量
實例變量
表達式以及 if 語句等六十多種不同的語言元素
它將幫助您進行涉及范圍廣泛的重構
或難以滿足對標記進行一對一分類的模糊程度特別高的重構
要更清晰地了解何時使用掃描程序與何時使用 AST 之間的差別
請考慮清單
中的代碼
清單 模糊的變量引用 public class Foo {
int foo =
;
public int foo(int foo) {
return foo + this
foo;
}
public int getFoo() {
return foo;
}
}
如果作為重構的一部分
您希望查找對實例變量 foo 的引用
那麼就會明白一個單純的解析會使區分本地引用和實例變量引用成為一個難題
AST 創建了完整的分析樹
其中表示了 Java 源代碼的每個元素並對這些元素進行了區分
在這個特例中
不同的類會考慮
foo
引用的上下文
將
foo
引用表示成 AST 的節點(如 FieldDeclaration
SimpleName 和 ThisExpression)
因此您會很輕松地識別它們
正如前面提到的
本文將只討論我們所選擇的簡單例子
對於比較復雜的修改和分析示例
請參閱參考資料一節
現在
讓我們回到我們前面跳過的用省略號表示的代碼
這個代碼將使用 IScanner 的實例以確定並替換源代碼中確定成員可視性的關鍵字
我們將處理的可視性修飾符是 public
private
protected 和 final
通過采用
蠻力
方法
我們可以簡化這個解決方案
即
采用兩個步驟就可以完成
首先刪除方法特征符中所有的可視性修飾符(或至少掃描查找它們
如果找到
就刪除)
然後插入所希望的修飾符
特別地
如果在方法特征符中找到 public
private 或 protected
就刪除它們
插入所請求的可視性修飾符(對於包可視性的情況
不作任何操作
因為這是缺省操作
即沒有任何修飾符)
final 修飾符很簡單
因為所希望的行為就是插入和除去這個修飾符
所以如果它存在
我們除去它
否則就插入它
清單
中的代碼只顯示了一個例子
它無條件地將成員的可視性從 pubilc 改成 private
在與本文相關的解決方案中
您將看到每個操作的公共代碼都被移到了抽象超類中
它基本上與下面的代碼相同
只不過稍作了整理以避免冗余
清單 掃描是否有 pubilc 關鍵字 ICompilationUnit cu = member
getCompilationUnit();
if (cu
isWorkingCopy()) {
IBuffer buffer = cu
getBuffer();
IScanner scanner =
ToolFactory
createScanner(false
false
false
false);
scanner
setSource(buffer
getCharacters());
ISourceRange sr = member
getSourceRange();
scanner
resetTo(
sr
getOffset()
sr
getOffset() + sr
getLength()
);
int token = scanner
getNextToken();
while (token != ITerminalSymbols
TokenNameEOF
&& token != ITerminalSymbols
TokenNameLPAREN)
token = scanner
getNextToken();
if (token == ITerminalSymbols
TokenNamePUBLIC) {
buffer
replace(
scanner
getCurrentTokenStartPosition()
scanner
getCurrentTokenEndPosition()
scanner
getCurrentTokenStartPosition() +
private
);
break;
}
}
cu
reconcile();
}
注
ITerminalSymbols 定義了掃描程序可以返回的標記名稱
它們對應於 Java 語法的標准標記
您可以進一步查詢掃描程序以詢問當前標記在緩沖區中開始和結束的具體位置
它出現在哪一行上
當然還有標記本身(特別是象 ITerminalSymbols
TokenNameStringLiteral 和 ITerminalSymbols
TokenNameIdentifier 這樣的例子
它們不是保留的關鍵字)
上述代碼片段中
向 scanner
setSource 方法提供了編譯單元的完整源代碼
即 Java 源文件中的所有內容
正如前面提到的
掃描程序並不非常適合於大型分析
所以我們必須將它限制用於只有以目標方法的第一個字符開始
一直到調用 setSourceRange 方法作為結束的那部分源代碼
IMember 接口繼承了 ISourceReference
ISourceReference 是一個允許您查詢包含編譯單元內的源代碼字符串和源代碼位置的接口
這使我們不必確定目標方法在 Java 源代碼內開始和結束的位置
原本可以用 AST 實現這一點
而 ISourceReference 接口使 AST 成了多余的工具
由於 Java 方法特征符易於解析
所以 IScanner 接口的解析能力和它很匹配
我們必須做的就是查找 public 關鍵字
它出現在方法聲明的前一個字符之後
參數聲明的左圓括號之前
用 private 關鍵字替換它
當然
在該解決方案中
這個接口將處理所有的可能情況
不管該方法最初是 public
private
protected 還是 package(缺省)
下一步是什麼? 本文設定的目標是向您提供一個對 Eclipse 的 Java 開發環境頗具價值的擴展
這樣的擴展增強了這個開發環境的生產率
坦率地說
出於簡潔性考慮
我多次跳過了一些細節
該解決方案本身就作了一些簡化假設
象只允許在編輯器中對已打開的 Java 源代碼進行修改
您可能希望在更完整的實現中取消這個限制
雖然如此
但我還是希望您能感受到什麼是可能的
並確信這樣做不是特別困難
本文中我們討論的是 The Java Developer
s Guide to Eclipse 一書某一高級章節的部分內容
該書中有十一個比較淺顯的章節討論了插件開發的基礎
象本文一樣
大多數章節都包含了一個已文檔化的工作解決方案
它可以強化您所學到的知識
大多數內容是以本文中您已看到的相同風格編寫的(不過可能沒有以這麼快的節奏進行討論!)
重要
您可能需要向工作空間添加必要的插件
這樣解決方案才能編譯和運行
選擇 Window > Preferences > Plug
in Development > Target Platform
然後選擇 Not in Workspace
這將確保解決方案所依賴的基礎插件在導入和重新編譯過程中可用
一旦導入完成
您可能需要切換至 Plug
in Development 透視圖
在 com
ibm
lab
soln
jdt
excerpt 項目中選擇 plugin
xml
然後選擇 Update Classpath
這將修改由於 Eclipse 安裝路徑和解決方案的安裝路徑不同所引起的編譯錯誤
From:http://tw.wingwit.com/Article/program/Java/ky/201311/28318.html