1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1.2 +++ b/rt/emul/compact/src/main/java/java/beans/PropertyChangeSupport.java Tue Feb 26 16:54:16 2013 +0100
1.3 @@ -0,0 +1,536 @@
1.4 +/*
1.5 + * Copyright (c) 1996, 2008, Oracle and/or its affiliates. All rights reserved.
1.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
1.7 + *
1.8 + * This code is free software; you can redistribute it and/or modify it
1.9 + * under the terms of the GNU General Public License version 2 only, as
1.10 + * published by the Free Software Foundation. Oracle designates this
1.11 + * particular file as subject to the "Classpath" exception as provided
1.12 + * by Oracle in the LICENSE file that accompanied this code.
1.13 + *
1.14 + * This code is distributed in the hope that it will be useful, but WITHOUT
1.15 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
1.16 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
1.17 + * version 2 for more details (a copy is included in the LICENSE file that
1.18 + * accompanied this code).
1.19 + *
1.20 + * You should have received a copy of the GNU General Public License version
1.21 + * 2 along with this work; if not, write to the Free Software Foundation,
1.22 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
1.23 + *
1.24 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
1.25 + * or visit www.oracle.com if you need additional information or have any
1.26 + * questions.
1.27 + */
1.28 +package java.beans;
1.29 +
1.30 +import java.io.Serializable;
1.31 +import java.io.ObjectStreamField;
1.32 +import java.io.ObjectOutputStream;
1.33 +import java.io.ObjectInputStream;
1.34 +import java.io.IOException;
1.35 +import java.util.Hashtable;
1.36 +import java.util.Map.Entry;
1.37 +
1.38 +/**
1.39 + * This is a utility class that can be used by beans that support bound
1.40 + * properties. It manages a list of listeners and dispatches
1.41 + * {@link PropertyChangeEvent}s to them. You can use an instance of this class
1.42 + * as a member field of your bean and delegate these types of work to it.
1.43 + * The {@link PropertyChangeListener} can be registered for all properties
1.44 + * or for a property specified by name.
1.45 + * <p>
1.46 + * Here is an example of {@code PropertyChangeSupport} usage that follows
1.47 + * the rules and recommendations laid out in the JavaBeans™ specification:
1.48 + * <pre>
1.49 + * public class MyBean {
1.50 + * private final PropertyChangeSupport pcs = new PropertyChangeSupport(this);
1.51 + *
1.52 + * public void addPropertyChangeListener(PropertyChangeListener listener) {
1.53 + * this.pcs.addPropertyChangeListener(listener);
1.54 + * }
1.55 + *
1.56 + * public void removePropertyChangeListener(PropertyChangeListener listener) {
1.57 + * this.pcs.removePropertyChangeListener(listener);
1.58 + * }
1.59 + *
1.60 + * private String value;
1.61 + *
1.62 + * public String getValue() {
1.63 + * return this.value;
1.64 + * }
1.65 + *
1.66 + * public void setValue(String newValue) {
1.67 + * String oldValue = this.value;
1.68 + * this.value = newValue;
1.69 + * this.pcs.firePropertyChange("value", oldValue, newValue);
1.70 + * }
1.71 + *
1.72 + * [...]
1.73 + * }
1.74 + * </pre>
1.75 + * <p>
1.76 + * A {@code PropertyChangeSupport} instance is thread-safe.
1.77 + * <p>
1.78 + * This class is serializable. When it is serialized it will save
1.79 + * (and restore) any listeners that are themselves serializable. Any
1.80 + * non-serializable listeners will be skipped during serialization.
1.81 + *
1.82 + * @see VetoableChangeSupport
1.83 + */
1.84 +public class PropertyChangeSupport implements Serializable {
1.85 + private PropertyChangeListenerMap map = new PropertyChangeListenerMap();
1.86 +
1.87 + /**
1.88 + * Constructs a <code>PropertyChangeSupport</code> object.
1.89 + *
1.90 + * @param sourceBean The bean to be given as the source for any events.
1.91 + */
1.92 + public PropertyChangeSupport(Object sourceBean) {
1.93 + if (sourceBean == null) {
1.94 + throw new NullPointerException();
1.95 + }
1.96 + source = sourceBean;
1.97 + }
1.98 +
1.99 + /**
1.100 + * Add a PropertyChangeListener to the listener list.
1.101 + * The listener is registered for all properties.
1.102 + * The same listener object may be added more than once, and will be called
1.103 + * as many times as it is added.
1.104 + * If <code>listener</code> is null, no exception is thrown and no action
1.105 + * is taken.
1.106 + *
1.107 + * @param listener The PropertyChangeListener to be added
1.108 + */
1.109 + public void addPropertyChangeListener(PropertyChangeListener listener) {
1.110 + if (listener == null) {
1.111 + return;
1.112 + }
1.113 + if (listener instanceof PropertyChangeListenerProxy) {
1.114 + PropertyChangeListenerProxy proxy =
1.115 + (PropertyChangeListenerProxy)listener;
1.116 + // Call two argument add method.
1.117 + addPropertyChangeListener(proxy.getPropertyName(),
1.118 + proxy.getListener());
1.119 + } else {
1.120 + this.map.add(null, listener);
1.121 + }
1.122 + }
1.123 +
1.124 + /**
1.125 + * Remove a PropertyChangeListener from the listener list.
1.126 + * This removes a PropertyChangeListener that was registered
1.127 + * for all properties.
1.128 + * If <code>listener</code> was added more than once to the same event
1.129 + * source, it will be notified one less time after being removed.
1.130 + * If <code>listener</code> is null, or was never added, no exception is
1.131 + * thrown and no action is taken.
1.132 + *
1.133 + * @param listener The PropertyChangeListener to be removed
1.134 + */
1.135 + public void removePropertyChangeListener(PropertyChangeListener listener) {
1.136 + if (listener == null) {
1.137 + return;
1.138 + }
1.139 + if (listener instanceof PropertyChangeListenerProxy) {
1.140 + PropertyChangeListenerProxy proxy =
1.141 + (PropertyChangeListenerProxy)listener;
1.142 + // Call two argument remove method.
1.143 + removePropertyChangeListener(proxy.getPropertyName(),
1.144 + proxy.getListener());
1.145 + } else {
1.146 + this.map.remove(null, listener);
1.147 + }
1.148 + }
1.149 +
1.150 + /**
1.151 + * Returns an array of all the listeners that were added to the
1.152 + * PropertyChangeSupport object with addPropertyChangeListener().
1.153 + * <p>
1.154 + * If some listeners have been added with a named property, then
1.155 + * the returned array will be a mixture of PropertyChangeListeners
1.156 + * and <code>PropertyChangeListenerProxy</code>s. If the calling
1.157 + * method is interested in distinguishing the listeners then it must
1.158 + * test each element to see if it's a
1.159 + * <code>PropertyChangeListenerProxy</code>, perform the cast, and examine
1.160 + * the parameter.
1.161 + *
1.162 + * <pre>
1.163 + * PropertyChangeListener[] listeners = bean.getPropertyChangeListeners();
1.164 + * for (int i = 0; i < listeners.length; i++) {
1.165 + * if (listeners[i] instanceof PropertyChangeListenerProxy) {
1.166 + * PropertyChangeListenerProxy proxy =
1.167 + * (PropertyChangeListenerProxy)listeners[i];
1.168 + * if (proxy.getPropertyName().equals("foo")) {
1.169 + * // proxy is a PropertyChangeListener which was associated
1.170 + * // with the property named "foo"
1.171 + * }
1.172 + * }
1.173 + * }
1.174 + *</pre>
1.175 + *
1.176 + * @see PropertyChangeListenerProxy
1.177 + * @return all of the <code>PropertyChangeListeners</code> added or an
1.178 + * empty array if no listeners have been added
1.179 + * @since 1.4
1.180 + */
1.181 + public PropertyChangeListener[] getPropertyChangeListeners() {
1.182 + return this.map.getListeners();
1.183 + }
1.184 +
1.185 + /**
1.186 + * Add a PropertyChangeListener for a specific property. The listener
1.187 + * will be invoked only when a call on firePropertyChange names that
1.188 + * specific property.
1.189 + * The same listener object may be added more than once. For each
1.190 + * property, the listener will be invoked the number of times it was added
1.191 + * for that property.
1.192 + * If <code>propertyName</code> or <code>listener</code> is null, no
1.193 + * exception is thrown and no action is taken.
1.194 + *
1.195 + * @param propertyName The name of the property to listen on.
1.196 + * @param listener The PropertyChangeListener to be added
1.197 + */
1.198 + public void addPropertyChangeListener(
1.199 + String propertyName,
1.200 + PropertyChangeListener listener) {
1.201 + if (listener == null || propertyName == null) {
1.202 + return;
1.203 + }
1.204 + listener = this.map.extract(listener);
1.205 + if (listener != null) {
1.206 + this.map.add(propertyName, listener);
1.207 + }
1.208 + }
1.209 +
1.210 + /**
1.211 + * Remove a PropertyChangeListener for a specific property.
1.212 + * If <code>listener</code> was added more than once to the same event
1.213 + * source for the specified property, it will be notified one less time
1.214 + * after being removed.
1.215 + * If <code>propertyName</code> is null, no exception is thrown and no
1.216 + * action is taken.
1.217 + * If <code>listener</code> is null, or was never added for the specified
1.218 + * property, no exception is thrown and no action is taken.
1.219 + *
1.220 + * @param propertyName The name of the property that was listened on.
1.221 + * @param listener The PropertyChangeListener to be removed
1.222 + */
1.223 + public void removePropertyChangeListener(
1.224 + String propertyName,
1.225 + PropertyChangeListener listener) {
1.226 + if (listener == null || propertyName == null) {
1.227 + return;
1.228 + }
1.229 + listener = this.map.extract(listener);
1.230 + if (listener != null) {
1.231 + this.map.remove(propertyName, listener);
1.232 + }
1.233 + }
1.234 +
1.235 + /**
1.236 + * Returns an array of all the listeners which have been associated
1.237 + * with the named property.
1.238 + *
1.239 + * @param propertyName The name of the property being listened to
1.240 + * @return all of the <code>PropertyChangeListeners</code> associated with
1.241 + * the named property. If no such listeners have been added,
1.242 + * or if <code>propertyName</code> is null, an empty array is
1.243 + * returned.
1.244 + * @since 1.4
1.245 + */
1.246 + public PropertyChangeListener[] getPropertyChangeListeners(String propertyName) {
1.247 + return this.map.getListeners(propertyName);
1.248 + }
1.249 +
1.250 + /**
1.251 + * Reports a bound property update to listeners
1.252 + * that have been registered to track updates of
1.253 + * all properties or a property with the specified name.
1.254 + * <p>
1.255 + * No event is fired if old and new values are equal and non-null.
1.256 + * <p>
1.257 + * This is merely a convenience wrapper around the more general
1.258 + * {@link #firePropertyChange(PropertyChangeEvent)} method.
1.259 + *
1.260 + * @param propertyName the programmatic name of the property that was changed
1.261 + * @param oldValue the old value of the property
1.262 + * @param newValue the new value of the property
1.263 + */
1.264 + public void firePropertyChange(String propertyName, Object oldValue, Object newValue) {
1.265 + if (oldValue == null || newValue == null || !oldValue.equals(newValue)) {
1.266 + firePropertyChange(new PropertyChangeEvent(this.source, propertyName, oldValue, newValue));
1.267 + }
1.268 + }
1.269 +
1.270 + /**
1.271 + * Reports an integer bound property update to listeners
1.272 + * that have been registered to track updates of
1.273 + * all properties or a property with the specified name.
1.274 + * <p>
1.275 + * No event is fired if old and new values are equal.
1.276 + * <p>
1.277 + * This is merely a convenience wrapper around the more general
1.278 + * {@link #firePropertyChange(String, Object, Object)} method.
1.279 + *
1.280 + * @param propertyName the programmatic name of the property that was changed
1.281 + * @param oldValue the old value of the property
1.282 + * @param newValue the new value of the property
1.283 + */
1.284 + public void firePropertyChange(String propertyName, int oldValue, int newValue) {
1.285 + if (oldValue != newValue) {
1.286 + firePropertyChange(propertyName, Integer.valueOf(oldValue), Integer.valueOf(newValue));
1.287 + }
1.288 + }
1.289 +
1.290 + /**
1.291 + * Reports a boolean bound property update to listeners
1.292 + * that have been registered to track updates of
1.293 + * all properties or a property with the specified name.
1.294 + * <p>
1.295 + * No event is fired if old and new values are equal.
1.296 + * <p>
1.297 + * This is merely a convenience wrapper around the more general
1.298 + * {@link #firePropertyChange(String, Object, Object)} method.
1.299 + *
1.300 + * @param propertyName the programmatic name of the property that was changed
1.301 + * @param oldValue the old value of the property
1.302 + * @param newValue the new value of the property
1.303 + */
1.304 + public void firePropertyChange(String propertyName, boolean oldValue, boolean newValue) {
1.305 + if (oldValue != newValue) {
1.306 + firePropertyChange(propertyName, Boolean.valueOf(oldValue), Boolean.valueOf(newValue));
1.307 + }
1.308 + }
1.309 +
1.310 + /**
1.311 + * Fires a property change event to listeners
1.312 + * that have been registered to track updates of
1.313 + * all properties or a property with the specified name.
1.314 + * <p>
1.315 + * No event is fired if the given event's old and new values are equal and non-null.
1.316 + *
1.317 + * @param event the {@code PropertyChangeEvent} to be fired
1.318 + */
1.319 + public void firePropertyChange(PropertyChangeEvent event) {
1.320 + Object oldValue = event.getOldValue();
1.321 + Object newValue = event.getNewValue();
1.322 + if (oldValue == null || newValue == null || !oldValue.equals(newValue)) {
1.323 + String name = event.getPropertyName();
1.324 +
1.325 + PropertyChangeListener[] common = this.map.get(null);
1.326 + PropertyChangeListener[] named = (name != null)
1.327 + ? this.map.get(name)
1.328 + : null;
1.329 +
1.330 + fire(common, event);
1.331 + fire(named, event);
1.332 + }
1.333 + }
1.334 +
1.335 + private static void fire(PropertyChangeListener[] listeners, PropertyChangeEvent event) {
1.336 + if (listeners != null) {
1.337 + for (PropertyChangeListener listener : listeners) {
1.338 + listener.propertyChange(event);
1.339 + }
1.340 + }
1.341 + }
1.342 +
1.343 + /**
1.344 + * Reports a bound indexed property update to listeners
1.345 + * that have been registered to track updates of
1.346 + * all properties or a property with the specified name.
1.347 + * <p>
1.348 + * No event is fired if old and new values are equal and non-null.
1.349 + * <p>
1.350 + * This is merely a convenience wrapper around the more general
1.351 + * {@link #firePropertyChange(PropertyChangeEvent)} method.
1.352 + *
1.353 + * @param propertyName the programmatic name of the property that was changed
1.354 + * @param index the index of the property element that was changed
1.355 + * @param oldValue the old value of the property
1.356 + * @param newValue the new value of the property
1.357 + * @since 1.5
1.358 + */
1.359 + public void fireIndexedPropertyChange(String propertyName, int index, Object oldValue, Object newValue) {
1.360 + if (oldValue == null || newValue == null || !oldValue.equals(newValue)) {
1.361 + firePropertyChange(new IndexedPropertyChangeEvent(source, propertyName, oldValue, newValue, index));
1.362 + }
1.363 + }
1.364 +
1.365 + /**
1.366 + * Reports an integer bound indexed property update to listeners
1.367 + * that have been registered to track updates of
1.368 + * all properties or a property with the specified name.
1.369 + * <p>
1.370 + * No event is fired if old and new values are equal.
1.371 + * <p>
1.372 + * This is merely a convenience wrapper around the more general
1.373 + * {@link #fireIndexedPropertyChange(String, int, Object, Object)} method.
1.374 + *
1.375 + * @param propertyName the programmatic name of the property that was changed
1.376 + * @param index the index of the property element that was changed
1.377 + * @param oldValue the old value of the property
1.378 + * @param newValue the new value of the property
1.379 + * @since 1.5
1.380 + */
1.381 + public void fireIndexedPropertyChange(String propertyName, int index, int oldValue, int newValue) {
1.382 + if (oldValue != newValue) {
1.383 + fireIndexedPropertyChange(propertyName, index, Integer.valueOf(oldValue), Integer.valueOf(newValue));
1.384 + }
1.385 + }
1.386 +
1.387 + /**
1.388 + * Reports a boolean bound indexed property update to listeners
1.389 + * that have been registered to track updates of
1.390 + * all properties or a property with the specified name.
1.391 + * <p>
1.392 + * No event is fired if old and new values are equal.
1.393 + * <p>
1.394 + * This is merely a convenience wrapper around the more general
1.395 + * {@link #fireIndexedPropertyChange(String, int, Object, Object)} method.
1.396 + *
1.397 + * @param propertyName the programmatic name of the property that was changed
1.398 + * @param index the index of the property element that was changed
1.399 + * @param oldValue the old value of the property
1.400 + * @param newValue the new value of the property
1.401 + * @since 1.5
1.402 + */
1.403 + public void fireIndexedPropertyChange(String propertyName, int index, boolean oldValue, boolean newValue) {
1.404 + if (oldValue != newValue) {
1.405 + fireIndexedPropertyChange(propertyName, index, Boolean.valueOf(oldValue), Boolean.valueOf(newValue));
1.406 + }
1.407 + }
1.408 +
1.409 + /**
1.410 + * Check if there are any listeners for a specific property, including
1.411 + * those registered on all properties. If <code>propertyName</code>
1.412 + * is null, only check for listeners registered on all properties.
1.413 + *
1.414 + * @param propertyName the property name.
1.415 + * @return true if there are one or more listeners for the given property
1.416 + */
1.417 + public boolean hasListeners(String propertyName) {
1.418 + return this.map.hasListeners(propertyName);
1.419 + }
1.420 +
1.421 + /**
1.422 + * @serialData Null terminated list of <code>PropertyChangeListeners</code>.
1.423 + * <p>
1.424 + * At serialization time we skip non-serializable listeners and
1.425 + * only serialize the serializable listeners.
1.426 + */
1.427 + private void writeObject(ObjectOutputStream s) throws IOException {
1.428 + Hashtable<String, PropertyChangeSupport> children = null;
1.429 + PropertyChangeListener[] listeners = null;
1.430 + synchronized (this.map) {
1.431 + for (Entry<String, PropertyChangeListener[]> entry : this.map.getEntries()) {
1.432 + String property = entry.getKey();
1.433 + if (property == null) {
1.434 + listeners = entry.getValue();
1.435 + } else {
1.436 + if (children == null) {
1.437 + children = new Hashtable<String, PropertyChangeSupport>();
1.438 + }
1.439 + PropertyChangeSupport pcs = new PropertyChangeSupport(this.source);
1.440 + pcs.map.set(null, entry.getValue());
1.441 + children.put(property, pcs);
1.442 + }
1.443 + }
1.444 + }
1.445 + ObjectOutputStream.PutField fields = s.putFields();
1.446 + fields.put("children", children);
1.447 + fields.put("source", this.source);
1.448 + fields.put("propertyChangeSupportSerializedDataVersion", 2);
1.449 + s.writeFields();
1.450 +
1.451 + if (listeners != null) {
1.452 + for (PropertyChangeListener l : listeners) {
1.453 + if (l instanceof Serializable) {
1.454 + s.writeObject(l);
1.455 + }
1.456 + }
1.457 + }
1.458 + s.writeObject(null);
1.459 + }
1.460 +
1.461 + private void readObject(ObjectInputStream s) throws ClassNotFoundException, IOException {
1.462 + this.map = new PropertyChangeListenerMap();
1.463 +
1.464 + ObjectInputStream.GetField fields = s.readFields();
1.465 +
1.466 + Hashtable<String, PropertyChangeSupport> children = (Hashtable<String, PropertyChangeSupport>) fields.get("children", null);
1.467 + this.source = fields.get("source", null);
1.468 + fields.get("propertyChangeSupportSerializedDataVersion", 2);
1.469 +
1.470 + Object listenerOrNull;
1.471 + while (null != (listenerOrNull = s.readObject())) {
1.472 + this.map.add(null, (PropertyChangeListener)listenerOrNull);
1.473 + }
1.474 + if (children != null) {
1.475 + for (Entry<String, PropertyChangeSupport> entry : children.entrySet()) {
1.476 + for (PropertyChangeListener listener : entry.getValue().getPropertyChangeListeners()) {
1.477 + this.map.add(entry.getKey(), listener);
1.478 + }
1.479 + }
1.480 + }
1.481 + }
1.482 +
1.483 + /**
1.484 + * The object to be provided as the "source" for any generated events.
1.485 + */
1.486 + private Object source;
1.487 +
1.488 + /**
1.489 + * @serialField children Hashtable
1.490 + * @serialField source Object
1.491 + * @serialField propertyChangeSupportSerializedDataVersion int
1.492 + */
1.493 + private static final ObjectStreamField[] serialPersistentFields = {
1.494 + new ObjectStreamField("children", Hashtable.class),
1.495 + new ObjectStreamField("source", Object.class),
1.496 + new ObjectStreamField("propertyChangeSupportSerializedDataVersion", Integer.TYPE)
1.497 + };
1.498 +
1.499 + /**
1.500 + * Serialization version ID, so we're compatible with JDK 1.1
1.501 + */
1.502 + static final long serialVersionUID = 6401253773779951803L;
1.503 +
1.504 + /**
1.505 + * This is a {@link ChangeListenerMap ChangeListenerMap} implementation
1.506 + * that works with {@link PropertyChangeListener PropertyChangeListener} objects.
1.507 + */
1.508 + private static final class PropertyChangeListenerMap extends ChangeListenerMap<PropertyChangeListener> {
1.509 + private static final PropertyChangeListener[] EMPTY = {};
1.510 +
1.511 + /**
1.512 + * Creates an array of {@link PropertyChangeListener PropertyChangeListener} objects.
1.513 + * This method uses the same instance of the empty array
1.514 + * when {@code length} equals {@code 0}.
1.515 + *
1.516 + * @param length the array length
1.517 + * @return an array with specified length
1.518 + */
1.519 + @Override
1.520 + protected PropertyChangeListener[] newArray(int length) {
1.521 + return (0 < length)
1.522 + ? new PropertyChangeListener[length]
1.523 + : EMPTY;
1.524 + }
1.525 +
1.526 + /**
1.527 + * Creates a {@link PropertyChangeListenerProxy PropertyChangeListenerProxy}
1.528 + * object for the specified property.
1.529 + *
1.530 + * @param name the name of the property to listen on
1.531 + * @param listener the listener to process events
1.532 + * @return a {@code PropertyChangeListenerProxy} object
1.533 + */
1.534 + @Override
1.535 + protected PropertyChangeListener newProxy(String name, PropertyChangeListener listener) {
1.536 + return new PropertyChangeListenerProxy(name, listener);
1.537 + }
1.538 + }
1.539 +}