Main Content

カスタム ファインダーの作成と使用

MATLAB Report Generator レポート生成 API は、指定されたオブジェクトをデータ コンテナーから検索し、結果をレポート可能な形式で返すファインダーの作成をサポートしています。ファインダーを使用すると、レポート ジェネレーターで検索ロジックをレポート ロジックから分離できます。また、ファインダーにより検索ロジックの再利用が促進されるため、レポート ジェネレーターの開発がスピードアップします。この例では、ファインダーを開発および使用してレポートを生成する方法を示します。

ファインダーの定義

ファインダーを作成するには、ファインダーのプロパティと動作を定義する MATLAB クラスを作成する必要があります。続くセクションでは、ファインダー クラスを作成するために必要な手順について説明します。説明では、例として GrantFinder という名前のクラスを使用します。クラスのコードは、このスクリプトに付属するファイル GrantFinder.m にあります。GrantFinder クラスは、全米人文科学基金 (NEH) によって交付された助成金の検索や書式設定ができるファインダーを定義します。

スケルトン クラス定義ファイルの作成

ファインダーのスケルトン クラス定義を作成するには、"MATLAB エディター" ("ライブ エディター" ではない) を使用します。たとえば、次のようになります。

GrantFinder.png

ファインダーの基底クラスの指定

レポート API の mlreportgen.finder.Finder クラスをファインダーの基底クラスとして指定します。

finder2.png

この基底クラスは、ファインダーに共通のプロパティを定義します。以下が含まれます。

  • Container: ファインダーによって検索されるコンテナーを参照するために使用されるプロパティ。たとえば、GrantFinder はこのプロパティを使用して、作成する助成金データベースへの参照を格納します。

  • Properties: 検索の条件を満たすオブジェクトのプロパティ値を指定するためにファインダー クライアントが使用するプロパティ。たとえば、このプロパティにより、GrantFinder クライアントが助成金のプロパティを指定し、NEH 助成金データベースの検索の結果として条件を満たす助成金を返すようにすることができます。

mlreportgen.finder.Finder クラスでは、ファインダー クラス定義で定義する必要があるその他のプロパティとメソッドを指定します。このようにして、ファインダーがレポート API で確実に機能するようにします。

ファインダー コンストラクターの定義

ファインダーのインスタンスを作成する関数を定義します。たとえば、次のようになります。

finderConstructor.png

GrantFinder コンストラクターは、MATLAB 関数 parseFile を使用して、ディスクから助成金の XML ファイルを読み取り、MAXP DOM ドキュメントに変換します。次に、DOM ドキュメントを mlreportgen.finder.Finder コンストラクターに渡します。これにより、MAXP DOM ドキュメントがファインダーの Container プロパティの値として設定されます。NEH データベースを MAXP DOM ドキュメントとして保存することで、ファインダーが MATLAB のネイティブ API を使用してデータベースを検索できるようにします。

コンストラクターは、助成金データベースの検索に使用される変数を初期化する関数 reset も呼び出します。GrantFinder クラスは、この関数を定義します。同様に、使用するクラスでも関数 reset が定義されていなければなりません。関数 reset は、クライアントがファインダーを使用してコンテナーの複数の検索を確実に実行できるようにします。詳細については、reset メソッドの定義を参照してください。

find メソッドの定義

ユーザー指定の制約を満たすオブジェクトをファインダー コンテナーから検索するメソッドを定義します。find メソッドは、検出オブジェクトを含む結果オブジェクトの配列を返さなければなりません。結果オブジェクトは、基本型 mlreportgen.finder.Result のオブジェクトです。検索結果を結果オブジェクトとして返すことで、ファインダーのユーザーは結果をレポートまたはレポートの章に追加できます。詳細については、"ファインダーの結果の定義" を参照してください。検索結果を MATLAB 配列として返すことで、for ループを使用して検索結果を処理できます。たとえば、次のようになります。

defineFindMethod1.png

GrantFinderfind メソッドは、find メソッドの定義を示しています。

find method.png

