001/* BeanContextSupport.java -- 002 Copyright (C) 2003, 2005, 2006 Free Software Foundation, Inc. 003 004This file is part of GNU Classpath. 005 006GNU Classpath is free software; you can redistribute it and/or modify 007it under the terms of the GNU General Public License as published by 008the Free Software Foundation; either version 2, or (at your option) 009any later version. 010 011GNU Classpath is distributed in the hope that it will be useful, but 012WITHOUT ANY WARRANTY; without even the implied warranty of 013MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 014General Public License for more details. 015 016You should have received a copy of the GNU General Public License 017along with GNU Classpath; see the file COPYING. If not, write to the 018Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 01902110-1301 USA. 020 021Linking this library statically or dynamically with other modules is 022making a combined work based on this library. Thus, the terms and 023conditions of the GNU General Public License cover the whole 024combination. 025 026As a special exception, the copyright holders of this library give you 027permission to link this library with independent modules to produce an 028executable, regardless of the license terms of these independent 029modules, and to copy and distribute the resulting executable under 030terms of your choice, provided that you also meet, for each linked 031independent module, the terms and conditions of the license of that 032module. An independent module is a module which is not derived from 033or based on this library. If you modify this library, you may extend 034this exception to your version of the library, but you are not 035obligated to do so. If you do not wish to do so, delete this 036exception statement from your version. */ 037 038 039package java.beans.beancontext; 040 041import java.beans.Beans; 042import java.beans.DesignMode; 043import java.beans.PropertyChangeEvent; 044import java.beans.PropertyChangeListener; 045import java.beans.PropertyVetoException; 046import java.beans.VetoableChangeListener; 047import java.beans.Visibility; 048import java.io.IOException; 049import java.io.InputStream; 050import java.io.ObjectInputStream; 051import java.io.ObjectOutputStream; 052import java.io.Serializable; 053import java.net.URL; 054import java.util.ArrayList; 055import java.util.Collection; 056import java.util.HashMap; 057import java.util.Iterator; 058import java.util.List; 059import java.util.Locale; 060 061/** 062 * This is a helper class for implementing a bean context. It is 063 * intended to be used either by subclassing or by calling methods 064 * of this implementation from another. 065 * 066 * @author Michael Koch 067 * @author Andrew John Hughes (gnu_andrew@member.fsf.org) 068 * @since 1.2 069 */ 070public class BeanContextSupport extends BeanContextChildSupport 071 implements BeanContext, Serializable, PropertyChangeListener, 072 VetoableChangeListener 073{ 074 private static final long serialVersionUID = -4879613978649577204L; 075 076 /** 077 * Deserializes a stored bean context. Hook methods are provided to allow 078 * subclasses to perform their own deserialization after the default 079 * deserialization but prior to the deserialization of the children. Note that 080 * {@link #readChildren(ObjectInputStream)} is only called if there 081 * is no distinct peer. If there is, the peer is expected to call 082 * the method instead. 083 * 084 * @param s the stream to deserialize. 085 * @throws ClassNotFoundException if the class of an object being deserialized 086 * could not be found. 087 * @throws IOException if an I/O error occurs. 088 */ 089 private void readObject (ObjectInputStream s) 090 throws ClassNotFoundException, IOException 091 { 092 s.defaultReadObject(); 093 bcsPreDeserializationHook(s); 094 BeanContext peer = getBeanContextPeer(); 095 if (peer == null || peer == this) 096 readChildren(s); 097 } 098 099 /** 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}