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