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