本文目录导读:

我来详细解释一下 JEditorPane、HTMLEditorKit 和 ParserFilter 在 Java Swing 中的使用和实现。
基本概念
JEditorPane
- Swing 中的文本组件,可以显示和编辑多种格式的文本
- 支持 HTML、RTF 等格式
HTMLEditorKit
- 负责解析和渲染 HTML 内容
- 提供自定义解析和渲染的能力
ParserFilter
- 用于过滤和修改 HTML 解析过程中的内容
- 可以在解析器处理 HTML 标签时进行干预
完整示例代码
import javax.swing.*;
import javax.swing.text.*;
import javax.swing.text.html.*;
import javax.swing.text.html.parser.*;
import java.awt.*;
import java.io.*;
public class HtmlParserFilterExample {
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> {
JFrame frame = new JFrame("HTML Parser Filter Demo");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(600, 400);
// 创建 JEditorPane
JEditorPane editorPane = new JEditorPane();
editorPane.setEditable(false);
// 创建自定义 HTMLEditorKit
CustomHtmlEditorKit kit = new CustomHtmlEditorKit();
editorPane.setEditorKit(kit);
// 设置 HTML 内容
String htmlContent = "<html><body>" +
"<h1>测试标题</h1>" +
"<p>这是一个段落。</p>" +
"<p><b>加粗文本</b></p>" +
"<p><i>斜体文本</i></p>" +
"<a href='http://example.com'>链接示例</a>" +
"<img src='test.jpg' alt='测试图片'>" +
"</body></html>";
editorPane.setText(htmlContent);
// 添加到滚动面板
JScrollPane scrollPane = new JScrollPane(editorPane);
frame.add(scrollPane, BorderLayout.CENTER);
// 添加控制按钮
JPanel controlPanel = new JPanel();
JButton filterButton = new JButton("应用过滤器");
JButton resetButton = new JButton("重置");
filterButton.addActionListener(e -> {
// 重新设置以触发过滤
editorPane.setText(htmlContent);
});
resetButton.addActionListener(e -> {
// 重置为默认编辑器
editorPane.setEditorKit(new HTMLEditorKit());
editorPane.setText(htmlContent);
});
controlPanel.add(filterButton);
controlPanel.add(resetButton);
frame.add(controlPanel, BorderLayout.SOUTH);
frame.setVisible(true);
});
}
}
// 自定义 HTMLEditorKit
class CustomHtmlEditorKit extends HTMLEditorKit {
@Override
public Document createDefaultDocument() {
HTMLDocument doc = (HTMLDocument) super.createDefaultDocument();
// 设置自定义解析器
doc.setParser(new CustomHtmlParser(doc, doc.getParser()));
return doc;
}
}
// 自定义 HTML 解析器
class CustomHtmlParser extends HtmlParser {
private HTMLDocument doc;
private HtmlParser originalParser;
public CustomHtmlParser(HTMLDocument doc, HtmlParser originalParser) {
this.doc = doc;
this.originalParser = originalParser;
}
@Override
public void parse(Reader reader, HTMLReader callback, boolean ignoreCharSet)
throws IOException {
// 使用自定义的读取器来过滤内容
CustomHTMLReader customReader = new CustomHTMLReader(doc);
// 调用原始解析器的 parse 方法
originalParser.parse(reader, callback, ignoreCharSet);
}
}
// 自定义 HTML 读取器
class CustomHTMLReader extends HTMLEditorKit.ParserCallback {
private HTMLDocument doc;
private StringBuilder filteredContent = new StringBuilder();
public CustomHTMLReader(HTMLDocument doc) {
this.doc = doc;
}
@Override
public void handleText(char[] data, int pos) {
// 过滤文本内容
String text = new String(data);
// 移除所有数字
text = text.replaceAll("\\d+", "");
// 限制文本长度为50个字符
if (text.length() > 50) {
text = text.substring(0, 50) + "...";
}
filteredContent.append(text);
System.out.println("过滤文本: " + text);
}
@Override
public void handleStartTag(HTML.Tag t, MutableAttributeSet a, int pos) {
System.out.println("开始标签: " + t.toString() +
", 属性: " + a);
// 过滤 img 标签
if (t == HTML.Tag.IMG) {
System.out.println("过滤掉图片标签");
return; // 不处理 img 标签
}
// 修改链接属性
if (t == HTML.Tag.A) {
// 添加 target="_blank" 属性
a.addAttribute("target", "_blank");
}
}
@Override
public void handleEndTag(HTML.Tag t, int pos) {
System.out.println("结束标签: " + t.toString());
}
@Override
public void handleSimpleTag(HTML.Tag t, MutableAttributeSet a, int pos) {
System.out.println("简单标签: " + t.toString());
}
@Override
public void handleComment(char[] data, int pos) {
// 过滤注释
System.out.println("过滤注释: " + new String(data));
}
@Override
public void handleError(String errorMsg, int pos) {
System.err.println("解析错误: " + errorMsg + " at position " + pos);
}
public String getFilteredContent() {
return filteredContent.toString();
}
}
// 使用 ParserFilter 的另一种实现方式
class CustomParserFilter extends HTMLEditorKit.ParserCallback {
private StringBuilder output = new StringBuilder();
private boolean filterActive = true;
// 过滤配置
private boolean filterLinks = true;
private boolean filterImages = true;
private boolean filterScripts = true;
private int maxTextLength = 100;
public CustomParserFilter() {
}
public void setFilterActive(boolean active) {
this.filterActive = active;
}
@Override
public void handleText(char[] data, int pos) {
if (filterActive) {
String text = new String(data);
// 文本过滤规则
text = removeScript(text);
text = truncateText(text);
text = removeSensitiveInfo(text);
output.append(text);
} else {
output.append(data);
}
}
@Override
public void handleStartTag(HTML.Tag t, MutableAttributeSet a, int pos) {
if (filterActive) {
// 过滤不需要的标签
if (shouldFilterTag(t)) {
return;
}
// 修改标签属性
modifyAttributes(t, a);
}
// 构建输出
output.append("<").append(t.toString());
// 添加修改后的属性
output.append(">");
}
@Override
public void handleEndTag(HTML.Tag t, int pos) {
if (filterActive && shouldFilterTag(t)) {
return;
}
output.append("</").append(t.toString()).append(">");
}
private boolean shouldFilterTag(HTML.Tag tag) {
// 判断是否需要过滤该标签
if (tag == HTML.Tag.SCRIPT && filterScripts) {
return true;
}
if (tag == HTML.Tag.IMG && filterImages) {
return true;
}
if (tag == HTML.Tag.A && filterLinks) {
return true;
}
return false;
}
private void modifyAttributes(HTML.Tag tag, MutableAttributeSet attributes) {
// 修改标签属性
if (tag == HTML.Tag.A) {
// 为链接添加安全属性
attributes.addAttribute("rel", "noopener noreferrer");
}
if (tag == HTML.Tag.IMG) {
// 添加图片安全处理
attributes.addAttribute("loading", "lazy");
}
}
private String removeScript(String text) {
// 移除潜在的脚本内容
return text.replaceAll("<script[^>]*>.*?</script>", "")
.replaceAll("javascript:", "")
.replaceAll("onerror=", "")
.replaceAll("onclick=", "");
}
private String truncateText(String text) {
// 截断过长的文本
if (text.length() > maxTextLength) {
return text.substring(0, maxTextLength) + "...";
}
return text;
}
private String removeSensitiveInfo(String text) {
// 移除敏感信息(示例)
return text.replaceAll("\\b\\d{17}[\\dXx]\\b", "[ID被隐藏]") // 身份证号
.replaceAll("\\b1[3-9]\\d{9}\\b", "[手机号被隐藏]") // 手机号
.replaceAll("\\b\\d{16,19}\\b", "[银行卡号被隐藏]"); // 银行卡号
}
@Override
public String toString() {
return output.toString();
}
}
// 更高级的示例:使用过滤器链
class FilterChainExample {
public static void main(String[] args) {
JEditorPane editorPane = new JEditorPane();
// 创建过滤器链
FilterChain filterChain = new FilterChain();
filterChain.addFilter(new XssFilter());
filterChain.addFilter(new TextLengthFilter());
filterChain.addFilter(new LinkModifierFilter());
// 设置自定义编辑器套件
FilteredHtmlEditorKit kit = new FilteredHtmlEditorKit(filterChain);
editorPane.setEditorKit(kit);
}
}
// 过滤器接口
interface HtmlFilter {
String filter(String html);
}
// 过滤器链
class FilterChain {
private java.util.List<HtmlFilter> filters = new java.util.ArrayList<>();
public void addFilter(HtmlFilter filter) {
filters.add(filter);
}
public String applyFilters(String html) {
String result = html;
for (HtmlFilter filter : filters) {
result = filter.filter(result);
}
return result;
}
}
// XSS 过滤器
class XssFilter implements HtmlFilter {
@Override
public String filter(String html) {
// 过滤 XSS 攻击代码
return html.replaceAll("<script[^>]*>.*?</script>", "")
.replaceAll("on\\w+\\s*=", "")
.replaceAll("javascript\\s*:", "");
}
}
// 文本长度过滤器
class TextLengthFilter implements HtmlFilter {
private int maxLength = 100;
@Override
public String filter(String html) {
// 限制文本长度
StringBuilder sb = new StringBuilder();
String[] parts = html.split("(?<=\\>)|(?=\\<)");
for (String part : parts) {
if (!part.startsWith("<")) {
if (part.length() > maxLength) {
part = part.substring(0, maxLength) + "...";
}
}
sb.append(part);
}
return sb.toString();
}
}
// 链接修改过滤器
class LinkModifierFilter implements HtmlFilter {
@Override
public String filter(String html) {
// 为所有链接添加 target="_blank"
return html.replaceAll("<a\\s+([^>]*)>",
"<a $1 target=\"_blank\" rel=\"noopener noreferrer\">");
}
}
// 使用过滤器的编辑器套件
class FilteredHtmlEditorKit extends HTMLEditorKit {
private FilterChain filterChain;
public FilteredHtmlEditorKit(FilterChain filterChain) {
this.filterChain = filterChain;
}
@Override
public void read(InputStream in, Document doc, int pos)
throws IOException, BadLocationException {
// 读取并过滤内容
String content = new String(in.readAllBytes());
content = filterChain.applyFilters(content);
super.read(new ByteArrayInputStream(content.getBytes()), doc, pos);
}
@Override
public void read(Reader in, Document doc, int pos)
throws IOException, BadLocationException {
// 读取并过滤内容
StringBuilder sb = new StringBuilder();
char[] buffer = new char[1024];
int length;
while ((length = in.read(buffer)) != -1) {
sb.append(buffer, 0, length);
}
String filteredContent = filterChain.applyFilters(sb.toString());
super.read(new StringReader(filteredContent), doc, pos);
}
}
使用场景
安全过滤
- 防止 XSS 攻击
- 移除恶意脚本
- 验证链接安全性
内容控制
- 限制文本长度
- 过滤敏感信息
- 修改链接行为
格式处理
- 自定义标签渲染
- 修改属性值
- 添加额外样式
最佳实践
// 1. 使用线程安全的过滤
public class ThreadSafeFilter extends HTMLEditorKit.ParserCallback {
private final Object lock = new Object();
@Override
public void handleText(char[] data, int pos) {
synchronized (lock) {
// 线程安全的过滤操作
}
}
}
// 2. 性能优化
public class PerformanceFilter extends HTMLEditorKit.ParserCallback {
private StringBuilder buffer = new StringBuilder();
private static final int MAX_BUFFER_SIZE = 1024 * 1024; // 1MB
@Override
public void handleText(char[] data, int pos) {
if (buffer.length() > MAX_BUFFER_SIZE) {
// 清空缓冲区以防止内存溢出
buffer.setLength(0);
}
// 过滤操作
}
}
// 3. 可配置的过滤器
public class ConfigurableFilter extends HTMLEditorKit.ParserCallback {
private Properties config = new Properties();
public ConfigurableFilter(String configFile) throws IOException {
config.load(new FileInputStream(configFile));
}
}
注意事项
- 性能考虑: 复杂的过滤可能会影响渲染性能
- 兼容性: 确保过滤后的 HTML 仍然有效
- 安全性: 始终优先考虑安全过滤
- 维护性: 使用过滤器链模式提高代码可维护性
这个示例展示了 JEditorPane 中 HTMLEditorKit 和 ParserFilter 的完整使用方式,包括自定义解析、内容过滤和安全性处理。