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