本文目录导读:

我来解释一下 JEditorPane、HTMLEditorKit 和 Parser 之间的实体解析机制。
结构关系
JEditorPane -> HTMLEditorKit -> Parser (ParserDelegator)
-> HTMLReader (Callback)
-> ViewFactory
实体解析流程
基础设置
JEditorPane editor = new JEditorPane();
editor.setContentType("text/html");
editor.setText("<html><body>& < > "</body></html>");
HTMLEditorKit 的解析机制
// HTMLEditorKit 内部使用 ParserDelegator
public class HTMLEditorKit extends StyledEditorKit {
// 默认解析器
private static final DefaultHTMLParser defaultParser = new DefaultHTMLParser();
public Parser getParser() {
return new ParserDelegator();
}
// 创建回调处理器
protected HTMLEditorKit.HTMLReader getCallback() {
return new HTMLReader();
}
}
实体解析的核心实现
内置实体映射
public class HTMLEditorKit.Parser {
// 预定义的实体映射表
private static final Hashtable<String, Character> entityTable = new Hashtable<>();
static {
entityTable.put("amp", '&');
entityTable.put("lt", '<');
entityTable.put("gt", '>');
entityTable.put("quot", '"');
entityTable.put("apos", '\'');
entityTable.put("nbsp", (char)160);
entityTable.put("copy", (char)169);
entityTable.put("reg", (char)174);
// ... 更多实体
}
}
ParserDelegator 解析逻辑
public class ParserDelegator extends HTMLEditorKit.Parser {
private static final char[] REPLACEMENT_CHARS = {
'&', '<', '>', '"', '\'', ' '
};
@Override
public void parse(Reader r, HTMLEditorKit.ParserCallback cb, boolean ignoreCharSet) {
// 1. 读取 HTML 内容
// 2. 识别 &xxx; 模式
// 3. 查表替换
// 4. 调用 callback 方法
}
}
实际解析过程演示
import javax.swing.*;
import javax.swing.text.*;
import javax.swing.text.html.*;
import javax.swing.text.html.parser.*;
import java.io.*;
public class EntityParsingDemo {
public static void main(String[] args) {
JEditorPane editor = new JEditorPane();
editor.setContentType("text/html");
// 包含各种实体的 HTML
String html = "<html><body>" +
"<p>& < > " '</p>" +
"<p>© © ©</p>" +
"<p>普通文本 & 更多</p>" +
"</body></html>";
editor.setText(html);
System.out.println("解析后的文本: " + editor.getText());
// 自定义解析观察
HTMLEditorKit kit = (HTMLEditorKit) editor.getEditorKit();
HTMLEditorKit.Parser parser = kit.getParser();
try {
StringReader reader = new StringReader(html);
parser.parse(reader, new HTMLEditorKit.ParserCallback() {
@Override
public void handleText(char[] data, int pos) {
System.out.println("解析文本: " + new String(data));
}
@Override
public void handleStartTag(HTML.Tag t, MutableAttributeSet a, int pos) {
System.out.println("开始标签: " + t);
}
@Override
public void handleEndTag(HTML.Tag t, int pos) {
System.out.println("结束标签: " + t);
}
@Override
public void handleSimpleTag(HTML.Tag t, MutableAttributeSet a, int pos) {
System.out.println("简单标签: " + t);
}
}, false);
} catch (Exception e) {
e.printStackTrace();
}
}
}
自定义实体处理
public class CustomHTMLEditorKit extends HTMLEditorKit {
@Override
public Parser getParser() {
return new ParserDelegator() {
@Override
public void parse(Reader r, ParserCallback cb, boolean ignoreCharSet)
throws IOException {
// 自定义预处理
BufferedReader br = new BufferedReader(r);
StringBuilder sb = new StringBuilder();
String line;
while ((line = br.readLine()) != null) {
sb.append(line);
}
// 添加自定义实体
String content = sb.toString()
.replace("&myentity;", "自定义值")
.replace("&custom;", "custom value");
// 调用父类解析
super.parse(new StringReader(content), cb, ignoreCharSet);
}
};
}
}
实体解析的关键类
| 类名 | 作用 |
|---|---|
ParserDelegator |
默认解析器实现 |
DTD |
HTML DTD 定义 |
HTMLEditorKit.ParserCallback |
解析回调接口 |
HTMLReader |
内部回调实现 |
HTML.Attribute |
属性常量定义 |
HTML.Tag |
标签常量定义 |
Debug 示例
import javax.swing.text.html.parser.*;
public class EntityDebugger {
public static void main(String[] args) throws Exception {
// 查看所有内置实体
DTD dtd = DTD.getDTD("html32");
System.out.println("=== 实体列表 ===");
// 通过反射获取实体表
java.lang.reflect.Field entitiesField = DTD.class.getDeclaredField("entityHash");
entitiesField.setAccessible(true);
@SuppressWarnings("unchecked")
java.util.Hashtable<String, Entity> entities =
(java.util.Hashtable<String, Entity>) entitiesField.get(dtd);
entities.forEach((name, entity) -> {
System.out.println(name + " -> " + (char)entity.data);
});
// 测试自定义实体
String html = "&unknown; &test; &";
// unknown 和 test 不会被解析
// & 会被解析为 &
}
}
注意事项
- 实体类型:支持命名实体(&)和数字实体(&)
- Unicode 支持:支持 Unicode 编码实体(©)
- 大小写敏感:实体名称对大小写敏感
- 未知实体:未知实体保留原样
- 性能考虑:大量实体解析可能影响性能
常见问题解决
// 处理特殊字符
editor.setText(text.replace("&", "&")); // 手动转义
// 禁用实体解析
editor.getDocument().putProperty("IgnoreCharset", Boolean.TRUE);
// 使用 setText 而不是 write
editor.setText(html); // 自动解析
editor.write(writer); // 可能不会解析实体
这就是 JEditorPane 中 HTMLEditorKit 和 Parser 的实体解析机制,理解这个机制对于正确处理 HTML 内容中特殊字符的显示非常重要。