ボディを持ったカスタムタグ
HTMLで使われるタグの多くは、開始タグと終了タグを持っています。2つのタグの間にはさんだテキスト(ボディ)に対し、何らかの処理を行うわけです。カスタムタグでも、このようなボディを持ったタグを作ることは可能です。ただし、それにはSimpleTagSupportではなく、別のクラスを使用します。
ボディを持ったタグでは、今までの1つのタグだけだったカスタムタグとはいろいろと細かな点で違いがあります。それらに留意しながら作成をしていきましょう。これも、先のMyTagを修正する形で作成することにします。MyTagにcolor属性を追加しましたが、これに更にボディを持たせる形で修正を行うことにしましょう。
まず、TLDファイルの修正からです。例によって<tag>部分だけをあげておきましょう。
<tag> <name>MyTag</name> <tag-class>jp.codezine.MySimpleTag</tag-class> <body-content>JSP</body-content> <attribute> <name>color</name> <type>java.lang.String</type> <required>false</required> </attribute> </tag>
修正したのは1箇所だけ。<body-content>の値を「JSP」と変更してあります。これにより、ボディ部分にJSPのコードが置かれるようになります。TLDの記述は、実はボディを持つものと持たないものの違いはわずかこの1箇所だけです。
BodyTagSupportクラスを作成する
ボディを利用したカスタムタグは、「BodyTagSupport」というクラスを継承して作成します。これはSimpleTagSupportと異なり、タグ出力に関するメソッドが多数用意されています。それらを組み合わせて、全体の出力を構成していくのです。では、先のMySimpleTagクラスを全面的に書き換えて、BodyTagSupportを利用した形に変更してみましょう。
public class MySimpleTag extends BodyTagSupport { private String color = "#000000"; public int doStartTag() throws JspException { try { JspWriter out = this.pageContext.getOut(); out.print("<span style=\"color:" + this.getColor() + ";border-bottom-style:solid;border-width:1pt;\">"); } catch (IOException ex) { ex.printStackTrace(); } return BodyTagSupport.EVAL_BODY_BUFFERED; } public void doInitBody() throws JspException { super.doInitBody(); } public void setBodyContent(BodyContent b) { Locale.setDefault(new Locale("ja", "JP", "JP")); Calendar cal = Calendar.getInstance(); SimpleDateFormat format = new SimpleDateFormat("GGGGyyyy年M月d日"); try { b.print("["); b.print(format.format(cal.getTime())); b.print("]<span style=\"font-weight:bold;\">"); } catch (IOException ex) { ex.printStackTrace(); } super.setBodyContent(b); } public BodyContent getBodyContent() { BodyContent b = super.getBodyContent(); try { b.print("</span>"); } catch (IOException ex) { ex.printStackTrace(); } return b; } public int doAfterBody() throws JspException { try { BodyContent bodyContent = this.getBodyContent(); JspWriter out = bodyContent.getEnclosingWriter(); bodyContent.writeOut(out); bodyContent.clearBody(); } catch (IOException ex) { ex.printStackTrace(); } return BodyTagSupport.EVAL_BODY_INCLUDE; } public int doEndTag() throws JspException { try { JspWriter out = this.pageContext.getOut(); out.print("</span>"); } catch (IOException ex) { ex.printStackTrace(); } return BodyTagSupport.EVAL_PAGE; } public void setColor(String s) { this.color = s; } public String getColor() { return this.color; } }

このタグは、開始タグと終了タグの間にテキストを書くと、そのテキストの前に日付を出力し、全体を指定したcolorで表示します。例えば、
<my:MyTag color="#FF0000">私の誕生日</my:MyTag>
このように書くと、「[平成19年7月7日]私の誕生日」というようにテキストが赤色で出力されるわけです。実際に生成されるHTMLコードを確認してみると、以下のようになっているはずです。
<span style="color:#FF0000;border-style:none none solid none;border-width:0px 0px 1px 0px;border-color:0000ff;">[平成19年7月7日]
<span style="font-weight:bold;">私の誕生日</span></span>
二重の<span>タグで囲まれ、前半に日付、後半にボディのテキストが出力されています。かなり複雑なタグが生成されていることがわかりますね。
タグ出力の流れ
最後のsetColor/getColor以外は、すべてタグの出力に関連するメソッドです。ここでは、基本的に呼び出される順にメソッドを並べてあります。それぞれのメソッドの役割を簡単に整理してみましょう。
- doStartTag
- setBodyContent
- doInitBody
- getBodyContent
- doAfterBody
- doEndTag
基本的に、doStartTagで開始タグの処理、doInitBodyでボディの初期化処理、doAfterBodyでボディの終了処理、そしてdoEndTagで終了タグ、この4つのメソッドがわかればボディ付きタグは使えるようになります。setBodyContent/getBodyContentは、ボディのコンテンツ(BodyContent)を出力する際に設定したり取り出したりする処理をオーバーライドしたものですので、わざわざこれを利用する必要はありません(ここでは例として使ってあります)。
出力に関するストリームは、大きく分けて2つのものから取得します。1つはthis.pageContextフィールド、もう1つはthis.getBodyContentで得られるBodyContentです。これらからJspWriterを取得し出力を行います。どちらも最終的にJspWriterを取得して出力するのだから同じだろう……と思ってしまいがちですが、両者は明確に違います。
pageContextは、ページのコンテキストに対するものであり、BodyContentはカスタムタグのボディのコンテキストに対するものです。従って、BodyContentはボディの出力に関するメソッドで使います。また開始タグや終了タグに関係する出力は、(これらはボディではありませんから)pageContextに対して出力を行います。「開始・終了タグはpageContext、ボディ関係はBodyContent」と頭に入れておきましょう。
まとめ
以上、最も単純な「開始タグのみのカスタムタグ」から、「属性を持ったタグ」、そして「ボディを持ったタグ」と、カスタムタグの基本的な形について簡単にですが説明をしました。これらの知識が身につけば、一通りのカスタムタグは作成できるようになります。
このカスタムタグという技術は、単に「自分でタグを作る」というだけでなく、さまざまなところで応用されています。例えば、標準で用意されている<jsp:○○>といったアクションタグもカスタムタグの一種ですし、Webアプリケーション開発に用いられているフレームワーク(「Struts」や「JSF」といったものなど)でも、多くはカスタムタグを使ってさまざまな機能をJSPに組み込んで使うようになっています。
カスタムタグは、いわば「より高度なWeb開発を行う場合の基礎知識の一つ」とも言えます。カスタムタグを作れなくともここにあげたような技術を使うことはできるでしょうが、その原理がわかるか否かで理解度は大きく違ってくるはずです。今は「カスタムタグってどういう役に立つんだ?」と思う人も、いずれこうした技術を使うようになったときに、ここで説明したような知識がきっと役に立ってくるはずです。

border-width:0px 0px 1px 0px;border-color:0000ff;">