History

My first initial commit with all of mine experimental changes.

Dmitry Kabak (userdima committed 13 years ago

Commit d37b88d1 parent 3f5b46618352 files · +2598093 −-8690Browse files

Showing the changes to java/pkg/java/beans/VetoableChangeSupport.java (1 of 18352 files). Show all

Unified · Split

java/pkg/java/beans/VetoableChangeSupport.java+525−0
@@ -0,0 +1,525 @@
1+/*
2+ * Copyright (c) 1996, 2011, 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>
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>
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<String, VetoableChangeSupport>();
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+ Hashtable<String, VetoableChangeSupport> children = (Hashtable<String, VetoableChangeSupport>) fields.get("children", null);
453+ this.source = fields.get("source", null);
454+ fields.get("vetoableChangeSupportSerializedDataVersion", 2);
455+
456+ Object listenerOrNull;
457+ while (null != (listenerOrNull = s.readObject())) {
458+ this.map.add(null, (VetoableChangeListener)listenerOrNull);
459+ }
460+ if (children != null) {
461+ for (Entry<String, VetoableChangeSupport> entry : children.entrySet()) {
462+ for (VetoableChangeListener listener : entry.getValue().getVetoableChangeListeners()) {
463+ this.map.add(entry.getKey(), listener);
464+ }
465+ }
466+ }
467+ }
468+
469+ /**
470+ * The object to be provided as the "source" for any generated events.
471+ */
472+ private Object source;
473+
474+ /**
475+ * @serialField children Hashtable
476+ * @serialField source Object
477+ * @serialField vetoableChangeSupportSerializedDataVersion int
478+ */
479+ private static final ObjectStreamField[] serialPersistentFields = {
480+ new ObjectStreamField("children", Hashtable.class),
481+ new ObjectStreamField("source", Object.class),
482+ new ObjectStreamField("vetoableChangeSupportSerializedDataVersion", Integer.TYPE)
483+ };
484+
485+ /**
486+ * Serialization version ID, so we're compatible with JDK 1.1
487+ */
488+ static final long serialVersionUID = -5090210921595982017L;
489+
490+ /**
491+ * This is a {@link ChangeListenerMap ChangeListenerMap} implementation
492+ * that works with {@link VetoableChangeListener VetoableChangeListener} objects.
493+ */
494+ private static final class VetoableChangeListenerMap extends ChangeListenerMap<VetoableChangeListener> {
495+ private static final VetoableChangeListener[] EMPTY = {};
496+
497+ /**
498+ * Creates an array of {@link VetoableChangeListener VetoableChangeListener} objects.
499+ * This method uses the same instance of the empty array
500+ * when {@code length} equals {@code 0}.
501+ *
502+ * @param length the array length
503+ * @return an array with specified length
504+ */
505+ @Override
506+ protected VetoableChangeListener[] newArray(int length) {
507+ return (0 < length)
508+ ? new VetoableChangeListener[length]
509+ : EMPTY;
510+ }
511+
512+ /**
513+ * Creates a {@link VetoableChangeListenerProxy VetoableChangeListenerProxy}
514+ * object for the specified property.
515+ *
516+ * @param name the name of the property to listen on
517+ * @param listener the listener to process events
518+ * @return a {@code VetoableChangeListenerProxy} object
519+ */
520+ @Override
521+ protected VetoableChangeListener newProxy(String name, VetoableChangeListener listener) {
522+ return new VetoableChangeListenerProxy(name, listener);
523+ }
524+ }
525+}