この find メソッドは、GrantFinder クラスが定義する検索ユーティリティ関数 ("検索ユーティリティ メソッドの定義" を参照) である getElements を使用して、ファインダーの Properties プロパティによって指定されたプロパティ値の制約を満たす助成金を助成金データベースから検索します。関数 getElements は、検索結果に Elements という名前の内部プロパティを設定します。結果は matlab.io.xml.dom.Element (matlab.io.xml.dom.Element) のリストになります。

次に、find メソッドは、この要素を GrantResult 型の結果オブジェクトの配列に変換します。GrantResult コンストラクターを使用して、助成金データを含む matlab.io.xml.dom.Element オブジェクトから交付結果オブジェクトを作成します。

hasNext メソッドおよび next メソッドの定義

ファインダー クラスの定義では、hasNext メソッドと next メソッドを定義しなければなりません。最初の呼び出しで、hasNext メソッドは検索結果のキューを作成し、キューが空でない場合に true を返さなければなりません。後続の呼び出しにおいて、hasNext メソッドは、キューが空の場合に true を返し、それ以外の場合に false を返さなければなりません。next メソッドは、最初の呼び出しで最初の結果をキューに返さなければなりません。次の呼び出しでは次の結果を返し、以降同様に、キューが空になるまで行います。

これらのメソッドは、ファインダーのクライアントが MATLAB の while ループを使用してファインダーのコンテナーを検索できるようにすることを目的としています。たとえば、次のようになります。

hasNextMethod.png

GrantFinder クラスは hasNext メソッドを示しています。

このメソッドは、ファインダーの IsIterating プロパティが示すところに従って、検索キューが既に作成されているかどうかを最初に確認します。キューが既に存在し、かつ空でない場合、このメソッドは true を返します。キューが存在し、かつ空の場合、このメソッドは false を返します。キューがまだ存在しない場合 (つまり、これがメソッドの最初の呼び出しである場合)、hasNext メソッドは次のように結果キューを作成します。まず、内部の getElements メソッドを使用して、ファインダーの Properties プロパティで指定された検索基準を満たす交付情報を取得します。getElements メソッドは、ElementCount という名前の内部ファインダー プロパティを、検出結果の数に設定します。ElementCount が 0 より大きい場合、hasNext メソッドは、NextElementIndex という名前の内部プロパティを 1 に設定します。ファインダーの next メソッドは、このプロパティを使用して検索キューの状態、つまりキュー内の次の項目を保存します。最後に、キューが最初から空でない場合、ファインダーは true を返します。それ以外の場合は false を返します。

GrantFindernext メソッドは、hasNext メソッドによって作成されたキューで動作します。

next method.png

検索ユーティリティ メソッドの定義

ファインダーの find メソッドと hasNext メソッドは、検索制約を満たすオブジェクトをファインダーのコンテナーから検索しなければなりません。両方のメソッドを使用できる検索ユーティリティを定義することを検討する必要があります。たとえば、GrantFinderhasNext メソッドと next メソッドはどちらも、検索を getElements という名前の内部ユーティリティに委任します。getElements メソッドは、XPath という名前の XML ドキュメント検索 API に検索を委任します (XPath Tutorial を参照)。

search util.png

InvalidPropertyNames プロパティの作成

ファインダーは、検索の制約に使用できないオブジェクト プロパティを指定する InvalidPropertyNames という名前のプロパティを定義しなければなりません。mlreportgen.finder.Finder 基底クラスは、このプロパティを使用して、ファインダーの Properties プロパティで指定されたユーザー指定の検索プロパティが有効であることを確認します。そうでない場合、基底クラスはエラーをスローします。つまり、クライアントがファインダーの Properties プロパティを無効なプロパティに設定した場合、基底クラスはエラーをスローします。このようにして、レポート API の基底ファインダーは、ファインダーのプロパティの有効性チェックを処理します。

ファインダーが任意の検索オブジェクト プロパティを検索制約として使用できるようにする場合は、InvalidPropertyNames プロパティを空に設定する必要があります。たとえば、GrantFinder が任意の助成金プロパティを処理できるとします。この場合、このプロパティを次のように空に設定します。

