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