状態遷移図で定義されたモデル情報から動作可能なコードを生成する“自作”のMDDツールの開発にチャレンジする!
少しさかのぼりますが、本連載の第3回「続・ソフトウェアのモデル駆動開発にチャレンジ!」では、簡易的なMDDツールを用いて、モデルからそのまま動作可能なコード生成を行いました(皆さん覚えていますか?)。
今回はいつもの解説を一休みして、フリーのライブラリを活用し、MDDツールを“自作”してみたいと思います。以降で、MDDツールの作り方を解説していきますのでぜひチャレンジしてみてください!
今回開発するMDDツールは、“ツール化することで高い効果が得られる”であろう個所のみをツール化することにします。
今回は、その高い効果が得られる個所として「状態遷移図で定義されたモデル情報からソースコードを生成するまで」を機能として実現したいと思います。
コードを生成する範囲は限定しますが、少しだけ背伸びをして、コードのスケルトンを出力するのではなく、そのままビルドして実行できる“動作可能なコード”を出力することに挑戦します。
今回自作するMDDツールは、「astah*ファイル」と「出力先フォルダ名」を入力とし、C言語の「ヘッダファイル(.h)」と「ソースコード(.c)」を出力するコマンドラインツールとします。
まずは、MDDツールを開発する際に必要となる開発環境を準備します。表1の入手先から各種開発環境・ライブラリをダウンロードし、インストールしてください。
―Eclipse JDT(Eclipse IDE for Java Developersに付属)
Eclipseに標準で付属しているJava用の開発環境です。その中にあるEclipse JDTを利用します。
―astah* API(astah* communityに付属)
本連載でモデルを描くために用いたツール「astah*」には、astah* APIが付属しています。このAPIを使ってプログラム上からモデル情報にアクセスします。
―FreeMarker
FreeMarkerは、テンプレートファイルとデータモデルを与えると、それらをマージしたテキストデータを出力するJavaで実装されたテンプレートエンジンです。今回はこれを使って、astah* APIで取得したモデル情報をデータモデルとして与え、C言語のソースコードを出力します。
―Apache Commons CLI
CLIは、コマンドライン引数の解析を手軽に行うことができるライブラリです。ツール起動時に与えられるコマンドパラメータの解析処理に利用します。
名前 | バージョン | 入手先 | |
---|---|---|---|
Eclipse | JDT | 3.6.1 | http://www.eclipse.org/downloads/ |
astah* community | 6.2.1 | http://astah.change-vision.com/ja/product/astah-community.html | |
FreeMarke | 2.3.16 | http://freemarker.sourceforge.net/freemarkerdownload.html | |
CLI | 1.2 | http://commons.apache.org/cli/download_cli.cgi | |
表1 各種開発環境・ライブラリの入手先一覧 |
今回開発するMDDツールでは、astah* API、FreeMarker、Apache Commons CLIというJavaベースの3つのライブラリを利用しますので、Eclipseを用いJavaで実装していきます。
なお、今回開発するソフトウェアのサンプル「AstahCodeGenerator」は、以下のリンクからダウンロードできます(注1)。以降の解説では、プログラムのポイントとなる部分にのみフォーカスしています。プログラム全体を確認しながら読み進めたい場合は、本ファイルをダウンロードしてください。
>>サンプル「AstahCodeGenerator.zip」のダウンロード<<
まずは、Eclipse上で新規Javaプロジェクトを作成してください。プロジェクトのフォルダ構成を以下のとおりにしてください。
AstahCodeGenerator/ ├ bin/ │ 実行バイナリの配置フォルダ ├ example/ │ サンプル入力ファイルの配置フォルダ ├ lib/ │ 外部ライブラリの配置フォルダ ├ out/ │ 生成コードの出力先フォルダ ├ src/ │ Javaソースコードの配置フォルダ ├ template/ │ FreeMarkerテンプレート定義ファイルの配置フォルダ └ tool/ 本ツールを起動するためのバッチファイルを配置するフォルダ
続いて、ビルド・パスの設定を行います。
プロジェクトを右クリックして[プロパティ]を選択し、表示されたダイアログの左ペインから[Javaのビルド・パス]を選択してください。続いて[ソース]タブにて、“src”と“template”フォルダを追加します。さらに、[ライブラリ]タブにて、今回利用する3つのライブラリ(astah-api.jar、freemarker.jar、commons-cli-1.2.jar)を追加します。
以上で準備が整いました。
それではコーディング上の主なポイントを解説しつつ、MDDツールの開発を進めていきたいと思います。
astah* APIを用いて、入力パラメータとして渡されたファイルからモデル情報を読み込みます。モデル情報にアクセスするためには、最初にプロジェクトアクセサを作成する必要があります。サンプルでは、AstahDiagramFinderクラスでこの実装を行っています(リスト1)。
ProjectAccessor prjAccessor; public AstahDiagramFinder(String inputFile) { prjAccessor = ProjectAccessorFactory.getProjectAccessor(); prjAccessor.open(inputFile, true, false, true); }
AstahDiagramFinderクラスのコンストラクタにて、パラメータ(inputFile)に与えられたastah*ファイルパスを基に、astah*プロジェクトをオープンしています。
astah*プロジェクトがオープンできたら、今回の読み込み対象であるステートマシン図にアクセスします。サンプルでは、プロジェクト直下に1つのモデル図のみが配置されていますが、通常はプロジェクトに複数のモデル図がフォルダ構造で定義されます。ですので、フォルダ構造を走査して該当するモデル図のみをリスト化する必要があります。リスト2がその部分の実装になります。
public List<IStateMachineDiagram> findAllStateMachineDiagrams() throws ProjectNotFoundException { return getAllStateMachineDiagrams(prjAccessor.getProject()); } private List<IStateMachineDiagram> getAllStateMachineDiagrams (INamedElement iNamedElement) { ArrayList<IStateMachineDiagram> diagrams = new ArrayList<IStateMachineDiagram>(); IDiagram[] dgms = iNamedElement.getDiagrams(); for (int i = 0; i < dgms.length; i++) { if (dgms[i] instanceof IStateMachineDiagram) { diagrams.add((IStateMachineDiagram) dgms[i]); } } :
リスト2 AstahDiagramFinder.java(33〜44行目) |
プロジェクトアクセサから取得したプロジェクトオブジェクトは、ツリー構造のルートノード要素として扱われます。getAllStateMachineDiagrams()メソッドは、再帰的に呼び出されて、プロジェクト配下のフォルダを表すノードオブジェクトから、そのフォルダ配下のモデル図(IDiagramクラス)のリストを収集します。ここで収集されるモデル図には、ステートマシン図をはじめとしたさまざまなUMLのダイヤグラムが含まれていますので、その中からステートマシン図(IStateMachineDiagram)に該当するものだけを取り出します。
これで必要なモデル情報として、ステートマシン図のみが収集できました。
テンプレートエンジンFreeMarkerを用いて、テンプレートファイルに記述されたルールに従い、ソースコードを生成します。テンプレートファイルの記述方法は後述することとし、まずはFreeMarkerの主な処理手順について見ていきたいと思います。
FreeMarkerを動作させるためには、最初にConfigurationクラスを用いて、実行に必要なパラメータを構成(コンフィグ)します。リスト3に、この部分の実装であるcreateTemplateConfig()メソッドを示します。
// テンプレートのコンフィグを行う private static Configuration createTemplateConfig() throws IOException { final String projDir = CourseCodeGenerator.class.getProtectionDomain() .getCodeSource().getLocation().getPath(); Configuration cfg = new Configuration(); cfg.setDirectoryForTemplateLoading(new File(projDir + File.separator + "../template")); cfg.setObjectWrapper(new DefaultObjectWrapper()); return cfg; }
リスト3 CourseCodeGenerator.java(91〜102行目) |
ここではパラメータの1つとして、テンプレートファイルが配置されているディレクトリを指定しています。なおFreeMarkerでは、テンプレートのロード方法としてディレクトリ指定以外にもクラスパス指定で行うことも可能です。
続いて、FreeMarkerに渡すパラメータを作成します。リスト4に、この部分の実装であるcreateTemplateRootMap()メソッドを示します。
private static Map<String, Object> createTemplateRootMap (IStateMachineDiagram diagram) { Map<String, Object> root = new HashMap<String, Object>(); : root.put("lastUpdated", new Date()); root.put("diagram", diagram); return root; }
リスト4 CourseCodeGenerator.java(105〜112行目) |
FreeMarkerでは、テンプレート処理を行うデータモデルを、テンプレートファイルに記述する「名前」と、その名前で呼び出す「オブジェクト」を対応付けたマップオブジェクトで渡す必要があります。従って、このメソッド内でこのマップオブジェクトを作成します。
今回は、コード生成日時をスタンプするための情報(Dateオブジェクト)と、astah*から読み取ったモデル情報(IStateMachineDiagramインターフェイスの実装オブジェクト)をマップオブジェクトに設定しています。
最後に、テンプレートエンジン(Templateクラス)に処理を移譲します。この部分の実装をリスト5に示します。
// ソースファイルを生成 private static void generateSourceFile(Configuration templateConfig, Map<String, Object> templateRootMap, String sourceFilename) throws IOException, TemplateException { Writer sourceWriter = new FileWriter(sourceFilename); Template sourceTemplate = templateConfig.getTemplate("course.ftl"); sourceTemplate.process(templateRootMap, sourceWriter); sourceWriter.flush(); }
リスト5 CourseCodeGenerator.java(114〜123行目) |
前述したConfigurationクラスのインスタンスからTemplateクラスのインスタンスを取り出し、入力元(astah*から取得したモデル情報)、出力先(出力ファイル名を指定したWriterインスタンス)を指定して変換処理を実行します。
FreeMarkerを用いたテンプレート処理手順については以上になります。
Copyright © ITmedia, Inc. All Rights Reserved.