1. /*
  2. * @(#)SwingPropertyChangeSupport.java 1.18 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 javax.swing.event;
  8. import java.beans.*;
  9. import java.io.Serializable;
  10. import java.io.ObjectOutputStream;
  11. import java.io.ObjectInputStream;
  12. import java.io.IOException;
  13. import java.util.Arrays;
  14. import java.util.ArrayList;
  15. import java.util.Iterator;
  16. import java.util.List;
  17. /**
  18. * This subclass of java.beans.PropertyChangeSupport is identical
  19. * in functionality -- it sacrifices thread-safety (not a Swing
  20. * concern) for reduce memory consumption, which helps performance
  21. * (both big Swing concerns). Most of the overridden methods are
  22. * only necessary because all of PropertyChangeSupport's instance
  23. * data is private, without accessor methods.
  24. *
  25. * @version 1.18 01/23/03
  26. * @author unattributed
  27. */
  28. public final class SwingPropertyChangeSupport extends PropertyChangeSupport {
  29. /**
  30. * Constructs a SwingPropertyChangeSupport object.
  31. *
  32. * @param sourceBean The bean to be given as the source for any events.
  33. */
  34. public SwingPropertyChangeSupport(Object sourceBean) {
  35. super(sourceBean);
  36. source = sourceBean;
  37. }
  38. /**
  39. * Add a PropertyChangeListener to the listener list.
  40. * The listener is registered for all properties.
  41. *
  42. * @param listener The PropertyChangeListener to be added
  43. */
  44. public synchronized void addPropertyChangeListener(
  45. PropertyChangeListener listener) {
  46. if (listener instanceof PropertyChangeListenerProxy) {
  47. PropertyChangeListenerProxy proxy =
  48. (PropertyChangeListenerProxy)listener;
  49. // Call two argument add method.
  50. addPropertyChangeListener(proxy.getPropertyName(),
  51. (PropertyChangeListener)proxy.getListener());
  52. } else {
  53. if (listeners == null) {
  54. listeners = new EventListenerList();
  55. }
  56. listeners.add(PropertyChangeListener.class, listener);
  57. }
  58. }
  59. /**
  60. * Remove a PropertyChangeListener from the listener list.
  61. * This removes a PropertyChangeListener that was registered
  62. * for all properties.
  63. *
  64. * @param listener The PropertyChangeListener to be removed
  65. */
  66. public synchronized void removePropertyChangeListener(
  67. PropertyChangeListener listener) {
  68. if (listener instanceof PropertyChangeListenerProxy) {
  69. PropertyChangeListenerProxy proxy =
  70. (PropertyChangeListenerProxy)listener;
  71. // Call two argument remove method.
  72. removePropertyChangeListener(proxy.getPropertyName(),
  73. (PropertyChangeListener)proxy.getListener());
  74. } else {
  75. if (listeners == null) {
  76. return;
  77. }
  78. listeners.remove(PropertyChangeListener.class, listener);
  79. }
  80. }
  81. /**
  82. * Returns an array of all the listeners that were added to the
  83. * SwingPropertyChangeSupport object with addPropertyChangeListener().
  84. * <p>
  85. * If some listeners have been added with a named property, then
  86. * the returned array will be a mixture of PropertyChangeListeners
  87. * and <code>PropertyChangeListenerProxy</code>s. If the calling
  88. * method is interested in distinguishing the listeners then it must
  89. * test each element to see if it's a
  90. * <code>PropertyChangeListenerProxy</code> perform the cast and examine
  91. * the parameter.
  92. *
  93. * <pre>
  94. * PropertyChangeListener[] listeners = support.getPropertyChangeListeners();
  95. * for (int i = 0; i < listeners.length; i++) {
  96. * if (listeners[i] instanceof PropertyChangeListenerProxy) {
  97. * PropertyChangeListenerProxy proxy =
  98. * (PropertyChangeListenerProxy)listeners[i];
  99. * if (proxy.getPropertyName().equals("foo")) {
  100. * // proxy is a PropertyChangeListener which was associated
  101. * // with the property named "foo"
  102. * }
  103. * }
  104. * }
  105. *</pre>
  106. *
  107. * @see java.beans.PropertyChangeListenerProxy
  108. * @see java.beans.PropertyChangeSupport#getPropertyChangeListeners
  109. * @return all of the <code>PropertyChangeListener</code>s added or an
  110. * empty array if no listeners have been added
  111. * @since 1.4
  112. */
  113. public synchronized PropertyChangeListener[] getPropertyChangeListeners() {
  114. List returnList = new ArrayList();
  115. // Add all the PropertyChangeListeners
  116. if (listeners != null) {
  117. returnList.addAll(Arrays.asList(listeners.getListeners(PropertyChangeListener.class)));
  118. }
  119. // Add all the PropertyChangeListenerProxys
  120. if (children != null) {
  121. Iterator iterator = children.keySet().iterator();
  122. while (iterator.hasNext()) {
  123. String key = (String)iterator.next();
  124. SwingPropertyChangeSupport child =
  125. (SwingPropertyChangeSupport)children.get(key);
  126. PropertyChangeListener[] childListeners =
  127. child.getPropertyChangeListeners();
  128. for (int index = childListeners.length - 1; index >= 0;
  129. index--) {
  130. returnList.add(new PropertyChangeListenerProxy(
  131. key, childListeners[index]));
  132. }
  133. }
  134. }
  135. return (PropertyChangeListener[])returnList.toArray(new PropertyChangeListener[returnList.size()]);
  136. }
  137. /**
  138. * Add a PropertyChangeListener for a specific property. The listener
  139. * will be invoked only when a call on firePropertyChange names that
  140. * specific property.
  141. *
  142. * @param propertyName The name of the property to listen on.
  143. * @param listener The PropertyChangeListener to be added
  144. */
  145. public synchronized void addPropertyChangeListener(
  146. String propertyName,
  147. PropertyChangeListener listener) {
  148. if (children == null) {
  149. children = new java.util.Hashtable();
  150. }
  151. SwingPropertyChangeSupport child =
  152. (SwingPropertyChangeSupport)children.get(propertyName);
  153. if (child == null) {
  154. child = new SwingPropertyChangeSupport(source);
  155. children.put(propertyName, child);
  156. }
  157. child.addPropertyChangeListener(listener);
  158. }
  159. /**
  160. * Remove a PropertyChangeListener for a specific property.
  161. *
  162. * @param propertyName The name of the property that was listened on.
  163. * @param listener The PropertyChangeListener to be removed
  164. */
  165. public synchronized void removePropertyChangeListener(
  166. String propertyName,
  167. PropertyChangeListener listener) {
  168. if (children == null) {
  169. return;
  170. }
  171. SwingPropertyChangeSupport child =
  172. (SwingPropertyChangeSupport)children.get(propertyName);
  173. if (child == null) {
  174. return;
  175. }
  176. child.removePropertyChangeListener(listener);
  177. }
  178. /**
  179. * Returns an array of all the listeners which have been associated
  180. * with the named property.
  181. *
  182. * @return all of the <code>PropertyChangeListeners</code> associated with
  183. * the named property or an empty array if no listeners have
  184. * been added
  185. */
  186. public synchronized PropertyChangeListener[] getPropertyChangeListeners(
  187. String propertyName) {
  188. List returnList = new ArrayList();
  189. if (children != null) {
  190. SwingPropertyChangeSupport support =
  191. (SwingPropertyChangeSupport)children.get(propertyName);
  192. if (support != null) {
  193. returnList.addAll(
  194. Arrays.asList(support.getPropertyChangeListeners()));
  195. }
  196. }
  197. return (PropertyChangeListener[])(returnList.toArray(
  198. new PropertyChangeListener[0]));
  199. }
  200. /**
  201. * Report a bound property update to any registered listeners.
  202. * No event is fired if old and new are equal and non-null.
  203. *
  204. * @param propertyName The programmatic name of the property
  205. * that was changed.
  206. * @param oldValue The old value of the property.
  207. * @param newValue The new value of the property.
  208. */
  209. public void firePropertyChange(String propertyName,
  210. Object oldValue, Object newValue) {
  211. firePropertyChange(new PropertyChangeEvent(source, propertyName,
  212. oldValue, newValue));
  213. }
  214. /**
  215. * Fire an existing PropertyChangeEvent to any registered listeners.
  216. * No event is fired if the given event's old and new values are
  217. * equal and non-null.
  218. * @param evt The PropertyChangeEvent object.
  219. */
  220. public void firePropertyChange(PropertyChangeEvent evt) {
  221. Object oldValue = evt.getOldValue();
  222. Object newValue = evt.getNewValue();
  223. String propertyName = evt.getPropertyName();
  224. if (oldValue != null && newValue != null && oldValue.equals(newValue)) {
  225. return;
  226. }
  227. SwingPropertyChangeSupport child = null;
  228. if (children != null) {
  229. synchronized (this) {
  230. if (children != null && propertyName != null) {
  231. child = (SwingPropertyChangeSupport)children.get(propertyName);
  232. }
  233. }
  234. }
  235. if (listeners != null) {
  236. Object[] listenerList = listeners.getListenerList();
  237. for (int i = 0 ; i <= listenerList.length-2; i += 2) {
  238. if (listenerList[i] == PropertyChangeListener.class) {
  239. ((PropertyChangeListener)listenerList[i+1]).propertyChange(evt);
  240. }
  241. }
  242. }
  243. if (child != null) {
  244. child.firePropertyChange(evt);
  245. }
  246. }
  247. /**
  248. * Check if there are any listeners for a specific property.
  249. *
  250. * @param propertyName the property name.
  251. * @return true if there are ore or more listeners for the given property
  252. */
  253. public synchronized boolean hasListeners(String propertyName) {
  254. if (listeners != null && listeners.getListenerCount(PropertyChangeListener.class) > 0) {
  255. // there is a generic listener
  256. return true;
  257. }
  258. if (children != null) {
  259. SwingPropertyChangeSupport child =
  260. (SwingPropertyChangeSupport)children.get(propertyName);
  261. if (child != null) {
  262. // The child will always have a listeners Vector.
  263. return child.hasListeners(propertyName);
  264. }
  265. }
  266. return false;
  267. }
  268. private void writeObject(ObjectOutputStream s) throws IOException {
  269. s.defaultWriteObject();
  270. if (listeners != null) {
  271. Object[] listenerList = listeners.getListenerList();
  272. for (int i = 0; i <= listenerList.length-2; i += 2) {
  273. if (listenerList[i] == PropertyChangeListener.class &&
  274. (PropertyChangeListener)listenerList[i+1] instanceof Serializable) {
  275. s.writeObject((PropertyChangeListener)listenerList[i+1]);
  276. }
  277. }
  278. }
  279. s.writeObject(null);
  280. }
  281. private void readObject(ObjectInputStream s) throws ClassNotFoundException, IOException {
  282. s.defaultReadObject();
  283. Object listenerOrNull;
  284. while (null != (listenerOrNull = s.readObject())) {
  285. addPropertyChangeListener((PropertyChangeListener)listenerOrNull);
  286. }
  287. }
  288. // "listeners" lists all the generic listeners.
  289. transient private EventListenerList listeners;
  290. // "children" contains SwingPropertyChangeSupports for individual properties
  291. private java.util.Hashtable children;
  292. private Object source;
  293. // Serialization version ID
  294. static final long serialVersionUID = 7162625831330845068L;
  295. }