reset メソッドの定義

すべての検索ごとにファインダーを作成しなくてもよいように、ファインダーは複数の検索をサポートしなければなりません。このため、レポート API のファインダー基底クラスは、ファインダー クラスに、ファインダーの検索ロジックで使用される変数をリセットする reset メソッドを定義するように強制します。たとえば、次のようになります。

reset.png

ファインダーの結果の定義

適切な定義が存在しない場合は、クラスを作成して、ファインダーによって返される結果オブジェクトを定義しなければなりません。このセクションでは、ファインダー結果オブジェクトを定義する方法を示します。例として GrantResult という名前のクラスを使用します。GrantResult クラスは、"ファインダーの定義" セクションで例として使用されている GrantFinder クラスによって返される結果を定義します。このスクリプトに付属する GrantResult.m ファイルには、GrantResult クラスのコードが含まれています。ファインダーの結果の定義には、以下のタスクが伴います。

結果基底クラスの指定

結果クラスの基底クラスとして mlreportgen.finder.Result を定義します。たとえば、次のようになります。

result class.png

Object プロパティの定義

結果オブジェクトのクライアントが結果オブジェクトに含まれる検出オブジェクトにアクセスするために使用できる、Object という名前のプロパティを定義します。ファインダーの Object プロパティの SetAccess 値として protected を指定します。これにより、確実にこの結果だけが、そこに含まれる検出オブジェクトを指定できるようにすることができます。

ObjectProperty.png

結果コンストラクターは、検出オブジェクトをその Object プロパティの値として設定しなければなりません。結果コンストラクターは、基底クラスのコンストラクターを使用してこのタスクを実行できます。たとえば、次のようになります。

GrantResult.png

検出オブジェクト プロパティの公開

結果の Object プロパティにより、クライアントは検出オブジェクト、つまりそのプロパティにアクセスできます。ただし、プロパティにアクセスするには、追加のコードや専門知識が必要になる場合があります。検出オブジェクトのプロパティの一部またはすべてを、結果オブジェクトのプロパティとして公開する場合があります。たとえば、GrantResult クラスは、次のような助成金のプロパティのサブセットを公開します。

Properties.png

これにより、助成金ファインダーの結果オブジェクトのクライアントがこれらのプロパティ自体を抽出する必要がなくなります。結果のコンストラクターは、公開するプロパティの値を抽出し、その抽出された値を、対応する結果のプロパティに設定する必要があります。たとえば、次のようになります。

grantResultFull.png

GrantResult が、いくつかの助成金プロパティを 1 つの公開プロパティに結合していることに注意してください。たとえば、助成金の InstCityInstStateInstPostalCode、および InstCountry のプロパティを、Location という名前の単一の結果プロパティにして公開します。

この例では、コンストラクターは内部メソッドを使用して、助成金オブジェクトから助成金プロパティを抽出します。これは MAXP DOM Element オブジェクトです。たとえば、次のようになります。

getGrantProperty.png

getReporter メソッドの定義

結果オブジェクトに含まれる検出オブジェクトについて報告するレポーター オブジェクトを返すように、結果オブジェクトの getReporter メソッドを定義しなければなりません。このメソッドは、ファインダーのクライアントが、結果を Report オブジェクト、Section オブジェクト、または Chapter オブジェクトに追加するだけで、検索操作の結果についてレポートを作成できるようにします。例:

defineFindMethod1.png

レポートまたは章の append メソッドは、結果オブジェクトを、結果に含まれるデータを書式設定するレポーターを返す getReporter メソッドをもつものとして扱います。そのため、結果オブジェクトがレポートまたは章に追加された場合、append メソッドは、結果の getReporter メソッドを呼び出して結果レポーターを取得し、結果レポーターをレポートまたはレポーターに追加します。それにより、結果データが書式設定され、レポートに含められます。

