1. /*
  2. * @(#)VetoableChangeSupport.java 1.41 03/01/23
  3. *
  4. * Copyright 2003 Sun Microsystems, Inc. All rights reserved.
  5. * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
  6. */
  7. package java.beans;
  8. import java.io.Serializable;
  9. import java.io.ObjectOutputStream;
  10. import java.io.ObjectInputStream;
  11. import java.io.IOException;
  12. import java.util.Arrays;
  13. import java.util.ArrayList;
  14. import java.util.Iterator;
  15. import java.util.List;
  16. /**
  17. * This is a utility class that can be used by beans that support constrained
  18. * properties. You can use an instance of this class as a member field
  19. * of your bean and delegate various work to it.
  20. *
  21. * This class is serializable. When it is serialized it will save
  22. * (and restore) any listeners that are themselves serializable. Any
  23. * non-serializable listeners will be skipped during serialization.
  24. */
  25. public class VetoableChangeSupport implements java.io.Serializable {
  26. /**
  27. * Constructs a <code>VetoableChangeSupport</code> object.
  28. *
  29. * @param sourceBean The bean to be given as the source for any events.
  30. */
  31. public VetoableChangeSupport(Object sourceBean) {
  32. if (sourceBean == null) {
  33. throw new NullPointerException();
  34. }
  35. source = sourceBean;
  36. }
  37. /**
  38. * Add a VetoableListener to the listener list.
  39. * The listener is registered for all properties.
  40. *
  41. * @param listener The VetoableChangeListener to be added
  42. */
  43. public synchronized void addVetoableChangeListener(
  44. VetoableChangeListener listener) {
  45. if (listener instanceof VetoableChangeListenerProxy) {
  46. VetoableChangeListenerProxy proxy =
  47. (VetoableChangeListenerProxy)listener;
  48. // Call two argument add method.
  49. addVetoableChangeListener(proxy.getPropertyName(),
  50. (VetoableChangeListener)proxy.getListener());
  51. } else {
  52. if (listeners == null) {
  53. listeners = new java.util.Vector();
  54. }
  55. }
  56. listeners.addElement(listener);
  57. }
  58. /**
  59. * Remove a VetoableChangeListener from the listener list.
  60. * This removes a VetoableChangeListener that was registered
  61. * for all properties.
  62. *
  63. * @param listener The VetoableChangeListener to be removed
  64. */
  65. public synchronized void removeVetoableChangeListener(
  66. VetoableChangeListener listener) {
  67. if (listener instanceof VetoableChangeListenerProxy) {
  68. VetoableChangeListenerProxy proxy =
  69. (VetoableChangeListenerProxy)listener;
  70. // Call two argument remove method.
  71. removeVetoableChangeListener(proxy.getPropertyName(),
  72. (VetoableChangeListener)proxy.getListener());
  73. } else {
  74. if (listeners == null) {
  75. return;
  76. }
  77. listeners.removeElement(listener);
  78. }
  79. }
  80. /**
  81. * Returns the list of VetoableChangeListeners. If named vetoable change listeners
  82. * were added, then VetoableChangeListenerProxy wrappers will returned
  83. * <p>
  84. * @return List of VetoableChangeListeners and VetoableChangeListenerProxys
  85. * if named property change listeners were added.
  86. * @since 1.4
  87. */
  88. public synchronized VetoableChangeListener[] getVetoableChangeListeners(){
  89. List returnList = new ArrayList();
  90. // Add all the VetoableChangeListeners
  91. if (listeners != null) {
  92. returnList.addAll(listeners);
  93. }
  94. // Add all the VetoableChangeListenerProxys
  95. if (children != null) {
  96. Iterator iterator = children.keySet().iterator();
  97. while (iterator.hasNext()) {
  98. String key = (String)iterator.next();
  99. VetoableChangeSupport child =
  100. (VetoableChangeSupport)children.get(key);
  101. VetoableChangeListener[] childListeners =
  102. child.getVetoableChangeListeners();
  103. for (int index = childListeners.length - 1; index >= 0;
  104. index--) {
  105. returnList.add(new VetoableChangeListenerProxy(
  106. key, childListeners[index]));
  107. }
  108. }
  109. }
  110. return (VetoableChangeListener[])(returnList.toArray(
  111. new VetoableChangeListener[0]));
  112. }
  113. /**
  114. * Add a VetoableChangeListener for a specific property. The listener
  115. * will be invoked only when a call on fireVetoableChange names that
  116. * specific property.
  117. *
  118. * @param propertyName The name of the property to listen on.
  119. * @param listener The VetoableChangeListener to be added
  120. */
  121. public synchronized void addVetoableChangeListener(
  122. String propertyName,
  123. VetoableChangeListener listener) {
  124. if (children == null) {
  125. children = new java.util.Hashtable();
  126. }
  127. VetoableChangeSupport child = (VetoableChangeSupport)children.get(propertyName);
  128. if (child == null) {
  129. child = new VetoableChangeSupport(source);
  130. children.put(propertyName, child);
  131. }
  132. child.addVetoableChangeListener(listener);
  133. }
  134. /**
  135. * Remove a VetoableChangeListener for a specific property.
  136. *
  137. * @param propertyName The name of the property that was listened on.
  138. * @param listener The VetoableChangeListener to be removed
  139. */
  140. public synchronized void removeVetoableChangeListener(
  141. String propertyName,
  142. VetoableChangeListener listener) {
  143. if (children == null) {
  144. return;
  145. }
  146. VetoableChangeSupport child = (VetoableChangeSupport)children.get(propertyName);
  147. if (child == null) {
  148. return;
  149. }
  150. child.removeVetoableChangeListener(listener);
  151. }
  152. /**
  153. * Returns an array of all the listeners which have been associated
  154. * with the named property.
  155. *
  156. * @return all the <code>VetoableChangeListeners</code> associated with
  157. * the named property or an empty array if no listeners have
  158. * been added.
  159. */
  160. public synchronized VetoableChangeListener[] getVetoableChangeListeners(
  161. String propertyName) {
  162. List returnList = new ArrayList();
  163. if (children != null) {
  164. VetoableChangeSupport support =
  165. (VetoableChangeSupport)children.get(propertyName);
  166. if (support != null) {
  167. returnList.addAll(
  168. Arrays.asList(support.getVetoableChangeListeners()));
  169. }
  170. }
  171. return (VetoableChangeListener[])(returnList.toArray(new
  172. VetoableChangeListener[0]));
  173. }
  174. /**
  175. * Report a vetoable property update to any registered listeners. If
  176. * anyone vetos the change, then fire a new event reverting everyone to
  177. * the old value and then rethrow the PropertyVetoException.
  178. * <p>
  179. * No event is fired if old and new are equal and non-null.
  180. *
  181. * @param propertyName The programmatic name of the property
  182. * that is about to change..
  183. * @param oldValue The old value of the property.
  184. * @param newValue The new value of the property.
  185. * @exception PropertyVetoException if the recipient wishes the property
  186. * change to be rolled back.
  187. */
  188. public void fireVetoableChange(String propertyName,
  189. Object oldValue, Object newValue)
  190. throws PropertyVetoException {
  191. if (listeners == null && children == null) {
  192. return;
  193. }
  194. PropertyChangeEvent evt = new PropertyChangeEvent(source, propertyName,
  195. oldValue, newValue);
  196. fireVetoableChange(evt);
  197. }
  198. /**
  199. * Report a int vetoable property update to any registered listeners.
  200. * No event is fired if old and new are equal and non-null.
  201. * <p>
  202. * This is merely a convenience wrapper around the more general
  203. * fireVetoableChange method that takes Object values.
  204. *
  205. * @param propertyName The programmatic name of the property
  206. * that is about to change.
  207. * @param oldValue The old value of the property.
  208. * @param newValue The new value of the property.
  209. */
  210. public void fireVetoableChange(String propertyName,
  211. int oldValue, int newValue)
  212. throws PropertyVetoException {
  213. if (oldValue == newValue) {
  214. return;
  215. }
  216. fireVetoableChange(propertyName, new Integer(oldValue), new Integer(newValue));
  217. }
  218. /**
  219. * Report a boolean vetoable property update to any registered listeners.
  220. * No event is fired if old and new are equal and non-null.
  221. * <p>
  222. * This is merely a convenience wrapper around the more general
  223. * fireVetoableChange method that takes Object values.
  224. *
  225. * @param propertyName The programmatic name of the property
  226. * that is about to change.
  227. * @param oldValue The old value of the property.
  228. * @param newValue The new value of the property.
  229. */
  230. public void fireVetoableChange(String propertyName,
  231. boolean oldValue, boolean newValue)
  232. throws PropertyVetoException {
  233. if (oldValue == newValue) {
  234. return;
  235. }
  236. fireVetoableChange(propertyName, Boolean.valueOf(oldValue), Boolean.valueOf(newValue));
  237. }
  238. /**
  239. * Fire a vetoable property update to any registered listeners. If
  240. * anyone vetos the change, then fire a new event reverting everyone to
  241. * the old value and then rethrow the PropertyVetoException.
  242. * <p>
  243. * No event is fired if old and new are equal and non-null.
  244. *
  245. * @param evt The PropertyChangeEvent to be fired.
  246. * @exception PropertyVetoException if the recipient wishes the property
  247. * change to be rolled back.
  248. */
  249. public void fireVetoableChange(PropertyChangeEvent evt)
  250. throws PropertyVetoException {
  251. Object oldValue = evt.getOldValue();
  252. Object newValue = evt.getNewValue();
  253. String propertyName = evt.getPropertyName();
  254. if (oldValue != null && newValue != null && oldValue.equals(newValue)) {
  255. return;
  256. }
  257. java.util.Vector targets = null;
  258. VetoableChangeSupport child = null;
  259. synchronized (this) {
  260. if (listeners != null) {
  261. targets = (java.util.Vector) listeners.clone();
  262. }
  263. if (children != null && propertyName != null) {
  264. child = (VetoableChangeSupport)children.get(propertyName);
  265. }
  266. }
  267. if (listeners != null) {
  268. try {
  269. for (int i = 0; i < targets.size(); i++) {
  270. VetoableChangeListener target =
  271. (VetoableChangeListener)targets.elementAt(i);
  272. target.vetoableChange(evt);
  273. }
  274. } catch (PropertyVetoException veto) {
  275. // Create an event to revert everyone to the old value.
  276. evt = new PropertyChangeEvent(source, propertyName, newValue, oldValue);
  277. for (int i = 0; i < targets.size(); i++) {
  278. try {
  279. VetoableChangeListener target =
  280. (VetoableChangeListener)targets.elementAt(i);
  281. target.vetoableChange(evt);
  282. } catch (PropertyVetoException ex) {
  283. // We just ignore exceptions that occur during reversions.
  284. }
  285. }
  286. // And now rethrow the PropertyVetoException.
  287. throw veto;
  288. }
  289. }
  290. if (child != null) {
  291. child.fireVetoableChange(evt);
  292. }
  293. }
  294. /**
  295. * Check if there are any listeners for a specific property.
  296. *
  297. * @param propertyName the property name.
  298. * @return true if there are one or more listeners for the given property
  299. */
  300. public synchronized boolean hasListeners(String propertyName) {
  301. if (listeners != null && !listeners.isEmpty()) {
  302. // there is a generic listener
  303. return true;
  304. }
  305. if (children != null) {
  306. VetoableChangeSupport child = (VetoableChangeSupport)children.get(propertyName);
  307. if (child != null && child.listeners != null) {
  308. return !child.listeners.isEmpty();
  309. }
  310. }
  311. return false;
  312. }
  313. /**
  314. * @serialData Null terminated list of <code>VetoableChangeListeners</code>.
  315. * <p>
  316. * At serialization time we skip non-serializable listeners and
  317. * only serialize the serializable listeners.
  318. *
  319. */
  320. private void writeObject(ObjectOutputStream s) throws IOException {
  321. s.defaultWriteObject();
  322. java.util.Vector v = null;
  323. synchronized (this) {
  324. if (listeners != null) {
  325. v = (java.util.Vector) listeners.clone();
  326. }
  327. }
  328. if (v != null) {
  329. for(int i = 0; i < v.size(); i++) {
  330. VetoableChangeListener l = (VetoableChangeListener)v.elementAt(i);
  331. if (l instanceof Serializable) {
  332. s.writeObject(l);
  333. }
  334. }
  335. }
  336. s.writeObject(null);
  337. }
  338. private void readObject(ObjectInputStream s) throws ClassNotFoundException, IOException {
  339. s.defaultReadObject();
  340. Object listenerOrNull;
  341. while(null != (listenerOrNull = s.readObject())) {
  342. addVetoableChangeListener((VetoableChangeListener)listenerOrNull);
  343. }
  344. }
  345. /**
  346. * "listeners" lists all the generic listeners.
  347. *
  348. * This is transient - its state is written in the writeObject method.
  349. */
  350. transient private java.util.Vector listeners;
  351. /**
  352. * Hashtable for managing listeners for specific properties.
  353. * Maps property names to VetoableChangeSupport objects.
  354. * @serial
  355. * @since 1.2
  356. */
  357. private java.util.Hashtable children;
  358. /**
  359. * The object to be provided as the "source" for any generated events.
  360. * @serial
  361. */
  362. private Object source;
  363. /**
  364. * Internal version number
  365. * @serial
  366. */
  367. private int vetoableChangeSupportSerializedDataVersion = 2;
  368. /**
  369. * Serialization version ID, so we're compatible with JDK 1.1
  370. */
  371. static final long serialVersionUID = -5090210921595982017L;
  372. }