blob: 48f5f85a0fe1932c171ff1441598f56397d48ee4 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package java.beans.beancontext;
import java.awt.Component;
import java.beans.Beans;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.beans.PropertyVetoException;
import java.beans.VetoableChangeListener;
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 org.apache.harmony.beans.internal.nls.Messages;
/**
* This support class implements <code>BeanContext</code> interface.
* This class can be used directly, or be a super class of your class,
* or be a delegate of your implementation that needs to support
* <code>BeanContext</code> interface.
*
*/
public class BeanContextSupport extends BeanContextChildSupport implements
BeanContext, PropertyChangeListener, VetoableChangeListener,
Serializable {
/**
* Every child of context is companied with a <code>BCSChild</code>
* instance. It can hold implementation specific information about
* each child.
* <p>
* This class holds references of the child and its peer if there is one.</p>
*
*/
protected class BCSChild implements Serializable {
private static final long serialVersionUID = -5815286101609939109L;
Object child;
Object proxyPeer;
BCSChild(Object child, Object proxyPeer) {
this.child = child;
this.proxyPeer = proxyPeer;
}
}
/**
* This implementation wraps an iterator and override
* <code>remove()</code> with a noop method.
*
*/
protected static final class BCSIterator implements Iterator {
private Iterator backIter;
BCSIterator(Iterator backIter) {
this.backIter = backIter;
}
public boolean hasNext() {
return backIter.hasNext();
}
public Object next() {
return backIter.next();
}
public void remove() {
// no-op
}
}
private static final long serialVersionUID = -4879613978649577204L; //J2SE 1.4.2
/**
* A list of registered membership listeners.
* All access to this object should be synchronized on itself.
*/
@SuppressWarnings("unchecked")
transient protected ArrayList bcmListeners;
/**
* A map of children - key is child instance, value is <code>BCSChild</code> instance.
* All access to this object should be synchronized on itself.
*/
@SuppressWarnings("unchecked")
transient protected HashMap children;
transient private boolean serializing;
transient private boolean inNeedsGui;
transient private PropertyChangeListener nonSerPCL;
private int serializable;
/**
* The locale of this context.
*/
protected Locale locale;
/**
* A flag indicating whether this context is allowed to use GUI.
*/
protected boolean okToUseGui;
/**
* A flag indicating whether this context is in design mode.
*/
protected boolean designTime;
/**
* Constructs a standload <code>BeanContextSupport</code>.
*/
public BeanContextSupport() {
this(null, Locale.getDefault(), false, true);
}
/**
* Constructs a <code>BeanContextSupport</code> which is a delegate
* of the given peer.
*
* @param peer the peer of this context
*/
public BeanContextSupport(BeanContext peer) {
this(peer, Locale.getDefault(), false, true);
}
/**
* Constructs a <code>BeanContextSupport</code> which is a delegate
* of the given peer.
*
* @param peer the peer of this context
* @param locale the locale of this context
*/
public BeanContextSupport(BeanContext peer, Locale locale) {
this(peer, locale, false, true);
}
/**
* Constructs a <code>BeanContextSupport</code> which is a delegate
* of the given peer.
*
* @param peer the peer of this context
* @param locale the locale of this context
* @param designTime whether in design mode or not
*/
public BeanContextSupport(BeanContext peer, Locale locale,
boolean designTime) {
this(peer, locale, designTime, true);
}
/**
* Constructs a <code>BeanContextSupport</code> which is a delegate
* of the given peer.
*
* @param peer the peer of this context
* @param locale the locale of this context
* @param designTime whether in design mode or not
* @param okToUseGui whether GUI is usable or not
*/
public BeanContextSupport(BeanContext peer, Locale locale,
boolean designTime, boolean okToUseGui) {
super(peer);
if (locale == null) {
locale = Locale.getDefault();
}
this.locale = locale;
this.designTime = designTime;
this.okToUseGui = okToUseGui;
initialize();
}
/**
* Add a child to this context.
* <p>
* If the child already exists in this context, simply returns false.
* Otherwise, it is validated by calling <code>validatePendingAdd()</code>.
* If the add is valid, the child and its proxy (if the child implements
* <code>BeanContextProxy</code>) is then added, and <code>setBeanContext()</code>
* is called on it (if the child implements <code>BeanContextChild</code>
* or it has a proxy). Last, the <code>childJustAddedHook()</code> is
* called and all registered <code>BeanContextMembershipListener</code>s
* are notified.</p>
*
* @param child the child to add
* @return true if the child is added to this context; otherwise false
* @throws IllegalStateException if the child is not valid to add
* @see java.util.Collection#add(java.lang.Object)
*/
@SuppressWarnings("unchecked")
public boolean add(Object child) {
if (child == null) {
throw new IllegalArgumentException(Messages.getString("beans.67"));
}
BeanContextChild proxy = null;
synchronized (globalHierarchyLock) {
// check existence
if (contains(child)) {
return false;
}
// check serializing state
if (serializing) {
throw new IllegalStateException(
Messages.getString("beans.68"));
}
// validate
boolean valid = validatePendingAdd(child);
if (!valid) {
throw new IllegalStateException(
Messages.getString("beans.69"));
}
// find the proxy, if there's one
if (child instanceof BeanContextProxy) {
proxy = ((BeanContextProxy) child).getBeanContextProxy();
if (proxy == null) {
throw new NullPointerException(
Messages.getString("beans.6A"));
}
}
BeanContextChild beanContextChild = getChildBeanContextChild(child);
// add to children
BCSChild childBCSC = null, proxyBCSC = null;
synchronized (children) {
childBCSC = createBCSChild(child, proxy);
children.put(child, childBCSC);
if (proxy != null) {
proxyBCSC = createBCSChild(proxy, child);
children.put(proxy, proxyBCSC);
}
}
// set child's beanContext property
if (beanContextChild != null) {
try {
beanContextChild.setBeanContext(getBeanContextPeer());
} catch (PropertyVetoException e) {
synchronized (children) {
children.remove(child);
if (proxy != null) {
children.remove(proxy);
}
}
throw new IllegalStateException(
Messages.getString("beans.6B"));
}
// ensure no duplicate listener
beanContextChild.removePropertyChangeListener("beanContext",
nonSerPCL);
// listen to child's beanContext change
beanContextChild.addPropertyChangeListener("beanContext",
nonSerPCL);
}
// trigger hook
synchronized (child) {
addSerializable(childBCSC);
childJustAddedHook(child, childBCSC);
}
if (proxy != null) {
synchronized (proxy) {
addSerializable(proxyBCSC);
childJustAddedHook(proxy, proxyBCSC);
}
}
}
// notify listeners
fireChildrenAdded(new BeanContextMembershipEvent(getBeanContextPeer(),
proxy == null ? new Object[] { child } : new Object[] { child,
proxy }));
return true;
}
/**
* This method is unsupported, throws <code>UnsupportedOperationException</code>.
*
* @see java.util.Collection#addAll(java.util.Collection)
*/
public boolean addAll(Collection collection) {
throw new UnsupportedOperationException();
}
/* (non-Javadoc)
* @see java.beans.beancontext.BeanContext#addBeanContextMembershipListener(java.beans.beancontext.BeanContextMembershipListener)
*/
public void addBeanContextMembershipListener(
BeanContextMembershipListener listener) {
if (listener == null) {
throw new NullPointerException();
}
synchronized (bcmListeners) {
if (!bcmListeners.contains(listener)) {
bcmListeners.add(listener);
}
}
}
/* (non-Javadoc)
* @see java.beans.Visibility#avoidingGui()
*/
public boolean avoidingGui() {
// Avoiding GUI means that
// GUI is needed but not allowed to use at this time
return (needsGui() && !this.okToUseGui);
}
/**
* Returns an iterator of all <code>BCSChild</code> instances,
* with <code>remove()</code> disabled.
*
* @return an iterator of all <code>BCSChild</code> instances
*/
protected Iterator bcsChildren() {
synchronized (children) {
return new BCSIterator(children.values().iterator());
}
}
/**
* This method is called by <code>readObject()</code> after
* <code>defaultReadObject()</code> and before deserializing any
* children or listeners. Subclass can insert its specific
* deserialization behavior by overrideing this method.
* <p>
* The default implementation does nothing.</p>
*
* @param ois the object input stream
* @throws IOException
* @throws ClassNotFoundException
*/
protected void bcsPreDeserializationHook(ObjectInputStream ois)
throws IOException, ClassNotFoundException {
// to be overridden
}
/**
* This method is called by <code>writeObject()</code> after
* <code>defaultWriteObject()</code> and before serializing any
* children or listeners. Subclass can insert its specific
* serialization behavior by overrideing this method.
* <p>
* The default implementation does nothing.</p>
*
* @param oos the object output stream
* @throws IOException
*/
protected void bcsPreSerializationHook(ObjectOutputStream oos)
throws IOException {
// to be overridden
}
/**
* This method is called during deserialization everytime a child is read.
* <p>
* The default implementation does nothing.</p>
*
* @param child the child just deserialized
* @param bcsChild the <code>BCSChild</code> just deserialized
*/
protected void childDeserializedHook(Object child, BCSChild bcsChild) {
// to be overridden
}
/**
* This method is called everytime a child is added to this context.
* This method is called with child synchronized.
* <p>
* The default implementation does nothing.</p>
*
* @param child the child just added
* @param bcsChild the <code>BCSChild</code> just added
*/
protected void childJustAddedHook(Object child, BCSChild bcsChild) {
// to be overridden
}
/**
* This method is called everytime a child is removed from this context.
* This method is called with child synchronized.
* <p>
* The default implementation does nothing.</p>
*
* @param child the child just removed
* @param bcsChild the <code>BCSChild</code> just removed
*/
protected void childJustRemovedHook(Object child, BCSChild bcsChild) {
// to be overridden
}
/**
* Compares if two classes are equal or their class names are equal.
*
* @param clz1 a class
* @param clz2 another class
* @return true if two class objects are equal or their class names are equal.
*/
protected static final boolean classEquals(Class clz1, Class clz2) {
if (clz1 == null || clz2 == null) {
throw new NullPointerException();
}
return clz1 == clz2 || clz1.getName().equals(clz2.getName());
}
/**
* This method is unsupported, throws <code>UnsupportedOperationException</code>.
*
* @see java.util.Collection#clear()
*/
public void clear() {
throw new UnsupportedOperationException();
}
/**
* Returns true if the given object is a child of this context.
*
* @param child the object to test
* @return true if the given object is a child of this context
* @see java.util.Collection#contains(java.lang.Object)
*/
public boolean contains(Object child) {
synchronized (children) {
return children.containsKey(child);
}
}
/**
* Returns true if given objects are children of this context.
*
* @param collection a collection of objects
* @return true if given objects are children of this context
* @see java.util.Collection#containsAll(java.util.Collection)
*/
@SuppressWarnings("unchecked")
public boolean containsAll(Collection collection) {
synchronized (children) {
return children.keySet().containsAll(collection);
}
}
/**
* Returns true if the given object is a child of this context.
*
* @param child the object to test
* @return true if the given object is a child of this context
*/
public boolean containsKey(Object child) {
synchronized (children) {
return children.containsKey(child);
}
}
/**
* Returns an array containing all children of this context.
*
* @return an array containing all children of this context
*/
protected final Object[] copyChildren() {
synchronized (children) {
return children.keySet().toArray();
}
}
/**
* Creates a <code>BCSChild</code> object to company the given child.
*
* @param child the child
* @param proxyPeer the proxy peer of the child if there is one
* @return a <code>BCSChild</code> object to company the given child
*/
protected BCSChild createBCSChild(Object child, Object proxyPeer) {
return new BCSChild(child, proxyPeer);
}
/**
* Deserialize a collection.
* <p>
* First read a <code>int</code> indicating of number of rest objects,
* then read the objects one by one.</p>
*
* @param ois the stream where the collection is read from
* @param collection the collection to hold read objects
* @throws IOException if I/O exception occurs
* @throws ClassNotFoundException if class of any read object is not found
*/
@SuppressWarnings("unchecked")
protected final void deserialize(ObjectInputStream ois,
Collection collection) throws IOException, ClassNotFoundException {
int size = ois.readInt();
for (int i = 0; i < size; i++) {
collection.add(ois.readObject());
}
}
/* (non-Javadoc)
* @see java.beans.Visibility#dontUseGui()
*/
public void dontUseGui() {
okToUseGui = false;
}
/**
* Notifies registered <code>BeanContextMembershipListener</code>s that
* a new child has been added.
*
* @param event the <code>BeanContextMembershipEvent</code>
*/
protected final void fireChildrenAdded(BeanContextMembershipEvent event) {
Object listeners[];
synchronized (bcmListeners) {
listeners = bcmListeners.toArray();
}
for (int i = 0; i < listeners.length; i++) {
BeanContextMembershipListener l = (BeanContextMembershipListener) listeners[i];
l.childrenAdded(event);
}
}
/**
* Notifies registered <code>BeanContextMembershipListener</code>s that
* a child has been removed.
*
* @param event the <code>BeanContextMembershipEvent</code>
*/
protected final void fireChildrenRemoved(BeanContextMembershipEvent event) {
Object listeners[];
synchronized (bcmListeners) {
listeners = bcmListeners.toArray();
}
for (int i = 0; i < listeners.length; i++) {
BeanContextMembershipListener l = (BeanContextMembershipListener) listeners[i];
l.childrenRemoved(event);
}
}
/**
* Returns the peer of this context casted as <code>BeanContext</code>.
*
* @return the peer of this context casted as <code>BeanContext</code>
*/
public BeanContext getBeanContextPeer() {
return (BeanContext) beanContextChildPeer;
}
/**
* Returns the <code>BeanContextChild</code> related with the given child.
* <p>
* If the child implements <code>BeanContextChild</code>, it is returned.
* If the child implements <code>BeanContextProxy</code>, the proxy is returned.
* Otherwise, null is returned.</p>
*
* @param child a child
* @return the <code>BeanContextChild</code> related with the given child
* @throws IllegalStateException if the child implements both <code>BeanContextChild</code> and <code>BeanContextProxy</code>
*/
protected static final BeanContextChild getChildBeanContextChild(
Object child) {
if (child instanceof BeanContextChild) {
if (child instanceof BeanContextProxy) {
throw new IllegalArgumentException(
Messages.getString("beans.6C"));
}
return (BeanContextChild) child;
}
if (child instanceof BeanContextProxy) {
if (child instanceof BeanContextChild) {
throw new IllegalArgumentException(
Messages.getString("beans.6C"));
}
return ((BeanContextProxy) child).getBeanContextProxy();
}
return null;
}
/**
* Returns the given child casted to <code>BeanContextMembershipListener</code>,
* or null if it does not implements the interface.
*
* @param child a child
* @return the given child casted to <code>BeanContextMembershipListener</code>,
* or null if it does not implements the interface
*/
protected static final BeanContextMembershipListener getChildBeanContextMembershipListener(
Object child) {
if (child instanceof BeanContextMembershipListener) {
return (BeanContextMembershipListener) child;
} else {
return null;
}
}
/**
* Returns the given child casted to <code>PropertyChangeListener</code>,
* or null if it does not implements the interface.
*
* @param child a child
* @return the given child casted to <code>PropertyChangeListener</code>,
* or null if it does not implements the interface
*/
protected static final PropertyChangeListener getChildPropertyChangeListener(
Object child) {
if (child instanceof PropertyChangeListener) {
return (PropertyChangeListener) child;
} else {
return null;
}
}
/**
* Returns the given child casted to <code>Serializable</code>,
* or null if it does not implements the interface.
*
* @param child a child
* @return the given child casted to <code>Serializable</code>,
* or null if it does not implements the interface
*/
protected static final Serializable getChildSerializable(Object child) {
if (child instanceof Serializable) {
return (Serializable) child;
} else {
return null;
}
}
/**
* Returns the given child casted to <code>VetoableChangeListener</code>,
* or null if it does not implements the interface.
*
* @param child a child
* @return the given child casted to <code>VetoableChangeListener</code>,
* or null if it does not implements the interface
*/
protected static final VetoableChangeListener getChildVetoableChangeListener(
Object child) {
if (child instanceof VetoableChangeListener) {
return (VetoableChangeListener) child;
} else {
return null;
}
}
/**
* Returns the given child casted to <code>Visibility</code>,
* or null if it does not implements the interface.
*
* @param child a child
* @return the given child casted to <code>Visibility</code>,
* or null if it does not implements the interface
*/
protected static final Visibility getChildVisibility(Object child) {
if (child instanceof Visibility) {
return (Visibility) child;
} else {
return null;
}
}
/**
* Returns the locale of this context.
*
* @return the locale of this context
*/
public Locale getLocale() {
return locale;
}
/* (non-Javadoc)
* @see java.beans.beancontext.BeanContext#getResource(java.lang.String, java.beans.beancontext.BeanContextChild)
*/
public URL getResource(String resourceName, BeanContextChild child) {
if (resourceName == null || child == null) {
throw new NullPointerException();
}
if (!contains(child)) {
throw new IllegalArgumentException(Messages.getString("beans.6D"));
}
return ClassLoader.getSystemResource(resourceName);
}
/* (non-Javadoc)
* @see java.beans.beancontext.BeanContext#getResourceAsStream(java.lang.String, java.beans.beancontext.BeanContextChild)
*/
public InputStream getResourceAsStream(String resourceName,
BeanContextChild child) throws IllegalArgumentException {
if (resourceName == null || child == null) {
throw new NullPointerException();
}
if (!contains(child)) {
throw new IllegalArgumentException(Messages.getString("beans.6D"));
}
return ClassLoader.getSystemResourceAsStream(resourceName);
}
/**
* Initializes all transient fields of this instance, called by
* constructors and <code>readObject()</code>.
*/
protected void initialize() {
// init transient fields
bcmListeners = new ArrayList<BeanContextMembershipListener>();
children = new HashMap();
serializing = false;
inNeedsGui = false;
nonSerPCL = new PropertyChangeListener() {
public void propertyChange(PropertyChangeEvent event) {
BeanContextSupport.this.propertyChange(event);
}
};
}
/* (non-Javadoc)
* @see java.beans.beancontext.BeanContext#instantiateChild(java.lang.String)
*/
public Object instantiateChild(String beanName) throws IOException,
ClassNotFoundException {
return Beans.instantiate(getClass().getClassLoader(), beanName,
getBeanContextPeer());
}
/* (non-Javadoc)
* @see java.beans.DesignMode#isDesignTime()
*/
public boolean isDesignTime() {
return designTime;
}
/* (non-Javadoc)
* @see java.util.Collection#isEmpty()
*/
public boolean isEmpty() {
synchronized (children) {
return children.isEmpty();
}
}
/**
* Returns true if this context is currently being serialized
* (by another thread).
*
* @return true if this context is currently being serialized
* (by another thread)
*/
public boolean isSerializing() {
return serializing;
}
/**
* Returns an iterator of children of this context,
* with <code>remove()</code> disabled.
*
* @see java.util.Collection#iterator()
*/
public Iterator iterator() {
synchronized (children) {
return new BCSIterator(children.keySet().iterator());
}
}
/**
* Returns true if this context or its children needs GUI to work properly.
* <p>
* The implementation checks the peer and all the children that implement
* <code>Visibility</code> to see if any of their <code>needsGui()</code>
* returns true, and if any of the children extends
* <code>java.awt.Component</code>.</p>
*
* @see java.beans.Visibility#needsGui()
*/
public boolean needsGui() {
if (inNeedsGui) {
return false;
}
inNeedsGui = true;
try {
if (getBeanContextPeer() != this) {
if (getBeanContextPeer().needsGui()) {
return true;
}
}
Object childs[] = copyChildren();
for (int i = 0; i < childs.length; i++) {
if (childs[i] instanceof Component) {
return true;
}
Visibility v = getChildVisibility(childs[i]);
if (v != null && v.needsGui()) {
return true;
}
}
return false;
} finally {
inNeedsGui = false;
}
}
/* (non-Javadoc)
* @see java.beans.Visibility#okToUseGui()
*/
public void okToUseGui() {
okToUseGui = true;
}
/* (non-Javadoc)
* @see java.beans.PropertyChangeListener#propertyChange(java.beans.PropertyChangeEvent)
*/
public void propertyChange(PropertyChangeEvent event) {
if (contains(event.getSource())
&& "beanContext".equals(event.getPropertyName())
&& event.getOldValue() == getBeanContextPeer()) {
remove(event.getSource(), false);
}
}
/**
* Deserializes children from the given object input stream.
* <p>
* The implementation reads pairs of child object and <code>BCSChild</code>
* object according to <code>serializable</code> property. For each pair,
* it is added to the <code>children</code> map and the
* <code>childDeserializedHook()</code> is called. If the child implements
* <code>BeanContextChild</code>, its <code>setBeanContext()</code> is
* also called.
* </p>
* <p>
* This method is called by <code>readObject()</code> if the context works
* standalone. Or if this support object is a delegate of another
* <code>BeanContext</code> implementation, then this method should be
* called by the peer. Doing this means that derialization can proceed
* without any circular dependency problems.
*
* @param ois
* the object input stream
* @throws IOException
* if I/O exception occurs
* @throws ClassNotFoundException
* if class of read object is not found
*/
@SuppressWarnings("unchecked")
public final void readChildren(ObjectInputStream ois) throws IOException,
ClassNotFoundException {
synchronized (children) {
for (int i = 0; i < serializable; i++) {
Object child = ois.readObject();
BCSChild childBCSC = (BCSChild) ois.readObject();
children.put(child, childBCSC);
childDeserializedHook(child, childBCSC);
// set child's beanContext property
BeanContextChild beanContextChild = getChildBeanContextChild(child);
if (beanContextChild != null) {
try {
beanContextChild.setBeanContext(getBeanContextPeer());
} catch (PropertyVetoException e) {
throw new IOException(
Messages.getString("beans.6B"));
}
// ensure no duplicate listener
beanContextChild.removePropertyChangeListener(
"beanContext", nonSerPCL);
// listen to child's beanContext change
beanContextChild.addPropertyChangeListener("beanContext",
nonSerPCL);
}
}
}
}
/**
* Removes the given child from this context.
* <p>
* Delegates to <code>remove(child, true)</code>.</p>
*
* @param child a child of this context
* @return true if the child is removed; or false if it is not a child of this context
* @throws IllegalArgumentException if the child is null
* @throws IllegalStateException if the child is not valid to remove
* @see java.util.Collection#remove(java.lang.Object)
*/
public boolean remove(Object child) {
return remove(child, true);
}
/**
* Removes the given child from this context.
* <p>
* If the given child is not a child of this context, simply returns false.
* Otherwise, <code>validatePendingRemove()</code> is called. If the
* removal is valid, the child's <code>beanContext</code> property is
* updated (if required) and the child and its proxy peer (if there is one)
* is removed. Last, <code>childJustRemovedHook()</code> is called and
* listeners are notified.</p>
*
* @param child a child of this context
* @param setChildBC whether to call <code>setBeanContext()</code> on the child or not
* @return true if the child is removed; or false if it is not a child of this context
* @throws IllegalArgumentException if the child is null
* @throws IllegalStateException if the child is not valid to remove
*/
protected boolean remove(Object child, boolean setChildBC) {
if (child == null) {
throw new IllegalArgumentException(Messages.getString("beans.67"));
}
Object peer = null;
synchronized (globalHierarchyLock) {
// check existence
if (!contains(child)) {
return false;
}
// check serializing state
if (serializing) {
throw new IllegalStateException(
Messages.getString("beans.68"));
}
// validate
boolean valid = validatePendingRemove(child);
if (!valid) {
throw new IllegalStateException(
Messages.getString("beans.6E"));
}
// set child's beanContext property
BeanContextChild beanContextChild = getChildBeanContextChild(child);
if (beanContextChild != null && setChildBC) {
// remove listener, first
beanContextChild.removePropertyChangeListener("beanContext",
nonSerPCL);
try {
beanContextChild.setBeanContext(null);
} catch (PropertyVetoException e) {
// rollback the listener change
beanContextChild.addPropertyChangeListener("beanContext",
nonSerPCL);
throw new IllegalStateException(
Messages.getString("beans.6B"));
}
}
// remove from children
BCSChild childBCSC = null, peerBCSC = null;
synchronized (children) {
childBCSC = (BCSChild) children.remove(child);
peer = childBCSC.proxyPeer;
if (peer != null) {
peerBCSC = (BCSChild) children.remove(peer);
}
}
// trigger hook
synchronized (child) {
removeSerializable(childBCSC);
childJustRemovedHook(child, childBCSC);
}
if (peer != null) {
synchronized (peer) {
removeSerializable(peerBCSC);
childJustRemovedHook(peer, peerBCSC);
}
}
}
// notify listeners
fireChildrenRemoved(new BeanContextMembershipEvent(
getBeanContextPeer(), peer == null ? new Object[] { child }
: new Object[] { child, peer }));
return true;
}
/**
* This method is unsupported, throws <code>UnsupportedOperationException</code>.
*
* @see java.util.Collection#removeAll(java.util.Collection)
*/
public boolean removeAll(Collection collection) {
throw new UnsupportedOperationException();
}
/* (non-Javadoc)
* @see java.beans.beancontext.BeanContext#removeBeanContextMembershipListener(java.beans.beancontext.BeanContextMembershipListener)
*/
public void removeBeanContextMembershipListener(
BeanContextMembershipListener listener) {
if (listener == null) {
throw new NullPointerException();
}
synchronized (bcmListeners) {
bcmListeners.remove(listener);
}
}
/**
* This method is unsupported, throws <code>UnsupportedOperationException</code>.
*
* @see java.util.Collection#retainAll(java.util.Collection)
*/
public boolean retainAll(Collection collection) {
throw new UnsupportedOperationException();
}
/**
* Serializes the given collection.
* <p>
* First writes a <code>int</code> indicating the number of all
* serializable elements (implements <code>Serializable</code>, then
* objects are writtern one by one.</p>
*
* @param oos the stream where the collection is writtern to
* @param collection the collection to serialize
* @throws IOException if I/O exception occurs
*/
protected final void serialize(ObjectOutputStream oos, Collection collection)
throws IOException {
Object array[] = collection.toArray();
int serCount = 0;
for (int i = 0; i < array.length; i++) {
if (array[i] instanceof Serializable) {
serCount++;
}
}
oos.writeInt(serCount);
for (int i = 0; i < array.length; i++) {
if (array[i] instanceof Serializable) {
oos.writeObject(array[i]);
}
}
}
/* (non-Javadoc)
* @see java.beans.DesignMode#setDesignTime(boolean)
*/
public void setDesignTime(boolean designTime) {
this.designTime = designTime;
}
/**
* Sets the locale of this context. <code>VetoableChangeListener</code>s
* and <code>PropertyChangeListener</code>s are notified.
*
* @param newLocale the new locale to set
* @throws PropertyVetoException if any <code>VetoableChangeListener</code> vetos this change
*/
public void setLocale(Locale newLocale) throws PropertyVetoException {
if (newLocale == null || newLocale == locale) {
return; // ignore null locale
}
PropertyChangeEvent event = new PropertyChangeEvent(
beanContextChildPeer, "locale", locale, newLocale);
// apply change
Locale oldLocale = locale;
locale = newLocale;
try {
// notify vetoable listeners
vcSupport.fireVetoableChange(event);
} catch (PropertyVetoException e) {
// rollback change
locale = oldLocale;
throw e;
}
// Notify BeanContext about this change
this.pcSupport.firePropertyChange(event);
}
/**
* Returns the number children of this context.
*
* @return the number children of this context
* @see java.util.Collection#size()
*/
public int size() {
synchronized (children) {
return children.size();
}
}
/**
* Returns an array of children of this context.
*
* @return an array of children of this context
* @see java.util.Collection#toArray()
*/
public Object[] toArray() {
synchronized (children) {
return children.keySet().toArray();
}
}
/**
* Returns an array of children of this context.
*
* @return an array of children of this context
* @see java.util.Collection#toArray(java.lang.Object[])
*/
@SuppressWarnings("unchecked")
public Object[] toArray(Object[] array) {
synchronized (children) {
return children.keySet().toArray(array);
}
}
/**
* Validates the pending add of child.
* <p>
* Default implementation always returns true.</p>
*
* @param child the child to be added
* @return true if it is valid to add the child
*/
protected boolean validatePendingAdd(Object child) {
// to be overridden
return true;
}
/**
* Validates the pending removal of child.
* <p>
* Default implementation always returns true.</p>
*
* @param child the child to be removed
* @return true if it is valid to remove the child
*/
protected boolean validatePendingRemove(Object child) {
// to be overridden
return true;
}
/* (non-Javadoc)
* @see java.beans.VetoableChangeListener#vetoableChange(java.beans.PropertyChangeEvent)
*/
public void vetoableChange(PropertyChangeEvent pce)
throws PropertyVetoException {
if (pce == null) {
throw new NullPointerException(Messages.getString("beans.1C")); //$NON-NLS-1$
}
}
/**
* Serializes children to the given object input stream.
* <p>
* The implementation iterates through all children and writes out pairs
* of child object and <code>BCSChild</code> object if the child is
* serializable (implements <code>Serialization</code>.</p>
* <p>
* This method is called by <code>writeObject()</code> if the context
* works standalone. Or if this support object is a delegate of another
* <code>BeanContext</code> implementation, then this method should be
* called by the peer to avoid the 'chicken and egg' problem during
* deserialization.</p>
*
* @param oos the stream to write
* @throws IOException if I/O exception occurs
*/
public final void writeChildren(ObjectOutputStream oos) throws IOException {
boolean origSer = serializing;
serializing = true;
try {
int count = 0;
synchronized (children) {
for (Iterator iter = children.values().iterator(); iter
.hasNext();) {
BCSChild bcsc = (BCSChild) iter.next();
if (bcsc.child instanceof Serializable
&& (bcsc.proxyPeer == null || bcsc.proxyPeer instanceof Serializable)) {
oos.writeObject(bcsc.child);
oos.writeObject(bcsc);
count++;
}
}
}
// what if count not equals to serializable?
if (count != serializable) {
throw new IOException(Messages.getString("beans.6F"));
}
} finally {
serializing = origSer;
}
}
/**
* The implementation goes through following steps:
* <p>
* <ol>
* <li>Writes out non-transient properties by calling
* <code>defaultWriteObject()</code>, especially the
* <code>serializable</code> indicating the number of serializable
* children.</li>
* <li>Calls <code>bcsPreSerializationHook()</code>.</li>
* <li>Writes out children by calling <code>writeChildren()</code> if
* this context works standalone. Otherwise it is the peer's
* responsibility to call <code>writeChildren()</code> after this object
* is serialized.</li>
* <li>Writes out serializable membership listeners.</li>
* </ol>
* </p>
*
* @param oos the object output stream
* @throws IOException if I/O exception occurs
*/
private void writeObject(ObjectOutputStream oos) throws IOException {
boolean origSer = serializing;
serializing = true;
try {
oos.defaultWriteObject();
bcsPreSerializationHook(oos);
if (this == getBeanContextPeer()) {
writeChildren(oos);
}
synchronized (bcmListeners) {
serialize(oos, bcmListeners);
}
} finally {
serializing = origSer;
}
}
/**
* The implementation goes through following steps:
* <p>
* <ol>
* <li>Reads non-transient properties by calling
* <code>defaultReadObject()</code>.</li>
* <li>Calls <code>bcsPreDeserializationHook()</code>.</li>
* <li>Reads children by calling <code>readChildren()</code> if
* this context works standalone. Otherwise it is the peer's
* responsibility to call <code>readChildren()</code> after this object
* is deserialized.</li>
* <li>Reads serializable membership listeners.</li>
* </ol>
* </p>
*
* @param ois the object input stream
* @throws IOException if I/O error occurs
* @throws ClassNotFoundException if class of read object is not found
*/
private void readObject(ObjectInputStream ois) throws IOException,
ClassNotFoundException {
ois.defaultReadObject();
initialize(); // init transient fields
bcsPreDeserializationHook(ois);
if (this == getBeanContextPeer()) {
readChildren(ois);
}
deserialize(ois, bcmListeners);
}
/*
* Increase variable serializable if child and proxyPeer fields of the given
* BCSChild object are serializable
*/
private void addSerializable(BCSChild bcsc) {
if (bcsc.child instanceof Serializable
&& (bcsc.proxyPeer == null || bcsc.proxyPeer instanceof Serializable)) {
serializable++;
}
}
/*
* Decrease variable serializable if child and proxyPeer fields of the given
* BCSChild object are serializable
*/
private void removeSerializable(BCSChild bcsc) {
if (serializable > 0
&& bcsc.child instanceof Serializable
&& (bcsc.proxyPeer == null || bcsc.proxyPeer instanceof Serializable)) {
serializable--;
}
}
}