You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1415 lines
45 KiB
1415 lines
45 KiB
/*
|
|
* Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved.
|
|
* ORACLE 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>
|
|
* 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
|
|
* @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.instantiate.
|
|
* </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 {
|
|
|
|
private static final long serialVersionUID = -5815286101609939109L;
|
|
|
|
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 * @return Subtype-specific subclass of Child without overriding collection methods
|
|
*/
|
|
|
|
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>.
|
|
* @return whether or not was present before being removed
|
|
*/
|
|
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 thrown unconditionally by this implementation
|
|
* @return this implementation unconditionally throws {@code 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 thrown unconditionally by this implementation
|
|
* @return this implementation unconditionally throws {@code 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 thrown unconditionally by this implementation
|
|
* @return this implementation unconditionally throws {@code 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 thrown unconditionally by this implementation
|
|
*/
|
|
public void clear() {
|
|
throw new UnsupportedOperationException();
|
|
}
|
|
|
|
/**
|
|
* Adds a BeanContextMembershipListener
|
|
*
|
|
* @param bcml the BeanContextMembershipListener to add
|
|
* @throws NullPointerException if the argument is null
|
|
*/
|
|
|
|
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
|
|
* @throws NullPointerException if the argument is null
|
|
*/
|
|
|
|
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 bcc the child object making the request.
|
|
*
|
|
* @return the requested resource as an InputStream
|
|
* @throws NullPointerException if the argument is null
|
|
*/
|
|
|
|
public InputStream getResourceAsStream(String name, BeanContextChild bcc) {
|
|
if (name == null) throw new NullPointerException("name");
|
|
if (bcc == null) throw new NullPointerException("bcc");
|
|
|
|
if (containsKey(bcc)) {
|
|
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 bcc 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");
|
|
|
|
if (containsKey(bcc)) {
|
|
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", Boolean.valueOf(!dTime), Boolean.valueOf(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().
|
|
* @param oos the {@code ObjectOutputStream} to use during serialization
|
|
* @throws IOException if serialization failed
|
|
*/
|
|
|
|
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().
|
|
* @param ois the {@code ObjectInputStream} to use during deserialization
|
|
* @throws IOException if deserialization failed
|
|
* @throws ClassNotFoundException if needed classes are not found
|
|
*/
|
|
|
|
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
|
|
* @throws IOException if deserialization failed
|
|
* @throws ClassNotFoundException if needed classes are not found
|
|
*/
|
|
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
|
|
* @param ois the ObjectInputStream to use
|
|
* @throws IOException if deserialization failed
|
|
* @throws ClassNotFoundException if needed classes are not found
|
|
*/
|
|
|
|
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>
|
|
*
|
|
* @param targetChild the child to create the Child on behalf of
|
|
* @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>
|
|
*
|
|
* @param targetChild the child to create the Child on behalf of
|
|
* @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.
|
|
* @param child the child
|
|
* @param bcsc the BCSChild
|
|
*/
|
|
|
|
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.
|
|
* @param child the child
|
|
* @param bcsc the BCSChild
|
|
*/
|
|
|
|
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
|
|
* @throws 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
|
|
* @param bcme the event to fire
|
|
*/
|
|
|
|
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
|
|
* @param bcme the event to fire
|
|
*/
|
|
|
|
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;
|
|
}
|