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