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