• 追加された行はこの色です。
  • 削除された行はこの色です。
TITLE:JEditorPaneのHTMLDocumentからIDでElementを取得する
#navi(../)
#tags(JEditorPane, HTMLDocument, Element, HTMLEditorKit, ParserDelegator, Highlighter)
RIGHT:Posted by &author(aterai); at 2013-09-09
* JEditorPaneのHTMLDocumentからIDでElementを取得する [#d79a409a]
``JEditorPane``に設定した``HTMLDocument``を検索して``id``属性を持つ``Element``を取得します。

- &jnlp;
- &jar;
- &zip;

#ref(https://lh6.googleusercontent.com/-qbmJcawN3vU/UiyAF7K-MRI/AAAAAAAABz0/i1Hw-dPyqSw/s800/HTMLAttributeID.png)

** サンプルコード [#h1c7d46e]
#code(link){{
private void traverseElementById(Element element) {
  if(element.isLeaf()) {
    checkID(element);
  }else{
    for(int i=0;i<element.getElementCount();i++) {
      Element child = element.getElement(i);
      checkID(child);
      if(!child.isLeaf()) {
        traverseElementById(child);
      }
    }
  }
}
private void checkID(Element element) {
  AttributeSet attrs = element.getAttributes();
  Object elementName = attrs.getAttribute(
      AbstractDocument.ElementNameAttribute);
  Object name = (elementName != null)
    ? null : attrs.getAttribute(StyleConstants.NameAttribute);
  HTML.Tag tag;
  if(name instanceof HTML.Tag) {
    tag = (HTML.Tag)name;
  }else{
    return;
  }
  textArea.append(String.format("%s%n", tag));
  if(tag.isBlock()) { //block
    Object bid = attrs.getAttribute(HTML.Attribute.ID);
    if(bid!=null) {
      textArea.append(String.format("block: id=%s%n", bid));
      addHighlight(element, true);
    }
  }else{ //inline
    Enumeration e = attrs.getAttributeNames();
    while(e.hasMoreElements()) {
      Object obj = attrs.getAttribute(e.nextElement());
      //System.out.println("AttributeNames: "+obj);
      if(obj instanceof AttributeSet) {
        AttributeSet a = (AttributeSet)obj;
        Object iid = a.getAttribute(HTML.Attribute.ID);
        if(iid!=null) {
          textArea.append(String.format("inline: id=%s%n", iid));
          addHighlight(element, false);
        }
      }
    }
  }
}
}}

** 解説 [#r50ba582]
- ``Element#getElement(id)``
-- ``HTMLDocument#getElements(String)``メソッドを使用して指定した``id``を持つ``Element``を取得
-- これらの``Element``は、``org.w3c.dom.Element``ではなく、``javax.swing.text.Element``インターフェイスを実装する``HTMLDocument.BlockElement``など
--- ``org.w3c.dom.Document#getElementById(String)``などは使用できない
-- 指定した``id``の``Element``が存在した場合、``editorPane.select(element.getStartOffset(), element.getEndOffset());``で選択
--- ``element.getStartOffset()``などで取得されるオフセットは、``JEditorPane``に表示されない要素や属性は含まれない
- ``Highlight Element[@id]``
-- ``id``属性を持つ``Element``をハイライト表示
-- ``HTMLDocument.BlockElement``などには、``html``の要素や属性が後で復元する場合のために``AttributeSet``に備考として保存されている
--- ブロック要素とインライン要素で属性の保存されている場所が異なる
-- ``DefaultHighlighter#setDrawsLayeredHighlights(false)``の場合、改行を含むハイライトや選択状態の描画がおかしくなる?
- ``ParserDelegator``
-- ``ParserDelegator``を使って文字列をパースし、``HTMLEditorKit.ParserCallback#handleStartTag(...)``でタグの開始を見つけたら、``MutableAttributeSet#getAttribute(HTML.Attribute.ID);``でそのタグの``id``を取得
-- ``javax.swing.text.Element``とは無関係に、``JEditorPane#getText()``で取得した文字列を``html``として解析している
-- ``HTMLEditorKit``が設定された``JEditorPane``から``getText()``で取得された文字列には、``<body>``などのタグが自動的に補完されているので、元の``html``テキストとは異なる点に注意

#code{{
System.out.println("-- ParserDelegator --");
final Integer id = (Integer)spinner.getValue();
final String text = editorPane.getText();
ParserDelegator delegator = new ParserDelegator();
try {
  delegator.parse(new StringReader(text), new HTMLEditorKit.ParserCallback() {
    @Override public void handleStartTag(HTML.Tag tag, MutableAttributeSet a, int pos) {
      Object attrid = a.getAttribute(HTML.Attribute.ID);
      System.out.println(tag + "@id=" + attrid);
      if(id.toString().equals(attrid)) {
        System.out.println("found: pos="+pos);
        int endoffs = text.indexOf(">", pos);
        System.out.println(text.substring(pos, endoffs+1));
      }
    }
  }, Boolean.TRUE);
} catch(Exception ex) {
  ex.printStackTrace();
}
}}

** 参考リンク [#l6b05b44]
- [[JTextPaneで修飾したテキストをJTextAreaにHtmlソースとして表示する>Swing/HTMLEditorKit]]

** コメント [#pc868bfa]
#comment