001/* java.beans.beancontext.BeanContextChildSupport
002   Copyright (C) 1999 Free Software Foundation, Inc.
003
004This file is part of GNU Classpath.
005
006GNU Classpath is free software; you can redistribute it and/or modify
007it under the terms of the GNU General Public License as published by
008the Free Software Foundation; either version 2, or (at your option)
009any later version.
010 
011GNU Classpath is distributed in the hope that it will be useful, but
012WITHOUT ANY WARRANTY; without even the implied warranty of
013MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
014General Public License for more details.
015
016You should have received a copy of the GNU General Public License
017along with GNU Classpath; see the file COPYING.  If not, write to the
018Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
01902110-1301 USA.
020
021Linking this library statically or dynamically with other modules is
022making a combined work based on this library.  Thus, the terms and
023conditions of the GNU General Public License cover the whole
024combination.
025
026As a special exception, the copyright holders of this library give you
027permission to link this library with independent modules to produce an
028executable, regardless of the license terms of these independent
029modules, and to copy and distribute the resulting executable under
030terms of your choice, provided that you also meet, for each linked
031independent module, the terms and conditions of the license of that
032module.  An independent module is a module which is not derived from
033or based on this library.  If you modify this library, you may extend
034this exception to your version of the library, but you are not
035obligated to do so.  If you do not wish to do so, delete this
036exception statement from your version. */
037
038
039package java.beans.beancontext;
040
041import java.beans.PropertyChangeEvent;
042import java.beans.PropertyChangeListener;
043import java.beans.PropertyChangeSupport;
044import java.beans.PropertyVetoException;
045import java.beans.VetoableChangeListener;
046import java.beans.VetoableChangeSupport;
047import java.io.Serializable;
048
049/**
050 * Support for creating a <code>BeanContextChild</code>.
051 * This class contains the most common implementations of the methods in
052 * the <code>BeanContextChild</code>
053 *
054 * @specnote This class is not very well specified.  I had to "fill in the
055 *           blanks" in most places with what I thought was reasonable
056 *           behavior.  If there are problems, let me know.
057 *
058 * @author John Keiser
059 * @since 1.2
060 * @see java.beans.beancontext.BeanContextChild
061 */
062public class BeanContextChildSupport
063  implements BeanContextChild, BeanContextServicesListener, Serializable
064{
065  static final long serialVersionUID = 6328947014421475877L;
066
067        /**
068         * The peer on which to perform <code>set</code> actions.
069         * This is here so that this class can be used as a peer.
070         * <P>
071         *
072         * When extending this class, this variable will be set to
073         * <code>this</code>.
074         */
075        public BeanContextChild beanContextChildPeer;
076
077        /**
078         * The parent <code>BeanContext</code>.
079         */
080        protected transient BeanContext beanContext;
081
082        /**
083         * If <code>setBeanContext()</code> was vetoed once before, this
084         * is set to <code>true</code> so that the next time, vetoes will
085         * be ignored.
086         */
087        protected transient boolean rejectedSetBCOnce;
088
089        /**
090         * Listeners are registered here and events are fired through here.
091         */
092        protected PropertyChangeSupport pcSupport;
093
094        /**
095         * Listeners are registered here and events are fired through here.
096         */
097        protected VetoableChangeSupport vcSupport;
098
099        /**
100         * Create a new <code>BeanContextChildSupport</code> with itself as the peer.
101         * This is meant to be used when you subclass
102         * <code>BeanContextChildSupport</code> to create your child.
103         */
104        public BeanContextChildSupport()
105  {
106                this (null);
107        }
108
109        /**
110         * Create a new <code>BeanContextChildSupport</code> with the specified peer.
111         * @param peer the peer to use, or <code>null</code> to specify
112         *        <code>this</code>.
113         */
114        public BeanContextChildSupport (BeanContextChild peer)
115  {
116                if (peer == null)
117      {
118        peer = this;
119      }
120
121                beanContextChildPeer = peer;
122                pcSupport = new PropertyChangeSupport (peer);
123                vcSupport = new VetoableChangeSupport (peer);
124        }
125
126        /**
127         * Set the parent <code>BeanContext</code>.
128         * <P>
129         *
130         * When this Object is being added to a new BeanContext or moved
131         * from an old one, a non-null value will be passed in.
132         * <P>
133         *
134         * When this Object is being removed from the current
135         * <code>BeanContext</code>, <code>setBeanContext()</code> will
136         * receive the parameter <code>null</code>.
137         * <P>
138         *
139         * Order of events:
140         * <OL>
141         *   <LI>
142         *     If the new <code>BeanContext</code> is the same as the old
143         *     one, nothing happens.
144         *   </LI>
145         *   <LI>
146         *     If the change has not been rejected or vetoed before, call
147         *     <code>validatePendingSetBeanContext()</code>.  If this call
148         *     returns <code>false</code>, the change is rejected and a
149         *     <code>PropertyVetoException</code> is thrown.
150         *   </LI>
151         *   <LI>
152         *     If the change has not been rejected or vetoed before,
153         *     <code>VetoableChangeEvent</code>s are fired with the name
154         *     <code>"beanContext"</code>, using the
155         *     <code>fireVetoableChange()</code> method.  If a veto
156         *     occurs, reversion events are fired using the same method,
157         *     the change is rejected, and the veto is rethrown.
158         *   </LI>
159         *   <LI>
160         *     <code>releaseBeanContextResources()</code> is called.
161         *   </LI>
162         *   <LI>
163         *     The change is made.
164         *   </LI>
165         *   <LI>
166         *     <code>PropertyChangeEvent</code>s are fired using the
167         *     <code>firePropertyChange()</code> method.
168         *   </LI>
169         *   <LI>
170         *     <code>initializeBeanContextResources()</code> is called.
171         *   </LI>
172         * </OL>
173         * <P>
174         *
175         * @param newBeanContext the new parent for the
176         *        <code>BeanContextChild</code>, or <code>null</code> to
177         *        signify removal from a tree.
178         * @exception PropertyVetoException if the
179         *            <code>BeanContextChild</code> implementor does not
180         *            wish to have its parent changed.
181         */
182  public void setBeanContext(BeanContext newBeanContext)
183    throws PropertyVetoException
184  {
185    synchronized (beanContextChildPeer)
186      {
187        if (newBeanContext == beanContext)
188          return;
189
190        if (!rejectedSetBCOnce)
191          {
192            if (!validatePendingSetBeanContext (newBeanContext))
193              {
194                rejectedSetBCOnce = true;
195                throw new PropertyVetoException ("validatePendingSetBeanContext() rejected change",
196                                                 new PropertyChangeEvent(beanContextChildPeer, "beanContext", beanContext, newBeanContext));
197              }
198            
199            try
200              {
201                fireVetoableChange ("beanContext", beanContext, newBeanContext);
202              }
203            catch (PropertyVetoException e)
204              {
205                rejectedSetBCOnce = true;
206                throw e;
207              }
208          }
209
210                        releaseBeanContextResources ();
211
212                        beanContext = newBeanContext;
213                        rejectedSetBCOnce = false;
214
215                        firePropertyChange ("beanContext", beanContext, newBeanContext);
216
217                        initializeBeanContextResources ();
218                }
219        }
220
221        /**
222         * Get the parent <code>BeanContext</code>.
223         * @return the parent <code>BeanContext</code>.
224         */
225        public BeanContext getBeanContext()
226  {
227                return beanContext;
228        }
229
230        /**
231         * Get the peer (or <code>this</code> if there is no peer).
232         * @return the peer, or <code>this</code> if there is no peer.
233         */
234        public BeanContextChild getBeanContextChildPeer() {
235                return beanContextChildPeer;
236        }
237
238        /**
239         * Determine whether there is a peer.
240         * This is true iff <code>getBeanContextChildPeer() == this</code>.
241         * @return whether there is a peer.
242         */
243        public boolean isDelegated() {
244                return beanContextChildPeer == this;
245        }
246
247        /**
248         * Add a listener that will be notified when a specific property changes.
249         * @param propertyName the name of the property to listen on.
250         * @param listener the listener to listen on the property.
251         */
252        public void addPropertyChangeListener(String propertyName, PropertyChangeListener listener) {
253                pcSupport.addPropertyChangeListener(propertyName, listener);
254        }
255
256        /**
257         * Remove a listener to a certain property.
258         * 
259         * @param propertyName the name of the property being listened on.
260         * @param listener the listener listening on the property.
261         */
262        public void removePropertyChangeListener(String propertyName, PropertyChangeListener listener) {
263                pcSupport.removePropertyChangeListener(propertyName, listener);
264        }
265
266        /**
267         * Add a listener that will be notified when a specific property
268         * change is requested (a PropertyVetoException may be thrown) as
269         * well as after the change is successfully made.
270         *
271         * @param propertyName the name of the property to listen on.
272         * @param listener the listener to listen on the property.
273         */
274        public void addVetoableChangeListener(String propertyName, VetoableChangeListener listener) {
275                vcSupport.addVetoableChangeListener(propertyName, listener);
276        }
277
278        /**
279         * Remove a listener to a certain property.
280         *
281         * @param propertyName the name of the property being listened on
282         * @param listener the listener listening on the property.
283         */
284        public void removeVetoableChangeListener(String propertyName, VetoableChangeListener listener) {
285                vcSupport.removeVetoableChangeListener(propertyName, listener);
286        }
287
288        /**
289         * Fire a property change.
290         *
291         * @param propertyName the name of the property that changed
292         * @param oldVal the old value of the property
293         * @param newVal the new value of the property
294         */
295        public void firePropertyChange(String propertyName, Object oldVal, Object newVal) {
296                pcSupport.firePropertyChange(propertyName, oldVal, newVal);
297        }
298
299        /**
300         * Fire a vetoable property change.
301         *
302         * @param propertyName the name of the property that changed
303         * @param oldVal the old value of the property
304         * @param newVal the new value of the property
305         * @exception PropertyVetoException if the change is vetoed.
306         */
307        public void fireVetoableChange(String propertyName, Object oldVal, Object newVal)
308                        throws PropertyVetoException {
309                vcSupport.fireVetoableChange(propertyName, oldVal, newVal);
310        }
311
312        /**
313         * Called by <code>BeanContextServices.revokeService()</code> to indicate that a service has been revoked.
314         * If you have a reference to such a service, it should be
315         * discarded and may no longer function properly.
316         * <code>getService()</code> will no longer work on the specified
317         * service class after this event has been fired.
318         * <P>
319         *
320         * <EM>This method is meant to be overriden.</EM>
321         * <code>BeanContextChildSupport</code>'s implementation does
322         * nothing.
323         *
324         * @param event the service revoked event.
325         * @see java.beans.beancontext.BeanContextServices#revokeService(java.lang.Class,java.beans.beancontext.BeanContextServiceProvider,boolean)
326         */
327        public void serviceRevoked(BeanContextServiceRevokedEvent event) {
328        }
329
330        /**
331         * Called by <code>BeanContextServices</code> whenever a service is made available.
332         * <P>
333         *
334         * <EM>This method is meant to be overriden.</EM>
335         * <code>BeanContextChildSupport</code>'s implementation does
336         * nothing.
337         *
338         * @param event the service revoked event, with useful information
339         *        about the new service.
340         */
341        public void serviceAvailable(BeanContextServiceAvailableEvent event) {
342        }
343
344        /**
345         * Called by <code>setBeanContext()</code> to determine whether the set should be rejected.
346         * <P>
347         *
348         * <EM>This method is meant to be overriden.</EM>
349         * <code>BeanContextChildSupport</code>'s implementation simply
350         * returns <code>true</code>.
351         *
352         * @param newBeanContext the new parent.
353         * @return whether to allow the parent to be changed to the new
354         *         value.
355         */
356        public boolean validatePendingSetBeanContext(BeanContext newBeanContext) {
357                return true;
358        }
359
360        /**
361         * Called by <code>setBeanContext()</code> to release resources of a what will soon no longer be the parent.
362         * <P>
363         *
364         * <EM>This method is meant to be overriden.</EM>
365         * <code>BeanContextChildSupport</code>'s implementation does
366         * nothing.
367         */
368        protected void releaseBeanContextResources() {
369        }
370
371        /**
372         * Called by <code>setBeanContext()</code> to grab resources when the parent has been set.
373         * <P>
374         *
375         * <EM>This method is meant to be overriden.</EM>
376         * <code>BeanContextChildSupport</code>'s implementation does
377         * nothing.
378         */
379        protected void initializeBeanContextResources() {
380        }
381}