Tag
インターフェイス、IterationTag
インターフェイス、および BodyTag
インターフェイスを使用すると、テンプレートテキスト、JSP スクリプト要素、およびネストしたカスタムタグをカスタムタグの本文に含めることができます。カスタムタグが本文コンテンツとの対話を行わない場合は、TagSupport
クラスを拡張し、doStartTag
メソッドで EVAL_BODY_INCLUDE
を返します。
ただし、カスタムタグが本文コンテンツのループ処理または変更を行う必要がある場合は、次のクラスのいずれかを拡張します。
IterationTag
を実装します。doAfterBody
メソッドを提供します。タグハンドラが本文コンテンツのループ処理のみを行う必要がある場合は、このクラスを拡張します。
BodyTag
を実装します。doInitBody
メソッドと doAfterBody
メソッドを提供します。タグハンドラが本文コンテンツを変更する必要がある場合は、このクラスを拡張します。 doStartTag
メソッドは、EVAL_BODY_INCLUDE
と SKIP_BODY
の他に EVAL_BODY_BUFFERED
を返すことができます。メモ: TLD ファイル内でカスタムタグの bodycontent
要素に empty
を指定すると、本文コンテンツを無効にできます。タグハンドラは、doStartTag
メソッドで SKIP_BODY
を返すことによって、本文コンテンツを無視できます。
BodyContent オブジェクトは JspWriter のサブクラスです。JspWriter は、JSP のout
変数のために内部的に使用されるライターです。BodyContent オブジェクトは、bodyContent
変数を介して、doInitBody
、doAfterBody
、および doEndTag
で使用できます
(BodyContent オブジェクトと bodyContent
変数の、大文字の使用方法の違いに注意してください)。このオブジェクトのコンテンツは、doEndTag
メソッドで元の JspWriter と統合できます。
BodyContent オブジェクトには、出力を書き出すために使用するメソッドのほかに、このオブジェクトのコンテンツの読み取り、クリア、および取り出しを行うメソッドが含まれています。たとえば、bodyContent.getString
を使用すると、ライターのコンテンツを取り出せるだけでなく、必要であれば、そのコンテンツを元の JspWriter
と統合する前に変更できます。
メモ: bodyContent
変数を使用する前に、その値が null でないことを確認します。
doStartTag
メソッドが EVAL_BODY_BUFFERED
を返した場合、JRun はdoInitBody
メソッドを呼び出します。このメソッドは、必要に応じて本文コンテンツの初期化に使用します。次にタグハンドラが本文を処理し、JRun が doAfterBody
メソッドを呼び出します。
doAfterBody
メソッドは、SKIP_BODY
または EVAL_BODY_AGAIN
を返します。EVAL_BODY_AGAIN
を返した場合、JRun はループに戻って本文を再実行します。これにより、リストやデータベースの結果セットなどの反復データ全体を繰り返し処理できます。
次の例は、doInitBody
と doAfterBody
メソッドの使用方法を示しています。
また、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()); } } }
doAfterBody
が EVAL_BODY_AGAIN
を返すようにコーディングすることによって、カスタムタグの本文を繰り返し実行するループを作成できます。
メモ: カスタムタグとループで使用されるスクリプト変数のスコープは、TLD ファイルまたは TEI クラスによって制御されます。詳細については、 「スクリプト変数の使用」 を参照してください。
<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; } }