1. /*
  2. * @(#)BeanContextSupport.java 1.46 03/01/13
  3. *
  4. * Copyright 2003 Sun Microsystems, Inc. All rights reserved.
  5. * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
  6. */
  7. package java.beans.beancontext;
  8. import java.awt.Component;
  9. import java.awt.Container;
  10. import java.beans.Beans;
  11. import java.beans.AppletInitializer;
  12. import java.beans.DesignMode;
  13. import java.beans.PropertyChangeEvent;
  14. import java.beans.PropertyChangeListener;
  15. import java.beans.PropertyChangeSupport;
  16. import java.beans.VetoableChangeListener;
  17. import java.beans.VetoableChangeSupport;
  18. import java.beans.PropertyVetoException;
  19. import java.beans.Visibility;
  20. import java.io.IOException;
  21. import java.io.InputStream;
  22. import java.io.ObjectInputStream;
  23. import java.io.ObjectOutputStream;
  24. import java.io.Serializable;
  25. import java.net.URL;
  26. import java.util.ArrayList;
  27. import java.util.Collection;
  28. import java.util.HashMap;
  29. import java.util.Iterator;
  30. import java.util.Locale;
  31. import java.util.Map;
  32. /**
  33. * This helper class provides a utility implementation of the
  34. * java.beans.beancontext.BeanContext interface.
  35. * </p>
  36. * <p>
  37. * Since this class directly implements the BeanContext interface, the class
  38. * can, and is intended to be used either by subclassing this implementation,
  39. * or via ad-hoc delegation of an instance of this class from another.
  40. * </p>
  41. *
  42. * @author Laurence P. G. Cable
  43. * @version 1.46, 01/13/03
  44. * @since 1.2
  45. */
  46. public class BeanContextSupport extends BeanContextChildSupport
  47. implements BeanContext,
  48. Serializable,
  49. PropertyChangeListener,
  50. VetoableChangeListener {
  51. // Fix for bug 4282900 to pass JCK regression test
  52. static final long serialVersionUID = -4879613978649577204L;
  53. /**
  54. *
  55. * Construct a BeanContextSupport instance
  56. *
  57. *
  58. * @param peer The peer <tt>BeanContext</tt> we are
  59. * supplying an implementation for,
  60. * or <tt>null</tt>
  61. * if this object is its own peer
  62. * @param lcle The current Locale for this BeanContext. If
  63. * <tt>lcle</tt> is <tt>null</tt>, the default locale
  64. * is assigned to the <tt>BeanContext</tt> instance.
  65. * @param dTime The initial state,
  66. * <tt>true</tt> if in design mode,
  67. * <tt>false</tt> if runtime.
  68. * @param visible The initial visibility.
  69. * @see java.util.Locale#getDefault()
  70. * @see java.util.Locale#setDefault(java.util.Locale)
  71. */
  72. public BeanContextSupport(BeanContext peer, Locale lcle, boolean dTime, boolean visible) {
  73. super(peer);
  74. locale = lcle != null ? lcle : Locale.getDefault();
  75. designTime = dTime;
  76. okToUseGui = visible;
  77. initialize();
  78. }
  79. /**
  80. * Create an instance using the specified Locale and design mode.
  81. *
  82. * @param peer The peer <tt>BeanContext</tt> we
  83. * are supplying an implementation for,
  84. * or <tt>null</tt> if this object is its own peer
  85. * @param lcle The current Locale for this <tt>BeanContext</tt>. If
  86. * <tt>lcle</tt> is <tt>null</tt>, the default locale
  87. * is assigned to the <tt>BeanContext</tt> instance.
  88. * @param dtime The initial state, <tt>true</tt>
  89. * if in design mode,
  90. * <tt>false</tt> if runtime.
  91. * @see java.util.Locale#getDefault()
  92. * @see java.util.Locale#setDefault(java.util.Locale)
  93. */
  94. public BeanContextSupport(BeanContext peer, Locale lcle, boolean dtime) {
  95. this (peer, lcle, dtime, true);
  96. }
  97. /**
  98. * Create an instance using the specified locale
  99. *
  100. * @param peer The peer BeanContext we are
  101. * supplying an implementation for,
  102. * or <tt>null</tt> if this object
  103. * is its own peer
  104. * @param lcle The current Locale for this
  105. * <tt>BeanContext</tt>. If
  106. * <tt>lcle</tt> is <tt>null</tt>,
  107. * the default locale
  108. * is assigned to the <tt>BeanContext</tt>
  109. * instance.
  110. * @see java.util.Locale#getDefault()
  111. * @see java.util.Locale#setDefault(java.util.Locale)
  112. */
  113. public BeanContextSupport(BeanContext peer, Locale lcle) {
  114. this (peer, lcle, false, true);
  115. }
  116. /**
  117. * Create an instance using with a default locale
  118. *
  119. * @param peer The peer <tt>BeanContext</tt> we are
  120. * supplying an implementation for,
  121. * or <tt>null</tt> if this object
  122. * is its own peer
  123. */
  124. public BeanContextSupport(BeanContext peer) {
  125. this (peer, null, false, true);
  126. }
  127. /**
  128. * Create an instance that is not a delegate of another object
  129. */
  130. public BeanContextSupport() {
  131. this (null, null, false, true);
  132. }
  133. /**
  134. * Gets the instance of <tt>BeanContext</tt> that
  135. * this object is providing the implementation for.
  136. * @return the BeanContext instance
  137. */
  138. public BeanContext getBeanContextPeer() { return (BeanContext)getBeanContextChildPeer(); }
  139. /**
  140. * <p>
  141. * The instantiateChild method is a convenience hook
  142. * in BeanContext to simplify
  143. * the task of instantiating a Bean, nested,
  144. * into a <tt>BeanContext</tt>.
  145. * </p>
  146. * <p>
  147. * The semantics of the beanName parameter are defined by java.beans.Beans.instantate.
  148. * </p>
  149. *
  150. * @param beanName the name of the Bean to instantiate within this BeanContext
  151. * @throws IOException if there is an I/O error when the bean is being deserialized
  152. * @throws ClassNotFoundException if the class
  153. * identified by the beanName parameter is not found
  154. * @return the new object
  155. */
  156. public Object instantiateChild(String beanName)
  157. throws IOException, ClassNotFoundException {
  158. BeanContext bc = getBeanContextPeer();
  159. return Beans.instantiate(bc.getClass().getClassLoader(), beanName, bc);
  160. }
  161. /**
  162. * Gets the number of children currently nested in
  163. * this BeanContext.
  164. *
  165. * @return number of children
  166. */
  167. public int size() {
  168. synchronized(children) {
  169. return children.size();
  170. }
  171. }
  172. /**
  173. * Reports whether or not this
  174. * <tt>BeanContext</tt> is empty.
  175. * A <tt>BeanContext</tt> is considered
  176. * empty when it contains zero
  177. * nested children.
  178. * @return if there are not children
  179. */
  180. public boolean isEmpty() {
  181. synchronized(children) {
  182. return children.isEmpty();
  183. }
  184. }
  185. /**
  186. * Determines whether or not the specified object
  187. * is currently a child of this <tt>BeanContext</tt>.
  188. * @param o the Object in question
  189. * @return if this object is a child
  190. */
  191. public boolean contains(Object o) {
  192. synchronized(children) {
  193. return children.containsKey(o);
  194. }
  195. }
  196. /**
  197. * Determines whether or not the specified object
  198. * is currently a child of this <tt>BeanContext</tt>.
  199. * @param o the Object in question
  200. * @return if this object is a child
  201. */
  202. public boolean containsKey(Object o) {
  203. synchronized(children) {
  204. return children.containsKey(o);
  205. }
  206. }
  207. /**
  208. * Gets all JavaBean or <tt>BeanContext</tt> instances
  209. * currently nested in this <tt>BeanContext</tt>.
  210. * @return an <tt>Iterator</tt> of the nested children
  211. */
  212. public Iterator iterator() {
  213. synchronized(children) {
  214. return new BCSIterator(children.keySet().iterator());
  215. }
  216. }
  217. /**
  218. * Gets all JavaBean or <tt>BeanContext</tt>
  219. * instances currently nested in this BeanContext.
  220. */
  221. public Object[] toArray() {
  222. synchronized(children) {
  223. return children.keySet().toArray();
  224. }
  225. }
  226. /**
  227. * Gets an array containing all children of
  228. * this <tt>BeanContext</tt> that match
  229. * the types contained in arry.
  230. * @param arry The array of object
  231. * types that are of interest.
  232. * @return an array of children
  233. */
  234. public Object[] toArray(Object[] arry) {
  235. synchronized(children) {
  236. return children.keySet().toArray(arry);
  237. }
  238. }
  239. /************************************************************************/
  240. /**
  241. * protected final subclass that encapsulates an iterator but implements
  242. * a noop remove() method.
  243. */
  244. protected static final class BCSIterator implements Iterator {
  245. BCSIterator(Iterator i) { super(); src = i; }
  246. public boolean hasNext() { return src.hasNext(); }
  247. public Object next() { return src.next(); }
  248. public void remove() { /* do nothing */ }
  249. private Iterator src;
  250. }
  251. /************************************************************************/
  252. /*
  253. * protected nested class containing per child information, an instance
  254. * of which is associated with each child in the "children" hashtable.
  255. * subclasses can extend this class to include their own per-child state.
  256. *
  257. * Note that this 'value' is serialized with the corresponding child 'key'
  258. * when the BeanContextSupport is serialized.
  259. */
  260. protected class BCSChild implements Serializable {
  261. BCSChild(Object bcc, Object peer) {
  262. super();
  263. child = bcc;
  264. proxyPeer = peer;
  265. }
  266. Object getChild() { return child; }
  267. void setRemovePending(boolean v) { removePending = v; }
  268. boolean isRemovePending() { return removePending; }
  269. boolean isProxyPeer() { return proxyPeer != null; }
  270. Object getProxyPeer() { return proxyPeer; }
  271. /*
  272. * fields
  273. */
  274. private Object child;
  275. private Object proxyPeer;
  276. private transient boolean removePending;
  277. }
  278. /**
  279. * <p>
  280. * Subclasses can override this method to insert their own subclass
  281. * of Child without having to override add() or the other Collection
  282. * methods that add children to the set.
  283. * </p>
  284. *
  285. * @param targetChild the child to create the Child on behalf of
  286. * @param peer the peer if the tragetChild and the peer are related by an implementation of BeanContextProxy
  287. */
  288. protected BCSChild createBCSChild(Object targetChild, Object peer) {
  289. return new BCSChild(targetChild, peer);
  290. }
  291. /************************************************************************/
  292. /**
  293. * Adds/nests a child within this <tt>BeanContext</tt>.
  294. * <p>
  295. * Invoked as a side effect of java.beans.Beans.instantiate().
  296. * If the child object is not valid for adding then this method
  297. * throws an IllegalStateException.
  298. * </p>
  299. *
  300. *
  301. * @param targetChild The child objects to nest
  302. * within this <tt>BeanContext</tt>
  303. * @return true if the child was added successfully.
  304. * @see #validatePendingAdd
  305. */
  306. public boolean add(Object targetChild) {
  307. if (targetChild == null) throw new IllegalArgumentException();
  308. // The specification requires that we do nothing if the child
  309. // is already nested herein.
  310. if (children.containsKey(targetChild)) return false; // test before locking
  311. synchronized(BeanContext.globalHierarchyLock) {
  312. if (children.containsKey(targetChild)) return false; // check again
  313. if (!validatePendingAdd(targetChild)) {
  314. throw new IllegalStateException();
  315. }
  316. // The specification requires that we invoke setBeanContext() on the
  317. // newly added child if it implements the java.beans.beancontext.BeanContextChild interface
  318. BeanContextChild cbcc = getChildBeanContextChild(targetChild);
  319. BeanContextChild bccp = null;
  320. synchronized(targetChild) {
  321. if (targetChild instanceof BeanContextProxy) {
  322. bccp = ((BeanContextProxy)targetChild).getBeanContextProxy();
  323. if (bccp == null) throw new NullPointerException("BeanContextPeer.getBeanContextProxy()");
  324. }
  325. BCSChild bcsc = createBCSChild(targetChild, bccp);
  326. BCSChild pbcsc = null;
  327. synchronized (children) {
  328. children.put(targetChild, bcsc);
  329. if (bccp != null) children.put(bccp, pbcsc = createBCSChild(bccp, targetChild));
  330. }
  331. if (cbcc != null) synchronized(cbcc) {
  332. try {
  333. cbcc.setBeanContext(getBeanContextPeer());
  334. } catch (PropertyVetoException pve) {
  335. synchronized (children) {
  336. children.remove(targetChild);
  337. if (bccp != null) children.remove(bccp);
  338. }
  339. throw new IllegalStateException();
  340. }
  341. cbcc.addPropertyChangeListener("beanContext", childPCL);
  342. cbcc.addVetoableChangeListener("beanContext", childVCL);
  343. }
  344. Visibility v = getChildVisibility(targetChild);
  345. if (v != null) {
  346. if (okToUseGui)
  347. v.okToUseGui();
  348. else
  349. v.dontUseGui();
  350. }
  351. if (getChildSerializable(targetChild) != null) serializable++;
  352. childJustAddedHook(targetChild, bcsc);
  353. if (bccp != null) {
  354. v = getChildVisibility(bccp);
  355. if (v != null) {
  356. if (okToUseGui)
  357. v.okToUseGui();
  358. else
  359. v.dontUseGui();
  360. }
  361. if (getChildSerializable(bccp) != null) serializable++;
  362. childJustAddedHook(bccp, pbcsc);
  363. }
  364. }
  365. // The specification requires that we fire a notification of the change
  366. fireChildrenAdded(new BeanContextMembershipEvent(getBeanContextPeer(), bccp == null ? new Object[] { targetChild } : new Object[] { targetChild, bccp } ));
  367. }
  368. return true;
  369. }
  370. /**
  371. * Removes a child from this BeanContext. If the child object is not
  372. * for adding then this method throws an IllegalStateException.
  373. * @param targetChild The child objects to remove
  374. * @see #validatePendingRemove
  375. */
  376. public boolean remove(Object targetChild) {
  377. return remove(targetChild, true);
  378. }
  379. /**
  380. * internal remove used when removal caused by
  381. * unexpected <tt>setBeanContext</tt> or
  382. * by <tt>remove()</tt> invocation.
  383. * @param targetChild the JavaBean, BeanContext, or Object to be removed
  384. * @param callChildSetBC used to indicate that
  385. * the child should be notified that it is no
  386. * longer nested in this <tt>BeanContext</tt>.
  387. */
  388. protected boolean remove(Object targetChild, boolean callChildSetBC) {
  389. if (targetChild == null) throw new IllegalArgumentException();
  390. synchronized(BeanContext.globalHierarchyLock) {
  391. if (!containsKey(targetChild)) return false;
  392. if (!validatePendingRemove(targetChild)) {
  393. throw new IllegalStateException();
  394. }
  395. BCSChild bcsc = (BCSChild)children.get(targetChild);
  396. BCSChild pbcsc = null;
  397. Object peer = null;
  398. // we are required to notify the child that it is no longer nested here if
  399. // it implements java.beans.beancontext.BeanContextChild
  400. synchronized(targetChild) {
  401. if (callChildSetBC) {
  402. BeanContextChild cbcc = getChildBeanContextChild(targetChild);
  403. if (cbcc != null) synchronized(cbcc) {
  404. cbcc.removePropertyChangeListener("beanContext", childPCL);
  405. cbcc.removeVetoableChangeListener("beanContext", childVCL);
  406. try {
  407. cbcc.setBeanContext(null);
  408. } catch (PropertyVetoException pve1) {
  409. cbcc.addPropertyChangeListener("beanContext", childPCL);
  410. cbcc.addVetoableChangeListener("beanContext", childVCL);
  411. throw new IllegalStateException();
  412. }
  413. }
  414. }
  415. synchronized (children) {
  416. children.remove(targetChild);
  417. if (bcsc.isProxyPeer()) {
  418. pbcsc = (BCSChild)children.get(peer = bcsc.getProxyPeer());
  419. children.remove(peer);
  420. }
  421. }
  422. if (getChildSerializable(targetChild) != null) serializable--;
  423. childJustRemovedHook(targetChild, bcsc);
  424. if (peer != null) {
  425. if (getChildSerializable(peer) != null) serializable--;
  426. childJustRemovedHook(peer, pbcsc);
  427. }
  428. }
  429. fireChildrenRemoved(new BeanContextMembershipEvent(getBeanContextPeer(), peer == null ? new Object[] { targetChild } : new Object[] { targetChild, peer } ));
  430. }
  431. return true;
  432. }
  433. /**
  434. * Tests to see if all objects in the
  435. * specified <tt>Collection</tt> are children of
  436. * this <tt>BeanContext</tt>.
  437. * @param c the specified <tt>Collection</tt>
  438. *
  439. * @return <tt>true</tt> if all objects
  440. * in the collection are children of
  441. * this <tt>BeanContext</tt>, false if not.
  442. */
  443. public boolean containsAll(Collection c) {
  444. synchronized(children) {
  445. Iterator i = c.iterator();
  446. while (i.hasNext())
  447. if(!contains(i.next()))
  448. return false;
  449. return true;
  450. }
  451. }
  452. /**
  453. * add Collection to set of Children (Unsupported)
  454. * implementations must synchronized on the hierarchy lock and "children" protected field
  455. * @throws UnsupportedOperationException
  456. */
  457. public boolean addAll(Collection c) {
  458. throw new UnsupportedOperationException();
  459. }
  460. /**
  461. * remove all specified children (Unsupported)
  462. * implementations must synchronized on the hierarchy lock and "children" protected field
  463. * @throws UnsupportedOperationException
  464. */
  465. public boolean removeAll(Collection c) {
  466. throw new UnsupportedOperationException();
  467. }
  468. /**
  469. * retain only specified children (Unsupported)
  470. * implementations must synchronized on the hierarchy lock and "children" protected field
  471. * @throws UnsupportedOperationException
  472. */
  473. public boolean retainAll(Collection c) {
  474. throw new UnsupportedOperationException();
  475. }
  476. /**
  477. * clear the children (Unsupported)
  478. * implementations must synchronized on the hierarchy lock and "children" protected field
  479. * @throws UnsupportedOperationException
  480. */
  481. public void clear() {
  482. throw new UnsupportedOperationException();
  483. }
  484. /**
  485. * Adds a BeanContextMembershipListener
  486. *
  487. * @param bcml the BeanContextMembershipListener to add
  488. * @throws NullPointerException
  489. */
  490. public void addBeanContextMembershipListener(BeanContextMembershipListener bcml) {
  491. if (bcml == null) throw new NullPointerException("listener");
  492. synchronized(bcmListeners) {
  493. if (bcmListeners.contains(bcml))
  494. return;
  495. else
  496. bcmListeners.add(bcml);
  497. }
  498. }
  499. /**
  500. * Removes a BeanContextMembershipListener
  501. *
  502. * @param bcml the BeanContextMembershipListener to remove
  503. * @throws NullPointerException
  504. */
  505. public void removeBeanContextMembershipListener(BeanContextMembershipListener bcml) {
  506. if (bcml == null) throw new NullPointerException("listener");
  507. synchronized(bcmListeners) {
  508. if (!bcmListeners.contains(bcml))
  509. return;
  510. else
  511. bcmListeners.remove(bcml);
  512. }
  513. }
  514. /**
  515. * @param name the name of the resource requested.
  516. * @param bcc the child object making the request.
  517. *
  518. * @return the requested resource as an InputStream
  519. * @throws NullPointerException
  520. */
  521. public InputStream getResourceAsStream(String name, BeanContextChild bcc) {
  522. if (name == null) throw new NullPointerException("name");
  523. if (bcc == null) throw new NullPointerException("bcc");
  524. if (containsKey(bcc)) {
  525. ClassLoader cl = bcc.getClass().getClassLoader();
  526. return cl != null ? cl.getResourceAsStream(name)
  527. : ClassLoader.getSystemResourceAsStream(name);
  528. } else throw new IllegalArgumentException("Not a valid child");
  529. }
  530. /**
  531. * @param name the name of the resource requested.
  532. * @param bcc the child object making the request.
  533. *
  534. * @return the requested resource as an InputStream
  535. */
  536. public URL getResource(String name, BeanContextChild bcc) {
  537. if (name == null) throw new NullPointerException("name");
  538. if (bcc == null) throw new NullPointerException("bcc");
  539. if (containsKey(bcc)) {
  540. ClassLoader cl = bcc.getClass().getClassLoader();
  541. return cl != null ? cl.getResource(name)
  542. : ClassLoader.getSystemResource(name);
  543. } else throw new IllegalArgumentException("Not a valid child");
  544. }
  545. /**
  546. * Sets the new design time value for this <tt>BeanContext</tt>.
  547. * @param dTime the new designTime value
  548. */
  549. public synchronized void setDesignTime(boolean dTime) {
  550. if (designTime != dTime) {
  551. designTime = dTime;
  552. firePropertyChange("designMode", Boolean.valueOf(!dTime), Boolean.valueOf(dTime));
  553. }
  554. }
  555. /**
  556. * Reports whether or not this object is in
  557. * currently in design time mode.
  558. * @return <tt>true</tt> if in design time mode,
  559. * <tt>false</tt> if not
  560. */
  561. public synchronized boolean isDesignTime() { return designTime; }
  562. /**
  563. * Sets the locale of this BeanContext.
  564. * @param newLocale the new locale. This method call will have
  565. * no effect if newLocale is <CODE>null</CODE>.
  566. * @throws PropertyVetoException if the new value is rejected
  567. */
  568. public synchronized void setLocale(Locale newLocale) throws PropertyVetoException {
  569. if ((locale != null && !locale.equals(newLocale)) || newLocale != null) {
  570. Locale old = locale;
  571. fireVetoableChange("locale", old, newLocale); // throws
  572. locale = newLocale;
  573. firePropertyChange("locale", old, newLocale);
  574. }
  575. }
  576. /**
  577. * Gets the locale for this <tt>BeanContext</tt>.
  578. *
  579. * @return the current Locale of the <tt>BeanContext</tt>
  580. */
  581. public synchronized Locale getLocale() { return locale; }
  582. /**
  583. * <p>
  584. * This method is typically called from the environment in order to determine
  585. * if the implementor "needs" a GUI.
  586. * </p>
  587. * <p>
  588. * The algorithm used herein tests the BeanContextPeer, and its current children
  589. * to determine if they are either Containers, Components, or if they implement
  590. * Visibility and return needsGui() == true.
  591. * </p>
  592. * @return <tt>true</tt> if the implementor needs a GUI
  593. */
  594. public synchronized boolean needsGui() {
  595. BeanContext bc = getBeanContextPeer();
  596. if (bc != this) {
  597. if (bc instanceof Visibility) return ((Visibility)bc).needsGui();
  598. if (bc instanceof Container || bc instanceof Component)
  599. return true;
  600. }
  601. synchronized(children) {
  602. for (Iterator i = children.keySet().iterator(); i.hasNext();) {
  603. Object c = i.next();
  604. try {
  605. return ((Visibility)c).needsGui();
  606. } catch (ClassCastException cce) {
  607. // do nothing ...
  608. }
  609. if (c instanceof Container || c instanceof Component)
  610. return true;
  611. }
  612. }
  613. return false;
  614. }
  615. /**
  616. * notify this instance that it may no longer render a GUI.
  617. */
  618. public synchronized void dontUseGui() {
  619. if (okToUseGui) {
  620. okToUseGui = false;
  621. // lets also tell the Children that can that they may not use their GUI's
  622. synchronized(children) {
  623. for (Iterator i = children.keySet().iterator(); i.hasNext();) {
  624. Visibility v = getChildVisibility(i.next());
  625. if (v != null) v.dontUseGui();
  626. }
  627. }
  628. }
  629. }
  630. /**
  631. * Notify this instance that it may now render a GUI
  632. */
  633. public synchronized void okToUseGui() {
  634. if (!okToUseGui) {
  635. okToUseGui = true;
  636. // lets also tell the Children that can that they may use their GUI's
  637. synchronized(children) {
  638. for (Iterator i = children.keySet().iterator(); i.hasNext();) {
  639. Visibility v = getChildVisibility(i.next());
  640. if (v != null) v.okToUseGui();
  641. }
  642. }
  643. }
  644. }
  645. /**
  646. * Used to determine if the <tt>BeanContext</tt>
  647. * child is avoiding using its GUI.
  648. * @return is this instance avoiding using its GUI?
  649. * @see Visibility
  650. */
  651. public boolean avoidingGui() {
  652. return !okToUseGui && needsGui();
  653. }
  654. /**
  655. * Is this <tt>BeanContext</tt> in the
  656. * process of being serialized?
  657. * @return if this <tt>BeanContext</tt> is
  658. * currently being serialized
  659. */
  660. public boolean isSerializing() { return serializing; }
  661. /**
  662. * Returns an iterator of all children
  663. * of this <tt>BeanContext</tt>.
  664. * @return an iterator for all the current BCSChild values
  665. */
  666. protected Iterator bcsChildren() { synchronized(children) { return children.values().iterator(); } }
  667. /**
  668. * called by writeObject after defaultWriteObject() but prior to
  669. * serialization of currently serializable children.
  670. *
  671. * This method may be overridden by subclasses to perform custom
  672. * serialization of their state prior to this superclass serializing
  673. * the children.
  674. *
  675. * This method should not however be used by subclasses to replace their
  676. * own implementation (if any) of writeObject().
  677. */
  678. protected void bcsPreSerializationHook(ObjectOutputStream oos) throws IOException {
  679. }
  680. /**
  681. * called by readObject after defaultReadObject() but prior to
  682. * deserialization of any children.
  683. *
  684. * This method may be overridden by subclasses to perform custom
  685. * deserialization of their state prior to this superclass deserializing
  686. * the children.
  687. *
  688. * This method should not however be used by subclasses to replace their
  689. * own implementation (if any) of readObject().
  690. */
  691. protected void bcsPreDeserializationHook(ObjectInputStream ois) throws IOException, ClassNotFoundException {
  692. }
  693. /**
  694. * Called by readObject with the newly deserialized child and BCSChild.
  695. * @param child the newly deserialized child
  696. * @param bcsc the newly deserialized BCSChild
  697. */
  698. protected void childDeserializedHook(Object child, BCSChild bcsc) {
  699. synchronized(children) {
  700. children.put(child, bcsc);
  701. }
  702. }
  703. /**
  704. * Used by writeObject to serialize a Collection.
  705. * @param oos the <tt>ObjectOutputStream</tt>
  706. * to use during serialization
  707. * @param coll the <tt>Collection</tt> to serialize
  708. * @throws IOException if serialization failed
  709. */
  710. protected final void serialize(ObjectOutputStream oos, Collection coll) throws IOException {
  711. int count = 0;
  712. Object[] objects = coll.toArray();
  713. for (int i = 0; i < objects.length; i++) {
  714. if (objects[i] instanceof Serializable)
  715. count++;
  716. else
  717. objects[i] = null;
  718. }
  719. oos.writeInt(count); // number of subsequent objects
  720. for (int i = 0; count > 0; i++) {
  721. Object o = objects[i];
  722. if (o != null) {
  723. oos.writeObject(o);
  724. count--;
  725. }
  726. }
  727. }
  728. /**
  729. * used by readObject to deserialize a collection.
  730. * @param ois the ObjectInputStream to use
  731. * @param coll the Collection
  732. */
  733. protected final void deserialize(ObjectInputStream ois, Collection coll) throws IOException, ClassNotFoundException {
  734. int count = 0;
  735. count = ois.readInt();
  736. while (count-- > 0) {
  737. coll.add(ois.readObject());
  738. }
  739. }
  740. /**
  741. * Used to serialize all children of
  742. * this <tt>BeanContext</tt>.
  743. * @param oos the <tt>ObjectOutputStream</tt>
  744. * to use during serialization
  745. * @throws IOException if serialization failed
  746. */
  747. public final void writeChildren(ObjectOutputStream oos) throws IOException {
  748. if (serializable <= 0) return;
  749. boolean prev = serializing;
  750. serializing = true;
  751. int count = 0;
  752. synchronized(children) {
  753. Iterator i = children.entrySet().iterator();
  754. while (i.hasNext() && count < serializable) {
  755. Map.Entry entry = (Map.Entry)i.next();
  756. if (entry.getKey() instanceof Serializable) {
  757. try {
  758. oos.writeObject(entry.getKey()); // child
  759. oos.writeObject(entry.getValue()); // BCSChild
  760. } catch (IOException ioe) {
  761. serializing = prev;
  762. throw ioe;
  763. }
  764. count++;
  765. }
  766. }
  767. }
  768. serializing = prev;
  769. if (count != serializable) {
  770. throw new IOException("wrote different number of children than expected");
  771. }
  772. }
  773. /**
  774. * Serialize the BeanContextSupport, if this instance has a distinct
  775. * peer (that is this object is acting as a delegate for another) then
  776. * the children of this instance are not serialized here due to a
  777. * 'chicken and egg' problem that occurs on deserialization of the
  778. * children at the same time as this instance.
  779. *
  780. * Therefore in situations where there is a distinct peer to this instance
  781. * it should always call writeObject() followed by writeChildren() and
  782. * readObject() followed by readChildren().
  783. *
  784. * @param oos the ObjectOutputStream
  785. */
  786. private synchronized void writeObject(ObjectOutputStream oos) throws IOException, ClassNotFoundException {
  787. serializing = true;
  788. synchronized (BeanContext.globalHierarchyLock) {
  789. try {
  790. oos.defaultWriteObject(); // serialize the BeanContextSupport object
  791. bcsPreSerializationHook(oos);
  792. if (serializable > 0 && this.equals(getBeanContextPeer()))
  793. writeChildren(oos);
  794. serialize(oos, (Collection)bcmListeners);
  795. } finally {
  796. serializing = false;
  797. }
  798. }
  799. }
  800. /**
  801. * When an instance of this class is used as a delegate for the
  802. * implementation of the BeanContext protocols (and its subprotocols)
  803. * there exists a 'chicken and egg' problem during deserialization
  804. */
  805. public final void readChildren(ObjectInputStream ois) throws IOException, ClassNotFoundException {
  806. int count = serializable;
  807. while (count-- > 0) {
  808. Object child = null;
  809. BeanContextSupport.BCSChild bscc = null;
  810. try {
  811. child = ois.readObject();
  812. bscc = (BeanContextSupport.BCSChild)ois.readObject();
  813. } catch (IOException ioe) {
  814. continue;
  815. } catch (ClassNotFoundException cnfe) {
  816. continue;
  817. }
  818. synchronized(child) {
  819. BeanContextChild bcc = null;
  820. try {
  821. bcc = (BeanContextChild)child;
  822. } catch (ClassCastException cce) {
  823. // do nothing;
  824. }
  825. if (bcc != null) {
  826. try {
  827. bcc.setBeanContext(getBeanContextPeer());
  828. bcc.addPropertyChangeListener("beanContext", childPCL);
  829. bcc.addVetoableChangeListener("beanContext", childVCL);
  830. } catch (PropertyVetoException pve) {
  831. continue;
  832. }
  833. }
  834. childDeserializedHook(child, bscc);
  835. }
  836. }
  837. }
  838. /**
  839. * deserialize contents ... if this instance has a distinct peer the
  840. * children are *not* serialized here, the peer's readObject() must call
  841. * readChildren() after deserializing this instance.
  842. */
  843. private synchronized void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {
  844. synchronized(BeanContext.globalHierarchyLock) {
  845. ois.defaultReadObject();
  846. initialize();
  847. bcsPreDeserializationHook(ois);
  848. if (serializable > 0 && this.equals(getBeanContextPeer()))
  849. readChildren(ois);
  850. deserialize(ois, bcmListeners = new ArrayList(1));
  851. }
  852. }
  853. /**
  854. * subclasses may envelope to monitor veto child property changes.
  855. */
  856. public void vetoableChange(PropertyChangeEvent pce) throws PropertyVetoException {
  857. String propertyName = pce.getPropertyName();
  858. Object source = pce.getSource();
  859. synchronized(children) {
  860. if ("beanContext".equals(propertyName) &&
  861. containsKey(source) &&
  862. !getBeanContextPeer().equals(pce.getNewValue())
  863. ) {
  864. if (!validatePendingRemove(source)) {
  865. throw new PropertyVetoException("current BeanContext vetoes setBeanContext()", pce);
  866. } else ((BCSChild)children.get(source)).setRemovePending(true);
  867. }
  868. }
  869. }
  870. /**
  871. * subclasses may envelope to monitor child property changes.
  872. */
  873. public void propertyChange(PropertyChangeEvent pce) {
  874. String propertyName = pce.getPropertyName();
  875. Object source = pce.getSource();
  876. synchronized(children) {
  877. if ("beanContext".equals(propertyName) &&
  878. containsKey(source) &&
  879. ((BCSChild)children.get(source)).isRemovePending()) {
  880. BeanContext bc = getBeanContextPeer();
  881. if (bc.equals(pce.getOldValue()) && !bc.equals(pce.getNewValue())) {
  882. remove(source, false);
  883. } else {
  884. ((BCSChild)children.get(source)).setRemovePending(false);
  885. }
  886. }
  887. }
  888. }
  889. /**
  890. * <p>
  891. * Subclasses of this class may override, or envelope, this method to
  892. * add validation behavior for the BeanContext to examine child objects
  893. * immediately prior to their being added to the BeanContext.
  894. * </p>
  895. *
  896. * @return true iff the child may be added to this BeanContext, otherwise false.
  897. */
  898. protected boolean validatePendingAdd(Object targetChild) {
  899. return true;
  900. }
  901. /**
  902. * <p>
  903. * Subclasses of this class may override, or envelope, this method to
  904. * add validation behavior for the BeanContext to examine child objects
  905. * immediately prior to their being removed from the BeanContext.
  906. * </p>
  907. *
  908. * @return true iff the child may be removed from this BeanContext, otherwise false.
  909. */
  910. protected boolean validatePendingRemove(Object targetChild) {
  911. return true;
  912. }
  913. /**
  914. * subclasses may override this method to simply extend add() semantics
  915. * after the child has been added and before the event notification has
  916. * occurred. The method is called with the child synchronized.
  917. */
  918. protected void childJustAddedHook(Object child, BCSChild bcsc) {
  919. }
  920. /**
  921. * subclasses may override this method to simply extend remove() semantics
  922. * after the child has been removed and before the event notification has
  923. * occurred. The method is called with the child synchronized.
  924. */
  925. protected void childJustRemovedHook(Object child, BCSChild bcsc) {
  926. }
  927. /**
  928. * Gets the Component (if any) associated with the specified child.
  929. * @param child the specified child
  930. * @return the Component (if any) associated with the specified child.
  931. */
  932. protected static final Visibility getChildVisibility(Object child) {
  933. try {
  934. return (Visibility)child;
  935. } catch (ClassCastException cce) {
  936. return null;
  937. }
  938. }
  939. /**
  940. * Gets the Serializable (if any) associated with the specified Child
  941. * @param child the specified child
  942. * @return the Serializable (if any) associated with the specified Child
  943. */
  944. protected static final Serializable getChildSerializable(Object child) {
  945. try {
  946. return (Serializable)child;
  947. } catch (ClassCastException cce) {
  948. return null;
  949. }
  950. }
  951. /**
  952. * Gets the PropertyChangeListener
  953. * (if any) of the specified child
  954. * @param child the specified child
  955. * @return the PropertyChangeListener (if any) of the specified child
  956. */
  957. protected static final PropertyChangeListener getChildPropertyChangeListener(Object child) {
  958. try {
  959. return (PropertyChangeListener)child;
  960. } catch (ClassCastException cce) {
  961. return null;
  962. }
  963. }
  964. /**
  965. * Gets the VetoableChangeListener
  966. * (if any) of the specified child
  967. * @param child the specified child
  968. * @return the VetoableChangeListener (if any) of the specified child
  969. */
  970. protected static final VetoableChangeListener getChildVetoableChangeListener(Object child) {
  971. try {
  972. return (VetoableChangeListener)child;
  973. } catch (ClassCastException cce) {
  974. return null;
  975. }
  976. }
  977. /**
  978. * Gets the BeanContextMembershipListener
  979. * (if any) of the specified child
  980. * @param child the specified child
  981. * @return the BeanContextMembershipListener (if any) of the specified child
  982. */
  983. protected static final BeanContextMembershipListener getChildBeanContextMembershipListener(Object child) {
  984. try {
  985. return (BeanContextMembershipListener)child;
  986. } catch (ClassCastException cce) {
  987. return null;
  988. }
  989. }
  990. /**
  991. * Gets the BeanContextChild (if any) of the specified child
  992. * @param child the specified child
  993. * @return the BeanContextChild (if any) of the specified child
  994. * @throws IllegalArgumentException if child implements both BeanContextChild and BeanContextProxy
  995. */
  996. protected static final BeanContextChild getChildBeanContextChild(Object child) {
  997. try {
  998. BeanContextChild bcc = (BeanContextChild)child;
  999. if (child instanceof BeanContextChild && child instanceof BeanContextProxy)
  1000. throw new IllegalArgumentException("child cannot implement both BeanContextChild and BeanContextProxy");
  1001. else
  1002. return bcc;
  1003. } catch (ClassCastException cce) {
  1004. try {
  1005. return ((BeanContextProxy)child).getBeanContextProxy();
  1006. } catch (ClassCastException cce1) {
  1007. return null;
  1008. }
  1009. }
  1010. }
  1011. /**
  1012. * Fire a BeanContextshipEvent on the BeanContextMembershipListener interface
  1013. */
  1014. protected final void fireChildrenAdded(BeanContextMembershipEvent bcme) {
  1015. Object[] copy;
  1016. synchronized(bcmListeners) { copy = bcmListeners.toArray(); }
  1017. for (int i = 0; i < copy.length; i++)
  1018. ((BeanContextMembershipListener)copy[i]).childrenAdded(bcme);
  1019. }
  1020. /**
  1021. * Fire a BeanContextshipEvent on the BeanContextMembershipListener interface
  1022. */
  1023. protected final void fireChildrenRemoved(BeanContextMembershipEvent bcme) {
  1024. Object[] copy;
  1025. synchronized(bcmListeners) { copy = bcmListeners.toArray(); }
  1026. for (int i = 0; i < copy.length; i++)
  1027. ((BeanContextMembershipListener)copy[i]).childrenRemoved(bcme);
  1028. }
  1029. /**
  1030. * protected method called from constructor and readObject to initialize
  1031. * transient state of BeanContextSupport instance.
  1032. *
  1033. * This class uses this method to instantiate inner class listeners used
  1034. * to monitor PropertyChange and VetoableChange events on children.
  1035. *
  1036. * subclasses may envelope this method to add their own initialization
  1037. * behavior
  1038. */
  1039. protected synchronized void initialize() {
  1040. children = new HashMap(serializable + 1);
  1041. bcmListeners = new ArrayList(1);
  1042. childPCL = new PropertyChangeListener() {
  1043. /*
  1044. * this adaptor is used by the BeanContextSupport class to forward
  1045. * property changes from a child to the BeanContext, avoiding
  1046. * accidential serialization of the BeanContext by a badly
  1047. * behaved Serializable child.
  1048. */
  1049. public void propertyChange(PropertyChangeEvent pce) {
  1050. BeanContextSupport.this.propertyChange(pce);
  1051. }
  1052. };
  1053. childVCL = new VetoableChangeListener() {
  1054. /*
  1055. * this adaptor is used by the BeanContextSupport class to forward
  1056. * vetoable changes from a child to the BeanContext, avoiding
  1057. * accidential serialization of the BeanContext by a badly
  1058. * behaved Serializable child.
  1059. */
  1060. public void vetoableChange(PropertyChangeEvent pce) throws PropertyVetoException {
  1061. BeanContextSupport.this.vetoableChange(pce);
  1062. }
  1063. };
  1064. }
  1065. /**
  1066. * Gets a copy of the this BeanContext's children.
  1067. * @return a copy of the current nested children
  1068. */
  1069. protected final Object[] copyChildren() {
  1070. synchronized(children) { return children.keySet().toArray(); }
  1071. }
  1072. /**
  1073. * Tests to see if two class objects,
  1074. * or their names are equal.
  1075. * @param first the first object
  1076. * @param second the second object
  1077. * @return true if equal, false if not
  1078. */
  1079. protected static final boolean classEquals(Class first, Class second) {
  1080. return first.equals(second) || first.getName().equals(second.getName());
  1081. }
  1082. /*
  1083. * fields
  1084. */
  1085. /**
  1086. * all accesses to the <code> protected HashMap children </code> field
  1087. * shall be synchronized on that object.
  1088. */
  1089. protected transient HashMap children;
  1090. private int serializable = 0; // children serializable
  1091. /**
  1092. * all accesses to the <code> protected ArrayList bcmListeners </code> field
  1093. * shall be synchronized on that object.
  1094. */
  1095. protected transient ArrayList bcmListeners;
  1096. //
  1097. /**
  1098. * The current locale of this BeanContext.
  1099. */
  1100. protected Locale locale;
  1101. /**
  1102. * A <tt>boolean</tt> indicating if this
  1103. * instance may now render a GUI.
  1104. */
  1105. protected boolean okToUseGui;
  1106. /**
  1107. * A <tt>boolean</tt> indicating whether or not
  1108. * this object is currently in design time mode.
  1109. */
  1110. protected boolean designTime;
  1111. /*
  1112. * transient
  1113. */
  1114. private transient PropertyChangeListener childPCL;
  1115. private transient VetoableChangeListener childVCL;
  1116. private transient boolean serializing;
  1117. }