Home of: [工房 "藤車"] > [SourceForge.net における PageMixer]

部分シーケンスの反復

本節では、 PageMixer における HTML ページの一部を反復する方法に関して説明します。 説明のため、 本節では "買い物篭" 例と、 HTML の "<tr>" および対応する "</tr>" に挟まれた部分を繰り返しつつデータをはめ込むフィルタを用います。


<table class="basket">
  <tr class="Shop-Basket-Entry">
    <td>
      <span class="Shop-Basket-EntryName">(名前)</span>
    </td>
    <td>
      <span class="Shop-Basket-EntryPrice">(価格)</span>
    </td>
    <td>
      <input type="text" class="Shop-Basket-EntryCount"
                name="xxx" value="0">
    </td>
    <td>
      <span class="Shop-Basket-EntryTotal">(小計)</span>
    </td>
  </tr>
</tabl>

処理前

<table class="basket">
  <tr class="Shop-Basket-Entry">
    <td>
      <span class="Shop-Basket-EntryName">バナナ</span>
    </td>
    <td>
      <span class="Shop-Basket-EntryPrice">120</span>
    </td>
    <td>
      <input type="text" class="Shop-Basket-EntryCount"
             name="Shop.Basket.Entry.ID:i0001" value="1">
    </td>
    <td>
      <span class="Shop-Basket-EntryTotal">120</span>
    </td>
  </tr>
  <tr class="Shop-Basket-Entry">
    <td>
      <span class="Shop-Basket-EntryName">ブロッコリ</span>
    </td>
    <td>
      <span class="Shop-Basket-EntryPrice">165</span>
    </td>
    <td>
      <input type="text" class="Shop-Basket-EntryCount"
             name="Shop.Basket.Entry.ID:i0002" value="2">
    </td>
    <td>
      <span class="Shop-Basket-EntryTotal">330</span>
    </td>
  </tr>

          :
          :

</table>

処理後

概要

クラス図

本節におけるクラス図を以下に示します。

クラス図
クラス図 (クリックで拡大表示)

色づけされているのが本節で定義するクラスで、 それ以外は PageMixer において定義済みです。

オブジェクト図

本節におけるオブジェクト図を以下に示します。

Object diagram
オブジェクト図 (クリックで拡大表示)

シーケンス図

本節におけるシーケンス図を以下に示します。

最初の図は、 繰り返し対象となる部分シーケンスの先頭を、 フィルタが検知するまでのシーケンスです。

シーケンス図 - (1)
シーケンス図 - (1) (クリックで拡大表示)

次の図は、 DataProvider から反復子を取得してからのシーケンスです。

シーケンス図 - (2)
シーケンス図 - (2) (クリックで拡大表示)

クラス名

本チュートリアルでは、 クラスは全てクラス名のみで表記されています。 完全な名称は以下の通りです。

PageMixer フレームワークのクラス

表記完全名
ConsumerContext jp.ne.dti.lares.foozy.pagemixer.mixer.ConsumerContext
DataProvider jp.ne.dti.lares.foozy.pagemixer.mixer.DataProvider
HTMLSymbolSet jp.ne.dti.lares.foozy.pagemixer.HTMLSymbolSet
ListDataProvider jp.ne.dti.lares.foozy.pagemixer.mixer.ListDataProvider
SequenceDataIterationFilter jp.ne.dti.lares.foozy.pagemixer.mixer.SequenceDataIterationFilter
SequenceWatcher jp.ne.dti.lares.foozy.pagemixer.mixer.SequenceWatcher

チュートリアルの固有クラス

表記完全名
BasketEntry common.BasketEntry
BasketEntryIterationFilter pagemixer.filter.BasketEntryIterationFilter
Bootstrap pagemixer.filter.Bootstrap
EntryCountAttr pagemixer.filter.BasketEntryIterationFilter.EntryCountAttr
EntryNameText pagemixer.filter.BasketEntryIterationFilter.EntryNameText
EntryPriceText pagemixer.filter.BasketEntryIterationFilter.EntryPriceText
EntryTotalText pagemixer.filter.BasketEntryIterationFilter.EntryTotalText

データクラスの定義

本節では、 HTML ページの一部を、 以下のクラスオブジェクトの集合のために繰り返します。


public class BasketEntry
{
    // "ShoppingItem" には ID, 名前, 価格が格納されています
    final
    private ShoppingItem item_;

    private int count_;

    ////////////////////////////////////////

    public BasketEntry(ShoppingItem item, int count){
        item_ = item;
        count_ = count;
    }

    ////////////////////////////////////////

    public String getID(){ return item_.getID(); }

    public String getName(){ return item_.getName(); }

    public String getName(Locale locale){
        return item_.getName(locale);
    }

    public int getPrice(){ return item_.getPrice(); }

    public void setCount(int count){ count_ = count; }

    public int getCount(){ return count_; }
}

繰り返しのためのデータ

DataProvider の生成

HTML ページの一部を繰り返すためには、 データ集合の供給が必要です。 与えられたデータ集合によって、 繰り返しの回数や、 どのようなデータをはめ込むのかを知ることが出来ます。

PageMixer フレームワークは、 データ集合の供給を行う主体を、 "DataProvider" として一般化しています。 このインタフェースは、 データ保持の実現を隠蔽します。

このインタフェースは、以下のように、 1つのメソッドだけを定義しています。


public interface  DataProvider
{
    public java.util.Iterator provide();
}

DataProvider

"provide()" メソッドは、 データ集合を取り出すための java.util.Iterator を返します。 必要な時に、 DataProvider#provide を起動することで、 常にデータ集合を取り出すための Iterator を取得することが出来ます。

