/* * Copyright (c) 1997, 2014, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ 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 {@code BeanContext} we are * supplying an implementation for, * or {@code null} * if this object is its own peer * @param lcle The current Locale for this BeanContext. If * {@code lcle} is {@code null}, the default locale * is assigned to the {@code BeanContext} instance. * @param dTime The initial state, * {@code true} if in design mode, * {@code false} 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 {@code BeanContext} we * are supplying an implementation for, * or {@code null} if this object is its own peer * @param lcle The current Locale for this {@code BeanContext}. If * {@code lcle} is {@code null}, the default locale * is assigned to the {@code BeanContext} instance. * @param dtime The initial state, {@code true} * if in design mode, * {@code false} 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 {@code null} if this object * is its own peer * @param lcle The current Locale for this * {@code BeanContext}. If * {@code lcle} is {@code null}, * the default locale * is assigned to the {@code BeanContext} * 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 {@code BeanContext} we are * supplying an implementation for, * or {@code null} 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 {@code BeanContext} 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 {@code BeanContext}. * </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 * {@code BeanContext} is empty. * A {@code BeanContext} 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 {@code BeanContext}. * @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 {@code BeanContext}. * @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 {@code BeanContext} instances * currently nested in this {@code BeanContext}. * @return an {@code Iterator} of the nested children */ public Iterator<Object> iterator() { synchronized(children) { return new BCSIterator(children.keySet().iterator()); } } /** * Gets all JavaBean or {@code BeanContext} * instances currently nested in this BeanContext. */ public Object[] toArray() { synchronized(children) { return children.keySet().toArray(); } } /** * Gets an array containing all children of * this {@code BeanContext} 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<Object> { 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 {@code BeanContext}. * <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 {@code BeanContext} * @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 {@code setBeanContext} or * by {@code remove()} 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 {@code BeanContext}. * @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 = 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 = 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 {@code Collection} are children of * this {@code BeanContext}. * @param c the specified {@code Collection} * * @return {@code true} if all objects * in the collection are children of * this {@code BeanContext}, false if not. */ @SuppressWarnings("rawtypes") 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} */ @SuppressWarnings("rawtypes") 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} */ @SuppressWarnings("rawtypes") 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} */ @SuppressWarnings("rawtypes") 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 {@code BeanContext}. * @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 {@code true} if in design time mode, * {@code false} 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}. * @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 {@code BeanContext}. * * @return the current Locale of the {@code BeanContext} */ 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 {@code true} 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<Object> 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<Object> 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<Object> i = children.keySet().iterator(); i.hasNext();) { Visibility v = getChildVisibility(i.next()); if (v != null) v.okToUseGui(); } } } } /** * Used to determine if the {@code BeanContext} * child is avoiding using its GUI. * @return is this instance avoiding using its GUI? * @see Visibility */ public boolean avoidingGui() { return !okToUseGui && needsGui(); } /** * Is this {@code BeanContext} in the * process of being serialized? * @return if this {@code BeanContext} is * currently being serialized */ public boolean isSerializing() { return serializing; } /** * Returns an iterator of all children * of this {@code BeanContext}. * @return an iterator for all the current BCSChild values */ protected Iterator<BCSChild> 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 {@code ObjectOutputStream} * to use during serialization * @param coll the {@code Collection} 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 */ @SuppressWarnings({"rawtypes", "unchecked"}) 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 {@code BeanContext}. * @param oos the {@code ObjectOutputStream} * 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<Map.Entry<Object, BCSChild>> i = children.entrySet().iterator(); while (i.hasNext() && count < serializable) { Map.Entry<Object, BCSChild> 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 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) && children.get(source).isRemovePending()) { BeanContext bc = getBeanContextPeer(); if (bc.equals(pce.getOldValue()) && !bc.equals(pce.getNewValue())) { remove(source, false); } else { 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} field * shall be synchronized on that object. */ protected transient HashMap<Object, BCSChild> children; private int serializable = 0; // children serializable /** * all accesses to the {@code protected ArrayList bcmListeners} field * shall be synchronized on that object. */ protected transient ArrayList<BeanContextMembershipListener> bcmListeners; // /** * The current locale of this BeanContext. */ protected Locale locale; /** * A {@code boolean} indicating if this * instance may now render a GUI. */ protected boolean okToUseGui; /** * A {@code boolean} 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; }