Index: test/unit/src/org/openide/explorer/propertysheet/PropertyPanelTest.java
===================================================================
RCS file: test/unit/src/org/openide/explorer/propertysheet/PropertyPanelTest.java
diff -N test/unit/src/org/openide/explorer/propertysheet/PropertyPanelTest.java
*** /dev/null 1 Jan 1970 00:00:00 -0000
--- test/unit/src/org/openide/explorer/propertysheet/PropertyPanelTest.java 22 May 2002 11:48:24 -0000
***************
*** 0 ****
--- 1,247 ----
+ /*
+ * Sun Public License Notice
+ *
+ * The contents of this file are subject to the Sun Public License
+ * Version 1.0 (the "License"). You may not use this file except in
+ * compliance with the License. A copy of the License is available at
+ * http://www.sun.com/
+ *
+ * The Original Code is NetBeans. The Initial Developer of the Original
+ * Code is Sun Microsystems, Inc. Portions Copyright 1997-2002 Sun
+ * Microsystems, Inc. All Rights Reserved.
+ */
+
+ package org.openide.explorer.propertysheet;
+
+ import java.beans.*;
+ import java.lang.reflect.*;
+ import javax.swing.*;
+
+ import org.openide.*;
+ import org.openide.explorer.propertysheet.*;
+
+ import junit.framework.*;
+ import junit.textui.TestRunner;
+
+ import org.netbeans.junit.*;
+ import java.beans.PropertyDescriptor;
+ import java.awt.IllegalComponentStateException;
+ import java.lang.ref.WeakReference;
+ import java.lang.reflect.InvocationTargetException;
+
+ /** A test of a property panel.
+ */
+ public final class PropertyPanelTest extends NbTestCase {
+
+
+ public PropertyPanelTest(String name) {
+ super(name);
+ }
+
+ public static void main (String[] args) {
+ junit.textui.TestRunner.run (new NbTestSuite (PropertyPanelTest.class));
+ }
+
+ //
+ // Sample property impl
+ //
+
+ private String prop;
+
+ public void setProp (String x) {
+ prop = x;
+ }
+
+ public String getProp () {
+ return prop;
+ }
+
+ protected void setUp() throws Exception {
+ }
+
+
+ public void testStateUpdates () throws Exception {
+ PropertyDescriptor feature = new PropertyDescriptor ("prop", this.getClass ());
+ feature.setPropertyEditorClass (Ed.class);
+ DefaultPropertyModel model = new DefaultPropertyModel (
+ this, feature
+ );
+
+ PropertyPanel pp = new PropertyPanel (model, pp.PREF_CUSTOM_EDITOR);
+
+ assertTrue ("Ed editor created", pp.getPropertyEditor() instanceof Ed);
+
+ Ed ed = (Ed)pp.getPropertyEditor ();
+
+ assertNotNull("Environment has been attached", ed.env);
+
+ Listener envListener = new Listener ();
+ Listener panelListener = new Listener ();
+
+ pp.addPropertyChangeListener(panelListener);
+ ed.env.addPropertyChangeListener (envListener);
+ ed.env.addVetoableChangeListener (envListener);
+
+ ed.env.setState (PropertyEnv.STATE_INVALID);
+
+ assertEquals ("State of panel is invalid", PropertyEnv.STATE_INVALID, pp.getState ());
+ envListener.assertChanges ("Notified in environment", 1, 1);
+ panelListener.assertChanges ("Notified in panel", 1, 0);
+
+ ed.env.setState (PropertyEnv.STATE_INVALID);
+ assertEquals ("Remains invalid", PropertyEnv.STATE_INVALID, pp.getState ());
+ envListener.assertChanges ("No changes notified", 0, 0);
+ panelListener.assertChanges ("No changes notified in panel", 0, 0);
+
+ pp.updateValue();
+
+ assertEquals ("Update valud does not change the state if invalid", PropertyEnv.STATE_INVALID, pp.getState ());
+ envListener.assertChanges ("Changes notified in env", 0, 0);
+ panelListener.assertChanges ("Notified in panel", 0, 0);
+
+ ed.env.setState (PropertyEnv.STATE_NEEDS_VALIDATION);
+ assertEquals ("Now we need validation", PropertyEnv.STATE_NEEDS_VALIDATION, pp.getState ());
+ envListener.assertChanges ("Notified in environment", 1, 1);
+ panelListener.assertChanges ("Notified in panel", 1, 0);
+
+ pp.updateValue ();
+ assertEquals ("Update from needs validation shall switch to valid state if not vetoed", PropertyEnv.STATE_VALID, pp.getState ());
+ envListener.assertChanges ("Notified in environment", 1, 1);
+ panelListener.assertChanges ("Notified in panel", 1, 0);
+
+ ed.env.setState (PropertyEnv.STATE_NEEDS_VALIDATION);
+ assertEquals ("Now we need validation", PropertyEnv.STATE_NEEDS_VALIDATION, pp.getState ());
+ envListener.assertChanges ("Notified in environment", 1, 1);
+ panelListener.assertChanges ("Notified in panel", 1, 0);
+
+
+ envListener.shallVeto = true;
+ pp.updateValue ();
+ assertTrue ("Was vetoed", !envListener.shallVeto);
+
+ assertEquals ("The state remains", PropertyEnv.STATE_NEEDS_VALIDATION, pp.getState ());
+ envListener.assertChanges ("No approved property changes", 0, -1);
+ panelListener.assertChanges ("No approved property changes", 0, -1);
+
+
+ //
+ // Now try to do the cleanup
+ //
+
+ DefaultPropertyModel replace = new DefaultPropertyModel (this, "prop");
+ pp.setModel (replace);
+
+ assertEquals ("Model changed", replace, pp.getModel());
+
+
+ WeakReference wEd = new WeakReference (ed);
+ WeakReference wEnv = new WeakReference (ed.env);
+
+ ed = null;
+
+ assertGC ("Property editor should disappear", wEd);
+ assertGC ("Environment should disapper", wEnv);
+ }
+
+ public void testPropertyPanelShallGCEvenIfEditorExists () throws Exception {
+ PropertyDescriptor feature = new PropertyDescriptor ("prop", this.getClass ());
+ feature.setPropertyEditorClass (Ed.class);
+ DefaultPropertyModel model = new DefaultPropertyModel (
+ this, feature
+ );
+
+ PropertyPanel pp = new PropertyPanel (model, pp.PREF_CUSTOM_EDITOR);
+
+ assertTrue ("Ed editor created", pp.getPropertyEditor() instanceof Ed);
+
+ Ed ed = (Ed)pp.getPropertyEditor ();
+ assertNotNull("Environment has been attached", ed.env);
+
+ //
+ // Make sure that the panel listens on changes in env
+ //
+ Listener panelListener = new Listener ();
+
+ pp.addPropertyChangeListener(panelListener);
+ ed.env.setState (PropertyEnv.STATE_INVALID);
+ panelListener.assertChanges ("Change notified in panel", 1, 0);
+
+
+ WeakReference weak = new WeakReference (pp);
+ pp = null;
+ model = null;
+ feature = null;
+
+ assertGC ("Panel should disappear even if we have reference to property editor", weak);
+ }
+
+ /** Assert GC.
+ */
+ private static void assertGC (String text, java.lang.ref.Reference ref) {
+ for (int i = 0; i < 10; i++) {
+ if (ref.get () == null) {
+ return;
+ }
+ System.gc ();
+ System.runFinalization();
+ }
+ fail (text + " " + ref.get ());
+ }
+
+ /** Listener that counts changes.
+ */
+ private static final class Listener
+ implements PropertyChangeListener, VetoableChangeListener {
+ public boolean shallVeto;
+
+ private int veto;
+ private int change;
+
+ public void assertChanges (String t, int c, int v) {
+ if (c != -1) {
+ assertEquals (t + " [propertychange]", c, change);
+ }
+
+ if (v != -1) {
+ assertEquals (t + " [vetochange]", v, veto);
+ }
+
+ change = 0;
+ veto = 0;
+ }
+
+ public void propertyChange(java.beans.PropertyChangeEvent propertyChangeEvent) {
+ change++;
+ }
+
+ public void vetoableChange(java.beans.PropertyChangeEvent propertyChangeEvent) throws java.beans.PropertyVetoException {
+ if (shallVeto) {
+ shallVeto = false;
+ PropertyVetoException e = new PropertyVetoException ("Veto", propertyChangeEvent);
+
+ // marks this exception as one that we do not want to notify
+ PropertyDialogManager.doNotNotify (e);
+ throw e;
+ }
+
+ veto++;
+ }
+
+ }
+
+ /** Sample property editor.
+ */
+ private static final class Ed extends java.beans.PropertyEditorSupport
+ implements ExPropertyEditor {
+ public PropertyEnv env;
+
+ public Ed () {
+ }
+
+ public void attachEnv(PropertyEnv env) {
+ this.env = env;
+ }
+ }
+
+ }
+
Index: src/org/openide/explorer/propertysheet/PropertyDialogManager.java
===================================================================
RCS file: /cvs/openide/src/org/openide/explorer/propertysheet/PropertyDialogManager.java,v
retrieving revision 1.58
diff -c -r1.58 PropertyDialogManager.java
*** src/org/openide/explorer/propertysheet/PropertyDialogManager.java 17 May 2002 13:47:57 -0000 1.58
--- src/org/openide/explorer/propertysheet/PropertyDialogManager.java 22 May 2002 11:48:25 -0000
***************
*** 457,466 ****
--- 457,479 ----
return NbBundle.getBundle(PropertyDialogManager.class).getString(key);
}
+ /** For testing purposes we need to _not_ notify some exceptions.
+ * That is why here is a package private method to register an exception
+ * that should not be fired.
+ */
+ static void doNotNotify (Throwable ex) {
+ doNotNotify = ex;
+ }
+ private static Throwable doNotNotify;
+
/** Notifies an exception to error manager or prints its it to stderr.
* @param ex exception to notify
*/
static void notify(Throwable ex) {
+ Throwable d = doNotNotify;
+ doNotNotify = null;
+ if (d == ex) return;
+
ErrorManager.getDefault ().notify (ex);
}
***************
*** 468,473 ****
--- 481,490 ----
* @param ex exception to notify
*/
static void notify(int severity, Throwable ex) {
+ Throwable d = doNotNotify;
+ doNotNotify = null;
+ if (d == ex) return;
+
ErrorManager.getDefault ().notify(severity, ex);
}
/**
Index: src/org/openide/explorer/propertysheet/PropertyEnv.java
===================================================================
RCS file: /cvs/openide/src/org/openide/explorer/propertysheet/PropertyEnv.java,v
retrieving revision 1.7
diff -c -r1.7 PropertyEnv.java
*** src/org/openide/explorer/propertysheet/PropertyEnv.java 11 Apr 2002 16:31:38 -0000 1.7
--- src/org/openide/explorer/propertysheet/PropertyEnv.java 22 May 2002 11:48:26 -0000
***************
*** 14,19 ****
--- 14,21 ----
package org.openide.explorer.propertysheet;
import java.beans.FeatureDescriptor;
+ import java.beans.PropertyChangeListener;
+ import java.beans.PropertyChangeSupport;
import java.beans.VetoableChangeListener;
import java.beans.VetoableChangeSupport;
import java.beans.PropertyVetoException;
***************
*** 64,69 ****
--- 66,73 ----
/** The support is lazy initialized in getSupport. */
private VetoableChangeSupport support;
+ /** change support here */
+ private PropertyChangeSupport change;
/**
* The value of this field is basically taken from
***************
*** 133,141 ****
--- 137,148 ----
*/
public void setState (Object state) {
Object oldState = this.state;
+ // JST: should not this line move to //2//?
this.state = state;
try {
getSupport().fireVetoableChange(PROP_STATE, oldState, state);
+ //2// this.state = state;
+ getChange ().firePropertyChange (PROP_STATE, oldState, state);
} catch (PropertyVetoException pve) {
// the change should already be reverted (refired) by
// the fireVetoableChange method
***************
*** 166,171 ****
--- 173,186 ----
public void addVetoableChangeListener(VetoableChangeListener l) {
getSupport().addVetoableChangeListener(l);
}
+
+ /**
+ * Property change listener: listenning here you will be notified
+ * when the state of the environment is has been changed.
+ */
+ public void addPropertyChangeListener (PropertyChangeListener l) {
+ getChange ().addPropertyChangeListener (l);
+ }
/**
* Vetoable change listener removal.
***************
*** 174,179 ****
--- 189,202 ----
getSupport().removeVetoableChangeListener(l);
}
+ /**
+ * Removes Property change listener.
+ */
+ public void removePropertyChangeListener (PropertyChangeListener l) {
+ getChange ().removePropertyChangeListener (l);
+ }
+
+
/** Getter for property changeImmediate.
* @return Value of property changeImmediate.
*/
***************
*** 197,201 ****
--- 220,235 ----
}
return support;
}
+
+ /**
+ * Lazy initialization of the PropertyChangeSupport.
+ */
+ private synchronized PropertyChangeSupport getChange () {
+ if (change == null) {
+ change = new PropertyChangeSupport (this);
+ }
+ return change;
+ }
+
}
Index: src/org/openide/explorer/propertysheet/PropertyPanel.java
===================================================================
RCS file: /cvs/openide/src/org/openide/explorer/propertysheet/PropertyPanel.java,v
retrieving revision 1.107
diff -c -r1.107 PropertyPanel.java
*** src/org/openide/explorer/propertysheet/PropertyPanel.java 18 Apr 2002 17:09:21 -0000 1.107
--- src/org/openide/explorer/propertysheet/PropertyPanel.java 22 May 2002 11:48:28 -0000
***************
*** 92,97 ****
--- 92,100 ----
/** Name of the read-only property 'propertyEditor'. */
public static final String PROP_PROPERTY_EDITOR = "propertyEditor"; // NOI18N
+ /** Name of property 'state' that describes the state of the embeded PropertyEditor */
+ public static final String PROP_STATE = PropertyEnv.PROP_STATE;
+
/** Name of 'canEditAsText' property. */
private static final String PROP_CAN_EDIT_AS_TEXT = "canEditAsText"; // NOI18N
***************
*** 350,364 ****
reset();
firePropertyChange (PROP_MODEL, oldModel, model);
}
!
! /**
! * Updates the value if
* org.openide.explorer.propertysheet.editors.EnhancedCustomPropertyEditor
! * is used.
*/
public void updateValue() {
if (editor == null) return;
if (editor.supportsCustomEditor()) {
Component customEditor = editor.getCustomEditor();
if (customEditor instanceof EnhancedCustomPropertyEditor) {
--- 353,390 ----
reset();
firePropertyChange (PROP_MODEL, oldModel, model);
}
!
! /** Getter for the state of the property editor. The editor can be in
! * not valid states just if it implements the ExPropertyEditor
! * and changes state by the setState
method of the PropertyEnv
! * environment.
! *
! * @return PropertyEnv.STATE_VALID
if the editor is not the ExPropertyEditor
! * one or other constant from PropertyEnv.STATE_*
that was assigned to PropertyEnv
! */
! public final Object getState () {
! PropertyEnv e = env;
! return e == null ? PropertyEnv.STATE_VALID : e.getState();
! }
!
! /** If the editor is ExPropertyEditor it tries to change the
! * getState
property to PropertyEnv.STATE_VALID
! * state. This may be vetoed, in such case a warning is presented to the user
! * and the getState
will still return the original value
! * (different from STATE_VALID).
! *
! * Also updates the value if
* org.openide.explorer.propertysheet.editors.EnhancedCustomPropertyEditor
! * is used.
*/
public void updateValue() {
if (editor == null) return;
+ PropertyEnv e = env;
+ if (e != null && e.getState () == PropertyEnv.STATE_NEEDS_VALIDATION) {
+ e.setState (PropertyEnv.STATE_VALID);
+ }
+
if (editor.supportsCustomEditor()) {
Component customEditor = editor.getCustomEditor();
if (customEditor instanceof EnhancedCustomPropertyEditor) {
***************
*** 588,593 ****
--- 614,623 ----
// find new editor
editor = null;
+ if (env != null) {
+ env.removePropertyChangeListener(getEditorListener ());
+ }
+
env = null;
if (model instanceof ExPropertyModel) {
***************
*** 611,617 ****
Class editorClass = model.getPropertyEditorClass();
if (editorClass != null) {
try {
! editor = (PropertyEditor) editorClass.newInstance();
} catch (Exception e) {
PropertyDialogManager.notify(e);
}
--- 641,649 ----
Class editorClass = model.getPropertyEditorClass();
if (editorClass != null) {
try {
! java.lang.reflect.Constructor c = editorClass.getConstructor(new Class[0]);
! c.setAccessible (true);
! editor = (PropertyEditor) c.newInstance(new Object[0]);
} catch (Exception e) {
PropertyDialogManager.notify(e);
}
***************
*** 659,664 ****
--- 691,699 ----
}
if (canWrite) {
editor.addPropertyChangeListener(getEditorListener());
+ if (env != null) {
+ env.addPropertyChangeListener (getEditorListener ());
+ }
}
}
***************
*** 1865,1870 ****
--- 1900,1916 ----
private class EditorListener implements PropertyChangeListener {
/** Property was changed. */
public void propertyChange(PropertyChangeEvent evt) {
+ if (evt.getSource () instanceof PropertyEnv) {
+ // we are listening also on PropertyEnv
+
+ if (PropertyEnv.PROP_STATE.equals (evt.getPropertyName ())) {
+ PropertyPanel.this.firePropertyChange (PropertyEnv.PROP_STATE, evt.getOldValue(), evt.getNewValue());
+ }
+
+ return;
+ }
+
+
if (ignoreEvents) return;
if ((!isWriteState) && (!customDialogShown) &&