GrantResult クラス定義は、レポート API の mlreportgen.report.BaseTable レポーターのカスタマイズ バージョンを返す getReporter メソッドを定義します。BaseTable レポーターは、番号付きタイトルがあるテーブルを生成します。GrantResult クラスは BaseTable レポーターをカスタマイズして、助成金プロパティのテーブルを生成します。たとえば、次のようになります。

次のコードは、GrantResult クラスが BaseReporter をカスタマイズして、番号付きの助成金プロパティ テーブルを生成する方法を示しています。

getreporter1.png

getReporter2.png

getReporter3.png

ファインダーの使用

このスクリプトは、ファインダーを使用してレポートを生成する方法を示します。このスクリプトは、「ファインダーの定義」セクションで使用された GrantFinder の例を使用して、選択された州の機関に対する 2010 年の NEH 助成金交付についての PDF レポートを生成します。スクリプトは以下のタスクを実行します。

Report Generator API のインポート

MATLAB Report Generator のレポート API に含まれるクラスをインポートします。クラスをインポートすることで、スクリプトが非修飾名 (短縮名など) を使用してクラスを参照できるようになります。

import mlreportgen.report.*
import mlreportgen.dom.*

レポート コンテナーの作成

レポート API の mlreportgen.report.Report クラスを使用して、レポート用の PDF コンテナーを作成します。スクリプトがレポート API をインポートすることによって、非修飾名でクラスを参照できることに注意してください。

rpt = Report("grant","pdf");

レポートのタイトル ページの作成

レポート API の TitlePage クラスを使用して、レポートにタイトル ページを追加します。

append(rpt,TitlePage( ...
    "Title","NEH Grants", ...
    "Subtitle","By State for 2010", ...
    "Image","neh_logo.jpg", ...
    "Author","John Doe" ...
    ));

レポートの目次の作成

レポート API の TableOfContents クラスを使用して、目次を追加します。

append(rpt,TableOfContents);

レポート データの検索

構造体の配列を使用して、このレポートに含める州を指定します。各構造体には、特定の州に対する次のデータが格納されます。

  • Name: 州の名前

  • PostalCode: 州の郵便番号

  • Grants: 州の機関に対する助成金交付。このフィールドは最初は空です

  • NGrants: この州の機関に対する助成金の交付数 (最初は空)

states = struct( ...
    "Name",{"California","Massachusetts","New York"}, ...
    "PostalCode",{"CA","MA","NY"}, ...
    "Grants",cell(1,3), ...
    "NGrants",cell(1,3) ...
    );

助成金ファインダーを使用して、州の構造体の Grants フィールドと NGrants フィールドに入力します。助成金ファインダーを作成します。

f = GrantFinder;

州の配列をループします。州ごとに、ファインダーの Properties プロパティを使用して、州に交付された助成金の検索を制約します。以下の助成金プロパティを使用して、検索を制約します。

  • InstState:助成金を受け取った機関が所在する州の郵便番号を指定します。

  • YearAwarded:助成金が交付された年を指定します。

n = numel(states);
for i = 1:n
    f.Properties = [
        {"InstState",states(i).PostalCode}, ...
        {"YearAwarded","2010"}];
    states(i).Grants = find(f);
    states(i).NGrants = numel(states(i).Grants);
end

助成金交付概要の章の作成

レポートの最初の章として助成金交付概要を作成します。助成金交付概要の章には、タイトルと助成金交付概要テーブルが含まれます。テーブルの各行には、2010 年に州の機関に対して交付された助成金の総交付数と総額がリストされます。州は、交付数の降順でテーブルに表示されます。各州は、交付された助成金の詳細を説明する章にハイパーリンクされます。

chapter.png

概要の章コンテナーの作成

章コンテナーを作成することから始めます。

ch = Chapter("Title","Grant Summary");

助成金交付概要テーブルのコンテンツの作成

テーブル ヘッダーのコンテンツを格納する cell 配列を作成します。

header = {'State','Grants Awarded','Amount Awarded'};

テーブル本体のコンテンツを格納する cell 配列を事前に割り当てます。cell 配列には R 行 3 列の行と列があります。ここで、R は州の数、3 は各州について報告される概要の項目数です。

