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

Eclipse 插件開發實戰:基於 WTP 打造自定義 JSP 編輯器(四)— 開發 Structured Document 分析視圖驗證模型認知

     編輯:關於JSP

理論學習需工具驗證。本文指導開發一個 Eclipse View 插件,實時可視化當前編輯器的 `IStructuredDocument` 節點樹與文本區域劃分,支援雙向同步選中(編輯器光標 ↔ 視圖節點),作為理解 SSE 語法模型的「瑞士軍刀」,並為後續 Model 視圖開發建立範式。

需求規格

交互方向觸發條件響應行為
編輯器 → 視圖編輯器激活加載並顯示該編輯器的 Document 節點樹
光標位置變化視圖自動展開並選中對應節點
文本內容修改視圖增量更新樹結構(保持展開狀態)
編輯器關閉清空視圖內容
視圖 → 編輯器雙擊樹節點編輯器高亮選中對應文本區域

技術實現關鍵點

1. Eclipse Adapter 機制支撐 Properties 視圖擴展

WTP 利用 `IAdaptable` / `IAdapterFactory` 為編輯器提供屬性視圖支援。`StructuredTextEditor.getAdapter()` 中註冊 `ConfigurablePropertySheetPage`,根據光標位置顯示對應標籤屬性編輯界面。理解此機制有助於自定義視圖集成。

2. 視圖開發核心代碼結構

public class StructuredDocumentView extends ViewPart {
    private TreeViewer treeViewer;
    private IEditorPart activeEditor;
    private IStructuredDocument currentDocument;
    private IPartListener partListener = new IPartListener() {
        public void partActivated(IWorkbenchPart part) {
            if (part instanceof StructuredTextEditor) {
                hookEditor((StructuredTextEditor) part);
            }
        }
        public void partClosed(IWorkbenchPart part) {
            if (part == activeEditor) clearViewer();
        }
        // ... partBroughtToTop, partDeactivated, partOpened
    };

    private void hookEditor(StructuredTextEditor editor) {
        activeEditor = editor;
        IDocumentProvider provider = editor.getDocumentProvider();
        IDocument doc = provider.getDocument(editor.getEditorInput());
        if (doc instanceof IStructuredDocument) {
            currentDocument = (IStructuredDocument) doc;
            // 1. 構建樹輸入
            treeViewer.setInput(currentDocument.getStructuredNode());
            // 2. 監聽文本變更 → 增量刷新
            currentDocument.addDocumentListener(new DocumentListener() {
                public void documentChanged(DocumentEvent e) {
                    // 僅刷新受影響子樹,避免全量重建
                    treeViewer.refresh(); // 簡化處理,生產環境需精細化
                }
            });
            // 3. 監聽選中變化 → 同步光標
            editor.getSelectionProvider().addSelectionChangedListener(event -> {
                ISelection sel = event.getSelection();
                if (sel instanceof ITextSelection) {
                    int offset = ((ITextSelection) sel).getOffset();
                    IStructuredNode node = currentDocument.findNodeAtOffset(offset);
                    if (node != null) treeViewer.setSelection(new StructuredSelection(node), true);
                }
            });
        }
    }
}

3. 樹內容/標籤提供者

class NodeContentProvider implements ITreeContentProvider {
    public Object[] getChildren(Object parent) {
        if (parent instanceof IStructuredNode) {
            return ((IStructuredNode) parent).getChildren().toArray();
        }
        return new Object[0];
    }
    public Object getParent(Object child) {
        return child instanceof IStructuredNode ? ((IStructuredNode) child).getParent() : null;
    }
    public boolean hasChildren(Object obj) { return getChildren(obj).length > 0; }
    public Object[] getElements(Object input) { return getChildren(input); }
}

class NodeLabelProvider extends LabelProvider {
    public String getText(Object obj) {
        if (obj instanceof IStructuredNode) {
            IStructuredNode node = (IStructuredNode) obj;
            return String.format("%s [%d-%d]", node.getType(), node.getStartOffset(), node.getEndOffset());
        }
        return super.getText(obj);
    }
}

4. 雙擊跳轉編輯器

treeViewer.addDoubleClickListener(event -> {
    IStructuredSelection sel = (IStructuredSelection) event.getSelection();
    IStructuredNode node = (IStructuredNode) sel.getFirstElement();
    if (node != null && activeEditor instanceof StructuredTextEditor) {
        IDocument doc = ((StructuredTextEditor) activeEditor).getDocumentProvider()
            .getDocument(activeEditor.getEditorInput());
        // 高亮選中區域
        ((StructuredTextEditor) activeEditor).selectAndReveal(node.getStartOffset(), node.getEndOffset() - node.getStartOffset());
    }
});

Properties 視圖擴展機制深度解析(附錄)

WTP 在 `StructuredTextEditor.getAdapter(Class)` 中:

public Object getAdapter(Class required) {
    if (IPropertySheetPage.class.equals(required) && isEditable()) {
        if (fPropertySheetPage == null || fPropertySheetPage.getControl().isDisposed()) {
            PropertySheetConfiguration cfg = createPropertySheetConfiguration();
            if (cfg != null) {
                ConfigurablePropertySheetPage page = new ConfigurablePropertySheetPage();
                page.setConfiguration(cfg);
                fPropertySheetPage = page;
            }
        }
        return fPropertySheetPage;
    }
    return super.getAdapter(required);
}

`PropertySheetConfiguration` 定義了當前光標節點支援的屬性頁面(如 JSP 指令屬性、HTML 標籤屬性),實現了**上下文敏感的屬性編輯**。

驗證模型理解的實戰清單

使用本視圖對任意 JSP 文件進行觀察,驗證以下認知:

  1. 輸入 `<%@ page %>` → 觀察根節點下出現 `JSP_DIRECTIVE` 類型節點,屬性包含 `import`, `contentType` 等。
  2. 輸入 `` → 觀察 `ELEMENT` 節點,類型 `jsp:include`,屬性 `page`。
  3. 3. 在 `
    ` 中輸入文本 → 觀察 `TEXT` 節點夾雜在 `ELEMENT(div)` 子節點中。 4. 光標移動至 `<% ... %>` 內部 → 視圖選中 `JSP_SCRIPTLET` 節點,而非其內部 Java Token(語法層不解析 Java)。 5. 故意寫錯嵌套 `
    ` → 觀察節點樹仍按解析順序構建,**語法層不報錯**,錯誤由語義層驗證器檢測。

工具價值總結

  • **模型調試器**:開發 Content Assist/Validator/Hyperlink 時,實時觀察節點類型、偏移量、屬性,避免盲目猜測 API 返回值。
  • **教學輔助**:直觀展示「文本 → 區域 → 節點樹」的三層轉化。
  • **異常定位**:編輯器行為異常(如提示不出現、高亮錯位)時,對比視圖節點與預期,快速定位分區器/協調器/節點構建器問題。

下一篇預告

開發 `IStructuredModel (DOM Document)` 分析視圖:可視化語義模型(`IDOMDocument`、`ICSSDocument`),對比語法樹與語義樹差異,理解為什麼高級功能需要語義模型支撐。

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