JEditorPaneHTMLEditorKitParserEntity实体解析

wen java案例 1

本文目录导读:

JEditorPaneHTMLEditorKitParserEntity实体解析

  1. 结构关系
  2. 实体解析流程
  3. 注意事项
  4. 常见问题解决

我来解释一下 JEditorPane、HTMLEditorKit 和 Parser 之间的实体解析机制。

结构关系

JEditorPane -> HTMLEditorKit -> Parser (ParserDelegator)
                               -> HTMLReader (Callback)
                               -> ViewFactory

实体解析流程

基础设置

JEditorPane editor = new JEditorPane();
editor.setContentType("text/html");
editor.setText("<html><body>&amp; &lt; &gt; &quot;</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>&amp; &lt; &gt; &quot; &apos;</p>" +
                      "<p>&#169; &#x00A9; ©</p>" +
                      "<p>普通文本 &amp; 更多</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; &amp;";
        // unknown 和 test 不会被解析
        // &amp; 会被解析为 &
    }
}

注意事项

  1. 实体类型:支持命名实体(&)和数字实体(&)
  2. Unicode 支持:支持 Unicode 编码实体(©)
  3. 大小写敏感:实体名称对大小写敏感
  4. 未知实体:未知实体保留原样
  5. 性能考虑:大量实体解析可能影响性能

常见问题解决

// 处理特殊字符
editor.setText(text.replace("&", "&amp;")); // 手动转义
// 禁用实体解析
editor.getDocument().putProperty("IgnoreCharset", Boolean.TRUE);
// 使用 setText 而不是 write
editor.setText(html); // 自动解析
editor.write(writer); // 可能不会解析实体

这就是 JEditorPane 中 HTMLEditorKit 和 Parser 的实体解析机制,理解这个机制对于正确处理 HTML 内容中特殊字符的显示非常重要。

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