body = cell(numel(states), 3);

MATLAB 関数 sort を使用して、助成金の交付数で州の配列を並べ替えます。関数 sort は、州の配列に対するインデックスの配列である ind を返します。配列 ind の最初のインデックスは交付数が最も多い州のインデックスで、2 番目は交付数が 2 番目に多い州のインデックスといった具合になります。

[~, ind] = sort([states.NGrants],"descend");

助成金交付数で州をループ処理し、各州の概要情報を埋めていきます。現在の州に対応する cell 配列行へのインデックスとして、変数 rowIdx を使用します。

rowIdx = 0;

次の行は、助成金の受領順に states 配列を並べ替えた後、並べ替えられた states 配列内の各構造体をループの反復ごとに変数 state に割り当てる for ループを作成します。

for state = states(ind)

現在の州に対応する cell 配列の行を指すように行インデックスを更新します。

    rowIdx = rowIdx+1;

スクリプトは、州のテーブルの最初のエントリとして、その州の助成金詳細の章へのハイパーリンクを入力します。たとえば、次のようになります。

続く行で、DOM InternalLink コンストラクターを使用してハイパーリンクを作成します。InternalLink コンストラクターは、リンク ターゲット ID とハイパーリンク テキストの 2 つの引数を取ります。このスクリプトは、現在の州の郵便番号をリンク ターゲット ID として使用し、州の名前をリンク テキストとして使用します。後で、スクリプトが助成金詳細の章を作成するときに、州の郵便番号を ID とするリンク ターゲットが章のタイトルに挿入されます。これでハイパーリンクの作成は完了です。

    body(rowIdx, 1) = {InternalLink(state.PostalCode,state.Name)};

この州の助成金の総交付数を cell 配列行の 2 番目の項目に割り当てます。

    body(rowIdx, 2) = {state.NGrants};

この州に対する総交付額を計算します。

    totalAwarded = 0;
    for grant = state.Grants
        totalAwarded = totalAwarded + str2double(grant.AwardAmount);
    end

cur2str メソッドを使用して、総額を金額として書式設定します。たとえば、次のようになります。

書式設定された結果を、この州の cell 配列の 3 番目 (最後) の項目として割り当てます。

    formattedCurrency = sprintf('\x24%.2f',totalAwarded);
    body(rowIdx,3) = {regexprep(formattedCurrency,'\d{1,3}(?=(\d{3})+\>)','$&,')};
end

概要テーブルを作成するには、ヘッダーと本体の cell 配列を mlreportgen.dom.FormalTable オブジェクトのコンストラクターに渡します。

table = FormalTable(header,body);

形式的テーブルは、ヘッダーと本体をもつテーブルです。FormalTable コンストラクターは、2 つの引数 (テーブルのヘッダーのコンテンツを指定する cell 配列、およびその本体のコンテンツを指定する cell 配列) を取ります。コンストラクターによって、cell 配列のコンテンツは、テーブルを定義する DOM TableRow および TableEntry オブジェクトに変換されるため、必要なテーブル オブジェクトをスクリプトに作成させる必要がなくなります。

助成金交付概要テーブルの書式設定

この時点で、概要テーブルは次のようになります。

unformatted.png

これは非常に読みにくいです。見出しは本体と同じ形式で、列も間隔が空いていません。

続く手順では、スクリプトはヘッダー テキストの書式設定を次のように調整します。

foramtted table.png

最初に、スクリプトは DOM TableColSpecGroup オブジェクトを使用して、テーブル列の幅と配置を指定します。TableColSpecGroup オブジェクトは、列のグループの書式を指定します。概要テーブルには列のグループが 1 つしかないため、スクリプトが作成する必要のある TableColSpecGroup オブジェクトは 1 つだけです。

grp = TableColSpecGroup;

TableColSpecGroup オブジェクトを使用すると、スクリプトでテーブルの列の既定のスタイルを指定できます。スクリプトは、列の既定の幅として 1.5in を指定し、列の既定の配置として中央揃えを指定します。

