SHOEISHA iD

※旧SEメンバーシップ会員の方は、同じ登録情報(メールアドレス&パスワード)でログインいただけます

DeveloperZine(デベロッパージン)- エンジニアの意思決定を支える技術情報メディア ProductZine

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

WTPではじめるサーバサイドJava入門

サーバサイドJava入門 カスタムタグの使い方

第7回 カスタムタグの定義方法と呼び出し方

ボディを持ったカスタムタグ

 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;
  }
}
修正したMySimpleTag。<my:MyTag color="#FF0000">私の誕生日</my:MyTag>と書くとこのように表示される。
修正したMySimpleTag。<my:MyTag color="#FF0000">私の誕生日</my:MyTag>と書くとこのように表示される。

 このタグは、開始タグと終了タグの間にテキストを書くと、そのテキストの前に日付を出力し、全体を指定した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
  • まず、開始タグに関する処理を行います。EVAL_BODY_BUFFEREDを返すと、ボディをバッファする処理が開始されます。
  • setBodyContent
  • ボディのコンテンツであるBodyContentをセットします。
  • doInitBody
  • ボディの初期化処理を開始します。
  • getBodyContent
  • ボディのコンテンツであるBodyContentを取得します。
  • doAfterBody
  • ボディ終了後の後処理を行います。EVAL_BODY_INCLUDEを返すと、バッファしていたボディをページに組み込みます。KIP_BODYを実行するとボディ部分をスキップし出力しません。またEVAL_BODY_AGAINを返すと再度ボディの処理を繰り返します。
  • doEndTag
  • 終了タグの処理を行います。EVAL_PAGEを返すと、ページの続きの処理に戻ります。

 基本的に、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開発を行う場合の基礎知識の一つ」とも言えます。カスタムタグを作れなくともここにあげたような技術を使うことはできるでしょうが、その原理がわかるか否かで理解度は大きく違ってくるはずです。今は「カスタムタグってどういう役に立つんだ?」と思う人も、いずれこうした技術を使うようになったときに、ここで説明したような知識がきっと役に立ってくるはずです。

この記事は参考になりましたか?

連載通知を行うには会員登録(無料)が必要です。
既に会員の方はを行ってください。
WTPではじめるサーバサイドJava入門連載記事一覧

もっと読む

この記事の著者

掌田 津耶乃(ショウダ ツヤノ)

三文ライター&三流プログラマ。主にビギナーに向けたプログラミング関連の執筆を中心に活動している。※現在、入門ドキュメントサイト「libro」、カード型学習サイト「CARD.tuyano.com」を公開...

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

この記事は参考になりましたか?

この記事をシェア

CodeZine(コードジン)
https://codezine.jp/article/detail/1526 2007/07/30 14:00

イベント

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

新規会員登録無料のご案内

  • ・全ての過去記事が閲覧できます
  • ・会員限定メルマガを受信できます

メールバックナンバー