JSFでは、UIコンポーネントを自分で作ることができます。こうした、自作したコンポーネントのことを「カスタム・コンポーネント」と言います。
では、カスタム・コンポーネントを使った例を紹介しましょう。次のようなJSPがあります。
<%@ page contentType="text/html; charset=Shift_JIS" %>
<%@ taglib uri="http://java.sun.com/jsf/html" prefix="h" %>
<%@ taglib uri="http://java.sun.com/jsf/core" prefix="f" %>
<%@ taglib uri="/WEB-INF/blink.tld" prefix="my" %>
<html>
<head>
<link href="style.css" type="text/css" rel="stylesheet" />
<title>JSF: カスタムコンポーネントのテスト (1)</title>
</head>
<body>
<h1>JSF: カスタムコンポーネントのテスト (1)</h1>
<f:view>
<my:blink>Hello !</my:blink>
</f:view>
</body>
</html>
このJSPでは、次のようなカスタム・タグが使われています。
<my:blink>Hello !</my:blink>
このJSPを動かすと、図11.1[カスタム・タグを使った例]のように動作します。
このJSPでは、my:blinkという要素の内容が"Hello !"という文字列になっています。実行画面では、この文字列が点滅しています。文字列の点滅には、HTMLのblinkタグが使われています。
カスタムコンポーネントを作成するには、次のファイルが必要になります。
では、これらのプログラムや設定ファイルについて見ていきましょう。
なお、Javaプログラムは、パッケージ宣言をする必要があります。パッケージ宣言をしないとうまく動きません。
UIコンポーネントクラスは、UIコンポーネントを表すクラスです。すべてのUIコンポーネントは、UIComponentBaseクラスを継承しています。
では、このサンプルのUIコンポーネントクラスである、UIBlink.javaを見てみましょう。
package blink;
import javax.faces.component.UIComponentBase;
public class UIBlink extends UIComponentBase {
public String getFamily() {
return "MyFamily";
}
}
このクラスでは、getFamilyメソッドが定義されています。このメソッドでは「コンポーネント・ファミリ」を設定しています。コンポーネント・ファミリは、UIコンポーネントと後述するレンダラを関連づけるために使われます。
タグハンドラクラスは、JSPからUIコンポーネントを利用するためのクラスです。次のいずれかのクラスを継承します。
UIComponentTagを継承することで十分な場合がほとんどです。
では、タグハンドラクラスのプログラムを見てみましょう。
package blink;
import javax.faces.webapp.UIComponentTag;
public class BlinkTag extends UIComponentTag {
public String getComponentType() {
return "Blink";
}
public String getRendererType() {
return "BlinkRenderer";
}
}
まず、BlinkTagクラスは、UIComponentTagを継承していることがわかります。
また、メソッドが2つあります。
getComponentTypeメソッドでは、コンポーネントの名前を返します。この名前は、faces-config.xmlで指定します。
getRendererTypeメソッドでは、レンダラの名前を返します。この名前も、faces-config.xmlで指定します。
「レンダラ」は2つの機能を持っています。
1つめは、「エンコード」という機能です。HTMLのフォームから入力されたデータを、UIコンポーネントに設定します。
2つめに、「デコード」という機能です。UIコンポーネントから、HTMLを出力します。
本章では、「デコード」の機能のみを取り扱います。
レンダラを行うクラスは、Rendererクラスを継承します。エンコードには、encodeBeginメソッドとencodeEndメソッドを使います。
では、レンダラを行うプログラムを見てみましょう。
package blink;
import java.io.IOException;
import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.context.ResponseWriter;
import javax.faces.render.Renderer;
public class BlinkRenderer extends Renderer {
public void encodeBegin(
FacesContext context, UIComponent component)
throws IOException {
if ((context == null) || (component == null)) {
throw new NullPointerException();
}
ResponseWriter writer = context.getResponseWriter();
writer.write("<blink>");
}
public void encodeEnd(
FacesContext context, UIComponent component)
throws IOException {
if ((context == null) || (component == null)) {
throw new NullPointerException();
}
ResponseWriter writer = context.getResponseWriter();
writer.write("</blink>");
}
}
このプログラムでは、メソッドが2つ定義されています。
1つめは、encodeBeginメソッドです。このメソッドでは、HTMLの開始タグを出力します。
具体的には、FacesContextからResponseWriterオブジェクトを取得して、HTMLのタグを書き出しています。
ResponseWriter writer = context.getResponseWriter();
writer.write("<blink>");
2つめは、encodeEndメソッドです。このメソッドでは、HTMLの終了タグを出力します。
タグライブラリディスクリプタ(TLD)は、JSPでどんなタグを利用できるか定義するための設定ファイルです。
さっそく、見てみましょう。
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE taglib
PUBLIC "-//Sun Microsystems, Inc.//DTD JSP Tag Library 1.2//EN"
"http://java.sun.com/dtd/web-jsptaglibrary_1_2.dtd">
<taglib>
<tlib-version>1.0</tlib-version>
<jsp-version>1.2</jsp-version>
<short-name>Blink !</short-name>
<tag>
<name>blink</name>
<tag-class>blink.BlinkTag</tag-class>
<body-content>scriptless</body-content>
</tag>
</taglib>
注目すべきなのは、tag要素です。
<tag>
<name>blink</name>
<tag-class>blink.BlinkTag</tag-class>
<body-content>scriptless</body-content>
</tag>
このうち、name要素はタグの名前です。tag-class要素はタグハンドラクラスの名前となります。body-content要素の"scriptless"は、要素の内容にテキストやValue Binding式などが入ることを示します。
faces-config.xmlでは、UIコンポーネントとレンダラを設定します。
<?xml version="1.0"?>
<!DOCTYPE faces-config PUBLIC
"-//Sun Microsystems, Inc.//DTD JavaServer Faces Config 1.1//EN"
"http://java.sun.com/dtd/web-facesconfig_1_1.dtd">
<faces-config>
<component>
<component-type>Blink</component-type>
<component-class>blink.UIBlink</component-class>
</component>
<render-kit>
<renderer>
<component-family>MyFamily</component-family>
<renderer-type>BlinkRenderer</renderer-type>
<renderer-class>blink.BlinkRenderer</renderer-class>
</renderer>
</render-kit>
</faces-config>
まず、component要素です。
<component>
<component-type>Blink</component-type>
<component-class>blink.UIBlink</component-class>
</component>
component-type要素には、タグハンドラクラスのcomponentTypeプロパティの値をセットします。component-class要素には、UIコンポーネントクラスのクラス名をセットします。
次に、render-kit要素です。
<render-kit>
<renderer>
<component-family>MyFamily</component-family>
<renderer-type>BlinkRenderer</renderer-type>
<renderer-class>blink.BlinkRenderer</renderer-class>
</renderer>
</render-kit>
render-kit要素の子要素であるrenderer要素について見てみましょう。
component-family要素には、UIコンポーネントクラスのfamilyプロパティの値をセットします。renderer-type要素には、タグハンドラクラスのrendererTypeプロパティをセットします。renderer-class要素には、レンダラクラス名をセットします。
最後に、JSPの記述をもう一度見てみましょう。
<%@ taglib uri="/WEB-INF/blink.tld" prefix="my" %>
......
<f:view>
<my:blink>Hello !</my:blink>
</f:view>
先に見たように、my:blinkというタグは、レンダラの働きによってblinkというHTMLのタグに置き換わります。
ディレクティブには次のような記述があります。
<%@ taglib uri="/WEB-INF/blink.tld" prefix="my" %>
このuri属性の指定によって、TLDをWEB-INFフォルダに置いておくことになります。
ここでは、現在の日時を表示するカスタム・コンポーネントを作成してみましょう。
JSPは次のようになります。
<%@ page contentType="text/html; charset=Shift_JIS" %>
<%@ taglib uri="http://java.sun.com/jsf/html" prefix="h" %>
<%@ taglib uri="http://java.sun.com/jsf/core" prefix="f" %>
<%@ taglib uri="/WEB-INF/now.tld" prefix="my" %>
<html>
<head>
<link href="style.css" type="text/css" rel="stylesheet" />
<title>JSF: カスタムコンポーネントのテスト (2)</title>
</head>
<body>
<h1>JSF: カスタムコンポーネントのテスト (2)</h1>
<f:view>
<my:now />
</f:view>
</body>
</html>
次のmy:nowというタグによって現在の日時が表示されます。このタグは、見ての通り子要素がない空要素タグになっています。
<my:now />
このJSPを動かすと、図11.2[カスタム・タグを使った例]のように動作します。
この節では、このような空要素タグの作り方について見てみましょう。
UIコンポーネントクラスは、前節とほぼ同様のスタイルで問題ありません。
package now;
import javax.faces.component.UIComponentBase;
public class UINow extends UIComponentBase {
public String getFamily() {
return "MyFamily";
}
}
タグハンドラクラスも、前節とほぼ同様のスタイルになります。
package now;
import javax.faces.webapp.UIComponentTag;
public class NowTag extends UIComponentTag {
public String getComponentType() {
return "Now";
}
public String getRendererType() {
return "NowRenderer";
}
}
次にレンダラです。このサンプルプログラムの場合、単に、現在の日時を表示するだけでよいのです。カスタムタグから出力されるHTMLには、特殊なタグは必要としていません。
このように、タグを出力する必要のないときは、encodeBeginメソッドだけを用意すれば良いのです。
package now;
import java.util.Date;
import java.text.DateFormat;
import java.io.IOException;
import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.context.ResponseWriter;
import javax.faces.render.Renderer;
public class NowRenderer extends Renderer {
public void encodeBegin(
FacesContext context, UIComponent component)
throws IOException {
if ((context == null) || (component == null)) {
throw new NullPointerException();
}
Date d = new Date();
DateFormat df = DateFormat.getDateTimeInstance();
ResponseWriter writer = context.getResponseWriter();
writer.write(df.format(d));
}
}
このプログラムのencodeBeginメソッドでは、DateFormat型のオブジェクトを生成し、ResponseWriter型のオブジェクトに現在の日時の情報を書き出しています。
TLDです。
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE taglib
PUBLIC "-//Sun Microsystems, Inc.//DTD JSP Tag Library 1.2//EN"
"http://java.sun.com/dtd/web-jsptaglibrary_1_2.dtd">
<taglib>
<tlib-version>1.0</tlib-version>
<jsp-version>1.2</jsp-version>
<short-name>now</short-name>
<tag>
<name>now</name>
<tag-class>now.NowTag</tag-class>
<body-content>empty</body-content>
</tag>
</taglib>
tag要素を見てみましょう。
<tag>
<name>now</name>
<tag-class>now.NowTag</tag-class>
<body-content>empty</body-content>
</tag>
body-content要素の"empty"は、このUIコンポーネントが空要素タグで表現されることを示します。
faces-config.xmlは、前節とほぼ同様のスタイルで問題ありません。
<?xml version="1.0"?>
<!DOCTYPE faces-config PUBLIC
"-//Sun Microsystems, Inc.//DTD JavaServer Faces Config 1.1//EN"
"http://java.sun.com/dtd/web-facesconfig_1_1.dtd">
<faces-config>
<component>
<component-type>Now</component-type>
<component-class>now.UINow</component-class>
</component>
<render-kit>
<renderer>
<component-family>MyFamily</component-family>
<renderer-type>NowRenderer</renderer-type>
<renderer-class>now.NowRenderer</renderer-class>
</renderer>
</render-kit>
</faces-config>
最後に、JSPの記述です。my:nowという空要素タグは、上述のような仕組みで現在の日時が表示されます。
<f:view>
<my:now />
</f:view>
前節の「空要素タグ」のカスタム・コンポーネントに属性を付け加え、日時の表示のスタイルを切り替えられるようにしましょう。
JSPは次のようになります。
<%@ page contentType="text/html; charset=Shift_JIS" %>
<%@ taglib uri="http://java.sun.com/jsf/html" prefix="h" %>
<%@ taglib uri="http://java.sun.com/jsf/core" prefix="f" %>
<%@ taglib uri="/WEB-INF/now.tld" prefix="my" %>
<html>
<head>
<link href="style.css" type="text/css" rel="stylesheet" />
<title>JSF: カスタムコンポーネントのテスト (3)</title>
</head>
<body>
<h1>JSF: カスタムコンポーネントのテスト (3)</h1>
<f:view>
<my:now format="FULL" /><br />
<my:now format="LONG" /><br />
<my:now format="MEDIUM" /><br />
<my:now format="SHORT" /><br />
</f:view>
</body>
</html>
このJSPを動かすと、図11.3[カスタム・タグを使った例]のように動作します。
<my:now format="FULL" /><br />
<my:now format="LONG" /><br />
<my:now format="MEDIUM" /><br />
<my:now format="SHORT" /><br />
my:nowタグにformat属性を加えることによって、表示される日時のスタイルが変わります。
UIコンポーネントクラスは、前節のものとまったく同じです。
package now;
import javax.faces.component.UIComponentBase;
public class UINow extends UIComponentBase {
public String getFamily() {
return "MyFamily";
}
}
タグハンドラクラスには、前節のものと比べると、3つのメソッドが加わっています。
package now;
import javax.faces.component.UIComponent;
import javax.faces.webapp.UIComponentTag;
public class NowTag extends UIComponentTag {
private String format;
public String getComponentType() {
return "Now";
}
public String getRendererType() {
return "NowRenderer";
}
protected void setProperties(UIComponent component) {
super.setProperties(component);
UINow now = (UINow)component;
now.getAttributes().put("format", format);
}
public String getFormat() {
return format;
}
public void setFormat(String format) {
this.format = format;
}
}
次の3つのメソッドが加わってます。
protected void setProperties(UIComponent component) {
super.setProperties(component);
UINow now = (UINow)component;
now.getAttributes().put("format", format);
}
public String getFormat() {
return format;
}
public void setFormat(String format) {
this.format = format;
}
getFormatとsetFormatメソッドによって、属性名であるformatが、このクラスではBeanのプロパティになっています。
setPropertiesメソッドでは、UIコンポーネントにformat属性を設定しています。
package now;
import java.util.Date;
import java.text.DateFormat;
import java.io.IOException;
import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.context.ResponseWriter;
import javax.faces.render.Renderer;
public class NowRenderer extends Renderer {
public void encodeBegin(
FacesContext context, UIComponent component)
throws IOException {
if ((context == null) || (component == null)) {
throw new NullPointerException();
}
Date d = new Date();
DateFormat df;
UINow now = (UINow)component;
String format = (String)now.getAttributes().get("format");
if (format.equals("FULL")) {
df = DateFormat.getDateTimeInstance(
DateFormat.FULL, DateFormat.FULL);
} else if (format.equals("LONG")) {
df = DateFormat.getDateTimeInstance(
DateFormat.LONG, DateFormat.LONG);
} else if (format.equals("MEDIUM")) {
df = DateFormat.getDateTimeInstance(
DateFormat.MEDIUM, DateFormat.MEDIUM);
} else if (format.equals("SHORT")) {
df = DateFormat.getDateTimeInstance(
DateFormat.SHORT, DateFormat.SHORT);
} else {
df = DateFormat.getDateTimeInstance();
}
ResponseWriter writer = context.getResponseWriter();
writer.write(df.format(d));
}
}
次の部分で、UIコンポーネントから、format属性の値を取得しています。
UINow now = (UINow)component;
String format = (String)now.getAttributes().get("format");
TLDでは、tag要素の中にattribute要素があります。
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE taglib
PUBLIC "-//Sun Microsystems, Inc.//DTD JSP Tag Library 1.2//EN"
"http://java.sun.com/dtd/web-jsptaglibrary_1_2.dtd">
<taglib>
<tlib-version>1.0</tlib-version>
<jsp-version>1.2</jsp-version>
<short-name>now</short-name>
<tag>
<name>now</name>
<tag-class>now.NowTag</tag-class>
<body-content>empty</body-content>
<attribute>
<name>format</name>
<required>true</required>
</attribute>
</tag>
</taglib>
attributeには2つの子要素があります。name要素で属性名を指定しています。required要素でこの属性が必須かどうかを示しています。
<attribute>
<name>format</name>
<required>true</required>
</attribute>
faces-config.xmlは、前節とまったく同じものになります。
<?xml version="1.0"?>
<!DOCTYPE faces-config PUBLIC
"-//Sun Microsystems, Inc.//DTD JavaServer Faces Config 1.1//EN"
"http://java.sun.com/dtd/web-facesconfig_1_1.dtd">
<faces-config>
<component>
<component-type>Now</component-type>
<component-class>now.UINow</component-class>
</component>
<render-kit>
<renderer>
<component-family>MyFamily</component-family>
<renderer-type>NowRenderer</renderer-type>
<renderer-class>now.NowRenderer</renderer-class>
</renderer>
</render-kit>
</faces-config>