本節での例の場合、 BasketEntry 集合を提供する DataProvider は、 以下のようにして生成することが出来ます。


    // jav.util.List 型の "entryList" は
    // "BasketEntry" を格納しています
    ListDataProvider provider =
    new ListDataProvider(entryList);

DataProvider の生成

"ListDataProvider" は、 java.util.List による DataProvider 実装クラスです。

勿論、 BasketEntry オブジェクト群を保持する "買い物篭" クラスを定義し、 それに DataProvider を実装させることも可能です。

備考: SequenceDataIterationFilter クラスの getIterator(Object) をオーバライドするのであれば、 DataProvider を生成する必要はありません。

このオーバライドにより、 利用者固有のオブジェクトから直接 Iterator を取得できるようになります。

データおよび DataProvider の識別子の定義

DataProvider およびはめ込むデータを識別するために、 SequenceDataIterationFilter は、 以下のような2つの鍵を必要とします。


Object providerKey = "Shop.Basket.BasketEntryProvider";

Object dataKey = "Shop.Basket.BasketEntry";

データおよび DataProvider の識別子

"providerKey" は DataProvider を、 "dataKey" ははめ込み対象データ (BasketEntryのことです)を識別します。

連携フィルタ群の定義

本節では、 "SequenceDataIterationFilter" を主フィルタとして用いますが、 Token 部分シーケンスを繰り返すことだけが SequenceDataIterationFilter が Token シーケンスに直接的に及ぼす唯一の影響です。 このフィルタ自身は、 反復された Token 部分シーケンスにデータをはめ込むことはしません。

データはめ込みを行う代わりに、 このフィルタはその責務を連携フィルタ群に委譲します。 この分離によって SequenceDataIterationFilter を単純で、且つ再利用性が高いものにできます。

SequenceDataIterationFilter#push(Filter) は連携フィルタ群を登録するのに使用され、 登録されたフィルタは、 反復された部分シーケンスにデータをはめ込むためだけに使用されます。

"買い物篭" の例の場合、 以下のようなフィルタが必要でしょう。

実のところ、 これらのフィルタはこの節のサンプルフィルタの内部クラスとして、 既に定義されています。 それぞれのフィルタは、 これまでの節での知識で十分理解できる程度の、 非常に単純で簡単なものですので、 本節ではこれらの説明を省略します。 詳細は、ソースファイルを直接参照してください。

SequenceWatcher の生成

前述のように、 本節では、 HTML の "<tr>" および対応する "</tr>" にはさまれた部分を、 繰り返しつつデータをはめ込むフィルタを作成します。 しかし、 与えられた HTML ページの該当する部分全ての位置にはめ込むのは、 得策ではありません。

そこで、対象となる部分シーケンスに以下の条件を定めます。

このような場合、 "SequenceWatcher" としては "SequenceWatcher.NameAttr" が利用されます。 これは "最初の Token の名前と属性で監視しているシーケンス監視クラス" のことです。


new SequenceWatcher.NameAttr(HTMLSymbolSet.SET.TR,
                             HTMLSymbolSet.SET.CLASS,
                             "Shop-Basket-Entry")

"tr" の間を繰り返すための SequenceWatcher

SequenceDataIterationFilter からの派生

本節のサンプルフィルタクラスは、 以下のように実装されます。


public class BasketEntryIterationFilter
    extends SequenceDataIterationFilter
{
    final static
    private HTMLSymbolSet SET = HTMLSymbolSet.SET;

    final static
    private String VALUE = "Shop-Basket-Entry";

    ////////////////////////////////////////

    public BasketEntryIterationFilter(Object providerKey,
                                      Object dataKey)
    {
        super(new SequenceWatcher.NameAttr(SET.TR,
                                           SET.CLASS,
                                           VALUE),
              providerKey,
              dataKey);

        push(new EntryNameText(dataKey));
        push(new EntryPriceText(dataKey));
        push(new EntryCountAttr(dataKey));
        push(new EntryTotalText(dataKey));
    }
}

SequenceDataIterationFilter からの派生

コンストラクタで push されているインスタンスが、 連携フィルタ群です。

繋いで混ぜる

以上で必要なものが全て揃いました。 実行コードは以下のようになります (詳細は BasketEntryIterationFilter を参照してください)。

try{
    // DataProvider の設定/取得用のキー
    final Object providerkey =
    "Shop.Basket.BasketEntryProvider";

    // 反復ごとのデータ設定/取得用のキー
    final Object dataKey =
    "Shop.Basket.BasketEntry";

    Bootstrap bootstrap =
    new Bootstrap.Default(filename)
    {
        protected void prepare(ConsumerContext context)
        {
            List entryList = BasketEntry.getEntryList();

            ListDataProvider provider =
            new ListDataProvider(entryList);

            // DataProvider をコンテキストに設定
            context.setValue(providerKey, provider);
        }
    };

    BasketEntryIterationFilter filter =
    new BasketEntryIterationFilter(providerKey, dataKey);

    // フィルタの適用
    bootstrap.execute(filter);
}
catch(Exception e){
    e.printStackTrace(System.err);
}
繋いで混ぜる

入力用のサンプル HTML ファイルは、 配布物中の "src/demo/servlet/war/WEB-INF/page/demosite" 配下にある "basket.ja.html" です。

上記実行コードが生成する HTML ファイルには、 "商品籠" の中に幾つか(乱数で決定)のの商品が列挙されます。


次節「フィルタの組み合わせ」へ