60 * return this.value;
61 * }
62 *
63 * public void setValue(String newValue) throws PropertyVetoException {
64 * String oldValue = this.value;
65 * this.vcs.fireVetoableChange("value", oldValue, newValue);
66 * this.value = newValue;
67 * }
68 *
69 * [...]
70 * }
71 * }</pre>
72 * <p>
73 * A {@code VetoableChangeSupport} instance is thread-safe.
74 * <p>
75 * This class is serializable. When it is serialized it will save
76 * (and restore) any listeners that are themselves serializable. Any
77 * non-serializable listeners will be skipped during serialization.
78 *
79 * @see PropertyChangeSupport
80 */
81 public class VetoableChangeSupport implements Serializable {
82 private VetoableChangeListenerMap map = new VetoableChangeListenerMap();
83
84 /**
85 * Constructs a <code>VetoableChangeSupport</code> object.
86 *
87 * @param sourceBean The bean to be given as the source for any events.
88 */
89 public VetoableChangeSupport(Object sourceBean) {
90 if (sourceBean == null) {
91 throw new NullPointerException();
92 }
93 source = sourceBean;
94 }
95
96 /**
97 * Add a VetoableChangeListener to the listener list.
98 * The listener is registered for all properties.
99 * The same listener object may be added more than once, and will be called
174 * @return all of the <code>VetoableChangeListeners</code> added or an
175 * empty array if no listeners have been added
176 * @since 1.4
177 */
178 public VetoableChangeListener[] getVetoableChangeListeners(){
179 return this.map.getListeners();
180 }
181
182 /**
183 * Add a VetoableChangeListener for a specific property. The listener
184 * will be invoked only when a call on fireVetoableChange names that
185 * specific property.
186 * The same listener object may be added more than once. For each
187 * property, the listener will be invoked the number of times it was added
188 * for that property.
189 * If <code>propertyName</code> or <code>listener</code> is null, no
190 * exception is thrown and no action is taken.
191 *
192 * @param propertyName The name of the property to listen on.
193 * @param listener The VetoableChangeListener to be added
194 */
195 public void addVetoableChangeListener(
196 String propertyName,
197 VetoableChangeListener listener) {
198 if (listener == null || propertyName == null) {
199 return;
200 }
201 listener = this.map.extract(listener);
202 if (listener != null) {
203 this.map.add(propertyName, listener);
204 }
205 }
206
207 /**
208 * Remove a VetoableChangeListener for a specific property.
209 * If <code>listener</code> was added more than once to the same event
210 * source for the specified property, it will be notified one less time
211 * after being removed.
212 * If <code>propertyName</code> is null, no exception is thrown and no
213 * action is taken.
214 * If <code>listener</code> is null, or was never added for the specified
215 * property, no exception is thrown and no action is taken.
216 *
217 * @param propertyName The name of the property that was listened on.
218 * @param listener The VetoableChangeListener to be removed
219 */
220 public void removeVetoableChangeListener(
221 String propertyName,
222 VetoableChangeListener listener) {
223 if (listener == null || propertyName == null) {
224 return;
225 }
226 listener = this.map.extract(listener);
227 if (listener != null) {
228 this.map.remove(propertyName, listener);
229 }
230 }
231
232 /**
233 * Returns an array of all the listeners which have been associated
234 * with the named property.
235 *
236 * @param propertyName The name of the property being listened to
237 * @return all the <code>VetoableChangeListeners</code> associated with
238 * the named property. If no such listeners have been added,
275 /**
276 * Reports an integer constrained property update to listeners
277 * that have been registered to track updates of
278 * all properties or a property with the specified name.
279 * <p>
280 * Any listener can throw a {@code PropertyVetoException} to veto the update.
281 * If one of the listeners vetoes the update, this method passes
282 * a new "undo" {@code PropertyChangeEvent} that reverts to the old value
283 * to all listeners that already confirmed this update
284 * and throws the {@code PropertyVetoException} again.
285 * <p>
286 * No event is fired if old and new values are equal.
287 * <p>
288 * This is merely a convenience wrapper around the more general
289 * {@link #fireVetoableChange(String, Object, Object)} method.
290 *
291 * @param propertyName the programmatic name of the property that is about to change
292 * @param oldValue the old value of the property
293 * @param newValue the new value of the property
294 * @throws PropertyVetoException if one of listeners vetoes the property update
295 */
296 public void fireVetoableChange(String propertyName, int oldValue, int newValue)
297 throws PropertyVetoException {
298 if (oldValue != newValue) {
299 fireVetoableChange(propertyName, Integer.valueOf(oldValue), Integer.valueOf(newValue));
300 }
301 }
302
303 /**
304 * Reports a boolean constrained property update to listeners
305 * that have been registered to track updates of
306 * all properties or a property with the specified name.
307 * <p>
308 * Any listener can throw a {@code PropertyVetoException} to veto the update.
309 * If one of the listeners vetoes the update, this method passes
310 * a new "undo" {@code PropertyChangeEvent} that reverts to the old value
311 * to all listeners that already confirmed this update
312 * and throws the {@code PropertyVetoException} again.
313 * <p>
314 * No event is fired if old and new values are equal.
315 * <p>
316 * This is merely a convenience wrapper around the more general
317 * {@link #fireVetoableChange(String, Object, Object)} method.
318 *
319 * @param propertyName the programmatic name of the property that is about to change
320 * @param oldValue the old value of the property
321 * @param newValue the new value of the property
322 * @throws PropertyVetoException if one of listeners vetoes the property update
323 */
324 public void fireVetoableChange(String propertyName, boolean oldValue, boolean newValue)
325 throws PropertyVetoException {
326 if (oldValue != newValue) {
327 fireVetoableChange(propertyName, Boolean.valueOf(oldValue), Boolean.valueOf(newValue));
328 }
329 }
330
331 /**
332 * Fires a property change event to listeners
333 * that have been registered to track updates of
334 * all properties or a property with the specified name.
335 * <p>
336 * Any listener can throw a {@code PropertyVetoException} to veto the update.
337 * If one of the listeners vetoes the update, this method passes
338 * a new "undo" {@code PropertyChangeEvent} that reverts to the old value
339 * to all listeners that already confirmed this update
340 * and throws the {@code PropertyVetoException} again.
341 * <p>
342 * No event is fired if the given event's old and new values are equal and non-null.
343 *
344 * @param event the {@code PropertyChangeEvent} to be fired
345 * @throws PropertyVetoException if one of listeners vetoes the property update
346 */
347 public void fireVetoableChange(PropertyChangeEvent event)
348 throws PropertyVetoException {
349 Object oldValue = event.getOldValue();
350 Object newValue = event.getNewValue();
351 if (oldValue == null || newValue == null || !oldValue.equals(newValue)) {
352 String name = event.getPropertyName();
353
354 VetoableChangeListener[] common = this.map.get(null);
355 VetoableChangeListener[] named = (name != null)
356 ? this.map.get(name)
357 : null;
358
359 VetoableChangeListener[] listeners;
360 if (common == null) {
361 listeners = named;
362 }
363 else if (named == null) {
364 listeners = common;
365 }
382 try {
383 listeners[i].vetoableChange(event);
384 }
385 catch (PropertyVetoException exception) {
386 // ignore exceptions that occur during rolling back
387 }
388 }
389 throw veto; // rethrow the veto exception
390 }
391 }
392 }
393 }
394
395 /**
396 * Check if there are any listeners for a specific property, including
397 * those registered on all properties. If <code>propertyName</code>
398 * is null, only check for listeners registered on all properties.
399 *
400 * @param propertyName the property name.
401 * @return true if there are one or more listeners for the given property
402 */
403 public boolean hasListeners(String propertyName) {
404 return this.map.hasListeners(propertyName);
405 }
406
407 /**
408 * @serialData Null terminated list of <code>VetoableChangeListeners</code>.
409 * <p>
410 * At serialization time we skip non-serializable listeners and
411 * only serialize the serializable listeners.
412 */
413 private void writeObject(ObjectOutputStream s) throws IOException {
414 Hashtable<String, VetoableChangeSupport> children = null;
415 VetoableChangeListener[] listeners = null;
416 synchronized (this.map) {
417 for (Entry<String, VetoableChangeListener[]> entry : this.map.getEntries()) {
418 String property = entry.getKey();
419 if (property == null) {
420 listeners = entry.getValue();
421 } else {
|
60 * return this.value;
61 * }
62 *
63 * public void setValue(String newValue) throws PropertyVetoException {
64 * String oldValue = this.value;
65 * this.vcs.fireVetoableChange("value", oldValue, newValue);
66 * this.value = newValue;
67 * }
68 *
69 * [...]
70 * }
71 * }</pre>
72 * <p>
73 * A {@code VetoableChangeSupport} instance is thread-safe.
74 * <p>
75 * This class is serializable. When it is serialized it will save
76 * (and restore) any listeners that are themselves serializable. Any
77 * non-serializable listeners will be skipped during serialization.
78 *
79 * @see PropertyChangeSupport
80 * @since 1.1
81 */
82 public class VetoableChangeSupport implements Serializable {
83 private VetoableChangeListenerMap map = new VetoableChangeListenerMap();
84
85 /**
86 * Constructs a <code>VetoableChangeSupport</code> object.
87 *
88 * @param sourceBean The bean to be given as the source for any events.
89 */
90 public VetoableChangeSupport(Object sourceBean) {
91 if (sourceBean == null) {
92 throw new NullPointerException();
93 }
94 source = sourceBean;
95 }
96
97 /**
98 * Add a VetoableChangeListener to the listener list.
99 * The listener is registered for all properties.
100 * The same listener object may be added more than once, and will be called
175 * @return all of the <code>VetoableChangeListeners</code> added or an
176 * empty array if no listeners have been added
177 * @since 1.4
178 */
179 public VetoableChangeListener[] getVetoableChangeListeners(){
180 return this.map.getListeners();
181 }
182
183 /**
184 * Add a VetoableChangeListener for a specific property. The listener
185 * will be invoked only when a call on fireVetoableChange names that
186 * specific property.
187 * The same listener object may be added more than once. For each
188 * property, the listener will be invoked the number of times it was added
189 * for that property.
190 * If <code>propertyName</code> or <code>listener</code> is null, no
191 * exception is thrown and no action is taken.
192 *
193 * @param propertyName The name of the property to listen on.
194 * @param listener The VetoableChangeListener to be added
195 * @since 1.2
196 */
197 public void addVetoableChangeListener(
198 String propertyName,
199 VetoableChangeListener listener) {
200 if (listener == null || propertyName == null) {
201 return;
202 }
203 listener = this.map.extract(listener);
204 if (listener != null) {
205 this.map.add(propertyName, listener);
206 }
207 }
208
209 /**
210 * Remove a VetoableChangeListener for a specific property.
211 * If <code>listener</code> was added more than once to the same event
212 * source for the specified property, it will be notified one less time
213 * after being removed.
214 * If <code>propertyName</code> is null, no exception is thrown and no
215 * action is taken.
216 * If <code>listener</code> is null, or was never added for the specified
217 * property, no exception is thrown and no action is taken.
218 *
219 * @param propertyName The name of the property that was listened on.
220 * @param listener The VetoableChangeListener to be removed
221 * @since 1.2
222 */
223 public void removeVetoableChangeListener(
224 String propertyName,
225 VetoableChangeListener listener) {
226 if (listener == null || propertyName == null) {
227 return;
228 }
229 listener = this.map.extract(listener);
230 if (listener != null) {
231 this.map.remove(propertyName, listener);
232 }
233 }
234
235 /**
236 * Returns an array of all the listeners which have been associated
237 * with the named property.
238 *
239 * @param propertyName The name of the property being listened to
240 * @return all the <code>VetoableChangeListeners</code> associated with
241 * the named property. If no such listeners have been added,
278 /**
279 * Reports an integer constrained property update to listeners
280 * that have been registered to track updates of
281 * all properties or a property with the specified name.
282 * <p>
283 * Any listener can throw a {@code PropertyVetoException} to veto the update.
284 * If one of the listeners vetoes the update, this method passes
285 * a new "undo" {@code PropertyChangeEvent} that reverts to the old value
286 * to all listeners that already confirmed this update
287 * and throws the {@code PropertyVetoException} again.
288 * <p>
289 * No event is fired if old and new values are equal.
290 * <p>
291 * This is merely a convenience wrapper around the more general
292 * {@link #fireVetoableChange(String, Object, Object)} method.
293 *
294 * @param propertyName the programmatic name of the property that is about to change
295 * @param oldValue the old value of the property
296 * @param newValue the new value of the property
297 * @throws PropertyVetoException if one of listeners vetoes the property update
298 * @since 1.2
299 */
300 public void fireVetoableChange(String propertyName, int oldValue, int newValue)
301 throws PropertyVetoException {
302 if (oldValue != newValue) {
303 fireVetoableChange(propertyName, Integer.valueOf(oldValue), Integer.valueOf(newValue));
304 }
305 }
306
307 /**
308 * Reports a boolean constrained property update to listeners
309 * that have been registered to track updates of
310 * all properties or a property with the specified name.
311 * <p>
312 * Any listener can throw a {@code PropertyVetoException} to veto the update.
313 * If one of the listeners vetoes the update, this method passes
314 * a new "undo" {@code PropertyChangeEvent} that reverts to the old value
315 * to all listeners that already confirmed this update
316 * and throws the {@code PropertyVetoException} again.
317 * <p>
318 * No event is fired if old and new values are equal.
319 * <p>
320 * This is merely a convenience wrapper around the more general
321 * {@link #fireVetoableChange(String, Object, Object)} method.
322 *
323 * @param propertyName the programmatic name of the property that is about to change
324 * @param oldValue the old value of the property
325 * @param newValue the new value of the property
326 * @throws PropertyVetoException if one of listeners vetoes the property update
327 * @since 1.2
328 */
329 public void fireVetoableChange(String propertyName, boolean oldValue, boolean newValue)
330 throws PropertyVetoException {
331 if (oldValue != newValue) {
332 fireVetoableChange(propertyName, Boolean.valueOf(oldValue), Boolean.valueOf(newValue));
333 }
334 }
335
336 /**
337 * Fires a property change event to listeners
338 * that have been registered to track updates of
339 * all properties or a property with the specified name.
340 * <p>
341 * Any listener can throw a {@code PropertyVetoException} to veto the update.
342 * If one of the listeners vetoes the update, this method passes
343 * a new "undo" {@code PropertyChangeEvent} that reverts to the old value
344 * to all listeners that already confirmed this update
345 * and throws the {@code PropertyVetoException} again.
346 * <p>
347 * No event is fired if the given event's old and new values are equal and non-null.
348 *
349 * @param event the {@code PropertyChangeEvent} to be fired
350 * @throws PropertyVetoException if one of listeners vetoes the property update
351 * @since 1.2
352 */
353 public void fireVetoableChange(PropertyChangeEvent event)
354 throws PropertyVetoException {
355 Object oldValue = event.getOldValue();
356 Object newValue = event.getNewValue();
357 if (oldValue == null || newValue == null || !oldValue.equals(newValue)) {
358 String name = event.getPropertyName();
359
360 VetoableChangeListener[] common = this.map.get(null);
361 VetoableChangeListener[] named = (name != null)
362 ? this.map.get(name)
363 : null;
364
365 VetoableChangeListener[] listeners;
366 if (common == null) {
367 listeners = named;
368 }
369 else if (named == null) {
370 listeners = common;
371 }
388 try {
389 listeners[i].vetoableChange(event);
390 }
391 catch (PropertyVetoException exception) {
392 // ignore exceptions that occur during rolling back
393 }
394 }
395 throw veto; // rethrow the veto exception
396 }
397 }
398 }
399 }
400
401 /**
402 * Check if there are any listeners for a specific property, including
403 * those registered on all properties. If <code>propertyName</code>
404 * is null, only check for listeners registered on all properties.
405 *
406 * @param propertyName the property name.
407 * @return true if there are one or more listeners for the given property
408 * @since 1.2
409 */
410 public boolean hasListeners(String propertyName) {
411 return this.map.hasListeners(propertyName);
412 }
413
414 /**
415 * @serialData Null terminated list of <code>VetoableChangeListeners</code>.
416 * <p>
417 * At serialization time we skip non-serializable listeners and
418 * only serialize the serializable listeners.
419 */
420 private void writeObject(ObjectOutputStream s) throws IOException {
421 Hashtable<String, VetoableChangeSupport> children = null;
422 VetoableChangeListener[] listeners = null;
423 synchronized (this.map) {
424 for (Entry<String, VetoableChangeListener[]> entry : this.map.getEntries()) {
425 String property = entry.getKey();
426 if (property == null) {
427 listeners = entry.getValue();
428 } else {
|