フォームのmodelにアクセス

前の項目でフォームのコンポーネントに結び付けられたswingモデルクラスにアクセスする方法がわかりました。 ここではボタン型のinputタグに結び付けられたmodelにActionListenerを登録し、name属性を表示するサンプルコードを示します。 対象のタイプはcheckbox、radio、image、resetとsubmitボタンです。

HTMLDocumentを調べる手順やタイミングは前の項目と同じです。 JEditorPaneのdocumentにDocumentListenerを登録し、changedUpdateでHTMLDocumentに収められたElementをチェックします。

Elementが持つAttributeSetにmodelが含まれていたら、そのElementはフォームのコンポーネントタグに結びついています。 AttributeSetからhtmlタグのname属性の値を調べてアクションコマンドに登録します。 この場合、AttributeSetには文字列で表すと「name」になるキーが2つあります。 1つはタグ名を表すnameで、属性値はHTML.Tagクラスのインスタンスです。 もう1つはhtmlタグに記述されたname属性です。 アクションコマンドに登録するのはこちらなので、HTML.Tag型の方は無視します。

private void checkAttributes(Element elem)
{
    AttributeSet attrs = elem.getAttributes();
    Enumeration attrNames = attrs.getAttributeNames();
    
    Object model = null;
    String tagName = null;
    
    while(attrNames.hasMoreElements() )
    {
        Object attrName = attrNames.nextElement();
        Object attr = attrs.getAttribute(attrName);
        
        // model属性があるなら記録
        if("model".equals(attrName.toString() ) )
        {
            model = attr;
        }
        // htmlタグにname属性があるならば記録
        else if(!(attr instanceof HTML.Tag) &&
                "name".equals(attrName.toString() ) )
        {
            tagName = attr.toString();
        }
    }
    
    // elemにmodel属性があり、それがButtonModelクラスの場合
    if(model instanceof ButtonModel)
    {
        ButtonModel buttonModel = (ButtonModel)model;
        buttonModel.addActionListener(this);
        buttonModel.setActionCommand(tagName);
    }
    // 他タイプのフォームコンポーネントを扱う場合
    // else if(model instanceof PlainDocumentなど) ...
}

今回はname属性を使いましたが、id属性やvalue属性でも同様にコーディングできます。


ボタン以外のコンポーネントにイベントを登録する場合

上の例ではButtonModelを対称にしたのでsetActionCommandでどのフォームコンポーネントがクリックされたか分かりました。 しかし他のコンポーネントの場合はもうひと工夫要ります。 例えば、リスナーにname属性を覚えさせるなどすると良いでしょう。 textareaにイベントを追加する場合は次のようなDocumentListenerを作ります。

class InputTextListener implements DocumentListener
{
    private String _name;

    public InputTextListener(String name)
    {
        _name = name;
    }

    public String getName()
    {
        return _name;
    }
    
    ... overrideメソッド
}

先程のcheckAttributesメソッドは次のようになります。

private void checkAttributes(Element elem)
{
    AttributeSet attrs = elem.getAttributes();
    Enumeration attrNames = attrs.getAttributeNames();
    
    Object model = null;
    boolean isTextArea = false;
    String tagName = null;
    
    while(attrNames.hasMoreElements() )
    {
        Object attrName = attrNames.nextElement();
        Object attr = attrs.getAttribute(attrName);
        
        // model属性があるなら記録
        if("model".equals(attrName.toString() ) )
        {
            model = attr;
        }
        // htmlタグがtextareaかチェック
        else if(attr == HTML.Tag.TEXTAREA)
        {
            isTextArea = true;
        }
        // htmlタグにname属性があるならば記録
        else if(!(attr instanceof HTML.Tag) &&
                "name".equals(attrName.toString() ) )
        {
            tagName = attr.toString();
        }
    }
    
    // elemにmodel属性があり、それがPlainDocumentクラスの場合
    if(isTextArea && (model instanceof PlainDocument) )
    {
        PlainDocument inputDoc = (PlainDocument)model;
        InputTextListener listener = new InputTextListener(tagName);
        inputDoc.addDocumentListener(listener);
    }
}

サンプルコード

以下はサンプルコードの全文です。 htmlファイルなどはネタのトップからダウンロードしてください。

// ModelTest.java
import java.io.File;
import java.net.URL;
import java.util.Enumeration;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.event.*;
import javax.swing.text.*;
import javax.swing.text.html.*;

public class ModelTest extends JFrame implements DocumentListener, ActionListener
{
    public static final String TEST_HTML = "jeditorpane_test.html";
    public static final String TEST_CSS = "jeditorpane_test.css";
    
    private JEditorPane _editorPane;
    
    public ModelTest()
    {
        setDefaultCloseOperation(EXIT_ON_CLOSE);
        
        try
        {
            _editorPane = new JEditorPane();
            _editorPane.setEditable(false);
            _editorPane.setContentType("text/html");
            
            HTMLEditorKit editorKit = (HTMLEditorKit)_editorPane.getEditorKit();
            editorKit.setAutoFormSubmission(false);
            File cssFile = new File(TEST_CSS);
            editorKit.getStyleSheet().importStyleSheet(cssFile.toURI().toURL() );
            
            _editorPane.setPage(new File(TEST_HTML).toURI().toURL() );
            JScrollPane scrollPane = new JScrollPane(_editorPane);
            getContentPane().add(scrollPane);
            
            _editorPane.getDocument().addDocumentListener(this);
        }
        catch(Exception exc)
        {
            exc.printStackTrace();
        }
    }
    
    public static void main(String[] args)
    {
        ModelTest app = new ModelTest();
        app.setSize(640, 480);
        app.setVisible(true);
    }
    
    private void checkElements(Element elem)
    {
        checkAttributes(elem);
        
        int count = elem.getElementCount();
        for(int i = 0; i < count; i++)
        {
            checkElements(elem.getElement(i) );
        }
    }

    private void checkAttributes(Element elem)
    {
        AttributeSet attrs = elem.getAttributes();
        Enumeration attrNames = attrs.getAttributeNames();
        
        Object model = null;
        String tagName = null;
        
        while(attrNames.hasMoreElements() )
        {
            Object attrName = attrNames.nextElement();
            Object attr = attrs.getAttribute(attrName);
            
            // model属性があるなら記録
            if("model".equals(attrName.toString() ) )
            {
                model = attr;
            }
            // htmlタグにname属性があるならば記録
            else if(!(attr instanceof HTML.Tag) &&
                    "name".equals(attrName.toString() ) )
            {
                tagName = attr.toString();
            }
        }
        
        // elemにmodel属性があり、それがButtonModelクラスの場合
        if(model instanceof ButtonModel)
        {
            ButtonModel buttonModel = (ButtonModel)model;
            buttonModel.addActionListener(this);
            buttonModel.setActionCommand(tagName);
        }
        // 他タイプのフォームコンポーネントを扱う場合
        // else if(model instanceof PlainDocumentなど) ...
    }
    
    public void insertUpdate(DocumentEvent event)
    {
    }

    public void removeUpdate(DocumentEvent event)
    {
    }

    public void changedUpdate(DocumentEvent event)
    {
        Document doc = _editorPane.getDocument();
        checkElements(doc.getDefaultRootElement() );
    }
    
    public void actionPerformed(ActionEvent event)
    {
        String command = event.getActionCommand();
        JOptionPane.showMessageDialog(this, "name = " + command);
    }
}