特定のタグに特定の属性を設定する

seasar-user MLにて、Teedaで<input>と<textarea>にlang="ja"という属性を自動的に付加するような方法はないか?という質問が出ていたのでYmirではどうするのか考えてみた。

Ymir(というかYmirのZPTね)では、org.seasar.ymir.zpt.TemplateParsingInterceptorというインタフェースが用意されている。これの実装クラスを作成することで、ZPTのパース処理をインターセプトして、パース結果に手を入れることができるようになる。

<input>と<textarea>にlang="ja"という属性を自動的に付加するには、以下のようなクラスを作成して、app.diconにコンポーネント登録しておけば良い:

public class LangInterceptor implements TemplateParsingInterceptor {
    private static final String[] SPECIALATTRIBUTEPATTERNSTRINGS = new String[0];

    private static final String[] SPECIALTAGPATTERNSTRINGS = new String[] {
        "input", "textarea" };

    public String[] getSpecialAttributePatternStrings() {
        return SPECIALATTRIBUTEPATTERNSTRINGS;
    }

    public String[] getSpecialTagPatternStrings() {
        return SPECIALTAGPATTERNSTRINGS;
    }

    public Element[] tagElementCreated(TagElement tagElement,
        TemplateParsingInterceptorChain chain) {

        List<Attribute> attributeList = new ArrayList<Attribute>(Arrays
            .asList(tagElement.getAttributes()));
        attributeList.add(new Attribute("lang", "ja", "\""));

        return new Element[] { new TagElement(tagElement.getName(),
            attributeList.toArray(new Attribute[0]), tagElement
                .getBodyElements()) };
    }
}

getSpecialTagPatternStringsメソッドでnew String[] { "input", "textarea" }を返すことで、<input>と<textarea>が見つかった場合だけtagElementCreatedメソッドが呼び出されるようにしている。

tagElementCreatedメソッドでは、lang="ja"属性を表すAttributeオブジェクトを生成し、受け取ったパース済みのTagElementオブジェクトの属性に追加する形で新しいTagElementオブジェクトを作って返すようにしている。これで全ての<input>と<textarea>にlang="ja"属性が追加される。