Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
PojavLauncherTeam
GitHub Repository: PojavLauncherTeam/openjdk-multiarch-jdk8u
Path: blob/aarch64-shenandoah-jdk8u272-b10/jdk/src/share/classes/java/beans/VetoableChangeSupport.java
38829 views
1
/*
2
* Copyright (c) 1996, 2013, Oracle and/or its affiliates. All rights reserved.
3
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4
*
5
* This code is free software; you can redistribute it and/or modify it
6
* under the terms of the GNU General Public License version 2 only, as
7
* published by the Free Software Foundation. Oracle designates this
8
* particular file as subject to the "Classpath" exception as provided
9
* by Oracle in the LICENSE file that accompanied this code.
10
*
11
* This code is distributed in the hope that it will be useful, but WITHOUT
12
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14
* version 2 for more details (a copy is included in the LICENSE file that
15
* accompanied this code).
16
*
17
* You should have received a copy of the GNU General Public License version
18
* 2 along with this work; if not, write to the Free Software Foundation,
19
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20
*
21
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22
* or visit www.oracle.com if you need additional information or have any
23
* questions.
24
*/
25
package java.beans;
26
27
import java.io.Serializable;
28
import java.io.ObjectStreamField;
29
import java.io.ObjectOutputStream;
30
import java.io.ObjectInputStream;
31
import java.io.IOException;
32
import java.util.Hashtable;
33
import java.util.Map.Entry;
34
35
/**
36
* This is a utility class that can be used by beans that support constrained
37
* properties. It manages a list of listeners and dispatches
38
* {@link PropertyChangeEvent}s to them. You can use an instance of this class
39
* as a member field of your bean and delegate these types of work to it.
40
* The {@link VetoableChangeListener} can be registered for all properties
41
* or for a property specified by name.
42
* <p>
43
* Here is an example of {@code VetoableChangeSupport} usage that follows
44
* the rules and recommendations laid out in the JavaBeans&trade; specification:
45
* <pre>{@code
46
* public class MyBean {
47
* private final VetoableChangeSupport vcs = new VetoableChangeSupport(this);
48
*
49
* public void addVetoableChangeListener(VetoableChangeListener listener) {
50
* this.vcs.addVetoableChangeListener(listener);
51
* }
52
*
53
* public void removeVetoableChangeListener(VetoableChangeListener listener) {
54
* this.vcs.removeVetoableChangeListener(listener);
55
* }
56
*
57
* private String value;
58
*
59
* public String getValue() {
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
100
* as many times as it is added.
101
* If <code>listener</code> is null, no exception is thrown and no action
102
* is taken.
103
*
104
* @param listener The VetoableChangeListener to be added
105
*/
106
public void addVetoableChangeListener(VetoableChangeListener listener) {
107
if (listener == null) {
108
return;
109
}
110
if (listener instanceof VetoableChangeListenerProxy) {
111
VetoableChangeListenerProxy proxy =
112
(VetoableChangeListenerProxy)listener;
113
// Call two argument add method.
114
addVetoableChangeListener(proxy.getPropertyName(),
115
proxy.getListener());
116
} else {
117
this.map.add(null, listener);
118
}
119
}
120
121
/**
122
* Remove a VetoableChangeListener from the listener list.
123
* This removes a VetoableChangeListener that was registered
124
* for all properties.
125
* If <code>listener</code> was added more than once to the same event
126
* source, it will be notified one less time after being removed.
127
* If <code>listener</code> is null, or was never added, no exception is
128
* thrown and no action is taken.
129
*
130
* @param listener The VetoableChangeListener to be removed
131
*/
132
public void removeVetoableChangeListener(VetoableChangeListener listener) {
133
if (listener == null) {
134
return;
135
}
136
if (listener instanceof VetoableChangeListenerProxy) {
137
VetoableChangeListenerProxy proxy =
138
(VetoableChangeListenerProxy)listener;
139
// Call two argument remove method.
140
removeVetoableChangeListener(proxy.getPropertyName(),
141
proxy.getListener());
142
} else {
143
this.map.remove(null, listener);
144
}
145
}
146
147
/**
148
* Returns an array of all the listeners that were added to the
149
* VetoableChangeSupport object with addVetoableChangeListener().
150
* <p>
151
* If some listeners have been added with a named property, then
152
* the returned array will be a mixture of VetoableChangeListeners
153
* and <code>VetoableChangeListenerProxy</code>s. If the calling
154
* method is interested in distinguishing the listeners then it must
155
* test each element to see if it's a
156
* <code>VetoableChangeListenerProxy</code>, perform the cast, and examine
157
* the parameter.
158
*
159
* <pre>{@code
160
* VetoableChangeListener[] listeners = bean.getVetoableChangeListeners();
161
* for (int i = 0; i < listeners.length; i++) {
162
* if (listeners[i] instanceof VetoableChangeListenerProxy) {
163
* VetoableChangeListenerProxy proxy =
164
* (VetoableChangeListenerProxy)listeners[i];
165
* if (proxy.getPropertyName().equals("foo")) {
166
* // proxy is a VetoableChangeListener which was associated
167
* // with the property named "foo"
168
* }
169
* }
170
* }
171
* }</pre>
172
*
173
* @see VetoableChangeListenerProxy
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,
239
* or if <code>propertyName</code> is null, an empty array is
240
* returned.
241
* @since 1.4
242
*/
243
public VetoableChangeListener[] getVetoableChangeListeners(String propertyName) {
244
return this.map.getListeners(propertyName);
245
}
246
247
/**
248
* Reports a constrained property update to listeners
249
* that have been registered to track updates of
250
* all properties or a property with the specified name.
251
* <p>
252
* Any listener can throw a {@code PropertyVetoException} to veto the update.
253
* If one of the listeners vetoes the update, this method passes
254
* a new "undo" {@code PropertyChangeEvent} that reverts to the old value
255
* to all listeners that already confirmed this update
256
* and throws the {@code PropertyVetoException} again.
257
* <p>
258
* No event is fired if old and new values are equal and non-null.
259
* <p>
260
* This is merely a convenience wrapper around the more general
261
* {@link #fireVetoableChange(PropertyChangeEvent)} method.
262
*
263
* @param propertyName the programmatic name of the property that is about to change
264
* @param oldValue the old value of the property
265
* @param newValue the new value of the property
266
* @throws PropertyVetoException if one of listeners vetoes the property update
267
*/
268
public void fireVetoableChange(String propertyName, Object oldValue, Object newValue)
269
throws PropertyVetoException {
270
if (oldValue == null || newValue == null || !oldValue.equals(newValue)) {
271
fireVetoableChange(new PropertyChangeEvent(this.source, propertyName, oldValue, newValue));
272
}
273
}
274
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
}
366
else {
367
listeners = new VetoableChangeListener[common.length + named.length];
368
System.arraycopy(common, 0, listeners, 0, common.length);
369
System.arraycopy(named, 0, listeners, common.length, named.length);
370
}
371
if (listeners != null) {
372
int current = 0;
373
try {
374
while (current < listeners.length) {
375
listeners[current].vetoableChange(event);
376
current++;
377
}
378
}
379
catch (PropertyVetoException veto) {
380
event = new PropertyChangeEvent(this.source, name, newValue, oldValue);
381
for (int i = 0; i < current; i++) {
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 {
422
if (children == null) {
423
children = new Hashtable<>();
424
}
425
VetoableChangeSupport vcs = new VetoableChangeSupport(this.source);
426
vcs.map.set(null, entry.getValue());
427
children.put(property, vcs);
428
}
429
}
430
}
431
ObjectOutputStream.PutField fields = s.putFields();
432
fields.put("children", children);
433
fields.put("source", this.source);
434
fields.put("vetoableChangeSupportSerializedDataVersion", 2);
435
s.writeFields();
436
437
if (listeners != null) {
438
for (VetoableChangeListener l : listeners) {
439
if (l instanceof Serializable) {
440
s.writeObject(l);
441
}
442
}
443
}
444
s.writeObject(null);
445
}
446
447
private void readObject(ObjectInputStream s) throws ClassNotFoundException, IOException {
448
this.map = new VetoableChangeListenerMap();
449
450
ObjectInputStream.GetField fields = s.readFields();
451
452
@SuppressWarnings("unchecked")
453
Hashtable<String, VetoableChangeSupport> children = (Hashtable<String, VetoableChangeSupport>)fields.get("children", null);
454
this.source = fields.get("source", null);
455
fields.get("vetoableChangeSupportSerializedDataVersion", 2);
456
457
Object listenerOrNull;
458
while (null != (listenerOrNull = s.readObject())) {
459
this.map.add(null, (VetoableChangeListener)listenerOrNull);
460
}
461
if (children != null) {
462
for (Entry<String, VetoableChangeSupport> entry : children.entrySet()) {
463
for (VetoableChangeListener listener : entry.getValue().getVetoableChangeListeners()) {
464
this.map.add(entry.getKey(), listener);
465
}
466
}
467
}
468
}
469
470
/**
471
* The object to be provided as the "source" for any generated events.
472
*/
473
private Object source;
474
475
/**
476
* @serialField children Hashtable
477
* @serialField source Object
478
* @serialField vetoableChangeSupportSerializedDataVersion int
479
*/
480
private static final ObjectStreamField[] serialPersistentFields = {
481
new ObjectStreamField("children", Hashtable.class),
482
new ObjectStreamField("source", Object.class),
483
new ObjectStreamField("vetoableChangeSupportSerializedDataVersion", Integer.TYPE)
484
};
485
486
/**
487
* Serialization version ID, so we're compatible with JDK 1.1
488
*/
489
static final long serialVersionUID = -5090210921595982017L;
490
491
/**
492
* This is a {@link ChangeListenerMap ChangeListenerMap} implementation
493
* that works with {@link VetoableChangeListener VetoableChangeListener} objects.
494
*/
495
private static final class VetoableChangeListenerMap extends ChangeListenerMap<VetoableChangeListener> {
496
private static final VetoableChangeListener[] EMPTY = {};
497
498
/**
499
* Creates an array of {@link VetoableChangeListener VetoableChangeListener} objects.
500
* This method uses the same instance of the empty array
501
* when {@code length} equals {@code 0}.
502
*
503
* @param length the array length
504
* @return an array with specified length
505
*/
506
@Override
507
protected VetoableChangeListener[] newArray(int length) {
508
return (0 < length)
509
? new VetoableChangeListener[length]
510
: EMPTY;
511
}
512
513
/**
514
* Creates a {@link VetoableChangeListenerProxy VetoableChangeListenerProxy}
515
* object for the specified property.
516
*
517
* @param name the name of the property to listen on
518
* @param listener the listener to process events
519
* @return a {@code VetoableChangeListenerProxy} object
520
*/
521
@Override
522
protected VetoableChangeListener newProxy(String name, VetoableChangeListener listener) {
523
return new VetoableChangeListenerProxy(name, listener);
524
}
525
526
/**
527
* {@inheritDoc}
528
*/
529
public final VetoableChangeListener extract(VetoableChangeListener listener) {
530
while (listener instanceof VetoableChangeListenerProxy) {
531
listener = ((VetoableChangeListenerProxy) listener).getListener();
532
}
533
return listener;
534
}
535
}
536
}
537
538