6630275: The spec on VetoableChangeSupport.fireVetoableChange should be updated
Reviewed-by: peterz, rupashka
--- a/jdk/src/share/classes/java/beans/PropertyChangeSupport.java Fri Jul 25 11:32:12 2008 -0400
+++ b/jdk/src/share/classes/java/beans/PropertyChangeSupport.java Fri Jul 25 21:00:05 2008 +0400
@@ -1,5 +1,5 @@
/*
- * Copyright 1996-2006 Sun Microsystems, Inc. All Rights Reserved.
+ * Copyright 1996-2008 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -208,91 +208,91 @@
}
/**
- * Report a bound property update to any registered listeners.
- * No event is fired if old and new are equal and non-null.
- *
+ * Reports a bound property update to listeners
+ * that have been registered to track updates of
+ * all properties or a property with the specified name.
+ * <p>
+ * No event is fired if old and new values are equal and non-null.
* <p>
* This is merely a convenience wrapper around the more general
- * firePropertyChange method that takes {@code
- * PropertyChangeEvent} value.
+ * {@link #firePropertyChange(PropertyChangeEvent)} method.
*
- * @param propertyName The programmatic name of the property
- * that was changed.
- * @param oldValue The old value of the property.
- * @param newValue The new value of the property.
+ * @param propertyName the programmatic name of the property that was changed
+ * @param oldValue the old value of the property
+ * @param newValue the new value of the property
*/
- public void firePropertyChange(String propertyName,
- Object oldValue, Object newValue) {
- if (oldValue != null && newValue != null && oldValue.equals(newValue)) {
- return;
+ public void firePropertyChange(String propertyName, Object oldValue, Object newValue) {
+ if (oldValue == null || newValue == null || !oldValue.equals(newValue)) {
+ firePropertyChange(new PropertyChangeEvent(this.source, propertyName, oldValue, newValue));
}
- firePropertyChange(new PropertyChangeEvent(source, propertyName,
- oldValue, newValue));
}
/**
- * Report an int bound property update to any registered listeners.
- * No event is fired if old and new are equal.
+ * Reports an integer bound property update to listeners
+ * that have been registered to track updates of
+ * all properties or a property with the specified name.
+ * <p>
+ * No event is fired if old and new values are equal.
* <p>
* This is merely a convenience wrapper around the more general
- * firePropertyChange method that takes Object values.
+ * {@link #firePropertyChange(String, Object, Object)} method.
*
- * @param propertyName The programmatic name of the property
- * that was changed.
- * @param oldValue The old value of the property.
- * @param newValue The new value of the property.
+ * @param propertyName the programmatic name of the property that was changed
+ * @param oldValue the old value of the property
+ * @param newValue the new value of the property
*/
- public void firePropertyChange(String propertyName,
- int oldValue, int newValue) {
- if (oldValue == newValue) {
- return;
+ public void firePropertyChange(String propertyName, int oldValue, int newValue) {
+ if (oldValue != newValue) {
+ firePropertyChange(propertyName, Integer.valueOf(oldValue), Integer.valueOf(newValue));
}
- firePropertyChange(propertyName, Integer.valueOf(oldValue), Integer.valueOf(newValue));
}
/**
- * Report a boolean bound property update to any registered listeners.
- * No event is fired if old and new are equal.
+ * Reports a boolean bound property update to listeners
+ * that have been registered to track updates of
+ * all properties or a property with the specified name.
+ * <p>
+ * No event is fired if old and new values are equal.
* <p>
* This is merely a convenience wrapper around the more general
- * firePropertyChange method that takes Object values.
+ * {@link #firePropertyChange(String, Object, Object)} method.
*
- * @param propertyName The programmatic name of the property
- * that was changed.
- * @param oldValue The old value of the property.
- * @param newValue The new value of the property.
+ * @param propertyName the programmatic name of the property that was changed
+ * @param oldValue the old value of the property
+ * @param newValue the new value of the property
*/
- public void firePropertyChange(String propertyName,
- boolean oldValue, boolean newValue) {
- if (oldValue == newValue) {
- return;
+ public void firePropertyChange(String propertyName, boolean oldValue, boolean newValue) {
+ if (oldValue != newValue) {
+ firePropertyChange(propertyName, Boolean.valueOf(oldValue), Boolean.valueOf(newValue));
}
- firePropertyChange(propertyName, Boolean.valueOf(oldValue), Boolean.valueOf(newValue));
}
/**
- * Fire an existing PropertyChangeEvent to any registered listeners.
- * No event is fired if the given event's old and new values are
- * equal and non-null.
- * @param evt The PropertyChangeEvent object.
+ * Fires a property change event to listeners
+ * that have been registered to track updates of
+ * all properties or a property with the specified name.
+ * <p>
+ * No event is fired if the given event's old and new values are equal and non-null.
+ *
+ * @param event the {@code PropertyChangeEvent} to be fired
*/
- public void firePropertyChange(PropertyChangeEvent evt) {
- Object oldValue = evt.getOldValue();
- Object newValue = evt.getNewValue();
- String propertyName = evt.getPropertyName();
- if (oldValue != null && newValue != null && oldValue.equals(newValue)) {
- return;
+ public void firePropertyChange(PropertyChangeEvent event) {
+ Object oldValue = event.getOldValue();
+ Object newValue = event.getNewValue();
+ if (oldValue == null || newValue == null || !oldValue.equals(newValue)) {
+ String name = event.getPropertyName();
+
+ PropertyChangeListener[] common = this.map.get(null);
+ PropertyChangeListener[] named = (name != null)
+ ? this.map.get(name)
+ : null;
+
+ fire(common, event);
+ fire(named, event);
}
- PropertyChangeListener[] common = this.map.get(null);
- PropertyChangeListener[] named = (propertyName != null)
- ? this.map.get(propertyName)
- : null;
-
- fire(common, evt);
- fire(named, evt);
}
- private void fire(PropertyChangeListener[] listeners, PropertyChangeEvent event) {
+ private static void fire(PropertyChangeListener[] listeners, PropertyChangeEvent event) {
if (listeners != null) {
for (PropertyChangeListener listener : listeners) {
listener.propertyChange(event);
@@ -301,78 +301,69 @@
}
/**
- * Report a bound indexed property update to any registered
- * listeners.
+ * Reports a bound indexed property update to listeners
+ * that have been registered to track updates of
+ * all properties or a property with the specified name.
* <p>
- * No event is fired if old and new values are equal
- * and non-null.
- *
+ * No event is fired if old and new values are equal and non-null.
* <p>
* This is merely a convenience wrapper around the more general
- * firePropertyChange method that takes {@code PropertyChangeEvent} value.
+ * {@link #firePropertyChange(PropertyChangeEvent)} method.
*
- * @param propertyName The programmatic name of the property that
- * was changed.
- * @param index index of the property element that was changed.
- * @param oldValue The old value of the property.
- * @param newValue The new value of the property.
+ * @param propertyName the programmatic name of the property that was changed
+ * @param index the index of the property element that was changed
+ * @param oldValue the old value of the property
+ * @param newValue the new value of the property
* @since 1.5
*/
- public void fireIndexedPropertyChange(String propertyName, int index,
- Object oldValue, Object newValue) {
- firePropertyChange(new IndexedPropertyChangeEvent
- (source, propertyName, oldValue, newValue, index));
+ public void fireIndexedPropertyChange(String propertyName, int index, Object oldValue, Object newValue) {
+ if (oldValue == null || newValue == null || !oldValue.equals(newValue)) {
+ firePropertyChange(new IndexedPropertyChangeEvent(source, propertyName, oldValue, newValue, index));
+ }
}
/**
- * Report an <code>int</code> bound indexed property update to any registered
- * listeners.
+ * Reports an integer bound indexed property update to listeners
+ * that have been registered to track updates of
+ * all properties or a property with the specified name.
* <p>
* No event is fired if old and new values are equal.
* <p>
* This is merely a convenience wrapper around the more general
- * fireIndexedPropertyChange method which takes Object values.
+ * {@link #fireIndexedPropertyChange(String, int, Object, Object)} method.
*
- * @param propertyName The programmatic name of the property that
- * was changed.
- * @param index index of the property element that was changed.
- * @param oldValue The old value of the property.
- * @param newValue The new value of the property.
+ * @param propertyName the programmatic name of the property that was changed
+ * @param index the index of the property element that was changed
+ * @param oldValue the old value of the property
+ * @param newValue the new value of the property
* @since 1.5
*/
- public void fireIndexedPropertyChange(String propertyName, int index,
- int oldValue, int newValue) {
- if (oldValue == newValue) {
- return;
+ public void fireIndexedPropertyChange(String propertyName, int index, int oldValue, int newValue) {
+ if (oldValue != newValue) {
+ fireIndexedPropertyChange(propertyName, index, Integer.valueOf(oldValue), Integer.valueOf(newValue));
}
- fireIndexedPropertyChange(propertyName, index,
- Integer.valueOf(oldValue),
- Integer.valueOf(newValue));
}
/**
- * Report a <code>boolean</code> bound indexed property update to any
- * registered listeners.
+ * Reports a boolean bound indexed property update to listeners
+ * that have been registered to track updates of
+ * all properties or a property with the specified name.
* <p>
* No event is fired if old and new values are equal.
* <p>
* This is merely a convenience wrapper around the more general
- * fireIndexedPropertyChange method which takes Object values.
+ * {@link #fireIndexedPropertyChange(String, int, Object, Object)} method.
*
- * @param propertyName The programmatic name of the property that
- * was changed.
- * @param index index of the property element that was changed.
- * @param oldValue The old value of the property.
- * @param newValue The new value of the property.
+ * @param propertyName the programmatic name of the property that was changed
+ * @param index the index of the property element that was changed
+ * @param oldValue the old value of the property
+ * @param newValue the new value of the property
* @since 1.5
*/
- public void fireIndexedPropertyChange(String propertyName, int index,
- boolean oldValue, boolean newValue) {
- if (oldValue == newValue) {
- return;
+ public void fireIndexedPropertyChange(String propertyName, int index, boolean oldValue, boolean newValue) {
+ if (oldValue != newValue) {
+ fireIndexedPropertyChange(propertyName, index, Boolean.valueOf(oldValue), Boolean.valueOf(newValue));
}
- fireIndexedPropertyChange(propertyName, index, Boolean.valueOf(oldValue),
- Boolean.valueOf(newValue));
}
/**
--- a/jdk/src/share/classes/java/beans/VetoableChangeSupport.java Fri Jul 25 11:32:12 2008 -0400
+++ b/jdk/src/share/classes/java/beans/VetoableChangeSupport.java Fri Jul 25 21:00:05 2008 +0400
@@ -1,5 +1,5 @@
/*
- * Copyright 1996-2006 Sun Microsystems, Inc. All Rights Reserved.
+ * Copyright 1996-2008 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -208,126 +208,149 @@
}
/**
- * Report a vetoable property update to any registered listeners. If
- * anyone vetos the change, then fire a new event reverting everyone to
- * the old value and then rethrow the PropertyVetoException.
+ * Reports a constrained property update to listeners
+ * that have been registered to track updates of
+ * all properties or a property with the specified name.
* <p>
- * No event is fired if old and new are equal and non-null.
+ * Any listener can throw a {@code PropertyVetoException} to veto the update.
+ * If one of the listeners vetoes the update, this method passes
+ * a new "undo" {@code PropertyChangeEvent} that reverts to the old value
+ * to all listeners that already confirmed this update
+ * and throws the {@code PropertyVetoException} again.
+ * <p>
+ * No event is fired if old and new values are equal and non-null.
+ * <p>
+ * This is merely a convenience wrapper around the more general
+ * {@link #fireVetoableChange(PropertyChangeEvent)} method.
*
- * @param propertyName The programmatic name of the property
- * that is about to change..
- * @param oldValue The old value of the property.
- * @param newValue The new value of the property.
- * @exception PropertyVetoException if the recipient wishes the property
- * change to be rolled back.
+ * @param propertyName the programmatic name of the property that is about to change
+ * @param oldValue the old value of the property
+ * @param newValue the new value of the property
+ * @throws PropertyVetoException if one of listeners vetoes the property update
*/
- public void fireVetoableChange(String propertyName,
- Object oldValue, Object newValue)
- throws PropertyVetoException {
- if (oldValue != null && newValue != null && oldValue.equals(newValue)) {
- return;
+ public void fireVetoableChange(String propertyName, Object oldValue, Object newValue)
+ throws PropertyVetoException {
+ if (oldValue == null || newValue == null || !oldValue.equals(newValue)) {
+ fireVetoableChange(new PropertyChangeEvent(this.source, propertyName, oldValue, newValue));
}
- PropertyChangeEvent evt = new PropertyChangeEvent(source, propertyName,
- oldValue, newValue);
- fireVetoableChange(evt);
}
/**
- * Report a int vetoable property update to any registered listeners.
- * No event is fired if old and new are equal.
+ * Reports an integer constrained property update to listeners
+ * that have been registered to track updates of
+ * all properties or a property with the specified name.
+ * <p>
+ * Any listener can throw a {@code PropertyVetoException} to veto the update.
+ * If one of the listeners vetoes the update, this method passes
+ * a new "undo" {@code PropertyChangeEvent} that reverts to the old value
+ * to all listeners that already confirmed this update
+ * and throws the {@code PropertyVetoException} again.
+ * <p>
+ * No event is fired if old and new values are equal.
* <p>
* This is merely a convenience wrapper around the more general
- * fireVetoableChange method that takes Object values.
+ * {@link #fireVetoableChange(String, Object, Object)} method.
*
- * @param propertyName The programmatic name of the property
- * that is about to change.
- * @param oldValue The old value of the property.
- * @param newValue The new value of the property.
+ * @param propertyName the programmatic name of the property that is about to change
+ * @param oldValue the old value of the property
+ * @param newValue the new value of the property
+ * @throws PropertyVetoException if one of listeners vetoes the property update
*/
- public void fireVetoableChange(String propertyName,
- int oldValue, int newValue)
- throws PropertyVetoException {
- if (oldValue == newValue) {
- return;
+ public void fireVetoableChange(String propertyName, int oldValue, int newValue)
+ throws PropertyVetoException {
+ if (oldValue != newValue) {
+ fireVetoableChange(propertyName, Integer.valueOf(oldValue), Integer.valueOf(newValue));
}
- fireVetoableChange(propertyName, Integer.valueOf(oldValue), Integer.valueOf(newValue));
}
/**
- * Report a boolean vetoable property update to any registered listeners.
- * No event is fired if old and new are equal.
+ * Reports a boolean constrained property update to listeners
+ * that have been registered to track updates of
+ * all properties or a property with the specified name.
+ * <p>
+ * Any listener can throw a {@code PropertyVetoException} to veto the update.
+ * If one of the listeners vetoes the update, this method passes
+ * a new "undo" {@code PropertyChangeEvent} that reverts to the old value
+ * to all listeners that already confirmed this update
+ * and throws the {@code PropertyVetoException} again.
+ * <p>
+ * No event is fired if old and new values are equal.
* <p>
* This is merely a convenience wrapper around the more general
- * fireVetoableChange method that takes Object values.
+ * {@link #fireVetoableChange(String, Object, Object)} method.
*
- * @param propertyName The programmatic name of the property
- * that is about to change.
- * @param oldValue The old value of the property.
- * @param newValue The new value of the property.
+ * @param propertyName the programmatic name of the property that is about to change
+ * @param oldValue the old value of the property
+ * @param newValue the new value of the property
+ * @throws PropertyVetoException if one of listeners vetoes the property update
*/
- public void fireVetoableChange(String propertyName,
- boolean oldValue, boolean newValue)
- throws PropertyVetoException {
- if (oldValue == newValue) {
- return;
+ public void fireVetoableChange(String propertyName, boolean oldValue, boolean newValue)
+ throws PropertyVetoException {
+ if (oldValue != newValue) {
+ fireVetoableChange(propertyName, Boolean.valueOf(oldValue), Boolean.valueOf(newValue));
}
- fireVetoableChange(propertyName, Boolean.valueOf(oldValue), Boolean.valueOf(newValue));
}
/**
- * Fire a vetoable property update to any registered listeners. If
- * anyone vetos the change, then fire a new event reverting everyone to
- * the old value and then rethrow the PropertyVetoException.
+ * Fires a property change event to listeners
+ * that have been registered to track updates of
+ * all properties or a property with the specified name.
* <p>
- * No event is fired if old and new are equal and non-null.
+ * Any listener can throw a {@code PropertyVetoException} to veto the update.
+ * If one of the listeners vetoes the update, this method passes
+ * a new "undo" {@code PropertyChangeEvent} that reverts to the old value
+ * to all listeners that already confirmed this update
+ * and throws the {@code PropertyVetoException} again.
+ * <p>
+ * No event is fired if the given event's old and new values are equal and non-null.
*
- * @param evt The PropertyChangeEvent to be fired.
- * @exception PropertyVetoException if the recipient wishes the property
- * change to be rolled back.
+ * @param event the {@code PropertyChangeEvent} to be fired
+ * @throws PropertyVetoException if one of listeners vetoes the property update
*/
- public void fireVetoableChange(PropertyChangeEvent evt)
- throws PropertyVetoException {
+ public void fireVetoableChange(PropertyChangeEvent event)
+ throws PropertyVetoException {
+ Object oldValue = event.getOldValue();
+ Object newValue = event.getNewValue();
+ if (oldValue == null || newValue == null || !oldValue.equals(newValue)) {
+ String name = event.getPropertyName();
- Object oldValue = evt.getOldValue();
- Object newValue = evt.getNewValue();
- String propertyName = evt.getPropertyName();
- if (oldValue != null && newValue != null && oldValue.equals(newValue)) {
- return;
- }
- VetoableChangeListener[] common = this.map.get(null);
- VetoableChangeListener[] named = (propertyName != null)
- ? this.map.get(propertyName)
- : null;
- fire(common, evt);
- fire(named, evt);
- }
+ VetoableChangeListener[] common = this.map.get(null);
+ VetoableChangeListener[] named = (name != null)
+ ? this.map.get(name)
+ : null;
- private void fire(VetoableChangeListener[] listeners, PropertyChangeEvent event) throws PropertyVetoException {
- if (listeners != null) {
- VetoableChangeListener current = null;
- try {
- for (VetoableChangeListener listener : listeners) {
- current = listener;
- listener.vetoableChange(event);
- }
- } catch (PropertyVetoException veto) {
- // Create an event to revert everyone to the old value.
- event = new PropertyChangeEvent( this.source,
- event.getPropertyName(),
- event.getNewValue(),
- event.getOldValue() );
- for (VetoableChangeListener listener : listeners) {
- if (current == listener) {
- break;
- }
- try {
- listener.vetoableChange(event);
- } catch (PropertyVetoException ex) {
- // We just ignore exceptions that occur during reversions.
+ VetoableChangeListener[] listeners;
+ if (common == null) {
+ listeners = named;
+ }
+ else if (named == null) {
+ listeners = common;
+ }
+ else {
+ listeners = new VetoableChangeListener[common.length + named.length];
+ System.arraycopy(common, 0, listeners, 0, common.length);
+ System.arraycopy(named, 0, listeners, common.length, named.length);
+ }
+ if (listeners != null) {
+ int current = 0;
+ try {
+ while (current < listeners.length) {
+ listeners[current].vetoableChange(event);
+ current++;
}
}
- // And now rethrow the PropertyVetoException.
- throw veto;
+ catch (PropertyVetoException veto) {
+ event = new PropertyChangeEvent(this.source, name, newValue, oldValue);
+ for (int i = 0; i < current; i++) {
+ try {
+ listeners[i].vetoableChange(event);
+ }
+ catch (PropertyVetoException exception) {
+ // ignore exceptions that occur during rolling back
+ }
+ }
+ throw veto; // rethrow the veto exception
+ }
}
}
}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/beans/VetoableChangeSupport/Test6630275.java Fri Jul 25 21:00:05 2008 +0400
@@ -0,0 +1,81 @@
+/*
+ * Copyright 2008 Sun Microsystems, Inc. All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+/*
+ * @test
+ * @bug 6630275
+ * @summary Tests VetoableChangeSupport specification
+ * @author Sergey Malenkov
+ */
+
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyVetoException;
+import java.beans.VetoableChangeListener;
+import java.beans.VetoableChangeSupport;
+
+public class Test6630275 {
+ private static final String PROPERTY = "property"; // NON-NLS: predefined property name
+
+ public static void main(String[] args) {
+ CheckListener first = new CheckListener(false);
+ CheckListener second = new CheckListener(true);
+ CheckListener third = new CheckListener(false);
+
+ VetoableChangeSupport vcs = new VetoableChangeSupport(Test6630275.class);
+ vcs.addVetoableChangeListener(first);
+ vcs.addVetoableChangeListener(PROPERTY, first);
+ vcs.addVetoableChangeListener(PROPERTY, second);
+ vcs.addVetoableChangeListener(PROPERTY, third);
+ try {
+ vcs.fireVetoableChange(PROPERTY, true, false);
+ } catch (PropertyVetoException exception) {
+ first.validate();
+ second.validate();
+ third.validate();
+ return; // expected exception
+ }
+ throw new Error("exception should be thrown");
+ }
+
+ private static class CheckListener implements VetoableChangeListener {
+ private final boolean veto;
+ private boolean odd; // even/odd check for notification
+
+ private CheckListener(boolean veto) {
+ this.veto = veto;
+ }
+
+ private void validate() {
+ if (this.veto != this.odd)
+ throw new Error(this.odd
+ ? "undo event expected"
+ : "unexpected undo event");
+ }
+
+ public void vetoableChange(PropertyChangeEvent event) throws PropertyVetoException {
+ this.odd = !this.odd;
+ if (this.veto)
+ throw new PropertyVetoException("disable all changes", event);
+ }
+ }
+}