JTextArea 無法滿足關鍵字著色、語法高亮等富文本需求,JTextPane 配合 DefaultStyledDocument 成為最佳選擇。本文源自實際 SQL 查詢分析器開發經驗,完整展示如何通過繼承 DefaultStyledDocument 實現實時語法高亮,支持多種數據庫方言的關鍵字識別與著色。
JDAStyledDocument 繼承 DefaultStyledDocument,重寫插入/刪除方法,在文檔變更時觸發語法分析與屬性應用:
public class JDAStyledDocument extends DefaultStyledDocument {
private int dbType = -1; // 數據庫類型:Oracle/MySQL/SQLServer 等
private AttributeSet currentAttrSet = null;
public JDAStyledDocument(int dbType) {
this.dbType = dbType;
}
// 插入文本時觸發語法著色
@Override
public void insertString(int offset, String str, AttributeSet a)
throws BadLocationException {
this.currentAttrSet = a;
super.insertString(offset, str, a);
setSyntaxColor(offset, str.length());
}
// 刪除文本後重新著色受影響區域
@Override
public void remove(int offs, int len) throws BadLocationException {
super.remove(offs, len);
setSyntaxColor(offs);
}
// ... 核心著色邏輯見下文
}
採用「以光標位置為中心,雙向擴展至單詞邊界」的策略,精確別關鍵字:
// 核心著色入口:處理指定範圍內的語法高亮
private int setSyntaxColor(int offset, int len) throws BadLocationException {
if (offset < 0 || len < 0) return offset;
int tmpOffset = offset;
while (tmpOffset < offset + len) {
tmpOffset = setSyntaxColor(tmpOffset); // 單詞級著色
tmpOffset = getNextWordOffset(tmpOffset); // 跳過空白字符
}
return setSyntaxColor(tmpOffset); // 處理最後一個單詞
}
// 單詞級著色:向前向後查找單詞邊界
private int setSyntaxColor(int offset) {
if (offset < 0 || currentAttrSet == null) return offset;
String before = getBeforeBlankString(offset - 1); // 向前找空格
String after = getAfterBlankString(offset); // 向後找空格
String syntax = (before + after).trim(); // 完整單詞
int start = offset - before.length();
if (start < 0 || syntax.isEmpty()) return offset;
// 關鍵字判斷與顏色應用
Color color = SyntaxMgr.isSyntax(dbType, syntax);
StyleConstants.setForeground((MutableAttributeSet) currentAttrSet, color);
setCharacterAttributes(start, syntax.length(), currentAttrSet, true);
return start + syntax.length();
}
// 獲取下一個單詞起始位置(跳過空白字符並著色)
private int getNextWordOffset(int offset) {
int rOffset = offset;
int length = getLength();
while (rOffset <= length && rOffset >= 0) {
String ch = getPositionChar(rOffset);
if (!SyntaxMgr.isSpaceChar(ch)) break;
rOffset++;
}
// 空白區域著色
if (rOffset != offset) {
StyleConstants.setForeground((MutableAttributeSet) currentAttrSet,
SyntaxColorMgr.getSpaceColor());
setCharacterAttributes(offset, rOffset - offset, currentAttrSet, true);
}
return rOffset;
}
通過 SyntaxMgr 統一管理不同數據庫方言的關鍵字與顏色映射:
// 使用示例:根據數據庫類型創建文檔
JTextPane sqlPane = new JTextPane(new JDAStyledDocument(DBType.ORACLE));
// 或
JTextPane sqlPane = new JTextPane(new JDAStyledDocument(DBType.MYSQL));