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