続いて、テンプレートの記述について見ていきたいと思います。
FreeMarkerでは、基本的に1つの出力ファイルにつき、1つのテンプレートファイルが対応します(あるテンプレートから別のテンプレートをインクルードすることもできますので、複数のテンプレートから共通的に利用されるユーティル関数を別ファイル化することも可能です)。
まずはヘッダファイル(.h)について、ヘッダコメントのテンプレート定義例と出力例をリスト6、7に示します。
/* * generated by AstahCodeGenerator * at ${lastUpdated?datetime} * * クラス名: ${diagram.name} * 属性: なし * 操作: AG_init, AG_run * 関連: なし */
リスト6 テンプレート定義例[courseHeader.ftl(1〜9行目)] |
/* * generated by AstahCodeGenerator * at 2010/10/12 19:33:46 * * クラス名: RunController_gen * 属性: なし * 操作: AG_init, AG_run * 関連: なし */
リスト7 出力例 |
まず基本ルールとして、FreeMarkerは、“${}”の中を動的な値で置き換え、それ以外の個所はそのまま固定文字列として出力します。
リスト6のテンプレート定義例では、${}でくくられた動的に値を置き換える個所が2つあります。
1カ所目は、ソースコードを生成したタイムスタンプで、“lastUpdated”をキー値として指定してマップオブジェクトから取得したオブジェクトに対し、FreeMarkerのビルトイン関数であるdatetimeを使って値を生成しています。datetime関数ですので、日付と時間が出力されています。
2カ所目は、クラス名で、“diagram”をキー値として指定してマップオジェクトから取得したオブジェクトに対し、その名前を生成しています。FreeMarkerでは、プロパティを取得するために、例えばJavaのgetName()メソッドであれば、対象オブジェクトに対し、“.name”(メソッド名からgetを取り除き、先頭を小文字とした名称をドットで連結したもの)を用いることでオブジェクトのプロパティにアクセスできます。
続いて、ソースファイル(.c)についても見ていきましょう。リスト8、9は、C言語のdefineの個所でのテンプレート定義例と出力例になります。
/** 状態定義 */ <#list diagram.stateMachine.vertexes as vertex> <#if isState(vertex) && vertex.incomings?has_content> #define ${vertex.name?upper_case} ${vertex_index} </#if> </#list>
リスト8 テンプレート定義例[course.ftl(31〜36行目)] |
/** 状態定義 */ #define FOLLOWER_LINETRACE 1 #define NORMAL_LINETRACE 2
リスト9 出力例 |
diagram.stateMachine.vertexes(同様の内容をJavaで記述すると、“diagram.getStateMachine().getVertexes()”になります)で、ステートマシン図上に配置された各状態をリストとして取得できます。それを、#listディレクティブによって1つずつ変数名vertexとして取り出し、状態の名前と、リストのインデックス番号を生成しています。
もう1つ、関数ロジックの個所を解説します。リスト10、11は、関数AG_next_status()のテンプレート記述例と出力例になります。
/** * 次の状態を決定する */ static signed int AG_next_status(void) { signed int next_status = -1; /** 次の状態を決定する */ switch (AG_current_status) { <#list diagram.stateMachine.vertexes as vertex> <#if isState(vertex) && vertex.incomings?has_content && vertex.outgoings?has_content> case ${vertex.name?upper_case}: <#list vertex.outgoings as outgoing> if (${outgoing.event}()) { next_status = ${outgoing.target.name?upper_case}; } </#list> break; </#if> </#list> } return next_status; }
リスト10 テンプレート定義例[course.ftl(76〜98行目)] |
/** * 次の状態を決定する */ static signed int AG_next_status(void) { signed int next_status = -1; /** 次の状態の決定する */ switch (AG_current_status) { case FOLLOWER_LINETRACE: if (RC_touch_sensor_pressed()) { next_status = NORMAL_LINETRACE; } break; case NORMAL_LINETRACE: if (RC_touch_sensor_pressed()) { next_status = FOLLOWER_LINETRACE; } break; } return next_status; }
リスト11 出力例 |
この関数は、タッチセンサが押されるごとに、走行状態を「追従走行」と「通常走行」とに切り替えるロジックとなります。図2に示すステートマシン図を参照しながら、テンプレート定義例を見ていただくと理解しやすいかと思います。
変数vertexには、ダイヤグラム図上の「ある状態」が入っていますので、まずcaseの後に状態名(vertex.name?upper_case)を生成します。次の行では、状態遷移イベントの有無を判断しますので、if文の条件の個所にこの状態から出ていく遷移名(outgoing.event)を生成します。そして、条件が“真”の場合には、次の行で遷移先の状態名(outgoing.target.name?upper_case)を生成します。
この処理を状態から出ていく遷移の数だけ、そしてさらに状態の数だけループさせることで、ロジック内のswitch〜case文を完成させています。
それでは動作確認してみましょう。
コマンドプロンプトを起動し、“AstahCodeGenerator\tool\”配下に移動してください。そして、そのフォルダにあるバッチファイル「AstahCodeGenerator.bat」を以下のようにコマンドラインオプションを付加して実行してください(注2)(注3)。
AstahCodeGenerator.bat -o ..\out ..\example\generate_model.asta
コマンドラインオプションで指定したフォルダ先に、コードが生成されていることを確認できると思います。
いかがでしたでしょうか。今回利用したライブラリであるastah* APIは、非常に簡潔な構文でモデル情報にアクセスできます。今回のサンプルではステートマシン図のみを扱いましたが、もちろんクラス図をはじめとするほかのダイヤグラム図も同様に扱うことができます。
また、FreeMarkerの方は制御構文も豊富で、C言語の実装に近い形式でテンプレート定義ができます。また、可読性が高い形式ですので、比較的容易に利用できるのではないでしょうか。今回のような簡易的なツールを素早く作るには、これらのライブラリはとても相性がよいと思います。
MDDツールによって、そのまま動作するソフトウェアの全コードを生成することが最終到達点かとは思いますが、そのためにはたくさんの課題をクリアする必要があります。そのため、一度にそれを実現しようとすると企画段階でつまずいてしまうことも多いのではないでしょうか。今回のように、まずは小さいところから実際に“やってみる”ことが、地道ではありますが現在置かれている状況をよりよい方向へと導く第一歩かと思います。
そのような場合に、今回紹介したようなフリーのライブラリの組み合わせで、まずは簡易的なツールを作ってみるところからはじめてはいかがでしょうか。今回は、連載第3回で説明し切れなかったことを補うために、番外編としてちょっと横道にそれてしまいました。次回は、通常の解説に戻ります。お楽しみに! (次回に続く)
Copyright © ITmedia, Inc. All Rights Reserved.