1. /*
  2. * @(#)VetoableChangeSupport.java 1.33 01/11/29
  3. *
  4. * Copyright 2002 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. /**
  13. * This is a utility class that can be used by beans that support constrained
  14. * properties. You can use an instance of this class as a member field
  15. * of your bean and delegate various work to it.
  16. *
  17. * This class is serializable. When it is serialized it will save
  18. * (and restore) any listeners that are themselves serializable. Any
  19. * non-serializable listeners will be skipped during serialization.
  20. */
  21. public class VetoableChangeSupport implements java.io.Serializable {
  22. /**
  23. * Constructs a <code>VetoableChangeSupport</code> object.
  24. *
  25. * @param sourceBean The bean to be given as the source for any events.
  26. */
  27. public VetoableChangeSupport(Object sourceBean) {
  28. if (sourceBean == null) {
  29. throw new NullPointerException();
  30. }
  31. source = sourceBean;
  32. }
  33. /**
  34. * Add a VetoableListener to the listener list.
  35. * The listener is registered for all properties.
  36. *
  37. * @param listener The VetoableChangeListener to be added
  38. */
  39. public synchronized void addVetoableChangeListener(
  40. VetoableChangeListener listener) {
  41. if (listeners == null) {
  42. listeners = new java.util.Vector();
  43. }
  44. listeners.addElement(listener);
  45. }
  46. /**
  47. * Remove a VetoableChangeListener from the listener list.
  48. * This removes a PropertyChangeListener that was registered
  49. * for all properties.
  50. *
  51. * @param listener The VetoableChangeListener to be removed
  52. */
  53. public synchronized void removeVetoableChangeListener(
  54. VetoableChangeListener listener) {
  55. if (listeners == null) {
  56. return;
  57. }
  58. listeners.removeElement(listener);
  59. }
  60. /**
  61. * Add a VetoableChangeListener for a specific property. The listener
  62. * will be invoked only when a call on fireVetoableChange names that
  63. * specific property.
  64. *
  65. * @param propertyName The name of the property to listen on.
  66. * @param listener The VetoableChangeListener to be added
  67. */
  68. public synchronized void addVetoableChangeListener(
  69. String propertyName,
  70. VetoableChangeListener listener) {
  71. if (children == null) {
  72. children = new java.util.Hashtable();
  73. }
  74. VetoableChangeSupport child = (VetoableChangeSupport)children.get(propertyName);
  75. if (child == null) {
  76. child = new VetoableChangeSupport(source);
  77. children.put(propertyName, child);
  78. }
  79. child.addVetoableChangeListener(listener);
  80. }
  81. /**
  82. * Remove a VetoableChangeListener for a specific property.
  83. *
  84. * @param propertyName The name of the property that was listened on.
  85. * @param listener The VetoableChangeListener to be removed
  86. */
  87. public synchronized void removeVetoableChangeListener(
  88. String propertyName,
  89. VetoableChangeListener listener) {
  90. if (children == null) {
  91. return;
  92. }
  93. VetoableChangeSupport child = (VetoableChangeSupport)children.get(propertyName);
  94. if (child == null) {
  95. return;
  96. }
  97. child.removeVetoableChangeListener(listener);
  98. }
  99. /**
  100. * Report a vetoable property update to any registered listeners. If
  101. * anyone vetos the change, then fire a new event reverting everyone to
  102. * the old value and then rethrow the PropertyVetoException.
  103. * <p>
  104. * No event is fired if old and new are equal and non-null.
  105. *
  106. * @param propertyName The programmatic name of the property
  107. * that is about to change..
  108. * @param oldValue The old value of the property.
  109. * @param newValue The new value of the property.
  110. * @exception PropertyVetoException if the recipient wishes the property
  111. * change to be rolled back.
  112. */
  113. public void fireVetoableChange(String propertyName,
  114. Object oldValue, Object newValue)
  115. throws PropertyVetoException {
  116. if (listeners == null && children == null) {
  117. return;
  118. }
  119. PropertyChangeEvent evt = new PropertyChangeEvent(source, propertyName,
  120. oldValue, newValue);
  121. fireVetoableChange(evt);
  122. }
  123. /**
  124. * Report a int vetoable property update to any registered listeners.
  125. * No event is fired if old and new are equal and non-null.
  126. * <p>
  127. * This is merely a convenience wrapper around the more general
  128. * fireVetoableChange method that takes Object values.
  129. *
  130. * @param propertyName The programmatic name of the property
  131. * that is about to change.
  132. * @param oldValue The old value of the property.
  133. * @param newValue The new value of the property.
  134. */
  135. public void fireVetoableChange(String propertyName,
  136. int oldValue, int newValue)
  137. throws PropertyVetoException {
  138. if (oldValue == newValue) {
  139. return;
  140. }
  141. fireVetoableChange(propertyName, new Integer(oldValue), new Integer(newValue));
  142. }
  143. /**
  144. * Report a boolean vetoable property update to any registered listeners.
  145. * No event is fired if old and new are equal and non-null.
  146. * <p>
  147. * This is merely a convenience wrapper around the more general
  148. * fireVetoableChange method that takes Object values.
  149. *
  150. * @param propertyName The programmatic name of the property
  151. * that is about to change.
  152. * @param oldValue The old value of the property.
  153. * @param newValue The new value of the property.
  154. */
  155. public void fireVetoableChange(String propertyName,
  156. boolean oldValue, boolean newValue)
  157. throws PropertyVetoException {
  158. if (oldValue == newValue) {
  159. return;
  160. }
  161. fireVetoableChange(propertyName, new Boolean(oldValue), new Boolean(newValue));
  162. }
  163. /**
  164. * Fire a vetoable property update to any registered listeners. If
  165. * anyone vetos the change, then fire a new event reverting everyone to
  166. * the old value and then rethrow the PropertyVetoException.
  167. * <p>
  168. * No event is fired if old and new are equal and non-null.
  169. *
  170. * @param evt The PropertyChangeEvent to be fired.
  171. * @exception PropertyVetoException if the recipient wishes the property
  172. * change to be rolled back.
  173. */
  174. public void fireVetoableChange(PropertyChangeEvent evt)
  175. throws PropertyVetoException {
  176. Object oldValue = evt.getOldValue();
  177. Object newValue = evt.getNewValue();
  178. String propertyName = evt.getPropertyName();
  179. if (oldValue != null && newValue != null && oldValue.equals(newValue)) {
  180. return;
  181. }
  182. java.util.Vector targets = null;
  183. VetoableChangeSupport child = null;
  184. synchronized (this) {
  185. if (listeners != null) {
  186. targets = (java.util.Vector) listeners.clone();
  187. }
  188. if (children != null && propertyName != null) {
  189. child = (VetoableChangeSupport)children.get(propertyName);
  190. }
  191. }
  192. if (listeners != null) {
  193. try {
  194. for (int i = 0; i < targets.size(); i++) {
  195. VetoableChangeListener target =
  196. (VetoableChangeListener)targets.elementAt(i);
  197. target.vetoableChange(evt);
  198. }
  199. } catch (PropertyVetoException veto) {
  200. // Create an event to revert everyone to the old value.
  201. evt = new PropertyChangeEvent(source, propertyName, newValue, oldValue);
  202. for (int i = 0; i < targets.size(); i++) {
  203. try {
  204. VetoableChangeListener target =
  205. (VetoableChangeListener)targets.elementAt(i);
  206. target.vetoableChange(evt);
  207. } catch (PropertyVetoException ex) {
  208. // We just ignore exceptions that occur during reversions.
  209. }
  210. }
  211. // And now rethrow the PropertyVetoException.
  212. throw veto;
  213. }
  214. }
  215. if (child != null) {
  216. child.fireVetoableChange(evt);
  217. }
  218. }
  219. /**
  220. * Check if there are any listeners for a specific property.
  221. *
  222. * @param propertyName the property name.
  223. * @return true if there are one or more listeners for the given property
  224. */
  225. public synchronized boolean hasListeners(String propertyName) {
  226. if (listeners != null && !listeners.isEmpty()) {
  227. // there is a generic listener
  228. return true;
  229. }
  230. if (children != null) {
  231. VetoableChangeSupport child = (VetoableChangeSupport)children.get(propertyName);
  232. if (child != null && child.listeners != null) {
  233. return !child.listeners.isEmpty();
  234. }
  235. }
  236. return false;
  237. }
  238. /**
  239. * @serialData Null terminated list of <code>VetoableChangeListeners</code>.
  240. * <p>
  241. * At serialization time we skip non-serializable listeners and
  242. * only serialize the serializable listeners.
  243. *
  244. */
  245. private void writeObject(ObjectOutputStream s) throws IOException {
  246. s.defaultWriteObject();
  247. java.util.Vector v = null;
  248. synchronized (this) {
  249. if (listeners != null) {
  250. v = (java.util.Vector) listeners.clone();
  251. }
  252. }
  253. if (v != null) {
  254. for(int i = 0; i < v.size(); i++) {
  255. VetoableChangeListener l = (VetoableChangeListener)v.elementAt(i);
  256. if (l instanceof Serializable) {
  257. s.writeObject(l);
  258. }
  259. }
  260. }
  261. s.writeObject(null);
  262. }
  263. private void readObject(ObjectInputStream s) throws ClassNotFoundException, IOException {
  264. s.defaultReadObject();
  265. Object listenerOrNull;
  266. while(null != (listenerOrNull = s.readObject())) {
  267. addVetoableChangeListener((VetoableChangeListener)listenerOrNull);
  268. }
  269. }
  270. /**
  271. * "listeners" lists all the generic listeners.
  272. *
  273. * This is transient - its state is written in the writeObject method.
  274. */
  275. transient private java.util.Vector listeners;
  276. /**
  277. * Hashtable for managing listeners for specific properties.
  278. * Maps property names to VetoableChangeSupport objects.
  279. * @serial
  280. * @since JDK 1.2
  281. */
  282. private java.util.Hashtable children;
  283. /**
  284. * The object to be provided as the "source" for any generated events.
  285. * @serial
  286. */
  287. private Object source;
  288. /**
  289. * Internal version number
  290. * @serial
  291. */
  292. private int vetoableChangeSupportSerializedDataVersion = 2;
  293. /**
  294. * Serialization version ID, so we're compatible with JDK 1.1
  295. */
  296. static final long serialVersionUID = -5090210921595982017L;
  297. }