next up previous contents
Next: hookupメソッドの呼び出し Up: BeanBoxの動的なイベント処理 Previous: BeanBoxの動的なイベント処理

Jugglerにstopボタンをつける

BeanBoxでは、たとえば、パレットからJugglerと適当なボタンを選んで、BeanBox上に 配置して、ボタンを押せば、Jugglerの動きを止めるようにすることが、一行の プログラムを書くことなくマウスの操作だけで実現できます。 ボタンを押せば何らかのイベントが発生して、それがJugglerに伝わっているので しょうが、それは、いままで見てきたJDK1.1のイベント・モデルでは、どのように 説明されるのでしょう?

今回のテーマは、Jugglerにstopボタンをつけるという、単純なサンプルを通じて、 この時、BeanBoxで何がおきているのかを、BeanBox特有の動的なイベント処理を中心に 見ることです。

BeanBox Eventsメニューの構成

まず、メニュー 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のメソッド

メソッドの選択とwiring

次のリストは、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();


maruyama@wakhok.ac.jp