BeanBoxでは、たとえば、パレットからJugglerと適当なボタンを選んで、BeanBox上に 配置して、ボタンを押せば、Jugglerの動きを止めるようにすることが、一行の プログラムを書くことなくマウスの操作だけで実現できます。 ボタンを押せば何らかのイベントが発生して、それがJugglerに伝わっているので しょうが、それは、いままで見てきたJDK1.1のイベント・モデルでは、どのように 説明されるのでしょう?
今回のテーマは、Jugglerにstopボタンをつけるという、単純なサンプルを通じて、 この時、BeanBoxで何がおきているのかを、BeanBox特有の動的なイベント処理を中心に 見ることです。
まず、メニュー Edit-->Eventsを選べば、イベントのソース側のBeanで発生するイベントの一覧がメニューに表示されます。その中からイベントを選ぶと、サブ・メニューで、 そのイベントが呼び出すメソッドを選択できます。 このように、イベントには、メソッド(の集まり)が対応づけられています。
Button Beanの場合には、"button push"と"bound property change"という項目が 選べて、サブ・メニューでは、それぞれ、"actionPerformed"と"propertyChange"と いうメソッドが選べます。
メニュー項目 イベント メソッド
---------------------------------------------------------------------------
button push (ActionEvent) actionPerformed
bound property change (PropertyChangeEvent) propertyChange
JugglerのBeanの場合には、もう少しイベントの数が増え、それぞれのイベント毎に 選択可能なメソッドも増えています。これは、以前に見た、java.awt.eventパッケージ で定義されている、同一のイベントが呼び出すメソッドを集めた、Listener インターフェース、あるいは Adapterクラスに対応しています。このイベントと メソッドの組を、Beansでは、EventSetと呼んでいます。BeansBoxでは、フォーカスされたBean毎に、そのEventSetに応じて、メニューの中身がダイナミックに変更されるの です。
メニュー項目 イベント メソッド
--------------------------------------------------------------------------
mouse MouseEvent MouseListenerのメソッド
key KeyEvent KeyListenerのメソッド
component ComponentEvent ComponentListenerのメソッド
container ContainerEvent ContainerListenerのメソッド
focus FocusEvent FocusListenerのメソッド
mouseMotion MouseMotionEvent MouseMotionListenerのメソッド
次のリストは、Editメニューから、EventSetとメソッドを選んだときにBeanBoxクラスで 実行されるメソッド doEventHookup のソースです。 eventMap,methodMapは、フォーカスを獲得したBeanのEventSetに応じて、メニューが 生成される時にメニューの値に応じて、それぞれEventSetDescriptorとMethodを格納 したHashtableです。 メソッドgetConnectionは、イベント発生元のBeanから、イベントの受け手となるBean まで赤い線を引きます。最後に、EventTargetDialogが呼び出されます。
-------------------------------------------------------------------------------
void doEventHookup(ActionEvent evt) {
Wrapper sourceWrapper = BeanBoxFrame.getCurrentWrapper();
MenuItem mi = (MenuItem)evt.getSource();
Menu parent = (Menu)mi.getParent();
EventSetDescriptor esd = (EventSetDescriptor) eventMap.get(parent);
Method meth = (Method) methodMap.get(mi);
if (esd == null || meth == null) {
return;
}
Wrapper targetWrapper = getConnection(sourceWrapper);
if (targetWrapper == null) {
return;
}
new EventTargetDialog(getFrame(), sourceWrapper, targetWrapper, esd, meth);
}
--------------------------------------------------------------------------------
EventTargetDialogでは、ターゲットのBeanのメソッドのうち、ソース側のメソッドと 同じ引数の型をもつメソッドか、あるいは、引数を持たず結果を返さないvoid型の メソッドを選びあげてソートしてリストに表示します。 次に、EventTargetDialogメソッドの主要部分を示します。
------------------------------------------------------------------------------
EventTargetDialog(Frame frame, Wrapper sourceWrapper, Wrapper targetWrapper,
EventSetDescriptor esd, Method listenerMethod) {
super(frame, "EventTargetDialog", false);
new WindowCloser(this);
setLayout(null);
this.esd = esd;
this.listenerMethod = listenerMethod;
Vector matchMethods = new Vector();
Vector zeroArgMethods = new Vector();
this.sourceWrapper = sourceWrapper;
this.targetWrapper = targetWrapper;
Object target = targetWrapper.getBean();
try {
// Search for target methods that either match the event
// listener signature or which have zero args and no results.
MethodDescriptor mds[] =
Introspector.getBeanInfo(target.getClass()).getMethodDescriptors();
Class eargs[] = listenerMethod.getParameterTypes();
for (int i = 0; i < mds.length; i++) {
MethodDescriptor md = mds[i];
Class margs[] = md.getMethod().getParameterTypes();
if (margs.length == 0 &&
md.getMethod().getReturnType() == Void.TYPE) {
zeroArgMethods.addElement(md);
continue;
}
if (eargs.length != margs.length) {
continue;
}
boolean match = true;
for (int j = 0; j < eargs.length; j++) {
if (!isSubclass(eargs[j], margs[j])) {
match = false;
break;
}
}
if (match) {
matchMethods.addElement(md);
}
}
} catch (Exception ex) {
new ErrorDialog(frame, "EventTargetDialog: Unexpected exception: \n" + ex);
return;
}
int width = 300;
sortMethods(matchMethods);
sortMethods(zeroArgMethods);
int count = matchMethods.size() + zeroArgMethods.size();
methods = new MethodDescriptor[count];
.......
.......
------------------------------------------------------------------------------
ここでは、次のようなところで、Reflection、Introspectorの機能が 活躍しています。
MethodDescriptor mds[] = Introspector.getBeanInfo(target.getClass()).getMethodDescriptors(); Class eargs[] = listenerMethod.getParameterTypes();