本文コンテンツとの対話

Tag インターフェイス、IterationTag インターフェイス、および BodyTag インターフェイスを使用すると、テンプレートテキスト、JSP スクリプト要素、およびネストしたカスタムタグをカスタムタグの本文に含めることができます。カスタムタグが本文コンテンツとの対話を行わない場合は、TagSupport クラスを拡張し、doStartTag メソッドで EVAL_BODY_INCLUDE を返します。

ただし、カスタムタグが本文コンテンツのループ処理または変更を行う必要がある場合は、次のクラスのいずれかを拡張します。

メモ:  TLD ファイル内でカスタムタグの bodycontent 要素に empty を指定すると、本文コンテンツを無効にできます。タグハンドラは、doStartTag メソッドで SKIP_BODY を返すことによって、本文コンテンツを無視できます。

BodyContent オブジェクトについて

BodyContent オブジェクトは JspWriter のサブクラスです。JspWriter は、JSP のout 変数のために内部的に使用されるライターです。BodyContent オブジェクトは、bodyContent 変数を介して、doInitBodydoAfterBody、および doEndTag で使用できます
(BodyContent オブジェクトと bodyContent 変数の、大文字の使用方法の違いに注意してください)。このオブジェクトのコンテンツは、doEndTag メソッドで元の JspWriter と統合できます。

BodyContent オブジェクトには、出力を書き出すために使用するメソッドのほかに、このオブジェクトのコンテンツの読み取り、クリア、および取り出しを行うメソッドが含まれています。たとえば、bodyContent.getString を使用すると、ライターのコンテンツを取り出せるだけでなく、必要であれば、そのコンテンツを元の JspWriter と統合する前に変更できます。

メモ:  bodyContent 変数を使用する前に、その値が null でないことを確認します。

doInitBody メソッドについて

doStartTag メソッドが EVAL_BODY_BUFFERED を返した場合、JRun はdoInitBody メソッドを呼び出します。このメソッドは、必要に応じて本文コンテンツの初期化に使用します。次にタグハンドラが本文を処理し、JRun が doAfterBody メソッドを呼び出します。

doAfterBody メソッドについて

doAfterBody メソッドは、SKIP_BODY または EVAL_BODY_AGAIN を返します。
EVAL_BODY_AGAIN を返した場合、JRun はループに戻って本文を再実行します。これにより、リストやデータベースの結果セットなどの反復データ全体を繰り返し処理できます。

簡単な例

次の例は、doInitBodydoAfterBody メソッドの使用方法を示しています。
また、bodyContent 出力を出力ストリームに統合する方法も示しています。

