next up previous contents
Next: PropertyEditorManager Up: Property Editor Previous: PropertySheetは、どのように作られるか?

PropertyEditorSupport

PropertySheet上のeditor達は、後でふれるCustomerEditorを除いて、基本的には、 java.beans.PropertyEditor インターフェースを実装しています。その多くは、この インターフェースを実装した、次のリストにある PropertyEditorSupport クラスを 継承したものになっています。ColorEditor、FontEditorでも、このクラスの インスタンスが、フィールドに含まれています。

  public class StringEditor extends PropertyEditorSupport
  public class BoolEditor   extends PropertyEditorSupport 
  public class NumberEditor extends PropertyEditorSupport (abstract)
  public class ByteEditor   extends NumberEditor
  public class DoubleEditor extends NumberEditor
  public class FloatEditor  extends NumberEditor
  public class IntEditor    extends NumberEditor 
  public class LongEditor   extends NumberEditor
  public class ShortEditor  extends NumberEditor
  public class ColorEditor  extends Panel implements PropertyEditor
  public class FontEditor   extends Panel implements PropertyEditor

editorでのプロパティの変更は、このクラスのsetValueメソッドの呼び出しとして 実行され、setValueメソッドは、値を設定するや、ただちに firePropertyChange メソッドを呼び出します。このメソッドは、PropertyChangeEventのコンストラクタを 呼び出しイベント・オブジェクトを生成し、登録されている全てのリスナー上で このイベント・オブジェクトを引数にして、メソッドpropertyChangeを呼び出します。 簡単に言えば、editor上でのsetValueメソッドの呼び出しが、propertyChangeEventを 発生させるということです。

============================================================================
public class PropertyEditorSupport implements PropertyEditor {
   .......
    public void setValue(Object value) {
        this.value = value;
        firePropertyChange();
    }
   .......
   .......
   public void firePropertyChange() {
        java.util.Vector targets;
        synchronized (this) {
            if (listeners == null) {
                    return;
            }
            targets = (java.util.Vector) listeners.clone();
        }
        // Tell our listeners that "everything" has changed.
        PropertyChangeEvent evt = 
             new PropertyChangeEvent(source, null, null, null);
        for (int i = 0; i < targets.size(); i++) {
            PropertyChangeListener target = 
                (PropertyChangeListener)targets.elementAt(i);
            target.propertyChange(evt);
        }
    }
   .......
}
============================================================================

発生したイベントは、editorのlistenerに送られるのですが、先に見たように、 editorのlistenerには、フォーカスを持ったbeanに対応する EditedAdaptorクラスの インスタンスが登録されています。このアダプタ上でのpropertyChangeメソッドの 呼び出しは、PropertySheet上でのwasModifiedメソッドの呼び出しに引き戻されます。 要するに、editor上でのsetValueメソッドの呼び出しは、PropertySheet上での wasModifiedメソッドの呼び出しを引き起こすということです。

class EditedAdaptor implements PropertyChangeListener {
    EditedAdaptor(PropertySheet t) {
        sink = t;
    }        
    public void propertyChange(PropertyChangeEvent evt) {
        sink.wasModified(evt);
    }
    PropertySheet sink;
}

wasModifiedメソッドは、イベント・オブジェクトから、イベントのソースである editorを知ることが出来ます。PropertySheetPanelには、前に見たように、ターゲット のbeanのプロパティ・シート作成時に、このbeanに属するPropertyDescriptorの配列や、それに対応するeditorの配列などが作られていますので、イベントから得られるeditor の情報をこのeditor配列とをつきあわせると、どのPropertyDescriptorの値が変わった のかを知ることが出来ます。Object value = editor.getValue()でその値を獲得し、 今度は、Object args[] = value ; setter.invoke(target, args) で、ターゲット であるbeanに値を設定します。この時用いられるsetterメソッドは、 PropertyDescriptorから簡単に求めることが出来るはずです。

============================================================================
class PropertySheetPanel extends Panel {
 .......
    synchronized void wasModified(PropertyChangeEvent evt) {
        ........
        if (evt.getSource() instanceof PropertyEditor) {
            PropertyEditor editor = (PropertyEditor) evt.getSource();
            for (int i = 0 ; i < editors.length; i++) {
                if (editors[i] == editor) {
                    PropertyDescriptor property = properties[i];
                    // editorに設定されている値を獲得。
                    Object value = editor.getValue(); 
                    values[i] = value;   
                    // propertyから setter を獲得。
                    Method setter = property.getWriteMethod();
                    try {
                        Object args[] = { value };
                        args[0] = value;
                        setter.invoke(target, args); // targetに値を設定。
                    } catch (java.lang.reflect.InvocationTargetException ex) {
                       ........
                    }
                    ..........
                    break;
                }
            }
        }

        // 一つのpropertyが修正されたら、それをきっかけとして
        // そのほかのpropertyが変化していないかをチェックする。
        for (int i = 0; i < properties.length; i++) {
            Object o;
            try {
                Method getter = properties[i].getReadMethod();
                Object args[] = { };
                o = getter.invoke(target, args);
            } catch (Exception ex) {
                o = null;
            }
            if (o == values[i] || (o != null && o.equals(values[i]))) {
                continue;  // もとと変化していなければ、そのまま。
            }
            // 値が変わっていたら、editorに値を設定し直す。
            values[i] = o;
            ......
            editors[i].setValue(o);
            if (views[i] != null) {
                views[i].repaint();
            }
        }

        // ターゲットbeanを repaint する。
        if (Beans.isInstanceOf(target, Component.class)) {
            ((Component)(Beans.getInstanceOf(target, 
                                             Component.class))).repaint();
        }
    }
 .......
}
============================================================================

実際には、なかなか複雑で微妙な処理が行われています。wasModifiedメソッドは、 editorに設定されている値を獲得して、targetのbeanに値を設定するだけでなく、 そのほかのpropertyが変化していないかをチェックして、変化をeditorとbeanの両方に 反映しようとします。setValueの呼び出しで起動された wasModifiedメソッドの中に、 再び、setValueの呼び出しがあるのですが、これが無限ループを形成しないかチェック してみてください。 細かいことのようですが、この辺の処理は、自前で beansのCustomEditorを作ろうと するときには、とても参考になります。



maruyama@wakhok.ac.jp