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