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();