1 /* 2 * Copyright (c) 1997, 2014, 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 {@code BeanContext} we are 89 * supplying an implementation for, 90 * or {@code null} 91 * if this object is its own peer 92 * @param lcle The current Locale for this BeanContext. If 93 * {@code lcle} is {@code null}, the default locale 94 * is assigned to the {@code BeanContext} instance. 95 * @param dTime The initial state, 96 * {@code true} if in design mode, 97 * {@code false} 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 {@code BeanContext} we 116 * are supplying an implementation for, 117 * or {@code null} if this object is its own peer 118 * @param lcle The current Locale for this {@code BeanContext}. If 119 * {@code lcle} is {@code null}, the default locale 120 * is assigned to the {@code BeanContext} instance. 121 * @param dtime The initial state, {@code true} 122 * if in design mode, 123 * {@code false} 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 {@code null} if this object 137 * is its own peer 138 * @param lcle The current Locale for this 139 * {@code BeanContext}. If 140 * {@code lcle} is {@code null}, 141 * the default locale 142 * is assigned to the {@code BeanContext} 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 {@code BeanContext} we are 155 * supplying an implementation for, 156 * or {@code null} 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 {@code BeanContext} 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 {@code BeanContext}. 184 * </p> 185 * <p> 186 * The semantics of the beanName parameter are defined by java.beans.Beans.instantiate. 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 * {@code BeanContext} is empty. 217 * A {@code BeanContext} 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 {@code BeanContext}. 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 {@code BeanContext}. 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 {@code BeanContext} instances 254 * currently nested in this {@code BeanContext}. 255 * @return an {@code Iterator} of the nested children 256 */ 257 public Iterator<Object> iterator() { 258 synchronized(children) { 259 return new BCSIterator(children.keySet().iterator()); 260 } 261 } 262 263 /** 264 * Gets all JavaBean or {@code BeanContext} 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 {@code BeanContext} 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<Object> { 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 @SuppressWarnings("serial") // Not statically typed as Serializable 342 private Object child; 343 @SuppressWarnings("serial") // Not statically typed as Serializable 344 private Object proxyPeer; 345 346 private transient boolean removePending; 347 } 348 349 /** 350 * <p> 351 * Subclasses can override this method to insert their own subclass 352 * of Child without having to override add() or the other Collection 353 * methods that add children to the set. 354 * </p> 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 * @return Subtype-specific subclass of Child without overriding collection methods 358 */ 359 360 protected BCSChild createBCSChild(Object targetChild, Object peer) { 361 return new BCSChild(targetChild, peer); 362 } 363 364 /************************************************************************/ 365 366 /** 367 * Adds/nests a child within this {@code BeanContext}. 368 * <p> 369 * Invoked as a side effect of java.beans.Beans.instantiate(). 370 * If the child object is not valid for adding then this method 371 * throws an IllegalStateException. 372 * </p> 373 * 374 * 375 * @param targetChild The child objects to nest 376 * within this {@code BeanContext} 377 * @return true if the child was added successfully. 378 * @see #validatePendingAdd 379 */ 380 public boolean add(Object targetChild) { 381 382 if (targetChild == null) throw new IllegalArgumentException(); 383 384 // The specification requires that we do nothing if the child 385 // is already nested herein. 386 387 if (children.containsKey(targetChild)) return false; // test before locking 388 389 synchronized(BeanContext.globalHierarchyLock) { 390 if (children.containsKey(targetChild)) return false; // check again 391 392 if (!validatePendingAdd(targetChild)) { 393 throw new IllegalStateException(); 394 } 395 396 397 // The specification requires that we invoke setBeanContext() on the 398 // newly added child if it implements the java.beans.beancontext.BeanContextChild interface 399 400 BeanContextChild cbcc = getChildBeanContextChild(targetChild); 401 BeanContextChild bccp = null; 402 403 synchronized(targetChild) { 404 405 if (targetChild instanceof BeanContextProxy) { 406 bccp = ((BeanContextProxy)targetChild).getBeanContextProxy(); 407 408 if (bccp == null) throw new NullPointerException("BeanContextPeer.getBeanContextProxy()"); 409 } 410 411 BCSChild bcsc = createBCSChild(targetChild, bccp); 412 BCSChild pbcsc = null; 413 414 synchronized (children) { 415 children.put(targetChild, bcsc); 416 417 if (bccp != null) children.put(bccp, pbcsc = createBCSChild(bccp, targetChild)); 418 } 419 420 if (cbcc != null) synchronized(cbcc) { 421 try { 422 cbcc.setBeanContext(getBeanContextPeer()); 423 } catch (PropertyVetoException pve) { 424 425 synchronized (children) { 426 children.remove(targetChild); 427 428 if (bccp != null) children.remove(bccp); 429 } 430 431 throw new IllegalStateException(); 432 } 433 434 cbcc.addPropertyChangeListener("beanContext", childPCL); 435 cbcc.addVetoableChangeListener("beanContext", childVCL); 436 } 437 438 Visibility v = getChildVisibility(targetChild); 439 440 if (v != null) { 441 if (okToUseGui) 442 v.okToUseGui(); 443 else 444 v.dontUseGui(); 445 } 446 447 if (getChildSerializable(targetChild) != null) serializable++; 448 449 childJustAddedHook(targetChild, bcsc); 450 451 if (bccp != null) { 452 v = getChildVisibility(bccp); 453 454 if (v != null) { 455 if (okToUseGui) 456 v.okToUseGui(); 457 else 458 v.dontUseGui(); 459 } 460 461 if (getChildSerializable(bccp) != null) serializable++; 462 463 childJustAddedHook(bccp, pbcsc); 464 } 465 466 467 } 468 469 // The specification requires that we fire a notification of the change 470 471 fireChildrenAdded(new BeanContextMembershipEvent(getBeanContextPeer(), bccp == null ? new Object[] { targetChild } : new Object[] { targetChild, bccp } )); 472 473 } 474 475 return true; 476 } 477 478 /** 479 * Removes a child from this BeanContext. If the child object is not 480 * for adding then this method throws an IllegalStateException. 481 * @param targetChild The child objects to remove 482 * @see #validatePendingRemove 483 */ 484 public boolean remove(Object targetChild) { 485 return remove(targetChild, true); 486 } 487 488 /** 489 * internal remove used when removal caused by 490 * unexpected {@code setBeanContext} or 491 * by {@code remove()} invocation. 492 * @param targetChild the JavaBean, BeanContext, or Object to be removed 493 * @param callChildSetBC used to indicate that 494 * the child should be notified that it is no 495 * longer nested in this {@code BeanContext}. 496 * @return whether or not was present before being removed 497 */ 498 protected boolean remove(Object targetChild, boolean callChildSetBC) { 499 500 if (targetChild == null) throw new IllegalArgumentException(); 501 502 synchronized(BeanContext.globalHierarchyLock) { 503 if (!containsKey(targetChild)) return false; 504 505 if (!validatePendingRemove(targetChild)) { 506 throw new IllegalStateException(); 507 } 508 509 BCSChild bcsc = children.get(targetChild); 510 BCSChild pbcsc = null; 511 Object peer = null; 512 513 // we are required to notify the child that it is no longer nested here if 514 // it implements java.beans.beancontext.BeanContextChild 515 516 synchronized(targetChild) { 517 if (callChildSetBC) { 518 BeanContextChild cbcc = getChildBeanContextChild(targetChild); 519 if (cbcc != null) synchronized(cbcc) { 520 cbcc.removePropertyChangeListener("beanContext", childPCL); 521 cbcc.removeVetoableChangeListener("beanContext", childVCL); 522 523 try { 524 cbcc.setBeanContext(null); 525 } catch (PropertyVetoException pve1) { 526 cbcc.addPropertyChangeListener("beanContext", childPCL); 527 cbcc.addVetoableChangeListener("beanContext", childVCL); 528 throw new IllegalStateException(); 529 } 530 531 } 532 } 533 534 synchronized (children) { 535 children.remove(targetChild); 536 537 if (bcsc.isProxyPeer()) { 538 pbcsc = children.get(peer = bcsc.getProxyPeer()); 539 children.remove(peer); 540 } 541 } 542 543 if (getChildSerializable(targetChild) != null) serializable--; 544 545 childJustRemovedHook(targetChild, bcsc); 546 547 if (peer != null) { 548 if (getChildSerializable(peer) != null) serializable--; 549 550 childJustRemovedHook(peer, pbcsc); 551 } 552 } 553 554 fireChildrenRemoved(new BeanContextMembershipEvent(getBeanContextPeer(), peer == null ? new Object[] { targetChild } : new Object[] { targetChild, peer } )); 555 556 } 557 558 return true; 559 } 560 561 /** 562 * Tests to see if all objects in the 563 * specified {@code Collection} are children of 564 * this {@code BeanContext}. 565 * @param c the specified {@code Collection} 566 * 567 * @return {@code true} if all objects 568 * in the collection are children of 569 * this {@code BeanContext}, false if not. 570 */ 571 @SuppressWarnings("rawtypes") 572 public boolean containsAll(Collection c) { 573 synchronized(children) { 574 Iterator<?> i = c.iterator(); 575 while (i.hasNext()) 576 if(!contains(i.next())) 577 return false; 578 579 return true; 580 } 581 } 582 583 /** 584 * add Collection to set of Children (Unsupported) 585 * implementations must synchronized on the hierarchy lock and "children" protected field 586 * @throws UnsupportedOperationException thrown unconditionally by this implementation 587 * @return this implementation unconditionally throws {@code UnsupportedOperationException} 588 */ 589 @SuppressWarnings("rawtypes") 590 public boolean addAll(Collection c) { 591 throw new UnsupportedOperationException(); 592 } 593 594 /** 595 * remove all specified children (Unsupported) 596 * implementations must synchronized on the hierarchy lock and "children" protected field 597 * @throws UnsupportedOperationException thrown unconditionally by this implementation 598 * @return this implementation unconditionally throws {@code UnsupportedOperationException} 599 600 */ 601 @SuppressWarnings("rawtypes") 602 public boolean removeAll(Collection c) { 603 throw new UnsupportedOperationException(); 604 } 605 606 607 /** 608 * retain only specified children (Unsupported) 609 * implementations must synchronized on the hierarchy lock and "children" protected field 610 * @throws UnsupportedOperationException thrown unconditionally by this implementation 611 * @return this implementation unconditionally throws {@code UnsupportedOperationException} 612 */ 613 @SuppressWarnings("rawtypes") 614 public boolean retainAll(Collection c) { 615 throw new UnsupportedOperationException(); 616 } 617 618 /** 619 * clear the children (Unsupported) 620 * implementations must synchronized on the hierarchy lock and "children" protected field 621 * @throws UnsupportedOperationException thrown unconditionally by this implementation 622 */ 623 public void clear() { 624 throw new UnsupportedOperationException(); 625 } 626 627 /** 628 * Adds a BeanContextMembershipListener 629 * 630 * @param bcml the BeanContextMembershipListener to add 631 * @throws NullPointerException if the argument is null 632 */ 633 634 public void addBeanContextMembershipListener(BeanContextMembershipListener bcml) { 635 if (bcml == null) throw new NullPointerException("listener"); 636 637 synchronized(bcmListeners) { 638 if (bcmListeners.contains(bcml)) 639 return; 640 else 641 bcmListeners.add(bcml); 642 } 643 } 644 645 /** 646 * Removes a BeanContextMembershipListener 647 * 648 * @param bcml the BeanContextMembershipListener to remove 649 * @throws NullPointerException if the argument is null 650 */ 651 652 public void removeBeanContextMembershipListener(BeanContextMembershipListener bcml) { 653 if (bcml == null) throw new NullPointerException("listener"); 654 655 synchronized(bcmListeners) { 656 if (!bcmListeners.contains(bcml)) 657 return; 658 else 659 bcmListeners.remove(bcml); 660 } 661 } 662 663 /** 664 * @param name the name of the resource requested. 665 * @param bcc the child object making the request. 666 * 667 * @return the requested resource as an InputStream 668 * @throws NullPointerException if the argument is null 669 */ 670 671 public InputStream getResourceAsStream(String name, BeanContextChild bcc) { 672 if (name == null) throw new NullPointerException("name"); 673 if (bcc == null) throw new NullPointerException("bcc"); 674 675 if (containsKey(bcc)) { 676 ClassLoader cl = bcc.getClass().getClassLoader(); 677 678 return cl != null ? cl.getResourceAsStream(name) 679 : ClassLoader.getSystemResourceAsStream(name); 680 } else throw new IllegalArgumentException("Not a valid child"); 681 } 682 683 /** 684 * @param name the name of the resource requested. 685 * @param bcc the child object making the request. 686 * 687 * @return the requested resource as an InputStream 688 */ 689 690 public URL getResource(String name, BeanContextChild bcc) { 691 if (name == null) throw new NullPointerException("name"); 692 if (bcc == null) throw new NullPointerException("bcc"); 693 694 if (containsKey(bcc)) { 695 ClassLoader cl = bcc.getClass().getClassLoader(); 696 697 return cl != null ? cl.getResource(name) 698 : ClassLoader.getSystemResource(name); 699 } else throw new IllegalArgumentException("Not a valid child"); 700 } 701 702 /** 703 * Sets the new design time value for this {@code BeanContext}. 704 * @param dTime the new designTime value 705 */ 706 public synchronized void setDesignTime(boolean dTime) { 707 if (designTime != dTime) { 708 designTime = dTime; 709 710 firePropertyChange("designMode", Boolean.valueOf(!dTime), Boolean.valueOf(dTime)); 711 } 712 } 713 714 715 /** 716 * Reports whether or not this object is in 717 * currently in design time mode. 718 * @return {@code true} if in design time mode, 719 * {@code false} if not 720 */ 721 public synchronized boolean isDesignTime() { return designTime; } 722 723 /** 724 * Sets the locale of this BeanContext. 725 * @param newLocale the new locale. This method call will have 726 * no effect if newLocale is {@code null}. 727 * @throws PropertyVetoException if the new value is rejected 728 */ 729 public synchronized void setLocale(Locale newLocale) throws PropertyVetoException { 730 731 if ((locale != null && !locale.equals(newLocale)) && newLocale != null) { 732 Locale old = locale; 733 734 fireVetoableChange("locale", old, newLocale); // throws 735 736 locale = newLocale; 737 738 firePropertyChange("locale", old, newLocale); 739 } 740 } 741 742 /** 743 * Gets the locale for this {@code BeanContext}. 744 * 745 * @return the current Locale of the {@code BeanContext} 746 */ 747 public synchronized Locale getLocale() { return locale; } 748 749 /** 750 * <p> 751 * This method is typically called from the environment in order to determine 752 * if the implementor "needs" a GUI. 753 * </p> 754 * <p> 755 * The algorithm used herein tests the BeanContextPeer, and its current children 756 * to determine if they are either Containers, Components, or if they implement 757 * Visibility and return needsGui() == true. 758 * </p> 759 * @return {@code true} if the implementor needs a GUI 760 */ 761 public synchronized boolean needsGui() { 762 BeanContext bc = getBeanContextPeer(); 763 764 if (bc != this) { 765 if (bc instanceof Visibility) return ((Visibility)bc).needsGui(); 766 767 if (bc instanceof Container || bc instanceof Component) 768 return true; 769 } 770 771 synchronized(children) { 772 for (Iterator<Object> i = children.keySet().iterator(); i.hasNext();) { 773 Object c = i.next(); 774 775 try { 776 return ((Visibility)c).needsGui(); 777 } catch (ClassCastException cce) { 778 // do nothing ... 779 } 780 781 if (c instanceof Container || c instanceof Component) 782 return true; 783 } 784 } 785 786 return false; 787 } 788 789 /** 790 * notify this instance that it may no longer render a GUI. 791 */ 792 793 public synchronized void dontUseGui() { 794 if (okToUseGui) { 795 okToUseGui = false; 796 797 // lets also tell the Children that can that they may not use their GUI's 798 synchronized(children) { 799 for (Iterator<Object> i = children.keySet().iterator(); i.hasNext();) { 800 Visibility v = getChildVisibility(i.next()); 801 802 if (v != null) v.dontUseGui(); 803 } 804 } 805 } 806 } 807 808 /** 809 * Notify this instance that it may now render a GUI 810 */ 811 812 public synchronized void okToUseGui() { 813 if (!okToUseGui) { 814 okToUseGui = true; 815 816 // lets also tell the Children that can that they may use their GUI's 817 synchronized(children) { 818 for (Iterator<Object> i = children.keySet().iterator(); i.hasNext();) { 819 Visibility v = getChildVisibility(i.next()); 820 821 if (v != null) v.okToUseGui(); 822 } 823 } 824 } 825 } 826 827 /** 828 * Used to determine if the {@code BeanContext} 829 * child is avoiding using its GUI. 830 * @return is this instance avoiding using its GUI? 831 * @see Visibility 832 */ 833 public boolean avoidingGui() { 834 return !okToUseGui && needsGui(); 835 } 836 837 /** 838 * Is this {@code BeanContext} in the 839 * process of being serialized? 840 * @return if this {@code BeanContext} is 841 * currently being serialized 842 */ 843 public boolean isSerializing() { return serializing; } 844 845 /** 846 * Returns an iterator of all children 847 * of this {@code BeanContext}. 848 * @return an iterator for all the current BCSChild values 849 */ 850 protected Iterator<BCSChild> bcsChildren() { synchronized(children) { return children.values().iterator(); } } 851 852 /** 853 * called by writeObject after defaultWriteObject() but prior to 854 * serialization of currently serializable children. 855 * 856 * This method may be overridden by subclasses to perform custom 857 * serialization of their state prior to this superclass serializing 858 * the children. 859 * 860 * This method should not however be used by subclasses to replace their 861 * own implementation (if any) of writeObject(). 862 * @param oos the {@code ObjectOutputStream} to use during serialization 863 * @throws IOException if serialization failed 864 */ 865 866 protected void bcsPreSerializationHook(ObjectOutputStream oos) throws IOException { 867 } 868 869 /** 870 * called by readObject after defaultReadObject() but prior to 871 * deserialization of any children. 872 * 873 * This method may be overridden by subclasses to perform custom 874 * deserialization of their state prior to this superclass deserializing 875 * the children. 876 * 877 * This method should not however be used by subclasses to replace their 878 * own implementation (if any) of readObject(). 879 * @param ois the {@code ObjectInputStream} to use during deserialization 880 * @throws IOException if deserialization failed 881 * @throws ClassNotFoundException if needed classes are not found 882 */ 883 884 protected void bcsPreDeserializationHook(ObjectInputStream ois) throws IOException, ClassNotFoundException { 885 } 886 887 /** 888 * Called by readObject with the newly deserialized child and BCSChild. 889 * @param child the newly deserialized child 890 * @param bcsc the newly deserialized BCSChild 891 */ 892 protected void childDeserializedHook(Object child, BCSChild bcsc) { 893 synchronized(children) { 894 children.put(child, bcsc); 895 } 896 } 897 898 /** 899 * Used by writeObject to serialize a Collection. 900 * @param oos the {@code ObjectOutputStream} 901 * to use during serialization 902 * @param coll the {@code Collection} to serialize 903 * @throws IOException if serialization failed 904 */ 905 protected final void serialize(ObjectOutputStream oos, Collection<?> coll) throws IOException { 906 int count = 0; 907 Object[] objects = coll.toArray(); 908 909 for (int i = 0; i < objects.length; i++) { 910 if (objects[i] instanceof Serializable) 911 count++; 912 else 913 objects[i] = null; 914 } 915 916 oos.writeInt(count); // number of subsequent objects 917 918 for (int i = 0; count > 0; i++) { 919 Object o = objects[i]; 920 921 if (o != null) { 922 oos.writeObject(o); 923 count--; 924 } 925 } 926 } 927 928 /** 929 * used by readObject to deserialize a collection. 930 * @param ois the ObjectInputStream to use 931 * @param coll the Collection 932 * @throws IOException if deserialization failed 933 * @throws ClassNotFoundException if needed classes are not found 934 */ 935 @SuppressWarnings({"rawtypes", "unchecked"}) 936 protected final void deserialize(ObjectInputStream ois, Collection coll) throws IOException, ClassNotFoundException { 937 int count = 0; 938 939 count = ois.readInt(); 940 941 while (count-- > 0) { 942 coll.add(ois.readObject()); 943 } 944 } 945 946 /** 947 * Used to serialize all children of 948 * this {@code BeanContext}. 949 * @param oos the {@code ObjectOutputStream} 950 * to use during serialization 951 * @throws IOException if serialization failed 952 */ 953 public final void writeChildren(ObjectOutputStream oos) throws IOException { 954 if (serializable <= 0) return; 955 956 boolean prev = serializing; 957 958 serializing = true; 959 960 int count = 0; 961 962 synchronized(children) { 963 Iterator<Map.Entry<Object, BCSChild>> i = children.entrySet().iterator(); 964 965 while (i.hasNext() && count < serializable) { 966 Map.Entry<Object, BCSChild> entry = i.next(); 967 968 if (entry.getKey() instanceof Serializable) { 969 try { 970 oos.writeObject(entry.getKey()); // child 971 oos.writeObject(entry.getValue()); // BCSChild 972 } catch (IOException ioe) { 973 serializing = prev; 974 throw ioe; 975 } 976 count++; 977 } 978 } 979 } 980 981 serializing = prev; 982 983 if (count != serializable) { 984 throw new IOException("wrote different number of children than expected"); 985 } 986 987 } 988 989 /** 990 * Serialize the BeanContextSupport, if this instance has a distinct 991 * peer (that is this object is acting as a delegate for another) then 992 * the children of this instance are not serialized here due to a 993 * 'chicken and egg' problem that occurs on deserialization of the 994 * children at the same time as this instance. 995 * 996 * Therefore in situations where there is a distinct peer to this instance 997 * it should always call writeObject() followed by writeChildren() and 998 * readObject() followed by readChildren(). 999 * 1000 * @param oos the ObjectOutputStream 1001 */ 1002 1003 private synchronized void writeObject(ObjectOutputStream oos) throws IOException { 1004 serializing = true; 1005 1006 synchronized (BeanContext.globalHierarchyLock) { 1007 try { 1008 oos.defaultWriteObject(); // serialize the BeanContextSupport object 1009 1010 bcsPreSerializationHook(oos); 1011 1012 if (serializable > 0 && this.equals(getBeanContextPeer())) 1013 writeChildren(oos); 1014 1015 serialize(oos, (Collection)bcmListeners); 1016 } finally { 1017 serializing = false; 1018 } 1019 } 1020 } 1021 1022 /** 1023 * When an instance of this class is used as a delegate for the 1024 * implementation of the BeanContext protocols (and its subprotocols) 1025 * there exists a 'chicken and egg' problem during deserialization 1026 * @param ois the ObjectInputStream to use 1027 * @throws IOException if deserialization failed 1028 * @throws ClassNotFoundException if needed classes are not found 1029 */ 1030 1031 public final void readChildren(ObjectInputStream ois) throws IOException, ClassNotFoundException { 1032 int count = serializable; 1033 1034 while (count-- > 0) { 1035 Object child = null; 1036 BeanContextSupport.BCSChild bscc = null; 1037 1038 try { 1039 child = ois.readObject(); 1040 bscc = (BeanContextSupport.BCSChild)ois.readObject(); 1041 } catch (IOException ioe) { 1042 continue; 1043 } catch (ClassNotFoundException cnfe) { 1044 continue; 1045 } 1046 1047 1048 synchronized(child) { 1049 BeanContextChild bcc = null; 1050 1051 try { 1052 bcc = (BeanContextChild)child; 1053 } catch (ClassCastException cce) { 1054 // do nothing; 1055 } 1056 1057 if (bcc != null) { 1058 try { 1059 bcc.setBeanContext(getBeanContextPeer()); 1060 1061 bcc.addPropertyChangeListener("beanContext", childPCL); 1062 bcc.addVetoableChangeListener("beanContext", childVCL); 1063 1064 } catch (PropertyVetoException pve) { 1065 continue; 1066 } 1067 } 1068 1069 childDeserializedHook(child, bscc); 1070 } 1071 } 1072 } 1073 1074 /** 1075 * deserialize contents ... if this instance has a distinct peer the 1076 * children are *not* serialized here, the peer's readObject() must call 1077 * readChildren() after deserializing this instance. 1078 */ 1079 1080 private synchronized void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException { 1081 1082 synchronized(BeanContext.globalHierarchyLock) { 1083 ois.defaultReadObject(); 1084 1085 initialize(); 1086 1087 bcsPreDeserializationHook(ois); 1088 1089 if (serializable > 0 && this.equals(getBeanContextPeer())) 1090 readChildren(ois); 1091 1092 deserialize(ois, bcmListeners = new ArrayList<>(1)); 1093 } 1094 } 1095 1096 /** 1097 * subclasses may envelope to monitor veto child property changes. 1098 */ 1099 1100 public void vetoableChange(PropertyChangeEvent pce) throws PropertyVetoException { 1101 String propertyName = pce.getPropertyName(); 1102 Object source = pce.getSource(); 1103 1104 synchronized(children) { 1105 if ("beanContext".equals(propertyName) && 1106 containsKey(source) && 1107 !getBeanContextPeer().equals(pce.getNewValue()) 1108 ) { 1109 if (!validatePendingRemove(source)) { 1110 throw new PropertyVetoException("current BeanContext vetoes setBeanContext()", pce); 1111 } else children.get(source).setRemovePending(true); 1112 } 1113 } 1114 } 1115 1116 /** 1117 * subclasses may envelope to monitor child property changes. 1118 */ 1119 1120 public void propertyChange(PropertyChangeEvent pce) { 1121 String propertyName = pce.getPropertyName(); 1122 Object source = pce.getSource(); 1123 1124 synchronized(children) { 1125 if ("beanContext".equals(propertyName) && 1126 containsKey(source) && 1127 children.get(source).isRemovePending()) { 1128 BeanContext bc = getBeanContextPeer(); 1129 1130 if (bc.equals(pce.getOldValue()) && !bc.equals(pce.getNewValue())) { 1131 remove(source, false); 1132 } else { 1133 children.get(source).setRemovePending(false); 1134 } 1135 } 1136 } 1137 } 1138 1139 /** 1140 * <p> 1141 * Subclasses of this class may override, or envelope, this method to 1142 * add validation behavior for the BeanContext to examine child objects 1143 * immediately prior to their being added to the BeanContext. 1144 * </p> 1145 * 1146 * @param targetChild the child to create the Child on behalf of 1147 * @return true iff the child may be added to this BeanContext, otherwise false. 1148 */ 1149 1150 protected boolean validatePendingAdd(Object targetChild) { 1151 return true; 1152 } 1153 1154 /** 1155 * <p> 1156 * Subclasses of this class may override, or envelope, this method to 1157 * add validation behavior for the BeanContext to examine child objects 1158 * immediately prior to their being removed from the BeanContext. 1159 * </p> 1160 * 1161 * @param targetChild the child to create the Child on behalf of 1162 * @return true iff the child may be removed from this BeanContext, otherwise false. 1163 */ 1164 1165 protected boolean validatePendingRemove(Object targetChild) { 1166 return true; 1167 } 1168 1169 /** 1170 * subclasses may override this method to simply extend add() semantics 1171 * after the child has been added and before the event notification has 1172 * occurred. The method is called with the child synchronized. 1173 * @param child the child 1174 * @param bcsc the BCSChild 1175 */ 1176 1177 protected void childJustAddedHook(Object child, BCSChild bcsc) { 1178 } 1179 1180 /** 1181 * subclasses may override this method to simply extend remove() semantics 1182 * after the child has been removed and before the event notification has 1183 * occurred. The method is called with the child synchronized. 1184 * @param child the child 1185 * @param bcsc the BCSChild 1186 */ 1187 1188 protected void childJustRemovedHook(Object child, BCSChild bcsc) { 1189 } 1190 1191 /** 1192 * Gets the Component (if any) associated with the specified child. 1193 * @param child the specified child 1194 * @return the Component (if any) associated with the specified child. 1195 */ 1196 protected static final Visibility getChildVisibility(Object child) { 1197 try { 1198 return (Visibility)child; 1199 } catch (ClassCastException cce) { 1200 return null; 1201 } 1202 } 1203 1204 /** 1205 * Gets the Serializable (if any) associated with the specified Child 1206 * @param child the specified child 1207 * @return the Serializable (if any) associated with the specified Child 1208 */ 1209 protected static final Serializable getChildSerializable(Object child) { 1210 try { 1211 return (Serializable)child; 1212 } catch (ClassCastException cce) { 1213 return null; 1214 } 1215 } 1216 1217 /** 1218 * Gets the PropertyChangeListener 1219 * (if any) of the specified child 1220 * @param child the specified child 1221 * @return the PropertyChangeListener (if any) of the specified child 1222 */ 1223 protected static final PropertyChangeListener getChildPropertyChangeListener(Object child) { 1224 try { 1225 return (PropertyChangeListener)child; 1226 } catch (ClassCastException cce) { 1227 return null; 1228 } 1229 } 1230 1231 /** 1232 * Gets the VetoableChangeListener 1233 * (if any) of the specified child 1234 * @param child the specified child 1235 * @return the VetoableChangeListener (if any) of the specified child 1236 */ 1237 protected static final VetoableChangeListener getChildVetoableChangeListener(Object child) { 1238 try { 1239 return (VetoableChangeListener)child; 1240 } catch (ClassCastException cce) { 1241 return null; 1242 } 1243 } 1244 1245 /** 1246 * Gets the BeanContextMembershipListener 1247 * (if any) of the specified child 1248 * @param child the specified child 1249 * @return the BeanContextMembershipListener (if any) of the specified child 1250 */ 1251 protected static final BeanContextMembershipListener getChildBeanContextMembershipListener(Object child) { 1252 try { 1253 return (BeanContextMembershipListener)child; 1254 } catch (ClassCastException cce) { 1255 return null; 1256 } 1257 } 1258 1259 /** 1260 * Gets the BeanContextChild (if any) of the specified child 1261 * @param child the specified child 1262 * @return the BeanContextChild (if any) of the specified child 1263 * @throws IllegalArgumentException if child implements both BeanContextChild and BeanContextProxy 1264 */ 1265 protected static final BeanContextChild getChildBeanContextChild(Object child) { 1266 try { 1267 BeanContextChild bcc = (BeanContextChild)child; 1268 1269 if (child instanceof BeanContextChild && child instanceof BeanContextProxy) 1270 throw new IllegalArgumentException("child cannot implement both BeanContextChild and BeanContextProxy"); 1271 else 1272 return bcc; 1273 } catch (ClassCastException cce) { 1274 try { 1275 return ((BeanContextProxy)child).getBeanContextProxy(); 1276 } catch (ClassCastException cce1) { 1277 return null; 1278 } 1279 } 1280 } 1281 1282 /** 1283 * Fire a BeanContextshipEvent on the BeanContextMembershipListener interface 1284 * @param bcme the event to fire 1285 */ 1286 1287 protected final void fireChildrenAdded(BeanContextMembershipEvent bcme) { 1288 Object[] copy; 1289 1290 synchronized(bcmListeners) { copy = bcmListeners.toArray(); } 1291 1292 for (int i = 0; i < copy.length; i++) 1293 ((BeanContextMembershipListener)copy[i]).childrenAdded(bcme); 1294 } 1295 1296 /** 1297 * Fire a BeanContextshipEvent on the BeanContextMembershipListener interface 1298 * @param bcme the event to fire 1299 */ 1300 1301 protected final void fireChildrenRemoved(BeanContextMembershipEvent bcme) { 1302 Object[] copy; 1303 1304 synchronized(bcmListeners) { copy = bcmListeners.toArray(); } 1305 1306 for (int i = 0; i < copy.length; i++) 1307 ((BeanContextMembershipListener)copy[i]).childrenRemoved(bcme); 1308 } 1309 1310 /** 1311 * protected method called from constructor and readObject to initialize 1312 * transient state of BeanContextSupport instance. 1313 * 1314 * This class uses this method to instantiate inner class listeners used 1315 * to monitor PropertyChange and VetoableChange events on children. 1316 * 1317 * subclasses may envelope this method to add their own initialization 1318 * behavior 1319 */ 1320 1321 protected synchronized void initialize() { 1322 children = new HashMap<>(serializable + 1); 1323 bcmListeners = new ArrayList<>(1); 1324 1325 childPCL = new PropertyChangeListener() { 1326 1327 /* 1328 * this adaptor is used by the BeanContextSupport class to forward 1329 * property changes from a child to the BeanContext, avoiding 1330 * accidential serialization of the BeanContext by a badly 1331 * behaved Serializable child. 1332 */ 1333 1334 public void propertyChange(PropertyChangeEvent pce) { 1335 BeanContextSupport.this.propertyChange(pce); 1336 } 1337 }; 1338 1339 childVCL = new VetoableChangeListener() { 1340 1341 /* 1342 * this adaptor is used by the BeanContextSupport class to forward 1343 * vetoable changes from a child to the BeanContext, avoiding 1344 * accidential serialization of the BeanContext by a badly 1345 * behaved Serializable child. 1346 */ 1347 1348 public void vetoableChange(PropertyChangeEvent pce) throws PropertyVetoException { 1349 BeanContextSupport.this.vetoableChange(pce); 1350 } 1351 }; 1352 } 1353 1354 /** 1355 * Gets a copy of the this BeanContext's children. 1356 * @return a copy of the current nested children 1357 */ 1358 protected final Object[] copyChildren() { 1359 synchronized(children) { return children.keySet().toArray(); } 1360 } 1361 1362 /** 1363 * Tests to see if two class objects, 1364 * or their names are equal. 1365 * @param first the first object 1366 * @param second the second object 1367 * @return true if equal, false if not 1368 */ 1369 protected static final boolean classEquals(Class<?> first, Class<?> second) { 1370 return first.equals(second) || first.getName().equals(second.getName()); 1371 } 1372 1373 1374 /* 1375 * fields 1376 */ 1377 1378 1379 /** 1380 * all accesses to the {@code protected HashMap children} field 1381 * shall be synchronized on that object. 1382 */ 1383 protected transient HashMap<Object, BCSChild> children; 1384 1385 private int serializable = 0; // children serializable 1386 1387 /** 1388 * all accesses to the {@code protected ArrayList bcmListeners} field 1389 * shall be synchronized on that object. 1390 */ 1391 protected transient ArrayList<BeanContextMembershipListener> bcmListeners; 1392 1393 // 1394 1395 /** 1396 * The current locale of this BeanContext. 1397 */ 1398 protected Locale locale; 1399 1400 /** 1401 * A {@code boolean} indicating if this 1402 * instance may now render a GUI. 1403 */ 1404 protected boolean okToUseGui; 1405 1406 1407 /** 1408 * A {@code boolean} indicating whether or not 1409 * this object is currently in design time mode. 1410 */ 1411 protected boolean designTime; 1412 1413 /* 1414 * transient 1415 */ 1416 1417 private transient PropertyChangeListener childPCL; 1418 1419 private transient VetoableChangeListener childVCL; 1420 1421 private transient boolean serializing; 1422 }