TITLE:JEditorPaneのHTMLDocumentからIDでElementを取得する

Posted by at 2013-09-09

JEditorPaneのHTMLDocumentからIDでElementを取得する

`JEditorPaneに設定したHTMLDocumentを検索してid属性を持つElement`を取得します。

  • &jnlp;
  • &jar;
  • &zip;
HTMLAttributeID.png

サンプルコード

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);
        }
      }
    }
  }
}
View in GitHub: Java, Kotlin

解説

  • `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)`などは使用できない
    • 指定した`idElementが存在した場合、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`テキストとは異なる点に注意
System.out.println("ParserDelegator");
final String id = field.getText().trim();
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);
      textArea.append(String.format("%s@id=%s%n", tag, attrid));
      if(id.equals(attrid)) {
        textArea.append(String.format("found: pos=%d%n", pos));
        int endoffs = text.indexOf('>', pos);
        textArea.append(String.format("%s%n", text.substring(pos, endoffs+1)));
      }
    }
  }, Boolean.TRUE);
}catch(Exception ex) {
  ex.printStackTrace();
}

参考リンク

コメント