1 /*
   2  * Copyright (c) 1997, 2004, Oracle and/or its affiliates. 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.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * 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 }