Powered by SmartDoc

カスタム・コンポーネント

カスタム・コンポーネントの作成技法

カスタム・コンポーネントとは何か

JSFでは、UIコンポーネントを自分で作ることができます。こうした、自作したコンポーネントのことを「カスタム・コンポーネント」と言います。

では、カスタム・コンポーネントを使った例を紹介しましょう。次のようなJSPがあります。

component.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のプログラム
設定ファイル

では、これらのプログラムや設定ファイルについて見ていきましょう。

なお、Javaプログラムは、パッケージ宣言をする必要があります。パッケージ宣言をしないとうまく動きません。

UIコンポーネントクラス

UIコンポーネントクラスは、UIコンポーネントを表すクラスです。すべてのUIコンポーネントは、UIComponentBaseクラスを継承しています。

では、このサンプルのUIコンポーネントクラスである、UIBlink.javaを見てみましょう。

UIBlink.java
package blink;

import javax.faces.component.UIComponentBase;

public class UIBlink extends UIComponentBase {
    public String getFamily() {
        return "MyFamily";
    }
}

このクラスでは、getFamilyメソッドが定義されています。このメソッドでは「コンポーネント・ファミリ」を設定しています。コンポーネント・ファミリは、UIコンポーネントと後述するレンダラを関連づけるために使われます。

タグハンドラクラス

タグハンドラクラスは、JSPからUIコンポーネントを利用するためのクラスです。次のいずれかのクラスを継承します。

UIComponentTagを継承することで十分な場合がほとんどです。

では、タグハンドラクラスのプログラムを見てみましょう。

BlinkTag.java
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メソッドを使います。

では、レンダラを行うプログラムを見てみましょう。

BlinkRenderer.java
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)

タグライブラリディスクリプタ(TLD)は、JSPでどんなタグを利用できるか定義するための設定ファイルです。

さっそく、見てみましょう。

blank.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>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 の設定

faces-config.xmlでは、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>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の記述

最後に、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は次のようになります。

component.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コンポーネントクラス

UIコンポーネントクラスは、前節とほぼ同様のスタイルで問題ありません。

UINow.java
package now;

import javax.faces.component.UIComponentBase;

public class UINow extends UIComponentBase {
    public String getFamily() {
        return "MyFamily";
    }
}

タグハンドラクラス

タグハンドラクラスも、前節とほぼ同様のスタイルになります。

NowTag.java
package now;

import javax.faces.webapp.UIComponentTag;

public class NowTag extends UIComponentTag {

    public String getComponentType() {
        return "Now";
    }

    public String getRendererType() {
        return "NowRenderer";
    }

}

レンダラ

次にレンダラです。このサンプルプログラムの場合、単に、現在の日時を表示するだけでよいのです。カスタムタグから出力されるHTMLには、特殊なタグは必要としていません。

このように、タグを出力する必要のないときは、encodeBeginメソッドだけを用意すれば良いのです。

NowRenderer.java
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)

TLDです。

now.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 の設定

faces-config.xmlは、前節とほぼ同様のスタイルで問題ありません。

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の記述

最後に、JSPの記述です。my:nowという空要素タグは、上述のような仕組みで現在の日時が表示されます。

<f:view>
    <my:now />
</f:view>

属性の記述

サンプルプログラム

前節の「空要素タグ」のカスタム・コンポーネントに属性を付け加え、日時の表示のスタイルを切り替えられるようにしましょう。

JSPは次のようになります。

component.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コンポーネントクラス

UIコンポーネントクラスは、前節のものとまったく同じです。

UINow.java
package now;

import javax.faces.component.UIComponentBase;

public class UINow extends UIComponentBase {
    public String getFamily() {
        return "MyFamily";
    }
}

タグハンドラクラス

タグハンドラクラスには、前節のものと比べると、3つのメソッドが加わっています。

NowTag.java
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属性を設定しています。

レンダラ

NowRenderer.java
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)

TLDでは、tag要素の中にattribute要素があります。

now.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>
        <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 の設定

faces-config.xmlは、前節とまったく同じものになります。

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>