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