# テンプレートAPI

ビュークラスはformatメソッドの引数に渡されたテンプレートに変更を加えてレスポンスとします。テンプレートはfeat.templateパッケージのテンプレートAPIを使って操作します。

## テンプレートをロードする

### ファイルからロード

    // 2番目の引数をnullにするとエンコーディング名をHTMLから取得する
    HTMLTemplate template = TemplateLoader.load(new File("shop_top_page.html"), null);

### featのViewコンポーネントの場合

formatメソッドの引数で渡されるので、アプリケーションがロードする必要はありません。

    public HTMLDocument format(HTMLDocument template, String[] params, ContextAccessor acc) throws Exception {
        ...
    }

## HTMLの要素を検索する

### 要素のIDで検索する

要素につけられたIDで要素を検索するには、ドキュメントノード(HTMLDocument)または要素ノード(HTMLElement)でfindElementメソッドを使います。

    <html><body>
        <p id="hello">こんにちは</p>
    </body></html>

    try {
        HTMLElement hello = doc.findElement("hello");
        ...
    }
    catch(NodeNotFoundException ex) {
        // ノードが見つからなかったとき
        ...
    }

### class属性で検索する

ドキュメントノード、または要素ノードのfindElementByClassメソッドを使うとclass属性で要素の検索ができます。class属性に複数の名前が設定されている場合にも対応しています。

    <td>
        <span class="price number">10,000</span>円
    </td>

    try {
        HTMLElement price = cell.findElementByClass("price");
        ...
    }
    catch(NodeNotFoundException ex) {
        // ノードが見つからなかったとき
        ...
    }

### XPathで検索する

ドキュメントノードのfindメソッドでXPathを使ったノードの検索ができます。

#### 単一のノードの検索

    try {
        HTMLElement title = (HTMLElement)doc.find("/html/head/title");
        ...
    }
    catch (NodeNotFoundException ex) {
        // ノードが見つからなかったとき
        ...
    }

#### 複数ノードの検索

    // すべてのA要素を検索
    HTMLNodeList link = doc.select("//a");

### NodeSelectorで検索する

feat.v1.template.NodeSelectorクラスにはgetで始まる名前のメソッドとselectで始まる名前のメソッドがあります。get〜メソッドは単一のノードを、select〜メソッドは複数のノードを返します。

ノードが見つからなかったとき、get〜メソッドはnullを、select〜メソッドは空のリストやマップを返します。

    // 顧客名を抽出
    HTMLElement body; // body要素
    ...
    HTMLNodeList customerNames = NodeSelector.selectElementsByClass(new NodeTreeIterator(body), "customer-name");

#### NodeSelectorと組み合わせて使うと便利なイテレータ

NodeSelectorのメソッドのほとんどはIteratorを引数にとります。HTMLのツリーからノードを見つけ出すにはツリーのノードを順にたどる必要がありますが、NodeTreeIteratorを使うと指定のノードを頂点とするノードツリーをたどることができます。これにより、HTMLツリーの任意の部分ツリーからノードを検索することができます。

    // ドキュメントで最初に見つかったtitle要素を返す
    HTMLELement title = NodeSelector.getTag(new NodeTreeIterator(doc), "title");

NodeListIteratorは指定のノードの兄弟ノードをたどります。

    // 表のtr要素を取り出す
    try {
        HTMLElement table = doc.findElement("search-results");
        HTMLNodeList rows = NodeSelector.selectTags(new NodeListIterator(table.getFirstChild()), "tr");
        ...
    }
    catch(NodeNotFoundException ex) {
        ...
    }

## 要素の作成

新しい要素を作成するときはfeat.v1.template.NodeFactoryクラスを使います。NodeFactoryクラスでは要素のほか、ドキュメントノード、テキストノード、注釈ノードも作ることができます。

    HTMLElement copyright = NodeFactory.createElement("b", true);
    copyright.addText("Copyright (C) 2006 ...");
    body.addChild(copyright);

## ノードの複製

ノードをコピーするにはノードのcopyメソッドを使います。引数deepがtrueの場合、ノードツリー全体をコピーします。引数deepがfalseの場合でもオブジェクトのプロパティはディープコピーとなります。copyメソッドが何をコピーするかは下の表を参照してください。

引数deepの値 | 子ノード | 属性 | 親ノードへの参照 | 前の兄弟ノードへの参照 | 次の兄弟ノードへの参照
--- | --- | --- | --- | --- | ---
true | ディープコピー | ディープコピー | コピーしない | コピーしない | コピーしない
false | コピーしない | ディープコピー | コピーしない | コピーしない | コピーしない

コピーするノードに要素ノードが含まれている場合は注意が必要です。copyメソッドは要素のID属性もコピーするので、コピーした要素をドキュメントツリーに追加したときにIDが重複してしまう可能性があります。このような場合、ID属性を削除するコードをその都度書かなければなりません。

## テキストノードを操作する

テンプレートAPIではテキストノードをノードオブジェクトとして意識する必要はあまりないかもしれません。CompositeNodeのメソッドでテキストノードを操作する場合がほとんどです。また、CompositeNodeやHTMLTextのメソッドでテキストを読み書きするとき、HTMLの特殊文字は自動的にエスケープ/アンエスケープされます。

エスケープしないで文字列を追加したい場合は、HTMLTextのsetRowTextメソッドを使います。

## 要素を削除する

ノードをドキュメントツリーから削除するときはHTMLNodeのdetachメソッドを使います。このメソッドはノードを削除するわけではなく、指定ノードの親ノードと兄弟ノードとの関係を断ち切るだけです。参照がなくなればオブジェクトはいつか削除されます。

detachメソッドで切り離したサブツリーは再びドキュメントツリーに追加できます。元のドキュメントツリーだけでなく、どのドキュメントツリーにも追加できます。ただし、同じインスタンスを複数のドキュメントツリーに追加することはできません。

## フォームに値を入れる

フォームもドキュメントツリー以下の要素の集合でしかないので、ほかの要素と同様に扱うことが可能です。しかし、ドキュメントツリーを直接操作してフォームの値を読み書きするのは非常に面倒です。テンプレートAPIにはフォームの値の読み書きを簡単にするためにfeat.v1.template.form.Formクラスがあります。

### Formオブジェクトの作成

    Form form = new Form(formElement);

### コントロールに値を設定する

単一の値をセットする場合。

    form.setValue("controlName", "value");

リストやチェックボックスなど、複数の値を許容するコントロールに値をセットする場合。

    form.setValues("controlName", new String[] {"tokyo", "kanagawa"});

### コントロールのラベルを設定する

コントロールに対応したlabel要素の値を設定することができます。input要素のボタンの場合はlabel要素ではなくinput要素のvalue属性が設定されます。

    form.setLabel("controlName", "送信");

名前を共有するラジオボタン、チェックボックスの場合は同名のコントロールに対応したlabel要素を一度に指定します。Formクラスでは、名前を共有するコントロールのラベルを個別に設定することはできません。

    form.setLabel("controlName", new String[] {"東証", "大証", "名証", "JQ", "HC"});

$Id: template.html,v 1.4 2006/02/01 15:43:50 sugimotokenichi Exp $