如〈了解生命週期與架構〉中提過,若是巢狀標籤中的內層標籤,則還會呼叫標籤處理器的 setParent()
方法,並傳入外層標籤處理器的實例。
同樣地,在這邊以開發 <f:choose>
、<f:when>
與 <f:otherwise>
作為示範。首先是標籤處理器的開發:
package cc.openhome;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.tagext.TagSupport;
public class ChooseTag extends TagSupport {
private boolean matched;
@Override
public int doStartTag() throws JspException {
matched = false;
return EVAL_BODY_INCLUDE;
}
public boolean isMatched() {
return matched;
}
public void setMatched(boolean matched) {
this.matched = matched;
}
}
ChooseTag
基本上什麼都不作,之所以要重新定義 doStartTag()
,因為 TagSupport
的 doStartTag()
方法預設傳回 SKIP_BODY
,因為 <f:choose>
用來包括內層標籤,你不能忽略本體內容,所以必須傳回 EVAL_BODY_INCLUDE
。另一方面,記得 Tag
的實 例會在不使用時放回標籤池,所以若標籤上一次執行過後有狀態存在,下次再度從標籤池中取出時,必須考慮進行狀態重置的動作,這個動作我們放在 doStartTag()
中完成。
package cc.openhome;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.JspTagException;
import javax.servlet.jsp.tagext.BodyTagSupport;
import javax.servlet.jsp.tagext.JspTag;
import javax.servlet.jsp.tagext.Tag;
public class WhenTag extends BodyTagSupport {
private boolean test;
@Override
public int doStartTag() throws JspException {
JspTag parent = null;
if (!((parent = getParent()) instanceof ChooseTag)) {
throw new JspTagException("必須置於choose標籤中");
}
if (((ChooseTag) parent).isMatched() || !test) {
return SKIP_BODY;
}
((ChooseTag) parent).setMatched(true);
return EVAL_BODY_INCLUDE;
}
public void setTest(boolean test) {
this.test = test;
}
}
在這邊,doStartTag()
判斷是否包括在 <f:choose>
標籤中,判斷先前的 <f:when>
是否曾經通過測試,以決定是否要執行或忽略自己的本體內容。
package cc.openhome;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.JspTagException;
import javax.servlet.jsp.tagext.JspTag;
import javax.servlet.jsp.tagext.Tag;
import javax.servlet.jsp.tagext.TagSupport;
public class OtherwiseTag extends TagSupport {
@Override
public int doStartTag() throws JspException {
JspTag parent = null;
if (!((parent = getParent()) instanceof ChooseTag)) {
throw new JspTagException("必須置於choose標籤中");
}
if (((ChooseTag) parent).isMatched()) {
return SKIP_BODY;
}
return EVAL_BODY_INCLUDE;
}
}
基本上,OtherwiseTag
的 doStartTag()
與 WhenTag
是類似的,只不過不用檢查 test`屬性。記得在TLD檔案中加入標籤定義:
f.tld
<?xml version="1.0" encoding="UTF-8"?>
<taglib version="2.1" xmlns="http://java.sun.com/xml/ns/j2ee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee
web-jsptaglibrary_2_1.xsd">
<tlib-version>1.0</tlib-version>
<short-name>f</short-name>
<uri>https://openhome.cc/jstl/fake</uri>
// 略...
<tag>
<name>choose</name>
<tag-class>cc.openhome.ChooseTag</tag-class>
<body-content>JSP</body-content>
</tag>
<tag>
<name>when</name>
<tag-class>cc.openhome.WhenTag</tag-class>
<body-content>JSP</body-content>
<attribute>
<name>test</name>
<required>true</required>
<rtexprvalue>true</rtexprvalue>
<type>boolean</type>
</attribute>
</tag>
<tag>
<name>otherwise</name>
<tag-class>cc.openhome.OtherwiseTag</tag-class>
<body-content>JSP</body-content>
</tag>
</taglib>