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
26package java.beans.beancontext;
27
28import java.awt.Component;
29import java.awt.Container;
30
31import java.beans.Beans;
32import java.beans.AppletInitializer;
33
34import java.beans.DesignMode;
35
36import java.beans.PropertyChangeEvent;
37import java.beans.PropertyChangeListener;
38import java.beans.PropertyChangeSupport;
39
40import java.beans.VetoableChangeListener;
41import java.beans.VetoableChangeSupport;
42import java.beans.PropertyVetoException;
43
44import java.beans.Visibility;
45
46import java.io.IOException;
47import java.io.InputStream;
48import java.io.ObjectInputStream;
49import java.io.ObjectOutputStream;
50import java.io.Serializable;
51
52import java.net.URL;
53
54import java.util.ArrayList;
55import java.util.Collection;
56import java.util.HashMap;
57import java.util.Iterator;
58import java.util.Locale;
59import 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 */
74public 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, ClassNotFoundException {
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}
1421