grp.Style = {HAlign("center"),Width("1.5in")};

スクリプトは、TableColSpec オブジェクトを使用して、最初の列の既定の列配置をオーバーライドします。

specs(1) = TableColSpec;
specs(1).Style = {HAlign("left")};
grp.ColSpecs = specs;
grp.Span = 3;
table.ColSpecGroups = grp;

メモ: スクリプトは、テーブル列ごとに 1 つずつ、最大 3 つの TableColSpec オブジェクトを使用して、グループの列スタイルをオーバーライドできます。最初の TableColSpec オブジェクトは最初の列に適用され、2 番目は 2 番目の列に適用されるといった具合になります。スクリプトは、最初の列の既定のスタイルのみをオーバーライドするため、グループに列仕様オブジェクトを 1 つだけ割り当てる必要があります。ただし、3 番目の列のみを変更する必要がある場合は、最初の 2 つの列仕様オブジェクトの Style プロパティを空のままにし、3 つの列仕様オブジェクトを割り当てる必要があります。

既定のテーブル スタイルでは、テーブル エントリが密接しています。そのため、スクリプトは DOM InnerMargin 形式のオブジェクトを使用してエントリの上部にスペースを作成し、エントリを上の行のエントリから離します。InnerMargin オブジェクトは、ドキュメント オブジェクトとそれを含むオブジェクトとの間、たとえば、テーブル エントリ内のテキストとテーブル エントリの境界線の間に空白 (内側の余白) を作成します。InnerMargin コンストラクターは、オプションで 4 つの引数 (ドキュメント オブジェクトの左、右、上、下の内側の余白) を取ります。

スクリプトはこのコンストラクターを使用して、3 ポイントの上部内側余白の書式を作成します。次に、概要テーブルの本体セクション内のエントリのスタイルに対して、この書式を割り当てます。

table.Body.TableEntriesStyle = {InnerMargin("0pt","0pt","3pt","0pt")};

最後に、スクリプトはテーブル ヘッダーを次のように書式設定して、グレーの背景に太字の白いテキストで構成されるようにします。

table.Header.row(1).Style = {Bold,Color("white"),BackgroundColor("gray")};

レポートへの概要の章の追加

append(ch,table);

append(rpt,ch);

助成金詳細の章の作成

州の構造体をループ処理します。

for state = states

州ごとに章を作成して、州の助成金詳細を保持します。章のタイトルにリンク ターゲットを挿入し、最初の章にある概要テーブルのハイパーリンクのターゲットとして機能するようにします。

    ch = Chapter("Title",{LinkTarget(state.PostalCode),state.Name});

州の交付結果をループ処理します。

    for grant = state.Grants

各交付結果について、その結果を章に追加します。

        append(ch,grant);

交付結果は、選択された助成金プロパティのテーブルを作成するレポーターを返す getReporter メソッドをもちます。章の append メソッドは、結果のレポーターを取得して章に追加するように事前構成されています。したがって、章に交付情報を追加することは、章に結果プロパティ テーブルを追加するのと同じことになります。たとえば、次のようになります。

    end
    append(rpt,ch);
end

レポート オブジェクトを閉じる

レポート オブジェクトを閉じると、レポート オブジェクトが指定する PDF 出力ファイル (grant.pdf) が生成されます。

close(rpt);

レポートの表示

rptview(rpt);

付録: NEH 助成金データベース

この例で使用されるデータベースのソースは、全米人文科学基金 (NEH) です。データベースには、2010 ~ 2019 年までの期間の NEH 助成金に関する情報が格納されています。XML 形式の約 6000 個の記録が格納されています。これは、NEH Grant Data で入手することができます。この例では、データベース XML ファイル NEH_Grants2010s.xml のローカル コピーを使用します。

このデータベースは、1 つの Grants 要素で構成されています。これには Grant 要素のセットが含まれており、その各々に助成金データ要素のセットが含まれます。以下はデータベースからの抜粋で、その構造を示しています。

参考

| | |

関連するトピック