1 /*
   2  * Copyright (c) 1997, 2014, 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 {@code BeanContext} we are
  89      *                  supplying an implementation for,
  90      *                  or {@code null}
  91      *                  if this object is its own peer
  92      * @param lcle      The current Locale for this BeanContext. If
  93      *                  {@code lcle} is {@code null}, the default locale
  94      *                  is assigned to the {@code BeanContext} instance.
  95      * @param dTime     The initial state,
  96      *                  {@code true} if in design mode,
  97      *                  {@code false} 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 {@code BeanContext} we
 116      *                  are supplying an implementation for,
 117      *                  or {@code null} if this object is its own peer
 118      * @param lcle      The current Locale for this {@code BeanContext}. If
 119      *                  {@code lcle} is {@code null}, the default locale
 120      *                  is assigned to the {@code BeanContext} instance.
 121      * @param dtime     The initial state, {@code true}
 122      *                  if in design mode,
 123      *                  {@code false} 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 {@code null} if this object
 137      *                  is its own peer
 138      * @param lcle      The current Locale for this
 139      *                  {@code BeanContext}. If
 140      *                  {@code lcle} is {@code null},
 141      *                  the default locale
 142      *                  is assigned to the {@code BeanContext}
 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 {@code BeanContext} we are
 155      *                  supplying an implementation for,
 156      *                  or {@code null} 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 {@code BeanContext} 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 {@code BeanContext}.
 184      * </p>
 185      * <p>
 186      * The semantics of the beanName parameter are defined by java.beans.Beans.instantiate.
 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      * {@code BeanContext} is empty.
 217      * A {@code BeanContext} 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 {@code BeanContext}.
 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 {@code BeanContext}.
 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 {@code BeanContext} instances
 254      * currently nested in this {@code BeanContext}.
 255      * @return an {@code Iterator} of the nested children
 256      */
 257     public Iterator<Object> iterator() {
 258         synchronized(children) {
 259             return new BCSIterator(children.keySet().iterator());
 260         }
 261     }
 262 
 263     /**
 264      * Gets all JavaBean or {@code BeanContext}
 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 {@code BeanContext} 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<Object> {
 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         @SuppressWarnings("serial") // Not statically typed as Serializable
 342         private           Object   child;
 343         @SuppressWarnings("serial") // Not statically typed as Serializable
 344         private           Object   proxyPeer;
 345 
 346         private transient boolean  removePending;
 347     }
 348 
 349     /**
 350      * <p>
 351      * Subclasses can override this method to insert their own subclass
 352      * of Child without having to override add() or the other Collection
 353      * methods that add children to the set.
 354      * </p>
 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      * @return Subtype-specific subclass of Child without overriding collection methods
 358      */
 359 
 360     protected BCSChild createBCSChild(Object targetChild, Object peer) {
 361         return new BCSChild(targetChild, peer);
 362     }
 363 
 364     /************************************************************************/
 365 
 366     /**
 367      * Adds/nests a child within this {@code BeanContext}.
 368      * <p>
 369      * Invoked as a side effect of java.beans.Beans.instantiate().
 370      * If the child object is not valid for adding then this method
 371      * throws an IllegalStateException.
 372      * </p>
 373      *
 374      *
 375      * @param targetChild The child objects to nest
 376      * within this {@code BeanContext}
 377      * @return true if the child was added successfully.
 378      * @see #validatePendingAdd
 379      */
 380     public boolean add(Object targetChild) {
 381 
 382         if (targetChild == null) throw new IllegalArgumentException();
 383 
 384         // The specification requires that we do nothing if the child
 385         // is already nested herein.
 386 
 387         if (children.containsKey(targetChild)) return false; // test before locking
 388 
 389         synchronized(BeanContext.globalHierarchyLock) {
 390             if (children.containsKey(targetChild)) return false; // check again
 391 
 392             if (!validatePendingAdd(targetChild)) {
 393                 throw new IllegalStateException();
 394             }
 395 
 396 
 397             // The specification requires that we invoke setBeanContext() on the
 398             // newly added child if it implements the java.beans.beancontext.BeanContextChild interface
 399 
 400             BeanContextChild cbcc  = getChildBeanContextChild(targetChild);
 401             BeanContextChild  bccp = null;
 402 
 403             synchronized(targetChild) {
 404 
 405                 if (targetChild instanceof BeanContextProxy) {
 406                     bccp = ((BeanContextProxy)targetChild).getBeanContextProxy();
 407 
 408                     if (bccp == null) throw new NullPointerException("BeanContextPeer.getBeanContextProxy()");
 409                 }
 410 
 411                 BCSChild bcsc  = createBCSChild(targetChild, bccp);
 412                 BCSChild pbcsc = null;
 413 
 414                 synchronized (children) {
 415                     children.put(targetChild, bcsc);
 416 
 417                     if (bccp != null) children.put(bccp, pbcsc = createBCSChild(bccp, targetChild));
 418                 }
 419 
 420                 if (cbcc != null) synchronized(cbcc) {
 421                     try {
 422                         cbcc.setBeanContext(getBeanContextPeer());
 423                     } catch (PropertyVetoException pve) {
 424 
 425                         synchronized (children) {
 426                             children.remove(targetChild);
 427 
 428                             if (bccp != null) children.remove(bccp);
 429                         }
 430 
 431                         throw new IllegalStateException();
 432                     }
 433 
 434                     cbcc.addPropertyChangeListener("beanContext", childPCL);
 435                     cbcc.addVetoableChangeListener("beanContext", childVCL);
 436                 }
 437 
 438                 Visibility v = getChildVisibility(targetChild);
 439 
 440                 if (v != null) {
 441                     if (okToUseGui)
 442                         v.okToUseGui();
 443                     else
 444                         v.dontUseGui();
 445                 }
 446 
 447                 if (getChildSerializable(targetChild) != null) serializable++;
 448 
 449                 childJustAddedHook(targetChild, bcsc);
 450 
 451                 if (bccp != null) {
 452                     v = getChildVisibility(bccp);
 453 
 454                     if (v != null) {
 455                         if (okToUseGui)
 456                             v.okToUseGui();
 457                         else
 458                             v.dontUseGui();
 459                     }
 460 
 461                     if (getChildSerializable(bccp) != null) serializable++;
 462 
 463                     childJustAddedHook(bccp, pbcsc);
 464                 }
 465 
 466 
 467             }
 468 
 469             // The specification requires that we fire a notification of the change
 470 
 471             fireChildrenAdded(new BeanContextMembershipEvent(getBeanContextPeer(), bccp == null ? new Object[] { targetChild } : new Object[] { targetChild, bccp } ));
 472 
 473         }
 474 
 475         return true;
 476     }
 477 
 478     /**
 479      * Removes a child from this BeanContext.  If the child object is not
 480      * for adding then this method throws an IllegalStateException.
 481      * @param targetChild The child objects to remove
 482      * @see #validatePendingRemove
 483      */
 484     public boolean remove(Object targetChild) {
 485         return remove(targetChild, true);
 486     }
 487 
 488     /**
 489      * internal remove used when removal caused by
 490      * unexpected {@code setBeanContext} or
 491      * by {@code remove()} invocation.
 492      * @param targetChild the JavaBean, BeanContext, or Object to be removed
 493      * @param callChildSetBC used to indicate that
 494      * the child should be notified that it is no
 495      * longer nested in this {@code BeanContext}.
 496      * @return whether or not was present before being removed
 497      */
 498     protected boolean remove(Object targetChild, boolean callChildSetBC) {
 499 
 500         if (targetChild == null) throw new IllegalArgumentException();
 501 
 502         synchronized(BeanContext.globalHierarchyLock) {
 503             if (!containsKey(targetChild)) return false;
 504 
 505             if (!validatePendingRemove(targetChild)) {
 506                 throw new IllegalStateException();
 507             }
 508 
 509             BCSChild bcsc  = children.get(targetChild);
 510             BCSChild pbcsc = null;
 511             Object   peer  = null;
 512 
 513             // we are required to notify the child that it is no longer nested here if
 514             // it implements java.beans.beancontext.BeanContextChild
 515 
 516             synchronized(targetChild) {
 517                 if (callChildSetBC) {
 518                     BeanContextChild cbcc = getChildBeanContextChild(targetChild);
 519                     if (cbcc != null) synchronized(cbcc) {
 520                         cbcc.removePropertyChangeListener("beanContext", childPCL);
 521                         cbcc.removeVetoableChangeListener("beanContext", childVCL);
 522 
 523                         try {
 524                             cbcc.setBeanContext(null);
 525                         } catch (PropertyVetoException pve1) {
 526                             cbcc.addPropertyChangeListener("beanContext", childPCL);
 527                             cbcc.addVetoableChangeListener("beanContext", childVCL);
 528                             throw new IllegalStateException();
 529                         }
 530 
 531                     }
 532                 }
 533 
 534                 synchronized (children) {
 535                     children.remove(targetChild);
 536 
 537                     if (bcsc.isProxyPeer()) {
 538                         pbcsc = children.get(peer = bcsc.getProxyPeer());
 539                         children.remove(peer);
 540                     }
 541                 }
 542 
 543                 if (getChildSerializable(targetChild) != null) serializable--;
 544 
 545                 childJustRemovedHook(targetChild, bcsc);
 546 
 547                 if (peer != null) {
 548                     if (getChildSerializable(peer) != null) serializable--;
 549 
 550                     childJustRemovedHook(peer, pbcsc);
 551                 }
 552             }
 553 
 554             fireChildrenRemoved(new BeanContextMembershipEvent(getBeanContextPeer(), peer == null ? new Object[] { targetChild } : new Object[] { targetChild, peer } ));
 555 
 556         }
 557 
 558         return true;
 559     }
 560 
 561     /**
 562      * Tests to see if all objects in the
 563      * specified {@code Collection} are children of
 564      * this {@code BeanContext}.
 565      * @param c the specified {@code Collection}
 566      *
 567      * @return {@code true} if all objects
 568      * in the collection are children of
 569      * this {@code BeanContext}, false if not.
 570      */
 571     @SuppressWarnings("rawtypes")
 572     public boolean containsAll(Collection c) {
 573         synchronized(children) {
 574             Iterator<?> i = c.iterator();
 575             while (i.hasNext())
 576                 if(!contains(i.next()))
 577                     return false;
 578 
 579             return true;
 580         }
 581     }
 582 
 583     /**
 584      * add Collection to set of Children (Unsupported)
 585      * implementations must synchronized on the hierarchy lock and "children" protected field
 586      * @throws UnsupportedOperationException thrown unconditionally by this implementation
 587      * @return this implementation unconditionally throws {@code UnsupportedOperationException}
 588      */
 589     @SuppressWarnings("rawtypes")
 590     public boolean addAll(Collection c) {
 591         throw new UnsupportedOperationException();
 592     }
 593 
 594     /**
 595      * remove all specified children (Unsupported)
 596      * implementations must synchronized on the hierarchy lock and "children" protected field
 597      * @throws UnsupportedOperationException thrown unconditionally by this implementation
 598      * @return this implementation unconditionally throws {@code UnsupportedOperationException}
 599 
 600      */
 601     @SuppressWarnings("rawtypes")
 602     public boolean removeAll(Collection c) {
 603         throw new UnsupportedOperationException();
 604     }
 605 
 606 
 607     /**
 608      * retain only specified children (Unsupported)
 609      * implementations must synchronized on the hierarchy lock and "children" protected field
 610      * @throws UnsupportedOperationException thrown unconditionally by this implementation
 611      * @return this implementation unconditionally throws {@code UnsupportedOperationException}
 612      */
 613     @SuppressWarnings("rawtypes")
 614     public boolean retainAll(Collection c) {
 615         throw new UnsupportedOperationException();
 616     }
 617 
 618     /**
 619      * clear the children (Unsupported)
 620      * implementations must synchronized on the hierarchy lock and "children" protected field
 621      * @throws UnsupportedOperationException thrown unconditionally by this implementation
 622      */
 623     public void clear() {
 624         throw new UnsupportedOperationException();
 625     }
 626 
 627     /**
 628      * Adds a BeanContextMembershipListener
 629      *
 630      * @param  bcml the BeanContextMembershipListener to add
 631      * @throws NullPointerException if the argument is null
 632      */
 633 
 634     public void addBeanContextMembershipListener(BeanContextMembershipListener bcml) {
 635         if (bcml == null) throw new NullPointerException("listener");
 636 
 637         synchronized(bcmListeners) {
 638             if (bcmListeners.contains(bcml))
 639                 return;
 640             else
 641                 bcmListeners.add(bcml);
 642         }
 643     }
 644 
 645     /**
 646      * Removes a BeanContextMembershipListener
 647      *
 648      * @param  bcml the BeanContextMembershipListener to remove
 649      * @throws NullPointerException if the argument is null
 650      */
 651 
 652     public void removeBeanContextMembershipListener(BeanContextMembershipListener bcml) {
 653         if (bcml == null) throw new NullPointerException("listener");
 654 
 655         synchronized(bcmListeners) {
 656             if (!bcmListeners.contains(bcml))
 657                 return;
 658             else
 659                 bcmListeners.remove(bcml);
 660         }
 661     }
 662 
 663     /**
 664      * @param name the name of the resource requested.
 665      * @param bcc  the child object making the request.
 666      *
 667      * @return  the requested resource as an InputStream
 668      * @throws  NullPointerException if the argument is null
 669      */
 670 
 671     public InputStream getResourceAsStream(String name, BeanContextChild bcc) {
 672         if (name == null) throw new NullPointerException("name");
 673         if (bcc  == null) throw new NullPointerException("bcc");
 674 
 675         if (containsKey(bcc)) {
 676             ClassLoader cl = bcc.getClass().getClassLoader();
 677 
 678             return cl != null ? cl.getResourceAsStream(name)
 679                               : ClassLoader.getSystemResourceAsStream(name);
 680         } else throw new IllegalArgumentException("Not a valid child");
 681     }
 682 
 683     /**
 684      * @param name the name of the resource requested.
 685      * @param bcc  the child object making the request.
 686      *
 687      * @return the requested resource as an InputStream
 688      */
 689 
 690     public URL getResource(String name, BeanContextChild bcc) {
 691         if (name == null) throw new NullPointerException("name");
 692         if (bcc  == null) throw new NullPointerException("bcc");
 693 
 694         if (containsKey(bcc)) {
 695             ClassLoader cl = bcc.getClass().getClassLoader();
 696 
 697             return cl != null ? cl.getResource(name)
 698                               : ClassLoader.getSystemResource(name);
 699         } else throw new IllegalArgumentException("Not a valid child");
 700     }
 701 
 702     /**
 703      * Sets the new design time value for this {@code BeanContext}.
 704      * @param dTime the new designTime value
 705      */
 706     public synchronized void setDesignTime(boolean dTime) {
 707         if (designTime != dTime) {
 708             designTime = dTime;
 709 
 710             firePropertyChange("designMode", Boolean.valueOf(!dTime), Boolean.valueOf(dTime));
 711         }
 712     }
 713 
 714 
 715     /**
 716      * Reports whether or not this object is in
 717      * currently in design time mode.
 718      * @return {@code true} if in design time mode,
 719      * {@code false} if not
 720      */
 721     public synchronized boolean isDesignTime() { return designTime; }
 722 
 723     /**
 724      * Sets the locale of this BeanContext.
 725      * @param newLocale the new locale. This method call will have
 726      *        no effect if newLocale is {@code null}.
 727      * @throws PropertyVetoException if the new value is rejected
 728      */
 729     public synchronized void setLocale(Locale newLocale) throws PropertyVetoException {
 730 
 731         if ((locale != null && !locale.equals(newLocale)) && newLocale != null) {
 732             Locale old = locale;
 733 
 734             fireVetoableChange("locale", old, newLocale); // throws
 735 
 736             locale = newLocale;
 737 
 738             firePropertyChange("locale", old, newLocale);
 739         }
 740     }
 741 
 742     /**
 743      * Gets the locale for this {@code BeanContext}.
 744      *
 745      * @return the current Locale of the {@code BeanContext}
 746      */
 747     public synchronized Locale getLocale() { return locale; }
 748 
 749     /**
 750      * <p>
 751      * This method is typically called from the environment in order to determine
 752      * if the implementor "needs" a GUI.
 753      * </p>
 754      * <p>
 755      * The algorithm used herein tests the BeanContextPeer, and its current children
 756      * to determine if they are either Containers, Components, or if they implement
 757      * Visibility and return needsGui() == true.
 758      * </p>
 759      * @return {@code true} if the implementor needs a GUI
 760      */
 761     public synchronized boolean needsGui() {
 762         BeanContext bc = getBeanContextPeer();
 763 
 764         if (bc != this) {
 765             if (bc instanceof Visibility) return ((Visibility)bc).needsGui();
 766 
 767             if (bc instanceof Container || bc instanceof Component)
 768                 return true;
 769         }
 770 
 771         synchronized(children) {
 772             for (Iterator<Object> i = children.keySet().iterator(); i.hasNext();) {
 773                 Object c = i.next();
 774 
 775                 try {
 776                         return ((Visibility)c).needsGui();
 777                     } catch (ClassCastException cce) {
 778                         // do nothing ...
 779                     }
 780 
 781                     if (c instanceof Container || c instanceof Component)
 782                         return true;
 783             }
 784         }
 785 
 786         return false;
 787     }
 788 
 789     /**
 790      * notify this instance that it may no longer render a GUI.
 791      */
 792 
 793     public synchronized void dontUseGui() {
 794         if (okToUseGui) {
 795             okToUseGui = false;
 796 
 797             // lets also tell the Children that can that they may not use their GUI's
 798             synchronized(children) {
 799                 for (Iterator<Object> i = children.keySet().iterator(); i.hasNext();) {
 800                     Visibility v = getChildVisibility(i.next());
 801 
 802                     if (v != null) v.dontUseGui();
 803                }
 804             }
 805         }
 806     }
 807 
 808     /**
 809      * Notify this instance that it may now render a GUI
 810      */
 811 
 812     public synchronized void okToUseGui() {
 813         if (!okToUseGui) {
 814             okToUseGui = true;
 815 
 816             // lets also tell the Children that can that they may use their GUI's
 817             synchronized(children) {
 818                 for (Iterator<Object> i = children.keySet().iterator(); i.hasNext();) {
 819                     Visibility v = getChildVisibility(i.next());
 820 
 821                     if (v != null) v.okToUseGui();
 822                 }
 823             }
 824         }
 825     }
 826 
 827     /**
 828      * Used to determine if the {@code BeanContext}
 829      * child is avoiding using its GUI.
 830      * @return is this instance avoiding using its GUI?
 831      * @see Visibility
 832      */
 833     public boolean avoidingGui() {
 834         return !okToUseGui && needsGui();
 835     }
 836 
 837     /**
 838      * Is this {@code BeanContext} in the
 839      * process of being serialized?
 840      * @return if this {@code BeanContext} is
 841      * currently being serialized
 842      */
 843     public boolean isSerializing() { return serializing; }
 844 
 845     /**
 846      * Returns an iterator of all children
 847      * of this {@code BeanContext}.
 848      * @return an iterator for all the current BCSChild values
 849      */
 850     protected Iterator<BCSChild> bcsChildren() { synchronized(children) { return children.values().iterator();  } }
 851 
 852     /**
 853      * called by writeObject after defaultWriteObject() but prior to
 854      * serialization of currently serializable children.
 855      *
 856      * This method may be overridden by subclasses to perform custom
 857      * serialization of their state prior to this superclass serializing
 858      * the children.
 859      *
 860      * This method should not however be used by subclasses to replace their
 861      * own implementation (if any) of writeObject().
 862      * @param oos the {@code ObjectOutputStream} to use during serialization
 863      * @throws IOException if serialization failed
 864      */
 865 
 866     protected void bcsPreSerializationHook(ObjectOutputStream oos) throws IOException {
 867     }
 868 
 869     /**
 870      * called by readObject after defaultReadObject() but prior to
 871      * deserialization of any children.
 872      *
 873      * This method may be overridden by subclasses to perform custom
 874      * deserialization of their state prior to this superclass deserializing
 875      * the children.
 876      *
 877      * This method should not however be used by subclasses to replace their
 878      * own implementation (if any) of readObject().
 879      * @param ois the {@code ObjectInputStream} to use during deserialization
 880      * @throws IOException if deserialization failed
 881      * @throws ClassNotFoundException if needed classes are not found
 882      */
 883 
 884     protected void bcsPreDeserializationHook(ObjectInputStream ois) throws IOException, ClassNotFoundException {
 885     }
 886 
 887     /**
 888      * Called by readObject with the newly deserialized child and BCSChild.
 889      * @param child the newly deserialized child
 890      * @param bcsc the newly deserialized BCSChild
 891      */
 892     protected void childDeserializedHook(Object child, BCSChild bcsc) {
 893         synchronized(children) {
 894             children.put(child, bcsc);
 895         }
 896     }
 897 
 898     /**
 899      * Used by writeObject to serialize a Collection.
 900      * @param oos the {@code ObjectOutputStream}
 901      * to use during serialization
 902      * @param coll the {@code Collection} to serialize
 903      * @throws IOException if serialization failed
 904      */
 905     protected final void serialize(ObjectOutputStream oos, Collection<?> coll) throws IOException {
 906         int      count   = 0;
 907         Object[] objects = coll.toArray();
 908 
 909         for (int i = 0; i < objects.length; i++) {
 910             if (objects[i] instanceof Serializable)
 911                 count++;
 912             else
 913                 objects[i] = null;
 914         }
 915 
 916         oos.writeInt(count); // number of subsequent objects
 917 
 918         for (int i = 0; count > 0; i++) {
 919             Object o = objects[i];
 920 
 921             if (o != null) {
 922                 oos.writeObject(o);
 923                 count--;
 924             }
 925         }
 926     }
 927 
 928     /**
 929      * used by readObject to deserialize a collection.
 930      * @param ois the ObjectInputStream to use
 931      * @param coll the Collection
 932      * @throws IOException if deserialization failed
 933      * @throws ClassNotFoundException if needed classes are not found
 934      */
 935     @SuppressWarnings({"rawtypes", "unchecked"})
 936     protected final void deserialize(ObjectInputStream ois, Collection coll) throws IOException, ClassNotFoundException {
 937         int count = 0;
 938 
 939         count = ois.readInt();
 940 
 941         while (count-- > 0) {
 942             coll.add(ois.readObject());
 943         }
 944     }
 945 
 946     /**
 947      * Used to serialize all children of
 948      * this {@code BeanContext}.
 949      * @param oos the {@code ObjectOutputStream}
 950      * to use during serialization
 951      * @throws IOException if serialization failed
 952      */
 953     public final void writeChildren(ObjectOutputStream oos) throws IOException {
 954         if (serializable <= 0) return;
 955 
 956         boolean prev = serializing;
 957 
 958         serializing = true;
 959 
 960         int count = 0;
 961 
 962         synchronized(children) {
 963             Iterator<Map.Entry<Object, BCSChild>> i = children.entrySet().iterator();
 964 
 965             while (i.hasNext() && count < serializable) {
 966                 Map.Entry<Object, BCSChild> entry = i.next();
 967 
 968                 if (entry.getKey() instanceof Serializable) {
 969                     try {
 970                         oos.writeObject(entry.getKey());   // child
 971                         oos.writeObject(entry.getValue()); // BCSChild
 972                     } catch (IOException ioe) {
 973                         serializing = prev;
 974                         throw ioe;
 975                     }
 976                     count++;
 977                 }
 978             }
 979         }
 980 
 981         serializing = prev;
 982 
 983         if (count != serializable) {
 984             throw new IOException("wrote different number of children than expected");
 985         }
 986 
 987     }
 988 
 989     /**
 990      * Serialize the BeanContextSupport, if this instance has a distinct
 991      * peer (that is this object is acting as a delegate for another) then
 992      * the children of this instance are not serialized here due to a
 993      * 'chicken and egg' problem that occurs on deserialization of the
 994      * children at the same time as this instance.
 995      *
 996      * Therefore in situations where there is a distinct peer to this instance
 997      * it should always call writeObject() followed by writeChildren() and
 998      * readObject() followed by readChildren().
 999      *
1000      * @param oos the ObjectOutputStream
1001      */
1002 
1003     private synchronized void writeObject(ObjectOutputStream oos) throws IOException {
1004         serializing = true;
1005 
1006         synchronized (BeanContext.globalHierarchyLock) {
1007             try {
1008                 oos.defaultWriteObject(); // serialize the BeanContextSupport object
1009 
1010                 bcsPreSerializationHook(oos);
1011 
1012                 if (serializable > 0 && this.equals(getBeanContextPeer()))
1013                     writeChildren(oos);
1014 
1015                 serialize(oos, (Collection)bcmListeners);
1016             } finally {
1017                 serializing = false;
1018             }
1019         }
1020     }
1021 
1022     /**
1023      * When an instance of this class is used as a delegate for the
1024      * implementation of the BeanContext protocols (and its subprotocols)
1025      * there exists a 'chicken and egg' problem during deserialization
1026      * @param ois the ObjectInputStream to use
1027      * @throws IOException if deserialization failed
1028      * @throws ClassNotFoundException if needed classes are not found
1029      */
1030 
1031     public final void readChildren(ObjectInputStream ois) throws IOException, ClassNotFoundException {
1032         int count = serializable;
1033 
1034         while (count-- > 0) {
1035             Object                      child = null;
1036             BeanContextSupport.BCSChild bscc  = null;
1037 
1038             try {
1039                 child = ois.readObject();
1040                 bscc  = (BeanContextSupport.BCSChild)ois.readObject();
1041             } catch (IOException ioe) {
1042                 continue;
1043             } catch (ClassNotFoundException cnfe) {
1044                 continue;
1045             }
1046 
1047 
1048             synchronized(child) {
1049                 BeanContextChild bcc = null;
1050 
1051                 try {
1052                     bcc = (BeanContextChild)child;
1053                 } catch (ClassCastException cce) {
1054                     // do nothing;
1055                 }
1056 
1057                 if (bcc != null) {
1058                     try {
1059                         bcc.setBeanContext(getBeanContextPeer());
1060 
1061                        bcc.addPropertyChangeListener("beanContext", childPCL);
1062                        bcc.addVetoableChangeListener("beanContext", childVCL);
1063 
1064                     } catch (PropertyVetoException pve) {
1065                         continue;
1066                     }
1067                 }
1068 
1069                 childDeserializedHook(child, bscc);
1070             }
1071         }
1072     }
1073 
1074     /**
1075      * deserialize contents ... if this instance has a distinct peer the
1076      * children are *not* serialized here, the peer's readObject() must call
1077      * readChildren() after deserializing this instance.
1078      */
1079 
1080     private synchronized void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {
1081 
1082         synchronized(BeanContext.globalHierarchyLock) {
1083             ois.defaultReadObject();
1084 
1085             initialize();
1086 
1087             bcsPreDeserializationHook(ois);
1088 
1089             if (serializable > 0 && this.equals(getBeanContextPeer()))
1090                 readChildren(ois);
1091 
1092             deserialize(ois, bcmListeners = new ArrayList<>(1));
1093         }
1094     }
1095 
1096     /**
1097      * subclasses may envelope to monitor veto child property changes.
1098      */
1099 
1100     public void vetoableChange(PropertyChangeEvent pce) throws PropertyVetoException {
1101         String propertyName = pce.getPropertyName();
1102         Object source       = pce.getSource();
1103 
1104         synchronized(children) {
1105             if ("beanContext".equals(propertyName) &&
1106                 containsKey(source)                    &&
1107                 !getBeanContextPeer().equals(pce.getNewValue())
1108             ) {
1109                 if (!validatePendingRemove(source)) {
1110                     throw new PropertyVetoException("current BeanContext vetoes setBeanContext()", pce);
1111                 } else children.get(source).setRemovePending(true);
1112             }
1113         }
1114     }
1115 
1116     /**
1117      * subclasses may envelope to monitor child property changes.
1118      */
1119 
1120     public void propertyChange(PropertyChangeEvent pce) {
1121         String propertyName = pce.getPropertyName();
1122         Object source       = pce.getSource();
1123 
1124         synchronized(children) {
1125             if ("beanContext".equals(propertyName) &&
1126                 containsKey(source)                    &&
1127                 children.get(source).isRemovePending()) {
1128                 BeanContext bc = getBeanContextPeer();
1129 
1130                 if (bc.equals(pce.getOldValue()) && !bc.equals(pce.getNewValue())) {
1131                     remove(source, false);
1132                 } else {
1133                     children.get(source).setRemovePending(false);
1134                 }
1135             }
1136         }
1137     }
1138 
1139     /**
1140      * <p>
1141      * Subclasses of this class may override, or envelope, this method to
1142      * add validation behavior for the BeanContext to examine child objects
1143      * immediately prior to their being added to the BeanContext.
1144      * </p>
1145      *
1146      * @param targetChild the child to create the Child on behalf of
1147      * @return true iff the child may be added to this BeanContext, otherwise false.
1148      */
1149 
1150     protected boolean validatePendingAdd(Object targetChild) {
1151         return true;
1152     }
1153 
1154     /**
1155      * <p>
1156      * Subclasses of this class may override, or envelope, this method to
1157      * add validation behavior for the BeanContext to examine child objects
1158      * immediately prior to their being removed from the BeanContext.
1159      * </p>
1160      *
1161      * @param targetChild the child to create the Child on behalf of
1162      * @return true iff the child may be removed from this BeanContext, otherwise false.
1163      */
1164 
1165     protected boolean validatePendingRemove(Object targetChild) {
1166         return true;
1167     }
1168 
1169     /**
1170      * subclasses may override this method to simply extend add() semantics
1171      * after the child has been added and before the event notification has
1172      * occurred. The method is called with the child synchronized.
1173      * @param child the child
1174      * @param bcsc the BCSChild
1175      */
1176 
1177     protected void childJustAddedHook(Object child, BCSChild bcsc) {
1178     }
1179 
1180     /**
1181      * subclasses may override this method to simply extend remove() semantics
1182      * after the child has been removed and before the event notification has
1183      * occurred. The method is called with the child synchronized.
1184      * @param child the child
1185      * @param bcsc the BCSChild
1186      */
1187 
1188     protected void childJustRemovedHook(Object child, BCSChild bcsc) {
1189     }
1190 
1191     /**
1192      * Gets the Component (if any) associated with the specified child.
1193      * @param child the specified child
1194      * @return the Component (if any) associated with the specified child.
1195      */
1196     protected static final Visibility getChildVisibility(Object child) {
1197         try {
1198             return (Visibility)child;
1199         } catch (ClassCastException cce) {
1200             return null;
1201         }
1202     }
1203 
1204     /**
1205      * Gets the Serializable (if any) associated with the specified Child
1206      * @param child the specified child
1207      * @return the Serializable (if any) associated with the specified Child
1208      */
1209     protected static final Serializable getChildSerializable(Object child) {
1210         try {
1211             return (Serializable)child;
1212         } catch (ClassCastException cce) {
1213             return null;
1214         }
1215     }
1216 
1217     /**
1218      * Gets the PropertyChangeListener
1219      * (if any) of the specified child
1220      * @param child the specified child
1221      * @return the PropertyChangeListener (if any) of the specified child
1222      */
1223     protected static final PropertyChangeListener getChildPropertyChangeListener(Object child) {
1224         try {
1225             return (PropertyChangeListener)child;
1226         } catch (ClassCastException cce) {
1227             return null;
1228         }
1229     }
1230 
1231     /**
1232      * Gets the VetoableChangeListener
1233      * (if any) of the specified child
1234      * @param child the specified child
1235      * @return the VetoableChangeListener (if any) of the specified child
1236      */
1237     protected static final VetoableChangeListener getChildVetoableChangeListener(Object child) {
1238         try {
1239             return (VetoableChangeListener)child;
1240         } catch (ClassCastException cce) {
1241             return null;
1242         }
1243     }
1244 
1245     /**
1246      * Gets the BeanContextMembershipListener
1247      * (if any) of the specified child
1248      * @param child the specified child
1249      * @return the BeanContextMembershipListener (if any) of the specified child
1250      */
1251     protected static final BeanContextMembershipListener getChildBeanContextMembershipListener(Object child) {
1252         try {
1253             return (BeanContextMembershipListener)child;
1254         } catch (ClassCastException cce) {
1255             return null;
1256         }
1257     }
1258 
1259     /**
1260      * Gets the BeanContextChild (if any) of the specified child
1261      * @param child the specified child
1262      * @return  the BeanContextChild (if any) of the specified child
1263      * @throws  IllegalArgumentException if child implements both BeanContextChild and BeanContextProxy
1264      */
1265     protected static final BeanContextChild getChildBeanContextChild(Object child) {
1266         try {
1267             BeanContextChild bcc = (BeanContextChild)child;
1268 
1269             if (child instanceof BeanContextChild && child instanceof BeanContextProxy)
1270                 throw new IllegalArgumentException("child cannot implement both BeanContextChild and BeanContextProxy");
1271             else
1272                 return bcc;
1273         } catch (ClassCastException cce) {
1274             try {
1275                 return ((BeanContextProxy)child).getBeanContextProxy();
1276             } catch (ClassCastException cce1) {
1277                 return null;
1278             }
1279         }
1280     }
1281 
1282     /**
1283      * Fire a BeanContextshipEvent on the BeanContextMembershipListener interface
1284      * @param bcme the event to fire
1285      */
1286 
1287     protected final void fireChildrenAdded(BeanContextMembershipEvent bcme) {
1288         Object[] copy;
1289 
1290         synchronized(bcmListeners) { copy = bcmListeners.toArray(); }
1291 
1292         for (int i = 0; i < copy.length; i++)
1293             ((BeanContextMembershipListener)copy[i]).childrenAdded(bcme);
1294     }
1295 
1296     /**
1297      * Fire a BeanContextshipEvent on the BeanContextMembershipListener interface
1298      * @param bcme the event to fire
1299      */
1300 
1301     protected final void fireChildrenRemoved(BeanContextMembershipEvent bcme) {
1302         Object[] copy;
1303 
1304         synchronized(bcmListeners) { copy = bcmListeners.toArray(); }
1305 
1306         for (int i = 0; i < copy.length; i++)
1307             ((BeanContextMembershipListener)copy[i]).childrenRemoved(bcme);
1308     }
1309 
1310     /**
1311      * protected method called from constructor and readObject to initialize
1312      * transient state of BeanContextSupport instance.
1313      *
1314      * This class uses this method to instantiate inner class listeners used
1315      * to monitor PropertyChange and VetoableChange events on children.
1316      *
1317      * subclasses may envelope this method to add their own initialization
1318      * behavior
1319      */
1320 
1321     protected synchronized void initialize() {
1322         children     = new HashMap<>(serializable + 1);
1323         bcmListeners = new ArrayList<>(1);
1324 
1325         childPCL = new PropertyChangeListener() {
1326 
1327             /*
1328              * this adaptor is used by the BeanContextSupport class to forward
1329              * property changes from a child to the BeanContext, avoiding
1330              * accidential serialization of the BeanContext by a badly
1331              * behaved Serializable child.
1332              */
1333 
1334             public void propertyChange(PropertyChangeEvent pce) {
1335                 BeanContextSupport.this.propertyChange(pce);
1336             }
1337         };
1338 
1339         childVCL = new VetoableChangeListener() {
1340 
1341             /*
1342              * this adaptor is used by the BeanContextSupport class to forward
1343              * vetoable changes from a child to the BeanContext, avoiding
1344              * accidential serialization of the BeanContext by a badly
1345              * behaved Serializable child.
1346              */
1347 
1348             public void vetoableChange(PropertyChangeEvent pce) throws PropertyVetoException {
1349                 BeanContextSupport.this.vetoableChange(pce);
1350              }
1351         };
1352     }
1353 
1354     /**
1355      * Gets a copy of the this BeanContext's children.
1356      * @return a copy of the current nested children
1357      */
1358     protected final Object[] copyChildren() {
1359         synchronized(children) { return children.keySet().toArray(); }
1360     }
1361 
1362     /**
1363      * Tests to see if two class objects,
1364      * or their names are equal.
1365      * @param first the first object
1366      * @param second the second object
1367      * @return true if equal, false if not
1368      */
1369     protected static final boolean classEquals(Class<?> first, Class<?> second) {
1370         return first.equals(second) || first.getName().equals(second.getName());
1371     }
1372 
1373 
1374     /*
1375      * fields
1376      */
1377 
1378 
1379     /**
1380      * all accesses to the {@code protected HashMap children} field
1381      * shall be synchronized on that object.
1382      */
1383     protected transient HashMap<Object, BCSChild>         children;
1384 
1385     private             int             serializable  = 0; // children serializable
1386 
1387     /**
1388      * all accesses to the {@code protected ArrayList bcmListeners} field
1389      * shall be synchronized on that object.
1390      */
1391     protected transient ArrayList<BeanContextMembershipListener> bcmListeners;
1392 
1393     //
1394 
1395     /**
1396      * The current locale of this BeanContext.
1397      */
1398     protected           Locale          locale;
1399 
1400     /**
1401      * A {@code boolean} indicating if this
1402      * instance may now render a GUI.
1403      */
1404     protected           boolean         okToUseGui;
1405 
1406 
1407     /**
1408      * A {@code boolean} indicating whether or not
1409      * this object is currently in design time mode.
1410      */
1411     protected           boolean         designTime;
1412 
1413     /*
1414      * transient
1415      */
1416 
1417     private transient PropertyChangeListener childPCL;
1418 
1419     private transient VetoableChangeListener childVCL;
1420 
1421     private transient boolean                serializing;
1422 }