Asakusa FrameworkのDMDLについて。
|
|
Asakusa FrameworkのDMDL(Data Model Definition Language)は、データモデルを定義する言語。
データモデルは、レコードやファイルのレイアウト(RDBで例えるならテーブルレイアウト)に相当する。
AsakusaFWではデータモデルを使ってデータを操作(加工)する。
Batch DSL・Flow DSL・Operator DSLが「Javaをホスト言語とする内部DSL」であるのに対し、DMDLは「外部DSL」。
つまり、専用のDMDLコンパイラーがDMDLファイルを読み込んでコンパイルする。
1つのDMDLファイルの中に複数のデータモデルを記述できるし、1つのAsakusaプロジェクトの中で複数のDMDLファイルを使用することも出来る。
(DMDLファイルは単なるテキストファイルだが、拙作のDMDL
EditorX(Eclipseプラグイン)を使うと、キーワードに色が付いたりして便利(笑))
DMDLがコンパイルされると、データモデル毎にJavaのModelクラス(JavaBeansに相当)が生成される。(Operator DSLやFlow DSLからはそのクラスを指定する)
また、インポーター・エクスポータークラスやテストデータを記述する為のExcelファイルも生成される。
これらのファイルの生成はデータモデルドライバーで行われる。独自のデータモデルドライバーを作れば、DMDLのコンパイル時に独自の処理(ファイル生成とか)を行うことも出来る。けっこう大変だけど^^;
データモデルには、以下の4種類のモデルがある。
モデルの種類 | 接頭辞 | 説明 |
---|---|---|
レコードモデル | なし | プロパティー一覧を持つデータ構造。 |
結合モデル | joined |
2つのデータモデルを結合して生成するデータモデル。 マスター結合演算子(@MasterJoin)で使われる。 |
集計モデル | summarized |
データモデルを集計して生成するデータモデル。 単純集計演算子(@Summarize)で使われる。 |
射影モデル | projective |
(他のデータモデルでも使用する為の)共通のプロパティー群を定義しておくデータモデル。 多相データフローで使う。 |
データモデル内に定義する個々のフィールドを「プロパティー」と呼ぶ。
DMDLの書き方(モデルの種類によらず共通な部分)について。
モデル名やプロパティー名には小文字の英数字とアンダースコアしか使えない。つまり小文字のスネークケースである。
(Javaのクラス名やフィールド名に変換される際にはアンダースコアが削除され、各単語の先頭が大文字になる(フィールドの場合は一番先頭の単語は小文字))
また、最初と最後にアンダースコアを付けられないとか、アンダースコアを2つ以上連続させることは出来ないといった制限もある。
さらに、Javaの予約語を使うことや各単語を数字から始めることは非推奨となっている。
(単語を数字で始めると、a_0もa0もA0に変換されるので、重複してエラーになる可能性がある)
example_model = { ←モデル名 number : INT; ←プロパティー名 item_code : TEXT; ←プロパティー名 };
「/*〜*/」でブロックコメント、「--」「//」で行コメントを付けることが出来る。
/* モデルの例 */ example_model = { // プロパティー一覧 number : INT; --数値 item_code : TEXT; --商品コード };
モデルやプロパティーには「説明(description)」を付けることが出来る。
モデル名やプロパティー名の前にダブルクォーテーションで囲んだ文字列を付けると「説明(description)」になる。
コメントとは異なり、生成されるJavaのModelクラス等のJavadocに使われる。
レコードモデルの例 | 結合モデルの例 | 集計モデルの例 |
---|---|---|
"レコードモデルの例" example_model = { "数値" number : INT; "商品コード" item_code : TEXT; }; |
"結合モデルの例" joined example_join = example_model -> { "数値" number -> number; "商品コード" item_code -> item_code; } % item_code + example_item -> { "商品コード" code -> item_code; "商品名" name -> name: } % item_code; |
"集計モデルの例" summarized example_sum = example_model => { "商品コード" any item_code -> item_code; "数値" sum number -> number_total; } % item_code; |
モデルやプロパティーに「属性(attribute)」を付けることが出来る。
属性を付けると、その属性を使用するデータモデルドライバーによってファイル生成等が行われる。(属性自体は属性ドライバーで処理される)
モデル名やプロパティー名の前(および説明(description)の後)に属性を指定する。属性名は「@」で始まる。「@」で始まるので、個人的には属性のことをアノテーションと呼ぶこともある。
属性の種類によっては、さらに丸括弧を付けて属性の値を指定する。
1つのデータモデルやプロパティーに複数の属性を付けることも出来る。
レコードモデルの例 | 結合モデルの例 | 集計モデルの例 | |
---|---|---|---|
属性 のみ |
@directio.csv example_model = { @directio.csv.field(name = "数値") number : INT; @directio.csv.field(name = "商品コード") item_code : TEXT; }; |
@directio.csv joined example_join = example_model -> { @directio.csv.field(name = "数値") number -> number; @directio.csv.field(name = "商品コード") item_code -> item_code; } % item_code + example_item -> { @directio.csv.field(name = "商品コード") code -> item_code; @directio.csv.field(name = "商品名") name -> name: } % item_code; |
@directio.csv summarized example_sum = example_model => { @directio.csv.field(name = "商品コード") any item_code -> item_code; @directio.csv.field(name = "合計") sum number -> number_total; } % item_code; |
説明が ある 場合 |
"レコードモデルの例" @directio.csv example_model = { "数値" @directio.csv.field(name = "数値") number : INT; "商品コード" @directio.csv.field(name = "商品コード") item_code : TEXT; }; |
"結合モデルの例" @directio.csv joined example_join = example_model -> { "数値" @directio.csv.field(name = "数値") number -> number; "商品コード" @directio.csv.field(name = "商品コード") item_code -> item_code; } % item_code + example_item -> { "商品コード" @directio.csv.field(name = "商品コード") code -> item_code; "商品名" @directio.csv.field(name = "商品名") name -> name: } % item_code; |
"集計モデルの例" @directio.csv summarized example_sum = example_model => { "商品コード" @directio.csv.field(name = "商品コード") any item_code -> item_code; "数値の合計" @directio.csv.field(name = "合計") sum number -> number_total; } % item_code; |
属性 2つ |
"レコードモデルの例" @directio.csv @windgate.jdbc.table(name = "EXAMPLE") example_model = { "数値" @directio.csv.field(name = "数値") @windgate.jdbc.column(name = "NUM") number : INT; "商品コード" @directio.csv.field(name = "商品コード") @windgate.jdbc.column(name = "ITEM_CODE") item_code : TEXT; }; |
"結合モデルの例" @directio.csv @windgate.jdbc.table(name = "EXAMPLE") joined example_join = example_model -> { "数値" @directio.csv.field(name = "数値") @windgate.jdbc.column(name = "NUM") number -> number; "商品コード" @directio.csv.field(name = "商品コード") @windgate.jdbc.column(name = "ITEM_CODE") item_code -> item_code; } % item_code + example_item -> { "商品コード" @directio.csv.field(name = "商品コード") @windgate.jdbc.column(name = "ITEM_CODE") code -> item_code; "商品名" @directio.csv.field(name = "商品名") @windgate.jdbc.column(name = "ITEM_NAME") name -> name: } % item_code; |
"集計モデルの例" @directio.csv @windgate.jdbc.table(name = "EXAMPLE") summarized example_sum = example_model => { "商品コード" @directio.csv.field(name = "商品コード") @windgate.jdbc.column(name = "ITEM_CODE") any item_code -> item_code; "数値の合計" @directio.csv.field(name = "合計") @windgate.jdbc.column(name = "TOTAL") sum number -> number_total; } % item_code; |
指定できる属性には以下のようなものがある。
属性名 | 説明 | 備考 | 参照 |
---|---|---|---|
@directio.csv |
Direct I/OでHDFS(やAmazon S3)上のCSVファイル(カンマ区切り)を読み書きする為のインポーター・エクスポータークラスを生成する。 | Direct I/Oのアーキタイプで使用可能。 (WindGateにはDirect I/Oの機能も含まれているので、WindGateでも使用可能) |
CSVファイル連携 Direct I/Oユーザーガイドの『Direct I/OのCSVファイル連携』 |
@directio.text.csv [2017-04-30] |
AsakusaFW 0.9.1以降。 directio.csvより多機能。 |
整形テキストファイル連携 | |
@directio.line [2015-12-05] |
Direct I/OでHDFS(やAmazon S3)上のテキストファイル(区切り文字なし)を読み書きする為のインポーター・エクスポータークラスを生成する。 | AsakusaFW 0.7.6〜0.8.0では、Direct I/O のテキストファイル連携を導入すると使用可能。 AsakusaFW 0.8.1以降は標準で使用可能。[2016-07-31] |
テキストファイル連携 Direct I/Oユーザーガイドの『Direct I/O のテキストファイル連携』 |
@directio.json [2019-06-14] |
Direct I/OでJSONファイルを読み書きする為のインポーター・エクスポータークラスを生成する。 | AsakusaFW 0.10.3以降。 | Direct I/Oユーザーガイドの『Direct I/O JSON』 |
@directio.hive.orc [2015-03-15] |
Direct I/OでHDFS(やAmazon S3)上のHiveのORCファイルを読み書きする為のインポーター・エクスポータークラスを生成する。 | AsakusaFW 0.7.0以降。 | Direct I/Oユーザーガイドの『Direct I/OのHive連携』 |
@directio.hive.parquet [2015-03-15] |
Direct I/OでHDFS(やAmazon S3)上のHiveのParquetファイルを読み書きする為のインポーター・エクスポータークラスを生成する。 | AsakusaFW 0.7.0以降。 | Direct I/Oユーザーガイドの『Direct I/OのHive連携』 |
@directio.tsv |
Direct I/OでHDFS(やAmazon S3)上のTSVファイル(タブ区切り)を読み書きする為のインポーター・エクスポータークラスを生成する。 | Direct I/OのTSVファイル連携を導入すると使用可能。 | Direct I/OのTSVファイル連携 |
@directio.text.tabular [2017-04-30] |
AsakusaFW 0.9.1以降。 | 整形テキストファイル連携 | |
@directio.sequence_file |
Direct I/OでHDFS(やAmazon S3)上のSequenceFileを読み書きする為のインポーター・エクスポータークラスを生成する。 | Direct I/Oのアーキタイプで使用可能。 | Direct I/Oユーザーガイドの『内部データ形式を利用したシーケンスファイル形式のDataFormatの作成』 |
@windgate.csv |
WindGateでローカルマシン上のCSVファイル(カンマ区切り)を読み書きする為のインポーター・エクスポータークラスを生成する。 | WindGateのアーキタイプで使用可能。 | WindGateユーザーガイドの『CSV形式のDataModelStreamSupportの作成』 |
@windgate.stream_format (@windgate.tsvって名前じゃないんだな^^;) |
WindGateでローカルマシン上のTSVファイル(タブ区切り)を読み書きする為のインポーター・エクスポータークラスを生成する。 | WindGateのTSVファイル連携を導入すると使用可能。 | WindGateのTSVファイル連携 |
@windgate.jdbc |
WindGateでRDBのテーブルへアクセスする為のインポーター・エクスポータークラスを生成する。 | WindGateのアーキタイプで使用可能。 | WindGateユーザーガイドの『DataModelJdbcSupportの自動生成』 |
@namespace |
生成されるクラスのパッケージ名を指定する。 | DMDLユーザーガイドの『データモデルの名前空間』 | |
@auto_projection |
生成されるクラスに対し、“同一のプロパティーを持つ射影モデル”のインターフェースを自動的に実装する。 | DMDLユーザーガイドの『自動射影』 |
レコードモデルは、プロパティー一覧を持つデータ構造。
レコードモデルは、定義するモデル名の後ろに「=」を付け、その後ろに波括弧でブロックを作り、最後に「;
」を付ける。
波括弧のブロック内にプロパティー一覧を定義する。
レコードモデルのプロパティーは、「プロパティー名 : データ型;
」で定義する。
型の名前 | 対応するJavaの型 | 説明 |
---|---|---|
INT |
int |
32bit符号付き整数 |
LONG |
long |
64bit符号付き整数 |
FLOAT |
float |
単精度浮動小数 |
DOUBLE |
double |
倍精度浮動小数 |
TEXT |
org.hadoop.io.Text(java.lang.String) |
文字列 |
DECIMAL |
java.math.BigDecimal |
十進数 |
DATE |
com.asakusafw.runtime.value.Date |
日付 |
DATETIME |
com.asakusafw.runtime.value.DateTime |
日時 |
BOOLEAN |
boolean |
論理値 |
BYTE |
byte |
8bit符号付き整数 |
SHORT |
short |
16bit符号付き整数 |
{型} |
java.util.List |
リスト(0.9.2以降)[2017-08-13] |
{:型} |
java.util.Map |
マップ(0.9.2以降)[2017-08-13] |
例 | 説明 |
---|---|
"レコードモデルの例" example_model = { "数値" number : INT; "商品コード" item_code : TEXT; }; |
|
left = { left_value : INT; }; right = { right_value : TEXT; }; both = left + right; |
別々のデータモデルを合成して新しいレコードモデルを作ることが出来る。 データモデル同士を「+」でつなぐ。 双方に同じプロパティー名が存在する場合は1つだけ定義される(データ型が異なるとエラーになる)。 |
origin = { value : INT; }; extended = origin + { extra : TEXT; }; |
他のデータモデルを拡張して新しいレコードモデルを作ることが出来る。 モデル名と波括弧ブロックを「+」でつなぐ。 |
model_c = { first : INT; } + model_a + { second : LONG; } + model_b + { last : TEXT; }; |
データモデルの合成と拡張はいくつでも指定可能。 |
結合モデルは、2つのデータモデルを結合して生成するデータモデル。
マスター結合演算子(@MasterJoin)(と分割演算子(@Split))で使われる。
結合モデルは、定義するモデル名の前に「joined」を付ける。
そして、「=」以降に、2つの「結合元のデータモデル名 % 結合キー」を「+」でつないで指定する。
例 | 説明 |
---|---|
item = { code : LONG; id : TEXT; name : TEXT; name2 : TEXT; }; order = { item_code : LONG; item_id : TEXT; value : INT; }; "結合モデルの例" joined item_order = item % code, id + order % item_code, item_id; |
結合キーはカンマ区切りで複数プロパティーを指定可能。 |
joined item_order = item -> { code -> code; id -> id; name -> item_name; } % code, id + order -> { item_code -> item_code; item_id -> item_id; value -> value; } % item_code, item_id; |
結合モデルでは、プロパティーを絞ったり、名前を変えたりすることが出来る。 「-> { }」でブロックを作り、使いたいプロパティーを「 元のプロパティー名 -> 新しいプロパティー名; 」で指定する。左の例では、code・id・item_name・item_code・item_id・valueを持つ結合モデルが作られる。 codeとitem_code、idとitem_idは(結合キーなので)同一の値を持つことになり、冗長ではあるが。 |
joined item_order = item -> { code -> item_code; id -> item_id; name -> item_name; } % item_code, item_id + order -> { item_code -> item_code; item_id -> item_id; value -> value; } % item_code, item_id; |
結合モデルでは、結合キーに使用するプロパティーは(異なる結合元データモデル間で)同じプロパティー名にすることが出来る。 こうすると、名前が異なるだけの同じ値のプロパティーを無くせる。 左の例では、item_code・item_id・item_name・valueを持つ結合モデルが作られる。 |
集計モデルは、データモデルを集計して生成するデータモデル。
単純集計演算子(@Summarize)で使われる。
集計モデルは、定義するモデル名の前に「summarized」を付ける。
そして、定義するモデル名の後ろに「=」を付け、その後ろに集計元プロパティーを指定する。
その後ろに「=> { }」でブロックを作り、「% 集計キー」を指定して、最後に「;
」を付ける。
波括弧のブロック内にプロパティー一覧を定義する。
集計モデルのプロパティーは、「集約関数 プロパティー名 -> 新しいプロパティー名;
」で定義する。
集約関数 | 説明 |
---|---|
any |
グループ化されたレコード群(の指定された項目)のいずれかひとつの値(どのレコードの値が使われるかは不定) |
sum |
グループ化されたレコード群(の指定された項目)の合計値 |
max |
グループ化されたレコード群(の指定された項目)の最大値 |
min |
グループ化されたレコード群(の指定された項目)の最小値 |
count |
グループ化されたレコードの個数 |
例 | 説明 |
---|---|
order = { item_code : LONG; price : DECIMAL; }; "集計モデルの例" summarized order_summary = order => { any item_code -> code; sum price -> total; count item_code -> count; } % code; |
集計キーはカンマ区切りで複数プロパティーを指定可能。 集計キーとなる項目ではanyを指定する。 (グループ化するキーなので、同一グループ内では同じ値になる為、どのレコードの値が使われてもよい) countを使うときは、集計キー項目を指定しておく。 |
射影モデルは、他のデータモデルでも使用する為の共通のプロパティー群を定義しておくデータモデル。
多相データフローで使用する。
射影モデルは、モデル名の前に「projective」を付ける。それ以外はレコードモデルの定義方法と同じ。
射影モデルをコンパイルすると、Javaのインターフェースが作られる(他のデータモデルの場合はクラスが作られる)。
つまり、射影モデルを取り込んだデータモデルは“射影モデルを継承している”という扱いになる。
データモデルの例 | 生成されるモデルクラスのイメージ | 説明 |
---|---|---|
projective proj_model = { value : INT; }; |
public interface ProjModel { public int getValue(); public void setValue(int value); } |
射影モデルから生成されるJavaソースはインターフェースになる。 |
model1 = { id : LONG; } + proj_model; |
public class Model1 implements ProjModel { public long getId() { 〜 } public void setId(long value) { 〜 } public int getValue() { 〜 } public void setValue(int value) { 〜 } } |
データモデルを定義する際に、射影モデルを取り込む(合成する)ことが出来る。 生成されるモデルクラスは、射影モデルのインターフェースを実装する形になる。 |
model2 = model1 + { code : TEXT; }; |
public class Model2 { public long getId() { 〜 } public void setId(long value) { 〜 } public int getValue() { 〜 } public void setValue(int value) { 〜 } public String getCodeAsString() { 〜 } public void setCodeAsString(String value) { 〜 } } |
“射影モデルを合成したモデル”を取り込んで新しいデータモデルを作った場合、取り込み元の射影モデルのインターフェースは実装されない。 (プロパティーは取り込まれる(同一のsetter/getterメソッドは生成される)が、クラスとしての継承関係は無い) |
@auto_projection model3 = model1 + { code : TEXT; }; |
public class Model3 implements ProjModel { public long getId() { 〜 } public void setId(long value) { 〜 } public int getValue() { 〜 } public void setValue(int value) { 〜 } public String getCodeAsString() { 〜 } public void setCodeAsString(String value) { 〜 } } |
auto_projection属性を指定すると、合成元のモデルに含まれていた射影モデルのインターフェースが実装される。 (ただし、合成元以外の射影モデルのインターフェースも実装される可能性がある。model4の例が、本来のauto_projectionの仕組みである。合成元に射影モデルが含まれていると、必ずauto_projectionの条件を満たす、というのがmodel3の例) |
@auto_projection model4 = { code : TEXT; value : INT; }; |
public class Model4 implements ProjModel { public String getCodeAsString() { 〜 } public void setCodeAsString(String value) { 〜 } public int getValue() { 〜 } public void setValue(int value) { 〜 } } |
auto_projection属性を指定すると、(モデルの合成関係が無くても)プロパティーが一致する射影モデルのインターフェースが実装される。 (左記の例だと、「 value : INT; 」がproj_modelと一致するので、ProjModelが実装される)プロパティーが一致している射影モデルが複数見つかったら、それら全ての射影モデルインターフェースが実装される。 (射影モデルは、全dmdlファイルにわたって検索される) |
@auto_projection model5 = { code : TEXT; }; |
public class Model5 { public String getCodeAsString() { 〜 } public void setCodeAsString(String value) { 〜 } } |
したがって、どの射影モデルにもプロパティーが一致しない場合は、auto_projection属性を指定していても何の意味も無い。 |
他のデータモデルに「+ 射影モデル」で射影モデルを取り込む(合成する)ことが出来るが、データモデルの合成対象は射影モデルである必要は無い。
射影モデルに意味があるのは、多相データフロー(多相演算子)で使用する場合。
多相データフロー(多相演算子)では、個別のデータモデルを扱うのではなく、ある一定のルールに従ったデータモデルなら何でも受け付ける。
そのルールというのが「射影モデルであること」。
(多相演算子になりうるのは、演算子リファレンスで「型引数が指定可能」とされている演算子)
通常の演算子の例 | 多相演算子の例 | |
---|---|---|
Operator DSL |
@Update public void update(NormalModel in) { in.setValue(100); } |
@Update public <T extends ProjModel> void update(T in) { in.setValue(100); } |
Flow DSL |
In<NormalModel> in; Out<NormalModel> out; Update u = operators.update(this.in); this.out.add(u.out); |
In<Model1> in; Out<Model1> out; Update<Model1> u = operators.update(this.in);
this.out.add(u.out);
|
つまり、射影モデルを使うと(Javaのクラスとして)継承関係を持つことが出来るので、型引数(extendsのジェネリクス)で射影モデルのインターフェースを指定できるようになる、ということ。[2014-02-22]
AsakusaFW 0.10.2で、射影モデルを使った通常のレコードモデルをさらに使ったモデルを定義した場合、自動的に大元の射影モデル(のインターフェース)を継承するようになった。[2018-09-26]
データモデル | 生成されるJavaクラス | |
---|---|---|
AsakusaFW 0.10.1以前 | AsakusaFW 0.10.2 | |
projective proj_aaa = { a : TEXT; }; |
public interface ProjAaa extends Writable { |
public interface ProjAaa extends Writable { |
bbb = proj_aaa + { b : TEXT; }; |
public class Bbb implements DataModel<Bbb>,
ProjAaa, Writable { |
public class Bbb implements DataModel<Bbb>,
ProjAaa, Writable { |
ccc = bbb + { c : TEXT; }; |
public class Ccc implements DataModel<Ccc>,
Writable { |
public class Ccc implements DataModel<Ccc>,
ProjAaa, Writable { |
AsakusaFW 0.9.2でプロパティー参照(コレクションタイプ(コレクション型))が使えるようになった。[2017-08-13]
他のプロパティー(複数)にリストやマップでアクセスできる。
形式 | 説明 | |
---|---|---|
リスト | リストプロパティー名 = { 参照先プロパティー名, … }; |
プロパティー名の右側に「=」を書き、 その右側に波括弧で囲んで参照先プロパティーの一覧を書く。 |
マップ | マッププロパティー名 = { "キー" : 参照先プロパティー名, … }; |
プロパティー名の右側に「=」を書き、 その右側に波括弧で囲んでキーと参照先プロパティーの組の一覧を書く。 キーは文字列のみ指定可能。 |
参照先のプロパティーの型は全て一致している必要がある。
参照先のプロパティーにプロパティー参照(リストやマップ)を指定する事は出来ない。(リストやマップをネストさせることは出来ない)
このDMDLをコンパイルするとデータモデルクラスにJavaのListやMapを返すメソッドが追加され、それを使ってインデックスやキーによるアクセスが出来るようになる。
このリストやマップの要素の実体は参照先のプロパティーそのものなので、ただ単にアクセス方法が便利になるだけと言えばそれだけであるが、実際便利なケースがある(笑)
例えば以下のようにDMDLを定義すると、生成されるデータモデルクラスにListを返すメソッドが追加される。
list_example = { value01 : INT; value02 : INT; value03 : INT; value_list = { value01, value02, value03 }; };
↓
public class ListExample implements DataModel<ListExample>, Writable { 〜 public int getValue01() { 〜 } public void setValue01(int value) { 〜 } public IntOption getValue01Option() { 〜 } public void setValue01Option(IntOption option) { 〜 } 〜 public List<IntOption> getValueList() { 〜 } 〜 }
ここで返るリストを経由して、value01やvalue02といった各プロパティーの値を取得・設定することが出来る。
(なお、このリストに対して要素の追加・削除やソートは出来ない)
public abstract class ExampleOperator { @Update public void updateList(ListExample in, int index, int arg) { update(in, 0, 123); // value01に値をセット update(in, 1, 456); // value02に値をセット update(in, 2, 789); // value03に値をセット } @SuppressWarnings("deprecation") private void update(ListExample in, int index, int arg) { List<IntOption> list = in.getValueList(); IntOption value = list.get(index); value.modify(arg); } }
例えば以下のようにDMDLを定義すると、生成されるデータモデルクラスにMapを返すメソッドが追加される。
map_example = { value01 : INT; value02 : INT; value03 : INT; value_map = { "a" : value01, "b" : value02, "c" : value03 }; };
↓
public class MapExample implements DataModel<ListExample>, Writable { 〜 public int getValue01() { 〜 } public void setValue01(int value) { 〜 } public IntOption getValue01Option() { 〜 } public void setValue01Option(IntOption option) { 〜 } 〜 public Map<String, IntOption> getValueMap() { 〜 } 〜 }
ここで返るマップを経由して、value01やvalue02といった各プロパティーの値を取得・設定することが出来る。
(なお、このマップに対して要素の追加や削除は出来ない)
(entrySetやkeySet, valuesにおける並び順はDMDLで定義した順序になる)
public abstract class ExampleOperator { @Update public void updateMap(MapExample in) { update(in, "a", 123); // value01に値をセット update(in, "b", 456); // value02に値をセット update(in, "c", 789); // value03に値をセット } @SuppressWarnings("deprecation") private void update(MapExample in, String key, int arg) { Map<String, IntOption> map = in.getValueMap(); IntOption value = map.get(key); if (value == null) { throw new AssertionError(key); } value.modify(arg); } }
射影モデルでプロパティー参照を定義すると、「プロパティー参照が存在する」という事だけの定義になる。
つまり、実際の参照先プロパティー一覧は、射影モデルを合成したモデル側で定義する必要がある。
この場合は「= { 参照先プロパティー一覧 }
」を書けないので、データ型を明示する。
リストの場合は「{要素の型
}」、マップの場合は「{:要素の型}
」と書く。
projective proj_example = { ref_list : {INT}; ref_map : {:INT}; };
↓コンパイルするとJavaのインターフェースになる
public interface ProjExample extends Writable { public List<IntOption> getRefList(); public Map<String, IntOption> getRefMap(); }
射影モデルでも以下のようにプロパティーを指定してプロパティー参照を定義することは出来るが
、
コンパイルしても参照先プロパティー一覧は生成されず、そのプロパティーの型が要素の型として使われるだけである。
projective proj_example2 = { value0 : INT; ref_list = { value0 }; ref_map = { "0" : value0 }; };
↓コンパイルしても(Javaのインターフェースなので)具体的な参照先プロパティー一覧は生成されない
public interface ProjExample extends Writable { public int getValue0(); public void setValue0(int value); public IntOption getValue0Option(); public void setValue0Option(IntOption option); public List<IntOption> getRefList(); public Map<String, IntOption> getRefMap(); }
ただし、この射影モデルを合成した側のデータモデルでref_listやref_mapを再定義しない場合は、射影モデルで定義した一覧が生成される。
example2 = proj_example2 + { value1 : INT; // ref_listやref_mapは、proj_example2で定義された内容で作られる };
「{}
」で空のリスト、「{:}
」で空のマップを定義することが出来る。
この場合は要素の型が分からないので明示する必要がある。
リストの場合は「{要素の型
}」、マップの場合は「{:要素の型}
」と書く。
empty_example = { ref_list : {INT} = { }; ref_map : {:INT} = { : }; };
Maven版(古いバージョンのAsakusaFW)を使っている場合、DMDLのコンパイルは、以下のコマンドで実行する。
$ mvn generate-sources
DMDLをコンパイルすると、Javaソース(Modelクラスやインポーター・エクスポータークラス)およびテストに使用するExcelファイル等が生成される。
(AsakusaFW 0.5.2以降の)gradle版では以下のコマンドでDMDLのコンパイルを行う。[2013-11-26]
$ ./gradlew compileDMDL …Javaソースの生成 $ ./gradlew generateTestbook …Excelファイルの生成
Eclipse(Shafu)の場合は以下の手順でDMDLのコンパイルを行う。[2021-11-03]
DMDL EditorX(Eclipseプラグイン)を導入している場合は、以下の手順でもDMDLのコンパイルが実行できる。[2021-11-03]