JEditorPaneHTMLEditorKitParserFilter过滤器

wen java案例 1

本文目录导读:

JEditorPaneHTMLEditorKitParserFilter过滤器

  1. 基本概念
  2. 完整示例代码
  3. 使用场景
  4. 最佳实践
  5. 注意事项

我来详细解释一下 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));
    }
}

注意事项

  1. 性能考虑: 复杂的过滤可能会影响渲染性能
  2. 兼容性: 确保过滤后的 HTML 仍然有效
  3. 安全性: 始终优先考虑安全过滤
  4. 维护性: 使用过滤器链模式提高代码可维护性

这个示例展示了 JEditorPane 中 HTMLEditorKit 和 ParserFilter 的完整使用方式,包括自定义解析、内容过滤和安全性处理。

抱歉,评论功能暂时关闭!