JSFメモ3

Light Weight Javaを見ながら、JSF拡張タグの勉強中。
但し、書籍が若干古い為、拡張タグライブラリが既に存在しないものを指していた。

  誤:<%@ taglib uri="http://myfaces.apache.org/extensions" prefix="x" %>
  正:<%@ taglib uri="http://myfaces.apache.org/tomahawk" prefix="t"%>

拡張ライブラリは「tomahawk」というプロジェクト(?)に統合されたらしいです。

今日試してみたのは以下の2つ。

  • commandSortHeader(クリックによるソート)
  • dataScroller(ページャ)

早速テスツ。

commandSortHeader
Light Weight Javaの97ページを参考に、以下のようなコードを組んでみました。

JSP

<t:dataTable id="tb1" cellspacing="0" cellpadding="0" border="0" 
  var="item" value="#{BookList.list}"
  sortColumn="#{BookList.sort}" sortAscending="#{BookList.ascending}" 
  preserveDataModel="false" preserveSort="true">
  <h:column>
	<f:facet name="header">
	  <t:commandSortHeader columnName="name" arrow="true">
		  <h:outputText value="タイトル"/>
		</t:commandSortHeader>
	</f:facet>
	<h:commandLink><h:outputText id="id1" value="#{item.itemName}"/></h:commandLink>
  </h:column>
  <h:column>
	<f:facet name="header">
	  <t:commandSortHeader columnName="price" arrow="true">
		  <h:outputText value="価格"/>
		</t:commandSortHeader>
	</f:facet>
	<h:outputText id="id2" value="#{item.price}" >
	  <f:convertNumber type="currency" currencySymbol="\\" maxFractionDigits="0"/>
	</h:outputText>
  </h:column>
</t:dataTable>

もともと、マネージドビーン「BookList」には「sort」や「ascending」なんていうフィールドはなかったのですが、何やら必要そうなので新規に追加しました。
じゃあ、肝心の型は何?と思ったものの、Light Weight Javaを読んだだけでは分からずじまい。結局、Myfacesからサンプルを落としてきてソースを追いました。sort=String, ascending=booleanでございます。
さらにさらに、ソースを読んでいるとサンプルのマネージドビーンにはsortなんてメソッドが用意してあり、そこでソート処理をゴリゴリ記述している模様。Light Weight Java読んだ感じだと、拡張タグ使えばサクっとソートまでできるなんて凄い!と思ったのに、結局自前で書くことを知り、酷くがっかりしました。

ちなみに、修正したマネージドビーン(BookList.java)は以下のような感じ。
ソート可能なリストを実装しているという意味で、スーパクラス(SotableList.java)も追加しました。
SotableList.java

public abstract class SortableList {
  private String _sort;

  private boolean _ascending;

  protected SortableList(String defaultSortColumn) {
    _sort = defaultSortColumn;
    _ascending = isDefaultAscending(defaultSortColumn);
  }

  /**
   * Sort the list.
   */
  protected abstract void sort(String column, boolean ascending);

  /**
   * Is the default sort direction for the given column "ascending" ?
   */
  protected abstract boolean isDefaultAscending(String sortColumn);

  public void sort(String sortColumn) {
    if (sortColumn == null) {
      throw new IllegalArgumentException(
          "Argument sortColumn must not be null.");
    }

    if (_sort.equals(sortColumn)) {
      // current sort equals new sortColumn -> reverse sort order
      _ascending = !_ascending;
    } else {
      // sort new column in default direction
      _sort = sortColumn;
      _ascending = isDefaultAscending(_sort);
    }

    sort(_sort, _ascending);
  }

  public String getSort() {
    return _sort;
  }

  public void setSort(String sort) {
    _sort = sort;
  }

  public boolean isAscending() {
    return _ascending;
  }

  public void setAscending(boolean ascending) {
    _ascending = ascending;
  }

}

BookList.java

public class BookList extends SortableList implements Serializable  {
  /**
   * デフォルトコンストラクタ
   */
  public BookList() {
    super(null);
  }

