ジェネレーターユーザガイド Demo3
Generator ユーザーガイドデモ 3
デモ 2 では、次のような java ステートメントを生成していました。
Swing コンポーネントを作成してアプリケーションのコンテンツペインに追加します。
デモ 3 では、コンポーネントプロパティのサポートを追加します。これには、コンストラクターの呼び出しだけでなく、より複雑な初期化コードの生成が必要になります。さらに、生成されるプロパティ初期化コードは、コンポーネントの種類によって異なります。そのような要件を処理できる生成戦略を選択します。
新しい言語
繰り返しますが、新しい言語を設定し、それに Demo 2 ジェネレーターのアーティファクトをいくつかコピーする必要があります。
新しい言語を作成する: 'generator_demo.demoLang3'
言語プロパティダイアログで、「jetbrains.mps.sampleXML」および「jetbrains.mps.baseLanguage」への拡張依存関係を追加します。
この言語用の新しいジェネレーターが存在しない場合は作成します。詳細については、ジェネレーターユーザガイド Demo1 を参照してください。
マッピング構成「main」を demoLang3 ジェネレーターから削除します (デモ 2 のように、demoLang3 発生器に demoLang2 発生器からすべての必要な部分をコピーします)
コピー & ペースト demoLang3 ジェネレーターに demoLang2 ジェネレーターから「メイン」マッピング設定を
demoLang3 発生器に demoLang2 生成機からの「の DemoApp」テンプレートをコピー & ペースト
addContent(container)
ジェネレーターは、多くの点でデモ 2 で作成したものに似ています。条件付きルートルール を使用して、DemoApp クラスを出力モデルに挿入します。最初に、DemoApp クラスにいくつかの調整を加える必要があります。
「DemoApp」テンプレートを開きます
静的 void メソッド 'addContent(Container)' を追加します。
'main()' メソッドで、次の文を見つけます。
$LOOP$[container.add($SWITCH$[null]);上記のステートメントを以下のステートメントに置き換えます。
addContent(container);
これで、addContent() メソッドはすべてのコンポーネントを JFrame に追加することになっています。container.add() を呼び出し、初期化されたビジュアルコンポーネントを順番に渡します(入力 XML ドキュメントごとに 1 つ)。そのため、コードで使用できるダミーメソッドを追加し、ジェネレーターで実際のコンポーネント初期化コードに置き換えます。
コンポーネントの生成
入力要素ごとにコンポーネント初期化メソッドを生成するために、component() メソッド宣言を LOOP マクロでラップします。LOOP マクロは、入力モデル内のすべての要素を反復処理し、各要素のメソッドを生成します。
同様に、入力要素ごとにこれらの生成されたメソッドを呼び出し、返されたコンポーネントをコンテナーに追加する必要があるため、container.add(component()); の呼び出しの前後に別の LOOP マクロを使用する必要があります。 addContent() メソッド内のメソッド。
最後になりましたが、ダミーの component() メソッドの呼び出しを、同じ入力要素に対応する実際に生成されたメソッドの呼び出しに置き換える必要があります。コンポーネント(終了括弧なし)を選択し、参照マクロを挿入します。
これはまだ前進できないところです。現在のノードに対応するメソッド宣言を解決して、参照マクロから参照できるようにする方法がわかりません。この段階でマッピングラベルを使用します。それらは、メソッド宣言のレジストリとして機能し、それらが作成された要素からアクセスできます。したがって、参照マクロはそこから対応するメソッド宣言を取得できます。
マッピングラベルを定義するには、「メイン」マッピング構成に戻ります。
ここで、DempApp に戻り、参照マクロを終了できます。
明らかに、メソッド宣言の生成を完了し、メソッドマッピングラベルに適切に格納する必要があります。
コンポーネントの挿入
デモ 2 と同様に、メソッドを生成するときに、さまざまなタイプの要素に対応するために SWITCH マクロを使用します。ジェネレーターモデルを右クリックし、新規 -> テンプレートスイッチを選択します。要素の名前に応じて、ボタンまたはラベルのコードが生成されます。
今回は、生成されたコードがより複雑であるため、インラインテンプレートではロジックをキャプチャーするのに十分ではないため、ボタンとラベルの両方を生成するためのスタンドアロンテンプレートが必要です(それぞれ insert_Button と insert_Label という名前)。ジェネレーターモデルを右クリックし、新規 -> テンプレート宣言を選択して作成します。
テンプレートは、生成するコードのスニペットであり、入力モデルから計算された値でパラメーター化されます。私たちのケースでは、の DemoApp クラスに追加されるとの DemoApp クラステンプレートで宣言さ component() 方式と互換性のあるシグネチャーを持つことになり、新たな静的メソッドを生成する必要があります。ボタンに対して次に行うことは、静的メソッドを含むダミークラスを作成することです。この静的メソッドはテンプレートフラグメントになり、クラス全体ではなくメソッドのみがターゲットモデルに挿入されます。
まず、コンテンツノードとして ClassConcept を選択します。
必要な署名を使用して静的メソッドを宣言する以下のコードを入力します。
ここで、メソッド宣言全体を選択し、Alt+Enter を押して、メニューからテンプレートフラグメントの作成を選択します。これにより、テンプレートとして使用する必要があるのはメソッド宣言のみであることが示されます。周囲のクラスは、メソッドのコンテキストとしてのみ機能します。
これは、このテンプレートで生成されたすべてのメソッドをメソッドマッピングラベルに登録するときです。これにより、後でこれらのメソッドへの呼び出しを生成するときにこれらを取得できます。テンプレートフラグメント(<TF ビジュアル要素)のインスペクターウィンドウに目的のマッピングラベルを入力するだけです。
現在、同じ名前のすべての入力要素(createComponent)のメソッドを生成しています。これにより、入力モデルに複数の要素がある場合、Java のコンパイル時の問題が発生します。生成された各メソッドに一意の名前を付ける必要があり、プロパティマクロを介してそれを行います。
genContext 機能を使用して一意の識別子を生成します。templateValue パラメーターは「createComponent」(つまり、テンプレートに記述されているメソッドの名前) になります。
ここで、JLabel を作成するためのテンプレート宣言を作成してください。
完了すると、SWITCH テンプレート宣言を補完することができるはずです。
DempApp クラステンプレートで SWITCH マクロを修正し、言語を作成した後、新しいジェネレーターを最初に試すことができるはずです。
新しいテストモデル
新しい入力テストモデルを作成しましょう。
'test_models' ソリューションに移動します
モデル「test2」をモデル「test3」に複製します
モデルプロパティダイアログで、「生成中」言語の demoLang2 -> demoLang3 を置き換えます。詳細については、ジェネレーターユーザガイド Demo2 を参照してください。
エディターで「ボタン」ドキュメントを開きます(モデル「test3」から)
「button」要素に属性「text="Hello" 」を追加します
「button」要素に属性「enabled="false" 」を追加します
「ラベル」ドキュメントの「ラベル」要素に属性「text="world!"」を追加します
「ラベル」ドキュメントの「ラベル」要素に属性「background="orange" 」を追加します
生成されたコードのプレビューを押すと、有効な Java アプリケーションを取得する必要があります。
createComponent() メソッドの独自性に注目してください。
それでは、追加の XML 属性のセマンティクスを定義しましょう。
$IF$ マクロの追加
insert_Button と insert_Label テンプレート上のフォーカスをしてみましょう。これらは、入力 XML 要素で指定された値に基づいて Swing コンポーネントを初期化するコードを定義します。これまでのところ、入力モデルのすべての属性を無視していますが、これは変更されます。今のところ insert_Label に注目すると、入力要素に「text」という属性がある場合、次のステートメントを生成します。
ここで、text は、入力要素の「text」属性で指定された文字列です。
MPS がテンプレートの現在のノードを含むコードをタイプチェックできるように、上部で要素をテンプレートの入力として指定します
'createComponent()' メソッドの本体に次のコードを入力してください
「component.setText("text" );」の周囲に IF マクロを作成します。ステートメント (ステートメント全体を選択し、Ctrl-Shift + M を押します )
IF- マクロの条件関数にコードを入力します。これにより、「テキスト」属性の存在が確認されます
入力モデルからの実際の属性値で値をパラメーター化する必要があるため、文字列リテラル「text」内にプロパティマクロを作成します。
プロパティマクロの value 関数で、入力要素の「text」属性の値を返すコードを入力します
これで、insert_Button テンプレートに対して同じ手順を繰り返す必要があります。
再利用可能なテンプレート
同じ方法でコンポーネントプロパティのサポートを追加することもできますが、この方法ではテンプレートに多くのコードが重複することになります。
より良いアイデアは、いくつかの一般的なコードを含む 1 つのテンプレートを作成し、各コンポーネントのテンプレートで再利用することです。例: プロパティ 'enabled' のサポートを追加しましょう。「共有」テンプレートを作成し、CALL マクロを使用して他のテンプレートで再利用します。
demoLang3 ジェネレーターの「main @ generator」モデルに移動します (プロジェクトツリーで選択)
モデルポップアップメニューのルートノードの作成を使用して、新しいテンプレート宣言ノードを作成します
「include_ComponentProperties」という名前を付けます
入力を選択 - 要素
テンプレートのコンテンツノードとして StatementList を選択します。
ステートメントリストで変数宣言を作成します。
JComponent component = null;ブロックステートメントを追加する (変数宣言の後に <Enter> を押し、「{」と入力し、Ctrl + <スペース> を押して自動補完します)
ステートメントを作成します。
component.setEnabled(false);ブロックステートメント内
ブロックステートメントをテンプレートフラグメントとしてマークします(つまり、コンポーネント変数の宣言を除外します)。
MAP_SRC- マクロ
'text' プロパティのジェネレーターユーザガイド Demo3 として、'setEnabled()'メソッド呼び出しの生成は条件付きである必要があります。このステートメントは、入力要素が属性'enabled' を所有している場合にのみ生成される必要があります。
今回は、条件生成を実現するために、IF マクロの代わりに MAP_SRC- マクロを使用します。後述するように、この場合、MAP_SRC マクロには IF マクロよりもいくつかの利点があります。
MAP_SRC マクロを作成します (文全体をラップする必要があります)
次のように、マクロのマップされたノード関数にコードを入力します。
マップされたノード関数は、'enabled' 属性が存在する場合はそのノードを返し、存在しない場合は null を返します。 MAP_SRC マクロのマップされたノード 関数が null を返す場合、ジェネレーターはこのマクロによってラップされたノードを無視します(If マクロの場合と同様)。
ただし、マップされたノードが null でない場合は、ラップされたテンプレートコードの処理中に、現在の入力ノードになります。これにより、属性を取得して、IF マクロを使用する場合に比べてプロパティマクロの作成が少し簡単になります。'enabled' 属性を再度見つける必要はありません。この属性はすでに入力ノードです。
次に、テンプレートに正しい値を設定するには:
プロパティマクロをブール定数「false」に添付します
値関数にコードを入力します(この場合、値関数はブール値の戻り値を期待することに注意してください)。
CALL マクロ
CALL マクロを使用して、コード内で「component.setEnabled(..);」を挿入する場所を指定します。生成中のステートメント。
エディターで「insert_Button」テンプレートを開きます
ステートメントの直後に新しい空の行を挿入します
component.setText("text");空の行に CALL ノードマクロを挿入します
マクロのインスペクターで「include_ComponentProperties」テンプレートへの参照を作成します。
「insert_Label」テンプレートに同様のマクロを作成します
ジェネレーターモデルを再生成 (Shift+F9)
持っているものをテストする
「test3」モデルのコードを生成すると、有効な Java アプリケーションがレンダリングされます。
ボタンに対してのみ生成され、ラベルに対しては生成されない setEnabled() メソッドを正しく取得します。
参照マクロ (名前による解決)
多くの場合、参照は自動的に解決できないため、reference-macros が便利です。
例: 'background' プロパティのサポートをジェネレーターに追加しましょう:
「include_ComponentProperties」テンプレートを開きます
'component.setEnabled(false);' ステートメントの直後にブロック文を追加します
そのブロックステートメント内に次のコードを入力します。
component.setOpaque(true); component.setBackground(Color.black);MAP_SRC マクロでブロックステートメントをラップする
次のように、マクロのマップされたノード関数にコードを入力します。
入力 XML 要素の「background」属性の実際の値に応じて色をパラメーター化する必要があるため、reference-macro を静的フィールド参照「black」(「Color.black」式内)にアタッチします。
referent 関数内に次のコードを入力します。
この例では、リファレント関数の戻り値の型が組み合わされています。
eithor-or セマンティクスを持つ。これは、関数から何を返すかについて 2 つの選択肢があることを意味します。
クラス「Color」で適切な静的フィールド宣言を見つけて、そのノードを返すことができます
または、参照情報文字列 - 静的フィールド宣言の名前(色の名前)を返して、MPS に静的フィールドを見つけさせます
もちろん、2 番目のオプションははるかに魅力的です。「背景」属性の値 - 目的の色の名前を返します。
最終テスト
これで、言語の再構築後、「test3」モデルが Java アプリケーションに生成され、すべての入力要素の属性が考慮されます。
関連ページ:
ジェネレーターユーザガイド Demo1
Generator ユーザーガイドのデモ 1:このデモでは、入力 XML ドキュメントごとに java アプリケーションを生成するジェネレーターを作成します。以前に作成したソリューション「test_models」で「generator_demo.test1」モデルを再利用します。このモデルは「jetbrains.mps.sampleXML」言語を使用し、「ボタン」と「ラベル」の 2 つの XML ドキュメントを含みます。出力 java アプリケーションを生成するために、これらの各ドキュメントにル...
ジェネレーターユーザガイド Demo2
Generator ユーザーガイドデモ 2:このデモでは、Java Swing アプリケーションを再度生成しますが、前のデモ 1 とは異なり、今回は入力モデルごとに 1 つの Java アプリケーションになります(デモ 1 では、入力 XML ドキュメントごとに個別の Java アプリケーションを生成しました)。デモ 2 が作成する単一の Java アプリケーションには、入力モデルのすべての XML ドキュメントに記載されているすべてのコンポーネントが含まれます。新しい言語:最初に少し技術的な...
ジェネレーターユーザガイド Demo4
Generator ユーザーガイドデモ 4:このデモでは、demoLang3 ジェネレーター (デモ 3 を参照) をさらに進化させ、パネルコンポーネントのサポートを追加します。ボタンやラベルとは異なり、パネルには他のパネルを含む他のコンポーネントを含めることができます。ジェネレーターで再帰が発生します。削減がこの種の問題の解決にどのように役立つかを見ていきます。新しい言語:demoLang4 の demoLang3 ジェネレーターの大部分を再利用します。新しい言語「generator_demo...