Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
PojavLauncherTeam
GitHub Repository: PojavLauncherTeam/mobile
Path: blob/master/src/java.desktop/share/classes/java/beans/VetoableChangeSupport.java
41152 views
1
/*
2
* Copyright (c) 1996, 2021, 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
26
package java.beans;
27
28
import java.io.IOException;
29
import java.io.ObjectInputStream;
30
import java.io.ObjectOutputStream;
31
import java.io.ObjectStreamField;
32
import java.io.Serial;
33
import java.io.Serializable;
34
import java.util.Hashtable;
35
import java.util.Map.Entry;
36
37
/**
38
* This is a utility class that can be used by beans that support constrained
39
* properties. It manages a list of listeners and dispatches
40
* {@link PropertyChangeEvent}s to them. You can use an instance of this class
41
* as a member field of your bean and delegate these types of work to it.
42
* The {@link VetoableChangeListener} can be registered for all properties
43
* or for a property specified by name.
44
* <p>
45
* Here is an example of {@code VetoableChangeSupport} usage that follows
46
* the rules and recommendations laid out in the JavaBeans specification:
47
* <pre>{@code
48
* public class MyBean {
49
* private final VetoableChangeSupport vcs = new VetoableChangeSupport(this);
50
*
51
* public void addVetoableChangeListener(VetoableChangeListener listener) {
52
* this.vcs.addVetoableChangeListener(listener);
53
* }
54
*
55
* public void removeVetoableChangeListener(VetoableChangeListener listener) {
56
* this.vcs.removeVetoableChangeListener(listener);
57
* }
58
*
59
* private String value;
60
*
61
* public String getValue() {
62
* return this.value;
63
* }
64
*
65
* public void setValue(String newValue) throws PropertyVetoException {
66
* String oldValue = this.value;
67
* this.vcs.fireVetoableChange("value", oldValue, newValue);
68
* this.value = newValue;
69
* }
70
*
71
* [...]
72
* }
73
* }</pre>
74
* <p>
75
* A {@code VetoableChangeSupport} instance is thread-safe.
76
* <p>
77
* This class is serializable. When it is serialized it will save
78
* (and restore) any listeners that are themselves serializable. Any
79
* non-serializable listeners will be skipped during serialization.
80
*
81
* @see PropertyChangeSupport
82
* @since 1.1
83
*/
84
public class VetoableChangeSupport implements Serializable {
85
private VetoableChangeListenerMap map = new VetoableChangeListenerMap();
86
87
/**
88
* Constructs a {@code VetoableChangeSupport} object.
89
*
90
* @param sourceBean The bean to be given as the source for any events.
91
*/
92
public VetoableChangeSupport(Object sourceBean) {
93
if (sourceBean == null) {
94
throw new NullPointerException();
95
}
96
source = sourceBean;
97
}
98
99
/**
100
* Add a VetoableChangeListener to the listener list.
101
* The listener is registered for all properties.
102
* The same listener object may be added more than once, and will be called
103
* as many times as it is added.
104
* If {@code listener} is null, no exception is thrown and no action
105
* is taken.
106
*
107
* @param listener The VetoableChangeListener to be added
108
*/
109
public void addVetoableChangeListener(VetoableChangeListener listener) {
110
if (listener == null) {
111
return;
112
}
113
if (listener instanceof VetoableChangeListenerProxy) {
114
VetoableChangeListenerProxy proxy =
115
(VetoableChangeListenerProxy)listener;
116
// Call two argument add method.
117
addVetoableChangeListener(proxy.getPropertyName(),
118
proxy.getListener());
119
} else {
120
this.map.add(null, listener);
121
}
122
}
123
124
/**
125
* Remove a VetoableChangeListener from the listener list.
126
* This removes a VetoableChangeListener that was registered
127
* for all properties.
128
* If {@code listener} was added more than once to the same event
129
* source, it will be notified one less time after being removed.
130
* If {@code listener} is null, or was never added, no exception is
131
* thrown and no action is taken.
132
*
133
* @param listener The VetoableChangeListener to be removed
134
*/
135
public void removeVetoableChangeListener(VetoableChangeListener listener) {
136
if (listener == null) {
137
return;
138
}
139
if (listener instanceof VetoableChangeListenerProxy) {
140
VetoableChangeListenerProxy proxy =
141
(VetoableChangeListenerProxy)listener;
142
// Call two argument remove method.
143
removeVetoableChangeListener(proxy.getPropertyName(),
144
proxy.getListener());
145
} else {
146
this.map.remove(null, listener);
147
}
148
}
149
150
/**
151
* Returns an array of all the listeners that were added to the
152
* VetoableChangeSupport object with addVetoableChangeListener().
153
* <p>
154
* If some listeners have been added with a named property, then
155
* the returned array will be a mixture of VetoableChangeListeners
156
* and {@code VetoableChangeListenerProxy}s. If the calling
157
* method is interested in distinguishing the listeners then it must
158
* test each element to see if it's a
159
* {@code VetoableChangeListenerProxy}, perform the cast, and examine
160
* the parameter.
161
*
162
* <pre>{@code
163
* VetoableChangeListener[] listeners = bean.getVetoableChangeListeners();
164
* for (int i = 0; i < listeners.length; i++) {
165
* if (listeners[i] instanceof VetoableChangeListenerProxy) {
166
* VetoableChangeListenerProxy proxy =
167
* (VetoableChangeListenerProxy)listeners[i];
168
* if (proxy.getPropertyName().equals("foo")) {
169
* // proxy is a VetoableChangeListener which was associated
170
* // with the property named "foo"
171
* }
172
* }
173
* }
174
* }</pre>
175
*
176
* @see VetoableChangeListenerProxy
177
* @return all of the {@code VetoableChangeListeners} added or an
178
* empty array if no listeners have been added
179
* @since 1.4
180
*/
181
public VetoableChangeListener[] getVetoableChangeListeners(){
182
return this.map.getListeners();
183
}
184
185
/**
186
* Add a VetoableChangeListener for a specific property. The listener
187
* will be invoked only when a call on fireVetoableChange names that
188
* specific property.
189
* The same listener object may be added more than once. For each
190
* property, the listener will be invoked the number of times it was added
191
* for that property.
192
* If {@code propertyName} or {@code listener} is null, no
193
* exception is thrown and no action is taken.
194
*
195
* @param propertyName The name of the property to listen on.
196
* @param listener The VetoableChangeListener to be added
197
* @since 1.2
198
*/
199
public void addVetoableChangeListener(
200
String propertyName,
201
VetoableChangeListener listener) {
202
if (listener == null || propertyName == null) {
203
return;
204
}
205
listener = this.map.extract(listener);
206
if (listener != null) {
207
this.map.add(propertyName, listener);
208
}
209
}
210
211
/**
212
* Remove a VetoableChangeListener for a specific property.
213
* If {@code listener} was added more than once to the same event
214
* source for the specified property, it will be notified one less time
215
* after being removed.
216
* If {@code propertyName} is null, no exception is thrown and no
217
* action is taken.
218
* If {@code listener} is null, or was never added for the specified
219
* property, no exception is thrown and no action is taken.
220
*
221
* @param propertyName The name of the property that was listened on.
222
* @param listener The VetoableChangeListener to be removed
223
* @since 1.2
224
*/
225
public void removeVetoableChangeListener(
226
String propertyName,
227
VetoableChangeListener listener) {
228
if (listener == null || propertyName == null) {
229
return;
230
}
231
listener = this.map.extract(listener);
232
if (listener != null) {
233
this.map.remove(propertyName, listener);
234
}
235
}
236
237
/**
238
* Returns an array of all the listeners which have been associated
239
* with the named property.
240
*
241
* @param propertyName The name of the property being listened to
242
* @return all the {@code VetoableChangeListeners} associated with
243
* the named property. If no such listeners have been added,
244
* or if {@code propertyName} is null, an empty array is
245
* returned.
246
* @since 1.4
247
*/
248
public VetoableChangeListener[] getVetoableChangeListeners(String propertyName) {
249
return this.map.getListeners(propertyName);
250
}
251
252
/**
253
* Reports a constrained property update to listeners
254
* that have been registered to track updates of
255
* all properties or a property with the specified name.
256
* <p>
257
* Any listener can throw a {@code PropertyVetoException} to veto the update.
258
* If one of the listeners vetoes the update, this method passes
259
* a new "undo" {@code PropertyChangeEvent} that reverts to the old value
260
* to all listeners that already confirmed this update
261
* and throws the {@code PropertyVetoException} again.
262
* <p>
263
* No event is fired if old and new values are equal and non-null.
264
* <p>
265
* This is merely a convenience wrapper around the more general
266
* {@link #fireVetoableChange(PropertyChangeEvent)} method.
267
*
268
* @param propertyName the programmatic name of the property that is about to change
269
* @param oldValue the old value of the property
270
* @param newValue the new value of the property
271
* @throws PropertyVetoException if one of listeners vetoes the property update
272
*/
273
public void fireVetoableChange(String propertyName, Object oldValue, Object newValue)
274
throws PropertyVetoException {
275
if (oldValue == null || newValue == null || !oldValue.equals(newValue)) {
276
fireVetoableChange(new PropertyChangeEvent(this.source, propertyName, oldValue, newValue));
277
}
278
}
279
280
/**
281
* Reports an integer constrained property update to listeners
282
* that have been registered to track updates of
283
* all properties or a property with the specified name.
284
* <p>
285
* Any listener can throw a {@code PropertyVetoException} to veto the update.
286
* If one of the listeners vetoes the update, this method passes
287
* a new "undo" {@code PropertyChangeEvent} that reverts to the old value
288
* to all listeners that already confirmed this update
289
* and throws the {@code PropertyVetoException} again.
290
* <p>
291
* No event is fired if old and new values are equal.
292
* <p>
293
* This is merely a convenience wrapper around the more general
294
* {@link #fireVetoableChange(String, Object, Object)} method.
295
*
296
* @param propertyName the programmatic name of the property that is about to change
297
* @param oldValue the old value of the property
298
* @param newValue the new value of the property
299
* @throws PropertyVetoException if one of listeners vetoes the property update
300
* @since 1.2
301
*/
302
public void fireVetoableChange(String propertyName, int oldValue, int newValue)
303
throws PropertyVetoException {
304
if (oldValue != newValue) {
305
fireVetoableChange(propertyName, Integer.valueOf(oldValue), Integer.valueOf(newValue));
306
}
307
}
308
309
/**
310
* Reports a boolean constrained property update to listeners
311
* that have been registered to track updates of
312
* all properties or a property with the specified name.
313
* <p>
314
* Any listener can throw a {@code PropertyVetoException} to veto the update.
315
* If one of the listeners vetoes the update, this method passes
316
* a new "undo" {@code PropertyChangeEvent} that reverts to the old value
317
* to all listeners that already confirmed this update
318
* and throws the {@code PropertyVetoException} again.
319
* <p>
320
* No event is fired if old and new values are equal.
321
* <p>
322
* This is merely a convenience wrapper around the more general
323
* {@link #fireVetoableChange(String, Object, Object)} method.
324
*
325
* @param propertyName the programmatic name of the property that is about to change
326
* @param oldValue the old value of the property
327
* @param newValue the new value of the property
328
* @throws PropertyVetoException if one of listeners vetoes the property update
329
* @since 1.2
330
*/
331
public void fireVetoableChange(String propertyName, boolean oldValue, boolean newValue)
332
throws PropertyVetoException {
333
if (oldValue != newValue) {
334
fireVetoableChange(propertyName, Boolean.valueOf(oldValue), Boolean.valueOf(newValue));
335
}
336
}
337
338
/**
339
* Fires a property change event to listeners
340
* that have been registered to track updates of
341
* all properties or a property with the specified name.
342
* <p>
343
* Any listener can throw a {@code PropertyVetoException} to veto the update.
344
* If one of the listeners vetoes the update, this method passes
345
* a new "undo" {@code PropertyChangeEvent} that reverts to the old value
346
* to all listeners that already confirmed this update
347
* and throws the {@code PropertyVetoException} again.
348
* <p>
349
* No event is fired if the given event's old and new values are equal and non-null.
350
*
351
* @param event the {@code PropertyChangeEvent} to be fired
352
* @throws PropertyVetoException if one of listeners vetoes the property update
353
* @since 1.2
354
*/
355
public void fireVetoableChange(PropertyChangeEvent event)
356
throws PropertyVetoException {
357
Object oldValue = event.getOldValue();
358
Object newValue = event.getNewValue();
359
if (oldValue == null || newValue == null || !oldValue.equals(newValue)) {
360
String name = event.getPropertyName();
361
362
VetoableChangeListener[] common = this.map.get(null);
363
VetoableChangeListener[] named = (name != null)
364
? this.map.get(name)
365
: null;
366
367
VetoableChangeListener[] listeners;
368
if (common == null) {
369
listeners = named;
370
}
371
else if (named == null) {
372
listeners = common;
373
}
374
else {
375
listeners = new VetoableChangeListener[common.length + named.length];
376
System.arraycopy(common, 0, listeners, 0, common.length);
377
System.arraycopy(named, 0, listeners, common.length, named.length);
378
}
379
if (listeners != null) {
380
int current = 0;
381
try {
382
while (current < listeners.length) {
383
listeners[current].vetoableChange(event);
384
current++;
385
}
386
}
387
catch (PropertyVetoException veto) {
388
event = new PropertyChangeEvent(this.source, name, newValue, oldValue);
389
for (int i = 0; i < current; i++) {
390
try {
391
listeners[i].vetoableChange(event);
392
}
393
catch (PropertyVetoException exception) {
394
// ignore exceptions that occur during rolling back
395
}
396
}
397
throw veto; // rethrow the veto exception
398
}
399
}
400
}
401
}
402
403
/**
404
* Check if there are any listeners for a specific property, including
405
* those registered on all properties. If {@code propertyName}
406
* is null, only check for listeners registered on all properties.
407
*
408
* @param propertyName the property name.
409
* @return true if there are one or more listeners for the given property
410
* @since 1.2
411
*/
412
public boolean hasListeners(String propertyName) {
413
return this.map.hasListeners(propertyName);
414
}
415
416
/**
417
* Writes serializable fields to stream.
418
*
419
* @param s the {@code ObjectOutputStream} to write
420
* @throws IOException if an I/O error occurs
421
* @serialData Null terminated list of {@code VetoableChangeListeners}.
422
* <p>
423
* At serialization time we skip non-serializable listeners and
424
* only serialize the serializable listeners.
425
*/
426
@Serial
427
private void writeObject(ObjectOutputStream s) throws IOException {
428
Hashtable<String, VetoableChangeSupport> children = null;
429
VetoableChangeListener[] listeners = null;
430
synchronized (this.map) {
431
for (Entry<String, VetoableChangeListener[]> entry : this.map.getEntries()) {
432
String property = entry.getKey();
433
if (property == null) {
434
listeners = entry.getValue();
435
} else {
436
if (children == null) {
437
children = new Hashtable<>();
438
}
439
VetoableChangeSupport vcs = new VetoableChangeSupport(this.source);
440
vcs.map.set(null, entry.getValue());
441
children.put(property, vcs);
442
}
443
}
444
}
445
ObjectOutputStream.PutField fields = s.putFields();
446
fields.put("children", children);
447
fields.put("source", this.source);
448
fields.put("vetoableChangeSupportSerializedDataVersion", 2);
449
s.writeFields();
450
451
if (listeners != null) {
452
for (VetoableChangeListener l : listeners) {
453
if (l instanceof Serializable) {
454
s.writeObject(l);
455
}
456
}
457
}
458
s.writeObject(null);
459
}
460
461
/**
462
* Reads the {@code ObjectInputStream}.
463
*
464
* @param s the {@code ObjectInputStream} to read
465
* @throws ClassNotFoundException if the class of a serialized object could
466
* not be found
467
* @throws IOException if an I/O error occurs
468
*/
469
@Serial
470
private void readObject(ObjectInputStream s) throws ClassNotFoundException, IOException {
471
this.map = new VetoableChangeListenerMap();
472
473
ObjectInputStream.GetField fields = s.readFields();
474
475
@SuppressWarnings("unchecked")
476
Hashtable<String, VetoableChangeSupport> children = (Hashtable<String, VetoableChangeSupport>)fields.get("children", null);
477
this.source = fields.get("source", null);
478
fields.get("vetoableChangeSupportSerializedDataVersion", 2);
479
480
Object listenerOrNull;
481
while (null != (listenerOrNull = s.readObject())) {
482
this.map.add(null, (VetoableChangeListener)listenerOrNull);
483
}
484
if (children != null) {
485
for (Entry<String, VetoableChangeSupport> entry : children.entrySet()) {
486
for (VetoableChangeListener listener : entry.getValue().getVetoableChangeListeners()) {
487
this.map.add(entry.getKey(), listener);
488
}
489
}
490
}
491
}
492
493
/**
494
* The object to be provided as the "source" for any generated events.
495
*/
496
private Object source;
497
498
/**
499
* @serialField children Hashtable
500
* The list of {@code PropertyChangeListeners}
501
* @serialField source Object
502
* The object to be provided as the "source" for any generated
503
* events
504
* @serialField vetoableChangeSupportSerializedDataVersion int
505
* The version
506
*/
507
@Serial
508
private static final ObjectStreamField[] serialPersistentFields = {
509
new ObjectStreamField("children", Hashtable.class),
510
new ObjectStreamField("source", Object.class),
511
new ObjectStreamField("vetoableChangeSupportSerializedDataVersion", Integer.TYPE)
512
};
513
514
/**
515
* Use serialVersionUID from JDK 1.1 for interoperability.
516
*/
517
@Serial
518
private static final long serialVersionUID = -5090210921595982017L;
519
520
/**
521
* This is a {@link ChangeListenerMap ChangeListenerMap} implementation
522
* that works with {@link VetoableChangeListener VetoableChangeListener} objects.
523
*/
524
private static final class VetoableChangeListenerMap extends ChangeListenerMap<VetoableChangeListener> {
525
private static final VetoableChangeListener[] EMPTY = {};
526
527
/**
528
* Creates an array of {@link VetoableChangeListener VetoableChangeListener} objects.
529
* This method uses the same instance of the empty array
530
* when {@code length} equals {@code 0}.
531
*
532
* @param length the array length
533
* @return an array with specified length
534
*/
535
@Override
536
protected VetoableChangeListener[] newArray(int length) {
537
return (0 < length)
538
? new VetoableChangeListener[length]
539
: EMPTY;
540
}
541
542
/**
543
* Creates a {@link VetoableChangeListenerProxy VetoableChangeListenerProxy}
544
* object for the specified property.
545
*
546
* @param name the name of the property to listen on
547
* @param listener the listener to process events
548
* @return a {@code VetoableChangeListenerProxy} object
549
*/
550
@Override
551
protected VetoableChangeListener newProxy(String name, VetoableChangeListener listener) {
552
return new VetoableChangeListenerProxy(name, listener);
553
}
554
555
/**
556
* {@inheritDoc}
557
*/
558
public VetoableChangeListener extract(VetoableChangeListener listener) {
559
while (listener instanceof VetoableChangeListenerProxy) {
560
listener = ((VetoableChangeListenerProxy) listener).getListener();
561
}
562
return listener;
563
}
564
}
565
}
566
567