Frames | No Frames |
1: /* BeanContextSupport.java -- 2: Copyright (C) 2003, 2005, 2006 Free Software Foundation, Inc. 3: 4: This file is part of GNU Classpath. 5: 6: GNU Classpath is free software; you can redistribute it and/or modify 7: it under the terms of the GNU General Public License as published by 8: the Free Software Foundation; either version 2, or (at your option) 9: any later version. 10: 11: GNU Classpath is distributed in the hope that it will be useful, but 12: WITHOUT ANY WARRANTY; without even the implied warranty of 13: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14: General Public License for more details. 15: 16: You should have received a copy of the GNU General Public License 17: along with GNU Classpath; see the file COPYING. If not, write to the 18: Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 19: 02110-1301 USA. 20: 21: Linking this library statically or dynamically with other modules is 22: making a combined work based on this library. Thus, the terms and 23: conditions of the GNU General Public License cover the whole 24: combination. 25: 26: As a special exception, the copyright holders of this library give you 27: permission to link this library with independent modules to produce an 28: executable, regardless of the license terms of these independent 29: modules, and to copy and distribute the resulting executable under 30: terms of your choice, provided that you also meet, for each linked 31: independent module, the terms and conditions of the license of that 32: module. An independent module is a module which is not derived from 33: or based on this library. If you modify this library, you may extend 34: this exception to your version of the library, but you are not 35: obligated to do so. If you do not wish to do so, delete this 36: exception statement from your version. */ 37: 38: 39: package java.beans.beancontext; 40: 41: import java.beans.Beans; 42: import java.beans.DesignMode; 43: import java.beans.PropertyChangeEvent; 44: import java.beans.PropertyChangeListener; 45: import java.beans.PropertyVetoException; 46: import java.beans.VetoableChangeListener; 47: import java.beans.Visibility; 48: import java.io.IOException; 49: import java.io.InputStream; 50: import java.io.ObjectInputStream; 51: import java.io.ObjectOutputStream; 52: import java.io.Serializable; 53: import java.net.URL; 54: import java.util.ArrayList; 55: import java.util.Collection; 56: import java.util.HashMap; 57: import java.util.Iterator; 58: import java.util.List; 59: import java.util.Locale; 60: 61: /** 62: * This is a helper class for implementing a bean context. It is 63: * intended to be used either by subclassing or by calling methods 64: * of this implementation from another. 65: * 66: * @author Michael Koch 67: * @author Andrew John Hughes (gnu_andrew@member.fsf.org) 68: * @since 1.2 69: */ 70: public class BeanContextSupport extends BeanContextChildSupport 71: implements BeanContext, Serializable, PropertyChangeListener, 72: VetoableChangeListener 73: { 74: private static final long serialVersionUID = -4879613978649577204L; 75: 76: /** 77: * Deserializes a stored bean context. Hook methods are provided to allow 78: * subclasses to perform their own deserialization after the default 79: * deserialization but prior to the deserialization of the children. Note that 80: * {@link #readChildren(ObjectInputStream)} is only called if there 81: * is no distinct peer. If there is, the peer is expected to call 82: * the method instead. 83: * 84: * @param s the stream to deserialize. 85: * @throws ClassNotFoundException if the class of an object being deserialized 86: * could not be found. 87: * @throws IOException if an I/O error occurs. 88: */ 89: private void readObject (ObjectInputStream s) 90: throws ClassNotFoundException, IOException 91: { 92: s.defaultReadObject(); 93: bcsPreDeserializationHook(s); 94: BeanContext peer = getBeanContextPeer(); 95: if (peer == null || peer == this) 96: readChildren(s); 97: } 98: 99: /** 100: * Serializes a bean context. Hook methods are provided to allow 101: * subclasses to perform their own serialization after the default 102: * serialization but prior to serialization of the children. Note that 103: * {@link #writeChildren(ObjectOutputStream)} is only called if there 104: * is no distinct peer. If there is, the peer is expected to call 105: * the method instead. 106: * 107: * @param s the stream to serialize. 108: * @throws ClassNotFoundException if the class of an object being deserialized 109: * could not be found. 110: * @throws IOException if an I/O error occurs. 111: */ 112: private void writeObject (ObjectOutputStream s) 113: throws ClassNotFoundException, IOException 114: { 115: serializing = true; 116: s.defaultWriteObject(); 117: bcsPreSerializationHook(s); 118: BeanContext peer = getBeanContextPeer(); 119: if (peer == null || peer == this) 120: writeChildren(s); 121: serializing = false; 122: } 123: 124: protected class BCSChild implements Serializable 125: { 126: private static final long serialVersionUID = -5815286101609939109L; 127: 128: private Object targetChild; 129: private Object peer; 130: 131: BCSChild(Object targetChild, Object peer) 132: { 133: this.targetChild = targetChild; 134: this.peer = peer; 135: } 136: 137: private Object getTargetChild() 138: { 139: return targetChild; 140: } 141: 142: } 143: 144: protected static final class BCSIterator implements Iterator 145: { 146: private Iterator child; 147: 148: BCSIterator(Iterator child) 149: { 150: this.child = child; 151: } 152: 153: public boolean hasNext () 154: { 155: return child.hasNext(); 156: } 157: 158: public Object next () 159: { 160: return child.next(); 161: } 162: 163: public void remove () 164: { 165: // This must be a noop remove operation. 166: } 167: } 168: 169: protected transient ArrayList bcmListeners; 170: 171: protected transient HashMap children; 172: 173: protected transient boolean designTime; 174: 175: protected transient Locale locale; 176: 177: protected transient boolean okToUseGui; 178: 179: private transient boolean serializing; 180: 181: /** 182: * Construct a BeanContextSupport instance. 183: */ 184: public BeanContextSupport () 185: { 186: this (null, null, false, true); 187: } 188: 189: /** 190: * Construct a BeanContextSupport instance. 191: * 192: * @param peer the bean context peer (<code>null</code> permitted). 193: */ 194: public BeanContextSupport(BeanContext peer) 195: { 196: this (peer, null, false, true); 197: } 198: 199: /** 200: * Construct a BeanContextSupport instance. 201: * 202: * @param peer the bean context peer (<code>null</code> permitted). 203: * @param locale the locale (<code>null</code> permitted, equivalent to 204: * the default locale). 205: */ 206: public BeanContextSupport (BeanContext peer, Locale locale) 207: { 208: this (peer, locale, false, true); 209: } 210: 211: /** 212: * Construct a BeanContextSupport instance. 213: * 214: * @param peer the bean context peer (<code>null</code> permitted). 215: * @param locale the locale (<code>null</code> permitted, equivalent to 216: * the default locale). 217: * @param dtime a flag indicating whether or not the bean context is in 218: * design time mode. 219: */ 220: public BeanContextSupport (BeanContext peer, Locale locale, boolean dtime) 221: { 222: this (peer, locale, dtime, true); 223: } 224: 225: /** 226: * Construct a BeanContextSupport instance. 227: * 228: * @param peer the bean context peer (<code>null</code> permitted). 229: * @param locale the locale (<code>null</code> permitted, equivalent to 230: * the default locale). 231: * @param dtime a flag indicating whether or not the bean context is in 232: * design time mode. 233: * @param visible initial value of the <code>okToUseGui</code> flag. 234: */ 235: public BeanContextSupport (BeanContext peer, Locale locale, boolean dtime, 236: boolean visible) 237: { 238: super(peer); 239: 240: this.locale = locale == null ? Locale.getDefault() : locale; 241: designTime = dtime; 242: okToUseGui = visible; 243: 244: initialize (); 245: } 246: 247: /** 248: * <p> 249: * Add a child to the bean context. A child can be a simple 250: * <code>Object</code>, a <code>BeanContextChild</code> 251: * or another <code>BeanContext</code>. 252: * </p> 253: * <p> 254: * The children of a <code>BeanContext</code> form a set. As 255: * a result, this method returns <code>false</code> if the given 256: * object is already a child of this context. 257: * </p> 258: * <p> 259: * If the child is a <code>BeanContextChild</code>, or a proxy 260: * for such a child, the <code>setBeanContext()</code> method 261: * is invoked on the child. If this operation is vetoed by the 262: * child, via throwing a <code>PropertyVetoException</code>, 263: * then the current completion state of the <code>add()</code> 264: * operation is rolled back and a <code>IllegalStateException</code> 265: * is thrown. If the <code>BeanContextChild</code> is successfully 266: * added, then the context registers with its 267: * <code>PropertyChangeListener</code> and 268: * <code>VetoableChangeListener</code> for "beanContext" events. 269: * </p> 270: * <p> 271: * If the child implements <code>java.beans.Visibility</code>, 272: * then its ability to use a GUI is set based on that of 273: * this context. 274: * </p> 275: * <p> 276: * A <code>BeanContextMembershipEvent</code> is fired when the 277: * child is successfully added to the bean context. 278: * </p> 279: * <p> 280: * This method is synchronized over the global hierarchy lock. 281: * </p> 282: * 283: * @param targetChild the child to add. 284: * @return false if the child has already been added. 285: * @throws IllegalArgumentException if the child is null. 286: * @throws IllegalStateException if the child vetos the setting 287: * of its context. 288: */ 289: public boolean add(Object targetChild) 290: { 291: synchronized (globalHierarchyLock) 292: { 293: if (targetChild == null) 294: throw new IllegalArgumentException(); 295: 296: BCSChild child; 297: synchronized (children) 298: { 299: if (children.containsKey(targetChild) 300: || ! validatePendingAdd(targetChild)) 301: return false; 302: child = createBCSChild(targetChild, beanContextChildPeer); 303: children.put(targetChild, child); 304: } 305: synchronized (targetChild) 306: { 307: BeanContextChild bcChild = null; 308: if (targetChild instanceof BeanContextChild) 309: bcChild = (BeanContextChild) targetChild; 310: if (targetChild instanceof BeanContextProxy) 311: bcChild = ((BeanContextProxy) targetChild).getBeanContextProxy(); 312: if (bcChild != null) 313: try 314: { 315: bcChild.setBeanContext(this); 316: bcChild.addVetoableChangeListener("beanContext", this); 317: bcChild.addPropertyChangeListener("beanContext", this); 318: } 319: catch (PropertyVetoException e) 320: { 321: synchronized (children) 322: { 323: children.remove(targetChild); 324: } 325: throw new IllegalStateException("The child refused to " + 326: "associate itself with " + 327: "this context.", e); 328: } 329: if (targetChild instanceof Visibility) 330: { 331: Visibility visibleChild = (Visibility) targetChild; 332: if (okToUseGui) 333: visibleChild.okToUseGui(); 334: else 335: visibleChild.dontUseGui(); 336: } 337: childJustAddedHook(targetChild, child); 338: } 339: fireChildrenAdded(new BeanContextMembershipEvent(this, 340: new Object[]{ targetChild })); 341: return true; 342: } 343: } 344: 345: public boolean addAll (Collection c) 346: { 347: // Intentionally throws an exception. 348: throw new UnsupportedOperationException(); 349: } 350: 351: public void addBeanContextMembershipListener 352: (BeanContextMembershipListener listener) 353: { 354: synchronized (bcmListeners) 355: { 356: if (! bcmListeners.contains(listener)) 357: bcmListeners.add(listener); 358: } 359: } 360: 361: /** 362: * Returns true if this bean needs a GUI 363: * but is being prevented from using one. 364: * 365: * @return true if <code>needsGui()</code> 366: * is true but the bean has been 367: * told not to use it. 368: */ 369: public boolean avoidingGui() 370: { 371: return needsGui() && (!okToUseGui); 372: } 373: 374: protected Iterator bcsChildren () 375: { 376: synchronized (children) 377: { 378: return new BCSIterator(children.values().iterator()); 379: } 380: } 381: 382: /** 383: * Subclasses may use this method to perform their own deserialization 384: * after the default deserialization process has taken place, but 385: * prior to the deserialization of the children. It should not 386: * be used to replace the implementation of <code>readObject</code> 387: * in the subclass. 388: * 389: * @param ois the input stream. 390: * @throws ClassNotFoundException if the class of an object being deserialized 391: * could not be found. 392: * @throws IOException if an I/O error occurs. 393: */ 394: protected void bcsPreDeserializationHook (ObjectInputStream ois) 395: throws ClassNotFoundException, IOException 396: { 397: /* Purposefully left empty */ 398: } 399: 400: /** 401: * Subclasses may use this method to perform their own serialization 402: * after the default serialization process has taken place, but 403: * prior to the serialization of the children. It should not 404: * be used to replace the implementation of <code>writeObject</code> 405: * in the subclass. 406: * 407: * @param oos the output stream. 408: * @throws IOException if an I/O error occurs. 409: */ 410: protected void bcsPreSerializationHook (ObjectOutputStream oos) 411: throws IOException 412: { 413: /* Purposefully left empty */ 414: } 415: 416: /** 417: * Called when a child is deserialized. 418: * 419: * @param child the deserialized child. 420: * @param bcsc the deserialized context wrapper for the child. 421: */ 422: protected void childDeserializedHook (Object child, BeanContextSupport.BCSChild bcsc) 423: { 424: // Do nothing in the base class. 425: } 426: 427: protected void childJustAddedHook (Object child, BeanContextSupport.BCSChild bcsc) 428: { 429: // Do nothing in the base class. 430: } 431: 432: protected void childJustRemovedHook (Object child, BeanContextSupport.BCSChild bcsc) 433: { 434: // Do nothing in the base class. 435: } 436: 437: protected static final boolean classEquals (Class first, Class second) 438: { 439: // Lame function! 440: return (first == second || first.getName().equals(second.getName())); 441: } 442: 443: public void clear () 444: { 445: // This is the right thing to do. 446: // The JDK docs are really bad here. 447: throw new UnsupportedOperationException(); 448: } 449: 450: public boolean contains (Object o) 451: { 452: synchronized (children) 453: { 454: return children.containsKey(o); 455: } 456: } 457: 458: public boolean containsAll (Collection c) 459: { 460: synchronized (children) 461: { 462: Iterator it = c.iterator(); 463: while (it.hasNext()) 464: if (! children.containsKey(it.next())) 465: return false; 466: } 467: return true; 468: } 469: 470: public boolean containsKey (Object o) 471: { 472: synchronized (children) 473: { 474: return children.containsKey(o); 475: } 476: } 477: 478: protected final Object[] copyChildren () 479: { 480: synchronized (children) 481: { 482: return children.keySet().toArray(); 483: } 484: } 485: 486: protected BeanContextSupport.BCSChild createBCSChild (Object targetChild, Object peer) 487: { 488: return new BCSChild(targetChild, peer); 489: } 490: 491: /** 492: * Deserializes objects (written by {@link #serialize(ObjectOutputStream, 493: * Collection)}) and adds them to the specified collection. 494: * 495: * @param ois the input stream (<code>null</code> not permitted). 496: * @param coll the collection to add the objects to (<code>null</code> not 497: * permitted). 498: * 499: * @throws ClassNotFoundException 500: * @throws IOException 501: * 502: * @see #serialize(ObjectOutputStream, Collection) 503: */ 504: protected final void deserialize (ObjectInputStream ois, Collection coll) 505: throws ClassNotFoundException, IOException 506: { 507: int itemCount = ois.readInt(); 508: for (int i = 0; i < itemCount; i++) 509: coll.add(ois.readObject()); 510: } 511: 512: /** 513: * Informs this bean that is should not make 514: * use of the GUI. 515: */ 516: public void dontUseGui() 517: { 518: okToUseGui = false; 519: } 520: 521: protected final void fireChildrenAdded (BeanContextMembershipEvent bcme) 522: { 523: synchronized (bcmListeners) 524: { 525: Iterator it = bcmListeners.iterator(); 526: while (it.hasNext()) 527: { 528: BeanContextMembershipListener l 529: = (BeanContextMembershipListener) it.next(); 530: l.childrenAdded(bcme); 531: } 532: } 533: } 534: 535: protected final void fireChildrenRemoved (BeanContextMembershipEvent bcme) 536: { 537: synchronized (bcmListeners) 538: { 539: Iterator it = bcmListeners.iterator(); 540: while (it.hasNext()) 541: { 542: BeanContextMembershipListener l 543: = (BeanContextMembershipListener) it.next(); 544: l.childrenRemoved(bcme); 545: } 546: } 547: } 548: 549: /** 550: * Returns the bean context peer. 551: * 552: * @return The bean context peer. 553: * 554: * @see BeanContextChildSupport#beanContextChildPeer 555: */ 556: public BeanContext getBeanContextPeer() 557: { 558: return (BeanContext) beanContextChildPeer; 559: } 560: 561: /** 562: * Returns the {@link BeanContextChild} implementation for the given child. 563: * 564: * @param child the child (<code>null</code> permitted). 565: * 566: * @return The bean context child. 567: * 568: * @throws IllegalArgumentException if <code>child</code> implements both 569: * the {@link BeanContextChild} and {@link BeanContextProxy} interfaces. 570: */ 571: protected static final BeanContextChild getChildBeanContextChild(Object child) 572: { 573: if (child == null) 574: return null; 575: if (child instanceof BeanContextChild && child instanceof BeanContextProxy) 576: throw new IllegalArgumentException("Child cannot implement " 577: + "BeanContextChild and BeanContextProxy simultaneously."); 578: if (child instanceof BeanContextChild) 579: return (BeanContextChild) child; 580: if (child instanceof BeanContextProxy) 581: return ((BeanContextProxy) child).getBeanContextProxy(); 582: return null; 583: } 584: 585: /** 586: * Returns <code>child</code> as an instance of 587: * {@link BeanContextMembershipListener}, or <code>null</code> if 588: * <code>child</code> does not implement that interface. 589: * 590: * @param child the child (<code>null</code> permitted). 591: * 592: * @return The child cast to {@link BeanContextMembershipListener}. 593: */ 594: protected static final BeanContextMembershipListener 595: getChildBeanContextMembershipListener(Object child) 596: { 597: if (child instanceof BeanContextMembershipListener) 598: return (BeanContextMembershipListener) child; 599: else 600: return null; 601: } 602: 603: /** 604: * Returns <code>child</code> as an instance of 605: * {@link PropertyChangeListener}, or <code>null</code> if <code>child</code> 606: * does not implement that interface. 607: * 608: * @param child the child (<code>null</code> permitted). 609: * 610: * @return The child cast to {@link PropertyChangeListener}. 611: */ 612: protected static final PropertyChangeListener getChildPropertyChangeListener( 613: Object child) 614: { 615: if (child instanceof PropertyChangeListener) 616: return (PropertyChangeListener) child; 617: else 618: return null; 619: } 620: 621: /** 622: * Returns <code>child</code> as an instance of {@link Serializable}, or 623: * <code>null</code> if <code>child</code> does not implement that 624: * interface. 625: * 626: * @param child the child (<code>null</code> permitted). 627: * 628: * @return The child cast to {@link Serializable}. 629: */ 630: protected static final Serializable getChildSerializable(Object child) 631: { 632: if (child instanceof Serializable) 633: return (Serializable) child; 634: else 635: return null; 636: } 637: 638: /** 639: * Returns <code>child</code> as an instance of 640: * {@link VetoableChangeListener}, or <code>null</code> if <code>child</code> 641: * does not implement that interface. 642: * 643: * @param child the child (<code>null</code> permitted). 644: * 645: * @return The child cast to {@link VetoableChangeListener}. 646: */ 647: protected static final VetoableChangeListener getChildVetoableChangeListener( 648: Object child) 649: { 650: if (child instanceof VetoableChangeListener) 651: return (VetoableChangeListener) child; 652: else 653: return null; 654: } 655: 656: /** 657: * Returns <code>child</code> as an instance of {@link Visibility}, or 658: * <code>null</code> if <code>child</code> does not implement that interface. 659: * 660: * @param child the child (<code>null</code> permitted). 661: * 662: * @return The child cast to {@link Visibility}. 663: */ 664: protected static final Visibility getChildVisibility(Object child) 665: { 666: if (child instanceof Visibility) 667: return (Visibility) child; 668: else 669: return null; 670: } 671: 672: public Locale getLocale () 673: { 674: return locale; 675: } 676: 677: public URL getResource (String name, BeanContextChild bcc) 678: { 679: if (! contains(bcc)) 680: throw new IllegalArgumentException("argument not a child"); 681: ClassLoader loader = bcc.getClass().getClassLoader(); 682: return (loader == null ? ClassLoader.getSystemResource(name) 683: : loader.getResource(name)); 684: } 685: 686: public InputStream getResourceAsStream (String name, BeanContextChild bcc) 687: { 688: if (! contains(bcc)) 689: throw new IllegalArgumentException("argument not a child"); 690: ClassLoader loader = bcc.getClass().getClassLoader(); 691: return (loader == null ? ClassLoader.getSystemResourceAsStream(name) 692: : loader.getResourceAsStream(name)); 693: } 694: 695: protected void initialize () 696: { 697: bcmListeners = new ArrayList(); 698: children = new HashMap(); 699: } 700: 701: /** 702: * This is a convenience method for instantiating a bean inside this 703: * context. It delegates to the appropriate method in 704: * <code>java.beans.Beans</code> using the context's classloader. 705: * 706: * @param beanName the name of the class of bean to instantiate. 707: * @throws IOException if an I/O error occurs in loading the class. 708: * @throws ClassNotFoundException if the class, <code>beanName</code>, 709: * can not be found. 710: */ 711: public Object instantiateChild (String beanName) 712: throws IOException, ClassNotFoundException 713: { 714: return Beans.instantiate(getClass().getClassLoader(), beanName, this); 715: } 716: 717: /** 718: * Returns <code>true</code> if the <code>BeanContext</code> is in 719: * design time mode, and <code>false</code> if it is in runtime mode. 720: * 721: * @return A boolean. 722: * 723: * @see #setDesignTime(boolean) 724: */ 725: public boolean isDesignTime() 726: { 727: return designTime; 728: } 729: 730: /** 731: * Returns true if this bean context has no children. 732: * 733: * @return true if there are no children. 734: */ 735: public boolean isEmpty () 736: { 737: synchronized (children) 738: { 739: return children.isEmpty(); 740: } 741: } 742: 743: /** 744: * Returns true if the bean context is in the process 745: * of being serialized. 746: * 747: * @return true if the context is being serialized. 748: */ 749: public boolean isSerializing() 750: { 751: return serializing; 752: } 753: 754: public Iterator iterator () 755: { 756: synchronized (children) 757: { 758: return children.keySet().iterator(); 759: } 760: } 761: 762: /** 763: * Returns false as this bean does not a 764: * GUI for its operation. 765: * 766: * @return false 767: */ 768: public boolean needsGui() 769: { 770: return false; 771: } 772: 773: /** 774: * Informs this bean that it is okay to make use of 775: * the GUI. 776: */ 777: public void okToUseGui () 778: { 779: okToUseGui = true; 780: } 781: 782: /** 783: * Subclasses may use this method to catch property changes 784: * arising from the children of this context. At present, 785: * we just listen for the beans being assigned to a different 786: * context and remove them from here if such an event occurs. 787: * 788: * @param pce the property change event. 789: */ 790: public void propertyChange (PropertyChangeEvent pce) 791: { 792: if (pce.getNewValue() != this) 793: remove(pce.getSource(), false); 794: } 795: 796: /** 797: * Deserializes the children using the 798: * {@link #deserialize(ObjectInputStream, Collection} method 799: * and then calls {@link childDeserializedHook(Object, BCSChild)} 800: * for each child deserialized. 801: * 802: * @param ois the input stream. 803: * @throws IOException if an I/O error occurs. 804: */ 805: public final void readChildren (ObjectInputStream ois) 806: throws IOException, ClassNotFoundException 807: { 808: List temp = new ArrayList(); 809: deserialize(ois, temp); 810: Iterator i = temp.iterator(); 811: synchronized (globalHierarchyLock) 812: { 813: synchronized (children) 814: { 815: while (i.hasNext()) 816: { 817: BCSChild bcs = (BCSChild) i.next(); 818: childDeserializedHook(bcs.getTargetChild(), bcs); 819: children.put(bcs.getTargetChild(), bcs); 820: } 821: } 822: } 823: } 824: 825: /** 826: * Remove the specified child from the context. This is 827: * the same as calling <code>remove(Object,boolean)</code> 828: * with a request for the <code>setBeanContext()</code> method 829: * of the child to be called (i.e. the second argument is true). 830: * 831: * @param targetChild the child to remove. 832: */ 833: public boolean remove (Object targetChild) 834: { 835: return remove(targetChild, true); 836: } 837: 838: /** 839: * <p> 840: * Removes a child from the bean context. A child can be a simple 841: * <code>Object</code>, a <code>BeanContextChild</code> 842: * or another <code>BeanContext</code>. If the given child is not 843: * a child of this context, this method returns <code>false</code>. 844: * </p> 845: * <p> 846: * If the child is a <code>BeanContextChild</code>, or a proxy 847: * for such a child, the <code>setBeanContext()</code> method 848: * is invoked on the child (if specified). If this operation is vetoed 849: * by the child, via throwing a <code>PropertyVetoException</code>, 850: * then the current completion state of the <code>remove()</code> 851: * operation is rolled back and a <code>IllegalStateException</code> 852: * is thrown. If the <code>BeanContextChild</code> is successfully 853: * removed, then the context deregisters with its 854: * <code>PropertyChangeListener</code> and 855: * <code>VetoableChangeListener</code> for "beanContext" events. 856: * </p> 857: * <p> 858: * A <code>BeanContextMembershipEvent</code> is fired when the 859: * child is successfully removed from the bean context. 860: * </p> 861: * <p> 862: * This method is synchronized over the global hierarchy lock. 863: * </p> 864: * 865: * @param targetChild the child to remove. 866: * @param callChildSetBC true if the <code>setBeanContext()</code> 867: * method of the child should be called. 868: * @return false if the child doesn't exist. 869: * @throws IllegalArgumentException if the child is null. 870: * @throws IllegalStateException if the child vetos the setting 871: * of its context. 872: */ 873: protected boolean remove (Object targetChild, boolean callChildSetBC) 874: { 875: synchronized (globalHierarchyLock) 876: { 877: if (targetChild == null) 878: throw new IllegalArgumentException(); 879: 880: BCSChild child; 881: synchronized (children) 882: { 883: if (!children.containsKey(targetChild) 884: || !validatePendingRemove(targetChild)) 885: return false; 886: child = (BCSChild) children.remove(targetChild); 887: } 888: synchronized (targetChild) 889: { 890: BeanContextChild bcChild = null; 891: if (targetChild instanceof BeanContextChild) 892: bcChild = (BeanContextChild) targetChild; 893: if (targetChild instanceof BeanContextProxy) 894: bcChild = ((BeanContextProxy) targetChild).getBeanContextProxy(); 895: if (bcChild != null) 896: try 897: { 898: if (callChildSetBC) 899: bcChild.setBeanContext(null); 900: bcChild.removeVetoableChangeListener("beanContext", this); 901: bcChild.removePropertyChangeListener("beanContext", this); 902: } 903: catch (PropertyVetoException e) 904: { 905: synchronized (children) 906: { 907: children.put(targetChild, child); 908: } 909: throw new IllegalStateException("The child refused to " + 910: "disassociate itself with " + 911: "this context.", e); 912: } 913: childJustRemovedHook(targetChild, child); 914: } 915: fireChildrenRemoved(new BeanContextMembershipEvent(this, 916: new Object[]{ targetChild })); 917: return true; 918: } 919: } 920: 921: public boolean removeAll (Collection c) 922: { 923: // Intentionally throws an exception. 924: throw new UnsupportedOperationException(); 925: } 926: 927: public void removeBeanContextMembershipListener (BeanContextMembershipListener bcml) 928: { 929: synchronized (bcmListeners) 930: { 931: bcmListeners.remove(bcml); 932: } 933: } 934: 935: public boolean retainAll (Collection c) 936: { 937: // Intentionally throws an exception. 938: throw new UnsupportedOperationException(); 939: } 940: 941: /** 942: * Writes the items in the collection to the specified output stream. Items 943: * in the collection that are not instances of {@link Serializable} 944: * (this includes <code>null</code>) are simply ignored. 945: * 946: * @param oos the output stream (<code>null</code> not permitted). 947: * @param coll the collection (<code>null</code> not permitted). 948: * 949: * @throws IOException 950: * 951: * @see #deserialize(ObjectInputStream, Collection) 952: */ 953: protected final void serialize(ObjectOutputStream oos, Collection coll) 954: throws IOException 955: { 956: Object[] items = coll.toArray(); 957: int itemCount = 0; 958: for (int i = 0; i < items.length; i++) 959: { 960: if (items[i] instanceof Serializable) 961: itemCount++; 962: } 963: oos.writeInt(itemCount); 964: for (int i = 0; i < items.length; i++) 965: { 966: if (items[i] instanceof Serializable) 967: oos.writeObject(items[i]); 968: } 969: } 970: 971: /** 972: * Sets the flag that indicates whether or not the 973: * <code>BeanContext</code> is in design mode. If the flag changes 974: * value, a {@link PropertyChangeEvent} (with the property name 'designMode') 975: * is sent to registered listeners. Note that the property name used here 976: * does NOT match the specification in the {@link DesignMode} interface, we 977: * match the reference implementation instead - see bug parade entry 4295174. 978: * 979: * @param dtime the new value for the flag. 980: * 981: * @see #isDesignTime() 982: */ 983: public void setDesignTime(boolean dtime) 984: { 985: boolean save = designTime; 986: designTime = dtime; 987: // note that we use the same property name as Sun's implementation, 988: // even though this is a known bug: see bug parade entry 4295174 989: firePropertyChange("designMode", Boolean.valueOf(save), 990: Boolean.valueOf(dtime)); 991: } 992: 993: public void setLocale (Locale newLocale) 994: throws PropertyVetoException 995: { 996: if (newLocale == null || locale == newLocale) 997: return; 998: fireVetoableChange("locale", locale, newLocale); 999: Locale oldLocale = locale; 1000: locale = newLocale; 1001: firePropertyChange("locale", oldLocale, newLocale); 1002: } 1003: 1004: public int size () 1005: { 1006: synchronized (children) 1007: { 1008: return children.size(); 1009: } 1010: } 1011: 1012: /** 1013: * Returns an array containing the children of this <code>BeanContext</code>. 1014: * 1015: * @return An array containing the children. 1016: */ 1017: public Object[] toArray() 1018: { 1019: synchronized (children) 1020: { 1021: return children.keySet().toArray(); 1022: } 1023: } 1024: 1025: /** 1026: * Populates, then returns, the supplied array with the children of this 1027: * <code>BeanContext</code>. If the array is too short to hold the 1028: * children, a new array is allocated and returned. If the array is too 1029: * long, it is padded with <code>null</code> items at the end. 1030: * 1031: * @param array an array to populate (<code>null</code> not permitted). 1032: */ 1033: public Object[] toArray(Object[] array) 1034: { 1035: synchronized (children) 1036: { 1037: return children.keySet().toArray(array); 1038: } 1039: } 1040: 1041: protected boolean validatePendingAdd (Object targetChild) 1042: { 1043: return true; 1044: } 1045: 1046: protected boolean validatePendingRemove (Object targetChild) 1047: { 1048: return true; 1049: } 1050: 1051: /** 1052: * Subclasses may use this method to veto changes arising 1053: * from the children of this context. 1054: * 1055: * @param pce the vetoable property change event fired. 1056: */ 1057: public void vetoableChange (PropertyChangeEvent pce) 1058: throws PropertyVetoException 1059: { 1060: /* Purposefully left empty */ 1061: } 1062: 1063: /** 1064: * Serializes the children using the 1065: * {@link #serialize(ObjectOutputStream, Collection} method. 1066: * 1067: * @param oos the output stream. 1068: * @throws IOException if an I/O error occurs. 1069: */ 1070: public final void writeChildren (ObjectOutputStream oos) 1071: throws IOException 1072: { 1073: synchronized (children) 1074: { 1075: serialize(oos, children.values()); 1076: } 1077: } 1078: 1079: }