HTMLEditorKit and Custom tags in the JEditorPane

2019-08-22 07:41发布

问题:

I use the instructions to add my own tag http://java-sl.com/custom_tag_html_kit.html

class MyParserDelegator extends ParserDelegator {
public MyParserDelegator() {
    try {
        Field f=javax.swing.text.html.parser.ParserDelegator.class.getDeclaredField("dtd");
        f.setAccessible(true);
        DTD dtd=(DTD)f.get(null);
        javax.swing.text.html.parser.Element div=dtd.getElement("div");
        dtd.defineElement("button", div.getType(), true, true,div.getContent(),null, null,div.getAttributes());

    } catch (NoSuchFieldException | SecurityException | IllegalArgumentException | IllegalAccessException e) {
    }
}

}

Unfortunately it is not working properly:

Can anyone help me?

回答1:

It works for me using the following (jdk 1.7):

Field f = javax.swing.text.html.parser.ParserDelegator.class.getDeclaredField("DTD_KEY");

The only change is the key: "DTD_KEY" upper case!!

I found the key "DTD_KEY" using

Field[] flds = javax.swing.text.html.parser.ParserDelegator.class.getDeclaredFields();
for (Field f: flds)  
{
       System.err.println(f.getName());
}


回答2:

I looked at the sources of JDK 7: the DTD is no more store in an attribute dtd of javax.swing.text.html.parser.ParserDelegator but in sun.awt.AppContext. Coming from a sun package, AppContext should not be accessed by classes out of the JRE himself. So using the sources of javax.swing.text.html.parser.ParserDelegator, I wrote MyParserDelegator that load the DTD himself. After that, the custom tag can be added in DTD easily.

The code below also contains the other classes from http://java-sl.com/custom_tag_html_kit.html to get a working example.

import java.awt.Component;
import java.io.BufferedInputStream;
import java.io.DataInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.Reader;
import java.net.URL;

import javax.swing.JButton;
import javax.swing.JEditorPane;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.text.BadLocationException;
import javax.swing.text.ComponentView;
import javax.swing.text.Document;
import javax.swing.text.Element;
import javax.swing.text.MutableAttributeSet;
import javax.swing.text.View;
import javax.swing.text.ViewFactory;
import javax.swing.text.html.HTML;
import javax.swing.text.html.HTMLDocument;
import javax.swing.text.html.HTMLEditorKit;
import javax.swing.text.html.HTMLEditorKit.Parser;
import javax.swing.text.html.HTMLEditorKit.ParserCallback;
import javax.swing.text.html.StyleSheet;
import javax.swing.text.html.parser.DTD;
import javax.swing.text.html.parser.DocumentParser;
import javax.swing.text.html.parser.ParserDelegator;

public class CustomTag {
    public static String htmlText = "<html>\n" + "<body>\n" + "<p>\n" + "Text before button\n" + "<button>Text for &lt;button&gt; tag</button>\n"
            + "Text after button\n" + "</p>\n" + "</body>\n" + "</html>";
    JEditorPane edit = new JEditorPane();

    public CustomTag() {
        JFrame frame = new JFrame("Custom tag in HTMLEditorKit");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.getContentPane().add(new JScrollPane(edit));

        edit.setEditorKit(new MyHTMLEditorKit());
        edit.setText(htmlText);

        frame.setSize(300, 300);
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);
    }

    public static void main(String[] args) throws Exception {
        new CustomTag();
    }

}

class MyHTMLEditorKit extends HTMLEditorKit {

    public MyHTMLEditorKit() {
        super();
    }

    @Override
    public Document createDefaultDocument() {
        StyleSheet styles = getStyleSheet();
        StyleSheet ss = new StyleSheet();

        ss.addStyleSheet(styles);

        MyHTMLDocument doc = new MyHTMLDocument(ss);
        doc.setParser(getParser());
        doc.setAsynchronousLoadPriority(4);
        doc.setTokenThreshold(100);
        return doc;
    }

    @Override
    public ViewFactory getViewFactory() {
        return new MyHTMLFactory();
    }

    Parser defaultParser;

    @Override
    protected Parser getParser() {
        if (defaultParser == null) {
            defaultParser = new MyParserDelegator();
        }
        return defaultParser;
    }

    class MyHTMLFactory extends HTMLFactory implements ViewFactory {
        public MyHTMLFactory() {
            super();
        }

        @Override
        public View create(Element element) {
            HTML.Tag kind = (HTML.Tag) (element.getAttributes().getAttribute(javax.swing.text.StyleConstants.NameAttribute));

            if (kind instanceof HTML.UnknownTag && element.getName().equals("button")) {

                return new ComponentView(element) {
                    @Override
                    protected Component createComponent() {
                        JButton button = new JButton("Button : text unknown");

                        try {
                            int start = getElement().getStartOffset();
                            int end = getElement().getEndOffset();
                            String text = getElement().getDocument().getText(start, end - start);
                            button.setText(text);
                        } catch (BadLocationException e) {
                            e.printStackTrace();
                        }

                        return button;
                    }
                };

            }
            return super.create(element);
        }
    }
}

class MyHTMLDocument extends HTMLDocument {
    public MyHTMLDocument(StyleSheet styles) {
        super(styles);
    }

    @Override
    public HTMLEditorKit.ParserCallback getReader(int pos) {
        Object desc = getProperty(Document.StreamDescriptionProperty);
        if (desc instanceof URL) {
            setBase((URL) desc);
        }
        return new MyHTMLReader(pos);
    }

    class MyHTMLReader extends HTMLDocument.HTMLReader {
        public MyHTMLReader(int offset) {
            super(offset);
        }

        @Override
        public void handleStartTag(HTML.Tag t, MutableAttributeSet a, int pos) {
            if (t.toString().equals("button")) {
                registerTag(t, new BlockAction());
            }
            super.handleStartTag(t, a, pos);
        }
    }
}

class MyParserDelegator extends Parser {
    private DTD _dtd;

    public MyParserDelegator() {
        String nm = "html32";
        try {
            _dtd = DTD.getDTD(nm);
            createDTD(_dtd, nm);

            javax.swing.text.html.parser.Element div = _dtd.getElement("div");
            _dtd.defineElement("button", div.getType(), true, true, div.getContent(), null, null, div.getAttributes());
        } catch (IOException e) {
            // (PENDING) UGLY!
            System.out.println("Throw an exception: could not get default dtd: " + nm);
        }
    }

    protected static DTD createDTD(DTD dtd, String name) {
        InputStream in = null;
        try {
            String path = name + ".bdtd";
            in = ParserDelegator.class.getResourceAsStream(path);
            if (in != null) {
                dtd.read(new DataInputStream(new BufferedInputStream(in)));
                DTD.putDTDHash(name, dtd);
            }
        } catch (Exception e) {
            System.out.println(e);
        }
        return dtd;
    }

    @Override
    public void parse(Reader r, ParserCallback cb, boolean ignoreCharSet) throws IOException {
        new DocumentParser(_dtd).parse(r, cb, ignoreCharSet);
    }
}