duke@0: /* duke@0: * Copyright 1997-2004 Sun Microsystems, Inc. All Rights Reserved. duke@0: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. duke@0: * duke@0: * This code is free software; you can redistribute it and/or modify it duke@0: * under the terms of the GNU General Public License version 2 only, as duke@0: * published by the Free Software Foundation. Sun designates this duke@0: * particular file as subject to the "Classpath" exception as provided duke@0: * by Sun in the LICENSE file that accompanied this code. duke@0: * duke@0: * This code is distributed in the hope that it will be useful, but WITHOUT duke@0: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or duke@0: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License duke@0: * version 2 for more details (a copy is included in the LICENSE file that duke@0: * accompanied this code). duke@0: * duke@0: * You should have received a copy of the GNU General Public License version duke@0: * 2 along with this work; if not, write to the Free Software Foundation, duke@0: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. duke@0: * duke@0: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, duke@0: * CA 95054 USA or visit www.sun.com if you need additional information or duke@0: * have any questions. duke@0: */ duke@0: duke@0: package java.beans.beancontext; duke@0: duke@0: import java.awt.Component; duke@0: import java.awt.Container; duke@0: duke@0: import java.beans.Beans; duke@0: duke@0: duke@0: import java.beans.PropertyChangeEvent; duke@0: import java.beans.PropertyChangeListener; duke@0: duke@0: import java.beans.VetoableChangeListener; duke@0: import java.beans.PropertyVetoException; duke@0: duke@0: import java.beans.Visibility; duke@0: duke@0: import java.io.IOException; duke@0: import java.io.InputStream; duke@0: import java.io.ObjectInputStream; duke@0: import java.io.ObjectOutputStream; duke@0: import java.io.Serializable; duke@0: duke@0: import java.net.URL; duke@0: duke@0: import java.util.ArrayList; duke@0: import java.util.Collection; duke@0: import java.util.HashMap; duke@0: import java.util.Iterator; duke@0: import java.util.Locale; duke@0: import java.util.Map; duke@0: duke@0: duke@0: /** duke@0: * This helper class provides a utility implementation of the duke@0: * java.beans.beancontext.BeanContext interface. duke@0: *

duke@0: *

duke@0: * Since this class directly implements the BeanContext interface, the class duke@0: * can, and is intended to be used either by subclassing this implementation, duke@0: * or via ad-hoc delegation of an instance of this class from another. duke@0: *

duke@0: * duke@0: * @author Laurence P. G. Cable duke@0: * @since 1.2 duke@0: */ duke@0: public class BeanContextSupport extends BeanContextChildSupport duke@0: implements BeanContext, duke@0: Serializable, duke@0: PropertyChangeListener, duke@0: VetoableChangeListener { duke@0: duke@0: // Fix for bug 4282900 to pass JCK regression test duke@0: static final long serialVersionUID = -4879613978649577204L; duke@0: duke@0: /** duke@0: * duke@0: * Construct a BeanContextSupport instance duke@0: * duke@0: * duke@0: * @param peer The peer BeanContext we are duke@0: * supplying an implementation for, duke@0: * or null duke@0: * if this object is its own peer duke@0: * @param lcle The current Locale for this BeanContext. If duke@0: * lcle is null, the default locale duke@0: * is assigned to the BeanContext instance. duke@0: * @param dTime The initial state, duke@0: * true if in design mode, duke@0: * false if runtime. duke@0: * @param visible The initial visibility. duke@0: * @see java.util.Locale#getDefault() duke@0: * @see java.util.Locale#setDefault(java.util.Locale) duke@0: */ duke@0: public BeanContextSupport(BeanContext peer, Locale lcle, boolean dTime, boolean visible) { duke@0: super(peer); duke@0: duke@0: locale = lcle != null ? lcle : Locale.getDefault(); duke@0: designTime = dTime; duke@0: okToUseGui = visible; duke@0: duke@0: initialize(); duke@0: } duke@0: duke@0: /** duke@0: * Create an instance using the specified Locale and design mode. duke@0: * duke@0: * @param peer The peer BeanContext we duke@0: * are supplying an implementation for, duke@0: * or null if this object is its own peer duke@0: * @param lcle The current Locale for this BeanContext. If duke@0: * lcle is null, the default locale duke@0: * is assigned to the BeanContext instance. duke@0: * @param dtime The initial state, true duke@0: * if in design mode, duke@0: * false if runtime. duke@0: * @see java.util.Locale#getDefault() duke@0: * @see java.util.Locale#setDefault(java.util.Locale) duke@0: */ duke@0: public BeanContextSupport(BeanContext peer, Locale lcle, boolean dtime) { duke@0: this (peer, lcle, dtime, true); duke@0: } duke@0: duke@0: /** duke@0: * Create an instance using the specified locale duke@0: * duke@0: * @param peer The peer BeanContext we are duke@0: * supplying an implementation for, duke@0: * or null if this object duke@0: * is its own peer duke@0: * @param lcle The current Locale for this duke@0: * BeanContext. If duke@0: * lcle is null, duke@0: * the default locale duke@0: * is assigned to the BeanContext duke@0: * instance. duke@0: * @see java.util.Locale#getDefault() duke@0: * @see java.util.Locale#setDefault(java.util.Locale) duke@0: */ duke@0: public BeanContextSupport(BeanContext peer, Locale lcle) { duke@0: this (peer, lcle, false, true); duke@0: } duke@0: duke@0: /** duke@0: * Create an instance using with a default locale duke@0: * duke@0: * @param peer The peer BeanContext we are duke@0: * supplying an implementation for, duke@0: * or null if this object duke@0: * is its own peer duke@0: */ duke@0: public BeanContextSupport(BeanContext peer) { duke@0: this (peer, null, false, true); duke@0: } duke@0: duke@0: /** duke@0: * Create an instance that is not a delegate of another object duke@0: */ duke@0: duke@0: public BeanContextSupport() { duke@0: this (null, null, false, true); duke@0: } duke@0: duke@0: /** duke@0: * Gets the instance of BeanContext that duke@0: * this object is providing the implementation for. duke@0: * @return the BeanContext instance duke@0: */ duke@0: public BeanContext getBeanContextPeer() { return (BeanContext)getBeanContextChildPeer(); } duke@0: duke@0: /** duke@0: *

duke@0: * The instantiateChild method is a convenience hook duke@0: * in BeanContext to simplify duke@0: * the task of instantiating a Bean, nested, duke@0: * into a BeanContext. duke@0: *

duke@0: *

duke@0: * The semantics of the beanName parameter are defined by java.beans.Beans.instantate. duke@0: *

duke@0: * duke@0: * @param beanName the name of the Bean to instantiate within this BeanContext duke@0: * @throws IOException if there is an I/O error when the bean is being deserialized duke@0: * @throws ClassNotFoundException if the class duke@0: * identified by the beanName parameter is not found duke@0: * @return the new object duke@0: */ duke@0: public Object instantiateChild(String beanName) duke@0: throws IOException, ClassNotFoundException { duke@0: BeanContext bc = getBeanContextPeer(); duke@0: duke@0: return Beans.instantiate(bc.getClass().getClassLoader(), beanName, bc); duke@0: } duke@0: duke@0: /** duke@0: * Gets the number of children currently nested in duke@0: * this BeanContext. duke@0: * duke@0: * @return number of children duke@0: */ duke@0: public int size() { duke@0: synchronized(children) { duke@0: return children.size(); duke@0: } duke@0: } duke@0: duke@0: /** duke@0: * Reports whether or not this duke@0: * BeanContext is empty. duke@0: * A BeanContext is considered duke@0: * empty when it contains zero duke@0: * nested children. duke@0: * @return if there are not children duke@0: */ duke@0: public boolean isEmpty() { duke@0: synchronized(children) { duke@0: return children.isEmpty(); duke@0: } duke@0: } duke@0: duke@0: /** duke@0: * Determines whether or not the specified object duke@0: * is currently a child of this BeanContext. duke@0: * @param o the Object in question duke@0: * @return if this object is a child duke@0: */ duke@0: public boolean contains(Object o) { duke@0: synchronized(children) { duke@0: return children.containsKey(o); duke@0: } duke@0: } duke@0: duke@0: /** duke@0: * Determines whether or not the specified object duke@0: * is currently a child of this BeanContext. duke@0: * @param o the Object in question duke@0: * @return if this object is a child duke@0: */ duke@0: public boolean containsKey(Object o) { duke@0: synchronized(children) { duke@0: return children.containsKey(o); duke@0: } duke@0: } duke@0: duke@0: /** duke@0: * Gets all JavaBean or BeanContext instances duke@0: * currently nested in this BeanContext. duke@0: * @return an Iterator of the nested children duke@0: */ duke@0: public Iterator iterator() { duke@0: synchronized(children) { duke@0: return new BCSIterator(children.keySet().iterator()); duke@0: } duke@0: } duke@0: duke@0: /** duke@0: * Gets all JavaBean or BeanContext duke@0: * instances currently nested in this BeanContext. duke@0: */ duke@0: public Object[] toArray() { duke@0: synchronized(children) { duke@0: return children.keySet().toArray(); duke@0: } duke@0: } duke@0: duke@0: /** duke@0: * Gets an array containing all children of duke@0: * this BeanContext that match duke@0: * the types contained in arry. duke@0: * @param arry The array of object duke@0: * types that are of interest. duke@0: * @return an array of children duke@0: */ duke@0: public Object[] toArray(Object[] arry) { duke@0: synchronized(children) { duke@0: return children.keySet().toArray(arry); duke@0: } duke@0: } duke@0: duke@0: duke@0: /************************************************************************/ duke@0: duke@0: /** duke@0: * protected final subclass that encapsulates an iterator but implements duke@0: * a noop remove() method. duke@0: */ duke@0: duke@0: protected static final class BCSIterator implements Iterator { duke@0: BCSIterator(Iterator i) { super(); src = i; } duke@0: duke@0: public boolean hasNext() { return src.hasNext(); } duke@0: public Object next() { return src.next(); } duke@0: public void remove() { /* do nothing */ } duke@0: duke@0: private Iterator src; duke@0: } duke@0: duke@0: /************************************************************************/ duke@0: duke@0: /* duke@0: * protected nested class containing per child information, an instance duke@0: * of which is associated with each child in the "children" hashtable. duke@0: * subclasses can extend this class to include their own per-child state. duke@0: * duke@0: * Note that this 'value' is serialized with the corresponding child 'key' duke@0: * when the BeanContextSupport is serialized. duke@0: */ duke@0: duke@0: protected class BCSChild implements Serializable { duke@0: duke@0: private static final long serialVersionUID = -5815286101609939109L; duke@0: duke@0: BCSChild(Object bcc, Object peer) { duke@0: super(); duke@0: duke@0: child = bcc; duke@0: proxyPeer = peer; duke@0: } duke@0: duke@0: Object getChild() { return child; } duke@0: duke@0: void setRemovePending(boolean v) { removePending = v; } duke@0: duke@0: boolean isRemovePending() { return removePending; } duke@0: duke@0: boolean isProxyPeer() { return proxyPeer != null; } duke@0: duke@0: Object getProxyPeer() { return proxyPeer; } duke@0: /* duke@0: * fields duke@0: */ duke@0: duke@0: duke@0: private Object child; duke@0: private Object proxyPeer; duke@0: duke@0: private transient boolean removePending; duke@0: } duke@0: duke@0: /** duke@0: *

duke@0: * Subclasses can override this method to insert their own subclass duke@0: * of Child without having to override add() or the other Collection duke@0: * methods that add children to the set. duke@0: *

duke@0: * duke@0: * @param targetChild the child to create the Child on behalf of duke@0: * @param peer the peer if the tragetChild and the peer are related by an implementation of BeanContextProxy duke@0: */ duke@0: duke@0: protected BCSChild createBCSChild(Object targetChild, Object peer) { duke@0: return new BCSChild(targetChild, peer); duke@0: } duke@0: duke@0: /************************************************************************/ duke@0: duke@0: /** duke@0: * Adds/nests a child within this BeanContext. duke@0: *

duke@0: * Invoked as a side effect of java.beans.Beans.instantiate(). duke@0: * If the child object is not valid for adding then this method duke@0: * throws an IllegalStateException. duke@0: *

duke@0: * duke@0: * duke@0: * @param targetChild The child objects to nest duke@0: * within this BeanContext duke@0: * @return true if the child was added successfully. duke@0: * @see #validatePendingAdd duke@0: */ duke@0: public boolean add(Object targetChild) { duke@0: duke@0: if (targetChild == null) throw new IllegalArgumentException(); duke@0: duke@0: // The specification requires that we do nothing if the child duke@0: // is already nested herein. duke@0: duke@0: if (children.containsKey(targetChild)) return false; // test before locking duke@0: duke@0: synchronized(BeanContext.globalHierarchyLock) { duke@0: if (children.containsKey(targetChild)) return false; // check again duke@0: duke@0: if (!validatePendingAdd(targetChild)) { duke@0: throw new IllegalStateException(); duke@0: } duke@0: duke@0: duke@0: // The specification requires that we invoke setBeanContext() on the duke@0: // newly added child if it implements the java.beans.beancontext.BeanContextChild interface duke@0: duke@0: BeanContextChild cbcc = getChildBeanContextChild(targetChild); duke@0: BeanContextChild bccp = null; duke@0: duke@0: synchronized(targetChild) { duke@0: duke@0: if (targetChild instanceof BeanContextProxy) { duke@0: bccp = ((BeanContextProxy)targetChild).getBeanContextProxy(); duke@0: duke@0: if (bccp == null) throw new NullPointerException("BeanContextPeer.getBeanContextProxy()"); duke@0: } duke@0: duke@0: BCSChild bcsc = createBCSChild(targetChild, bccp); duke@0: BCSChild pbcsc = null; duke@0: duke@0: synchronized (children) { duke@0: children.put(targetChild, bcsc); duke@0: duke@0: if (bccp != null) children.put(bccp, pbcsc = createBCSChild(bccp, targetChild)); duke@0: } duke@0: duke@0: if (cbcc != null) synchronized(cbcc) { duke@0: try { duke@0: cbcc.setBeanContext(getBeanContextPeer()); duke@0: } catch (PropertyVetoException pve) { duke@0: duke@0: synchronized (children) { duke@0: children.remove(targetChild); duke@0: duke@0: if (bccp != null) children.remove(bccp); duke@0: } duke@0: duke@0: throw new IllegalStateException(); duke@0: } duke@0: duke@0: cbcc.addPropertyChangeListener("beanContext", childPCL); duke@0: cbcc.addVetoableChangeListener("beanContext", childVCL); duke@0: } duke@0: duke@0: Visibility v = getChildVisibility(targetChild); duke@0: duke@0: if (v != null) { duke@0: if (okToUseGui) duke@0: v.okToUseGui(); duke@0: else duke@0: v.dontUseGui(); duke@0: } duke@0: duke@0: if (getChildSerializable(targetChild) != null) serializable++; duke@0: duke@0: childJustAddedHook(targetChild, bcsc); duke@0: duke@0: if (bccp != null) { duke@0: v = getChildVisibility(bccp); duke@0: duke@0: if (v != null) { duke@0: if (okToUseGui) duke@0: v.okToUseGui(); duke@0: else duke@0: v.dontUseGui(); duke@0: } duke@0: duke@0: if (getChildSerializable(bccp) != null) serializable++; duke@0: duke@0: childJustAddedHook(bccp, pbcsc); duke@0: } duke@0: duke@0: duke@0: } duke@0: duke@0: // The specification requires that we fire a notification of the change duke@0: duke@0: fireChildrenAdded(new BeanContextMembershipEvent(getBeanContextPeer(), bccp == null ? new Object[] { targetChild } : new Object[] { targetChild, bccp } )); duke@0: duke@0: } duke@0: duke@0: return true; duke@0: } duke@0: duke@0: /** duke@0: * Removes a child from this BeanContext. If the child object is not duke@0: * for adding then this method throws an IllegalStateException. duke@0: * @param targetChild The child objects to remove duke@0: * @see #validatePendingRemove duke@0: */ duke@0: public boolean remove(Object targetChild) { duke@0: return remove(targetChild, true); duke@0: } duke@0: duke@0: /** duke@0: * internal remove used when removal caused by duke@0: * unexpected setBeanContext or duke@0: * by remove() invocation. duke@0: * @param targetChild the JavaBean, BeanContext, or Object to be removed duke@0: * @param callChildSetBC used to indicate that duke@0: * the child should be notified that it is no duke@0: * longer nested in this BeanContext. duke@0: */ duke@0: protected boolean remove(Object targetChild, boolean callChildSetBC) { duke@0: duke@0: if (targetChild == null) throw new IllegalArgumentException(); duke@0: duke@0: synchronized(BeanContext.globalHierarchyLock) { duke@0: if (!containsKey(targetChild)) return false; duke@0: duke@0: if (!validatePendingRemove(targetChild)) { duke@0: throw new IllegalStateException(); duke@0: } duke@0: duke@0: BCSChild bcsc = (BCSChild)children.get(targetChild); duke@0: BCSChild pbcsc = null; duke@0: Object peer = null; duke@0: duke@0: // we are required to notify the child that it is no longer nested here if duke@0: // it implements java.beans.beancontext.BeanContextChild duke@0: duke@0: synchronized(targetChild) { duke@0: if (callChildSetBC) { duke@0: BeanContextChild cbcc = getChildBeanContextChild(targetChild); duke@0: if (cbcc != null) synchronized(cbcc) { duke@0: cbcc.removePropertyChangeListener("beanContext", childPCL); duke@0: cbcc.removeVetoableChangeListener("beanContext", childVCL); duke@0: duke@0: try { duke@0: cbcc.setBeanContext(null); duke@0: } catch (PropertyVetoException pve1) { duke@0: cbcc.addPropertyChangeListener("beanContext", childPCL); duke@0: cbcc.addVetoableChangeListener("beanContext", childVCL); duke@0: throw new IllegalStateException(); duke@0: } duke@0: duke@0: } duke@0: } duke@0: duke@0: synchronized (children) { duke@0: children.remove(targetChild); duke@0: duke@0: if (bcsc.isProxyPeer()) { duke@0: pbcsc = (BCSChild)children.get(peer = bcsc.getProxyPeer()); duke@0: children.remove(peer); duke@0: } duke@0: } duke@0: duke@0: if (getChildSerializable(targetChild) != null) serializable--; duke@0: duke@0: childJustRemovedHook(targetChild, bcsc); duke@0: duke@0: if (peer != null) { duke@0: if (getChildSerializable(peer) != null) serializable--; duke@0: duke@0: childJustRemovedHook(peer, pbcsc); duke@0: } duke@0: } duke@0: duke@0: fireChildrenRemoved(new BeanContextMembershipEvent(getBeanContextPeer(), peer == null ? new Object[] { targetChild } : new Object[] { targetChild, peer } )); duke@0: duke@0: } duke@0: duke@0: return true; duke@0: } duke@0: duke@0: /** duke@0: * Tests to see if all objects in the duke@0: * specified Collection are children of duke@0: * this BeanContext. duke@0: * @param c the specified Collection duke@0: * duke@0: * @return true if all objects duke@0: * in the collection are children of duke@0: * this BeanContext, false if not. duke@0: */ duke@0: public boolean containsAll(Collection c) { duke@0: synchronized(children) { duke@0: Iterator i = c.iterator(); duke@0: while (i.hasNext()) duke@0: if(!contains(i.next())) duke@0: return false; duke@0: duke@0: return true; duke@0: } duke@0: } duke@0: duke@0: /** duke@0: * add Collection to set of Children (Unsupported) duke@0: * implementations must synchronized on the hierarchy lock and "children" protected field duke@0: * @throws UnsupportedOperationException duke@0: */ duke@0: public boolean addAll(Collection c) { duke@0: throw new UnsupportedOperationException(); duke@0: } duke@0: duke@0: /** duke@0: * remove all specified children (Unsupported) duke@0: * implementations must synchronized on the hierarchy lock and "children" protected field duke@0: * @throws UnsupportedOperationException duke@0: */ duke@0: public boolean removeAll(Collection c) { duke@0: throw new UnsupportedOperationException(); duke@0: } duke@0: duke@0: duke@0: /** duke@0: * retain only specified children (Unsupported) duke@0: * implementations must synchronized on the hierarchy lock and "children" protected field duke@0: * @throws UnsupportedOperationException duke@0: */ duke@0: public boolean retainAll(Collection c) { duke@0: throw new UnsupportedOperationException(); duke@0: } duke@0: duke@0: /** duke@0: * clear the children (Unsupported) duke@0: * implementations must synchronized on the hierarchy lock and "children" protected field duke@0: * @throws UnsupportedOperationException duke@0: */ duke@0: public void clear() { duke@0: throw new UnsupportedOperationException(); duke@0: } duke@0: duke@0: /** duke@0: * Adds a BeanContextMembershipListener duke@0: * duke@0: * @param bcml the BeanContextMembershipListener to add duke@0: * @throws NullPointerException duke@0: */ duke@0: duke@0: public void addBeanContextMembershipListener(BeanContextMembershipListener bcml) { duke@0: if (bcml == null) throw new NullPointerException("listener"); duke@0: duke@0: synchronized(bcmListeners) { duke@0: if (bcmListeners.contains(bcml)) duke@0: return; duke@0: else duke@0: bcmListeners.add(bcml); duke@0: } duke@0: } duke@0: duke@0: /** duke@0: * Removes a BeanContextMembershipListener duke@0: * duke@0: * @param bcml the BeanContextMembershipListener to remove duke@0: * @throws NullPointerException duke@0: */ duke@0: duke@0: public void removeBeanContextMembershipListener(BeanContextMembershipListener bcml) { duke@0: if (bcml == null) throw new NullPointerException("listener"); duke@0: duke@0: synchronized(bcmListeners) { duke@0: if (!bcmListeners.contains(bcml)) duke@0: return; duke@0: else duke@0: bcmListeners.remove(bcml); duke@0: } duke@0: } duke@0: duke@0: /** duke@0: * @param name the name of the resource requested. duke@0: * @param bcc the child object making the request. duke@0: * duke@0: * @return the requested resource as an InputStream duke@0: * @throws NullPointerException duke@0: */ duke@0: duke@0: public InputStream getResourceAsStream(String name, BeanContextChild bcc) { duke@0: if (name == null) throw new NullPointerException("name"); duke@0: if (bcc == null) throw new NullPointerException("bcc"); duke@0: duke@0: if (containsKey(bcc)) { duke@0: ClassLoader cl = bcc.getClass().getClassLoader(); duke@0: duke@0: return cl != null ? cl.getResourceAsStream(name) duke@0: : ClassLoader.getSystemResourceAsStream(name); duke@0: } else throw new IllegalArgumentException("Not a valid child"); duke@0: } duke@0: duke@0: /** duke@0: * @param name the name of the resource requested. duke@0: * @param bcc the child object making the request. duke@0: * duke@0: * @return the requested resource as an InputStream duke@0: */ duke@0: duke@0: public URL getResource(String name, BeanContextChild bcc) { duke@0: if (name == null) throw new NullPointerException("name"); duke@0: if (bcc == null) throw new NullPointerException("bcc"); duke@0: duke@0: if (containsKey(bcc)) { duke@0: ClassLoader cl = bcc.getClass().getClassLoader(); duke@0: duke@0: return cl != null ? cl.getResource(name) duke@0: : ClassLoader.getSystemResource(name); duke@0: } else throw new IllegalArgumentException("Not a valid child"); duke@0: } duke@0: duke@0: /** duke@0: * Sets the new design time value for this BeanContext. duke@0: * @param dTime the new designTime value duke@0: */ duke@0: public synchronized void setDesignTime(boolean dTime) { duke@0: if (designTime != dTime) { duke@0: designTime = dTime; duke@0: duke@0: firePropertyChange("designMode", Boolean.valueOf(!dTime), Boolean.valueOf(dTime)); duke@0: } duke@0: } duke@0: duke@0: duke@0: /** duke@0: * Reports whether or not this object is in duke@0: * currently in design time mode. duke@0: * @return true if in design time mode, duke@0: * false if not duke@0: */ duke@0: public synchronized boolean isDesignTime() { return designTime; } duke@0: duke@0: /** duke@0: * Sets the locale of this BeanContext. duke@0: * @param newLocale the new locale. This method call will have duke@0: * no effect if newLocale is null. duke@0: * @throws PropertyVetoException if the new value is rejected duke@0: */ duke@0: public synchronized void setLocale(Locale newLocale) throws PropertyVetoException { duke@0: duke@0: if ((locale != null && !locale.equals(newLocale)) && newLocale != null) { duke@0: Locale old = locale; duke@0: duke@0: fireVetoableChange("locale", old, newLocale); // throws duke@0: duke@0: locale = newLocale; duke@0: duke@0: firePropertyChange("locale", old, newLocale); duke@0: } duke@0: } duke@0: duke@0: /** duke@0: * Gets the locale for this BeanContext. duke@0: * duke@0: * @return the current Locale of the BeanContext duke@0: */ duke@0: public synchronized Locale getLocale() { return locale; } duke@0: duke@0: /** duke@0: *

duke@0: * This method is typically called from the environment in order to determine duke@0: * if the implementor "needs" a GUI. duke@0: *

duke@0: *

duke@0: * The algorithm used herein tests the BeanContextPeer, and its current children duke@0: * to determine if they are either Containers, Components, or if they implement duke@0: * Visibility and return needsGui() == true. duke@0: *

duke@0: * @return true if the implementor needs a GUI duke@0: */ duke@0: public synchronized boolean needsGui() { duke@0: BeanContext bc = getBeanContextPeer(); duke@0: duke@0: if (bc != this) { duke@0: if (bc instanceof Visibility) return ((Visibility)bc).needsGui(); duke@0: duke@0: if (bc instanceof Container || bc instanceof Component) duke@0: return true; duke@0: } duke@0: duke@0: synchronized(children) { duke@0: for (Iterator i = children.keySet().iterator(); i.hasNext();) { duke@0: Object c = i.next(); duke@0: duke@0: try { duke@0: return ((Visibility)c).needsGui(); duke@0: } catch (ClassCastException cce) { duke@0: // do nothing ... duke@0: } duke@0: duke@0: if (c instanceof Container || c instanceof Component) duke@0: return true; duke@0: } duke@0: } duke@0: duke@0: return false; duke@0: } duke@0: duke@0: /** duke@0: * notify this instance that it may no longer render a GUI. duke@0: */ duke@0: duke@0: public synchronized void dontUseGui() { duke@0: if (okToUseGui) { duke@0: okToUseGui = false; duke@0: duke@0: // lets also tell the Children that can that they may not use their GUI's duke@0: synchronized(children) { duke@0: for (Iterator i = children.keySet().iterator(); i.hasNext();) { duke@0: Visibility v = getChildVisibility(i.next()); duke@0: duke@0: if (v != null) v.dontUseGui(); duke@0: } duke@0: } duke@0: } duke@0: } duke@0: duke@0: /** duke@0: * Notify this instance that it may now render a GUI duke@0: */ duke@0: duke@0: public synchronized void okToUseGui() { duke@0: if (!okToUseGui) { duke@0: okToUseGui = true; duke@0: duke@0: // lets also tell the Children that can that they may use their GUI's duke@0: synchronized(children) { duke@0: for (Iterator i = children.keySet().iterator(); i.hasNext();) { duke@0: Visibility v = getChildVisibility(i.next()); duke@0: duke@0: if (v != null) v.okToUseGui(); duke@0: } duke@0: } duke@0: } duke@0: } duke@0: duke@0: /** duke@0: * Used to determine if the BeanContext duke@0: * child is avoiding using its GUI. duke@0: * @return is this instance avoiding using its GUI? duke@0: * @see Visibility duke@0: */ duke@0: public boolean avoidingGui() { duke@0: return !okToUseGui && needsGui(); duke@0: } duke@0: duke@0: /** duke@0: * Is this BeanContext in the duke@0: * process of being serialized? duke@0: * @return if this BeanContext is duke@0: * currently being serialized duke@0: */ duke@0: public boolean isSerializing() { return serializing; } duke@0: duke@0: /** duke@0: * Returns an iterator of all children duke@0: * of this BeanContext. duke@0: * @return an iterator for all the current BCSChild values duke@0: */ duke@0: protected Iterator bcsChildren() { synchronized(children) { return children.values().iterator(); } } duke@0: duke@0: /** duke@0: * called by writeObject after defaultWriteObject() but prior to duke@0: * serialization of currently serializable children. duke@0: * duke@0: * This method may be overridden by subclasses to perform custom duke@0: * serialization of their state prior to this superclass serializing duke@0: * the children. duke@0: * duke@0: * This method should not however be used by subclasses to replace their duke@0: * own implementation (if any) of writeObject(). duke@0: */ duke@0: duke@0: protected void bcsPreSerializationHook(ObjectOutputStream oos) throws IOException { duke@0: } duke@0: duke@0: /** duke@0: * called by readObject after defaultReadObject() but prior to duke@0: * deserialization of any children. duke@0: * duke@0: * This method may be overridden by subclasses to perform custom duke@0: * deserialization of their state prior to this superclass deserializing duke@0: * the children. duke@0: * duke@0: * This method should not however be used by subclasses to replace their duke@0: * own implementation (if any) of readObject(). duke@0: */ duke@0: duke@0: protected void bcsPreDeserializationHook(ObjectInputStream ois) throws IOException, ClassNotFoundException { duke@0: } duke@0: duke@0: /** duke@0: * Called by readObject with the newly deserialized child and BCSChild. duke@0: * @param child the newly deserialized child duke@0: * @param bcsc the newly deserialized BCSChild duke@0: */ duke@0: protected void childDeserializedHook(Object child, BCSChild bcsc) { duke@0: synchronized(children) { duke@0: children.put(child, bcsc); duke@0: } duke@0: } duke@0: duke@0: /** duke@0: * Used by writeObject to serialize a Collection. duke@0: * @param oos the ObjectOutputStream duke@0: * to use during serialization duke@0: * @param coll the Collection to serialize duke@0: * @throws IOException if serialization failed duke@0: */ duke@0: protected final void serialize(ObjectOutputStream oos, Collection coll) throws IOException { duke@0: int count = 0; duke@0: Object[] objects = coll.toArray(); duke@0: duke@0: for (int i = 0; i < objects.length; i++) { duke@0: if (objects[i] instanceof Serializable) duke@0: count++; duke@0: else duke@0: objects[i] = null; duke@0: } duke@0: duke@0: oos.writeInt(count); // number of subsequent objects duke@0: duke@0: for (int i = 0; count > 0; i++) { duke@0: Object o = objects[i]; duke@0: duke@0: if (o != null) { duke@0: oos.writeObject(o); duke@0: count--; duke@0: } duke@0: } duke@0: } duke@0: duke@0: /** duke@0: * used by readObject to deserialize a collection. duke@0: * @param ois the ObjectInputStream to use duke@0: * @param coll the Collection duke@0: */ duke@0: protected final void deserialize(ObjectInputStream ois, Collection coll) throws IOException, ClassNotFoundException { duke@0: int count = 0; duke@0: duke@0: count = ois.readInt(); duke@0: duke@0: while (count-- > 0) { duke@0: coll.add(ois.readObject()); duke@0: } duke@0: } duke@0: duke@0: /** duke@0: * Used to serialize all children of duke@0: * this BeanContext. duke@0: * @param oos the ObjectOutputStream duke@0: * to use during serialization duke@0: * @throws IOException if serialization failed duke@0: */ duke@0: public final void writeChildren(ObjectOutputStream oos) throws IOException { duke@0: if (serializable <= 0) return; duke@0: duke@0: boolean prev = serializing; duke@0: duke@0: serializing = true; duke@0: duke@0: int count = 0; duke@0: duke@0: synchronized(children) { duke@0: Iterator i = children.entrySet().iterator(); duke@0: duke@0: while (i.hasNext() && count < serializable) { duke@0: Map.Entry entry = (Map.Entry)i.next(); duke@0: duke@0: if (entry.getKey() instanceof Serializable) { duke@0: try { duke@0: oos.writeObject(entry.getKey()); // child duke@0: oos.writeObject(entry.getValue()); // BCSChild duke@0: } catch (IOException ioe) { duke@0: serializing = prev; duke@0: throw ioe; duke@0: } duke@0: count++; duke@0: } duke@0: } duke@0: } duke@0: duke@0: serializing = prev; duke@0: duke@0: if (count != serializable) { duke@0: throw new IOException("wrote different number of children than expected"); duke@0: } duke@0: duke@0: } duke@0: duke@0: /** duke@0: * Serialize the BeanContextSupport, if this instance has a distinct duke@0: * peer (that is this object is acting as a delegate for another) then duke@0: * the children of this instance are not serialized here due to a duke@0: * 'chicken and egg' problem that occurs on deserialization of the duke@0: * children at the same time as this instance. duke@0: * duke@0: * Therefore in situations where there is a distinct peer to this instance duke@0: * it should always call writeObject() followed by writeChildren() and duke@0: * readObject() followed by readChildren(). duke@0: * duke@0: * @param oos the ObjectOutputStream duke@0: */ duke@0: duke@0: private synchronized void writeObject(ObjectOutputStream oos) throws IOException, ClassNotFoundException { duke@0: serializing = true; duke@0: duke@0: synchronized (BeanContext.globalHierarchyLock) { duke@0: try { duke@0: oos.defaultWriteObject(); // serialize the BeanContextSupport object duke@0: duke@0: bcsPreSerializationHook(oos); duke@0: duke@0: if (serializable > 0 && this.equals(getBeanContextPeer())) duke@0: writeChildren(oos); duke@0: duke@0: serialize(oos, (Collection)bcmListeners); duke@0: } finally { duke@0: serializing = false; duke@0: } duke@0: } duke@0: } duke@0: duke@0: /** duke@0: * When an instance of this class is used as a delegate for the duke@0: * implementation of the BeanContext protocols (and its subprotocols) duke@0: * there exists a 'chicken and egg' problem during deserialization duke@0: */ duke@0: duke@0: public final void readChildren(ObjectInputStream ois) throws IOException, ClassNotFoundException { duke@0: int count = serializable; duke@0: duke@0: while (count-- > 0) { duke@0: Object child = null; duke@0: BeanContextSupport.BCSChild bscc = null; duke@0: duke@0: try { duke@0: child = ois.readObject(); duke@0: bscc = (BeanContextSupport.BCSChild)ois.readObject(); duke@0: } catch (IOException ioe) { duke@0: continue; duke@0: } catch (ClassNotFoundException cnfe) { duke@0: continue; duke@0: } duke@0: duke@0: duke@0: synchronized(child) { duke@0: BeanContextChild bcc = null; duke@0: duke@0: try { duke@0: bcc = (BeanContextChild)child; duke@0: } catch (ClassCastException cce) { duke@0: // do nothing; duke@0: } duke@0: duke@0: if (bcc != null) { duke@0: try { duke@0: bcc.setBeanContext(getBeanContextPeer()); duke@0: duke@0: bcc.addPropertyChangeListener("beanContext", childPCL); duke@0: bcc.addVetoableChangeListener("beanContext", childVCL); duke@0: duke@0: } catch (PropertyVetoException pve) { duke@0: continue; duke@0: } duke@0: } duke@0: duke@0: childDeserializedHook(child, bscc); duke@0: } duke@0: } duke@0: } duke@0: duke@0: /** duke@0: * deserialize contents ... if this instance has a distinct peer the duke@0: * children are *not* serialized here, the peer's readObject() must call duke@0: * readChildren() after deserializing this instance. duke@0: */ duke@0: duke@0: private synchronized void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException { duke@0: duke@0: synchronized(BeanContext.globalHierarchyLock) { duke@0: ois.defaultReadObject(); duke@0: duke@0: initialize(); duke@0: duke@0: bcsPreDeserializationHook(ois); duke@0: duke@0: if (serializable > 0 && this.equals(getBeanContextPeer())) duke@0: readChildren(ois); duke@0: duke@0: deserialize(ois, bcmListeners = new ArrayList(1)); duke@0: } duke@0: } duke@0: duke@0: /** duke@0: * subclasses may envelope to monitor veto child property changes. duke@0: */ duke@0: duke@0: public void vetoableChange(PropertyChangeEvent pce) throws PropertyVetoException { duke@0: String propertyName = pce.getPropertyName(); duke@0: Object source = pce.getSource(); duke@0: duke@0: synchronized(children) { duke@0: if ("beanContext".equals(propertyName) && duke@0: containsKey(source) && duke@0: !getBeanContextPeer().equals(pce.getNewValue()) duke@0: ) { duke@0: if (!validatePendingRemove(source)) { duke@0: throw new PropertyVetoException("current BeanContext vetoes setBeanContext()", pce); duke@0: } else ((BCSChild)children.get(source)).setRemovePending(true); duke@0: } duke@0: } duke@0: } duke@0: duke@0: /** duke@0: * subclasses may envelope to monitor child property changes. duke@0: */ duke@0: duke@0: public void propertyChange(PropertyChangeEvent pce) { duke@0: String propertyName = pce.getPropertyName(); duke@0: Object source = pce.getSource(); duke@0: duke@0: synchronized(children) { duke@0: if ("beanContext".equals(propertyName) && duke@0: containsKey(source) && duke@0: ((BCSChild)children.get(source)).isRemovePending()) { duke@0: BeanContext bc = getBeanContextPeer(); duke@0: duke@0: if (bc.equals(pce.getOldValue()) && !bc.equals(pce.getNewValue())) { duke@0: remove(source, false); duke@0: } else { duke@0: ((BCSChild)children.get(source)).setRemovePending(false); duke@0: } duke@0: } duke@0: } duke@0: } duke@0: duke@0: /** duke@0: *

duke@0: * Subclasses of this class may override, or envelope, this method to duke@0: * add validation behavior for the BeanContext to examine child objects duke@0: * immediately prior to their being added to the BeanContext. duke@0: *

duke@0: * duke@0: * @return true iff the child may be added to this BeanContext, otherwise false. duke@0: */ duke@0: duke@0: protected boolean validatePendingAdd(Object targetChild) { duke@0: return true; duke@0: } duke@0: duke@0: /** duke@0: *

duke@0: * Subclasses of this class may override, or envelope, this method to duke@0: * add validation behavior for the BeanContext to examine child objects duke@0: * immediately prior to their being removed from the BeanContext. duke@0: *

duke@0: * duke@0: * @return true iff the child may be removed from this BeanContext, otherwise false. duke@0: */ duke@0: duke@0: protected boolean validatePendingRemove(Object targetChild) { duke@0: return true; duke@0: } duke@0: duke@0: /** duke@0: * subclasses may override this method to simply extend add() semantics duke@0: * after the child has been added and before the event notification has duke@0: * occurred. The method is called with the child synchronized. duke@0: */ duke@0: duke@0: protected void childJustAddedHook(Object child, BCSChild bcsc) { duke@0: } duke@0: duke@0: /** duke@0: * subclasses may override this method to simply extend remove() semantics duke@0: * after the child has been removed and before the event notification has duke@0: * occurred. The method is called with the child synchronized. duke@0: */ duke@0: duke@0: protected void childJustRemovedHook(Object child, BCSChild bcsc) { duke@0: } duke@0: duke@0: /** duke@0: * Gets the Component (if any) associated with the specified child. duke@0: * @param child the specified child duke@0: * @return the Component (if any) associated with the specified child. duke@0: */ duke@0: protected static final Visibility getChildVisibility(Object child) { duke@0: try { duke@0: return (Visibility)child; duke@0: } catch (ClassCastException cce) { duke@0: return null; duke@0: } duke@0: } duke@0: duke@0: /** duke@0: * Gets the Serializable (if any) associated with the specified Child duke@0: * @param child the specified child duke@0: * @return the Serializable (if any) associated with the specified Child duke@0: */ duke@0: protected static final Serializable getChildSerializable(Object child) { duke@0: try { duke@0: return (Serializable)child; duke@0: } catch (ClassCastException cce) { duke@0: return null; duke@0: } duke@0: } duke@0: duke@0: /** duke@0: * Gets the PropertyChangeListener duke@0: * (if any) of the specified child duke@0: * @param child the specified child duke@0: * @return the PropertyChangeListener (if any) of the specified child duke@0: */ duke@0: protected static final PropertyChangeListener getChildPropertyChangeListener(Object child) { duke@0: try { duke@0: return (PropertyChangeListener)child; duke@0: } catch (ClassCastException cce) { duke@0: return null; duke@0: } duke@0: } duke@0: duke@0: /** duke@0: * Gets the VetoableChangeListener duke@0: * (if any) of the specified child duke@0: * @param child the specified child duke@0: * @return the VetoableChangeListener (if any) of the specified child duke@0: */ duke@0: protected static final VetoableChangeListener getChildVetoableChangeListener(Object child) { duke@0: try { duke@0: return (VetoableChangeListener)child; duke@0: } catch (ClassCastException cce) { duke@0: return null; duke@0: } duke@0: } duke@0: duke@0: /** duke@0: * Gets the BeanContextMembershipListener duke@0: * (if any) of the specified child duke@0: * @param child the specified child duke@0: * @return the BeanContextMembershipListener (if any) of the specified child duke@0: */ duke@0: protected static final BeanContextMembershipListener getChildBeanContextMembershipListener(Object child) { duke@0: try { duke@0: return (BeanContextMembershipListener)child; duke@0: } catch (ClassCastException cce) { duke@0: return null; duke@0: } duke@0: } duke@0: duke@0: /** duke@0: * Gets the BeanContextChild (if any) of the specified child duke@0: * @param child the specified child duke@0: * @return the BeanContextChild (if any) of the specified child duke@0: * @throws IllegalArgumentException if child implements both BeanContextChild and BeanContextProxy duke@0: */ duke@0: protected static final BeanContextChild getChildBeanContextChild(Object child) { duke@0: try { duke@0: BeanContextChild bcc = (BeanContextChild)child; duke@0: duke@0: if (child instanceof BeanContextChild && child instanceof BeanContextProxy) duke@0: throw new IllegalArgumentException("child cannot implement both BeanContextChild and BeanContextProxy"); duke@0: else duke@0: return bcc; duke@0: } catch (ClassCastException cce) { duke@0: try { duke@0: return ((BeanContextProxy)child).getBeanContextProxy(); duke@0: } catch (ClassCastException cce1) { duke@0: return null; duke@0: } duke@0: } duke@0: } duke@0: duke@0: /** duke@0: * Fire a BeanContextshipEvent on the BeanContextMembershipListener interface duke@0: */ duke@0: duke@0: protected final void fireChildrenAdded(BeanContextMembershipEvent bcme) { duke@0: Object[] copy; duke@0: duke@0: synchronized(bcmListeners) { copy = bcmListeners.toArray(); } duke@0: duke@0: for (int i = 0; i < copy.length; i++) duke@0: ((BeanContextMembershipListener)copy[i]).childrenAdded(bcme); duke@0: } duke@0: duke@0: /** duke@0: * Fire a BeanContextshipEvent on the BeanContextMembershipListener interface duke@0: */ duke@0: duke@0: protected final void fireChildrenRemoved(BeanContextMembershipEvent bcme) { duke@0: Object[] copy; duke@0: duke@0: synchronized(bcmListeners) { copy = bcmListeners.toArray(); } duke@0: duke@0: for (int i = 0; i < copy.length; i++) duke@0: ((BeanContextMembershipListener)copy[i]).childrenRemoved(bcme); duke@0: } duke@0: duke@0: /** duke@0: * protected method called from constructor and readObject to initialize duke@0: * transient state of BeanContextSupport instance. duke@0: * duke@0: * This class uses this method to instantiate inner class listeners used duke@0: * to monitor PropertyChange and VetoableChange events on children. duke@0: * duke@0: * subclasses may envelope this method to add their own initialization duke@0: * behavior duke@0: */ duke@0: duke@0: protected synchronized void initialize() { duke@0: children = new HashMap(serializable + 1); duke@0: bcmListeners = new ArrayList(1); duke@0: duke@0: childPCL = new PropertyChangeListener() { duke@0: duke@0: /* duke@0: * this adaptor is used by the BeanContextSupport class to forward duke@0: * property changes from a child to the BeanContext, avoiding duke@0: * accidential serialization of the BeanContext by a badly duke@0: * behaved Serializable child. duke@0: */ duke@0: duke@0: public void propertyChange(PropertyChangeEvent pce) { duke@0: BeanContextSupport.this.propertyChange(pce); duke@0: } duke@0: }; duke@0: duke@0: childVCL = new VetoableChangeListener() { duke@0: duke@0: /* duke@0: * this adaptor is used by the BeanContextSupport class to forward duke@0: * vetoable changes from a child to the BeanContext, avoiding duke@0: * accidential serialization of the BeanContext by a badly duke@0: * behaved Serializable child. duke@0: */ duke@0: duke@0: public void vetoableChange(PropertyChangeEvent pce) throws PropertyVetoException { duke@0: BeanContextSupport.this.vetoableChange(pce); duke@0: } duke@0: }; duke@0: } duke@0: duke@0: /** duke@0: * Gets a copy of the this BeanContext's children. duke@0: * @return a copy of the current nested children duke@0: */ duke@0: protected final Object[] copyChildren() { duke@0: synchronized(children) { return children.keySet().toArray(); } duke@0: } duke@0: duke@0: /** duke@0: * Tests to see if two class objects, duke@0: * or their names are equal. duke@0: * @param first the first object duke@0: * @param second the second object duke@0: * @return true if equal, false if not duke@0: */ duke@0: protected static final boolean classEquals(Class first, Class second) { duke@0: return first.equals(second) || first.getName().equals(second.getName()); duke@0: } duke@0: duke@0: duke@0: /* duke@0: * fields duke@0: */ duke@0: duke@0: duke@0: /** duke@0: * all accesses to the protected HashMap children field duke@0: * shall be synchronized on that object. duke@0: */ duke@0: protected transient HashMap children; duke@0: duke@0: private int serializable = 0; // children serializable duke@0: duke@0: /** duke@0: * all accesses to the protected ArrayList bcmListeners field duke@0: * shall be synchronized on that object. duke@0: */ duke@0: protected transient ArrayList bcmListeners; duke@0: duke@0: // duke@0: duke@0: /** duke@0: * The current locale of this BeanContext. duke@0: */ duke@0: protected Locale locale; duke@0: duke@0: /** duke@0: * A boolean indicating if this duke@0: * instance may now render a GUI. duke@0: */ duke@0: protected boolean okToUseGui; duke@0: duke@0: duke@0: /** duke@0: * A boolean indicating whether or not duke@0: * this object is currently in design time mode. duke@0: */ duke@0: protected boolean designTime; duke@0: duke@0: /* duke@0: * transient duke@0: */ duke@0: duke@0: private transient PropertyChangeListener childPCL; duke@0: duke@0: private transient VetoableChangeListener childVCL; duke@0: duke@0: private transient boolean serializing; duke@0: }