本文目录导读:

这是一个关于 Java Swing 中 JEditorPane、HTMLEditorKit 以及解析器/方法过滤器(通常指 HTMLParser 的 parse 方法回调过滤器,或者更常见的 AbstractDocumentParser/HTMLDocument.Parser 中的回调)的深入技术话题,开发者想要实现的是:在 JEditorPane 加载或解析 HTML 时,拦截、过滤或修改特定的 HTML 元素(如标签、属性、文本),而不是单纯地显示 HTML。
为了帮助你更精确地解决问题,我将从几个不同的“过滤器”含义来解释,并提供代码示例。
核心概念
- JEditorPane: Swing 的富文本组件,支持 HTML 3.2(通过 HTMLEditorKit)。
- HTMLEditorKit: 负责将 HTML 字符串解析并转换为 Swing 的文档模型(HTMLDocument)。
- 解析器(Parser):
javax.swing.text.html.parser.ParserDelegator或DocumentParser,它是实际的 HTML 解析引擎,它会在解析过程中回调各种方法(如handleStartTag、handleText、handleEndTag等)。 - Callback / Filter: 你在解析器回调中插入的代码,用于决定是否保留、修改或忽略某个 HTML 元素。
常见的 “Filter” 应用场景
- 安全过滤(XSS 防护): 禁止
<script>、<iframe>或onclick等危险事件。 - 标签黑/白名单: 只允许
<b>、<i>、<a>,禁止<img>。 - 属性修改: 强制将所有链接的
target设为_self。 - 文本替换: 将特定文本替换或高亮显示。
- 自定义渲染: 将某些标签替换为 Swing 组件。
关键类:HTMLEditorKit.ParserCallback
这是实现过滤器的核心,你需要创建一个继承自 HTMLEditorKit.ParserCallback 的类,并重写相关方法。
基础示例:一个只允许 <b> 和 <i> 标签的过滤器
import javax.swing.text.*;
import javax.swing.text.html.*;
import javax.swing.text.html.parser.*;
import java.io.*;
public class SimpleHTMLFilter extends HTMLEditorKit.ParserCallback {
// 允许的标签列表
private static final java.util.Set<HTML.Tag> ALLOWED_TAGS =
new java.util.HashSet<>(java.util.Arrays.asList(HTML.Tag.B, HTML.Tag.I, HTML.Tag.U));
@Override
public void handleStartTag(HTML.Tag t, MutableAttributeSet a, int pos) {
if (ALLOWED_TAGS.contains(t)) {
super.handleStartTag(t, a, pos);
}
// else: 忽略不允许的标签
}
@Override
public void handleEndTag(HTML.Tag t, int pos) {
if (ALLOWED_TAGS.contains(t)) {
super.handleEndTag(t, pos);
}
}
@Override
public void handleText(char[] data, int pos) {
super.handleText(data, pos); // 文本总是允许的
}
@Override
public void handleSimpleTag(HTML.Tag t, MutableAttributeSet a, int pos) {
// 处理自闭和标签如 <br>、<img>
if (ALLOWED_TAGS.contains(t)) {
super.handleSimpleTag(t, a, pos);
}
}
@Override
public void handleComment(char[] data, int pos) {
// 默认忽略注释,如果你想保留注释,可以 uncomment 下一行
// super.handleComment(data, pos);
}
@Override
public void handleError(String errorMsg, int pos) {
// 打印解析错误,但不中断
System.err.println("解析错误: " + errorMsg + " at position: " + pos);
}
}
如何将过滤器应用到 JEditorPane?
你不能直接将自定义的 ParserCallback 注入到标准的 HTMLEditorKit 中因为默认的 HTMLEditorKit 使用它自己内部的回调(HTMLDocument 的构建器)来构建文档。
方法:替换解析器回调
你需要创建一个自定义的 HTMLEditorKit,并覆盖 getParserCallback 方法(或者更准确地说,覆盖 createDocument 或使用 ParserDelegator 并传入你自己的 ParserCallback)。
更简单的做法:重写 ViewFactory 或使用 DocumentParser 手动解析
由于直接替换 HTMLEditorKit 解析器内部回调比较复杂且容易出错,更常见的方法是:
- 手动解析 HTML 字符串,使用
ParserDelegator和你的FilterCallback生成一个干净的 HTML 字符串。 - 再将其设置到 JEditorPane。
或者,可以使用一个较高级的库,Swing HTML Parsing Library (JSoup) 进行预处理,然后将干净的 HTML 交给 JEditorPane。
完整示例:使用 JSoup 进行预处理(推荐)
这是最稳定、最清晰的方式,尤其适合复杂的过滤需求。
import org.jsoup.Jsoup;
import org.jsoup.safety.Safelist;
import javax.swing.*;
import javax.swing.text.html.HTMLEditorKit;
import java.io.IOException;
public class JEditorPaneWithJsoupFilter {
public static void main(String[] args) {
JFrame frame = new JFrame("JEditorPane JSoup Filter Demo");
JEditorPane editorPane = new JEditorPane();
editorPane.setEditable(false);
editorPane.setContentType("text/html");
// 使用 JSoup 清洁 HTML
String dirtyHtml = "<html><body><script>alert('XSS')</script>" +
"<p>This is <b>bold</b> and <i>italic</i>.</p>" +
"<a href='#' onclick='alert(1)'>Link</a>" +
"<img src='x' onerror='alert(2)'></body></html>";
// 使用 JSoup 的 Safelist(白名单)
Safelist safelist = Safelist.none()
.addTags("p", "b", "i", "u", "a", "br")
.addAttributes("a", "href") // 只允许 href 属性
.addProtocols("a", "href", "http", "https"); // 只允许 http/https 链接
String cleanHtml = Jsoup.clean(dirtyHtml, safelist);
// 确保有基本的 HTML 结构
cleanHtml = "<html><body>" + cleanHtml + "</body></html>";
System.out.println("Cleaned HTML: " + cleanHtml);
editorPane.setText(cleanHtml);
frame.add(new JScrollPane(editorPane));
frame.setSize(400, 300);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
}
总结与选择
| 方法 | 复杂性 | 灵活性 | 性能 | 适用场景 |
|---|---|---|---|---|
| JSoup 预处理 | 低 | 极高 | 中等 | 推荐,适用于大多数过滤、修改、安全清洗。 |
| 自定义 ParserCallback | 中 | 中 | 高 | 需要直接操作 Swing 文档结构,或对性能要求极高。 |
| 覆盖 HTMLEditorKit | 高 | 低 | 高 | 不推荐,除非你非常熟悉 Swing HTML 内部机制。 |
如果你的需求是“在解析过程中过滤”,例如基于位置或上下文做复杂的决策,且不想使用外部库(JSoup),那么你需要深入研究 javax.swing.text.html.parser.ParserDelegator 和 DocumentParser,但通常,用 JSoup 做预处理是最佳实践。
如果你有更具体的过滤需求(只允许 <table> 标签且只能有 border='1' 属性”),请提供细节,我可以给出针对性的代码。