1. /*
  2. * @(#)BeanContextSupport.java 1.46 03/01/13
  3. *
  4. * Copyright 2004 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. private static final long serialVersionUID = -5815286101609939109L;
  262. BCSChild(Object bcc, Object peer) {
  263. super();
  264. child = bcc;
  265. proxyPeer = peer;
  266. }
  267. Object getChild() { return child; }
  268. void setRemovePending(boolean v) { removePending = v; }
  269. boolean isRemovePending() { return removePending; }
  270. boolean isProxyPeer() { return proxyPeer != null; }
  271. Object getProxyPeer() { return proxyPeer; }
  272. /*
  273. * fields
  274. */
  275. private Object child;
  276. private Object proxyPeer;
  277. private transient boolean removePending;
  278. }
  279. /**
  280. * <p>
  281. * Subclasses can override this method to insert their own subclass
  282. * of Child without having to override add() or the other Collection
  283. * methods that add children to the set.
  284. * </p>
  285. *
  286. * @param targetChild the child to create the Child on behalf of
  287. * @param peer the peer if the tragetChild and the peer are related by an implementation of BeanContextProxy
  288. */
  289. protected BCSChild createBCSChild(Object targetChild, Object peer) {
  290. return new BCSChild(targetChild, peer);
  291. }
  292. /************************************************************************/
  293. /**
  294. * Adds/nests a child within this <tt>BeanContext</tt>.
  295. * <p>
  296. * Invoked as a side effect of java.beans.Beans.instantiate().
  297. * If the child object is not valid for adding then this method
  298. * throws an IllegalStateException.
  299. * </p>
  300. *
  301. *
  302. * @param targetChild The child objects to nest
  303. * within this <tt>BeanContext</tt>
  304. * @return true if the child was added successfully.
  305. * @see #validatePendingAdd
  306. */
  307. public boolean add(Object targetChild) {
  308. if (targetChild == null) throw new IllegalArgumentException();
  309. // The specification requires that we do nothing if the child
  310. // is already nested herein.
  311. if (children.containsKey(targetChild)) return false; // test before locking
  312. synchronized(BeanContext.globalHierarchyLock) {
  313. if (children.containsKey(targetChild)) return false; // check again
  314. if (!validatePendingAdd(targetChild)) {
  315. throw new IllegalStateException();
  316. }
  317. // The specification requires that we invoke setBeanContext() on the
  318. // newly added child if it implements the java.beans.beancontext.BeanContextChild interface
  319. BeanContextChild cbcc = getChildBeanContextChild(targetChild);
  320. BeanContextChild bccp = null;
  321. synchronized(targetChild) {
  322. if (targetChild instanceof BeanContextProxy) {
  323. bccp = ((BeanContextProxy)targetChild).getBeanContextProxy();
  324. if (bccp == null) throw new NullPointerException("BeanContextPeer.getBeanContextProxy()");
  325. }
  326. BCSChild bcsc = createBCSChild(targetChild, bccp);
  327. BCSChild pbcsc = null;
  328. synchronized (children) {
  329. children.put(targetChild, bcsc);
  330. if (bccp != null) children.put(bccp, pbcsc = createBCSChild(bccp, targetChild));
  331. }
  332. if (cbcc != null) synchronized(cbcc) {
  333. try {
  334. cbcc.setBeanContext(getBeanContextPeer());
  335. } catch (PropertyVetoException pve) {
  336. synchronized (children) {
  337. children.remove(targetChild);
  338. if (bccp != null) children.remove(bccp);
  339. }
  340. throw new IllegalStateException();
  341. }
  342. cbcc.addPropertyChangeListener("beanContext", childPCL);
  343. cbcc.addVetoableChangeListener("beanContext", childVCL);
  344. }
  345. Visibility v = getChildVisibility(targetChild);
  346. if (v != null) {
  347. if (okToUseGui)
  348. v.okToUseGui();
  349. else
  350. v.dontUseGui();
  351. }
  352. if (getChildSerializable(targetChild) != null) serializable++;
  353. childJustAddedHook(targetChild, bcsc);
  354. if (bccp != null) {
  355. v = getChildVisibility(bccp);
  356. if (v != null) {
  357. if (okToUseGui)
  358. v.okToUseGui();
  359. else
  360. v.dontUseGui();
  361. }
  362. if (getChildSerializable(bccp) != null) serializable++;
  363. childJustAddedHook(bccp, pbcsc);
  364. }
  365. }
  366. // The specification requires that we fire a notification of the change
  367. fireChildrenAdded(new BeanContextMembershipEvent(getBeanContextPeer(), bccp == null ? new Object[] { targetChild } : new Object[] { targetChild, bccp } ));
  368. }
  369. return true;
  370. }
  371. /**
  372. * Removes a child from this BeanContext. If the child object is not
  373. * for adding then this method throws an IllegalStateException.
  374. * @param targetChild The child objects to remove
  375. * @see #validatePendingRemove
  376. */
  377. public boolean remove(Object targetChild) {
  378. return remove(targetChild, true);
  379. }
  380. /**
  381. * internal remove used when removal caused by
  382. * unexpected <tt>setBeanContext</tt> or
  383. * by <tt>remove()</tt> invocation.
  384. * @param targetChild the JavaBean, BeanContext, or Object to be removed
  385. * @param callChildSetBC used to indicate that
  386. * the child should be notified that it is no
  387. * longer nested in this <tt>BeanContext</tt>.
  388. */
  389. protected boolean remove(Object targetChild, boolean callChildSetBC) {
  390. if (targetChild == null) throw new IllegalArgumentException();
  391. synchronized(BeanContext.globalHierarchyLock) {
  392. if (!containsKey(targetChild)) return false;
  393. if (!validatePendingRemove(targetChild)) {
  394. throw new IllegalStateException();
  395. }
  396. BCSChild bcsc = (BCSChild)children.get(targetChild);
  397. BCSChild pbcsc = null;
  398. Object peer = null;
  399. // we are required to notify the child that it is no longer nested here if
  400. // it implements java.beans.beancontext.BeanContextChild
  401. synchronized(targetChild) {
  402. if (callChildSetBC) {
  403. BeanContextChild cbcc = getChildBeanContextChild(targetChild);
  404. if (cbcc != null) synchronized(cbcc) {
  405. cbcc.removePropertyChangeListener("beanContext", childPCL);
  406. cbcc.removeVetoableChangeListener("beanContext", childVCL);
  407. try {
  408. cbcc.setBeanContext(null);
  409. } catch (PropertyVetoException pve1) {
  410. cbcc.addPropertyChangeListener("beanContext", childPCL);
  411. cbcc.addVetoableChangeListener("beanContext", childVCL);
  412. throw new IllegalStateException();
  413. }
  414. }
  415. }
  416. synchronized (children) {
  417. children.remove(targetChild);
  418. if (bcsc.isProxyPeer()) {
  419. pbcsc = (BCSChild)children.get(peer = bcsc.getProxyPeer());
  420. children.remove(peer);
  421. }
  422. }
  423. if (getChildSerializable(targetChild) != null) serializable--;
  424. childJustRemovedHook(targetChild, bcsc);
  425. if (peer != null) {
  426. if (getChildSerializable(peer) != null) serializable--;
  427. childJustRemovedHook(peer, pbcsc);
  428. }
  429. }
  430. fireChildrenRemoved(new BeanContextMembershipEvent(getBeanContextPeer(), peer == null ? new Object[] { targetChild } : new Object[] { targetChild, peer } ));
  431. }
  432. return true;
  433. }
  434. /**
  435. * Tests to see if all objects in the
  436. * specified <tt>Collection</tt> are children of
  437. * this <tt>BeanContext</tt>.
  438. * @param c the specified <tt>Collection</tt>
  439. *
  440. * @return <tt>true</tt> if all objects
  441. * in the collection are children of
  442. * this <tt>BeanContext</tt>, false if not.
  443. */
  444. public boolean containsAll(Collection c) {
  445. synchronized(children) {
  446. Iterator i = c.iterator();
  447. while (i.hasNext())
  448. if(!contains(i.next()))
  449. return false;
  450. return true;
  451. }
  452. }
  453. /**
  454. * add Collection to set of Children (Unsupported)
  455. * implementations must synchronized on the hierarchy lock and "children" protected field
  456. * @throws UnsupportedOperationException
  457. */
  458. public boolean addAll(Collection c) {
  459. throw new UnsupportedOperationException();
  460. }
  461. /**
  462. * remove all specified children (Unsupported)
  463. * implementations must synchronized on the hierarchy lock and "children" protected field
  464. * @throws UnsupportedOperationException
  465. */
  466. public boolean removeAll(Collection c) {
  467. throw new UnsupportedOperationException();
  468. }
  469. /**
  470. * retain only specified children (Unsupported)
  471. * implementations must synchronized on the hierarchy lock and "children" protected field
  472. * @throws UnsupportedOperationException
  473. */
  474. public boolean retainAll(Collection c) {
  475. throw new UnsupportedOperationException();
  476. }
  477. /**
  478. * clear the children (Unsupported)
  479. * implementations must synchronized on the hierarchy lock and "children" protected field
  480. * @throws UnsupportedOperationException
  481. */
  482. public void clear() {
  483. throw new UnsupportedOperationException();
  484. }
  485. /**
  486. * Adds a BeanContextMembershipListener
  487. *
  488. * @param bcml the BeanContextMembershipListener to add
  489. * @throws NullPointerException
  490. */
  491. public void addBeanContextMembershipListener(BeanContextMembershipListener bcml) {
  492. if (bcml == null) throw new NullPointerException("listener");
  493. synchronized(bcmListeners) {
  494. if (bcmListeners.contains(bcml))
  495. return;
  496. else
  497. bcmListeners.add(bcml);
  498. }
  499. }
  500. /**
  501. * Removes a BeanContextMembershipListener
  502. *
  503. * @param bcml the BeanContextMembershipListener to remove
  504. * @throws NullPointerException
  505. */
  506. public void removeBeanContextMembershipListener(BeanContextMembershipListener bcml) {
  507. if (bcml == null) throw new NullPointerException("listener");
  508. synchronized(bcmListeners) {
  509. if (!bcmListeners.contains(bcml))
  510. return;
  511. else
  512. bcmListeners.remove(bcml);
  513. }
  514. }
  515. /**
  516. * @param name the name of the resource requested.
  517. * @param bcc the child object making the request.
  518. *
  519. * @return the requested resource as an InputStream
  520. * @throws NullPointerException
  521. */
  522. public InputStream getResourceAsStream(String name, BeanContextChild bcc) {
  523. if (name == null) throw new NullPointerException("name");
  524. if (bcc == null) throw new NullPointerException("bcc");
  525. if (containsKey(bcc)) {
  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 bcc 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. if (containsKey(bcc)) {
  541. ClassLoader cl = bcc.getClass().getClassLoader();
  542. return cl != null ? cl.getResource(name)
  543. : ClassLoader.getSystemResource(name);
  544. } else throw new IllegalArgumentException("Not a valid child");
  545. }
  546. /**
  547. * Sets the new design time value for this <tt>BeanContext</tt>.
  548. * @param dTime the new designTime value
  549. */
  550. public synchronized void setDesignTime(boolean dTime) {
  551. if (designTime != dTime) {
  552. designTime = dTime;
  553. firePropertyChange("designMode", Boolean.valueOf(!dTime), Boolean.valueOf(dTime));
  554. }
  555. }
  556. /**
  557. * Reports whether or not this object is in
  558. * currently in design time mode.
  559. * @return <tt>true</tt> if in design time mode,
  560. * <tt>false</tt> if not
  561. */
  562. public synchronized boolean isDesignTime() { return designTime; }
  563. /**
  564. * Sets the locale of this BeanContext.
  565. * @param newLocale the new locale. This method call will have
  566. * no effect if newLocale is <CODE>null</CODE>.
  567. * @throws PropertyVetoException if the new value is rejected
  568. */
  569. public synchronized void setLocale(Locale newLocale) throws PropertyVetoException {
  570. if ((locale != null && !locale.equals(newLocale)) && newLocale != null) {
  571. Locale old = locale;
  572. fireVetoableChange("locale", old, newLocale); // throws
  573. locale = newLocale;
  574. firePropertyChange("locale", old, newLocale);
  575. }
  576. }
  577. /**
  578. * Gets the locale for this <tt>BeanContext</tt>.
  579. *
  580. * @return the current Locale of the <tt>BeanContext</tt>
  581. */
  582. public synchronized Locale getLocale() { return locale; }
  583. /**
  584. * <p>
  585. * This method is typically called from the environment in order to determine
  586. * if the implementor "needs" a GUI.
  587. * </p>
  588. * <p>
  589. * The algorithm used herein tests the BeanContextPeer, and its current children
  590. * to determine if they are either Containers, Components, or if they implement
  591. * Visibility and return needsGui() == true.
  592. * </p>
  593. * @return <tt>true</tt> if the implementor needs a GUI
  594. */
  595. public synchronized boolean needsGui() {
  596. BeanContext bc = getBeanContextPeer();
  597. if (bc != this) {
  598. if (bc instanceof Visibility) return ((Visibility)bc).needsGui();
  599. if (bc instanceof Container || bc instanceof Component)
  600. return true;
  601. }
  602. synchronized(children) {
  603. for (Iterator i = children.keySet().iterator(); i.hasNext();) {
  604. Object c = i.next();
  605. try {
  606. return ((Visibility)c).needsGui();
  607. } catch (ClassCastException cce) {
  608. // do nothing ...
  609. }
  610. if (c instanceof Container || c instanceof Component)
  611. return true;
  612. }
  613. }
  614. return false;
  615. }
  616. /**
  617. * notify this instance that it may no longer render a GUI.
  618. */
  619. public synchronized void dontUseGui() {
  620. if (okToUseGui) {
  621. okToUseGui = false;
  622. // lets also tell the Children that can that they may not use their GUI's
  623. synchronized(children) {
  624. for (Iterator i = children.keySet().iterator(); i.hasNext();) {
  625. Visibility v = getChildVisibility(i.next());
  626. if (v != null) v.dontUseGui();
  627. }
  628. }
  629. }
  630. }
  631. /**
  632. * Notify this instance that it may now render a GUI
  633. */
  634. public synchronized void okToUseGui() {
  635. if (!okToUseGui) {
  636. okToUseGui = true;
  637. // lets also tell the Children that can that they may use their GUI's
  638. synchronized(children) {
  639. for (Iterator i = children.keySet().iterator(); i.hasNext();) {
  640. Visibility v = getChildVisibility(i.next());
  641. if (v != null) v.okToUseGui();
  642. }
  643. }
  644. }
  645. }
  646. /**
  647. * Used to determine if the <tt>BeanContext</tt>
  648. * child is avoiding using its GUI.
  649. * @return is this instance avoiding using its GUI?
  650. * @see Visibility
  651. */
  652. public boolean avoidingGui() {
  653. return !okToUseGui && needsGui();
  654. }
  655. /**
  656. * Is this <tt>BeanContext</tt> in the
  657. * process of being serialized?
  658. * @return if this <tt>BeanContext</tt> is
  659. * currently being serialized
  660. */
  661. public boolean isSerializing() { return serializing; }
  662. /**
  663. * Returns an iterator of all children
  664. * of this <tt>BeanContext</tt>.
  665. * @return an iterator for all the current BCSChild values
  666. */
  667. protected Iterator bcsChildren() { synchronized(children) { return children.values().iterator(); } }
  668. /**
  669. * called by writeObject after defaultWriteObject() but prior to
  670. * serialization of currently serializable children.
  671. *
  672. * This method may be overridden by subclasses to perform custom
  673. * serialization of their state prior to this superclass serializing
  674. * the children.
  675. *
  676. * This method should not however be used by subclasses to replace their
  677. * own implementation (if any) of writeObject().
  678. */
  679. protected void bcsPreSerializationHook(ObjectOutputStream oos) throws IOException {
  680. }
  681. /**
  682. * called by readObject after defaultReadObject() but prior to
  683. * deserialization of any children.
  684. *
  685. * This method may be overridden by subclasses to perform custom
  686. * deserialization of their state prior to this superclass deserializing
  687. * the children.
  688. *
  689. * This method should not however be used by subclasses to replace their
  690. * own implementation (if any) of readObject().
  691. */
  692. protected void bcsPreDeserializationHook(ObjectInputStream ois) throws IOException, ClassNotFoundException {
  693. }
  694. /**
  695. * Called by readObject with the newly deserialized child and BCSChild.
  696. * @param child the newly deserialized child
  697. * @param bcsc the newly deserialized BCSChild
  698. */
  699. protected void childDeserializedHook(Object child, BCSChild bcsc) {
  700. synchronized(children) {
  701. children.put(child, bcsc);
  702. }
  703. }
  704. /**
  705. * Used by writeObject to serialize a Collection.
  706. * @param oos the <tt>ObjectOutputStream</tt>
  707. * to use during serialization
  708. * @param coll the <tt>Collection</tt> to serialize
  709. * @throws IOException if serialization failed
  710. */
  711. protected final void serialize(ObjectOutputStream oos, Collection coll) throws IOException {
  712. int count = 0;
  713. Object[] objects = coll.toArray();
  714. for (int i = 0; i < objects.length; i++) {
  715. if (objects[i] instanceof Serializable)
  716. count++;
  717. else
  718. objects[i] = null;
  719. }
  720. oos.writeInt(count); // number of subsequent objects
  721. for (int i = 0; count > 0; i++) {
  722. Object o = objects[i];
  723. if (o != null) {
  724. oos.writeObject(o);
  725. count--;
  726. }
  727. }
  728. }
  729. /**
  730. * used by readObject to deserialize a collection.
  731. * @param ois the ObjectInputStream to use
  732. * @param coll the Collection
  733. */
  734. protected final void deserialize(ObjectInputStream ois, Collection coll) throws IOException, ClassNotFoundException {
  735. int count = 0;
  736. count = ois.readInt();
  737. while (count-- > 0) {
  738. coll.add(ois.readObject());
  739. }
  740. }
  741. /**
  742. * Used to serialize all children of
  743. * this <tt>BeanContext</tt>.
  744. * @param oos the <tt>ObjectOutputStream</tt>
  745. * to use during serialization
  746. * @throws IOException if serialization failed
  747. */
  748. public final void writeChildren(ObjectOutputStream oos) throws IOException {
  749. if (serializable <= 0) return;
  750. boolean prev = serializing;
  751. serializing = true;
  752. int count = 0;
  753. synchronized(children) {
  754. Iterator i = children.entrySet().iterator();
  755. while (i.hasNext() && count < serializable) {
  756. Map.Entry entry = (Map.Entry)i.next();
  757. if (entry.getKey() instanceof Serializable) {
  758. try {
  759. oos.writeObject(entry.getKey()); // child
  760. oos.writeObject(entry.getValue()); // BCSChild
  761. } catch (IOException ioe) {
  762. serializing = prev;
  763. throw ioe;
  764. }
  765. count++;
  766. }
  767. }
  768. }
  769. serializing = prev;
  770. if (count != serializable) {
  771. throw new IOException("wrote different number of children than expected");
  772. }
  773. }
  774. /**
  775. * Serialize the BeanContextSupport, if this instance has a distinct
  776. * peer (that is this object is acting as a delegate for another) then
  777. * the children of this instance are not serialized here due to a
  778. * 'chicken and egg' problem that occurs on deserialization of the
  779. * children at the same time as this instance.
  780. *
  781. * Therefore in situations where there is a distinct peer to this instance
  782. * it should always call writeObject() followed by writeChildren() and
  783. * readObject() followed by readChildren().
  784. *
  785. * @param oos the ObjectOutputStream
  786. */
  787. private synchronized void writeObject(ObjectOutputStream oos) throws IOException, ClassNotFoundException {
  788. serializing = true;
  789. synchronized (BeanContext.globalHierarchyLock) {
  790. try {
  791. oos.defaultWriteObject(); // serialize the BeanContextSupport object
  792. bcsPreSerializationHook(oos);
  793. if (serializable > 0 && this.equals(getBeanContextPeer()))
  794. writeChildren(oos);
  795. serialize(oos, (Collection)bcmListeners);
  796. } finally {
  797. serializing = false;
  798. }
  799. }
  800. }
  801. /**
  802. * When an instance of this class is used as a delegate for the
  803. * implementation of the BeanContext protocols (and its subprotocols)
  804. * there exists a 'chicken and egg' problem during deserialization
  805. */
  806. public final void readChildren(ObjectInputStream ois) throws IOException, ClassNotFoundException {
  807. int count = serializable;
  808. while (count-- > 0) {
  809. Object child = null;
  810. BeanContextSupport.BCSChild bscc = null;
  811. try {
  812. child = ois.readObject();
  813. bscc = (BeanContextSupport.BCSChild)ois.readObject();
  814. } catch (IOException ioe) {
  815. continue;
  816. } catch (ClassNotFoundException cnfe) {
  817. continue;
  818. }
  819. synchronized(child) {
  820. BeanContextChild bcc = null;
  821. try {
  822. bcc = (BeanContextChild)child;
  823. } catch (ClassCastException cce) {
  824. // do nothing;
  825. }
  826. if (bcc != null) {
  827. try {
  828. bcc.setBeanContext(getBeanContextPeer());
  829. bcc.addPropertyChangeListener("beanContext", childPCL);
  830. bcc.addVetoableChangeListener("beanContext", childVCL);
  831. } catch (PropertyVetoException pve) {
  832. continue;
  833. }
  834. }
  835. childDeserializedHook(child, bscc);
  836. }
  837. }
  838. }
  839. /**
  840. * deserialize contents ... if this instance has a distinct peer the
  841. * children are *not* serialized here, the peer's readObject() must call
  842. * readChildren() after deserializing this instance.
  843. */
  844. private synchronized void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {
  845. synchronized(BeanContext.globalHierarchyLock) {
  846. ois.defaultReadObject();
  847. initialize();
  848. bcsPreDeserializationHook(ois);
  849. if (serializable > 0 && this.equals(getBeanContextPeer()))
  850. readChildren(ois);
  851. deserialize(ois, bcmListeners = new ArrayList(1));
  852. }
  853. }
  854. /**
  855. * subclasses may envelope to monitor veto child property changes.
  856. */
  857. public void vetoableChange(PropertyChangeEvent pce) throws PropertyVetoException {
  858. String propertyName = pce.getPropertyName();
  859. Object source = pce.getSource();
  860. synchronized(children) {
  861. if ("beanContext".equals(propertyName) &&
  862. containsKey(source) &&
  863. !getBeanContextPeer().equals(pce.getNewValue())
  864. ) {
  865. if (!validatePendingRemove(source)) {
  866. throw new PropertyVetoException("current BeanContext vetoes setBeanContext()", pce);
  867. } else ((BCSChild)children.get(source)).setRemovePending(true);
  868. }
  869. }
  870. }
  871. /**
  872. * subclasses may envelope to monitor child property changes.
  873. */
  874. public void propertyChange(PropertyChangeEvent pce) {
  875. String propertyName = pce.getPropertyName();
  876. Object source = pce.getSource();
  877. synchronized(children) {
  878. if ("beanContext".equals(propertyName) &&
  879. containsKey(source) &&
  880. ((BCSChild)children.get(source)).isRemovePending()) {
  881. BeanContext bc = getBeanContextPeer();
  882. if (bc.equals(pce.getOldValue()) && !bc.equals(pce.getNewValue())) {
  883. remove(source, false);
  884. } else {
  885. ((BCSChild)children.get(source)).setRemovePending(false);
  886. }
  887. }
  888. }
  889. }
  890. /**
  891. * <p>
  892. * Subclasses of this class may override, or envelope, this method to
  893. * add validation behavior for the BeanContext to examine child objects
  894. * immediately prior to their being added to the BeanContext.
  895. * </p>
  896. *
  897. * @return true iff the child may be added to this BeanContext, otherwise false.
  898. */
  899. protected boolean validatePendingAdd(Object targetChild) {
  900. return true;
  901. }
  902. /**
  903. * <p>
  904. * Subclasses of this class may override, or envelope, this method to
  905. * add validation behavior for the BeanContext to examine child objects
  906. * immediately prior to their being removed from the BeanContext.
  907. * </p>
  908. *
  909. * @return true iff the child may be removed from this BeanContext, otherwise false.
  910. */
  911. protected boolean validatePendingRemove(Object targetChild) {
  912. return true;
  913. }
  914. /**
  915. * subclasses may override this method to simply extend add() semantics
  916. * after the child has been added and before the event notification has
  917. * occurred. The method is called with the child synchronized.
  918. */
  919. protected void childJustAddedHook(Object child, BCSChild bcsc) {
  920. }
  921. /**
  922. * subclasses may override this method to simply extend remove() semantics
  923. * after the child has been removed and before the event notification has
  924. * occurred. The method is called with the child synchronized.
  925. */
  926. protected void childJustRemovedHook(Object child, BCSChild bcsc) {
  927. }
  928. /**
  929. * Gets the Component (if any) associated with the specified child.
  930. * @param child the specified child
  931. * @return the Component (if any) associated with the specified child.
  932. */
  933. protected static final Visibility getChildVisibility(Object child) {
  934. try {
  935. return (Visibility)child;
  936. } catch (ClassCastException cce) {
  937. return null;
  938. }
  939. }
  940. /**
  941. * Gets the Serializable (if any) associated with the specified Child
  942. * @param child the specified child
  943. * @return the Serializable (if any) associated with the specified Child
  944. */
  945. protected static final Serializable getChildSerializable(Object child) {
  946. try {
  947. return (Serializable)child;
  948. } catch (ClassCastException cce) {
  949. return null;
  950. }
  951. }
  952. /**
  953. * Gets the PropertyChangeListener
  954. * (if any) of the specified child
  955. * @param child the specified child
  956. * @return the PropertyChangeListener (if any) of the specified child
  957. */
  958. protected static final PropertyChangeListener getChildPropertyChangeListener(Object child) {
  959. try {
  960. return (PropertyChangeListener)child;
  961. } catch (ClassCastException cce) {
  962. return null;
  963. }
  964. }
  965. /**
  966. * Gets the VetoableChangeListener
  967. * (if any) of the specified child
  968. * @param child the specified child
  969. * @return the VetoableChangeListener (if any) of the specified child
  970. */
  971. protected static final VetoableChangeListener getChildVetoableChangeListener(Object child) {
  972. try {
  973. return (VetoableChangeListener)child;
  974. } catch (ClassCastException cce) {
  975. return null;
  976. }
  977. }
  978. /**
  979. * Gets the BeanContextMembershipListener
  980. * (if any) of the specified child
  981. * @param child the specified child
  982. * @return the BeanContextMembershipListener (if any) of the specified child
  983. */
  984. protected static final BeanContextMembershipListener getChildBeanContextMembershipListener(Object child) {
  985. try {
  986. return (BeanContextMembershipListener)child;
  987. } catch (ClassCastException cce) {
  988. return null;
  989. }
  990. }
  991. /**
  992. * Gets the BeanContextChild (if any) of the specified child
  993. * @param child the specified child
  994. * @return the BeanContextChild (if any) of the specified child
  995. * @throws IllegalArgumentException if child implements both BeanContextChild and BeanContextProxy
  996. */
  997. protected static final BeanContextChild getChildBeanContextChild(Object child) {
  998. try {
  999. BeanContextChild bcc = (BeanContextChild)child;
  1000. if (child instanceof BeanContextChild && child instanceof BeanContextProxy)
  1001. throw new IllegalArgumentException("child cannot implement both BeanContextChild and BeanContextProxy");
  1002. else
  1003. return bcc;
  1004. } catch (ClassCastException cce) {
  1005. try {
  1006. return ((BeanContextProxy)child).getBeanContextProxy();
  1007. } catch (ClassCastException cce1) {
  1008. return null;
  1009. }
  1010. }
  1011. }
  1012. /**
  1013. * Fire a BeanContextshipEvent on the BeanContextMembershipListener interface
  1014. */
  1015. protected final void fireChildrenAdded(BeanContextMembershipEvent bcme) {
  1016. Object[] copy;
  1017. synchronized(bcmListeners) { copy = bcmListeners.toArray(); }
  1018. for (int i = 0; i < copy.length; i++)
  1019. ((BeanContextMembershipListener)copy[i]).childrenAdded(bcme);
  1020. }
  1021. /**
  1022. * Fire a BeanContextshipEvent on the BeanContextMembershipListener interface
  1023. */
  1024. protected final void fireChildrenRemoved(BeanContextMembershipEvent bcme) {
  1025. Object[] copy;
  1026. synchronized(bcmListeners) { copy = bcmListeners.toArray(); }
  1027. for (int i = 0; i < copy.length; i++)
  1028. ((BeanContextMembershipListener)copy[i]).childrenRemoved(bcme);
  1029. }
  1030. /**
  1031. * protected method called from constructor and readObject to initialize
  1032. * transient state of BeanContextSupport instance.
  1033. *
  1034. * This class uses this method to instantiate inner class listeners used
  1035. * to monitor PropertyChange and VetoableChange events on children.
  1036. *
  1037. * subclasses may envelope this method to add their own initialization
  1038. * behavior
  1039. */
  1040. protected synchronized void initialize() {
  1041. children = new HashMap(serializable + 1);
  1042. bcmListeners = new ArrayList(1);
  1043. childPCL = new PropertyChangeListener() {
  1044. /*
  1045. * this adaptor is used by the BeanContextSupport class to forward
  1046. * property changes from a child to the BeanContext, avoiding
  1047. * accidential serialization of the BeanContext by a badly
  1048. * behaved Serializable child.
  1049. */
  1050. public void propertyChange(PropertyChangeEvent pce) {
  1051. BeanContextSupport.this.propertyChange(pce);
  1052. }
  1053. };
  1054. childVCL = new VetoableChangeListener() {
  1055. /*
  1056. * this adaptor is used by the BeanContextSupport class to forward
  1057. * vetoable changes from a child to the BeanContext, avoiding
  1058. * accidential serialization of the BeanContext by a badly
  1059. * behaved Serializable child.
  1060. */
  1061. public void vetoableChange(PropertyChangeEvent pce) throws PropertyVetoException {
  1062. BeanContextSupport.this.vetoableChange(pce);
  1063. }
  1064. };
  1065. }
  1066. /**
  1067. * Gets a copy of the this BeanContext's children.
  1068. * @return a copy of the current nested children
  1069. */
  1070. protected final Object[] copyChildren() {
  1071. synchronized(children) { return children.keySet().toArray(); }
  1072. }
  1073. /**
  1074. * Tests to see if two class objects,
  1075. * or their names are equal.
  1076. * @param first the first object
  1077. * @param second the second object
  1078. * @return true if equal, false if not
  1079. */
  1080. protected static final boolean classEquals(Class first, Class second) {
  1081. return first.equals(second) || first.getName().equals(second.getName());
  1082. }
  1083. /*
  1084. * fields
  1085. */
  1086. /**
  1087. * all accesses to the <code> protected HashMap children </code> field
  1088. * shall be synchronized on that object.
  1089. */
  1090. protected transient HashMap children;
  1091. private int serializable = 0; // children serializable
  1092. /**
  1093. * all accesses to the <code> protected ArrayList bcmListeners </code> field
  1094. * shall be synchronized on that object.
  1095. */
  1096. protected transient ArrayList bcmListeners;
  1097. //
  1098. /**
  1099. * The current locale of this BeanContext.
  1100. */
  1101. protected Locale locale;
  1102. /**
  1103. * A <tt>boolean</tt> indicating if this
  1104. * instance may now render a GUI.
  1105. */
  1106. protected boolean okToUseGui;
  1107. /**
  1108. * A <tt>boolean</tt> indicating whether or not
  1109. * this object is currently in design time mode.
  1110. */
  1111. protected boolean designTime;
  1112. /*
  1113. * transient
  1114. */
  1115. private transient PropertyChangeListener childPCL;
  1116. private transient VetoableChangeListener childVCL;
  1117. private transient boolean serializing;
  1118. }