  @Override
  protected void sort(final String column, final boolean ascending) {
    Comparator comparator = new Comparator() {
      public int compare(Object o1, Object o2) {
        Item c1 = (Item) o1;
        Item c2 = (Item) o2;
        if (column == null) {
          return 0;
        }
        if (column.equals("name")) {
          return ascending ? 
              c1.getItemName().compareTo(c2.getItemName()) : 
              c2.getItemName().compareTo(c1.getItemName());
        } else if (column.equals("price")) {
          return ascending ?
              c1.getPrice().compareTo(c2.getPrice()) :
              c2.getPrice().compareTo(c1.getPrice());
        } else
          return 0;
      }
    };
    Collections.sort(this.list, comparator);
  }

  @Override
  protected boolean isDefaultAscending(String sortColumn) {
    return true;
  }

  <途中省略>

  public List getList() {
    this.list = this.bsService.getAllItems();
    sort(getSort(), isAscending());
    return this.list;
  }
}

端的にまとめると、ソート処理をマネージドビーンに追加し、リスト返却時にソートをかますって事ですね。

dataScroller
次はページ送り処理について。ソート処理が自前実装だったのにがっかりしていたので、こっちもそうなんじゃ?って思ってましたが、どうやらそんな事はなさそう。Light Weight Javaの98ページを参考に、以下のようなコードを組みました。

JSP

<t:dataTable id="tb1" cellspacing="0" cellpadding="0" border="0" var="item" value="#{BookList.list}"  preserveDataModel="false"  rows="5">
  <h:column>
	<f:facet name="header">
    <h:outputText value="タイトル"/>
	</f:facet>
	<h:commandLink><h:outputText id="id1" value="#{item.itemName}"/></h:commandLink>
  </h:column>
  <h:column>
	<f:facet name="header">
		<h:outputText value="価格"/>
	</f:facet>
	<h:outputText id="id2" value="#{item.price}" >
	  <f:convertNumber type="currency" currencySymbol="\\" maxFractionDigits="0"/>
	</h:outputText>
  </h:column>
</t:dataTable>
<t:dataScroller id="scroll_1" for="tb1" fastStep="5" pageCountVar="pageCount" pageIndexVar="pageIndex"
     paginator="true" paginatorMaxPages="9" immediate="true">
  <f:facet name="first"><h:graphicImage url="/htdocs/images/arrow-first.gif" /></f:facet>
  <f:facet name="last"><h:graphicImage url="/htdocs/images/arrow-last.gif" /></f:facet>
  <f:facet name="previous"><h:graphicImage url="/htdocs/images/arrow-previous.gif" /></f:facet>
  <f:facet name="next"><h:graphicImage url="/htdocs/images/arrow-next.gif" /></f:facet>
  <f:facet name="fastforward"><h:graphicImage url="/htdocs/images/arrow-ff.gif" /></f:facet>
  <f:facet name="fastrewind"><h:graphicImage url="/htdocs/images/arrow-fr.gif" /></f:facet>
</t:dataScroller>
<t:dataScroller id="scroll_2" for="tb1" pageCountVar="pageCount" pageIndexVar="pageIndex" immediate="true">
  <h:outputFormat value="ページ{0}/{1}">
    <f:param value="#{pageIndex}" />
    <f:param value="#{pageCount}" />
  </h:outputFormat>
</t:dataScroller>

5件ずつでページ送りを行うというもの。これは簡単に実装できました。
しかし、ひとつ問題が。どうやらこのページ送り、動的にテーブルから必要なデータを取ってくるようなものではなく、コレクション(List)の中から該当オブジェクトを引っ張ってくるだけなので、常に全件取得しないといけないという問題がありました。
まあ、サンプルだからいいけどこのままの実装じゃ実運用には使えないな。何か代替案があるんだろうけど・・・


追記:こんなページがあった。内容がまだ良く分からないけれど、どうも特定のデータだけを引っ張ってくるように実装する術について述べているらしい。もうちょっと調べる必要がありそうだ。