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

Eclipse 插件開發實戰:基於 WTP 打造自定義 JSP 編輯器(六)— 開發 Structured Model (DOM) 分析視圖對比語法與語義模型

     編輯:關於JSP

繼 Document 分析視圖後,本文指導開發 `StructuredModelView`,可視化 `IStructuredModel` 支撐的 `IDOMDocument` DOM 樹,支援與編輯器雙向同步,並通過對比語法樹與語義樹的差異,徹底釐清「為什麼高級功能需要語義模型」的核心認知。

需求規格(擴展 Document 視圖能力)

能力Document 視圖Model 視圖 (本文)
展示模型IStructuredDocument 節點樹IDOMDocument DOM 樹 (W3C DOM)
節點類型ELEMENT, TEXT, COMMENT... (語法通用)Element, Attr, Text, CDATASection, EntityReference... (DOM 標準)
屬性信息字符串鍵值對類型化屬性 (ID, IDREF, CDATA, NMTOKEN...)、Schema 衍生類型
命名空間不明確完整命名空間 URI、前綴映射、默認命名空間
語義驗證無節點標記錯誤/警告 (若驗證器已運行)

核心實現差異:獲取 DOM 模型

// 1. 獲取編輯器的 StructuredModel (句柄機制)
IEditorPart editor = ...;
if (editor instanceof StructuredTextEditor) {
    IStructuredModel model = ((StructuredTextEditor) editor).getModel(); // 關鍵:獲取模型句柄並加載
    if (model != null) {
        // 2. 適配到 IDOMDocument
        IDOMDocument domDoc = (IDOMDocument) model.getAdapter(IDOMDocument.class);
        if (domDoc != null) {
            // 3. 獲取 DOM 根元素
            Element root = domDoc.getDocumentElement();
            // 4. 構建樹視圖輸入
            treeViewer.setInput(root);
        }
    }
}

關鍵點:模型加載狀態同步

// 模型可能處於 UNLOADED/LOADING 狀態,需監聽加載完成
if (model.getModelState() != IStructuredModel.LOADED) {
    model.addModelListener(new ModelListener() {
        public void modelChanged(ModelEvent event) {
            if (event.getType() == ModelEvent.LOADED) {
                // 異步在 UI 線程刷新視圖
                Display.getDefault().asyncExec(() -> refreshViewer(model));
                model.removeModelListener(this);
            }
        }
    });
} else {
    refreshViewer(model);
}

DOM 樹內容/標籤提供者 (W3C DOM 適配)

class DOMContentProvider implements ITreeContentProvider {
    public Object[] getChildren(Object parent) {
        if (parent instanceof Node) {
            NodeList nl = ((Node) parent).getChildNodes();
            Object[] arr = new Object[nl.getLength()];
            for (int i = 0; i < nl.getLength(); i++) arr[i] = nl.item(i);
            return arr;
        }
        return new Object[0];
    }
    // ... getParent, hasChildren, getElements 類似
}

class DOMLabelProvider extends LabelProvider {
    public String getText(Object obj) {
        if (obj instanceof Element) {
            Element el = (Element) obj;
            String ns = el.getNamespaceURI();
            String prefix = el.getPrefix();
            String local = el.getLocalName();
            StringBuilder sb = new StringBuilder();
            if (ns != null) sb.append("{").append(ns).append("}");
            if (prefix != null) sb.append(prefix).append(":");
            sb.append(local != null ? local : el.getTagName());
            // 顯示 ID 屬性
            if (el.hasAttribute("id")) sb.append(" #").append(el.getAttribute("id"));
            // 顯示 Schema 類型 (若可用)
            TypeInfo ti = el.getSchemaTypeInfo();
            if (ti != null) sb.append(" [").append(ti.getTypeName()).append("]");
            return sb.toString();
        } else if (obj instanceof Attr) {
            Attr attr = (Attr) obj;
            return "@" + attr.getName() + "=\"" + attr.getValue() + "\"";
        } else if (obj instanceof Text) {
            String txt = ((Text) obj).getData().trim();
            return txt.isEmpty() ? "#text (whitespace)" : "#text: " + txt.substring(0, Math.min(30, txt.length()));
        }
        return super.getText(obj);
    }
}

雙向同步實現 (Model 視圖版)

編輯器光標 → Model 視圖節點選中