import javax.servlet.jsp.*;
import javax.servlet.jsp.tagext.*;
import java.io.IOException;
public class TestBody extends BodyTagSupport {
 public int doStartTag() throws JspException {
  try {
    pageContext.getOut().print("<h2>doStartTag() を使用しています。
</h2>");
    return EVAL_BODY_BUFFERED;  } catch(IOException ioe) {
    throw new JspException(ioe.getMessage());
  }
 }
 public void doInitBody() throws JspException {
  try {  
    // これは、doStartTag や doEndTag のライターとは違うことに
    // 注意してください。
    bodyContent.print("<h2>doInitBody() を使用しています。</h2>");
  } catch(IOException ioe) {
    throw new JspException(ioe.getMessage());
  } 
 }
 public int doAfterBody() throws JspException {
  try {
    // これは、doStartTag や doEndTag のライターとは違うことに
    // 注意してください。
    bodyContent.print("<h2>doAfterBody() を使用しています。</h2>");
    // return IterationTag.EVAL_BODY_AGAIN; // これを使用してループ処理します。
    return SKIP_BODY;
  } catch(IOException ioe) {
     throw new JspException(ioe.getMessage());
  } 
 }
public int doEndTag() throws JspException {
  try {  
    // bodyContent ライターから元のライターに書き込みます。
    pageContext.getOut().print(bodyContent.getString());
    //// バッファーが大きい場合は、前の行よりも
    //// 次のコードの方がより効率的です。
    //// 元の (囲まれている) ライターを取得します。
    // JspWriter jOut = bodyContent.getEnclosingWriter();
    //// 前のライターに本文の出力を追加します。
    //bodyContent.writeOut(jOut);
    // ここで元のライターに戻ります。
    pageContext.getOut().print("<h2>doEndTag() を使用しています。</h2>");
    return EVAL_PAGE;
  } catch(IOException ioe) {
    throw new JspException(ioe.getMessage());
  } 
 }
}

ループの例

doAfterBodyEVAL_BODY_AGAIN を返すようにコーディングすることによって、カスタムタグの本文を繰り返し実行するループを作成できます。

メモ:  カスタムタグとループで使用されるスクリプト変数のスコープは、TLD ファイルまたは TEI クラスによって制御されます。詳細については、 「スクリプト変数の使用」 を参照してください。

次の JSP の例は、タグハンドラを呼び出しています。

<html>
  <body>
  <%@ taglib prefix="test" uri="DocSamples.tld" %>
    <h1>ヘッダーのループ</h1>
    <table border="1">
      <tr>
        <th>名前</th>
        <th>値</th>
      </tr>
      <test:enumloop thisEnum="<%= request.getHeaderNames() %>">
        <tr>
          <% String header = 
(String)pageContext.getAttribute("nextElement");%>
          <td><%= header %></td>  
          <td><%= request.getHeader(header) %></td>  
        </tr>
      </test:enumloop>
    </table>
  </body>
</html>

次のタグハンドラは、Enumeration タイプの属性を受け入れて、Enumeration オブジェクト内のそれぞれの名前と値 (この例では HTTP ヘッダー) をループ処理します。

import java.util.Enumeration;
import javax.servlet.jsp.*;
import javax.servlet.jsp.tagext.*;
import java.io.IOException;
public class TestBodyLoopHeaders extends BodyTagSupport {
  Enumeration thisEnum;
  public void setThisEnum(Enumeration passedEnum) {
    this.thisEnum = passedEnum;
  }
  public int doStartTag() throws JspException {
    return EVAL_BODY_INCLUDE;
  }
  public void doInitBody() throws JspException {
    if (thisEnum.hasMoreElements()) {
      pageContext.setAttribute("nextElement", 
thisEnum.nextElement());
    }
  }
  public int doAfterBody() throws JspException {
    if (thisEnum.hasMoreElements()) {
      pageContext.setAttribute("nextElement", 
thisEnum.nextElement());
      return EVAL_BODY_AGAIN;  // ループ
    }else {
      return SKIP_BODY;
    }
  }
  public int doEndTag() throws JspException {
    try {  
      // bodyContent ライターから元のライターに書き込みます。
      pageContext.getOut().print(bodyContent.getString());
      return EVAL_PAGE;
    } catch(IOException ioe) {
      throw new JspException(ioe.getMessage());
    } 
  }
}

ネストしたタグハンドラのコーディング

Tag インターフェイスまたは BodyTag インターフェイスのどちらを実装している場合でも、カスタムタグをネストできます。タグをネストすると、ネストしたタグに関するタグハンドラは、findAncestorWithClass メソッドによって親クラスへのリファレンスを取得できます。ネストしたタグハンドラは、このリファレンスを親クラスにキャスティングすることによって、親クラス内のメソッドを呼び出せます。たとえば、親クラスはネストしたタグハンドラで出力ストリームを書き出すメソッドを実装する場合があります。

次の JSP の例は、bodyparent という親タグと bodynest というネストしたタグを使用しています。

<html>
  <body>
  <%@ taglib prefix="test" uri="DocSamples.tld" %>
    <h1>ネストしたカスタムタグのテスト</h1>
      <test:bodyparent name="Johnson">
        <test:bodynest name="Lorna"/>
        <test:bodynest name="Gretchen"/>
        <test:bodynest name="Brian"/>
      </test:bodyparent>
  </body>
</html>

次のコードは、親タグに関するサンプルタグハンドラを示したものです。このサンプルには、ネストしたタグに関するタグハンドラで出力ストリームを更新するときに呼び出すことができるメソッドが含まれています。

import javax.servlet.jsp.*;
import javax.servlet.jsp.tagext.*;
import java.io.IOException;
public class TestBodyParent extends TagSupport {
  String name;

public void setName(String name) {
  this.name = name;
}
public int doStartTag() throws JspException {
  try {
    // まず、親の名前をプリントします。
    pageContext.getOut().print("<h1> 親:" + name + "</h1>");
    return EVAL_BODY_INCLUDE;
  } catch(IOException ioe) {
    throw new JspException(ioe.getMessage());
  }
}
public int doEndTag() throws JspException {
  try {  
    // グループの後にルーラを追加します。
    pageContext.getOut().print("<hr>");
    return EVAL_PAGE;
  } catch(IOException ioe) {
    throw new JspException(ioe.getMessage());
  } 
}
public void setNestedName(String name) throws JspException {
  try {  
    // ネストされている名前をプリントします。
    pageContext.getOut().print("<p>ネスト:" + name);
  } catch(IOException ioe) {
    throw new JspException(ioe.getMessage());
  }
}
}

次のコードは、ネストしたタグに関するサンプルタグハンドラを示したものです。このサンプルでは、親のタグハンドラのメソッドを呼び出して、出力ストリームを更新します。

import javax.servlet.jsp.*;
import javax.servlet.jsp.tagext.*;
import java.io.IOException;
public class TestBodyNest extends TagSupport {
  private String name;
  private TestBodyParent parent = null;
public void setName(String name) {
  this.name = name;
}
public int doStartTag() throws JspException {
  // 親へのリファレンスを検索して保存します。
  Tag t = findAncestorWithClass(this, TestBodyParent.class);
  if (t == null) {
    throw new JspException("TestBodyNest must be in TestBodyParent.");
  } else {
    parent = (TestBodyParent)t;
    return EVAL_BODY_INCLUDE;
  } 
}
public int doEndTag() throws JspException {
  // 名前を親にコピーします。
  parent.setNestedName(name);
  return EVAL_PAGE;
}
}