001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017:
018: package java.beans;
019:
020: import java.io.IOException;
021: import java.io.ObjectInputStream;
022: import java.io.ObjectOutputStream;
023: import java.io.Serializable;
024: import java.util.ArrayList;
025: import java.util.Hashtable;
026: import java.util.Iterator;
027: import java.util.List;
028:
029: // FIXME: obviously need synchronization, when access listeners
030:
031: public class VetoableChangeSupport implements Serializable {
032:
033: private static final long serialVersionUID = -5090210921595982017l;
034:
035: private Hashtable<String, VetoableChangeSupport> children = new Hashtable<String, VetoableChangeSupport>();
036:
037: private transient ArrayList<VetoableChangeListener> globalListeners = new ArrayList<VetoableChangeListener>();
038:
039: private Object source;
040:
041: @SuppressWarnings("unused")
042: // for serialization
043: private int vetoableChangeSupportSerializedDataVersion = 2;
044:
045: public VetoableChangeSupport(Object sourceBean) {
046: if (sourceBean == null) {
047: throw new NullPointerException();
048: }
049: this .source = sourceBean;
050: }
051:
052: public synchronized void removeVetoableChangeListener(
053: String propertyName, VetoableChangeListener listener) {
054: if ((propertyName != null) && (listener != null)) {
055: VetoableChangeSupport listeners = children
056: .get(propertyName);
057:
058: if (listeners != null) {
059: listeners.removeVetoableChangeListener(listener);
060: }
061: }
062: }
063:
064: public synchronized void addVetoableChangeListener(
065: String propertyName, VetoableChangeListener listener) {
066: if (propertyName != null && listener != null) {
067: VetoableChangeSupport listeners = children
068: .get(propertyName);
069:
070: if (listeners == null) {
071: listeners = new VetoableChangeSupport(source);
072: children.put(propertyName, listeners);
073: }
074: listeners.addVetoableChangeListener(listener);
075: }
076: }
077:
078: public synchronized VetoableChangeListener[] getVetoableChangeListeners(
079: String propertyName) {
080: VetoableChangeSupport listeners = null;
081:
082: if (propertyName != null) {
083: listeners = children.get(propertyName);
084: }
085: return (listeners == null) ? new VetoableChangeListener[] {}
086: : getAsVetoableChangeListenerArray(listeners);
087: }
088:
089: public synchronized boolean hasListeners(String propertyName) {
090: boolean result = globalListeners.size() > 0;
091: if (!result && propertyName != null) {
092: VetoableChangeSupport listeners = children
093: .get(propertyName);
094: if (listeners != null) {
095: result = listeners.globalListeners.size() > 0;
096: }
097: }
098: return result;
099: }
100:
101: public synchronized void removeVetoableChangeListener(
102: VetoableChangeListener listener) {
103: if (listener != null) {
104: globalListeners.remove(listener);
105: }
106: }
107:
108: public synchronized void addVetoableChangeListener(
109: VetoableChangeListener listener) {
110: if (listener != null) {
111: if (listener instanceof VetoableChangeListenerProxy) {
112: VetoableChangeListenerProxy proxy = (VetoableChangeListenerProxy) listener;
113: addVetoableChangeListener(proxy.getPropertyName(),
114: (VetoableChangeListener) proxy.getListener());
115: } else {
116: globalListeners.add(listener);
117: }
118: }
119: }
120:
121: public synchronized VetoableChangeListener[] getVetoableChangeListeners() {
122: List<VetoableChangeListener> result = new ArrayList<VetoableChangeListener>();
123: if (globalListeners != null) {
124: result.addAll(globalListeners);
125: }
126:
127: for (Iterator<String> iterator = children.keySet().iterator(); iterator
128: .hasNext();) {
129: String propertyName = iterator.next();
130: VetoableChangeSupport namedListener = children
131: .get(propertyName);
132: VetoableChangeListener[] childListeners = namedListener
133: .getVetoableChangeListeners();
134: for (int i = 0; i < childListeners.length; i++) {
135: result.add(new VetoableChangeListenerProxy(
136: propertyName, childListeners[i]));
137: }
138: }
139: return (result
140: .toArray(new VetoableChangeListener[result.size()]));
141: }
142:
143: private void writeObject(ObjectOutputStream oos) throws IOException {
144: oos.defaultWriteObject();
145: VetoableChangeListener[] copy = new VetoableChangeListener[globalListeners
146: .size()];
147: globalListeners.toArray(copy);
148: for (VetoableChangeListener listener : copy) {
149: if (listener instanceof Serializable) {
150: oos.writeObject(listener);
151: }
152: }
153: // Denotes end of list
154: oos.writeObject(null);
155:
156: }
157:
158: private void readObject(ObjectInputStream ois) throws IOException,
159: ClassNotFoundException {
160: ois.defaultReadObject();
161: this .globalListeners = new ArrayList<VetoableChangeListener>();
162: if (null == this .children) {
163: this .children = new Hashtable<String, VetoableChangeSupport>();
164: }
165: Object listener;
166: do {
167: // Reads a listener _or_ proxy
168: listener = ois.readObject();
169: addVetoableChangeListener((VetoableChangeListener) listener);
170: } while (listener != null);
171: }
172:
173: @SuppressWarnings("boxing")
174: public void fireVetoableChange(String propertyName,
175: boolean oldValue, boolean newValue)
176: throws PropertyVetoException {
177: PropertyChangeEvent event = createPropertyChangeEvent(
178: propertyName, oldValue, newValue);
179: doFirePropertyChange(event);
180: }
181:
182: @SuppressWarnings("boxing")
183: public void fireVetoableChange(String propertyName, int oldValue,
184: int newValue) throws PropertyVetoException {
185: PropertyChangeEvent event = createPropertyChangeEvent(
186: propertyName, oldValue, newValue);
187: doFirePropertyChange(event);
188: }
189:
190: public void fireVetoableChange(String propertyName,
191: Object oldValue, Object newValue)
192: throws PropertyVetoException {
193: PropertyChangeEvent event = createPropertyChangeEvent(
194: propertyName, oldValue, newValue);
195: doFirePropertyChange(event);
196: }
197:
198: public void fireVetoableChange(PropertyChangeEvent event)
199: throws PropertyVetoException {
200: doFirePropertyChange(event);
201: }
202:
203: private PropertyChangeEvent createPropertyChangeEvent(
204: String propertyName, Object oldValue, Object newValue) {
205: return new PropertyChangeEvent(source, propertyName, oldValue,
206: newValue);
207: }
208:
209: private void doFirePropertyChange(PropertyChangeEvent event)
210: throws PropertyVetoException {
211: String propName = event.getPropertyName();
212: Object oldValue = event.getOldValue();
213: Object newValue = event.getNewValue();
214:
215: if (newValue != null && oldValue != null
216: && newValue.equals(oldValue)) {
217: return;
218: }
219:
220: /* Take note of who we are going to notify (and potentially un-notify) */
221:
222: VetoableChangeListener[] listensToAll;
223: VetoableChangeSupport listeners = null;
224: // property change
225: synchronized (this ) {
226: listensToAll = globalListeners
227: .toArray(new VetoableChangeListener[0]);
228: String propertyName = event.getPropertyName();
229: if (propertyName != null) {
230: listeners = children.get(propertyName);
231: }
232: }
233:
234: try {
235: for (VetoableChangeListener listener : listensToAll) {
236: listener.vetoableChange(event);
237: }
238: } catch (PropertyVetoException pve) {
239: // Tell them we have changed it back
240: PropertyChangeEvent revertEvent = createPropertyChangeEvent(
241: propName, newValue, oldValue);
242: for (VetoableChangeListener listener : listensToAll) {
243: try {
244: listener.vetoableChange(revertEvent);
245: } catch (PropertyVetoException ignored) {
246: // expected
247: }
248: }
249: throw pve;
250: }
251: if (listeners != null) {
252: listeners.fireVetoableChange(event);
253: }
254: }
255:
256: private static VetoableChangeListener[] getAsVetoableChangeListenerArray(
257: VetoableChangeSupport listeners) {
258: return listeners.globalListeners
259: .toArray(new VetoableChangeListener[0]);
260: }
261: }
|