首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

基於 JDT 實現 JAR 內原始碼與資源檔全文搜尋

     編輯:Java 相關知識

動機:Eclipse 搜尋的盲區

Eclipse 強大的搜尋建立在 Lucene 索引之上,僅涵蓋工作區原始碼。專案依賴的 JAR 內 .class 反編譯原始碼、.properties 等資源檔無法被檢索。RCP 開發中常需追蹤 UI 字串定義於哪個 JAR 的哪個檔案,本文提供基於 JDT API 的解決方案。

JDT 中 JAR 相關類別結構

  • IJavaProject → 多個 JarPackageFragmentRoot (JAR)
  • JarPackageFragmentRoot → PackageFragment (套件)、JarEntryDirectory (資料夾)、JarEntryFile (檔案)
  • PackageFragment → IClassFile、JarEntryFile

UML 類別結構 對應關係圖

解決方案邏輯

  1. 遍歷工作區所有 Java 專案。
  2. 取得各專案依賴的 JarPackageFragmentRoot 列表。
  3. 遞迴遍歷 JAR 內套件與資源。
  4. IClassFile.getSource() 取得反編譯原始碼;JarEntryFile.getContents() 讀取資源檔文字。
  5. 正則比對,輸出路徑、偏移量、長度。

關鍵步驟與代碼

1. 取得 Java 專案

IProject[] projects = ResourcesPlugin.getWorkspace().getRoot().getProjects();
for (IProject p : projects) {
    IJavaProject jp = JavaCore.create(p);
    if (jp != null && jp.exists()) { /* process */ }
}

2. 取得 JAR 列表並過濾 JRE

for (IJavaElement el : jp.getChildren()) {
    if (el instanceof JarPackageFragmentRoot) {
        JarPackageFragmentRoot jar = (JarPackageFragmentRoot) el;
        // 過濾 JRE 系統庫
        IClasspathEntry cpe = jar.getRawClasspathEntry();
        IClasspathContainer container = JavaCore.getClasspathContainer(cpe.getPath(), jp);
        if (container.getKind() == IClasspathContainer.K_DEFAULT_SYSTEM) continue;
        searchJar(jar);
    }
}

3. 遍歷 Class 檔案

void searchJar(JarPackageFragmentRoot root) {
    for (IJavaElement pack : root.getChildren()) {
        if (pack instanceof PackageFragment) {
            for (IJavaElement cls : ((PackageFragment)pack).getChildren()) {
                if (cls instanceof IClassFile) {
                    searchClassFile((IClassFile)cls);
                }
            }
        }
    }
    // 非 Java 資源
    searchNonJavaResources(root);
}

4. 取得 Class 原始碼並比對

void searchClassFile(IClassFile cf) {
    String src = cf.getSource(); // 需已綁定 Source Attachment
    if (src != null) {
        Matcher m = pattern.matcher(src);
        while (m.find()) {
            report(cf.getPath(), m.start(), m.group().length());
        }
    }
}

5. 遍歷非 Java 資源 (遞迴處理目錄)

void searchNonJavaResources(Object container) {
    Object[] res = container instanceof JarPackageFragmentRoot ?
        ((JarPackageFragmentRoot)container).getNonJavaResources() :
        ((JarEntryDirectory)container).getChildren();
    for (Object o : res) {
        if (o instanceof JarEntryDirectory) searchNonJavaResources(o);
        else if (o instanceof JarEntryFile) searchJarEntryFile((JarEntryFile)o);
    }
}

6. 讀取資源檔內容

void searchJarEntryFile(JarEntryFile f) {
    try (ByteArrayInputStream in = (ByteArrayInputStream) f.getContents()) {
        byte[] bs = new byte[in.available()];
        in.read(bs);
        String txt = new String(bs);
        Matcher m = pattern.matcher(txt);
        while (m.find()) report(f.getFullPath(), m.start(), m.group().length());
    } catch (Exception e) { /* log */ }
}

實戰應用:RCP 追蹤 UI 字串來源

  1. 搜尋 UI 顯示字串 (如 "This section provides...") → 定位 gui.properties 於某 JAR。
  2. 依慣例搜尋 com.ibm.btools.blm.ui.attributesview.resource.gui (Package + 檔名) → 找到使用該 ResourceBundle 的類別 BLMAttributesviewMessageKeys。
  3. Open Type (Ctrl+Shift+T) 開啟該類別原始碼。

搜尋 UI 字串 輸入介面 屬性檔搜尋結果 屬性檔原始碼 搜尋類別 類別搜尋結果

常用 JDT API 速查

類別方法說明
IClassFileisClass(), isInterface()判斷類型
PackageFragmentgetCompilationUnits(), getClassFiles()取得 Java/Class 檔列表
JarPackageFragmentRootgetJar(), getKind(), isArchive(), isReadOnly()JAR 元資料

結語

利用 JDT API 可突破 Eclipse 預設搜尋限制,深入 JAR 內部檢索原始碼與資源。歡迎優化效能 (如建立索引快取) 或封裝為外掛發佈,造福更多 Java/RCP 開發者。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved