src/share/classes/java/beans/beancontext/BeanContextSupport.java
author duke
Sat, 01 Dec 2007 00:00:00 +0000
changeset 0 37a05a11f281
child 1238 57914fd9382f
child 2395 00cd9dc3c2b5
permissions -rw-r--r--
Initial load
     1 /*
     2  * Copyright 1997-2004 Sun Microsystems, Inc.  All Rights Reserved.
     3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
     4  *
     5  * This code is free software; you can redistribute it and/or modify it
     6  * under the terms of the GNU General Public License version 2 only, as
     7  * published by the Free Software Foundation.  Sun designates this
     8  * particular file as subject to the "Classpath" exception as provided
     9  * by Sun in the LICENSE file that accompanied this code.
    10  *
    11  * This code is distributed in the hope that it will be useful, but WITHOUT
    12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
    13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
    14  * version 2 for more details (a copy is included in the LICENSE file that
    15  * accompanied this code).
    16  *
    17  * You should have received a copy of the GNU General Public License version
    18  * 2 along with this work; if not, write to the Free Software Foundation,
    19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
    20  *
    21  * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
    22  * CA 95054 USA or visit www.sun.com if you need additional information or
    23  * have any questions.
    24  */
    25 
    26 package java.beans.beancontext;
    27 
    28 import java.awt.Component;
    29 import java.awt.Container;
    30 
    31 import java.beans.Beans;
    32 import java.beans.AppletInitializer;
    33 
    34 import java.beans.DesignMode;
    35 
    36 import java.beans.PropertyChangeEvent;
    37 import java.beans.PropertyChangeListener;
    38 import java.beans.PropertyChangeSupport;
    39 
    40 import java.beans.VetoableChangeListener;
    41 import java.beans.VetoableChangeSupport;
    42 import java.beans.PropertyVetoException;
    43 
    44 import java.beans.Visibility;
    45 
    46 import java.io.IOException;
    47 import java.io.InputStream;
    48 import java.io.ObjectInputStream;
    49 import java.io.ObjectOutputStream;
    50 import java.io.Serializable;
    51 
    52 import java.net.URL;
    53 
    54 import java.util.ArrayList;
    55 import java.util.Collection;
    56 import java.util.HashMap;
    57 import java.util.Iterator;
    58 import java.util.Locale;
    59 import java.util.Map;
    60 
    61 
    62 /**
    63  * This helper class provides a utility implementation of the
    64  * java.beans.beancontext.BeanContext interface.
    65  * </p>
    66  * <p>
    67  * Since this class directly implements the BeanContext interface, the class
    68  * can, and is intended to be used either by subclassing this implementation,
    69  * or via ad-hoc delegation of an instance of this class from another.
    70  * </p>
    71  *
    72  * @author Laurence P. G. Cable
    73  * @since 1.2
    74  */
    75 public class      BeanContextSupport extends BeanContextChildSupport
    76        implements BeanContext,
    77                   Serializable,
    78                   PropertyChangeListener,
    79                   VetoableChangeListener {
    80 
    81     // Fix for bug 4282900 to pass JCK regression test
    82     static final long serialVersionUID = -4879613978649577204L;
    83 
    84     /**
    85      *
    86      * Construct a BeanContextSupport instance
    87      *
    88      *
    89      * @param peer      The peer <tt>BeanContext</tt> we are
    90      *                  supplying an implementation for,
    91      *                  or <tt>null</tt>
    92      *                  if this object is its own peer
    93      * @param lcle      The current Locale for this BeanContext. If
    94      *                  <tt>lcle</tt> is <tt>null</tt>, the default locale
    95      *                  is assigned to the <tt>BeanContext</tt> instance.
    96      * @param dTime     The initial state,
    97      *                  <tt>true</tt> if in design mode,
    98      *                  <tt>false</tt> if runtime.
    99      * @param visible   The initial visibility.
   100      * @see java.util.Locale#getDefault()
   101      * @see java.util.Locale#setDefault(java.util.Locale)
   102      */
   103     public BeanContextSupport(BeanContext peer, Locale lcle, boolean dTime, boolean visible) {
   104         super(peer);
   105 
   106         locale          = lcle != null ? lcle : Locale.getDefault();
   107         designTime      = dTime;
   108         okToUseGui      = visible;
   109 
   110         initialize();
   111     }
   112 
   113     /**
   114      * Create an instance using the specified Locale and design mode.
   115      *
   116      * @param peer      The peer <tt>BeanContext</tt> we
   117      *                  are supplying an implementation for,
   118      *                  or <tt>null</tt> if this object is its own peer
   119      * @param lcle      The current Locale for this <tt>BeanContext</tt>. If
   120      *                  <tt>lcle</tt> is <tt>null</tt>, the default locale
   121      *                  is assigned to the <tt>BeanContext</tt> instance.
   122      * @param dtime     The initial state, <tt>true</tt>
   123      *                  if in design mode,
   124      *                  <tt>false</tt> if runtime.
   125      * @see java.util.Locale#getDefault()
   126      * @see java.util.Locale#setDefault(java.util.Locale)
   127      */
   128     public BeanContextSupport(BeanContext peer, Locale lcle, boolean dtime) {
   129         this (peer, lcle, dtime, true);
   130     }
   131 
   132     /**
   133      * Create an instance using the specified locale
   134      *
   135      * @param peer      The peer BeanContext we are
   136      *                  supplying an implementation for,
   137      *                  or <tt>null</tt> if this object
   138      *                  is its own peer
   139      * @param lcle      The current Locale for this
   140      *                  <tt>BeanContext</tt>. If
   141      *                  <tt>lcle</tt> is <tt>null</tt>,
   142      *                  the default locale
   143      *                  is assigned to the <tt>BeanContext</tt>
   144      *                  instance.
   145      * @see java.util.Locale#getDefault()
   146      * @see java.util.Locale#setDefault(java.util.Locale)
   147      */
   148     public BeanContextSupport(BeanContext peer, Locale lcle) {
   149         this (peer, lcle, false, true);
   150     }
   151 
   152     /**
   153      * Create an instance using with a default locale
   154      *
   155      * @param peer      The peer <tt>BeanContext</tt> we are
   156      *                  supplying an implementation for,
   157      *                  or <tt>null</tt> if this object
   158      *                  is its own peer
   159      */
   160     public BeanContextSupport(BeanContext peer) {
   161         this (peer, null, false, true);
   162     }
   163 
   164     /**
   165      * Create an instance that is not a delegate of another object
   166      */
   167 
   168     public BeanContextSupport() {
   169         this (null, null, false, true);
   170     }
   171 
   172     /**
   173      * Gets the instance of <tt>BeanContext</tt> that
   174      * this object is providing the implementation for.
   175      * @return the BeanContext instance
   176      */
   177     public BeanContext getBeanContextPeer() { return (BeanContext)getBeanContextChildPeer(); }
   178 
   179     /**
   180      * <p>
   181      * The instantiateChild method is a convenience hook
   182      * in BeanContext to simplify
   183      * the task of instantiating a Bean, nested,
   184      * into a <tt>BeanContext</tt>.
   185      * </p>
   186      * <p>
   187      * The semantics of the beanName parameter are defined by java.beans.Beans.instantate.
   188      * </p>
   189      *
   190      * @param beanName the name of the Bean to instantiate within this BeanContext
   191      * @throws IOException if there is an I/O error when the bean is being deserialized
   192      * @throws ClassNotFoundException if the class
   193      * identified by the beanName parameter is not found
   194      * @return the new object
   195      */
   196     public Object instantiateChild(String beanName)
   197            throws IOException, ClassNotFoundException {
   198         BeanContext bc = getBeanContextPeer();
   199 
   200         return Beans.instantiate(bc.getClass().getClassLoader(), beanName, bc);
   201     }
   202 
   203     /**
   204      * Gets the number of children currently nested in
   205      * this BeanContext.
   206      *
   207      * @return number of children
   208      */
   209     public int size() {
   210         synchronized(children) {
   211             return children.size();
   212         }
   213     }
   214 
   215     /**
   216      * Reports whether or not this
   217      * <tt>BeanContext</tt> is empty.
   218      * A <tt>BeanContext</tt> is considered
   219      * empty when it contains zero
   220      * nested children.
   221      * @return if there are not children
   222      */
   223     public boolean isEmpty() {
   224         synchronized(children) {
   225             return children.isEmpty();
   226         }
   227     }
   228 
   229     /**
   230      * Determines whether or not the specified object
   231      * is currently a child of this <tt>BeanContext</tt>.
   232      * @param o the Object in question
   233      * @return if this object is a child
   234      */
   235     public boolean contains(Object o) {
   236         synchronized(children) {
   237             return children.containsKey(o);
   238         }
   239     }
   240 
   241     /**
   242      * Determines whether or not the specified object
   243      * is currently a child of this <tt>BeanContext</tt>.
   244      * @param o the Object in question
   245      * @return if this object is a child
   246      */
   247     public boolean containsKey(Object o) {
   248         synchronized(children) {
   249             return children.containsKey(o);
   250         }
   251     }
   252 
   253     /**
   254      * Gets all JavaBean or <tt>BeanContext</tt> instances
   255      * currently nested in this <tt>BeanContext</tt>.
   256      * @return an <tt>Iterator</tt> of the nested children
   257      */
   258     public Iterator iterator() {
   259         synchronized(children) {
   260             return new BCSIterator(children.keySet().iterator());
   261         }
   262     }
   263 
   264     /**
   265      * Gets all JavaBean or <tt>BeanContext</tt>
   266      * instances currently nested in this BeanContext.
   267      */
   268     public Object[] toArray() {
   269         synchronized(children) {
   270             return children.keySet().toArray();
   271         }
   272     }
   273 
   274     /**
   275      * Gets an array containing all children of
   276      * this <tt>BeanContext</tt> that match
   277      * the types contained in arry.
   278      * @param arry The array of object
   279      * types that are of interest.
   280      * @return an array of children
   281      */
   282     public Object[] toArray(Object[] arry) {
   283         synchronized(children) {
   284             return children.keySet().toArray(arry);
   285         }
   286     }
   287 
   288 
   289     /************************************************************************/
   290 
   291     /**
   292      * protected final subclass that encapsulates an iterator but implements
   293      * a noop remove() method.
   294      */
   295 
   296     protected static final class BCSIterator implements Iterator {
   297         BCSIterator(Iterator i) { super(); src = i; }
   298 
   299         public boolean hasNext() { return src.hasNext(); }
   300         public Object  next()    { return src.next();    }
   301         public void    remove()  { /* do nothing */      }
   302 
   303         private Iterator src;
   304     }
   305 
   306     /************************************************************************/
   307 
   308     /*
   309      * protected nested class containing per child information, an instance
   310      * of which is associated with each child in the "children" hashtable.
   311      * subclasses can extend this class to include their own per-child state.
   312      *
   313      * Note that this 'value' is serialized with the corresponding child 'key'
   314      * when the BeanContextSupport is serialized.
   315      */
   316 
   317     protected class BCSChild implements Serializable {
   318 
   319     private static final long serialVersionUID = -5815286101609939109L;
   320 
   321         BCSChild(Object bcc, Object peer) {
   322             super();
   323 
   324             child     = bcc;
   325             proxyPeer = peer;
   326         }
   327 
   328         Object  getChild()                  { return child; }
   329 
   330         void    setRemovePending(boolean v) { removePending = v; }
   331 
   332         boolean isRemovePending()           { return removePending; }
   333 
   334         boolean isProxyPeer()               { return proxyPeer != null; }
   335 
   336         Object  getProxyPeer()              { return proxyPeer; }
   337         /*
   338          * fields
   339          */
   340 
   341 
   342         private           Object   child;
   343         private           Object   proxyPeer;
   344 
   345         private transient boolean  removePending;
   346     }
   347 
   348     /**
   349      * <p>
   350      * Subclasses can override this method to insert their own subclass
   351      * of Child without having to override add() or the other Collection
   352      * methods that add children to the set.
   353      * </p>
   354      *
   355      * @param targetChild the child to create the Child on behalf of
   356      * @param peer        the peer if the tragetChild and the peer are related by an implementation of BeanContextProxy
   357      */
   358 
   359     protected BCSChild createBCSChild(Object targetChild, Object peer) {
   360         return new BCSChild(targetChild, peer);
   361     }
   362 
   363     /************************************************************************/
   364 
   365     /**
   366      * Adds/nests a child within this <tt>BeanContext</tt>.
   367      * <p>
   368      * Invoked as a side effect of java.beans.Beans.instantiate().
   369      * If the child object is not valid for adding then this method
   370      * throws an IllegalStateException.
   371      * </p>
   372      *
   373      *
   374      * @param targetChild The child objects to nest
   375      * within this <tt>BeanContext</tt>
   376      * @return true if the child was added successfully.
   377      * @see #validatePendingAdd
   378      */
   379     public boolean add(Object targetChild) {
   380 
   381         if (targetChild == null) throw new IllegalArgumentException();
   382 
   383         // The specification requires that we do nothing if the child
   384         // is already nested herein.
   385 
   386         if (children.containsKey(targetChild)) return false; // test before locking
   387 
   388         synchronized(BeanContext.globalHierarchyLock) {
   389             if (children.containsKey(targetChild)) return false; // check again
   390 
   391             if (!validatePendingAdd(targetChild)) {
   392                 throw new IllegalStateException();
   393             }
   394 
   395 
   396             // The specification requires that we invoke setBeanContext() on the
   397             // newly added child if it implements the java.beans.beancontext.BeanContextChild interface
   398 
   399             BeanContextChild cbcc  = getChildBeanContextChild(targetChild);
   400             BeanContextChild  bccp = null;
   401 
   402             synchronized(targetChild) {
   403 
   404                 if (targetChild instanceof BeanContextProxy) {
   405                     bccp = ((BeanContextProxy)targetChild).getBeanContextProxy();
   406 
   407                     if (bccp == null) throw new NullPointerException("BeanContextPeer.getBeanContextProxy()");
   408                 }
   409 
   410                 BCSChild bcsc  = createBCSChild(targetChild, bccp);
   411                 BCSChild pbcsc = null;
   412 
   413                 synchronized (children) {
   414                     children.put(targetChild, bcsc);
   415 
   416                     if (bccp != null) children.put(bccp, pbcsc = createBCSChild(bccp, targetChild));
   417                 }
   418 
   419                 if (cbcc != null) synchronized(cbcc) {
   420                     try {
   421                         cbcc.setBeanContext(getBeanContextPeer());
   422                     } catch (PropertyVetoException pve) {
   423 
   424                         synchronized (children) {
   425                             children.remove(targetChild);
   426 
   427                             if (bccp != null) children.remove(bccp);
   428                         }
   429 
   430                         throw new IllegalStateException();
   431                     }
   432 
   433                     cbcc.addPropertyChangeListener("beanContext", childPCL);
   434                     cbcc.addVetoableChangeListener("beanContext", childVCL);
   435                 }
   436 
   437                 Visibility v = getChildVisibility(targetChild);
   438 
   439                 if (v != null) {
   440                     if (okToUseGui)
   441                         v.okToUseGui();
   442                     else
   443                         v.dontUseGui();
   444                 }
   445 
   446                 if (getChildSerializable(targetChild) != null) serializable++;
   447 
   448                 childJustAddedHook(targetChild, bcsc);
   449 
   450                 if (bccp != null) {
   451                     v = getChildVisibility(bccp);
   452 
   453                     if (v != null) {
   454                         if (okToUseGui)
   455                             v.okToUseGui();
   456                         else
   457                             v.dontUseGui();
   458                     }
   459 
   460                     if (getChildSerializable(bccp) != null) serializable++;
   461 
   462                     childJustAddedHook(bccp, pbcsc);
   463                 }
   464 
   465 
   466             }
   467 
   468             // The specification requires that we fire a notification of the change
   469 
   470             fireChildrenAdded(new BeanContextMembershipEvent(getBeanContextPeer(), bccp == null ? new Object[] { targetChild } : new Object[] { targetChild, bccp } ));
   471 
   472         }
   473 
   474         return true;
   475     }
   476 
   477     /**
   478      * Removes a child from this BeanContext.  If the child object is not
   479      * for adding then this method throws an IllegalStateException.
   480      * @param targetChild The child objects to remove
   481      * @see #validatePendingRemove
   482      */
   483     public boolean remove(Object targetChild) {
   484         return remove(targetChild, true);
   485     }
   486 
   487     /**
   488      * internal remove used when removal caused by
   489      * unexpected <tt>setBeanContext</tt> or
   490      * by <tt>remove()</tt> invocation.
   491      * @param targetChild the JavaBean, BeanContext, or Object to be removed
   492      * @param callChildSetBC used to indicate that
   493      * the child should be notified that it is no
   494      * longer nested in this <tt>BeanContext</tt>.
   495      */
   496     protected boolean remove(Object targetChild, boolean callChildSetBC) {
   497 
   498         if (targetChild == null) throw new IllegalArgumentException();
   499 
   500         synchronized(BeanContext.globalHierarchyLock) {
   501             if (!containsKey(targetChild)) return false;
   502 
   503             if (!validatePendingRemove(targetChild)) {
   504                 throw new IllegalStateException();
   505             }
   506 
   507             BCSChild bcsc  = (BCSChild)children.get(targetChild);
   508             BCSChild pbcsc = null;
   509             Object   peer  = null;
   510 
   511             // we are required to notify the child that it is no longer nested here if
   512             // it implements java.beans.beancontext.BeanContextChild
   513 
   514             synchronized(targetChild) {
   515                 if (callChildSetBC) {
   516                     BeanContextChild cbcc = getChildBeanContextChild(targetChild);
   517                     if (cbcc != null) synchronized(cbcc) {
   518                         cbcc.removePropertyChangeListener("beanContext", childPCL);
   519                         cbcc.removeVetoableChangeListener("beanContext", childVCL);
   520 
   521                         try {
   522                             cbcc.setBeanContext(null);
   523                         } catch (PropertyVetoException pve1) {
   524                             cbcc.addPropertyChangeListener("beanContext", childPCL);
   525                             cbcc.addVetoableChangeListener("beanContext", childVCL);
   526                             throw new IllegalStateException();
   527                         }
   528 
   529                     }
   530                 }
   531 
   532                 synchronized (children) {
   533                     children.remove(targetChild);
   534 
   535                     if (bcsc.isProxyPeer()) {
   536                         pbcsc = (BCSChild)children.get(peer = bcsc.getProxyPeer());
   537                         children.remove(peer);
   538                     }
   539                 }
   540 
   541                 if (getChildSerializable(targetChild) != null) serializable--;
   542 
   543                 childJustRemovedHook(targetChild, bcsc);
   544 
   545                 if (peer != null) {
   546                     if (getChildSerializable(peer) != null) serializable--;
   547 
   548                     childJustRemovedHook(peer, pbcsc);
   549                 }
   550             }
   551 
   552             fireChildrenRemoved(new BeanContextMembershipEvent(getBeanContextPeer(), peer == null ? new Object[] { targetChild } : new Object[] { targetChild, peer } ));
   553 
   554         }
   555 
   556         return true;
   557     }
   558 
   559     /**
   560      * Tests to see if all objects in the
   561      * specified <tt>Collection</tt> are children of
   562      * this <tt>BeanContext</tt>.
   563      * @param c the specified <tt>Collection</tt>
   564      *
   565      * @return <tt>true</tt> if all objects
   566      * in the collection are children of
   567      * this <tt>BeanContext</tt>, false if not.
   568      */
   569     public boolean containsAll(Collection c) {
   570         synchronized(children) {
   571             Iterator i = c.iterator();
   572             while (i.hasNext())
   573                 if(!contains(i.next()))
   574                     return false;
   575 
   576             return true;
   577         }
   578     }
   579 
   580     /**
   581      * add Collection to set of Children (Unsupported)
   582      * implementations must synchronized on the hierarchy lock and "children" protected field
   583      * @throws UnsupportedOperationException
   584      */
   585     public boolean addAll(Collection c) {
   586         throw new UnsupportedOperationException();
   587     }
   588 
   589     /**
   590      * remove all specified children (Unsupported)
   591      * implementations must synchronized on the hierarchy lock and "children" protected field
   592      * @throws UnsupportedOperationException
   593      */
   594     public boolean removeAll(Collection c) {
   595         throw new UnsupportedOperationException();
   596     }
   597 
   598 
   599     /**
   600      * retain only specified children (Unsupported)
   601      * implementations must synchronized on the hierarchy lock and "children" protected field
   602      * @throws UnsupportedOperationException
   603      */
   604     public boolean retainAll(Collection c) {
   605         throw new UnsupportedOperationException();
   606     }
   607 
   608     /**
   609      * clear the children (Unsupported)
   610      * implementations must synchronized on the hierarchy lock and "children" protected field
   611      * @throws UnsupportedOperationException
   612      */
   613     public void clear() {
   614         throw new UnsupportedOperationException();
   615     }
   616 
   617     /**
   618      * Adds a BeanContextMembershipListener
   619      *
   620      * @param  bcml the BeanContextMembershipListener to add
   621      * @throws NullPointerException
   622      */
   623 
   624     public void addBeanContextMembershipListener(BeanContextMembershipListener bcml) {
   625         if (bcml == null) throw new NullPointerException("listener");
   626 
   627         synchronized(bcmListeners) {
   628             if (bcmListeners.contains(bcml))
   629                 return;
   630             else
   631                 bcmListeners.add(bcml);
   632         }
   633     }
   634 
   635     /**
   636      * Removes a BeanContextMembershipListener
   637      *
   638      * @param  bcml the BeanContextMembershipListener to remove
   639      * @throws NullPointerException
   640      */
   641 
   642     public void removeBeanContextMembershipListener(BeanContextMembershipListener bcml) {
   643         if (bcml == null) throw new NullPointerException("listener");
   644 
   645         synchronized(bcmListeners) {
   646             if (!bcmListeners.contains(bcml))
   647                 return;
   648             else
   649                 bcmListeners.remove(bcml);
   650         }
   651     }
   652 
   653     /**
   654      * @param name the name of the resource requested.
   655      * @param bcc  the child object making the request.
   656      *
   657      * @return  the requested resource as an InputStream
   658      * @throws  NullPointerException
   659      */
   660 
   661     public InputStream getResourceAsStream(String name, BeanContextChild bcc) {
   662         if (name == null) throw new NullPointerException("name");
   663         if (bcc  == null) throw new NullPointerException("bcc");
   664 
   665         if (containsKey(bcc)) {
   666             ClassLoader cl = bcc.getClass().getClassLoader();
   667 
   668             return cl != null ? cl.getResourceAsStream(name)
   669                               : ClassLoader.getSystemResourceAsStream(name);
   670         } else throw new IllegalArgumentException("Not a valid child");
   671     }
   672 
   673     /**
   674      * @param name the name of the resource requested.
   675      * @param bcc  the child object making the request.
   676      *
   677      * @return the requested resource as an InputStream
   678      */
   679 
   680     public URL getResource(String name, BeanContextChild bcc) {
   681         if (name == null) throw new NullPointerException("name");
   682         if (bcc  == null) throw new NullPointerException("bcc");
   683 
   684         if (containsKey(bcc)) {
   685             ClassLoader cl = bcc.getClass().getClassLoader();
   686 
   687             return cl != null ? cl.getResource(name)
   688                               : ClassLoader.getSystemResource(name);
   689         } else throw new IllegalArgumentException("Not a valid child");
   690     }
   691 
   692     /**
   693      * Sets the new design time value for this <tt>BeanContext</tt>.
   694      * @param dTime the new designTime value
   695      */
   696     public synchronized void setDesignTime(boolean dTime) {
   697         if (designTime != dTime) {
   698             designTime = dTime;
   699 
   700             firePropertyChange("designMode", Boolean.valueOf(!dTime), Boolean.valueOf(dTime));
   701         }
   702     }
   703 
   704 
   705     /**
   706      * Reports whether or not this object is in
   707      * currently in design time mode.
   708      * @return <tt>true</tt> if in design time mode,
   709      * <tt>false</tt> if not
   710      */
   711     public synchronized boolean isDesignTime() { return designTime; }
   712 
   713     /**
   714      * Sets the locale of this BeanContext.
   715      * @param newLocale the new locale. This method call will have
   716      *        no effect if newLocale is <CODE>null</CODE>.
   717      * @throws PropertyVetoException if the new value is rejected
   718      */
   719     public synchronized void setLocale(Locale newLocale) throws PropertyVetoException {
   720 
   721         if ((locale != null && !locale.equals(newLocale)) && newLocale != null) {
   722             Locale old = locale;
   723 
   724             fireVetoableChange("locale", old, newLocale); // throws
   725 
   726             locale = newLocale;
   727 
   728             firePropertyChange("locale", old, newLocale);
   729         }
   730     }
   731 
   732     /**
   733      * Gets the locale for this <tt>BeanContext</tt>.
   734      *
   735      * @return the current Locale of the <tt>BeanContext</tt>
   736      */
   737     public synchronized Locale getLocale() { return locale; }
   738 
   739     /**
   740      * <p>
   741      * This method is typically called from the environment in order to determine
   742      * if the implementor "needs" a GUI.
   743      * </p>
   744      * <p>
   745      * The algorithm used herein tests the BeanContextPeer, and its current children
   746      * to determine if they are either Containers, Components, or if they implement
   747      * Visibility and return needsGui() == true.
   748      * </p>
   749      * @return <tt>true</tt> if the implementor needs a GUI
   750      */
   751     public synchronized boolean needsGui() {
   752         BeanContext bc = getBeanContextPeer();
   753 
   754         if (bc != this) {
   755             if (bc instanceof Visibility) return ((Visibility)bc).needsGui();
   756 
   757             if (bc instanceof Container || bc instanceof Component)
   758                 return true;
   759         }
   760 
   761         synchronized(children) {
   762             for (Iterator i = children.keySet().iterator(); i.hasNext();) {
   763                 Object c = i.next();
   764 
   765                 try {
   766                         return ((Visibility)c).needsGui();
   767                     } catch (ClassCastException cce) {
   768                         // do nothing ...
   769                     }
   770 
   771                     if (c instanceof Container || c instanceof Component)
   772                         return true;
   773             }
   774         }
   775 
   776         return false;
   777     }
   778 
   779     /**
   780      * notify this instance that it may no longer render a GUI.
   781      */
   782 
   783     public synchronized void dontUseGui() {
   784         if (okToUseGui) {
   785             okToUseGui = false;
   786 
   787             // lets also tell the Children that can that they may not use their GUI's
   788             synchronized(children) {
   789                 for (Iterator i = children.keySet().iterator(); i.hasNext();) {
   790                     Visibility v = getChildVisibility(i.next());
   791 
   792                     if (v != null) v.dontUseGui();
   793                }
   794             }
   795         }
   796     }
   797 
   798     /**
   799      * Notify this instance that it may now render a GUI
   800      */
   801 
   802     public synchronized void okToUseGui() {
   803         if (!okToUseGui) {
   804             okToUseGui = true;
   805 
   806             // lets also tell the Children that can that they may use their GUI's
   807             synchronized(children) {
   808                 for (Iterator i = children.keySet().iterator(); i.hasNext();) {
   809                     Visibility v = getChildVisibility(i.next());
   810 
   811                     if (v != null) v.okToUseGui();
   812                 }
   813             }
   814         }
   815     }
   816 
   817     /**
   818      * Used to determine if the <tt>BeanContext</tt>
   819      * child is avoiding using its GUI.
   820      * @return is this instance avoiding using its GUI?
   821      * @see Visibility
   822      */
   823     public boolean avoidingGui() {
   824         return !okToUseGui && needsGui();
   825     }
   826 
   827     /**
   828      * Is this <tt>BeanContext</tt> in the
   829      * process of being serialized?
   830      * @return if this <tt>BeanContext</tt> is
   831      * currently being serialized
   832      */
   833     public boolean isSerializing() { return serializing; }
   834 
   835     /**
   836      * Returns an iterator of all children
   837      * of this <tt>BeanContext</tt>.
   838      * @return an iterator for all the current BCSChild values
   839      */
   840     protected Iterator bcsChildren() { synchronized(children) { return children.values().iterator();  } }
   841 
   842     /**
   843      * called by writeObject after defaultWriteObject() but prior to
   844      * serialization of currently serializable children.
   845      *
   846      * This method may be overridden by subclasses to perform custom
   847      * serialization of their state prior to this superclass serializing
   848      * the children.
   849      *
   850      * This method should not however be used by subclasses to replace their
   851      * own implementation (if any) of writeObject().
   852      */
   853 
   854     protected void bcsPreSerializationHook(ObjectOutputStream oos) throws IOException {
   855     }
   856 
   857     /**
   858      * called by readObject after defaultReadObject() but prior to
   859      * deserialization of any children.
   860      *
   861      * This method may be overridden by subclasses to perform custom
   862      * deserialization of their state prior to this superclass deserializing
   863      * the children.
   864      *
   865      * This method should not however be used by subclasses to replace their
   866      * own implementation (if any) of readObject().
   867      */
   868 
   869     protected void bcsPreDeserializationHook(ObjectInputStream ois) throws IOException, ClassNotFoundException {
   870     }
   871 
   872     /**
   873      * Called by readObject with the newly deserialized child and BCSChild.
   874      * @param child the newly deserialized child
   875      * @param bcsc the newly deserialized BCSChild
   876      */
   877     protected void childDeserializedHook(Object child, BCSChild bcsc) {
   878         synchronized(children) {
   879             children.put(child, bcsc);
   880         }
   881     }
   882 
   883     /**
   884      * Used by writeObject to serialize a Collection.
   885      * @param oos the <tt>ObjectOutputStream</tt>
   886      * to use during serialization
   887      * @param coll the <tt>Collection</tt> to serialize
   888      * @throws IOException if serialization failed
   889      */
   890     protected final void serialize(ObjectOutputStream oos, Collection coll) throws IOException {
   891         int      count   = 0;
   892         Object[] objects = coll.toArray();
   893 
   894         for (int i = 0; i < objects.length; i++) {
   895             if (objects[i] instanceof Serializable)
   896                 count++;
   897             else
   898                 objects[i] = null;
   899         }
   900 
   901         oos.writeInt(count); // number of subsequent objects
   902 
   903         for (int i = 0; count > 0; i++) {
   904             Object o = objects[i];
   905 
   906             if (o != null) {
   907                 oos.writeObject(o);
   908                 count--;
   909             }
   910         }
   911     }
   912 
   913     /**
   914      * used by readObject to deserialize a collection.
   915      * @param ois the ObjectInputStream to use
   916      * @param coll the Collection
   917      */
   918     protected final void deserialize(ObjectInputStream ois, Collection coll) throws IOException, ClassNotFoundException {
   919         int count = 0;
   920 
   921         count = ois.readInt();
   922 
   923         while (count-- > 0) {
   924             coll.add(ois.readObject());
   925         }
   926     }
   927 
   928     /**
   929      * Used to serialize all children of
   930      * this <tt>BeanContext</tt>.
   931      * @param oos the <tt>ObjectOutputStream</tt>
   932      * to use during serialization
   933      * @throws IOException if serialization failed
   934      */
   935     public final void writeChildren(ObjectOutputStream oos) throws IOException {
   936         if (serializable <= 0) return;
   937 
   938         boolean prev = serializing;
   939 
   940         serializing = true;
   941 
   942         int count = 0;
   943 
   944         synchronized(children) {
   945             Iterator i = children.entrySet().iterator();
   946 
   947             while (i.hasNext() && count < serializable) {
   948                 Map.Entry entry = (Map.Entry)i.next();
   949 
   950                 if (entry.getKey() instanceof Serializable) {
   951                     try {
   952                         oos.writeObject(entry.getKey());   // child
   953                         oos.writeObject(entry.getValue()); // BCSChild
   954                     } catch (IOException ioe) {
   955                         serializing = prev;
   956                         throw ioe;
   957                     }
   958                     count++;
   959                 }
   960             }
   961         }
   962 
   963         serializing = prev;
   964 
   965         if (count != serializable) {
   966             throw new IOException("wrote different number of children than expected");
   967         }
   968 
   969     }
   970 
   971     /**
   972      * Serialize the BeanContextSupport, if this instance has a distinct
   973      * peer (that is this object is acting as a delegate for another) then
   974      * the children of this instance are not serialized here due to a
   975      * 'chicken and egg' problem that occurs on deserialization of the
   976      * children at the same time as this instance.
   977      *
   978      * Therefore in situations where there is a distinct peer to this instance
   979      * it should always call writeObject() followed by writeChildren() and
   980      * readObject() followed by readChildren().
   981      *
   982      * @param oos the ObjectOutputStream
   983      */
   984 
   985     private synchronized void writeObject(ObjectOutputStream oos) throws IOException, ClassNotFoundException {
   986         serializing = true;
   987 
   988         synchronized (BeanContext.globalHierarchyLock) {
   989             try {
   990                 oos.defaultWriteObject(); // serialize the BeanContextSupport object
   991 
   992                 bcsPreSerializationHook(oos);
   993 
   994                 if (serializable > 0 && this.equals(getBeanContextPeer()))
   995                     writeChildren(oos);
   996 
   997                 serialize(oos, (Collection)bcmListeners);
   998             } finally {
   999                 serializing = false;
  1000             }
  1001         }
  1002     }
  1003 
  1004     /**
  1005      * When an instance of this class is used as a delegate for the
  1006      * implementation of the BeanContext protocols (and its subprotocols)
  1007      * there exists a 'chicken and egg' problem during deserialization
  1008      */
  1009 
  1010     public final void readChildren(ObjectInputStream ois) throws IOException, ClassNotFoundException {
  1011         int count = serializable;
  1012 
  1013         while (count-- > 0) {
  1014             Object                      child = null;
  1015             BeanContextSupport.BCSChild bscc  = null;
  1016 
  1017             try {
  1018                 child = ois.readObject();
  1019                 bscc  = (BeanContextSupport.BCSChild)ois.readObject();
  1020             } catch (IOException ioe) {
  1021                 continue;
  1022             } catch (ClassNotFoundException cnfe) {
  1023                 continue;
  1024             }
  1025 
  1026 
  1027             synchronized(child) {
  1028                 BeanContextChild bcc = null;
  1029 
  1030                 try {
  1031                     bcc = (BeanContextChild)child;
  1032                 } catch (ClassCastException cce) {
  1033                     // do nothing;
  1034                 }
  1035 
  1036                 if (bcc != null) {
  1037                     try {
  1038                         bcc.setBeanContext(getBeanContextPeer());
  1039 
  1040                        bcc.addPropertyChangeListener("beanContext", childPCL);
  1041                        bcc.addVetoableChangeListener("beanContext", childVCL);
  1042 
  1043                     } catch (PropertyVetoException pve) {
  1044                         continue;
  1045                     }
  1046                 }
  1047 
  1048                 childDeserializedHook(child, bscc);
  1049             }
  1050         }
  1051     }
  1052 
  1053     /**
  1054      * deserialize contents ... if this instance has a distinct peer the
  1055      * children are *not* serialized here, the peer's readObject() must call
  1056      * readChildren() after deserializing this instance.
  1057      */
  1058 
  1059     private synchronized void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {
  1060 
  1061         synchronized(BeanContext.globalHierarchyLock) {
  1062             ois.defaultReadObject();
  1063 
  1064             initialize();
  1065 
  1066             bcsPreDeserializationHook(ois);
  1067 
  1068             if (serializable > 0 && this.equals(getBeanContextPeer()))
  1069                 readChildren(ois);
  1070 
  1071             deserialize(ois, bcmListeners = new ArrayList(1));
  1072         }
  1073     }
  1074 
  1075     /**
  1076      * subclasses may envelope to monitor veto child property changes.
  1077      */
  1078 
  1079     public void vetoableChange(PropertyChangeEvent pce) throws PropertyVetoException {
  1080         String propertyName = pce.getPropertyName();
  1081         Object source       = pce.getSource();
  1082 
  1083         synchronized(children) {
  1084             if ("beanContext".equals(propertyName) &&
  1085                 containsKey(source)                    &&
  1086                 !getBeanContextPeer().equals(pce.getNewValue())
  1087             ) {
  1088                 if (!validatePendingRemove(source)) {
  1089                     throw new PropertyVetoException("current BeanContext vetoes setBeanContext()", pce);
  1090                 } else ((BCSChild)children.get(source)).setRemovePending(true);
  1091             }
  1092         }
  1093     }
  1094 
  1095     /**
  1096      * subclasses may envelope to monitor child property changes.
  1097      */
  1098 
  1099     public void propertyChange(PropertyChangeEvent pce) {
  1100         String propertyName = pce.getPropertyName();
  1101         Object source       = pce.getSource();
  1102 
  1103         synchronized(children) {
  1104             if ("beanContext".equals(propertyName) &&
  1105                 containsKey(source)                    &&
  1106                 ((BCSChild)children.get(source)).isRemovePending()) {
  1107                 BeanContext bc = getBeanContextPeer();
  1108 
  1109                 if (bc.equals(pce.getOldValue()) && !bc.equals(pce.getNewValue())) {
  1110                     remove(source, false);
  1111                 } else {
  1112                     ((BCSChild)children.get(source)).setRemovePending(false);
  1113                 }
  1114             }
  1115         }
  1116     }
  1117 
  1118     /**
  1119      * <p>
  1120      * Subclasses of this class may override, or envelope, this method to
  1121      * add validation behavior for the BeanContext to examine child objects
  1122      * immediately prior to their being added to the BeanContext.
  1123      * </p>
  1124      *
  1125      * @return true iff the child may be added to this BeanContext, otherwise false.
  1126      */
  1127 
  1128     protected boolean validatePendingAdd(Object targetChild) {
  1129         return true;
  1130     }
  1131 
  1132     /**
  1133      * <p>
  1134      * Subclasses of this class may override, or envelope, this method to
  1135      * add validation behavior for the BeanContext to examine child objects
  1136      * immediately prior to their being removed from the BeanContext.
  1137      * </p>
  1138      *
  1139      * @return true iff the child may be removed from this BeanContext, otherwise false.
  1140      */
  1141 
  1142     protected boolean validatePendingRemove(Object targetChild) {
  1143         return true;
  1144     }
  1145 
  1146     /**
  1147      * subclasses may override this method to simply extend add() semantics
  1148      * after the child has been added and before the event notification has
  1149      * occurred. The method is called with the child synchronized.
  1150      */
  1151 
  1152     protected void childJustAddedHook(Object child, BCSChild bcsc) {
  1153     }
  1154 
  1155     /**
  1156      * subclasses may override this method to simply extend remove() semantics
  1157      * after the child has been removed and before the event notification has
  1158      * occurred. The method is called with the child synchronized.
  1159      */
  1160 
  1161     protected void childJustRemovedHook(Object child, BCSChild bcsc) {
  1162     }
  1163 
  1164     /**
  1165      * Gets the Component (if any) associated with the specified child.
  1166      * @param child the specified child
  1167      * @return the Component (if any) associated with the specified child.
  1168      */
  1169     protected static final Visibility getChildVisibility(Object child) {
  1170         try {
  1171             return (Visibility)child;
  1172         } catch (ClassCastException cce) {
  1173             return null;
  1174         }
  1175     }
  1176 
  1177     /**
  1178      * Gets the Serializable (if any) associated with the specified Child
  1179      * @param child the specified child
  1180      * @return the Serializable (if any) associated with the specified Child
  1181      */
  1182     protected static final Serializable getChildSerializable(Object child) {
  1183         try {
  1184             return (Serializable)child;
  1185         } catch (ClassCastException cce) {
  1186             return null;
  1187         }
  1188     }
  1189 
  1190     /**
  1191      * Gets the PropertyChangeListener
  1192      * (if any) of the specified child
  1193      * @param child the specified child
  1194      * @return the PropertyChangeListener (if any) of the specified child
  1195      */
  1196     protected static final PropertyChangeListener getChildPropertyChangeListener(Object child) {
  1197         try {
  1198             return (PropertyChangeListener)child;
  1199         } catch (ClassCastException cce) {
  1200             return null;
  1201         }
  1202     }
  1203 
  1204     /**
  1205      * Gets the VetoableChangeListener
  1206      * (if any) of the specified child
  1207      * @param child the specified child
  1208      * @return the VetoableChangeListener (if any) of the specified child
  1209      */
  1210     protected static final VetoableChangeListener getChildVetoableChangeListener(Object child) {
  1211         try {
  1212             return (VetoableChangeListener)child;
  1213         } catch (ClassCastException cce) {
  1214             return null;
  1215         }
  1216     }
  1217 
  1218     /**
  1219      * Gets the BeanContextMembershipListener
  1220      * (if any) of the specified child
  1221      * @param child the specified child
  1222      * @return the BeanContextMembershipListener (if any) of the specified child
  1223      */
  1224     protected static final BeanContextMembershipListener getChildBeanContextMembershipListener(Object child) {
  1225         try {
  1226             return (BeanContextMembershipListener)child;
  1227         } catch (ClassCastException cce) {
  1228             return null;
  1229         }
  1230     }
  1231 
  1232     /**
  1233      * Gets the BeanContextChild (if any) of the specified child
  1234      * @param child the specified child
  1235      * @return  the BeanContextChild (if any) of the specified child
  1236      * @throws  IllegalArgumentException if child implements both BeanContextChild and BeanContextProxy
  1237      */
  1238     protected static final BeanContextChild getChildBeanContextChild(Object child) {
  1239         try {
  1240             BeanContextChild bcc = (BeanContextChild)child;
  1241 
  1242             if (child instanceof BeanContextChild && child instanceof BeanContextProxy)
  1243                 throw new IllegalArgumentException("child cannot implement both BeanContextChild and BeanContextProxy");
  1244             else
  1245                 return bcc;
  1246         } catch (ClassCastException cce) {
  1247             try {
  1248                 return ((BeanContextProxy)child).getBeanContextProxy();
  1249             } catch (ClassCastException cce1) {
  1250                 return null;
  1251             }
  1252         }
  1253     }
  1254 
  1255     /**
  1256      * Fire a BeanContextshipEvent on the BeanContextMembershipListener interface
  1257      */
  1258 
  1259     protected final void fireChildrenAdded(BeanContextMembershipEvent bcme) {
  1260         Object[] copy;
  1261 
  1262         synchronized(bcmListeners) { copy = bcmListeners.toArray(); }
  1263 
  1264         for (int i = 0; i < copy.length; i++)
  1265             ((BeanContextMembershipListener)copy[i]).childrenAdded(bcme);
  1266     }
  1267 
  1268     /**
  1269      * Fire a BeanContextshipEvent on the BeanContextMembershipListener interface
  1270      */
  1271 
  1272     protected final void fireChildrenRemoved(BeanContextMembershipEvent bcme) {
  1273         Object[] copy;
  1274 
  1275         synchronized(bcmListeners) { copy = bcmListeners.toArray(); }
  1276 
  1277         for (int i = 0; i < copy.length; i++)
  1278             ((BeanContextMembershipListener)copy[i]).childrenRemoved(bcme);
  1279     }
  1280 
  1281     /**
  1282      * protected method called from constructor and readObject to initialize
  1283      * transient state of BeanContextSupport instance.
  1284      *
  1285      * This class uses this method to instantiate inner class listeners used
  1286      * to monitor PropertyChange and VetoableChange events on children.
  1287      *
  1288      * subclasses may envelope this method to add their own initialization
  1289      * behavior
  1290      */
  1291 
  1292     protected synchronized void initialize() {
  1293         children     = new HashMap(serializable + 1);
  1294         bcmListeners = new ArrayList(1);
  1295 
  1296         childPCL = new PropertyChangeListener() {
  1297 
  1298             /*
  1299              * this adaptor is used by the BeanContextSupport class to forward
  1300              * property changes from a child to the BeanContext, avoiding
  1301              * accidential serialization of the BeanContext by a badly
  1302              * behaved Serializable child.
  1303              */
  1304 
  1305             public void propertyChange(PropertyChangeEvent pce) {
  1306                 BeanContextSupport.this.propertyChange(pce);
  1307             }
  1308         };
  1309 
  1310         childVCL = new VetoableChangeListener() {
  1311 
  1312             /*
  1313              * this adaptor is used by the BeanContextSupport class to forward
  1314              * vetoable changes from a child to the BeanContext, avoiding
  1315              * accidential serialization of the BeanContext by a badly
  1316              * behaved Serializable child.
  1317              */
  1318 
  1319             public void vetoableChange(PropertyChangeEvent pce) throws PropertyVetoException {
  1320                 BeanContextSupport.this.vetoableChange(pce);
  1321              }
  1322         };
  1323     }
  1324 
  1325     /**
  1326      * Gets a copy of the this BeanContext's children.
  1327      * @return a copy of the current nested children
  1328      */
  1329     protected final Object[] copyChildren() {
  1330         synchronized(children) { return children.keySet().toArray(); }
  1331     }
  1332 
  1333     /**
  1334      * Tests to see if two class objects,
  1335      * or their names are equal.
  1336      * @param first the first object
  1337      * @param second the second object
  1338      * @return true if equal, false if not
  1339      */
  1340     protected static final boolean classEquals(Class first, Class second) {
  1341         return first.equals(second) || first.getName().equals(second.getName());
  1342     }
  1343 
  1344 
  1345     /*
  1346      * fields
  1347      */
  1348 
  1349 
  1350     /**
  1351      * all accesses to the <code> protected HashMap children </code> field
  1352      * shall be synchronized on that object.
  1353      */
  1354     protected transient HashMap         children;
  1355 
  1356     private             int             serializable  = 0; // children serializable
  1357 
  1358     /**
  1359      * all accesses to the <code> protected ArrayList bcmListeners </code> field
  1360      * shall be synchronized on that object.
  1361      */
  1362     protected transient ArrayList       bcmListeners;
  1363 
  1364     //
  1365 
  1366     /**
  1367      * The current locale of this BeanContext.
  1368      */
  1369     protected           Locale          locale;
  1370 
  1371     /**
  1372      * A <tt>boolean</tt> indicating if this
  1373      * instance may now render a GUI.
  1374      */
  1375     protected           boolean         okToUseGui;
  1376 
  1377 
  1378     /**
  1379      * A <tt>boolean</tt> indicating whether or not
  1380      * this object is currently in design time mode.
  1381      */
  1382     protected           boolean         designTime;
  1383 
  1384     /*
  1385      * transient
  1386      */
  1387 
  1388     private transient PropertyChangeListener childPCL;
  1389 
  1390     private transient VetoableChangeListener childVCL;
  1391 
  1392     private transient boolean                serializing;
  1393 }