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

効率的な組み合わせ

本節では、 PageMixer における効率的なフィルタの組み合わせ方に関して説明します。

概要

クラス図

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

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

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

オブジェクト図

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

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

クラス名

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

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

表記完全名
ConsumerContext jp.ne.dti.lares.foozy.pagemixer.mixer.ConsumerContext
Filter jp.ne.dti.lares.foozy.pagemixer.mixer.Filter
FilterPipeline jp.ne.dti.lares.foozy.pagemixer.mixer.FilterPipeline
HTMLSymbolSet jp.ne.dti.lares.foozy.pagemixer.HTMLSymbolSet
SequenceHybridFilter jp.ne.dti.lares.foozy.pagemixer.mixer.SequenceHybridFilter
SequenceTrimFilter jp.ne.dti.lares.foozy.pagemixer.mixer.SequenceTrimFilter
SequenceWatcher jp.ne.dti.lares.foozy.pagemixer.mixer.SequenceWatcher
Token jp.ne.dti.lares.foozy.pagemixer.Token
TrimFilter jp.ne.dti.lares.foozy.pagemixer.mixer.TrimFilter

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

表記完全名
AlreadyLoginFilter pagemixer.filter.AlreadyLoginFilter
Bootstrap pagemixer.filter.Bootstrap
Trim pagemixer.filter.AlreadyLoginFilter.Trim
UsernameAttrInsetFilter pagemixer.filter.UsernameTextInsetFilter

動機

単純にフィルタを接続して使用した場合、 全てのフィルタが全ての Token 列を処理することになります。

しかし、 それらフィルタのいくつかは Token 列の殆ど全てに対して興味を示しません。 例えば、 HTML ページにおける "ログイン" 機能に関するフィルタは、 Token 列の "ログイン" 機能の部分にしか興味がありません。

10 のフィルタに 5000 の Token から構成されるシーケンスを処理させた場合、 処理の総量は 5,000 * 10 = 50,000 となります。 しかし、そのうちの3つだけにシーケンス全体を処理させ、 残りの7つには、それらが興味のある 100 の Token だけを処理させた場合、 処理の総量は (5,000 * 3) + (100 * 7) = 15,700 となり、 およそ 70% の処理コストを削減することが出来ます。

フィルタの "木目細かさ" を維持することは、 機能が多くのフィルタによって構成されることを意味し、 それ故に処理コスト低減が必要とされます。

SequenceHybridFilter の拡張

フィルタを効率的に組み合わせるために、 PageMixer フレームワークは "SequenceHybridFilter" クラスを提供しています。

このクラスのインスタンスは、 SequenceWatcher および Filter で構築されます。 指定された Filter には、 SequenceWatcher によって認識された部分シーケンスだけが提供されます。

FilterPipeline を用いることで、 部分シーケンスを複数のフィルタに提供することができます。

HTML ページにおける "ログイン" 機能向けのフィルタ群を、 効率的に組み合わせるフィルタは以下のようになります。


public class AlreadyLoginFilter
    extends SequenceHybridFilter
{
    final static
    private HTMLSymbolSet SET = HTMLSymbolSet.SET;

    final static
    private String ATTR_VALUE = "Auth-AlreadyLogin";

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

    public AlreadyLoginFilter(Object keyLogin){
        super(new SequenceWatcher.NameAttr(SET.SPAN,
                                           SET.CLASS,
                                           ATTR_VALUE),
              create(keyLogin));
    }

    // 非 staic メソッドは
    // 基底クラスのコンストラクタ起動には不適切
    static
    private Filter createFilter(Object keyLogin){
        FilterPipeline pipeline = new FilterPipeline();

        pipeline.push(new UsernameTextInsetFilter(keyLogin));
        pipeline.push(new Trim(keyLogin));

        return pipeline;
    }

}

SequenceHybridFilter の拡張

"AlreadyLoginFilter" は、 "<span class="Auth-AlreadyLogin">" および "</span>" で挟まれた部分シーケンスを、 "create" メソッドにおいて FilterPipeline に追加されたフィルタ群へと供給します。

上記部分シーケンスが、全体の 10% から構成されているとすると、 AlreadyLoginFilter こそシーケンス全体の 100% を処理しますが、 "ログイン" 機能用のフィルタ群は、 シーケンス全体の 10% だけを処理することになります。 結果、(100 + 10 * 2)/(100 * 2) = 120/200 = 0.6 から、 今回定義したクラスによって 40% の処理コストが削減されます。

より多くのフィルタを HybridSequenceFilter と組み合わせることで、 より多くの低減を得ることができます。 例えば、 10 のフィルタを併用した場合、 (100 + 10 * 10)/(100 * 10) = 200/1000 = 0.2 から 80% の処理コストが低減できます。

UsernameTextInsetFilter に関しては、 "シーケンスへのデータはめ込み" 節において説明済みです。

"Trim" フィルタは、 まだ説明していない "TrimFilter" の派生クラスです。 TrimFilter は、 与えられたシーケンス全体を刈り込むフィルタです。 SequenceTrimFilter.WholeTrimFilter の違いは、 SequenceWatcherによって認識された部分シーケンスを刈り込むか、 シーケンス全体を刈り込むか、という点だけです。

繋いで混ぜる

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

try{
    // ユーザ名設定/取得用のキー
    final Object key = "Auth.Login";
    // ユーザ名
    final String name = "foozy";

    Bootstrap bootstrap =
    new Bootstrap.Default(filename)
    {
        protected void prepare(ConsumerContext context)
        {
            // "login" はユーザ名のコンテキストへの
            // 設定の有無を保持する
            if(login){
                // ユーザ名をコンテキストに設定
                context.setValue(key, name);
            }
        }
    };

    AlreadyLoginFilter filter =
    new AlreadyLoginFilter(key);

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

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


次節「HTML ページの事前解析」へ