editor.getSelectionProvider().addSelectionChangedListener(event -> {
    ISelection sel = event.getSelection();
    if (sel instanceof ITextSelection) {
        int offset = ((ITextSelection) sel).getOffset();
        // 關鍵:通過 Document 找語法節點,再映射到 Model 節點
        IStructuredDocument sdoc = model.getDocument();
        IStructuredNode syntaxNode = sdoc.findNodeAtOffset(offset);
        if (syntaxNode != null) {
            // 語法節點 → DOM 節點映射 (WTP 內部維護雙向映射表)
            Node domNode = syntaxNode.getAdapter(Node.class); // 關鍵適配
            if (domNode != null) {
                treeViewer.setSelection(new StructuredSelection(domNode), true);
            }
        }
    }
});

Model 視圖雙擊 → 編輯器高亮

treeViewer.addDoubleClickListener(event -> {
    IStructuredSelection sel = (IStructuredSelection) event.getSelection();
    Node domNode = (Node) sel.getFirstElement();
    if (domNode != null && editor instanceof StructuredTextEditor) {
        // DOM 節點 → 語法節點 → 偏移量
        IStructuredNode syntaxNode = (IStructuredNode) domNode.getAdapter(IStructuredNode.class);
        if (syntaxNode != null) {
            ((StructuredTextEditor) editor).selectAndReveal(
                syntaxNode.getStartOffset(), syntaxNode.getEndOffset() - syntaxNode.getStartOffset());
        }
    }
});

語法樹 vs 語義樹:對比觀察實驗

打開同一 JSP 文件,並排開啟 Document 視圖與 Model 視圖,觀察:

場景Document 視圖 (語法)Model 視圖 (語義)差異本質
<jsp:include page="x.jsp"/>單個 ELEMENT 節點,類型 "jsp:include"Element 節點,命名空間 "https://java.sun.com/JSP/Page",局部名 "include",Schema 類型標註語義模型理解標籤**身份與契約**
<%@ page import="java.util.*" %>JSP_DIRECTIVE 區域節點,屬性字符串Directive 節點,屬性 import 類型化為 NMTOKENS,可驗證合法性語義模型理解**屬性語義與約束**
<div id="main">text</div>ELEMENT(div) 含 TEXT 子節點Element(div) 有 ID 屬性 (ID 類型),文本節點規範化語義模型建立**ID 索引**,支援 getElementById
錯誤嵌套 <div><span></div></span>按解析順序構建樹,不報錯驗證器運行後,節點標記錯誤標註語義模型支援**驗證與錯誤標記**
Java 代碼 <% int x=1; %>JSP_SCRIPTLET 區域,不解析內部CDATASection 或特定 Scriptlet 節點,**無 Java AST**Java 語義由 **JDT** 獨立管理,WTP 不處理

工具價值:作為定制開發的「模型調試器」

  • 開發 **Content Assist** 時:觀察當前光標對應的 DOM 元素、命名空間、Schema 類型、可用屬性列表 → 精準實現提示邏輯。
  • 開發 **Validator** 時:觀察違規節點的 DOM 結構、屬性類型、父子關係 → 編寫精準 XPath/遍歷校驗規則。
  • 開發 **Hyperlink Detector** 時:觀察屬性值中 URI 的 DOM 節點位置、TLD 引用的目標定義節點 → 實現精準跳轉。
  • 開發 **Formatter** 時:觀察 DOM 樹結構、混合內容模式、空白處理規則 → 實現語義感知格式化。

系列總結與下一階段預告

六篇文章建立了完整認知棧:

  1. WTP 架構與編輯器雛形
  2. JFace Text 三大基石
  3. IStructuredDocument 語法樹模型
  4. Document 分析視圖工具
  5. IStructuredModel 語義模型與 DOM 構建
  6. Model 分析視圖工具與模型對比

下一階段:功能定制實戰

  • 資源定位器:解析 JSP include/forward/taglib 路徑
  • 內容助手定制:標籤屬性枚舉、EL 表達式變量提示、自定義標籤庫支援
  • 驗證器定制:項目級編碼規範、安全規則 (防 XSS/CSRF)、自定義標籤嵌套約束
  • 超鏈接檢測器:跳轉到 TLD 定義、Java 類、CSS 類、資源文件
  • 自動編輯策略:JSP 標籤智能閉合、EL 表達式括號補全、縮進優化
  • 快速修復:自動添加缺失屬性、修正嵌套錯誤、引入標籤庫聲明

掌握模型,才能馭功能。兩大分析視圖將伴隨你完成所有定制開發,成為理解 WTP 內部運行機制的核心儀表盤。

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