プラグインとはデザイナーの動作を拡張し、コンポーネントやマッパー関数のプロパティ設定などの補助をするモジュールのことです。
コンポーネントやマッパー関数のクラスがフローの実行時にサーバー側で動作するモジュールであるのに対し、
プラグインはフローの設計時にデザイナー側で動作するのでフローの実行には影響を与えません。
プラグインはコンポーネント/マッパー関数の作成で必ず作らなければならないというものではありませんが、
多機能なコンポーネント/マッパー関数を作成する場合プラグインを作成するとデザイナーの操作性が向上し、
フロー作成の効率が大幅にアップすることもあります。
プラグイン作成では多くのクラスを使用します。
それらのクラス群はサーバーで使用しているクラスと同一の場合もありますが、ほとんどの場合プラグインで使用する
クラスとサーバーで使用するクラスは全く別物です。
たとえばプラグインでも「Component」というクラスを使用しますが、そのパッケージは
com.infoteria.asteria.flowbuilder2.component.Component
であり、サーバーで使用するクラス
com.infoteria.asteria.flowengine2.flow.Component
とは全く別のクラスです。
後者はサーバー上で実際にコンポーネントの行う処理を実行するクラスですが、
前者はデザイナーで使用されるxfpに保存されているコンポーネントの内容をラップしたクラスです。
現在作成可能なプラグインの種類は次の4つです。
以下にそれぞれのプラグインの概要を説明します。
コンポーネントやマッパー関数がどのようなプロパティを持っているかは、 定義ファイルのProperty要素で定義されます。
プロパティの型は定義ファイル内でtype属性によって示され、 int型のプロパティの場合は数値しか入力できす、 choice型のプロパティではドロップダウンリストからプロパティ値が選択できるようになっています。 またdate型やdatetime型のプロパティのように日付を選択するカレンダーが表示されるものもあります。
つまりプロパティの型によって値を設定するためのUIが決まっています。
そのプロパティ値を設定するためのUIがプロパティエディタです。
通常プロパティ型とプロパティエディタは1対1で対応していますが、 string型のようにeditor属性でプロパティエディタを選択できるものもあります。
| editor属性値 | プロパティエディタ |
|---|---|
| デフォルト(省略時) | 1行テキストダイアログ |
| multiline | 複数行テキストダイアログ |
| condition | 条件式の補完機能つき1行テキストダイアログ |
もっともわかりやすい例はBranchStartコンポーネントの条件式プロパティで使用されている
「condition」エディタでしょう。
このエディタは「$」や「.」をタイプした時に条件式の補完をしてくれます。
このようにプロパティ値を設定するための補助的な機能を付加したUIを自作できるプラグインが
プロパティエディタです。
プロパティリスナーはJavaプログラムで日常的に使用されるEventListenerの一種で、 プロパティ値の変更に反応するリスナーです。
プロパティ値の設定前後でイベントが発生するのでそこで何かしらの処理を行うことができます。
PropertyListenerの典型的な実装例としては、プロパティ値の変更に伴い別のプロパティの表示状態など をコントロールできるSimplePropertyControllerがあります。
コンポーネントエディタとは複数のプロパティ値やフィールド定義などをまとめて設定できるように
したものです。
コンポーネントエディタを使用している例としては
などがあります。
外部アプリケーション起動はその名のとおり外部アプリケーションを起動するためのプラグインです。
外部アプリケーション起動での処理の流れは以下のようになります。
外部アプリケーション起動を使用した例としてはExcelBuilder、PDFBuilderなどがあります。
プロパティエディタを作成するためにはPropertyEditorインターフェース
を実装したクラスを作成します。
このインターフェースはSwingのTableCellEditorインターフェースを拡張し、setPropertyメソッドを
追加したものです。
新しいPropertyEditorを作成する場合にインターフェースのメソッドをすべて自分で作成しなければ ならないケースはほとんどなく、通常は次のButtonTextCellEditorかButtonCellEditorを継承して作成します。
ButtonTextCellEditorはインスペクタの編集エリア内にテキストフィールドがあり、
その脇に「...」という小さなボタンがあるタイプのプロパティエディタです。
ボタンをクリックすることでそのプロパティ値を設定するための補助UI(ほとんどの場合はダイアログ)
が起動します。
ButtonTextCellEditorを継承したPropertyEditorの例としては
などがあります。
ButtonTextCellEditorを作成する場合実装しなければならないメソッドはdoButtonActionメソッド のみであり、そこにボタンがクリックされた場合の処理を記述します。
ButtonCellEditorはインスペクタの編集エリア全体がボタンで覆われたタイプのプロパティエディタです。
つまりインスペクタ内で値を設定することはできず、編集エリアをクリックすることでそのプロパティ値を設定するための補助UI(ほとんどの場合はダイアログ)
が起動します。
ButtonCellEditorを継承したPropertyEditorの例としては
などがあります。
ButtonCellEditorを作成する場合実装しなければならないメソッドはdoButtonActionメソッド のみであり、そこにボタンがクリックされた場合の処理を記述します。
ButtonTextCellEditorでもButtonCellEditorでもdoButtonActionメソッドの実装内容はほとんど同じになります。
典型的な例としてはダイアログを表示してそこでOKボタンがクリックされた場合にプロパティに値を設定します。
sampleフォルダに1行テキストダイアログの替わりに履歴機能つきComboBoxを使用するサンプル
(HistoryPropertyEditor.java)があります。
以下にそのコードの中からdoButtonActionの部分のポイントを説明します。
(コード中の「_combo」は履歴機能付きのComboBox、「_file」は履歴を保存するFileです。
完全なコードはsampleフォルダ内にあるjavaファイルを参照してください。)
/**
* ボタンが押された時の処理
* 履歴機能付きComboBoxの載ったダイアログを表示
*/
protected void doButtonAction(EventObject e) {
String value = getProperty().getValueAsString(); //1
_combo.setSelectedItem(value);
Frame owner = PluginUtil.getApplicationFrame(); //2
String title = getDialogTitle(); //3
MyDialog dlg = new MyDialog(owner, title);
try {
dlg.setLocationRelativeTo(owner);
dlg.setVisible(true);
if (dlg.isOK()) { //DialogがOKボタンで終わったか?
value = (String)_combo.getSelectedItem();
_combo.updateHistory();
setCellEditorValue(value); //4
stopCellEditing(); //5
if (_file != null)
_combo.saveToFile(_file);
} else
cancelCellEditing(); //6
} catch (IOException ex) {
ex.printStackTrace(); //7
} finally {
dlg.dispose();
}
}
作成したプロパティエディタを定義ファイルに組み込むには対象のProperty要素にeditorClass属性で指定します。
<Property displayName="ファイルパス" name="FilePath" required="true" toolTip="FilePath" type="string"
editorClass="com.infoteria.asteria.flowbuilder2.plugin.HistoryPropertyEditor" filename="exe.txt"/>
HistoryPropertyEditorはStringを扱うプロパティエディタですので、対象となるプロパティはstring型
またはその派生型でなければなりません。
またここでは説明しませんでしたがMetaDataインターフェースを実装しているので、
追加の属性としてfilename属性を定義しています。
MetaDataインターフェースについては後述します。
プロパティリスナーを作成するためにはPropertyChangeListenerインターフェース
を実装したクラスを作成します。
(ここでいうPropertyChangeListenerインターフェースはJavaBeansのPropertyChangeListenerではなく、
弊社独自のクラスです。)
このリスナーでは
の2つのイベントをハンドルすることができます。
propertyChangingではPropertyChangeVetoExceptionをthrowすることで、 プロパティ値の設定をキャンセルすることもできますので、 例えばプロパティ値の設定前に値をチェックして不正な場合はエラーとするようなプロパティリスナーを 作成することができます。
sampleフォルダに正規表現でプロパティ値をチェックして、
値が不正な場合にはエラーメッセージを表示した上で設定をキャンセルする
サンプル(RegexCheck.java)があります。
以下にその完全なコードを示しポイントを説明します。
package com.infoteria.asteria.flowbuilder2.plugin;
import com.infoteria.gui.property.event.PropertyChangeEvent;
import com.infoteria.gui.property.event.PropertyChangeListener;
import com.infoteria.gui.property.event.PropertyChangeVetoException;
import com.infoteria.gui.util.MetaData;
import org.w3c.dom.Element;
/**
* Property値の変更前に正規表現によるチェックを行うPropertyListenerです。
*/
public class RegexCheck implements PropertyChangeListener, MetaData, Cloneable {
private static final String A_REGEX = "regex";
private String _regex;
//MetaData //MetaDataインターフェースについては次章で説明します。
public void setup(Element el) {
_regex = el.getAttribute(A_REGEX);
}
public Object clone() throws CloneNotSupportedException {
return super.clone();
}
//PropertyChangeListener
public void propertyChanged(PropertyChangeEvent e) { //1
}
public void propertyChanging(PropertyChangeEvent e) throws PropertyChangeVetoException {
Object o = e.getNewValue(); //2
if (o instanceof String) {
String str = (String)o;
if (str.length() == 0)
return;
if (!str.matches(_regex)) {
PluginUtil.showError("Invalid value: " + str); //3
throw new PropertyChangeVetoException(); //4
}
}
}
}
作成したプロパティリスナーを定義ファイルに組み込むにはPropertyListener要素を追加し、 class属性でクラス名を、target属性で対象となるプロパティ名で指定します。
<!-- メールアドレス形式のチェック -->
<PropertyListener class="com.infoteria.asteria.flowbuilder2.plugin.RegexCheck"
target="From" regex="[a-zA-Z0-9_\.\-]+@[A-Za-z0-9_\.\-]+"/>
RegexCheckはMetaDataインターフェースを実装しているので、
追加の属性としてregex属性を定義しています。
MetaDataインターフェースについては次章で説明します。
PropertyEditorやPropertyChangeListenerを実装したクラスがさらにMetaDataインターフェースを 実装していた場合、そのクラスでは追加の定義情報を定義ファイルから読み込めるようになります。
定義ファイルの解析時にそれが定義された要素を引数としてMetaDataインターフェースのsetupメソッド が実行されるのでその情報を読み出すことができるのです。
ここまでに説明したふたつのサンプルではいずれもMetaDataインターフェースを実装し、
追加の定義情報を要素の属性値から取得していました。
サンプルには現れませんでしたが、要素に子要素を定義してさらに複雑な構造体を定義情報として
持たせることも可能です。
MetaDataインターフェースではもうひとつcloneメソッドも実装する必要がありますが、 プラグインではほとんどの場合DeepCopyを行う必要がないので単純にCloneableを宣言するだけで大丈夫です。
これから紹介するコンポーネントエディタと外部アプリケーション起動のふたつのプラグインでは 基底となるクラスでMetaDataインターフェースが実装されているので、 setupメソッドをオーバーライドする場合はその先頭で「super.setup(el);」を実行しなければなりません。
コンポーネントエディタはComponentEditorクラスのサブクラスとして作成します。
ComponentEditorは次のような処理が実装されています。
doActionメソッドはabstractメソッドとして定義されており、 プラグイン開発者がコンポーネントエディタの作成で実装しなければならないメソッドはこのメソッドのみです。
sampleフォルダのfcsample以下に標準のメールコンポーネントのプロパティをダイアログ上でまとめて 設定できるようにしたコンポーネントエディタのサンプルが入っています。
このサンプルコードには以下の処理が含まれています。
サンプルコードには現れませんがコンポーネントエディタでは他にも以下のようなことができます。
これらの処理の詳細な説明はここでは行いませんが興味のある方は御相談ください。
作成したコンポーネントエディタを定義ファイルに組み込むにはListener要素を追加し、 class属性でクラス名を、menuItem属性で右クリックメニューに追加するキャプションを指定します。
<!-- メールアドレス形式のチェック -->
<Listener class="com.infoteria.asteria.sample.plugin.MailEditor"
menuItem="プロパティの編集"/>
このサンプルでは使用していませんが、setupメソッドをオーバーライドして
独自の定義項目を追加することも可能です。
(setupメソッドをオーバーライドする場合は先頭でsuper.setupを行ってください。)
外部アプリケーション起動はLaunchExternalAppクラスのサブクラスとして作成します。
単純にプロパティ値をファイルに保存してアプリケーションを起動するだけならサブクラスを作成せずに
LaunchExternalAppクラスをそのまま使用することも可能です。
(定義ファイルリファレンスの外部アプリケーション起動を参照してください。)
外部アプリケーション起動はコンポーネントエディタの一種と考えることができます。
つまりコンポーネントエディタのdoActionメソッドの実装が以下の処理になっているものが外部アプリケーション起動です。
外部アプリケーションの起動中は以下のようなダイアログが表示され、デザイナーは外部アプリケーションが 終了するまでロックします。
LaunchExternalAppのサブクラスを作成する場合は通常はpreExecuteメソッドとpostExecuteメソッドを
オーバーライドします。
preExecuteでプロパティ値などのコンポーネント(マッパー関数)のプロパティなどの情報をファイルに書き出し、
postExecuteで外部アプリケーションが更新したファイルを読み込んでそれを書き戻します。
プロパティ値の取得や設定の方法自体はコンポーネントエディタの場合と同じです。
外部アプリケーション起動のサンプルは現在ありませんがこちらも興味のある方は御相談ください。