/*
 * @(#)BeanContextSupport.java	1.42 01/12/03
 *
 * Copyright 2002 Sun Microsystems, Inc. All rights reserved.
 * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
 */

package java.beans.beancontext;

import java.awt.Component;
import java.awt.Container;

import java.beans.Beans;
import java.beans.AppletInitializer;

import java.beans.DesignMode;

import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;

import java.beans.VetoableChangeListener;
import java.beans.VetoableChangeSupport;
import java.beans.PropertyVetoException;

import java.beans.Visibility;

import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;

import java.net.URL;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Locale;
import java.util.Map;


/**
 * This helper class provides a utility implementation of the
 * java.beans.beancontext.BeanContext interface.
 * </p>
 * <p>
 * Since this class directly implements the BeanContext interface, the class
 * can, and is intended to be used either by subclassing this implementation,
 * or via ad-hoc delegation of an instance of this class from another.
 * </p>
 *
 * @author Laurence P. G. Cable
 * @version 1.42, 12/03/01
 * @since 1.2
 */
public class      BeanContextSupport extends BeanContextChildSupport
       implements BeanContext,
		  Serializable,
		  PropertyChangeListener,
		  VetoableChangeListener {

    // Fix for bug 4282900 to pass JCK regression test
    static final long serialVersionUID = -4879613978649577204L;

    /**
     * 
     * Construct a BeanContextSupport instance
     * 
     *
     * @param peer	The peer <tt>BeanContext</tt> we are 
     *                  supplying an implementation for, 
     *                  or <tt>null</tt> 
     *                  if this object is its own peer
     * @param lcle	The current Locale for this BeanContext. If 
     *                  <tt>lcle</tt> is <tt>null</tt>, the default locale 
     *                  is assigned to the <tt>BeanContext</tt> instance. 
     * @param dtime	The initial state, 
     *                  <tt>true</tt> if in design mode, 
     *                  <tt>false</tt> if runtime.
     * @param visible	The initial visibility.
     * @see java.util.Locale#getDefault()      
     * @see java.util.Locale#setDefault(java.util.Locale)  
     */
    public BeanContextSupport(BeanContext peer, Locale lcle, boolean dTime, boolean visible) {
	super(peer);

	locale          = lcle != null ? lcle : Locale.getDefault();
	designTime      = dTime;
	okToUseGui      = visible;

	initialize();
    }

    /**
     * Create an instance using the specified Locale and design mode.
     *
     * @param peer	The peer <tt>BeanContext</tt> we 
     *                  are supplying an implementation for, 
     *                  or <tt>null</tt> if this object is its own peer
     * @param lcle	The current Locale for this <tt>BeanContext</tt>. If 
     *                  <tt>lcle</tt> is <tt>null</tt>, the default locale 
     *                  is assigned to the <tt>BeanContext</tt> instance. 
     * @param dtime	The initial state, <tt>true</tt> 
     *                  if in design mode, 
     *                  <tt>false</tt> if runtime.
     * @see java.util.Locale#getDefault()      
     * @see java.util.Locale#setDefault(java.util.Locale)  
     */
    public BeanContextSupport(BeanContext peer, Locale lcle, boolean dtime) {
	this (peer, lcle, dtime, true);
    }

    /**
     * Create an instance using the specified locale
     *
     * @param peer	The peer BeanContext we are 
     *                  supplying an implementation for, 
     *                  or <tt>null</tt> if this object 
     *                  is its own peer
     * @param lcle	The current Locale for this 
     *                  <tt>BeanContext</tt>. If 
     *                  <tt>lcle</tt> is <tt>null</tt>, 
     *                  the default locale 
     *                  is assigned to the <tt>BeanContext</tt> 
     *                  instance.
     * @see java.util.Locale#getDefault()      
     * @see java.util.Locale#setDefault(java.util.Locale)   
     */
    public BeanContextSupport(BeanContext peer, Locale lcle) {
	this (peer, lcle, false, true);
    }

    /**
     * Create an instance using with a default locale
     *
     * @param peer	The peer <tt>BeanContext</tt> we are 
     *                  supplying an implementation for, 
     *                  or <tt>null</tt> if this object 
     *                  is its own peer
     */
    public BeanContextSupport(BeanContext peer) {
	this (peer, null, false, true);
    }

    /**
     * Create an instance that is not a delegate of another object
     */

    public BeanContextSupport() {
	this (null, null, false, true);
    }

    /**
     * Gets the instance of <tt>BeanContext</tt> that
     * this object is providing the implementation for.
     * @return the BeanContext instance 
     */
    public BeanContext getBeanContextPeer() { return (BeanContext)getBeanContextChildPeer(); }

    /**
     * <p>
     * The instantiateChild method is a convenience hook 
     * in BeanContext to simplify
     * the task of instantiating a Bean, nested, 
     * into a <tt>BeanContext</tt>.
     * </p>
     * <p>
     * The semantics of the beanName parameter are defined by java.beans.Beans.instantate.
     * </p>
     *
     * @param beanName the name of the Bean to instantiate within this BeanContext
     * @throws IOException if there is an I/O error when the bean is being deserialized
     * @throws ClassNotFoundException if the class 
     * identified by the beanName parameter is not found
     * @return the new object
     */
    public Object instantiateChild(String beanName)
	   throws IOException, ClassNotFoundException {
	BeanContext bc = getBeanContextPeer();

	return Beans.instantiate(bc.getClass().getClassLoader(), beanName, bc);
    }

    /**
     * Gets the number of children currently nested in 
     * this BeanContext.
     *
     * @return number of children
     */
    public int size() {
	synchronized(children) {
	    return children.size();
	}
    }

    /**
     * Reports whether or not this 
     * <tt>BeanContext</tt> is empty.
     * A <tt>BeanContext</tt> is considered 
     * empty when it contains zero 
     * nested children.
     * @return if there are not children
     */
    public boolean isEmpty() {
	synchronized(children) {
	    return children.isEmpty();
	}
    }

    /**
     * Determines whether or not the specified object 
     * is currently a child of this <tt>BeanContext</tt>.
     * @param o the Object in question
     * @return if this object is a child
     */
    public boolean contains(Object o) {
	synchronized(children) {
	    return children.containsKey(o);
	}
    }

    /**
     * Determines whether or not the specified object 
     * is currently a child of this <tt>BeanContext</tt>.
     * @param o the Object in question
     * @return if this object is a child
     */
    public boolean containsKey(Object o) {
	synchronized(children) {
	    return children.containsKey(o);
	}
    }

    /**
     * Gets all JavaBean or <tt>BeanContext</tt> instances 
     * currently nested in this <tt>BeanContext</tt>.
     * @return an <tt>Iterator</tt> of the nested children
     */
    public Iterator iterator() {
	synchronized(children) {
	    return new BCSIterator(children.keySet().iterator());
	}
    }

    /**
     * Gets all JavaBean or <tt>BeanContext</tt> 
     * instances currently nested in this BeanContext.
     */
    public Object[] toArray() {
	synchronized(children) {
	    return children.keySet().toArray();
	}
    }

    /**
     * Gets an array containing all children of 
     * this <tt>BeanContext</tt> that match
     * the types contained in arry.
     * @param arry The array of object 
     * types that are of interest.
     * @return an array of children
     */
    public Object[] toArray(Object[] arry) {
	synchronized(children) {
	    return children.keySet().toArray(arry);
	}
    }


    /************************************************************************/

    /**
     * protected final subclass that encapsulates an iterator but implements
     * a noop remove() method.
     */

    protected static final class BCSIterator implements Iterator {
	BCSIterator(Iterator i) { super(); src = i; }

	public boolean hasNext() { return src.hasNext(); }
	public Object  next()    { return src.next();    }
	public void    remove()  { /* do nothing */      }

	private Iterator src;
    }

    /************************************************************************/

    /*
     * protected nested class containing per child information, an instance
     * of which is associated with each child in the "children" hashtable.
     * subclasses can extend this class to include their own per-child state.
     *
     * Note that this 'value' is serialized with the corresponding child 'key'
     * when the BeanContextSupport is serialized.
     */

    protected class BCSChild implements Serializable {
	BCSChild(Object bcc, Object peer) {
	    super();

	    child     = bcc;
	    proxyPeer = peer;
	}

	Object  getChild()	            { return child; }

	void    setRemovePending(boolean v) { removePending = v; }

	boolean isRemovePending()           { return removePending; }

        boolean isProxyPeer()		    { return proxyPeer != null; }

	Object  getProxyPeer()		    { return proxyPeer; }
	/*
	 * fields
	 */


	private		  Object   child;
	private           Object   proxyPeer;

	private transient boolean  removePending; 
    }

    /**
     * <p>
     * Subclasses can override this method to insert their own subclass
     * of Child without having to override add() or the other Collection
     * methods that add children to the set.
     * </p>
     *
     * @param targetChild the child to create the Child on behalf of
     * @param peer        the peer if the tragetChild and the peer are related by an implementation of BeanContextProxy
     */

    protected BCSChild createBCSChild(Object targetChild, Object peer) {
	return new BCSChild(targetChild, peer);
    }

    /************************************************************************/

    /**
     * Adds/nests a child within this <tt>BeanContext</tt>.
     * <p>
     * Invoked as a side effect of java.beans.Beans.instantiate().
     * If the child object is not valid for adding then this method
     * throws an IllegalStateException.
     * </p>
     *
     *
     * @param targetChild The child objects to nest 
     * within this <tt>BeanContext</tt>
     * @return true if the child was added successfully.
     * @see #validatePendingAdd
     */
    public boolean add(Object targetChild) {

	if (targetChild == null) throw new IllegalArgumentException();

	// The specification requires that we do nothing if the child
	// is already nested herein.

	if (children.containsKey(targetChild)) return false; // test before locking

	synchronized(BeanContext.globalHierarchyLock) {
	    if (children.containsKey(targetChild)) return false; // check again

	    if (!validatePendingAdd(targetChild)) {
	        throw new IllegalStateException();
	    }


	    // The specification requires that we invoke setBeanContext() on the
	    // newly added child if it implements the java.beans.beancontext.BeanContextChild interface

	    BeanContextChild cbcc  = getChildBeanContextChild(targetChild);
	    BeanContextChild  bccp = null;

	    synchronized(targetChild) {

		if (targetChild instanceof BeanContextProxy) {
		    bccp = ((BeanContextProxy)targetChild).getBeanContextProxy();

		    if (bccp == null) throw new NullPointerException("BeanContextPeer.getBeanContextProxy()");
		}

	        BCSChild bcsc  = createBCSChild(targetChild, bccp);
		BCSChild pbcsc = null;

	        synchronized (children) {
		    children.put(targetChild, bcsc);

		    if (bccp != null) children.put(bccp, pbcsc = createBCSChild(bccp, targetChild));
		}

	        if (cbcc != null) synchronized(cbcc) {
		    try {
	                cbcc.setBeanContext(getBeanContextPeer());
	            } catch (PropertyVetoException pve) {

		        synchronized (children) {
			    children.remove(targetChild);

			    if (bccp != null) children.remove(bccp);
			}

	                throw new IllegalStateException();
	            }

	            cbcc.addPropertyChangeListener("beanContext", childPCL);
	            cbcc.addVetoableChangeListener("beanContext", childVCL);
		}

	        Visibility v = getChildVisibility(targetChild);

	        if (v != null) {
	            if (okToUseGui)
	                v.okToUseGui();
	            else
	                v.dontUseGui();
	        }

	        if (getChildSerializable(targetChild) != null) serializable++;

	        childJustAddedHook(targetChild, bcsc);

		if (bccp != null) {
	            v = getChildVisibility(bccp);

	            if (v != null) {
	                if (okToUseGui)
	                    v.okToUseGui();
	                else
	                    v.dontUseGui();
	            }

		    if (getChildSerializable(bccp) != null) serializable++;

	            childJustAddedHook(bccp, pbcsc);
		}


	    }

	    // The specification requires that we fire a notification of the change

	    fireChildrenAdded(new BeanContextMembershipEvent(getBeanContextPeer(), bccp == null ? new Object[] { targetChild } : new Object[] { targetChild, bccp } ));

        }

	return true;
    }

    /**
     * Removes a child from this BeanContext.  If the child object is not
     * for adding then this method throws an IllegalStateException.
     * @param targetChild The child objects to remove
     * @see #validatePendingRemove
     */
    public boolean remove(Object targetChild) {
	return remove(targetChild, true);
    }

    /**
     * internal remove used when removal caused by 
     * unexpected <tt>setBeanContext</tt> or
     * by <tt>remove()</tt> invocation.
     * @param targetChild the JavaBean, BeanContext, or Object to be removed
     * @param callChildSetBC used to indicate that 
     * the child should be notified that it is no 
     * longer nested in this <tt>BeanContext</tt>.
     */
    protected boolean remove(Object targetChild, boolean callChildSetBC) {

	if (targetChild == null) throw new IllegalArgumentException();

	synchronized(BeanContext.globalHierarchyLock) {
	    if (!containsKey(targetChild)) return false;

	    if (!validatePendingRemove(targetChild)) {
	        throw new IllegalStateException();
	    }

            BCSChild bcsc  = (BCSChild)children.get(targetChild);
	    BCSChild pbcsc = null;
	    Object   peer  = null;

	    // we are required to notify the child that it is no longer nested here if
	    // it implements java.beans.beancontext.BeanContextChild

	    synchronized(targetChild) {
	        if (callChildSetBC) {
	            BeanContextChild cbcc = getChildBeanContextChild(targetChild);
	            if (cbcc != null) synchronized(cbcc) {
	                cbcc.removePropertyChangeListener("beanContext", childPCL);
	                cbcc.removeVetoableChangeListener("beanContext", childVCL);

			try {
	                    cbcc.setBeanContext(null);
	                } catch (PropertyVetoException pve1) {
	                    cbcc.addPropertyChangeListener("beanContext", childPCL);
	                    cbcc.addVetoableChangeListener("beanContext", childVCL);
	    	            throw new IllegalStateException();
	                }

		    }
	        }

	        synchronized (children) {
		    children.remove(targetChild);

		    if (bcsc.isProxyPeer()) {
			pbcsc = (BCSChild)children.get(peer = bcsc.getProxyPeer());
			children.remove(peer);
		    }
		}

	        if (getChildSerializable(targetChild) != null) serializable--;

	        childJustRemovedHook(targetChild, bcsc);

		if (peer != null) {
	            if (getChildSerializable(peer) != null) serializable--;

	            childJustRemovedHook(peer, pbcsc);
		}
	    }

	    fireChildrenRemoved(new BeanContextMembershipEvent(getBeanContextPeer(), peer == null ? new Object[] { targetChild } : new Object[] { targetChild, peer } ));

	}

	return true;
    }

    /**
     * Tests to see if all objects in the
     * specified <tt>Collection</tt> are children of
     * this <tt>BeanContext</tt>.
     * @param c the specified <tt>Collection</tt> 
     * 
     * @return <tt>true</tt> if all objects 
     * in the collection are children of 
     * this <tt>BeanContext</tt>, false if not.
     */
    public boolean containsAll(Collection c) {
	synchronized(children) {
	    Iterator i = c.iterator();
	    while (i.hasNext())
	        if(!contains(i.next()))
		    return false;

	    return true;
	}
    }

    /**
     * add Collection to set of Children (Unsupported)
     * implementations must synchronized on the hierarchy lock and "children" protected field
     * @throws UnsupportedOperationException
     */
    public boolean addAll(Collection c) {
	throw new UnsupportedOperationException();
    }

    /**
     * remove all specified children (Unsupported)
     * implementations must synchronized on the hierarchy lock and "children" protected field
     * @throws UnsupportedOperationException
     */
    public boolean removeAll(Collection c) {
	throw new UnsupportedOperationException();
    }


    /**
     * retain only specified children (Unsupported)
     * implementations must synchronized on the hierarchy lock and "children" protected field
     * @throws UnsupportedOperationException
     */
    public boolean retainAll(Collection c) {
	throw new UnsupportedOperationException();
    }

    /**
     * clear the children (Unsupported)
     * implementations must synchronized on the hierarchy lock and "children" protected field
     * @throws UnsupportedOperationException
     */
    public void clear() {
	throw new UnsupportedOperationException();
    }

    /**
     * Adds a BeanContextMembershipListener
     *
     * @param bcml the BeanContextMembershipListener to add
     * @throw NullPointerException
     */

    public void addBeanContextMembershipListener(BeanContextMembershipListener bcml) {
	if (bcml == null) throw new NullPointerException("listener");

	synchronized(bcmListeners) {
	    if (bcmListeners.contains(bcml))
		return;
	    else
	        bcmListeners.add(bcml);
	}
    }

    /**
     * Removes a BeanContextMembershipListener
     *
     * @param bcml the BeanContextMembershipListener to remove
     * @throw NullPointerException
     */

    public void removeBeanContextMembershipListener(BeanContextMembershipListener bcml) {
	if (bcml == null) throw new NullPointerException("listener");

	synchronized(bcmListeners) {
	    if (!bcmListeners.contains(bcml))
		return;
	    else
	        bcmListeners.remove(bcml);
	}
    }

    /**
     * @param name the name of the resource requested.
     * @param child the child object making the request.
     *
     * @return the requested resource as an InputStream
     * @throw  NullPointerException
     */

    public InputStream getResourceAsStream(String name, BeanContextChild bcc) {
	if (name == null) throw new NullPointerException("name");
	if (bcc  == null) throw new NullPointerException("bcc");

	boolean isChild;

	synchronized(children) {
	    isChild = children.containsValue(bcc);
	}

	if (isChild) {
	    ClassLoader cl = bcc.getClass().getClassLoader();

	    return cl != null ? cl.getResourceAsStream(name)
			      : ClassLoader.getSystemResourceAsStream(name);
	} else throw new IllegalArgumentException("Not a valid child");
    }

    /**
     * @param name the name of the resource requested.
     * @param child the child object making the request.
     *
     * @return the requested resource as an InputStream
     */

    public URL getResource(String name, BeanContextChild bcc) {
	if (name == null) throw new NullPointerException("name");
	if (bcc  == null) throw new NullPointerException("bcc");

	boolean isChild;

	synchronized(children) {
	    isChild = children.containsValue(bcc);
	}

	if (isChild) {
	    ClassLoader cl = bcc.getClass().getClassLoader();

	    return cl != null ? cl.getResource(name)
			      : ClassLoader.getSystemResource(name);
	} else throw new IllegalArgumentException("Not a valid child");
    }

    /**
     * Sets the new design time value for this <tt>BeanContext</tt>.
     * @param dTime the new designTime value
     */
    public synchronized void setDesignTime(boolean dTime) {
	if (designTime != dTime) {
	    designTime = dTime;

	    firePropertyChange("designMode", new Boolean(!dTime), new Boolean(dTime));
	}
    }

  
    /**
     * Reports whether or not this object is in
     * currently in design time mode.
     * @return <tt>true</tt> if in design time mode, 
     * <tt>false</tt> if not
     */
    public synchronized boolean isDesignTime() { return designTime; }

    /**
     * Sets the locale of this BeanContext.
     * @param newLocale the new locale. This method call will have
     *        no effect if newLocale is <CODE>null</CODE>.
     * @throws PropertyVetoException if the new value is rejected
     */
    public synchronized void setLocale(Locale newLocale) throws PropertyVetoException {

	if ((locale != null && !locale.equals(newLocale)) || newLocale != null) {
	    Locale old = locale;

	    fireVetoableChange("locale", old, newLocale); // throws

	    locale = newLocale;

	    firePropertyChange("locale", old, newLocale);
	}
    }

    /**
     * Gets the locale for this <tt>BeanContext</tt>.
     *
     * @return the current Locale of the <tt>BeanContext</tt>
     */
    public synchronized Locale getLocale() { return locale; }

    /**
     * <p>
     * This method is typically called from the environment in order to determine
     * if the implementor "needs" a GUI.
     * </p>
     * <p>
     * The algorithm used herein tests the BeanContextPeer, and its current children
     * to determine if they are either Containers, Components, or if they implement
     * Visibility and return needsGui() == true.
     * </p>
     * @return <tt>true</tt> if the implementor needs a GUI
     */
    public synchronized boolean needsGui() {
	BeanContext bc = getBeanContextPeer();

	if (bc != this) {
	    if (bc instanceof Visibility) return ((Visibility)bc).needsGui();

	    if (bc instanceof Container || bc instanceof Component)
		return true;
	}

	synchronized(children) {
	    for (Iterator i = children.keySet().iterator(); i.hasNext();) {
	        Object c = i.next();

	        try {
		        return ((Visibility)c).needsGui();
		    } catch (ClassCastException cce) {
		        // do nothing ...
		    }

		    if (c instanceof Container || c instanceof Component)
		        return true;
	    }
	}

	return false;
    }

    /**
     * notify this instance that it may no longer render a GUI.
     */

    public synchronized void dontUseGui() {
	if (okToUseGui) {
	    okToUseGui = false;

	    // lets also tell the Children that can that they may not use their GUI's
	    synchronized(children) {
	        for (Iterator i = children.keySet().iterator(); i.hasNext();) {
		    Visibility v = getChildVisibility(i.next());

		    if (v != null) v.dontUseGui();
	       }
	    }
	}
    }

    /**
     * Notify this instance that it may now render a GUI
     */

    public synchronized void okToUseGui() {
	if (!okToUseGui) {
	    okToUseGui = true;

	    // lets also tell the Children that can that they may use their GUI's
	    synchronized(children) {
	        for (Iterator i = children.keySet().iterator(); i.hasNext();) {
		    Visibility v = getChildVisibility(i.next());

		    if (v != null) v.okToUseGui();
	        }
	    }
	}
    }

    /**
     * Used to determine if the <tt>BeanContext</tt> 
     * child is avoiding using its GUI.
     * @return is this instance avoiding using its GUI?
     * @see Visibility
     */
    public boolean avoidingGui() {
	return !okToUseGui && needsGui();
    }

    /**
     * Is this <tt>BeanContext</tt> in the 
     * process of being serialized?
     * @return if this <tt>BeanContext</tt> is 
     * currently being serialized
     */
    public boolean isSerializing() { return serializing; }

    /**
     * Returns an iterator of all children 
     * of this <tt>BeanContext</tt>.
     * @return an iterator for all the current BCSChild values 
     */
    protected Iterator bcsChildren() { synchronized(children) { return children.values().iterator();  } }

    /**
     * called by writeObject after defaultWriteObject() but prior to
     * serialization of currently serializable children.
     *
     * This method may be overridden by subclasses to perform custom
     * serialization of their state prior to this superclass serializing
     * the children.
     *
     * This method should not however be used by subclasses to replace their
     * own implementation (if any) of writeObject().
     */

    protected void bcsPreSerializationHook(ObjectOutputStream oos) throws IOException {
    }

    /**
     * called by readObject after defaultReadObject() but prior to
     * deserialization of any children.
     *
     * This method may be overridden by subclasses to perform custom
     * deserialization of their state prior to this superclass deserializing
     * the children.
     *
     * This method should not however be used by subclasses to replace their
     * own implementation (if any) of readObject().
     */

    protected void bcsPreDeserializationHook(ObjectInputStream ois) throws IOException, ClassNotFoundException {
    }

    /**
     * Called by readObject with the newly deserialized child and BCSChild.
     * @param child the newly deserialized child
     * @param bcsc the newly deserialized BCSChild
     */
    protected void childDeserializedHook(Object child, BCSChild bcsc) {
	synchronized(children) {
	    children.put(child, bcsc);
	}
    }

    /**
     * Used by writeObject to serialize a Collection.
     * @param oos the <tt>ObjectOutputStream</tt> 
     * to use during serialization
     * @param coll the <tt>Collection</tt> to serialize
     * @throws IOException if serialization failed
     */
    protected final void serialize(ObjectOutputStream oos, Collection coll) throws IOException {
   	int      count   = 0;
	Object[] objects = coll.toArray();
    
	for (int i = 0; i < objects.length; i++) {
	    if (objects[i] instanceof Serializable)
		count++;
	    else
		objects[i] = null;
	}

        oos.writeInt(count); // number of subsequent objects

	for (int i = 0; count > 0; i++) {
	    Object o = objects[i];

	    if (o != null) {
		oos.writeObject(o);
		count--;
	    }
	}
    }

    /**
     * used by readObject to deserialize a collection.
     * @param ois the ObjectInputStream to use
     * @param coll the Collection
     */
    protected final void deserialize(ObjectInputStream ois, Collection coll) throws IOException, ClassNotFoundException {
	int count = 0;

	count = ois.readInt();

	while (count-- > 0) {
	    coll.add(ois.readObject());
	}
    }

    /** 
     * Used to serialize all children of 
     * this <tt>BeanContext</tt>.
     * @param oos the <tt>ObjectOutputStream</tt> 
     * to use during serialization
     * @throws IOException if serialization failed
     */
    public final void writeChildren(ObjectOutputStream oos) throws IOException {
	if (serializable <= 0) return;

	boolean prev = serializing;

	serializing = true;

	int count = 0;

	synchronized(children) {
	    Iterator i = children.entrySet().iterator();

	    while (i.hasNext() && count < serializable) {
	        Map.Entry entry = (Map.Entry)i.next();

	        if (entry.getKey() instanceof Serializable) {
	      	    try {
		        oos.writeObject(entry.getKey());   // child
		        oos.writeObject(entry.getValue()); // BCSChild
		    } catch (IOException ioe) {
		        serializing = prev;
		        throw ioe;
		    }
		    count++;
	        }
	    }
	}
	
        serializing = prev;

	if (count != serializable) {
	    throw new IOException("wrote different number of children than expected");
	}

    }

    /**
     * Serialize the BeanContextSupport, if this instance has a distinct 
     * peer (that is this object is acting as a delegate for another) then
     * the children of this instance are not serialized here due to a 
     * 'chicken and egg' problem that occurs on deserialization of the 
     * children at the same time as this instance. 
     *
     * Therefore in situations where there is a distinct peer to this instance
     * it should always call writeObject() followed by writeChildren() and
     * readObject() followed by readChildren().
     *
     * @param oos the ObjectOutputStream
     */

    private synchronized void writeObject(ObjectOutputStream oos) throws IOException, ClassNotFoundException {
	serializing = true;

	synchronized (BeanContext.globalHierarchyLock) {
	    try {
	        oos.defaultWriteObject(); // serialize the BeanContextSupport object

	        bcsPreSerializationHook(oos);

	        if (serializable > 0 && this.equals(getBeanContextPeer()))
	            writeChildren(oos);

	        serialize(oos, (Collection)bcmListeners);
	    } finally {
	        serializing = false;
	    }
 	}
    }

    /**
     * When an instance of this class is used as a delegate for the
     * implementation of the BeanContext protocols (and its subprotocols)
     * there exists a 'chicken and egg' problem during deserialization 
     */

    public final void readChildren(ObjectInputStream ois) throws IOException, ClassNotFoundException {
	int count = serializable; 

	while (count-- > 0) {
	    Object                      child = null;
	    BeanContextSupport.BCSChild bscc  = null;
	  
	    try {
	        child = ois.readObject();
	        bscc  = (BeanContextSupport.BCSChild)ois.readObject();
	    } catch (IOException ioe) {
		continue;
	    } catch (ClassNotFoundException cnfe) {
		continue;
	    }


	    synchronized(child) {
		BeanContextChild bcc = null;

		try {
		    bcc = (BeanContextChild)child;
		} catch (ClassCastException cce) {
		    // do nothing;
		}

		if (bcc != null) {
		    try {
			bcc.setBeanContext(getBeanContextPeer());

	               bcc.addPropertyChangeListener("beanContext", childPCL);
	               bcc.addVetoableChangeListener("beanContext", childVCL);
	
		    } catch (PropertyVetoException pve) {
			continue;
		    }
		}

		childDeserializedHook(child, bscc);
	    }
	}
    }

    /**
     * deserialize contents ... if this instance has a distinct peer the
     * children are *not* serialized here, the peer's readObject() must call
     * readChildren() after deserializing this instance.
     */

    private synchronized void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {

	synchronized(BeanContext.globalHierarchyLock) {
	    ois.defaultReadObject();

	    initialize();

	    bcsPreDeserializationHook(ois);

	    if (serializable > 0 && this.equals(getBeanContextPeer()))
	        readChildren(ois);

	    deserialize(ois, bcmListeners = new ArrayList(1));
	}
    }

    /**
     * subclasses may envelope to monitor veto child property changes.
     */

    public void vetoableChange(PropertyChangeEvent pce) throws PropertyVetoException {
	String propertyName = pce.getPropertyName();
	Object source	    = pce.getSource();

	synchronized(children) {
	    if ("beanContext".equals(propertyName) &&
	        containsKey(source)	               &&
	        !getBeanContextPeer().equals(pce.getNewValue())
	    ) {
	        if (!validatePendingRemove(source)) {
		    throw new PropertyVetoException("current BeanContext vetoes setBeanContext()", pce);
	        } else ((BCSChild)children.get(source)).setRemovePending(true);
	    }
	}
    }

    /**
     * subclasses may envelope to monitor child property changes.
     */

    public void propertyChange(PropertyChangeEvent pce) {
	String propertyName = pce.getPropertyName();
	Object source	    = pce.getSource();

	synchronized(children) {
	    if ("beanContext".equals(propertyName) &&
	        containsKey(source)		       &&
	        ((BCSChild)children.get(source)).isRemovePending()) {
	        BeanContext bc = getBeanContextPeer();

	        if (bc.equals(pce.getOldValue()) && !bc.equals(pce.getNewValue())) {
	            remove(source, false);
	        } else {
		    ((BCSChild)children.get(source)).setRemovePending(false);
	        }
	    }
        }
    }

    /**
     * <p>
     * Subclasses of this class may override, or envelope, this method to
     * add validation behavior for the BeanContext to examine child objects
     * immediately prior to their being added to the BeanContext. 
     * </p>
     *
     * @return true iff the child may be added to this BeanContext, otherwise false.
     */

    protected boolean validatePendingAdd(Object targetChild) {
	return true;
    }

    /**
     * <p>
     * Subclasses of this class may override, or envelope, this method to
     * add validation behavior for the BeanContext to examine child objects
     * immediately prior to their being removed from the BeanContext. 
     * </p>
     *
     * @return true iff the child may be removed from this BeanContext, otherwise false.
     */

    protected boolean validatePendingRemove(Object targetChild) {
	return true;
    }

    /**
     * subclasses may override this method to simply extend add() semantics
     * after the child has been added and before the event notification has
     * occurred. The method is called with the child synchronized.
     */

    protected void childJustAddedHook(Object child, BCSChild bcsc) {
    }

    /**
     * subclasses may override this method to simply extend remove() semantics
     * after the child has been removed and before the event notification has
     * occurred. The method is called with the child synchronized.
     */

    protected void childJustRemovedHook(Object child, BCSChild bcsc) {
    }

    /**
     * Gets the Component (if any) associated with the specified child.
     * @param child the specified child
     * @return the Component (if any) associated with the specified child.
     */
    protected static final Visibility getChildVisibility(Object child) {
	try {
	    return (Visibility)child;
	} catch (ClassCastException cce) {
	    return null;
	}
    }
 
    /**
     * Gets the Serializable (if any) associated with the specified Child
     * @param child the specified child
     * @return the Serializable (if any) associated with the specified Child
     */
    protected static final Serializable getChildSerializable(Object child) {
        try {
	    return (Serializable)child;
	} catch (ClassCastException cce) {
	    return null;
	}
    }

    /**
     * Gets the PropertyChangeListener 
     * (if any) of the specified child
     * @param child the specified child
     * @return the PropertyChangeListener (if any) of the specified child
     */
    protected static final PropertyChangeListener getChildPropertyChangeListener(Object child) {
	try {
	    return (PropertyChangeListener)child;
	} catch (ClassCastException cce) {
	    return null;
	}
    }

    /**
     * Gets the VetoableChangeListener 
     * (if any) of the specified child
     * @param child the specified child
     * @return the VetoableChangeListener (if any) of the specified child
     */
    protected static final VetoableChangeListener getChildVetoableChangeListener(Object child) {
	try {
	    return (VetoableChangeListener)child;
	} catch (ClassCastException cce) {
	    return null;
	}
    }

    /**
     * Gets the BeanContextMembershipListener 
     * (if any) of the specified child
     * @param child the specified child
     * @return the BeanContextMembershipListener (if any) of the specified child
     */
    protected static final BeanContextMembershipListener getChildBeanContextMembershipListener(Object child) {
	try {
	    return (BeanContextMembershipListener)child;
	} catch (ClassCastException cce) {
	    return null;
	}
    }

    /**
     * Gets the BeanContextChild (if any) of the specified child
     * @param child the specified child
     * @return the BeanContextChild (if any) of the specified child
     * @throw IllegalArgumentException if child implements both BeanContextChild and BeanContextProxy
     */
    protected static final BeanContextChild getChildBeanContextChild(Object child) {
        try {
	    BeanContextChild bcc = (BeanContextChild)child;

	    if (child instanceof BeanContextChild && child instanceof BeanContextProxy) 
		throw new IllegalArgumentException("child cannot implement both BeanContextChild and BeanContextProxy");
	    else
		return bcc;
	} catch (ClassCastException cce) {
	    try {
		return ((BeanContextProxy)child).getBeanContextProxy();
	    } catch (ClassCastException cce1) {
	        return null;
	    }
	}
    }

    /**
     * Fire a BeanContextshipEvent on the BeanContextMembershipListener interface
     */

    protected final void fireChildrenAdded(BeanContextMembershipEvent bcme) {
	Object[] copy;
							  
	synchronized(bcmListeners) { copy = bcmListeners.toArray(); }

	for (int i = 0; i < copy.length; i++)
	    ((BeanContextMembershipListener)copy[i]).childrenAdded(bcme);
    }

    /**
     * Fire a BeanContextshipEvent on the BeanContextMembershipListener interface
     */

    protected final void fireChildrenRemoved(BeanContextMembershipEvent bcme) {
	Object[] copy;
							  
	synchronized(bcmListeners) { copy = bcmListeners.toArray(); }

	for (int i = 0; i < copy.length; i++)
	    ((BeanContextMembershipListener)copy[i]).childrenRemoved(bcme);
    }

    /**
     * protected method called from constructor and readObject to initialize
     * transient state of BeanContextSupport instance.
     *
     * This class uses this method to instantiate inner class listeners used
     * to monitor PropertyChange and VetoableChange events on children.
     *
     * subclasses may envelope this method to add their own initialization
     * behavior
     */

    protected synchronized void initialize() {
	children     = new HashMap(serializable + 1);
	bcmListeners = new ArrayList(1);

	childPCL = new PropertyChangeListener() {

	    /*
	     * this adaptor is used by the BeanContextSupport class to forward
	     * property changes from a child to the BeanContext, avoiding 
	     * accidential serialization of the BeanContext by a badly 
	     * behaved Serializable child.
	     */

	    public void propertyChange(PropertyChangeEvent pce) {
	        BeanContextSupport.this.propertyChange(pce);
	    }
	};

	childVCL = new VetoableChangeListener() {

	    /*
	     * this adaptor is used by the BeanContextSupport class to forward
	     * vetoable changes from a child to the BeanContext, avoiding 
	     * accidential serialization of the BeanContext by a badly 
	     * behaved Serializable child.
	     */

	    public void vetoableChange(PropertyChangeEvent pce) throws PropertyVetoException {
	        BeanContextSupport.this.vetoableChange(pce);
             }
        };
    }

    /**
     * Gets a copy of the this BeanContext's children.
     * @return a copy of the current nested children
     */
    protected final Object[] copyChildren() {
	synchronized(children) { return children.keySet().toArray(); }
    }

    /**
     * Tests to see if two class objects, 
     * or their names are equal.
     * @param first the first object
     * @param second the second object
     * @return true if equal, false if not
     */
    protected static final boolean classEquals(Class first, Class second) {
	return first.equals(second) || first.getName().equals(second.getName());
    }


    /*
     * fields
     */


    /**
     * all accesses to the <code> protected HashMap children </code> field
     * shall be synchronized on that object.
     */
    protected transient HashMap		children;

    private   	        int 		serializable  = 0; // children serializable

    /**
     * all accesses to the <code> protected ArrayList bcmListeners </code> field
     * shall be synchronized on that object.
     */
    protected transient ArrayList	bcmListeners;

    // 

    /**
     * The current locale of this BeanContext.
     */
    protected 		Locale		locale;

    /**
     * A <tt>boolean</tt> indicating if this 
     * instance may now render a GUI.
     */
    protected 		boolean		okToUseGui;


    /**
     * A <tt>boolean</tt> indicating whether or not 
     * this object is currently in design time mode.
     */
    protected 		boolean		designTime;

    /*
     * transient 
     */

    private transient PropertyChangeListener childPCL;

    private transient VetoableChangeListener childVCL;

    private transient boolean		     serializing;
}
